webpack 配置指南
- 搭建环境
1 | npm init -y # 生成 package.json |
1 | "scripts": { |
webpack 旧版本必须在 webpack.config.js 通过 entry 属性定义入口,现在默认为./src/index.js
新建./src/index.js
文件.执行命令后,dist/main.js
为默认输出.
- 生产和开发模式
webpack 分为 ‘development’ 或 ‘production’ 或 ‘none’.
production 可以开箱即用地进行各种优化。 包括压缩,作用域提升,tree-shaking 等
development 针对速度进行了优化,仅仅提供了一种不压缩的 bundle.
1 | "scripts": { |
- 默认覆盖 entry 和 output
webpack 支持 ES6, CommonJS, AMD 规范
1 | /** |
webpack.config.js 是 webpack 默认的配置文件名,在根目录下创建.we
1 | const path = require('path') |
path.resolve() 方法会把一个路径或路径片段的序列解析为一个绝对路径。__dirname
: 当前模块的文件夹路径。
执行 build 后会生成多个 bundle 文件,这跟 AMD 的引入方式有关,在实际写代码的时候,最好使用 ES6 和 CommonJS 的规范来写.
// CleanWebpackPlugin 插件可以自动删除 dist 旧文件在打包.
1 | npm install clean-webpack-plugin --save-dev |
1 | const { CleanWebpackPlugin } = require('clean-webpack-plugin') |
- babel 转译
webpack 使用 loader 进行转译.babel-loader 可以将 es6 转译为 es5.
我们使用 babel7 版本.
@babel/core
@babel/preset-env: 包含 ES6、7 等版本的语法转化规则
@babel/plugin-transform-runtime: 可以重复使用 Babel 注入的程序代码来节省代码,减小体积.避免 polyfill 污染全局变量,减小打包体积
@babel/polyfill: ES6 内置方法和函数转化垫片,所谓垫片也就是垫平不同浏览器或者不同环境下的差异
babel-loader: 负责 ES6 语法转化
1 | npm i @babel/core babel-loader @babel/preset-env @babel/plugin-transform-runtime --save-dev |
在项目根目录创建.babelrc
配置 babel
1 | { |
webpack 配置 loader.
1 | module: { |
在入口文件处全局引入import @babel/polyfill
,执行 build 命令打包.
全局引入 @babel/polyfill 的这种方式可能会导入代码中不需要的 polyfill,从而使打包体积更大.
更改 .babelrc,只转译我们使用到的.
1 | { |
注释入口文件全局引入的import @babel/polyfill
,执行 build 命令打包.打包体积明显变小.
.browserslistrc
用于在不同前端工具之间共享目标浏览器和 Node.js 版本的配置,或在 package 文件中加入
1 | "browserslist": [ |
- 代码切割 code splitting
1 | npm i lodash |
新建src/index.js
并添加 lodash 相关代码,更改 webpack 配置
1 | entry: { |
运行 build 打包我们会发现,业务代码和第三方框架一起被打包,业务代码更新频繁,而第三方框架不会变,这会造成资源浪费和低效率.
浏览器是有缓存的,如果文件没变动的话,就不用再去发送 http 请求,而是直接从缓存中取,这样在刷新页面或者第二次进入的时候可以加快网页加载的速度。
所以我们可以利用 webpack 的代码分割功能,将第三方代码与业务代码分开,这样及时业务变动,浏览器也可以读取第三方框架的缓存.
webpack4 使用 内置的 splitChunksPlugins 进行代码分割. all 表示分割所有代码, async 为默认值,表示分割异步代码.
1 | optimization: { |
执行 build 命令,发现文件为dist/**.bundle.js dist/vendors-***.js
,表示代码分割成功.
对分割名称进行更改.添加 cacheGroups 对象.
1 | optimization: { |
- 懒加载,Prefetching 预加载
懒加载就是通过 import 去异步的加载一个模块,类似于 vue-router 中的懒加载component: () => import('***.vue')
.
懒加载并不是 webpack 里的概念,而是 ES6 中的 import 语法,webpack 只是能够识别 import 语法,能进行代码分割而已。
页面加载时,异步的代码不会执行但是确下载下来了,浪费了页面性能,我们希望 webpack 可以把异步代码放到一个模块中.当我们需要时,才会去加载这些代码.这也是为什么 webpack 的 splitChunks 中的 chunks 默认是 async,异步的.
问题又来了,如果异步的代码模块较大,而当我们需要加载时就要等待,体验不好.webpack 的 Prefetching/Preloading 预加载功能可以解决这个问题.它会在网络带宽空闲的时候预先帮我们加载.
webpackPrefetch: 等待核心代码加载完之后,有空闲之后再去加载
webpackPreload: 和核心的代码并行加载,不推荐
使用方式import(/* webpackPrefetch: true */, '......js').then...
- 自动生成 html 文件并自动引入相关资源
1 | npm i html-webpack-plugin html-loader --save-dev |
更改 webpack 配置
1 | const HtmlWebpackPlugin = require('html-webpack-plugin') |
由于使用了 title, 在 index.html 中加<title><%= htmlWebpackPlugin.options.title %></title>
,执行命令,dist 中生成 index 的 html 文件.我们发现其中 js 引入为绝对路径,需要更改为相对路径.找到 output 输出配置,更改 publicPath 公共路径,修改为 ./
- 处理 css 文件
这次我们需要用到 css-loader,style-loader 等 loader.
css-loader:负责解析 CSS 代码,主要是为了处理 CSS 中的依赖,例如 @import 和 url()
等引用外部文件的声明.
style-loader 会将 css-loader 解析的结果转变成 JS 代码,运行时动态插入 style 标签来让 CSS 代码生效。
1 | npm i css-loader style-loader --save-dev |
更改配置文件
1 | rules: [ |
可以发现在 style 标签中出现了相关 css,如果要单独打包成文件,需要 mini-css-extract-plugin 插件
1 | npm i mini-css-extract-plugin --save-dev |
1 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') |
执行 build,生成了单独的 css 文件,但是并没有压缩,引入 optimize-css-assets-webpack-plugin 插件来实现 css 压缩.
webpack5 使用 css-minimizer-webpack-plugin 插件.
1 | npm install css-minimizer-webpack-plugin --save-dev |
更改配置
1 | const CssMinimizerPlugin = require('css-minimizer-webpack-plugin') |
处理 scss 或 less.
1 | npm i node-sass sass-loader --save-dev |
更改 loader:
1 | { |
根据 webpack 规则:放在最后的 loader 首先被执行,从上往下写的话是下面先执行,从左往右写的话是右边先执行。
为 css 加上浏览器前缀.
1 | npm install postcss-loader autoprefixer --save-dev |
新建 postcss.config.js ,并修改 loader 配置
1 | module.exports = { |
- Tree Shaking
字面意思是摇树,项目中没有使用的代码会在打包的时候丢掉。JS 的 Tree Shaking 依赖的是 ES6 的模块系统 import 和 export.
对于经常使用的第三方库,要安装 ES 写法的版本,比如lodash-es
webpack5 可以通过 package.json 的 “sideEffects”(意为副作用) 属性,通知 webpack 安全的删除未使用的 export.而 optimization.usedExports: true,
依赖于 terser 去检测语句中的副作用.
css tree shaking: 略.
- 图片字体资源的处理
1 | npm install url-loader file-loader --save-dev |
更改配置
1 | rules: [ |
- 处理第三方 js 库
1 | resolve: { |
- webpack-dev-server
1 | npm i webpack-dev-server --save-dev |
1 | "scripts": { |
配置
1 | mode: 'development', // 开发模式 |
- DllPlugin 加快打包速度
新建 webpack.dll.js 文件,
1 | const path = require('path') |
json 文件中新增命令
1 | "build:dll": "webpack --config ./build/webpack.dll.js" |
在 webpack 配置中添加
1 | const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin') // dll自动引入 |
多页面打包
在 webpack.base.conf.js 中配置 entry,配置两个入口.然后多次调用 new HtmlWebpackPlugin({})插件.或写个函数自动生成配置.其他
- webpack-merge 用来合并 webpack 配置
- workbox-webpack-plugin 用来配置 PWA
- ts-loader 以及 ts.config.js 用来配置 ts
- eslint-loader 以及 .eslintrc.js
- 编写 loader
新建一个 js 文件
1 | module.exports = function (source) { |
source 参数就是我们的源代码,这里是将源码中的 world 替换成我们自定义的 字符串.
修改 webpack 配置添加自定义 loader.
1 | rules: [ |
- 编写 plugin
新建 xxx-webpack-plugin 的 js 文件,plugin 是一个类, 调用的时候要 new 这个类生成实例.
1 | class CopyrightWebpackPlugin { |
调用:
1 | const CopyrightWebpackPlugin = require('./plugins/copyright-webpack-plugin') |
- webpack 原理
初始化阶段: - 初始化参数: 从配置文件或 shell 获取参数 - 创建编译器对象: 根据参数创建 compile 对象 - 初始化编译环境: 注入内置插件,rule 规则,加载的插件. - 开始编译: 执行 compile 的 run 方法 - 确定入口: 根据配置的 entry 找到入口,将入口文件转换为 dependence 对象
构建阶段: - 编译模块(make): 根据 entry 对应的 dependence 创建 module 对象,调用 loader 将模块转译为标准 JS 内容,调用 JS 解释器将内容转换为 AST 对象,从中找出该模块依赖的模块,再 递归 本步骤直到所有入口依赖的文件都经过了本步骤的处理. - 完成编译: 得到了每个模块被翻译后的内容以及它们之间的 依赖关系图
生成阶段: - 输出资源: 根据入口和模块之间的依赖关系,组装成一个个包含多个模块的 Chunk,再把每个 Chunk 转换成一个单独的文件加入到输出列表 - 写入文件系统(emitAssets): 在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统.