1.工程化的概念
就是通过各种工具和技术,提升前端的开发效率。包括开发,编译,部署等各个阶段。
2.模块化的思想,commonjs,esmodule
- 早期的模块化,命名空间,自执行函数,
CommonJS 它是 Node.js 中所遵循的模块规范,
一个文件就代表了一个模块。每个文件都有单独的作用域,通过 module.exports 导出成员,通过 require 函数载入模块。是以同步的方式加载模块,所以运行效率较低
前端模块,浏览器环境的异步加载模块规范
- AMD()规范,requirejs,依赖前置,所有的 require 都会提前执行
- CMD 规范,seajs,就近依赖或者依赖后置,下载完之后,并不执行加载,回调函数中遇到 require 时才执行
- esmodule,是 ECMAScript 2015(es6)中制定的标准,通过 export 导出,import 导入。
3.webpack 的原理和构建流程
webpack 是现代 js 应用程序的静态模块打包工具,他从入口文件出发,对模块进行编辑,根据依赖关系递归进行编译,构建出一个依赖树。生成一个或者多个文件 bundle.
解决了什么问题
- 模块打包: 可以将不同模块的文件打包整合在一起,并且保证它们之间的引用正确,执行有序。保证项目结构的清晰和可读性。
- 编译兼容: 可以对代码做 polyfill ,还可以编译转换 .less, .vue, .jsx 文件。具备代码编译功能,将我们开发阶段编写的代码转换为大多数浏览器环境能运行的代码
- 能力拓展:通过 webpack 的 Plugin 机制,可以进一步实现诸如按需加载,代码压缩等一系列功能,帮助我们进一步提高自动化程度,工程效率以及打包输出的质量。
构建流程(简述)
- 初始化:读取和合并配置,加载 plugin,实例化 complier
- 编译:从入口文件出发,针对每个模块,调用模块对应的 loader 进行编译,然后根据依赖关系递归编译模块所依赖的模块。
- 输出:将编译后的模块组合成一个个的 chunk,将 chunk 转换成 bundle 文件。根据配置输出到文件列表中
稍微详细版本
Webpack 的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
- 初始化:读取和合并配置实例化 complier,加载 plugin 配置插件,执行对象的 run 方法开始执行编译
- 编译:从入口文件出发,针对每个模块,调用模块对应的 loader 进行编译,然后根据依赖关系递归编译模块所依赖的模块。得到每个模块被翻译后的最终内容以及它们之间的依赖关系
- 输出:根据入口和模块之间的依赖关系,将编译后的模块组合成一个个的 chunk,将 chunk 转换成 bundle 文件。根据配置确定输出的路径和文件名,输出到文件列表中
4.loader 和 plugin 的本质和区别
loader
- loader(直译器):本质上是一个函数,在函数中对导入的资源进行处理,然后到处一个为函数的 javascript 模块
- 为什么需要 loader,因为 webpack 把一切视为模块,而本身只能识别 js 和 json 文件。所以需要 loader 把其他类型的文件转换为 wepack 能识别解析的文件。
plugin
- webpack 在运行的生命周期中会广播出许多事件,plugin 通过监听这些事件,就可以在特定的阶段执行自己的插件任务,从而实现自己想要的功能改变输出结果。
区别
- loader 是解析文件,plugin 是拓展功能
- loader 是在 module.rules 作为模块的解析规则,类型为数组。配置什么类型的文件需要什么样的 loader 在什么范围和不包括什么范围进行配置,plugin 在 plugins 里面单独配置,类型为数组,每一项是一个 Plugin 的实例,
// 获取webpack配置的options,写loader的固定套路第一步
cosnt { getOptions } = require('loader-utils')
module.exports = function(content, map, meta){
const options = getOptions(this) || {}
cosnt code = JSON.stringify(content);
const esModule = typeof options.esModule !== 'undefined' ? options.esModule : true
// 返回内容
return `${esModule ? 'export default' : 'module.exports ='} ${json};`;
}
5.webpack 优化
开发优化
- 1.preload 插件去除 map 等不需要提前加载的 js 文件
- 2.删除 prefetch,Prefetch 链接将会消耗带宽。
- 3.因为开发环境的时候使用路由懒加载热更新变慢, 使用 babel-plugin-dynamic-import-node 使开发环境不需要懒加载
编译优化
- 使用高版本的 Webpack 和 Node.js
- 多进程/多实例构建:HappyPack(不维护了)、thread-loader
- external,dll 对不常改变的模块使用 cdn,或者打包成 dll 包引入,减少编译的时间。
- dllPlugin,DllReferencePlugin
- 通过 include 和 exclude 来限制 loader 编译返回
- 按需加载框架里面都有了
- 充分利用缓存提升二次构建速度:babel-loader 开启缓存
体积优化
- splitChunks 进行分包
- 最小化入口 chunk
new CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
});
6.babel 是什么
babel 就是一个 Javascript Transpiler。
用途
我们平时主要用 babel 做 3 件事情
- 转译 exNext、typescript、flow 等到目标环境支持的 js
- 一些特定用途的代码转换。比如自动埋点
- 代码的静态分析
babel 是如何把 es6 转成 es5 的
babel 的转译过程分为三个阶段:parsing(解析)、transforming(转化)、generating(生成)
- Parse: 通过 parser 把源码转成抽象语法树 (AST)
babel 将 es6+(指 es6 及以上版本)分为语法层和 api 层:语法层: let、const、class、箭头函数等,这些需要在构建时进行转译,是指在语法层面上的转译,(比如 class...将来会被转 译成 var function...) api 层:Promise、includes、map 等,这些是在全局或者 Object、Array 等的原型上新增的方法,它们可以由相应 es5 的方式重 新定义
- Transfrom: 遍历 AST, 调用各种 transform 插件对 AST 进行增删改
- generate: 把转换后的 AST 打印成目标代码,并生成 sourcemap
7.vite
是一个基于浏览器原生 ES 模块导入的开发服务器,在开发环境下,利用浏览器去解析 import,在服务器端按需编译返回,完全跳过了打包这个概念,服务器随启随用。同时不仅对 Vue 文件提供了支持,还支持热更新,而且热更新的速度不会随着模块增多而变慢。在生产环境下使用 Rollup 打包
- 1.Webpack、Rollup 这些构建工具在本地开发调试的时候,会提前把模块打包成浏览器可读取的 js bundle。
- 2.如路由懒加载等优化手段,但懒加载并不代表懒构建,Webpack 还是需要把你的异步路由用到的模块提前构建好。
-
- Vite 利用了浏览器原生 ES Module 的支持,在本地启动一个服务器,当浏览器读取这个 html 文件的时候,会在执行到 import 的时候才向服务端发送请求模块的请求。
- 4.vite 的服务端会通过内部的一些机制,将模块解析成浏览器可以执行的 js 文件返回到浏览器端。
- 5.这就保证了只用真正使用这个模块的时候,浏览器才会请求且解析加载,最大程度的做到了按需加载
- 6.依赖预编译,其实是 Vite 2.0 在为用户启动开发服务器之前,先用 esbuild 把检测到的依赖预先构建了一遍,全部打包成为一个传统的 js bundle
<div id="app"></div>
<script type="module">
import { createApp } from 'vue';
import Main from './Main.vue';
createApp(Main).mount('#app');
</script>
8.HMR(Hot Module Replacement)热更新又称热替换
当你对代码进行修改并保存后,webpack 将对代码重新打包,并将新的模块发送到浏览器端,浏览器通过新的模块替换老的模块,这样在不刷新浏览器的前提下就能够对应用进行更新。
- HMR 的核心就是客户端从服务端拉取更新后的文件。准确的说是 chunk diff(chunk 需要更新的部分),
- 实际上 WDS(webpack-dev-server)与浏览器之间维护了一个 websocket 进行通信。
- 当本地资源发生变化后, WDS 会向浏览器推送更新,并带上构建时的 hash,让浏览器与上一次的资源进行对比
- 客户端对比出差异后会向 WDS 发起 Ajax 请求来获取更改内容(文件列表、hash),也就是请求 update.json,
- 这样客户端就可以再借助这些信息继续向 WDS 发起 jsonp 请求获取该 chunk 的增量更新也就是请求 update.js。更新的 js 是存储在内存中的
其他
使用 webpack 开发时,你用过哪些可以提高效率的插件?
- webpack-dashboard:可以更友好的展示相关打包信息。
- webpack-merge:提取公共配置,减少重复配置代码