Vue项目优化心得分享


 

开篇

  本该继续更新【探究Vue原理】系列文章的,但今天我怀着激动的心情打算和大家分享下有关Vue项目优化的一点心得。
  先说下我遇到的问题。手上的项目目前虽然只完成了三分之二,但已经有100多个页面,和20多个公共组件了。在调试环境下,代码大小高达20.4MB!build之后经过压缩为2.24M。要知道,我的项目中是不包含任何静态资源的,这是20多M是纯代码的大小。在服务器带宽并不高的情况下,我打开首页竟然需要1分半!项目优化刻不容缓,下面分享我的实际优化步骤。

 
 

环境

我的项目是使用cli3的默认配置创建的。
Vue版本为2.6.10
其他插件版本如下:
@vue/cli-plugin-babel": “^3.8.0”,
@vue/cli-plugin-eslint": “^3.8.0”,
@vue/cli-service": “^3.8.0”,

 
 

异步组件

  首先确定问题出在哪里。(之后的操作全部是在调试环境下进行的)
  使用Chrome打开我的网站,按12调出控制台,并在最顶部选项卡中选择Network。F5刷新首页之后,Network会记录各个资源的基本信息与占用带宽的情况,在这一步我发现app.js文件有足足20MB大小。那么问题很明显了,几乎全部代码都在app.js这一个资源包里面,因此我首先要做的事情就是对各个组件分开打包。
  我的打包工具是默认的webpack,浏览Vue的文档之后我发现异步组件可以很好的满足分别打包的需求。异步的用法很简单,我们只需要将项目中某些地方的import xxx from xxx 改为 ()=>{import(‘xxxx’)}即可。
 

Q某些地方究竟指哪里?什么地方需要使用异步组件?

  项目中但凡是使用import xxx from xxx 这个语法引入的代码都会被webpack编译到app.js这个包里面。而app.js这个包在我们打开网站的那瞬间就会被’第一个’下载,因此,对于我的项目来说,只有项目的基本框架首页才需要使用import xxx from xxx来引入,其他所有资源都应该使用()=>{import('xxxx')}

   以我的项目为例,下面截图是我项目的home页。我在打开当前大家看到的这个页面的时候,除了顶部工具条左侧导航条首页以及一些公共基础组件之外不再需要加载其他任何一个组件了。而将来要创建的每一个tab都应该是动态加载的。也就是说,只有当用户点击菜单按钮之后对应页面的组件才应该被下载。说白了,就是上述那几个组件应该被放在app.js中,而其他页面应该每个页面单独存放在一个包中(一个包对应一个js文件)。
在这里插入图片描述

具体实现方式很简单,顶部工具条和菜单这些组件仍然使用import xxx from xxx引入,而其他组件我是利用一个Map来管理的,如下。
在这里插入图片描述
比如我点击了权限管理这个菜单项,那就给tab管理器传递一个名字account-show,然后让标签管理器根据account-show这个名字去Map里面找到account-show对应的import

 () => import(
            /* webpackChunkName: 'account-show' */
            "../../pages/authority-manage/AccountManage.vue"), //权限管理-账号管理(账号列表)

看到这句代码,浏览器就会去服务器下载名为account-show的js文件了,js文件内放的当然就是页面的全部代码。读者应该注意到了,import语句内有一个注释

/* webpackChunkName: 'account-show' */

这其实并不是一个普通的注释,而是魔法注释!,官方确实是这么叫的,这行注释会被webpack作为配置信息正常解析,以我代码中的webpackChunkName: 'account-show'为例解释一下,刚刚也说了,每一个使用() => import(xxx)引入的组件都会被打包成单独的js文件,那这个文件叫什么名字呢?默认情况下这个文件是以数字命名的,例如 64.js 。而webpackChunkName: 'account-show'这句注释实际上是告诉我webpack,‘我不想要那么丑的名字,我希望这个js文件叫account-show!’,webpack就不会再以64.js对这个包命名了,而是用account-show.js
那么,实际上还有很多其他的注释可以用,具体可以参考官网文档,注意,多个注释可以写在一起,但要用分隔,像是这样。/* webpackMode: "lazy-once", webpackChunkName: "all-i18n-data" */
OK,做完这一步之后,我们重新运行下项目,然后去浏览器按F5刷新一下,我以为一切都会像想象中那样美好,可惜并不是。

之前我们所有代码都被放app.js里面,然后一起下载对不对?现在倒好,虽然我分出了很多包,但是当我打开主页的一瞬间,这些包像瀑布一样被下载下来了。对,理想中我是在点击菜单之后才会下载对应包,可现在他们却在我没点击之前就被下载了。问题出在哪?请继续阅读下一部分内容。

 
 

prefetch(预拉取)

