前端-前端发展

1 Html5超文本标记语言的出现

为什么出现?解决了什么问题?

如果想要全世界一起分享知识,非常难。但是由于互联网通信可以传播数据,可以达到同伴间以特定形式的数据进行交流。那如何将团体扩展为全世界呢?那就需要一个规范,即 W3C的HTML5规范。浏览器webkit首先要做的事情是解析HTML,分析出里面的HTML元素,渲染为为页面。

2、Css的出现

当html变多时,页面复杂难看。提出了样式层叠。即 W3C的css规范。浏览器webkit进行匹配与解析。

在DOM中的节点接口中,加入获取和修改CSS属性或接口的JavaScript接口。开发者可以通过document.stylesheets查看当前网页包含的所有CSS样式表,因为CSSOM对DOM中的Document接口进行了扩展。

3 JavaScript的出现

应用于网页前端开发的脚本语言,用来给HTML网页增加动态功能

完整的JavaScript实现包含三个部分:ECMAScript,文档对象模型,浏览器对象模型。

Javascript引擎是一种软件,用于解释和执行Javascript代码,如V8引擎。其包含解释器和编译器

JavaScript的解释器负责解析和执行JavaScript代码。解释器将JavaScript代码解析为抽象语法树(AST),然后将AST转换为可执行代码,并将其传递给JavaScript的编译器。JavaScript的编译器转换为机器代码,以便直接在CPU上执行。

JavaScript的解释器--还需要学习

4、 jQuery的出现

为什么出现?

jQuery是一个JavaScript库为了让编写JavaScript代码变得有趣。jQuery通过封装通用性、重复性的功能,移除掉不必要的代码修辞,让JavaScript代码变得简单、精炼和更加可读。

jQuery主要解决了两大问题,第一:它提供了一整套简洁的API用于操作Dom,尤其是提供了一系列强大的方法去选择页面元素。第二个:它抹平了不同浏览器的差异,链式操作,动画、ajax等等

为什么弃用?

1.出现快速选取DOM节点方法,document.querySelector和document.querySelectorAll

2.可以方便操作DOM元素的API(classList),比如addClass 、removeClass 、toggleClass。

3.现在CSS3动画技术已经非常的成熟

4.JQuery的ajax操作可替代,原生JS的Fetch API还是axios。都为我们提供了强大的ajax使用能力

5.JQuery直接操作DOM,回流频繁,性能消耗大。vue的出现,先进行节点片段解析,再添加dom元素,只进行一次渲染。当需要操作时,会进行diff比较,通过改变Virtual DOM来以最小变动来改变真实DOM。

5 、Ajax的出现

Ajax 认为是Asynchronous JavaScript + XML (异步 JavaScript + XML)。通过异步通信,允许浏览器与服务器通信而无需刷新当前页面的技术。

5.1 Ajax出现前:服务端渲染SSR

参考:谈谈我对服务端渲染(SSR)的理解 - 掘金 (juejin.cn)

前后端不分离,客户端请求时,在服务器上使用模板引擎将模板与数据拼接成完整的html,再发送给客户端,客户端接收后直接解析 HTML就可以在浏览器上展示出来,不需要额外的异步请求获取数据。浏览器不需要再经过JavaScript执行就可以直接构建出DOM树并展示到页面中。但是这时候的web只能实现简单的信息展示,要使web有交互性,客户端需要再用 js 去操作 dom 或者渲染其他动态的部分。 传统服务端渲染流程如下:

如果我这个页面中只有一部分内容是动态绑定的,如果我需要看到这部分内容,就需要刷新整个页面重新获取才行,因为绑定数据只有在服务器上才能进行。(过程是怎样的?)

监听url变化,去后端请求对应页面数据

//hash模式
window.addEventListener('hashchange',function(event){
   console.log(event);
})
//history模式
window.addEventListener('popstate', function(event) {
  console.log('当前URL为:', document.location.href);
});

5.2 Ajax出现时

客户端渲染: 指使用 JS 来渲染页面大部分内容,代表是现在流行的 SPA 单页面应用(如react和vue)。客户端在请求时,服务端不做任何处理,直接将前端资源打包后生成的html返回给客户端一个空的静态HTML文件,需要客户端去加载执行js代码才能渲染生成页面内容,同时完成事件绑定,然后客户端再去通过ajax请求后端api获取数据更新视图。 客户端渲染流程如下:

