资讯专栏INFORMATION COLUMN

记一次使用 vue-admin-template 的优化历程

xumenger / 2259人阅读

摘要:同时也要引入对应版本的先引入引入组件库因为依赖是从外部引入的,所以需要告知在打包时,依赖的来源。然后在中加入一条命令执行或者即可完成打包。因此将此次优化记录下来,并传上了中。

本文原文

前言

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

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

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

优化

优化主要目的有:

加快资源加载速度,减少用户等待的时间和首页白屏时间,提高用户体验。

加快打包速度,不要将时间浪费在等待打包上。

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

CDN - 提高加载速度

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

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



  
    
    
    vue-admin-template
    
    
  
  
    

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

修改 webpack.base.conf.js

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

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

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

明明我在本地开发,但是由于引入了线上的生产版本的 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 的体积。

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

axios: ajax 请求。

vuex: 全局状态管理。

js-cookie: 前端处理 cookie。

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 引入:

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 中。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95022.html

相关文章

  • Vue-cli3 简qian易yi教程

    摘要:原文地址对于没有了解过的童鞋,建议先去看看官方的教程传送门新版本的新特性插件使用的插件,可以很快的搭建一个项目的结构。使用时直接引入即可。的界面让管理项目变得更加简单。如迁移过程中有任何疑问,可以留言一起探讨。 原文地址 对于没有了解过 vue-cli3 的童鞋,建议先去看看官方的教程: 传送门 新版本的新特性 1. 插件 使用 cli 的插件,可以很快的搭建一个项目的结构。如 ax...

    jemygraw 评论0 收藏0
  • 记一段SPASEO历程:Html5 History Api 大显神通!

    摘要:用户体验的需求,完美地保留了瀑布流模态框的阅读模式。不支持的话,就不拦截瀑布流文块的,也就是直接让其跳转。 背景 想当年,我做了一个新媒体网站项目(AIISPO,已下线)。跟普通资讯网站不一样的是,老板要求PC端前台的文章阅读模式一定得是瀑布流+模态框。瀑布流指的是以瀑布流的形式将文章罗列出来,而模态框则指的是点击瀑布流中代表文章的某个文块时,直接在当前页面弹出模态框来显示文章正文。 ...

    Lyux 评论0 收藏0
  • 记一段SPASEO历程:Html5 History Api 大显神通!

    摘要:用户体验的需求,完美地保留了瀑布流模态框的阅读模式。不支持的话,就不拦截瀑布流文块的,也就是直接让其跳转。 背景 想当年,我做了一个新媒体网站项目(AIISPO,已下线)。跟普通资讯网站不一样的是,老板要求PC端前台的文章阅读模式一定得是瀑布流+模态框。瀑布流指的是以瀑布流的形式将文章罗列出来,而模态框则指的是点击瀑布流中代表文章的某个文块时,直接在当前页面弹出模态框来显示文章正文。 ...

    reclay 评论0 收藏0
  • 一次前端项目重构要点总结

    摘要:重构总共耗时个工作日。第一个重构原因就是没有引入静态类型,导致查看一个对象结构需要翻来覆去在多个文件中查找。第三是各个状态模块耦合度高,加大了代码维护难度。但如果耦合度过高,往往是因为模块没有细分到位。这个项目也不列外。 showImg(https://segmentfault.com/img/remote/1460000019660483); 不知不觉已是2019年的7月,恍惚之间已...

    frolc 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<