详解使用vue-admin-template的优化历程


Posted in Javascript onMay 20, 2018

前言

公司有好几个项目都有后台管理系统,为了方便开发,所以选择了 vue 中比较火的后台模板 作为基础模板进行开发。但是,开始用的时候,作者并没有对此进行优化,到项目上线的时候,才发现,打包出来的文件都十分之大,就一个 vendor 就有 770k 的体积(下图是基础模板,什么都没加打包后的文件信息):

详解使用vue-admin-template的优化历程

通过 webpack-bundle-analyzer 进行分析可得,体积主要来源于饿了么UI(体积为 500k),因为没对其进行部分引入拆分组件,导致 webpack 把整个组件库都打包进去了。其次就是 vue 本身,体积也达到了 80k 之大。

详解使用vue-admin-template的优化历程

所以,对其进行打包优化,是一件刻不容缓的事情。

优化

优化主要目的有:

  1. 加快资源加载速度,减少用户等待的时间和首页白屏时间,提高用户体验。
  2. 加快打包速度,不要将时间浪费在等待打包上。

解决第一个问题,很多人都会想到资源文件放在 CDN 上就好了,没错,这次我们就是通过 CDN 来解决加载问题。

CDN - 提高加载速度

像 vue, element ui 这些比较成熟的框架/组件库,一般都有免费、高速、公共的 cdn 供开发者使用,鉴于大部分用户均在国内,所以这次使用了 bootcdn 这个库。该库热门资源比较齐全,各个版本都有,而且国内访问速度很快,简直是开发者的福音。

index.html 中引入 vue 和 饿了么组件。

<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>vue-admin-template</title>
  <!-- 同时也要引入对应版本的 css -->
  <link href="https://cdn.bootcss.com/element-ui/2.3.2/theme-chalk/index.css" rel="external nofollow" rel="stylesheet">
 </head>
 <body>
  <div id="app"></div>
  <!-- built files will be auto injected -->
  <!-- 先引入 Vue -->
  <script src="https://cdn.bootcss.com/vue/2.4.2/vue.min.js"></script>
  <!-- 引入组件库 -->
  <script src="https://cdn.bootcss.com/element-ui/2.3.2/index.js"></script>
  <script src="https://cdn.bootcss.com/element-ui/2.3.2/locale/zh-CN.min.js"></script>
 </body>
</html>

因为依赖是从外部引入的,所以需要告知 webpack 在打包时,依赖的来源。

修改 webpack.base.conf.js

module.exports = {
 ...
 externals: {
  vue: 'Vue',
  'element-ui':'ELEMENT'
 }
}

再一次打包,确实能极大的压缩了打包的体积,从 700k 骤减至 130k:

详解使用vue-admin-template的优化历程

但是随之而来的就有问题了:

详解使用vue-admin-template的优化历程

明明我在本地开发,但是由于引入了线上的生产版本的 vue 文件,因此 vue-dev-tools 就不能进行调试。

因此,我们需要再次调整一下 webpack 的配置,webpack.base.conf.js,而且 webpack 注入的 js 总是在最后面的,因此,我们需要 html-webpack-include-assets-plugin 帮忙在注入 app.js 后,再注入相对应的组件库 :

const HtmlWebpackIncludeAssetsPlugin = require('html-webpack-include-assets-plugin')

const externals = {
 // 因为打包时,还没注入,所以这里要去掉。
 // 'element-ui':'ELEMENT'
}
// 生产环境中使用生产环境的 vue
// 开发环境继续使用本地 node_modules 中的 vue
if (process.env.NODE_ENV === 'production') {
 externals['vue'] = 'Vue'
 // 如发现打包时依旧将 element-ui 打包进入 vendor,可以在打包时将其加入外部依赖。
 externals['element-ui'] = 'ELEMENT'
}
// 生产环境默认注入 vue 
// 开发环境中不注入
const defaultJS = process.env.NODE_ENV === 'production' ? [{ path: 'https://cdn.bootcss.com/vue/2.4.2/vue.min.js', type: 'js' }] : []
const plugins = [
 new HtmlWebpackIncludeAssetsPlugin({
   assets: defaultJS.concat([
    { path: 'https://cdn.bootcss.com/element-ui/2.3.2/index.js', type: 'js' },
    { path: 'https://cdn.bootcss.com/element-ui/2.3.2/locale/zh-CN.min.js', type: 'js' },
   ]),
   // 是否在 webpack 注入的 js 文件后新增?true 为 append, false 为 prepend。
   // 生产环境中,这些 js 应该先加载。
   append: process.env.NODE_ENV !== 'production',
   publicPath: '',
  })
]

module.exports = {
 ...
 externals,
 plugins,
 ...
}

OK,这时候,既能兼顾打包后的体积大小,也能在开发模式中使用 vue-dev-tool 进行调试。