5.3 SSR与CSR 对比

5.4、ajax技术实现

5.4.1、AJAX技术体系

AJAX技术体系的组成部分有HTML,css,dom,xml,xmlHttpRequest,javascript。

Ajax 是一个技术统称,具有局部刷新页面,无需重载整个页面特点利用 XMLHttpRequest 模块实现 Ajax。XmlHttpRequest使您可以使用JavaScript向服务器提出请求并处理响应,而不阻塞用户。通过XMLHttpRequest对象,Web开发人员可以在页面加载以后进行页面的局部更新。

5.4.2、ajax请求步骤

//1、创建 XMLHttpRequest 对象
var ajax = new XMLHttpRequest();
//2、规定请求的类型、URL 以及是否异步处理请求。
ajax.open('GET',url,true);
//3、使用post才会用到,get并不需要调用该方法
//ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); 
//4、发送请求
ajax.send(null);  
//5、接受服务器响应数据
//每当 readyState 改变时,就会触发 onreadystatechange 事件。
//onreadystatechange 事件被触发 4 次, 分别是: 0-1、1-2、2-3、3-4,
//对应着 readyState 的每个变化。
ajax.onreadystatechange = function () {
    if (ajax.readyState == 4 ) { 
    }
};

readyState的值及解释:用来存放XMLHttpRequest的状态

  1. 请求未初始化(还没有调用 open())。
  2. 请求已经建立,但是还没有发送(还没有调用 send())。
  3. 请求已发送,正在处理中(通常现在可以从响应中获取内容头)。
  4. 请求在处理中;通常响应中已有部分数据可用了,但是服务器还没有完成响应的生成。
  5. 响应已完成;您可以获取并使用服务器的响应了。

status 服务器返回的状态码

如果 XMLHttpRequest 出错,浏览器返回的 status 也为 0,其他和http状态码一致。

5.4.3、介绍一下XMLHttpRequest对象的常用方法和属性

  1. open(“method”,”URL”) 建立对服务器的调用,第一个参数是HTTP请求 方式可以为GET,POST或任何服务器所支持的您想调用的方式。第二个参数是请求页面的URL。
  2. send()方法,发送具体请求
  3. setRequestHeader()设置头信息
  4. onreadystatechange 用于监听ajax的工作状态
  5. abort()方法,停止当前请求
  6. readyState属性 请求的状态 有5个可取值0=未初始化 ,1=正在加载,2=以加载,3=交互中,4=完成
  7. responseText 属性 服务器的响应,表示为一个串
  8. reponseXML 属性 服务器的响应,表示为XML
  9. status 服务器的HTTP状态码,200对应ok 400对应not found

6、 axios

6.1、axios的由来、特性与使用

JQuery ajax 是对原生XHR的封装,由于MVC与JQuery不适合Vue项目。axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征:

  1. 从浏览器中创建 XMLHttpRequest,从 node.js 发出 http 请求
  2. 支持 Promise API,允许您更容易地处理异步操作,包括处理响应和错误。
  3. 拦截请求和响应,使用拦截器来全局配置请求和响应,以及在请求和响应发送之前执行特定操作。
  4. 可以取消请求,自动转换JSON数据
  5. 客户端支持防止CSRF/XSRF

发送请求

axios.post('https://api.example.com/post', { data: 'some data' })
  .then(response => {
    // 处理成功响应
    console.log(response.data);
  })
  .catch(error => {
    // 处理错误
    console.error(error);
  });

 添加拦截器函数

// 设置请求头
axios.defaults.headers.common['Authorization'] = 'Bearer TOKEN';

// 设置请求超时时间(毫秒)
axios.defaults.timeout = 5000;

// 自定义响应数据格式
axios.defaults.responseType = 'json';

// 添加一个请求拦截器。
axios.interceptors.request.use(function (config) {
    // 发起请求前执行一些处理任务
    return config; // 返回配置信息
  }, function (error) {
    // 请求错误时的处理
    return Promise.reject(error);
  });

// 添加一个响应拦截器
axios.interceptors.response.use(function (response) {
    // 处理响应数据
    return response; // 返回响应数据
  }, function (error) {
    // 响应出错后所做的处理工作
    return Promise.reject(error);
  });

并发请求

