前言 我入行的时候前端框架就盛行,react、angular、vue等框架大行其道。这些框架的好处是十分显然的,便捷、快速且稳定。如果框架搭建的足够便捷不熟悉业务的人也能够快速上手项目,拿vue-cli来说,现成的脚手架已经集成了webpack、eslint、bable等常见框架的基础配置,而这些配置未必最优却通用性很强,对于初学者来说甚者可以不去了解这些常见框架就可以直接上手开发了。正是由于框架的便捷性,使得框架更加流行。vue-cli3更是直接把配置内置在了第三方插件里,让你在工作目录中完全看不到基础配置可以专心于业务开发。各类框架正在往更便捷更傻瓜式的道路上越走越远。但是这对开发者来说是一件值得警惕的事情,框架隐藏相对复杂的黑盒不意味着我们可以不去关心其内部原理,因为通用并不意味着最优。因此本文尝试去讨论webpack优化方案。本文为webpack系列第一篇dllplugin优化。
DllPlugin优化 再讲DllPlugin之前,不得不先提到dll,首先依例放一个百度举的例子
动态链接库英文为DLL,是Dynamic Link Library的缩写。DLL是一个包含可由多个程序,同时使用的代码和数据的库。 可以更为容易地将更新应用于各个模块,而不会影响该程序的其他部分。例如,您可能具有一个工资计算程序,而税率每年都会更改。当这些更改被隔离到 DLL 中以后,您无需重新生成或安装整个程序就可以应用更新。
具体到前端就是,如果你使用vue技术栈来开发,你改动了一行业务代码这势必不会影响vue源码,但是你构建的时候发现了所有的代码都被重新构建了,这显然是不合理的,而dllplugin就是解决这个问题的。让我们将第三方库这种无论我们怎么改业务代码构建结果都不会发生改变的模块单独拆出来构建,下次只要不改变第三方库就直接引用现成的构建结果就可以了。
那要怎么使用呢? 因为webpack己经内置了对动态链接库的支持,所以只需要通过DllPlugin打包出动态链接库和用DllReferencePlugin在主构建中引入动态链接库就可以了。以下将会具体去讲这两个步骤。
1、构建动态链接库文件 构建文件需要和主配置分开写,因为主配置是用于打包主文件的,而动态链接库会作为主构建的依赖文件打包成库
const path = require('path')
const webpack = require('webpack')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
// dll文件存放的目录
const dllPath = 'dll'
module.exports = {
entry: {
// 需要提取的库文件
vendor: ['vue', 'vue-router', 'core-js']
},
output: {
path: path.join(__dirname, dllPath),
filename: '[name]-dll.[hash:8].js',
// 这会将你的 library bundle 暴露为名为[name]_[hash]的全局变量
// 保持与 webpack.DllPlugin 插件配置中name名称一致
library: '[name]_[hash]'
},
plugins: [
// 清除之前的dll文件
new CleanWebpackPlugin(),
// 该插件为webpack自带插件无需单独引用
// 设置环境变量
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: JSON.stringify('production')
}
}),
// 该插件为webpack自带插件无需单独引用
// manifest.json 描述动态链接库包含了哪些内容
new webpack.DllPlugin({
path: path.join(__dirname, dllPath, '[name]-manifest.json'),
// 这里的name需要保持与 output.library中名称一致
name: '[name]_[hash]',
context: process.cwd()
})
]
}
有了打包文件,接下来进行打包,先在package.json中添加命令dll,进行构建
"dll": "webpack --progress --config ./build/webpack.dllconf.js"
运行后得到,这个就是动态链接库文件了,先放在这里备用。然后在主构建中引入。
2、使用动态链接库文件 第一步配置动态链接库的DllReferencePlugin,这样执行构建时如果依赖已经在动态链接库中存在就不会重复打包,而是直接使用。manifest则说明了动态链接库中包含了哪些依赖模块。
// webpack自带插件
// 如果有打包多个动态链接库文件 这里分次创建多个DllReferencePlugin即可
new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require('./build/dll/vendor-manifest.json')
}),
到这里我们已经达到优化构建速度的目的了,但是如果要让项目跑起来,动态库文件还需要注入进index.html
// 该插件如果不能正常向html中插入文件,可能是未在html-webpack-plugin之后执行或者版本兼容问
// 建议看add-asset-html-webpack-plugin的最新说明
new AddAssetHtmlPlugin({
// dll文件位置
filepath: path.resolve(__dirname, './build/dll/*.js'),
// dll 引用路径
publicPath: './vendor',
// dll最终输出的目录
outputPath: './vendor'
})
因为是vue-cli直接创建的项目作为demo,因此使用vue.cnfig.js写的配置,使用configureWebpack配置项来写的,和webpack配置没什么区别
const path = require('path')
const webpack = require('webpack')
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
module.exports = {
publicPath: './',
// 该配置项意为将webpack配置合并进vue-cli自带框架
configureWebpack: {
plugins: [
// webpack自带插件
// 如果有打包多个动态链接库文件 这里分次创建多个DllReferencePlugin即可
new webpack.DllReferencePlugin({
context: process.cwd(),
manifest: require('./build/dll/vendor-manifest.json')
}),
// 该插件如果不能正常向html中插入文件,可能是未在html-webpack-plugin之后执行或者版本兼容问题
// 建议看add-asset-html-webpack-plugin的最新说明
new AddAssetHtmlPlugin({
// dll文件位置
filepath: path.resolve(__dirname, './build/dll/*.js'),
// dll 引用路径
publicPath: './vendor',
// dll最终输出的目录
outputPath: './vendor'
})
]
}
}
执行打包命令可以看到动态链接库的文件被复制到dist目录下且已经被index.html引用。因为如vue、loadash、vant等三方库文件,如果没有特殊情况大概率是不会有什么版本变动,而且本身资源是比较大的。如果每次都重复构建会比较消耗时间,而是用动态链接库的方式可以每次不重复构建,以此节省时间,优化开发体验。