DLL - 提高打包速度

经常打包的前端会发现,很多时候,我们为了修复某些bug(如 promise 在 ie Safari 下的 bug),而新引入了一个 polyfill,然而,打包完后发现,vendor 的 hash 值变了,而整个 vendor 只新加了一个 es6-promise 的依赖,但是付出的代价就是,需要抛弃之前打包好的 vendor,用户重新访问时,需要再一次拉取一个全新的 vendor,这个代价就有点大了。

这时候,使用 dllPlugin 打包就有优势了。它可以将一些基础依赖模块统一先打包起来,当正式打包时,则可以略过这些模块,不再重复打包进去 vendor,提高打包速度的同时也能减少 vendor 的体积。

如,后台管理系统基础模块基本有以下几个:

  1. axios: ajax 请求。
  2. vuex: 全局状态管理。
  3. js-cookie: 前端处理 cookie
  4. vue-router: 路由管理。

这四个基础模块几乎是必须的,那么可以先提取出来。

step 1 打包基础模块

先在 build 文件夹下新建一个用于打包 dll 的配置文件 webpack.dll.conf.js

const webpack = require('webpack');
const path = require('path');
const vueLoaderConfig = require('./vue-loader.conf')
const utils = require('./utils')
function resolve(dir) {
  return path.join(__dirname, '..', dir)
}

const vendor = [
  // 'vue/dist/vue.runtime.esm.js', // 由于 vue 在生产环境中使用的是 cdn 引入,所以也无需提前打包进 dll
  // 'raven-js', // 前端监控,若无此需求,可以忽略。
  'es6-promise', // 修复 promise 中某些 bug。
  'vue-router',
  'js-cookie',
  'axios',
  'vuex',
];

const webpackConfig = {
  context: __dirname,
  output: {
    path: path.join(__dirname, '../static/js/'),
    filename: '[name].dll.js',
    library: '[name]_[hash]',
  },
  entry: {
    vendor
  },
  plugins: [
    new webpack.DllPlugin({
      context: __dirname,
      path: path.join(__dirname, '.', '[name]-manifest.json'),
      name: '[name]_[hash]',
    }),
    new webpack.optimize.UglifyJsPlugin({
      compress: {
       warnings: false
      },
      sourceMap: true,
      // parallel: true
    })
  ],
  module: {
    rules: [{
        test: /\.vue$/,
        loader: 'vue-loader',
        options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        loader: 'babel-loader',
        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
      },
      {
        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('img/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('media/[name].[hash:7].[ext]')
        }
      },
      {
        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
        loader: 'url-loader',
        options: {
          limit: 10000,
          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
        }
      }
    ]
  }
};
module.exports = webpackConfig

然后在 package.json 中加入一条命令:

{
  "scripts": {
    ...
    "build:dll": "webpack --config build/webpack.dll.conf.js",
    ...
  }
}

执行 yarn build:dll 或者 npm run build:dll 即可完成打包 dll。执行完成后:

yarn build:dll
yarn run v1.5.1
$ webpack --config build/webpack.dll.conf.js
Hash: f6894dff019b2e0734af
Version: webpack 3.10.0
Time: 1295ms
     Asset   Size Chunks       Chunk Names
vendor.dll.js 62.6 kB    0 [emitted] vendor
  [8] dll vendor 12 bytes {0} [built]
  + 32 hidden modules
✨ Done in 1.89s.

同时,可以在 build 目录下,找到各个模块对应关系文件 vendors-manifest.jsonstatic/js 下的 vendor.dll.js

step 2 页面中引入 vendor

打包后的 dll 文件需要手动在 index.html 引入:

<div id="app"></div>
<!-- built files will be auto injected -->
<script src="static/js/vendors.dll.js"></script>

step 3 告诉 webpack 使用 dllPlugin 进行打包

修改 build/webpack.prod.conf.js:

module.exports = {
  plugins: [
    ...
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require('./vendor-manifest.json')
    }),
    ...
  ]
}

再次打包:

$ yarn build:report
yarn run v1.5.1
$ npm_config_report=true node build/build.js
Hash: b4ff51852866ed865cfd
Version: webpack 3.10.0
Time: 6532ms
                       Asset    Size Chunks       Chunk Names
     static/js/manifest.42b9584a653aec2b9c5e.js   1.5 kB    5 [emitted] manifest
             static/img/404.a57b6f3.png  98.1 kB     [emitted]
        static/js/1.9e4133a25808e2101dd3.js    1 kB    1 [emitted]
        static/js/2.2a8a8e01c51473fab882.js  4.34 kB    2 [emitted]
      static/js/vendor.c7b076ef3341d4711402.js  39.4 kB    3 [emitted] vendor
       static/js/app.6d52c7a5bf1bacb5cc85.js  21.4 kB    4 [emitted] app
        static/js/0.cbc645864aab28ae8055.js  15.3 kB    0 [emitted]