axios.all([
  axios.get('https://api.example.com/data1'),
  axios.get('https://api.example.com/data2')
])
.then(axios.spread((response1, response2) => {
  // 处理多个响应
  console.log(response1.data, response2.data);
}))
.catch(error => {
  // 处理错误
  console.error(error);
});

6.2、 补充:fetch

fetch是原生js一个是真实存在的 API,fetch是一种HTTP数据请求的方式,号称是ajax的替代品。同时解决了回调地狱。

<body>
  <script>
    function ajaxFetch(url) {
      fetch(url).then(res => res.json()).then(data => {
        console.info(data)
      })
    }
    ajaxFetch('https://smallpig.site/api/category/getCategory')
  </script>
</body>

7、模块化

7.1、前端发展进程

第一阶段: 前后端混合模式(1995-2005)

这个阶段前端代码包含在后端代码中,浏览器不会请求前端资源,而是请求到后端的控制器中,然后通过服务端渲染的方式把页面资源返回给浏览器,Javascript 仅仅实现页面的交互逻辑。

第二阶段: 前后端分离(2005-2013)

这个阶段借助 ajax 技术实现了前后端分离,浏览器会直接请求前端资源,前端通过 ajax 向后台请求数据,后台通过api的形式给前端调用。从此前端脱离后端独立存在,这个时候开始出现前端工程师这一职业。

第三阶段: 模块化开发(2013-2014)

这个阶段借助于 npm 的模块化管理,可以上传和下载模块,从而实现了模块化开发方式。同时借助 webpack 的打包功能把 js, css, img等资源统一打包成一个 bundle.js 文件,浏览器加载的时候就加载一个或几个资源,极大了提升了网页性能。

第四阶段: 模块化 + MVVM(2014年-至今)

React/Vue 等前端框架的出现,我们不会再去写 html, css 等代码了,而是写 vue 或者 jsx 的代码,最后利用 webpack 的 loader 工具把这些文件统一打包浏览器能识别的 js 文件。

7.2、前端模块化的演变

参考:前端模块化的演变 - 掘金 (juejin.cn)

模块是通过模块化拆分以后,我们能更清楚看到整个业务的逻辑,即各个模块是做什么的。

模块化

在模块里,将不同的功能封装成不同的函数,但是由于全局 function 模式容易产生命名冲突问题开始,引出了全局 namespace 模式。把模块写成一个对象,把方法与变量封装在一个特定的对象里面,这样就避免了命名的冲突。但是当外部引用模块(拷贝)时,可能会修改对象里面的变量。

那么让模块内的变量私有化?改进

紧接着利用函数作用域let和闭包的特性实现函数,但是变量是不会被释放,引发内存问题。进一步改进,通过函数作用域let与自(立即)执行函数创建闭包来实现。但是,这种模式无法解决模块间相互依赖的问题,因为模块写出来肯定要相互引用的。尽管通过把自定义模块挂载到 window 下并作为参数传入来达到模块间引用,但是这种方式无法支持大规模模块的开发。

针对以上问题,nodejs 采用了 commonjs 模块规范,而 es6 则采用了 esModule 模块规范。下一节将学习 commonjs

7.3、Commonjs

关键字:module、require、exports、module.exports

参考:CommonJS规范详解 - 掘金 (juejin.cn)

Commonjs 标准的提出弥补 Javascript 对于模块化没有统一标准的缺陷。nodejs 借鉴了 Commonjs 规范,实现了良好的模块化管理。Commonjs 代码只能在 nodejs 环境中运行,是不能在浏览器中运行的,如果要想在浏览器环境下使用 Commonjs 模块的代码,那就需要通过 Browserify 编译后才能在浏览器中使用。

  • 每个文件就是一个模块,称之为 module,所有代码运行在模块作用域中,不会污染全局作用域;
  • nodejs 中的 Commonjs 模块加载采用同步加载方式,模块的加载顺序,按照其在代码中出现的顺序进行加载;
  • 模块可以加载多次,第一次加载时会运行模块,模块输出结果会被缓存,再次加载时,会从缓存结果中读取输出模块;
  • 通过 require 加载模块,通过 exports (只能导出对象)或 module.exports 输出模块;

module.exports,exports,require 三者是如何工作的?

module: 记录当前模块信息;require: 引入模块的方法;exports: 当前模块导出的属性;

