SPA项目的优化方案(一) 主角:externals 与 Dll

1.前言

起初,这是很久之前优化的一个vue SPA项目,最近在总结时看到了记录的externals和Dll,
二者都是为了分离模块诞生,那么在效率上各自如何呢,还有分别适合于什么场景呢?
针对这两个疑惑,写下这篇笔记,顺便记录一下SPA项目的优化方案。

首先看数据

  • 优化前
优化前-打包信息 优化前-资源加载
  • 优化后
优化后-打包信息 ![]() 优化后-资源加载

优化后数据如下

dom树构建完毕时间页面完全加载时间打包文件大小
优化前1.79s2.01s1.15MB
优化后0.484s1.70s225.1KB

注意:DOMContentLoaded事件,即dom树构建完毕,通常这时候我们已经能看到页面了,这个等待时间直接决定了用户的初体验,这里可以看到优化前后明显的速度提升。

2.开始优化

2.1. 第三方模块按需引入

取消全局引入,在组件中import {xxx, xxx} from 'XXXXXX';

2.2. 路由按需加载

    export default new VueRouter({
      routes: [
        {
          path: '/',
          name: 'Home',
          component: () => import('../pages/Home'),
        },
        ...
      ]
    })

2.3. 提取第三方模块

在我们的项目中,大部分依赖库是不会改变的
比如vue、Ant、echarts等等,
对于这些基本不会改变的模块,我们希望他们不要影响我们的打包时间和打包后文件的加载速度。

2.3.1 externals

官网解释:

防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖(external dependencies)。

简单讲,就是把我们引入的三方模块声明为外部依赖,webpack不会对其进行打包,最后我们在打包的项目中通过script外链引入即可。

vue-cli 2.x的配置文件

  • webpack.base.conf.js: 基础公共配置
  • webpack.dev.conf.js: 开发环境配置
  • webpack.prod.conf.js: 生产环境配置

我们在选择配置文件时,注意不要选择base和dev,因为我们的开发环境需要实时编译,如果这时候编译忽略了第三方包,那么程序在运行时就会报错,xxx is not defined,所以下面的操作我们都选择webpack.prod.conf.js

具体操作:

  1. 在webpack.prod.conf.js中声明externals
    externals: {
        'vue': 'Vue',
        'echarts': 'echarts',
        ...
    }
  1. 在打包后的dist/index.html中手动添加、或者写脚本(比如node)自动添加
    <script src=...></script>
    <script src=...></script>
    <script src=...></script>
  1. 在webpack.prod.conf.js中声明libraryTarget

    打包时,webpack不会将external中声明的第三方模块记录到chunk信息中,所以我们需要通过libraryTarget来告知打包后的文件,当读到了externals中的key时,需要以umd的方式去获取资源名,否则可能出现找不到module的问题。

    output: {
        libraryTarget: 'umd',
        ...
    },
2.3.2 Dll

包括DllPlugin和DllReferencePlugin

官网解释:

使用于将项目依赖的基础模块(第三方模块)抽离出来,然后打包到一个个单独的动态链接库中。当下一次打包时,通过webpackReferencePlugin,如果打包过程中发现需要导入的模块存在于某个动态链接库中,就不能再次被打包,而是去动态链接库中get到。

简单讲,就是将第三依赖单独打包,通过引用 manifest.json来把依赖映射到模块上,相当于把原本的一个文件拆分成多个。

比如说,现在有一个1M的app.js文件,

<script src="/static/app.js"></script> <!-- 1M -->

优化后变成这种形式

<script src="/static/app.js"></script> <!-- 500KB -->
<script src="/static/dll.js"></script> <!-- 524KB -->

这样做有三个优点

  • 异步加载文件,减少了页面的白屏时间
  • Dll打包以后是独立存在的,只要其包含的库没有修改,其hash也不会变化,因此线上的dll文件基本不需要随着频繁更新。
  • 如果多个项目使用了相同的依赖库,可以共用一个文件。

我们准备让优化后的文件结构如下
优化后的文件结构如下

  1. 我们首先需要在项目的build目录下创建一个 webpack.dll.config.js 文件。然后配置代码如下:
    const path = require('path')
    const webpack = require('webpack')
    module.exports = {
      entry: {
        vendor: [
          "vue-router",
          "vuex",
          "echarts"
          ...
        ]
      },
      output: {
        path: path.resolve('./dist/dll/'),
        // publicPath: '.',
        filename: '[name].dll.js',
        library: '[name]_library'
      },
      plugins: [
        new webpack.DllPlugin({
          path: path.resolve('./dist/dll/', '[name]-manifest.json'),
          name: '[name]_library'
        }),
        new webpack.optimize.UglifyJsPlugin({
          compress: {
            warnings: false
          }
        })
      ]
    }
  1. 然后在webpack.prod.conf.js使用
    const DllReferencePlugin = require('webpack/lib/DllReferencePlugin');
    module.exports = {
        plugins: [
            new webpack.DllReferencePlugin({
              manifest: require('../dist/dll/vendor-manifest.json')
            })
            ...
        ],
        ...
    }

3.在package.json中添加打包命令

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

4.在index.html中引入

    <script src="./dll/vendor.dll.js"></script>
2.3.3 二者的对比与选取
  • 打包效率

    • externals:每次更新第三方模块时,只需修改webpack的externals属性,然后在打包以后的文件中引入外链。
    • Dll:每次更新第三方模块时,需要修改webpack.dll.config.js,然后单独打包成dll。
    • 结论:externals更方便些
  • 项目的配置管理

    • externals:上文的三个步骤
    • Dll:修改配置文件、重新打包
    • 结论:Dll更方便
  • 页面效率

    • 都是资源并行引入,没有本质区别,只是同一域名下的资源有并发数限制,而使用externals方案,唯一担心的问题是cdn服务器宕机了,所以可以考虑部署到自己专门的资源服务器上。
  • 总结

    • 二者看似各有千秋,但是考虑到 【项目的配置管理】 这一项我们只需配置一次,所以在二者皆可的其概况下,我们优先选用externals。

其他优化方案(探索中,后续记录)

  • 公共组件上传npm

    在项目中作为第三方模块引入,然后通过dll打包引入页面,以减少打包时间。

  • HappyPack开启多进程任务

    因为nodeJs是单线程,非阻塞模型,所以可以开启多进程来提高效率,HappyPack就是利用这一点,可以大大缩短打包时间。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值