4

本文主要介绍两个插件的使用,DllPlugin 和 DllReferencePlugin,后者配合前者使用。

项目 Github 地址:https://github.com/luxiancan/vue-permission-manage

介绍

打包会输出一个类 dll 包(dll 包源于 windows 的动态链接库),这些代码本身不会执行,主要是提供给我们的业务代码引用。(比如 dll 中有一个工具方法为获取本月日期数组,这个方法本身并不会执行,但是当我们的业务中需要获取本月日期时,就会引用这个方法在我们的业务中执行相关逻辑)。

DllPlugin 可以将特定的类库提前打包然后引入。这种方式可以极大的减少打包类库的次数,只有当类库更新版本才有需要重新打包,并且也实现了将公共代码抽离成单独文件的优化方案。

简单来说就是:
将静态资源文件(运行依赖包)与业务代码源文件分开打包,先使用 DllPlugin 给静态资源打包,再使用 DllReferencePlugin 让源文件引用资源文件。

作用

当我们一个项目引入了多个较大的包以后,这些包本身并不会运行,我们也不会修改这些包的代码,但是每当我们修改了业务代码之后,这些包也会被重新打包。极大的浪费了时间,这时我们就需要使用这个工具预先把静态资源提前打包,以后修改源文件再打包时就不会打包这些静态资源文件了。

开始搬砖

我们以 vue-cli 生成的项目为例:

1.基础安装

## 全局安装 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 文件夹):
image.png

2.使用依赖及打包测试

我们编辑 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

image.png

截图中可以看见 Time:65164ms,打包花费了65秒,vue 全家桶以及刚才引入的各种包和源代码全部被打包了。(打包时间也跟电脑配置有关,大家的都不一样,可能我的笔记本比较low所以有点慢吧,影响不大,往下走)

我们才引入了5-6个包而已,就已经花费了65秒,如果以后还要加上各种包及静态资源,打包时间肯定会更久。

这就是我们为什么要引入 DllPlugin 的原因!

3.预打包依赖模块

我们知道,我们刚才所引入的 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

image.png

可以看到 dll 相关的资源文件已经打包成功,此时的目录结构:

image.png

可见我们的目录结构中 static 下生成了 js 子目录,打包好的 dll 文件(js/vendor.dll.js)就放在该目录下,除此之外根目录中还生成了 vendor-manifest.json。
现在我们已经不再需要将那些静态资源包跟源文件一起打包了,但是这也需要在源文件的 webpack.base.conf.js 中配置 DllReferencePlugin 使用 vendor-manifest.json 来引用这个 dll。

4.打包源文件

这一步我们只需要改写 vue-cli 为我们生成好的 build/webpack.base.conf.js 即可:

image.png

该文件主要是添加了 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

image.png

看时间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)


lxcan
337 声望32 粉丝