node 在编译的过程中对 js 的代码块进行首尾包装,require ,exports ,module 是通过形参的方式传递到包装函数中的。在模块加载的时候,会通过 runInThisContext执行 modulefunction(包装函数) ,其传入require,exports,module 等参数,最终 nodejs 文件就这么执行了。解决了变量私有化的问题;

runInThisContext(modulefunction)(module, exports, require, __filename, __dirname)
require加载文件类型,require() 方法先查缓存,缓存没有会创建一个 module 对象,缓存到 Module 上,然后执行文件,最后返回 module.exports 对象。require() 会将路径转换成真实路径,并以真实路径作为索引,将编译后的结果缓存起来,第二次加载的时候会更快。

Node 定义了一个构造函数 Module,所有的模块都是 Module 的实例。在 Module 的原型有一个 require 方法,require 方法调用了 Module._load 方法

自定义模块处理

自定义模块,一般指的是非核心的模块,它可能是一个文件或者一个包,它的查找会遵循以下原则:

  • 在当前目录下的 node_modules 目录查找。
  • 如果没有,在父级目录的 node_modules 查找,如果没有在父级目录的父级目录的 node_modules 中查找。
  • 沿着路径向上递归,直到根目录下的 node_modules 目录。
  • 在查找过程中,会找 package.json 下 main 属性指向的文件,如果没有 package.json,在 node 环境下会以此查找 index.js,index.json,index.node。

7.4、ESModule规范

参考:ESModule规范详解 - 掘金 (juejin.cn)

ESModule 规范

  • ESModule 设计理念是希望在编译时就确定模块依赖关系即输入输出(在编译阶段把import进行了提升,类似于var的变量提升,所以首先执行import语句里面代码。)
  • Commonjs 和 AMD 必须在运行时才能确定依赖和输入、输出
  • ESModule 通过 import 加载模块,通过 export 输出模块,默认导出 export default

 Commonjs 和 ESModule 的区别:

  • Commonjs 的输出是值的拷贝,ESModule 的输出是值的引用。
  • Commonjs 是运行时加载,只有在运行结束后才能确定输入和输出,ESModule 在编译的时候就能明确的知道输入和输出,它是一个确定的结果。

8、Webpack

为什么会出现webpack???

Webpack 究竟解决了什么问题? - 知乎 (zhihu.com)

8.1、webpack出现的原因

但是随着模块化思想的引入,我们的前端应用又会产生了一些新的问题,比如:

  • 首先,我们所使用的 ES Modules 模块系统本身就存在环境兼容问题。尽管现如今主流浏览器的最新版本都支持这一特性,但是目前还无法保证用户的浏览器使用情况。所以我们还需要解决兼容问题。
  • 其次,模块化的方式划分出来的模块文件过多,而前端应用又运行在浏览器中,每一个文件都需要单独从服务器请求回来。零散的模块文件必然会导致浏览器的频繁发送网络请求,影响应用的工作效率。
  • 最后,谈一下在实现 JS 模块化的基础上的发散。随着应用日益复杂,在前端应用开发过程中不仅仅只有 JavaScript 代码需要模块化,HTML 和 CSS 这些资源文件也会面临需要被模块化的问题。而且从宏观角度来看,这些文件也都应该看作前端应用中的一个模块,只不过这些模块的种类和用途跟 JavaScript 不同。

8.2、webpack解决了什么?

Webpack 最初的目标就是实现前端项目的模块化,也就是说它所解决的问题是如何在前端项目中更高效地管理和维护项目中的每一个资源

第一,它需要具备编译代码的能力,减少网络请求。也就是将我们开发阶段编写的那些包含新特性的代码转换为能够兼容大多数环境的代码,解决我们所面临的环境兼容问题。

第二,能够将散落的模块再打包到一起,这样就解决了浏览器频繁请求模块文件的问题。这里需要注意,只是在开发阶段才需要模块化的文件划分,因为它能够帮我们更好地组织代码,到了实际运行阶段,这种划分就没有必要了

第三,它需要支持不同种类的前端模块类型,也就是说可以将开发过程中涉及的样式、图片、字体等所有资源文件都作为模块使用,这样我们就拥有了一个统一的模块化方案,所有资源文件的加载都可以通过代码控制,与业务代码统一维护,更为合理

8.3、前端模块打包工具webpack

