Vue 项目首次加载速度优化

vue-cli 版本2.9.6

优化手段:

CDN 加载静态资源
路由按需加载
服务端配置nginx 开启gzip 压缩

前段时间接到公司任务。项目首次加载速度特别慢,首次加载要10多秒才能加载完。这个时间对于一个正常用户绝对是无法忍受的。于是开始网上查阅vue 相关速度优化资料。最终经过三天时间的摸索改造,通过使用CDN静态资源加载,重构路由模块代码,完成按需加载。最终达到生产环境首次加载平均速度1-2s 左右加载完成。4g 网络下3-4秒打开。这期间发现了很多坑,也学习到了很多东西,而且在网上找了很多资料发现都是复制黏贴的,千篇一律写的详细的也不是很多。决定更要整理一下。

项目之所以这么慢,也算是历史遗留问题了。因为来到这个公司的时候项目已经做了一部分,整个项目采用的 vue + vuex + vue-router + iview 技术栈。听同事说这个项目是从github 上找的 admin iview 下载下来改了改(那时候 admin iview 还没有收费)就直接用了(确实省事儿)。但是这个项目的配置坑还是挺多的。

首先来看下项目优化前的加载速度

在这里插入图片描述
这还是开启nginx 的gzip 压缩功能后的文件体积(没有开启gzip压缩的话体积会更大,具体的大小当时忘记截图了)。比较大的文件有5个:
app.css 这个css 文件是打包时抽取的所有公共样式文件
echarts.js 这个很熟悉eacharts 图表插件功能很强大,但是包也不小,所以打包的时候webpack 配置里给单独打包了一份出来
iview-area.js 省市县三级联动插件,因为所有的数据都一起打包到了里边,这个插件包也是很大
vendor.js 第三方插件打包后的文件
app.js 项目里自己写的所有代码的合集

而且项目中有很多的图片,当时记得整个项目打包完后,打成压缩包大概有 8.2M 。而且项目很大,做到现在大概有 80 多个页面。

当时就觉得奇怪打包后app.js 特别大,而且生成js 文件也就10来个,但是根据我之前配置react 项目的经验来看,如果路由是按需加载的话,每个路由对应的文件打包后都会生成一个单独的js 文件,等访问到那个路由的时候才会在 head 标签内插入对应的js 文件。因为页面都分开打包了,最终打包生成的app.js 文件也不会太大。这才是正常的按需加载。
所以最初的判断是路由没有按需加载,都打包到app.js 里去了

然后先去看了下项目中的路由文件,当时的源代码的实现过程大概是这样的
通过 require.context() 方法动态来实现路由加载:

 const routes = [
    { path: '*', redirect: '/index' }
  ];
 importPages(require.context('./views', true, /\.vue$/,'lazy'))
 function importPages (r) {
    r.keys().forEach(key => {
      routes.push({ path: (key.split('.'))[1], component: ()=>r(key)})
    });
  }

网上也有一些使用 require.context() 方法动态添加路由的文章比如:
戳这里查看

注意:这里有一个坑

这个方法确实很方便,动态创建路由,不用每次去手动添加路由,根据路由规则直接写一个方法,然后去添加页面会自动读取到对应的文件。刚开始加入这个项目的时候看到这个方法我也在想确实挺方便,但是后来打包的时候每个路由对应的页面都不能单独拎出来自己生成一个文件,后来经过在网上查找相关资料,发现webpack 文档中有一段话:
在这里插入图片描述
英文不太好翻译过来大概意思就是: 这里的关键字是静态的。普通import语句不能在其他逻辑中动态使用或包含变量
webpack 官方链接
还有一段提示:
在这里插入图片描述
结论:import或者require里的路径必须是个静态的字符串,不然webpack识别不到,因为webpack打包的时候是静态分析,不是动态执行的,获取不到变量的值。因此我又花费几个小时去把路由的逻辑全部重构了一遍,全部静态引入页面,
代码如下:

  {
    path: '/',
    name: 'index',
    component: () => import(/* webpackChunkName: 'index' */ '@/views/index/index.vue'),
    meta: { unlimited: true, type: 'index' }
  },
  {
    path: '/login',
    name: 'login',
    component: () => import(/* webpackChunkName: 'login' */ '@/views/common/login.vue'),
    meta: { unlimited: true }
  },
  {
    path: '/register',
    name: 'register',
    component: () => import(/* webpackChunkName: 'register' */ '@/views/common/register.vue')
  },

这里的 /* webpackChunkName: ‘index’ */ 是这个文件打包以后生成的文件名,

  output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[name].[chunkhash].js')
  },

webpack output 对象中的 chunkFilename 把原来的id 改成name 就会自动读取到 路由里的 webpackChunkName 对应的名字

然后重新打包发现每个路由都生成了对应的单独的js 文件,并且app.js 小了很多。然后谷歌浏览器的审查元素中观察,只有路由被加载了,head 标签中才会被插入对应的js 文件。

这部分的参考链接

https://www.jb51.net/article/143925.htm
https://segmentfault.com/q/1010000015123542

在重复一次:require.context() 这个方法确实很方便,但是如果要做路由懒加载的话还是老老实实的去手写路由。

前边app.js 文件包过大的问题解决了, 还有个 vendor.js 第三方资源包。
这个包其实就好搞了,因为这个包里主要打包的是第三方静态资源包,那我们就把这些资源包单独拿出来,通过CDN 引入页面,这个包自然就小了。

一、先找到CDN 静态资源包

如果公司允许的话,也可以购买自己的CDN 然后把项目中的静态资源放上去使用,充钱的肯定比免费的要好用。