static/css/app.1b30f8eba210e245a5f96d7bf0d6fb6c.css   7.6 kB    4 [emitted] app
                    favicon.ico  67.6 kB     [emitted]
                     index.html 986 bytes     [emitted]
              static/js/vendor.dll.js  62.6 kB     [emitted]

 Build complete.

 Tip: built files are meant to be served over an HTTP server.
 Opening index.html over file:// won't work.

发现 vendor 现在只有 40k 的体积,减少了一半的体积,而且打包速度也快了 2s,而相对于最开始的基础模板,打包速度快了 12s,这是很让人欣慰。

后记

使用了 cdn 和 dll 打包后,无论是打包速度还是页面加载的速度都有很大的提升。因此将此次优化记录下来,并传上了 GitHub 中。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
input的focus方法使用
Mar 13 Javascript
基于JQuery的一句代码实现表格的简单筛选
Jul 26 Javascript
没有document.getElementByName方法
Aug 19 Javascript
jQuery实现购物车多物品数量的加减+总价计算
Jun 06 Javascript
JavaScript实现简单图片翻转的方法
Apr 17 Javascript
JavaScript多线程详解
Aug 12 Javascript
javascript的几种继承方法介绍
Mar 22 Javascript
jQuery向父辈遍历的简单方法
Sep 18 Javascript
JavaScript原生节点操作小结
Jan 17 Javascript
一个Vue页面的内存泄露分析详解
Jun 25 Javascript
JS实现的tab页切换效果完整示例
Dec 18 Javascript
基于Vue+ElementUI的省市区地址选择通用组件
Nov 20 Javascript
vuex进阶知识点巩固
May 20 #Javascript
简单的三步vuex入门
May 20 #Javascript
vue项目如何刷新当前页面的方法
May 18 #Javascript
原生JS实现的碰撞检测功能示例
May 18 #Javascript
JS实现json对象数组按对象属性排序操作示例
May 18 #Javascript
vue.js使用3DES加密的方法示例
May 18 #Javascript
JS实现的3des+base64加密解密算法完整示例
May 18 #Javascript
You might like
php.ini 中文版
2006/10/28 PHP
php 中文处理函数集合
2008/08/27 PHP
基于CI(CodeIgniter)框架实现购物车功能的方法
2018/04/09 PHP
PHP保留两位小数的几种方法
2019/07/24 PHP
php查看一个变量的占用内存的实例代码
2020/03/29 PHP
JavaScript中令你抓狂的魔术变量
2006/11/30 Javascript
List the Stored Procedures in a SQL Server database
2007/06/20 Javascript
javascript 命名空间以提高代码重用性
2008/11/13 Javascript
向大师们学习Javascript(视频与PPT)
2009/12/27 Javascript
JQery 渐变图片导航效果代码 漂亮
2010/01/01 Javascript
js判断一个字符串是否包含一个子串的方法
2015/01/26 Javascript
javascript操作Cookie(设置、读取、删除)方法详解
2015/03/18 Javascript
jquery实现简单手风琴菜单效果实例
2015/06/13 Javascript
Bootstrap入门书籍之(零)Bootstrap简介
2016/02/17 Javascript
vue.js指令v-for使用及索引获取
2016/11/03 Javascript
jQuery ajax 当async为false时解决同步操作失败的问题
2016/11/18 Javascript
jQuery给表格添加分页效果
2017/03/02 Javascript
ES6中Proxy与Reflect实现重载(overload)的方法
2017/03/30 Javascript
详解如何配置vue-cli3.0的vue.config.js
2018/08/23 Javascript
vue-自定义组件传值的实例讲解
2018/09/18 Javascript
AngularJS 多指令Scope问题的解决
2018/10/25 Javascript
实例分析JS中的相等性判断===、 ==和Object.is()
2019/11/17 Javascript
[01:29:46]DOTA2上海特级锦标赛C组资格赛#1 OG VS LGD第二局
2016/02/27 DOTA
python threading模块操作多线程介绍
2015/04/08 Python
Python中有趣在__call__函数
2015/06/21 Python
Python实现类的创建与使用方法示例
2017/07/25 Python
dataframe设置两个条件取值的实例
2018/04/12 Python
解决Python plt.savefig 保存图片时一片空白的问题
2019/01/10 Python
python基础梳理(一)(推荐)
2019/04/06 Python
python对Excel按条件进行内容补充(推荐)
2019/11/24 Python
英国假发网站:Hothair
2018/02/23 全球购物
美术指导求职信
2014/03/17 职场文书
不忘国耻振兴中华演讲稿
2014/05/14 职场文书
无保留意见审计报告
2015/06/05 职场文书
Elasticsearch Recovery 详细介绍
2022/04/19 Java/Android
Linux磁盘管理方法介绍
2022/06/01 Servers