参考:如何使用 Webpack 实现模块化打包? - 知乎 (zhihu.com)

webpack 是 Webpack 的核心模块,webpack-cli 是 Webpack 的 CLI 程序,用来在命令行中调用 Webpack。webpack-cli 所提供的 CLI 程序就会出现在 node_modules/.bin 目录当中,我们可以通过 npx 快速找到 CLI 并运行它。npx webpack 会自动从 src/index.js 文件开始打包,然后根据代码中的模块导入操作,自动将所有用到的模块代码打包到一起。之后项目的根目录下多出了一个 dist 目录,我们的打包结果就存放在这个目录下的 main.js 文件中。

8.3.1、配置 Webpack 的打包过程

在项目的根目录下添加一个 webpack.config.js,webpack.config.js 是一个运行在 Node.js 环境中的 JS 文件,也就是说我们需要按照 CommonJS 的方式编写代码,这个文件可以导出一个对象,我们可以通过所导出对象的属性完成相应的配置选项。

// ./webpack.config.js
import { Configuration } from 'webpack'  //代码提示
/**
 * @type {Configuration}
 */
const config = {
  entry: './src/index.js',
//resolve 配置是帮助 Webpack 查找依赖模块的,通过 resolve 的配置,
//可以帮助 Webpack 快速查找依赖,也可以替换对应的依赖
  output: {
    filename: 'bundle.js'
    path: path.join(__dirname, 'output')
  },
//resolve.extensions是帮助 Webpack 解析扩展名的配置
 resolve: {
     extensions: ['.js', '.json', '.css'],
     alias: {
            src: path.resolve(__dirname, 'src'),
            '@lib': path.resolve(__dirname, 'src/lib')
        }
  },
//
 mode: 'development',
}
module.exports = config

Webpack (Webpack 本身只会识别js文件)可以在打包过程中通过 Loader 机制对其实现编译转换,然后再进行打包。 plugins 进行功能的扩展,可以用于执行范围更广的任务。

plugins参考:详解webpack plugin的原理及编写一个plugin - 掘金 (juejin.cn)

当 Webpack 内部进行插件挂载时会执行 apply 函数。我们可以在 apply 方法中订阅各种生命周期钩子,当到达对应的时间点时就会执行。

  • Entry:入口,Webpack 执行构建的第一步将从 Entry 开始,告诉webpack要使用哪个模块作为构建项目的入口,默认为./src/index.js。
  • output :出口,告诉webpack在哪里输出它打包好的代码以及如何命名,默认为./dist
  • Module:模块,在 Webpack 里一切皆模块,一个模块对应着一个文件。Webpack 会从配置的 Entry 开始递归找出所有依赖的模块。
  • Chunk:代码块,一个 Chunk 由多个模块组合而成,用于代码合并Tree Shaking与分割Code Splitting。
  • Loader:模块转换器,用于把模块原内容按照需求转换成新内容。.css 相关的文件:安装 style-loader 和 css-loader。.less 相关的文件:安装 style-loader 、css-loader和less-loader。.scss 相关的文件:安装 style-loader 、css-loader和sass-loader。
  • Plugin:扩展插件,在 Webpack 构建流程中的特定时机会广播出对应的事件,插件可以监听这些事件的发生,在特定时机做对应的事情。

8.3.2、webpack 构建流程

参考:webpack打包原理 ? 看完这篇你就懂了 ! - 知乎 (zhihu.com)
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程 :

  1. 初始化参数:从配置文件和 Shell 语句中读取与合并参数,得出最终的参数。
  2. 开始编译:用上一步得到的参数初始化 Compiler 对象,加载所有配置的插件,执行对象的 run 方法开始执行编译。
  3. 确定入口:根据配置中的 entry 找出所有的入口文件。
  4. 编译模块:从入口文件出发,调用所有配置的 Loader 对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
  5. 完成模块编译:在经过第 4 步使用 Loader 翻译完所有模块后,得到了每个模块被翻译后的最终内容以及它们之间的依赖关系。
  6. 输出资源:根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表,这步是可以修改输出内容的最后机会。
  7. 输出完成:在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。

在以上过程中,Webpack 会在特定的时间点广播出特定的事件,插件在监听到感兴趣的事件后会执行特定的逻辑,并且插件可以调用 Webpack 提供的 API 改变 Webpack 的运行结果。

