- WebPack 是一个模块打包工具,可以使用WebPack管理模块,并分析模块间的依赖关系,最终编绎输出模块为HTML、JavaScript、CSS以及各种静态文件(图片、字体等),让开发过程更加高效。
- 对于不同类型的资源,webpack有对应的模块加载器loader,比如说,
CSS
解析CSS的css-loader、style-loader,
解析less的less-loader,sass的sass-loader,
JS
解析将 TypeScript 转换成 JavaScript的ts-loader,
解析ES6为ES5的babel-loader,
解析JavaScript 代码规范的eslint-loader
Vue
解析.vue文件的vue-loader、
静态资源:音视频、文件、json
解析常用图片以及音视频资源的url-loader、
解析文件的file-loader,
解析 JSON 文件的json-loader,
2、webpack的基本功能(也就是各种loader的作用)
- 代码转换:TypeScript 编译成 JavaScript、ES6转ES5、SCSS 编译成 CSS 等等(各种loader)
- 代码语法检测:自动检测代码是否符合语法 (eslint-loader)
- 代码分割:打包代码时,可以将代码切割成不同的chunk(块),实现按需加载,降低了初始化时间,提升了首屏渲染效率
- 监测代码更新,自动编译,刷新页面:监听本地源代码的变化,自动构建,刷新浏览器(自动刷新)
- 自动发布:更新完代码后,自动构建出线上发布代码并传输给发布系统(没用过)。
- 文件压缩:压缩 JavaScript、CSS、HTML 代码,缩小文件体积(比如说,打包后的js、css、html文件会去掉代码之间的空隔,紧凑显示)
- 模块合并:由于模块化的开发,一个页面可能会由多个模块组成,所以编译时需要把各个模块合并成一个文件(模块化开发引出的功能)
3、webpack的两大特色
- 自动分割(code splitting) code splitting,即打包代码时,可以将代码切割成不同的chunk(块),实现按需加载,降低了初始化时间,提升了首屏渲染效率
- loader 加载器可以处理各种类型的静态文件,并且支持串联操作
4、webpack配制说明
Webpack运行在node.js环境下,它的配置文件webpack.config.js遵循CommonJS规范,最终export出一个json对象。
webpack.config.js基础配置说明:
- entry,指定了模块的入口,它让源文件加入构建流程中被webpack控制。
- output,配置输出文件的存放位置、文件名、文件基础路径publicPath。
- module,配置各种类型文件的解析规则,比如说.vue文件、.js文件。
- rosolve,配置alias(别名),或者定义寻找模块的规则。
- plugins,配置扩展插件,扩展webpack的更多功能。
- devServer,实现本地http服务等。
4、webpack打包流程
- 从入口(entry) 开始,递归转换入口文件所依赖的module
- 每找到一个module,就根据对应的loader去转换这个module
- 然后,再对当前module依赖的所有module进行转换,如果子module还有依赖的话,再转换,直至没有依赖
- 其次,以入口文件(entry)为单位进行分组,一个entry和其所有依赖的module被分到一个块(Chunk)。
- 最后,Webpack会把所有Chunk转换成文件输出,在整个流程中Webpack会在恰当的时机执行plugin里定义的扩展插件。
5、有哪些常见的Plugin(插件)
- html-webpack-plugin:根据模板页面生成打包的 html 页面
- clean-webpack-plugin: 目录清理
- copy-webpack-plugin: 拷贝文件
- uglifyjs-webpack-plugin:压缩代码(不支持 ES6 压缩)
- mini-css-extract-plugin: 分离样式文件,CSS 提取为独立文件,支持按需加载
6、说一说Loader和Plugin的区别
loader:解析器(modules---->rules配制)
plugin:webpack功能的一个扩展器(plugins中配制)
- Loader 本质就是一个函数,在该函数中对接收到的内容进行转换,返回转换后的结果。因为 Webpack 只认识 JavaScript,所以Loader 就成了翻译官,对其他类型的资源进行转译的预处理工作。
- Plugin 就是webpack插件,可以扩展 Webpack 的功能,在 Webpack 运行的生命周期中会发生许多事件,Plugin 可以监听这些事件,在合适的时机通过 Webpack 提供的 API 改变输出结果。
- Loader 在 module.rules 中配置,作为模块的解析规则,类型为数组。每一项都是一个 Object,内部包含了 test(类型文件)、loader、options (参数)等属性。
- Plugin 在 plugins 中单独配置,类型为数组,每一项是一个 Plugin 的实例,参数都通过构造函数传入。
7、Webpack 的热更新原理
- 是什么
Webpack 的热更新又称热替换(Hot Module Replacement),缩写为 HMR。 这个机制可以做到不用刷新浏览器而将新变更的模块替换掉旧的模块。 - 核心
热更新的核心就是客户端与服务端建立的websocket连接,服务端监听文件变化,编译后再推送给客户端告之其哪些地方改变了,最终客户端发送ajax请求,获取最新资源,使用文件系统替换修改的内容实现局部更新。
- 具体实现步骤
- 使用webpack-dev-server去启动本地服务,其主要使用了webpack、http server(express)、websocket。
- 服务端和客户端使用websocket实现长连接
- webpack监听源文件的变化,即文件改变时触发webpack的重新编译,编译完成后通过socket向客户端推送当前编译的hash戳3.1、每次编译都会生成hash值、已改动模块的json文件、已改动模块代码的js文件
3.2、编译完成后通过socket向客户端推送当前编译的hash戳 - 客户端接收到推送过来的hash戳,会和上一次对比4.1、一致则走缓存
4.2、不一致则通过ajax和jsonp向服务端获取最新资源
- 使用内存文件系统去替换有修改的内容实现局部刷新
8、如何优化 Webpack 的构建速度
- 使用高版本的 Webpack 和 Node.js
- 压缩代码
1). 通过uglifyjs-webpack-plugin, 压缩JS代码
2). 通过mini-css-extract-plugin,分离 CSS 代码到单独文件,
3). 通过css-loader的 minimize 选项开启 cssnano压缩 CSS
- 压缩图片:image-webpack-loader
- 多线程/多进程构建:thread-loader、HappyPack
- 缩小打包作用域
1). exclude/include (确定 loader 规则范围)
2). resolve.modules 指明第三方模块的绝对路径 (减少不必要的查找)
3). resolve.mainFields 只采用 main 字段作为入口文件描述字段 (减少搜索步骤,需要考虑到所有运行时依赖的第三方模块的入口文件描述字段)
4). resolve.extensions 尽可能减少后缀尝试的可能性
5). noParse 对完全不需要解析的库进行忽略 (不去解析但仍会打包到 bundle 中,注意被忽略掉的文件里不应该包含 import、require、define 等模块化语句)
6). ignorePlugin (完全排除模块)
7). 合理使用alias - 提取页面公共资源, 基础包分离
1). 使用html-webpack-externals-plugin,将基础包通过 CDN 引入,不打入 bundle 中。
2). 使用SplitChunksPlugin进行(公共脚本、基础包、页面公共文件)分离(Webpack4内置) ,替代了 CommonsChunkPlugin 插件。
- 充分利用缓存提升二次构建速度:
1). babel-loader 开启缓存
2). terser-webpack-plugin 开启缓存
3). 使用 cache-loader 或者 hard-source-webpack-plugin - Tree shaking打包过程中检测工程中没有引用过的模块并进行标记,在资源压缩时将它们从最终的bundle中去掉(只能对ES6 Modlue生效)开发中尽可能使用ES6 Module的模块,提高tree shaking效率 禁用 babel-loader 的模块依赖解析,否则Webpack 接收到的就都是转换过的 CommonJS 形式的模块,无法进行 tree-shaking使用PurifyCSS(不在维护) 或者 uncss 去除无用 CSS 代码purgecss-webpack-plugin 和 mini-css-extract-plugin配合使用(建议)
- Scope hoisting构建后的代码会存在大量闭包,造成体积增大,运行代码时创建的函数作用域变多,内存开销变大。Scope hoisting 将所有模块的代码按照引用顺序放在一个函数作用域里,然后适当的重命名一些变量以防止变量名冲突