引言
最近新项目过多,在新项目中每次使用 webpack 都是拷贝之前的项目的配置文件过来,改改直接使用,很多配置还是一知半解,一直想用心的从头配置一次 webpack,加深对 webpack 的理解,所以,有了本文,先献上以下内容github地址。
基本配置 webpack.base.config.js
首先,配置entry
1 | const base = { |
自 webpack4 起,webpack 提供了默认 entry,也就是我们上面使用的 './src/index'
,这里我们用数组包裹一下,方便动态增删,往下
配置 output:
1 | const base = { |
配置 resolve.extensions, require的时候省略文件后缀
1 | const base = { |
配置 devServer,开发环境 webpack-dev-server 配置使用
1 | const host = 'localhost'; |
根据不同的环境,我们需要对默认的 entry 进行处理,如下
1 | const CleanWebpackPlugin = require('clean-webpack-plugin'); |
添加图片、字体文件处理:
1 | const base = { |
production 生成环境对编译进行 optimization
1 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); |
splitChunks 配置的 chunks: 'all'
会改变html的引进的脚本,加了chunksHash后每次编译的结果会不一致,需要结合html-webpack-plugin 使用。
下面添加 plugins
1 | const ProgressBarPlugin = require('progress-bar-webpack-plugin'); |
导出配置
1 | module.exports = base; |
React 配置 webpack.react.config.js
webpack 的 react 配置,只要是针对 babel-loader 进行配置,首先声明一个 bable-loader:
1 | const path = require('path'); |
首先对preset进行理解,就是bable的一个套餐,里面包含了各种plugin
- 使用 babel-preset-react 让其解析jsx语法
- 使用 babel-preset-stage-0(stage中最高级的套餐),让其对ES6的语法进行解析
- 使用 babel-preset-env,让其针对配置,对其加入不同的 polyfill,这里使用的是 useBuiltIns,针对我们在 base 配置中的 babel-polyfill 进行切割,针对我们在项目中使用到的不兼容的特性进行 polyfill。
另外,添加另外的 plugins
- babel-plugin-transform-decorators-legacy 解析装饰器语法,也就是变量前边的@符号,如antd高阶组件中的@Form.create()
- babel-plugin-transform-class-properties 解析 class 语法
另外,我们针对开发环境,为react组件添加热替换 preset,babel-preset-react-hmre
1 | if (isDebug) { |
另外,为了加快编译速度,我们使用happypack进行多线程编译
1 | const HappyPack = require('happypack'); |
LESS 配置 webpack.less.config.js
首先,配置 css-loader
1 | const isDebug = process.env.NODE_ENV !== 'production'; |
配置 postcss-loader
1 | const postcssLoader = { |
这里我们使用配置文件 postcss.config.js 路径指向当前文件夹,然后新建配置文件 postcss.config.js,如下
1 | module.exports = { |
配置 less-loader
1 | const lessLoader = { |
接下来,我们针对不同的环境,为webpack添加不同的 module.rules 和 plugins,首先是开发环境,我们使用 style-loader 将css进行内联(个人认为内联css对热部署比较友好),另外,同react配置,为了加快编译,我们使用 happypack 对 loader 进行包裹
1 | const HappyPack = require('happypack'); |
然后,对于生产环境,我们使用 mini-css-extract-plugin 将 css 文件分离出来,并打包成 chunks,以便减少线上的首屏加载时间。
1 | if (!isDebug) { |
最后,导出配置
1 | const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); |
合并配置 webpack.config.js
最后,我们将所有配置 merge 在一起
1 | const merge = require('webpack-merge'); |
然后我们配置 package.json 的 sctipts,这里我们使用better-npm-run
导出环境变量
1 | { |
好的,配置到这里已经完成,我们可以肆无忌惮的执行 npm run start
了。
额外配置
针对 React 项目,对于开发过程,我们只关心业务代码的增量编译,对于一些第三方 module 我们不需要对齐进行更改,对于生产环境,这些第三方包也可以利用缓存将其缓存起来,优化线上用户体验,所以我们可以使用DllPlugin
或者SplitChunksPlugin
对这些第三方包进行分离。
DllPlugin 可以将指定的module提前编译好,然后在每次解析到这些指定的module时,webpack可直接使用这些module,而不用重新编译,这样可以大大的增加我们的编译速度。
SplitChunksPlugin,可以使用test对module进行正则匹配,对指定的模块打包成chunk,然后每次编译的时候直接使用这些chunk的缓存,而不用每次解析组装这些module。当然,使用SplitChunksPlugin生成的chunk在生成环境可能因为我们指定了chunkHash
每次文件名不一样,导致我们不能好好利用浏览器缓存这些第三方库,也会因此影响到我们html中每次引入的script,必须结合html-webpack-plugin进行使用,但对于一些没有完全前后端分离的业务项目来说(如路由由后端来控制,html渲染是后端控制),这很明显是一个麻烦。
dllPlugin
dllPlugin的原理就是预先编译模块,然后在html中最先引进这些打包完的包,这样 webpack 就可以从全局变量里面去找这些预先编译好的模块。
下面我们使用配置使用 dllPlugin,新建配置文件 webpack.dll.config.js,这个文件为 webpack 需要事先编译的配置文件
首先声明输出 output
1 | const path = require('path'); |
然后声明总体配置
1 | const dllConfig = { |
然后,我们根据不同的环境,添加配置
1 | if (!isDebug) { |
需要注意的是,当我们使用dllPlugin对react进行编译时,我们需要使用isDebug对react进行生产环境和开发环境的区分,因为当我们在生成环境使用开发环境的react的时候,react会报错,所以,我们这里需要对不同环境的库进行打包。
编译打包,最后生成了一个 vendor.js 和 vendor.js.json,然后,我们可以在我们编译的配置中使用 dllReferencePlugin 引进这个json
下面我们新建配置文件 webpack.dll.reference.config.js
1 | const path = require('path'); |
最后,我们把这个配置在 webpack.config.js 里 merge 进来
1 | const merge = require('webpack-merge'); |
然后在package.json添加预编译脚本
1 | { |
打完收工,最后,在npm run start
之前,我们得先执行npm run start:dll
,并在html中引进这个vendor.js
,不然会报错,找不到library,html如下
1 |
|
SplitChunksPlugin
针对 SplitChunksPlugin,其实就是打包 chunks,如我们把node_modules下的所有模块打到一个chunk中
1 | const splitChunkConfig = { |
使用 test 匹配 node_modules,最后会生成一个 vendor.chunk.js,如果设置有 chunkHash,文件名会带hash,然后在html中引进即可。
最后
以上,基本搞了一套 webpack 相对编译较快的配置,嗯呢~,该沉淀一下,献上以上github地址,以上配置,已整理成cli,项目根目录一键生成,详情见 README