8.4、Webpack提供的基本功能有哪些

  • 文件优化:压缩 JavaScript、CSS、html 代码,压缩合并图片等
  • 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等等
  • 模块合并:在采用模块化的项目有很多模块和文件,需要构建功能把模块分类合并成一个文件
  • 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载

8.5、为什么出现vite?

参考:前端新玩具:Vite 及相关思考 - 知乎 (zhihu.com)

1、但是像 Webpack 这类工具的做法是将所有模块提前编译、打包进 bundle 里,换句话说,不管模块是否会被执行,都要被编译和打包到 bundle 里。随着项目越来越大打包后的 bundle 也越来越大,打包的速度自然也就越来越慢。

Vite 利用现代浏览器原生支持 ESM 特性,省略了对模块的打包。对于需要编译的文件,Vite 采用的是另外一种模式:即时编译。也就是说,只有具体去请求某个文件时才会编译这个文件。所以,这种「即时编译」的好处主要体现在:按需编译。这种方式可以避免冗余的代码和性能瓶颈

2.Vite 只需要立即编译当前所修改的文件即可,所以响应速度非常快。

而 Webpack 修改某个文件过后,会自动以这个文件为入口重写 build 一次,所有的涉及到的依赖也都会被加载一遍,所以反应速度会慢很多。

3.插件系统 Vite 的插件系统更为简单,只需要编写一个普通的 JavaScript 模块即可。而 Webpack 的插件系统设计更为复杂,需要了解其内部机制才能编写高质量的插件。生态环境 Webpack 的生态环境非常丰富,有大量的社区支持和第三方插件可以使用。而 Vite 相对较新,在生态环境上还不如 Webpack 成熟。

$ yarn create vite-app <project-name>
$ cd <project-name>
$ yarn
$ yarn dev

9、npm

9.1 ESModule模块化管理出现问题

现在各大主流浏览器都支持了 ESModule,因此可以直接在script标签中引入ESModule模块化的文件,只需要加上type = module即可。<script type="module" src=""></script>
但是直接在浏览器中使用 ESModule 有很多局限。

必须引入所有文件:不能只引入入口文件,还需要把入口文件引入的依赖都要引入。入口文件 entry.js 引入了两个依赖文件api.js 和 sum.js ,当在index.html 引入entry.js 的时候,也需要把依赖文件 api.js 和 sum.js 也引入。ESModule 不像 Commonjs 那样只需要引入入口文件即可。当模块文件变多的时候,而且模块是分散在各个目录当中,我们就非常不好管理。另外,在大型项目中,每次都要加载这么多文件,导致性能非常差,所以这种开发模式无法满足需求。

必须完整引入文件地址

// 错误
import handle from './handle'
// 正确 后缀不能省略
import handle from './handle.js'

9.2、npm

npm是一个 NodeJS 包管理和分发工具。Node.js 中的第三方模块又叫做包。包是由第三方个人或团队开发出来的,免费供所有人使用。npm 解决的核心问题是模块管理的问题。

初步思路:

  • 集中管理所有的模块,所有的模块都上传到仓库(registry)
  • 模块内创建 package.json 标注模块的基本信息
  • 通过 npm publish 发布模块,上传到registry
  • 通过 npm install 安装模块,模块安装到node_modules目录

9.2.1、package.json

每个 JavaScript 项目(无论是 Node.js 还是浏览器应用程序)都可以被当作 npm 软件包,并且通过  package.json  来描述项目和软件包信息。

  • name:JavaScript 项目或库的名称。
  • version:项目的版本。
  • description:项目的描述。
  • scripts:运行项目的脚本
  •  dependencies:生产环境依赖
  • devDependencies:开发环境依赖

带有  --save  或  --save-dev  标志的  npm install  命令安装。 分别用于生产(echarts、element-ui)和开发/测试环境(比如webpack、babel)。^:表示最新的次版本,例如, ^1.0.4  可能会安装主版本系列  1  的最新次版本 1.3.0

:表示最新的补丁程序版本,与  ^  类似, 〜1.0.4  可能会安装次版本系列 1.0  的最新次版本1.0.7

给定一个版本号:主版本号.次版本号.补丁版本号, 以下这三种情况需要增加相应的版本号:

  • 主版本号: 当API发生改变,并与之前的版本不兼容的时候
  • 次版本号: 当增加了功能,但是向后兼容的时候
  • 补丁版本号: 当做了向后兼容的缺陷修复的时候