幸亏chrome的控制台足够强大,能告诉我们这些js包究竟是谁下载的。观察Initiator列。
在这里插入图片描述
这里显示所有包都是index.html下载的,但很奇怪,我们写Vue一般很少会去动index.html这个文件,我敢打赌我没在这个页面里面引入这些包!
点开index.html之后我发现index.html中多了这么一行标签,很明显这是webpack打包时候写进去的。
在这里插入图片描述
很明显, 浏览器会提前下载js包就是因为这个link标签中的rel中指定了prefetch。
解释下什么是prefetch。
  prefetch是浏览器的一种优化策略,当一个资源被标注为prefetch实际上是告诉浏览器 “这个资源我本次路由用不到,但是会在将来某个路由中使用到”,浏览器接收到这个要求后会将这个资源标记为低优先级的,并利用空闲资源来下载它,资源被下载下来之后会被存到浏览器的缓存中,等将来用户请求这个js包的时候,浏览器会优先从缓存中取已经下载好的包。这样就提升了用户体验。如果还没理解的话,试想一个情景,页面上有个登录按钮,用户点击按钮会跳转到个人中心。整个流程中,从用户看到这个按钮到输入完表单数据然后去真正点击这个按钮之间浏览器是有一大段空闲时间,如果个人中心页面的代码被标记为prefetch,那浏览器就可以在用户填写表单的时候去提前下载个人中心的代码,这样用户登陆后无序等待立刻就可以看到个人中心的页面。用户体验直线提升。
  这里有些地方要注意,首先,空闲这个定义比较模糊,我刚刚也说了,实际上我在打开主页的时候其他页面的包也被下载了,并且还是在我主页的一些小图标被下载之前。不过官方也说了,如果当前路由有资源被开始加载的话,这些异步资源会暂停下载,等到当前路由资源被全部下载完毕,浏览器再从缓存中去除之前下载了一半的异步资源然后继续下载。所以我不清楚是不是虽然其他组件先于首页的图标被加载了,但中途暂停过。这个我没进行过测试。读者可以测试一下。

 
 

perload(预加载)

既然提到了prefetch就得顺便再提一下perload,资源被标记为perload实际上是告诉浏览器“这个资源非常重要,当前路由就要使用”,浏览器收到这个请求之后,就会将这个资源和当前路由的资源并行加载。所以其实 perload与prefetch的区别就是,对于被标记为prefetch的资源,浏览器是在当前页面资源加载完毕后才去加载它的,说白了就是不会因为加载这个资源而影响到当前页面资源的加载。而 perload则不同,由于它是和当前页面的资源并行加载的,因此它会占一部分带宽,很有可能会拖慢当前页面资源的下载,造成当前页面响应时间增加。因此使用perload需要小心,稍有不慎可能会损害性能,起到反作用。

 
 

使用率不高并且体积大的组件单独引入

到目前为止我做了很多事,然而我项目的app.js包只减少到了17.8M,仍然非常大。经过排查我发现我项目中使用到的video.js插件占用了8M的体积,而这个js文件是我在man.js中引入的。可实际上我只在很少的页面中使用到了这个插件,于是我将插件封装为基础组件。最重要的是,一定要在使用到这个组件的其他组件内利用import引入它。像是这样:
在其他组件的components属性内这么写即可。

components: {
    BaseVideoPlayer: () =>
      import(
        /* webpackChunkName: 'BaseVideoPlayer' */ "../../src/components/TheVideoPlayer.vue"
      )
  },

 
 

按需引入

我在项目中使用到了elementui,而我是在main.js文件的顶部一次性引入了所有组件的代码的,这样做虽然省了不少事,可也引入了不少无用代码,白白增加了我们代码的体积。要想实现按需引入十分简单,只需要按照官方文档的说明一步步去做就可以了。
只是这里有两点需要注意。

  1. 官方文档中提到需要修改.babelrc文件,而cli3使用默认配置创建的项目是没有这个文件的。我们只需要在项目根目录下创建一个名为.babelrc的文件即可,注意名字前面的.
  2. 官方给出的配置文件写法有些过时了,我们自己在写配置文件的时候不需要写presets属性。正确写法如下
{
  "plugins": [
    [
      "component",
      {
        "libraryName": "element-ui",
        "styleLibraryName": "theme-chalk"
      }
    ]
  ]
}

 
 

将第三方的公共css放到index.html的标签内

这么做有几个好处。

  1. 浏览器会缓存从cdn下载下来的资源,并且这个资源是全局共享的。说白了就是,如果别人网站link了和你一样的css,用户先浏览了那个网站并下载了css,那么这个css会被浏览器放到缓存中,当用户再去浏览你的网站的时候就不用重复下载一模一样的css文件了,这就提高了你页面的加载速度。
  2. 由于把css从我们代码里面拿出去了,因此减少了我们app.js包的体积,虽然减少的不多吧。
  3. 即使用户的缓存中并没有你需要的css,浏览器也会将你需要的css与app.js并行地去下载,这样做充分利用了网络带宽和计算机资源,提高了页面加载速度。

如果你希望使用cdn上面资源而官方又没有在文档中提供的话,可以在这里查一下对应的资源地址。但是要注意,cdn服务器有被关闭的可能性,大家尽量使用国外cdn,或者如果官方有提供地址的话,用官方的最好,比如我项目中用到的element-ui就提供了官方的css地址,因此我直接把它写到head中即可,像这样:<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">

 
 

参考

https://www.cnblogs.com/powertoolsteam/p/10873677.html Webpack 4教程
https://msd.misuland.com/pd/3223833238703182918 Webpack 4教程
https://www.cnblogs.com/wjunwei/p/9242142.html
https://cn.vuejs.org/v2/guide/components-dynamic-async.html#%E5%BC%82%E6%AD%A5%E7%BB%84%E4%BB%B6
https://segmentfault.com/a/1190000012138052

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值