本文主要介绍两个插件的使用,DllPlugin 和 DllReferencePlugin,后者配合前者使用。
项目 Github 地址:https://github.com/luxiancan/vue-permission-manage
打包会输出一个类 dll 包(dll 包源于 windows 的动态链接库),这些代码本身不会执行,主要是提供给我们的业务代码引用。(比如 dll 中有一个工具方法为获取本月日期数组,这个方法本身并不会执行,但是当我们的业务中需要获取本月日期时,就会引用这个方法在我们的业务中执行相关逻辑)。
DllPlugin 可以将特定的类库提前打包然后引入。这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。
简单来说就是:
将静态资源文件(运行依赖包)与业务代码源文件分开打包,先使用 DllPlugin 给静态资源打包,再使用 DllReferencePlugin 让源文件引用资源文件。
当我们一个项目引入了多个较大的包以后,这些包本身并不会运行,我们也不会修改这些包的代码,但是每当我们修改了业务代码之后,这些包也会被重新打包。极大的浪费了时间,这时我们就需要使用这个工具预先把静态资源提前打包,以后修改源文件再打包时就不会打包这些静态资源文件了。
我们以 vue-cli 生成的项目为例:
## 全局安装 vue-cli 脚手架 和 webpack cnpm install -g vue-cli webpack-dev-server ## 初始化项目 winpty vue.cmd init webpack vue-permission-manage cd vue-permission-manage ## 安装基础配置包 cnpm install ## 安装依赖模块(静态资源) cnpm install vuex axios element-ui echarts -S
安装完成后的目录结构 (此处已经隐藏 node_modules 文件夹):
我们编辑 main.js,引入我们所安装的静态资源,结果为:
import 'babel-polyfill'; // The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue'; import App from './App'; import router from './router'; import store from './store/store.js'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; import '@/assets/css/normalize.css'; import '@/assets/css/common.scss'; import httpRequest from '@/assets/js/service/http.js'; import '@/assets/js/service/mock.js'; Vue.config.productionTip = false; Vue.use(ElementUI); Vue.prototype.$http = httpRequest; /* eslint-disable no-new */ new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' });
回到命令窗口,执行打包命令:
## 编译打包 npm run build
截图中可以看见 Time:65164ms,打包花费了65秒,vue 全家桶以及刚才引入的各种包和源代码全部被打包了。(打包时间也跟电脑配置有关,大家的都不一样,可能我的笔记本比较low所以有点慢吧,影响不大,往下走)
我们才引入了5-6个包而已,就已经花费了65秒,如果以后还要加上各种包及静态资源,打包时间肯定会更久。
这就是我们为什么要引入 DllPlugin 的原因!
我们知道,我们刚才所引入的 vue 或者 axios 之类的,我们只是使用它们,并不会改变它们的源码,它们本身也不会运行,那么我们就可以把这些模块拆分出来提前打包。
那么如何提前打包它们呢? 我们在根目录的 build 文件夹下创建一个 webpack 配置文件(webpack.dll.conf.js),既然这个文件是 webpack 配置文件,那么它的格式肯定也和 webpack 的其他配置文件一样:
var path = require('path'); var webpack = require('webpack'); /* 将特定的类库提前打包然后引入,不但能够极大减少打包时间, 也实现了将公共代码抽离成单独文件的优化方案,可以很大程度的减小打包之后的文件体积。 */ module.exports = { // 你想要提前打包的类库的数组。注意 vue 要写成别名 entry: { // 如果这些类库有版本更新了(一般很少更新),就需要重新执行 npm run dll 打包类库,再执行 npm run build 打包项目上线 // 这里用 vendor 作为 key 值表示后文用到的 [name] ,后续生成的打包文件就为 vendor-manifest.json vendor.dll.js vendor: ['vue/dist/vue.esm.js', 'vuex', 'axios', 'vue-router', 'element-ui', 'echarts'] }, output: { path: path.join(__dirname, '../static/js'), // 打包后文件输出的位置,放到项目根目录的 static/js 下 filename: '[name].dll.js', // 打包后的文件名 vendor.dll.js library: '[name]_library' // vendor.dll.js 中暴露出的全局变量名,主要是给 DllPlugin 中的 name 使用。 // 所以这里需要和 webpack.DllPlugin 中的 name: '[name]_library', 保持一致。 }, plugins: [ new webpack.DllPlugin({ // manifest.json 生成的文件夹及名字,这里路径写成 .. 代表上一级目录,也就是让它生成在了根目录下 path: path.join(__dirname, '../[name]-manifest.json'), // 和 output.library 保持一致即可 name: '[name]_library', // manifest 文件中请求的上下文,默认为本文件的上下文 context: __dirname }), // 压缩打包的文件,使用 UglifyJsPlugin 插件压缩代码 new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }) ] };
重点:这里引入的 Dllplugin 插件,该插件将生成一个 manifest.json 文件,该文件供 webpack.dll.conf.js 中加入的 DllReferencePlugin 使用,使我们所编写的源文件能正确地访问到我们所需要的静态资源(运行时依赖包)。相关字段的解释已经在代码注释中写明。
编写该 webpack 配置之后,我们就可以预打包资源文件了。
## 以指定的 webpack 配置文件执行打包 webpack --config build/webpack.dll.conf.js
emmm。。。感觉这命令有点难记哎,那我们就将它加入 package.json 里吧
"scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "lint": "eslint --ext .js,.vue src test/unit", "dll": "webpack --config build/webpack.dll.conf.js", "build": "node build/build.js" },
预打包资源文件:
npm run dll
可以看到 dll 相关的资源文件已经打包成功,此时的目录结构:
可见我们的目录结构中 static 下生成了 js 子目录,打包好的 dll 文件(js/vendor.dll.js)就放在该目录下,除此之外根目录中还生成了 vendor-manifest.json。
现在我们已经不再需要将那些静态资源包跟源文件一起打包了,但是这也需要在源文件的 webpack.base.conf.js 中配置 DllReferencePlugin 使用 vendor-manifest.json 来引用这个 dll。
这一步我们只需要改写 vue-cli 为我们生成好的 build/webpack.base.conf.js
即可:
该文件主要是添加了 plugins 配置:
plugins: [ new webpack.DllReferencePlugin({ context: __dirname, // 与 Dllplugin 里的 context 所指向的上下文保持一致,这里都是指向了当前文件的 build 目录 manifest: require('../vendor-manifest.json') // 引入 Dllplugin 所生成的的 manifest }) ],
到这里配置完这个就。。哦还没完别急,我们需要手动在根目录的 index.html 里引入生成的 dll 库
<body> <div id="app"></div> <!-- built files will be auto injected --> <script src="./static/js/vendor.dll.js"></script> <!--引入 vendor.dll.js --> </body>
好的,激动人心的时刻终于来了!!!我们现在只需要 npm run build
看时间Time:20746ms!!!,比起之前的65秒我们缩短到了20秒左右!时间就是$$,时间就是生命啊!!
往后如果我们只是改动了业务代码,就不需要重新打包那些个各种庞大的类库了。不过还要注意一点,如果相关类库版本更新了,再次执行 npm install 的时候类库源码有更新,此时就需要重新执行一下 npm run dll 再执行 npm run build。为此,我们在 package.json 再加一个命令 dll_build:
"scripts": { "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js", "lint": "eslint --ext .js,.vue src test/unit", "dll": "webpack --config build/webpack.dll.conf.js", "build": "node build/build.js", "dll_build": "npm run dll && npm run build" },
第一次打包时,或者类库版本更新了,执行 npm run dll_build
就好了~
到这里优化就结束啦,文章有不妥的地方欢迎指出留言哦,觉得不错的话点个赞点个星吧 hhh,github 源码地址在文章开头哦~
参考资料:
webpack 中文文档 | webpack 中文网
webpack进阶——DllPlugin优化打包性能(基于vue-cli)