npm install XXX -g 表示全局安装,安装一次过后,你就可以在其他直接用啦。

9.2.2、package-lock.json

该文件描述了 npm JavaScript 项目中使用的依赖项的确切版本。

9.2.3、npm 的局限

npm2 只能解决模块的高效管理和获取问题,无法解决性能加载的问题制约其广泛使用的因素就是性能问题

参考:一文看懂npm、yarn、pnpm之间的区别 - 知乎 (zhihu.com)

  1. 安装版本不确定,理论上,次版本号的变化并不会影响向后兼容性。因此,安装最新版的依赖库应该是能正常工作的,而且能引入最新版本以后的重要错误和安全方面的修复。但是,另一方面,即使不同的开发人员使用了相同的package.json文件,在他们自己的机器上也可能会安装同一个库的不同种版本,这样就会存在潜在的难以调试的错误
  2. 大多数npm库都严重依赖于其他npm库,这会导致嵌套依赖关系(不是扁平化),并增加无法匹配相应版本的几率。windows 的文件路径最长是 260 多个字符,这样嵌套是会超过 windows 路径的长度限制的。
  3. 每个依赖在其模块的node.module下,同样的依赖不会复用,会复制很多次,会占据比较大的磁盘空间。

npm 3采用了扁平依赖关系树来解决这个问题,但是也出现了问题

  1. 耗时:npm必须首先遍历所有的项目依赖关系,然后再决定如何生成扁平的node_modules目录结构。npm必须为所有使用到的模块构建一个完整的依赖关系树,这是一个耗时的操作,这是npm下载慢的主要原因吧。
  2. 重复安装:npm 3.x 版本并未完全解决老版本的模块冗余问题,甚至还会带来新的问题。包的安装和更新可能会产生冲突或版本不一致的问题。如果同一个包的多个版本在项目中被依赖时,多个版本的包只能有一个被提升上来,其余版本的包会嵌套安装到各自的依赖当中,至于哪个版本的包被提升,依赖于包的安装顺序!当包有多个版本,会被重复安装多次。
  3. 幽灵依赖:扁平化之后,又引入了新的问题幽灵依赖,解释起来很简单,即某个包没有在package.json 被依赖,但是用户却能够引用到这个包。
  4. 依赖版本更新:假设有依赖A: ^2.4.8。团队第一个安装的时候A版本是2.4.8,依赖在npm install之后发布了新版本,第二个人再安装时A版本可能已经是2.5.1了,就会存了版本不同导致的兼容问题

为了解决上面的问题及npm v3的问题,在npm v5后每次npm install都会生成一个package-lock.json。当npm install执行时,如果package.json中的版本与package-lock.json兼容,会根据package-lock.json中的版本下载;如果不兼容则会根据package.json中的版本更新package-lock.json。在有了package-lock.json以后,可以保证团队开发中每个成员安装的依赖版本是一致的,生成的node_modules树是唯一的。在安装时npm会比较node_modules已有的包和package-lock.json,如果重复的话就跳过安装,优化安装过程。

npm v5之后,提供了缓存设置,~/.npm/_cacache 中存的是一些二进制文件,以及对应的索引。npm install 时,有缓存的话,会通过 pacote 把对应的二进制文件解压到相应的 node_modules 下面。(不是很懂,哈哈哈哈)

9.4、yarn

为了解决当时npm在一致性、安全、性能等方面的问题。yarn为了解决一致性的问题,率先增加了lockfiles--yarn.lock(npm v5后来也吸纳了lock机制,即后面的package-lock.json)。

优点1、安装速度更快,通过并行安装和缓存机制优化了性能。

  • 速度快 。速度快主要来自以下两个方面:
  1. 并行安装:无论 npm 还是 Yarn 在执行包的安装时,都会执行一系列任务。npm 是按照队列执行每个 package,也就是说必须要等到当前 package 安装完成之后,才能继续后面的安装。而 Yarn 是同步执行所有任务,提高了性能。
  2. 离线模式:如果之前已经安装过一个软件包,用Yarn再次安装时之间从缓存中获取,就不用像npm那样再从网络下载了。

