前言:
最近在优化项目的时候,发现随着前端项目变得越来越大,导致vendor.js也随之变得很大,虽然用了代码分离和依赖文件按需加载,解决了vendor.js单个文件比较大的问题。 但是每次运行打包都需要很久,这个让人也很痛苦。于是开始研究怎么优化这些结构。首先,项目中有很多依赖文件,webpack每次在打包这些文件是很耗时的,但是这些文件在项目开发过程中是基本上不会变的。 这样每次都打包就显得很浪费。
基础技术框架: vue2.0生成的项目文件结构。webpack@^1.13.2
探索过程:
webpack打包优化。于是就找到了webpack 优化 体积篇, 这篇文章,优质好文。 里面提供了两种方案, 一种是webpack的external配置依赖; 一种就是本文介绍的 DllPlugin&DllReferencePlugin。 前者比较简单,使用起来也很方便。但是不够灵活。 于是作者采用了第二种方案。
第二种方案的好处就是可以按需将多个依赖打包成一个js文件。 在js中还是可以正常引用,同时逼格也稍微高一点, 我承认是因为这一点选择的这个方案。 但是看了文章中的第二个方案后发现。我只能把文件打包成js,并不能很完美的在index.html中引入这个js文件。 这个真的很苦恼, 这么明显掉逼格的方式博主肯定是不会用的。于是开始研究解决方案,首先想到的是html-webpack-plugin, 这个插件是webpack中用作将webpack打包后输出的文件chunks,插入到HTML中的。 但是有两个限制条件:
- 我们这里的dll.js 是提前打包好了的,而不是在每次build的时候去打包输出的;这样才能做到依赖包一次构建,无限次使用
- vue中webpack输出的文件名都带有hash值; 而用dll构建后输出的文件名是固定的。
博主就想到了一个歪招, 就是在html-webpack-plugin中添加自定义参数,然后再HTML中使用 ejs语法 htmlWebpackPlugin.options.xx来获取参数值,来向HTML中注入script标签。 后来发现在本地开发环境每次启动都会编译失败, 导致script标签插入不进去。本来就已经绝望了,要放弃了。 但是这个时候其实距离最后的成功已经很近了。html-webpack-plugin中HTML 是支持ejs语法的,只是没有从html-webpack-plugin中正确的获取到参数。 只要能够将参数通过webpack的配置传到HTML页面,就大功告成了。
html-webpack-plugin中有句话:webpackConfig: the webpack configuration that was used for this compilation. This can be used, for example, to get the publicPath (webpackConfig.output.publicPath)
就是HTML中可以通过webpackConfig获取到webpack的配置。 哈哈, 这就可以了。[注: 此功能在webpack2.0以上不能使用]
=====================================
一个月后
当博主想要自己做一个插件的时候,发现了这个好用的小工具 add-asset-html-webpack-plugin, 正是博主要实现的功能, 果断使用。 有个小坑, 在附录代码中会说明。
=====================================
下面是主要文件的配置。 有问题欢迎反馈。
操作:
- config/index.js 中配置依赖包
modules.exports = {
…. 原始配置
library: {
'lib_v1_0': ["babel-polyfill", "mqtt"],
'vueBucket_v1_2': [
"vue/dist/vue.esm.js",
"vue-lazyload",
"vue-resource",
"vue-router",
"vuex",
"vuex-router-sync",
]
}
}
- 新建 build/webpack.dll.conf.js
- 这个文件读取config/index.js 的library 中配置了那些module需要打包
- dll build 导出manifest.json 文件, 留给 DllReferencePlugin在打包时候识别dll都打包了那些node_module
// webpack.dll.conf.js
const webpack = require('webpack');
const path = require('path')
const CleanWebpackPlugin = require('clean-webpack-plugin');
const {build, dev, library} = require('../config')
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? build.assetsSubDirectory : dev.assetsSubDirectory
module.exports = {
// 读取library.entry 里配置的node_module
entry: library,
// 输出到static文件夹下面, 补充知识[vue项目目录之static]
output: {
path: path.resolve(__dirname, '..', assetsSubDirectory),
filename: `[name].dll.js`,
library: '[name]_library'
},
plugins: [
new CleanWebpackPlugin(['static'], { root: path.resolve(__dirname, '..')}),
// 文件输出到 ./build/manifest.json 中
new webpack.DllPlugin({
path: path.resolve(__dirname, '..', assetsSubDirectory, '[name].manifest.json'),
name: '[name]_library',
}),
// 压缩打包的文件
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
}
})
]
};
- 解析dll打包的node_module
// webpack.base.conf.js
var path = require('path')
var config = require('../config')
var webpack = require('webpack')
var AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
...
module.exports = {
...
plugins: [
...,
...Object.keys(config.library).map(name => {
return new webpack.DllReferencePlugin({
context: '.',
manifest: require(`../static/${name}.manifest.json`),
})
}),
new AddAssetHtmlPlugin(Object.keys(config.library).map(name => {
return {
filepath: require.resolve(path.resolve(`./static/${name}.dll.js`)),
includeSourcemap: false
}
})),
]
}
- 在
package.json
中配置打包命令
- 运行命令
npm run build:dll
{
"scripts": {
"dev": "node build/dev-server.js",
"build:dll": "webpack --config build/webpack.dll.conf.js"
}
}
-
项目目录如下
参考知识:
- webpack 优化 体积篇,速度篇。 优质好文
- vue-webpack 解释vue中static文件夹的含义
- DllPlugin & DllReferencePlugin
- html-webpack-plugin