这里推荐两个CND 资源
https://www.bootcdn.cn/
https://unpkg.com/

经过使用测试发现,第一个网址上的CDN 资源加载要比第二个快,但是第一个网址上托管的CDN 并不是太多,有一些第三方插件包找不到。
第二个页面开头有一句话:unpkg是npm上所有内容的快速全球内容交付网络。也就是说所有的npm 包在这个网站上都能找到,使用方法也很简单,比如我们要找到vuex 的静态资源包:https://unpkg.com/vuex@3.1.1/ 在网址上输入package 资源名称 @ 加版本号 加 / 回车,就会自动跳出对应的静态资源,然后根据需求引入对应的文件即可。

二、引入CDN 资源

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>document</title>
	<link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/iview/3.4.2/styles/iview.css">
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
    <script type="text/javascript" src="https://cdn.bootcss.com/vue/2.5.20/vue.min.js"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/iview/3.4.2/iview.min.js"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/echarts/4.2.1/echarts.min.js"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/vue-router/3.0.1/vue-router.min.js"></script>
    <script type="text/javascript" src="https://cdn.bootcss.com/vuex/3.1.1/vuex.min.js"></script>
    <script type="text/javascript" src="https://unpkg.com/vuescroll@4.12.1/dist/vuescroll-native.min.js"></script>
    <script type="text/javascript" src="https://unpkg.com/iview-area@1.6.0/dist/iview-area.js" async="async"></script>
  </body>
</html>
注意:如果是css样式资源,要放到head标签内, js 文件放到 反斜杠body 标签上边。有利于加载速度。页面显示的更快。

三、修改webpack 配置

在webpack.base.conf.js 文件中添加如下代码

  externals: {
    'vue': 'Vue',
    'vuex': 'Vuex',
    'vue-router': 'VueRouter',
    'iview': 'iView',
    'iview-area': 'iviewArea',
    'echarts': 'echarts',
    'axios':'axios',
    'vuescroll': 'Vuescroll'
  },
  output: {
	......
  }

externals 对象的作用官方给出的解释:

webpack 中的 externals 配置提供了不从 bundle 中引用依赖的方式。解决的是,所创建的 bundle 依赖于那些存在于用户环境(consumer environment)中的依赖。

怎么理解呢,意思是如果需要引用一个库,但是又不想让webpack打包(减少打包的时间),并且又不影响我们在程序中以CMD、AMD或者window/global全局等方式进行使用(一般都以import方式引用使用),那就可以通过配置externals。

也可以查看这篇文章
externals 官方链接

注意:externals对象中的value值 不是随便写的,我们要看下文件中对外暴露的对象名称。

然后在运行打包命令看下,vendor.js 文件包是不是比之前小了很多。

三、开启gzip 压缩

如果项目已经配置了nginx 并且开启了gzip 压缩,前端打包就没必要在开启了。
可以查看加载的资源中
在这里插入图片描述
Response Headers 下的content-encoding: 如果包含gizp 的话说明已经开启gzip 压缩。

前端开启的话先打开 config/index.js 文件:
在这里插入图片描述
然后先安装

npm install --save-dev compression-webpack-plugin

然后把productionGzip 改成true 就可以了

productionGzip: true

图片中的 productionSourceMap 如果为true 的话,也要改成false ,这个属性是用来控制打包的时候生成map 文件的。

最后一点要说的一点,vue-cli2 webpack 中配置了 extract-text-webpack-plugin 这个插件作用是:

它会将所有 required 的 *.css 模块抽取到分离的 CSS 文件。 所以你的样式将不会内联到 JS bundle,而是在一个单独的 CSS 文件。如果你的样式文件很大,这样会提速,因为 CSS bundle 和 JS bundle 是平行加载的

但是如果你的项目太大,抽取到的app.css 文件包太大的话单独加载这个文件也是很影响速度的,可以尝试关掉抽取css 功能
在webpack.prod.conf.js 文件中把allChunks 改成false 重新打包app.css 文件将会消失,样式会一起打包到js 文件中。

    new ExtractTextPlugin({
      filename: utils.assetsPath('css/[name].[contenthash].css'),
      // Setting the following option to `false` will not extract CSS from codesplit chunks.
      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 
      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
      // 把allChunks 改成false
      allChunks: false,
    }),

四、图片压缩

这里推荐一个很好的图片压缩网站,可以把项目中所有的图片用这个工具压缩一遍。

https://tinypng.com/

五、vue-cli3

这期间我把现有项目配置升级到了vue-cli3,具体步骤这里不再多说,基本上和vue-cli2 的配置区别不大。因为vue-cli3 内部封装的是 webpack4, vue-cli2 中封装的是webpack3。 众所周知,webpack4 无论是从打包速度还是代码压缩拆包方面都比webpack3 优秀很多,所以才尝试了下,效果确实不错,webpack4 打包后 会生成 对应的单独的js 和css 文件,而且事实证明 webpack4 打包后的代码 比webpack3 打包后的代码在浏览器运行的更快,更流畅。如果是新开的项目,强烈建议使用vue-cli3 来创建。由于我们项目是 老项目,升级以后有好多配置不兼容 和eslint 规则不匹配问题,怕影响开发中项目的稳定性和代码规范性等问题,最终放弃了升级到vue-cli3。

六、关于一些服务器带宽的知识

http://www.west.cn/docs/39126.html

到此为止这篇博客就写完了,如果有写的不对的地方,或者其他同学有更好的方法欢迎指正交流。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值