优点2、锁定依赖版本,确保在不同环境中安装相同的包版本。
缺点:幽灵依赖
和 npm 一样的是,yarn 也存在幽灵依赖的问题。这是因为 yarn 在依赖管理方式上采用的也是扁平化的方式。

9.5、pnpm

参考:npm、yarn、pnpm的区别 - 掘金 (juejin.cn)

1、使用软连接与硬链接,节省磁盘空间,因为共享相同的依赖项只占用一次磁盘空间。
安装速度更快,因为不需要重复安装相同的依赖项。pnpm 通过软链接的方式将依赖关系连接到项目。只在全局仓库保存一份 npm 包的内容,其余的地方都 link 过去.npm使用硬链接这样不会有复制多次的磁盘空间浪费,而且也不会有路径过长的问题。
2、更严格的依赖关系解析
pnpm 不会让包访问那些没有被明确列为其依赖的包。

软链接(软连接):是一个指向另一个文件的路径的特殊文件。软链接是对实际文件的引用,它不包含实际内容,而是只包含指向实际文件的地址。当目标文件被删除后,软链接将不再有效可以创建不同文件系统之间的软链接,实现跨文件系统的链接。(例如快捷键)

硬链接(硬连接):是指与一个文件关联的两个或多个目录条目。硬链接只有在与原始文件在同一个文件系统上时才有效。硬链接的创建方式是复制索引节点同时与原文件共享一片空间),因此,硬链接与原始文件有相同的属性。硬链接本身不包含源文件的路径,只有对应的索引节点信息。删除源文件不影响硬链接,因为硬链接不依赖于源文件的路径。硬链接实际上允许一个文件在不同目录中具有不同的文件名。

缺点

1、兼容性问题
由于 pnpm 的依赖关系解析方式更严格,某些包可能会在 pnpm 下运行不正常,尽管在 npm 或 yarn 下可以正常运行。

2、手动维护依赖
pnpm 为了避免幽灵依赖的存在,要求所有的依赖都需要在 pakeage.json 文件中显式声明,随着依赖项的增多,package.json 文件的维护难度也会增加

10、nodejs

Node.js是一个服务端的JavaScript运行环境,通过Node.js可以实现用JavaScript写独立程序。可以读写本地系统文件这种操作。

提到了那么多次nodejs,那么nodejs是何方神圣?他是如何运作的??参考【前端进阶-nodejs】

11、vue的出现

jQuery操作真实DOM,大量重排,消耗性能高。vue是一门自底向上、渐进式的用于构建用户界面的渐进式的JavaScript框架。基于虚拟DOM操作,提高性能。

vue的声明式、响应式的数据绑定、组件化的开发、虚拟DOM

单页面运用、组件化,数据驱动视图,带来了一系列框架变化。

MVC模式

我们可以理解为网页上的一个个诸如Div的DOM元素为“View视图”,而改变DOM元素属性或值的数据来源方(如Ajax从服务器获取数据),可以理解为“model数据模型”,而使用诸如Jquery之类的脚本去实现页面动态交互,通过事件机制来响应用户的交互操作(例如用户点击按钮之后弹出一个对话框,或修改了标签中的值),即为control层。

  • View传送指令到Controller
  • Controller完成业务逻辑后,要求Model改变状态
  • Model将新的数据发送到View,用户得到反馈

MVVM模式以及解决的问题

而Vue的最强大之处就在于解决了上面control层过重的问题。

ViewModel是干嘛的?它实现了View和Model的自动同步,也就是当Model的属性改变时,我们不用再自己手动操作Dom元素,来改变View的显示,而是改变属性后该属性对应View层显示会自动改变。我们可以理解为我们只需要调整元素属性,剩下的DOM操作由框架来帮我们搞。

实现了双向数据绑定

Vue-CLi

它是一个vue.js的脚手架工具。说白了就是一个自动帮你生成好项目目录,配置好Webpack,以及各种依赖包的工具,它可以通过npm install vue-cli -g的方式安装

Vuex和Vue-router

Vuex是vue的一个状态管理器。用于集中管理一个单页应用程序中的各种状态。

Vue-route是vue的一个前端路由器,这个路由器不是我们上网用的路由器,而是一个管理请求入口和页面映射关系的东西。它可以实现对页面局部进行无刷新的替换,让用户感觉就像切换到了网页一样。

参考vue全家桶

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值