module: {
rules: [{
test: /\.js$/,
include: path.resolve(__dirname, '../src'), // 只对src目录下的js文件做打包转译工作
// exclude: /node_modules/, // 如果你的js文件在node_modules里边,就不使用babel-loader了,因为它里边的代码都是些第三方代码,已经做好了转译的工作。
use: [{
loader: "babel-loader"
}]
}]
}
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-plugin');
const prodConfig = {
mode: 'production', // 生产环境
optimization: {
minimizer: [new OptimizeCSSAssetsPlugin({})]
}
}
module.exports = prodConfig;
resolve: { // 新增配置项(如果想在模块里引入js文件,直接可省略后缀的话)
extensions: ['.js', '.jsx'], // 当引入一个其他目录下的模块的时候,首先会去找.js为后缀的文件,如果没有匹配到,再去找.jsx为后缀的文件
mainFiles: ['index', 'child'], // 当引入一个目录下的内容的时候,不知道具体要引入哪个文件,那么可以通过mainFiles配置先尝试去找index文件,如果没有index,再去找child文件
alias: {
child: path.resolve(__dirname, '../src/a/b/child') // 配置引入模块的别名
}
}
当我们的js代码里,引入第三方库 的时候,每次重新打包,都要重新分析所引入的第三方库代码,最终把他们打包到我们的项目之中。第三方库的代码都有个特点,那就是他们是不变的(不随业务逻辑变化而变化),所以我们可以把所有引入的第三方的代码都打包生成一个文件里,只在第一次打包时分析第三方代码,之后再执行打包时,直接用上次分析好的结果即可。
具体步骤如下:
如何结合全局变量和manifest.json映射文件进行webpack的配置??下方的例子:
使用DllReferencePlugin和DllPlugin进行配置:
我们单独配置一个第三方库的打包文件:webpack.dll.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
mode: 'production',
entry: {
vendors: ['react', 'react-dom', 'lodash']
},
plugins: [
new webpack.DllPlugin({
name: '[name]', // 用DllPlugin对库进行分析
path: path.resolve(__dirname, '../dll/[name].manifest.json') // 把库里边第三方的一些映射关系,放到name.manifest.json文件下
})
],
output: {
filename: '[name].dll.js',
path: path.resolve(__dirname, '../dll'), // 把第三方库代码打包到dll文件夹下
library: '[name]' // 暴露全局变量
}
}
scripts配置:package.json文件
"scripts": {
"build": "webpack --config ./build/webpack.prod.js",
"build:dll": "webpack --config ./build/webpack.dll.js"
}
执行打包第三方库文件:npm run build:dll
webpack.common.js中的插件配置:
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
const webpack = require('webpack');
module.exports = {
plugins: [
new AddAssetHtmlWebpackPlugin({ // 为页面引入打包好的第三方库代码文件
filepath: path.resolve(__dirname, '../dll/vendors.dll.js')
}),
new webpack.DllReferencePlugin({ // 查找第三方库的映射关系
manifest: path.resolve(__dirname, '../dll/vendors.manifest.json')
})
]
}
再次打包 npm run build 我们会发现:
不配置DllReferencePlugin插件时:打包速度基本稳定在1000ms
配置DllReferencePlugin插件之后:打包速度基本稳定在600ms
实际上,在webpack配置大型复杂项目过程中,DllPlugin和DllReferencePlugin用的还是比较多的。
在webpack做打包的时候,我们就可以结合全局变量、以及我们生成的manifest映射文件,然后来对我们的源代码进行分析,一旦发现你使用的第三方库的内容是在vendors.dll.js里边,那么它就会直接使用vendors.dll.js里的内容了。就不会再去node_modules里引入我们的模块了。
在我们的项目开发中,经常会引入一些没有用到的模块,引入之后,其实我们并没有使用这些模块。这个时候,如果你没有配置tree-shaking,就会导致在打包的过程中有很多冗余的代码。那么这些冗余的代码,实际上就会拖累webpack的打包速度。所以,在我们做打包的时候,对于一些冗余的代码,我们可以通过 tree-shaking(查看详解) 把它去除掉。(或者直接不去引用它)这样我们就可以控制webpack打包生成文件的大小,从而提升打包速度。
也可以通过splitChunksPlugin插件,对代码进行拆分,把一个大的文件,拆分成几个小的文件,做webpack的打包处理,这样也可以提高webpack的打包速度。
webpack默认是基于node.js来运行的,所以它是一个单进程的打包过程。我们也可以借助webpack里的多进程来帮我们提升打包速度
如:thread-loader, parallel-webpack, happypack 多进程打包。
他们可以利于node中的多进程,同时利用多个cpu进行打包,具体需要开几个cpu进行打包速度是最快的,对其他应用影响是最小的,要根据项目实际情况,多做几次尝试来做一个权衡。
webpack-dev-server做打包的时候不会生成dist目录,而是会把打包生成的文件放到内存里,那么内存的读取,肯定要比硬盘的读取要快的多,所以采用这种手段,也会让我们在开发的过程中webpack打包的性能得到很大的提升。
另外,提升webpack打包速度的方式还有很多,比如:一些loader会提供一些参数,帮助我们去提升它的打包速度。
我们对webpack.dll.js文件里的entry再做一个变更:(对打包的dll文件做拆分)
entry: {
vendors: ['lodash'],
react: ['react', 'react-dom'] // 让react、react-dom库的代码打包到文件名为react的文件里
}
在webpack公共配置文件中多配置一份插件:
单独打包第三方库代码:(执行打包)
npm run build:dll
npm run dev
页面显示正常,react、vendors也可以正常访问。
如果在大型项目中,对dll文件做拆分比较多的话,这样会导致在webpack公共的配置文件里,重复实例化多个相同的插件,下边分享个更好的方法:
通过node来分析dll目录下究竟有几个dll文件和manifest文件,然后动态的往plugins里添加AddAssetHtmlWebpackPlugin和DllReferencePlugin。
const path = require('path'); // 引入node的path模块(loader模块)
const fs = require('fs');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
const webpack = require('webpack');
// 创建一个插件数组
const plugins = [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin()
]
// 使用fs读取dll目录下的文件
const files = fs.readdirSync(path.resolve(__dirname, '../dll'));
// 对获取到的所有文件进行循环
files.forEach(file => {
if(/.*\.dll.js/) { // 如果是以.dll.js结尾的文件,就以走AddAssetHtmlWebpackPlugin插件
plugins.push(
new AddAssetHtmlWebpackPlugin({ // 为页面引入打包好的第三方库代码文件
filepath: path.resolve(__dirname, '../dll', file)
})
)
}
if(/.*\.manifest.json/) { // 如果是以.manifest.json结尾的文件,就走DllReferencePlugin插件
plugins.push(
new webpack.DllReferencePlugin({
manifest: path.resolve(__dirname, '../dll', file)
})
)
}
})
module.exports = {
entry: {
main: './src/index.js'
},
resolve: { // 新增配置项(如果想在模块里引入js文件,直接可省略后缀的话)
extensions: ['.js', '.jsx']
},
module: {
rules: [{
test: /\.jsx?$/, // 这里代表既匹配js文件,也匹配jsx文件
include: path.resolve(__dirname, '../src'), // 只对src目录下的js文件做打包转译工作
// exclude: /node_modules/, // 如果你的js文件在node_modules里边,就不使用babel-loader了,因为它里边的代码都是些第三方代码,已经做好了转译的工作。
use: [{
loader: "babel-loader"
}]
}]
},
plugins, // 插件配置项
optimization: {
splitChunks: { // 有默认配置项
chunks: "all" // 不管是同步还是异步,都进行代码分割
}
},
output: {
filename: '[name].js', // 打包之后的输出文件
path: path.resolve(__dirname, '../dist')
}
}
执行打包:
npm run dev
打开控制台我们发现,index.html页面引入了我们打包过后的第三方库文件。
此时,再对打包的dll文件做拆分,就比较方便了,每次做拆分就不必一次次的添加插件的实例了。