Vue项目的一次性能优化

1,923 阅读9分钟

当你开发的前端项目好不容易部署上线,但却发现首次访问的时候异常的慢,用户会不会在等待几秒后就觉得这个站点是不是有问题?当然会,然后用户会关闭这个页面,吐槽一句:这什么网站,访问不了啊。但其实不是访问不了,而是需要用户再等一秒到几秒,可以几乎没人愿意去等待。

对于前端项目,前端性能优化是必不可少的;但好在时至今日已经有了不少好的工具可以帮助我们实现这点了,比如各种构建工具:wenpack、gulp、grunt等,同时各大前端框架的官方脚手架也已经默默地做了不少性能优化上的工作了。

项目背景

大概2个月前,我上线了我的个人博客,整个项目包括:基于next.js开发的博客前端、基于react开发的博客管理后台spa、基于egg.js开发的中台接口。后来我又用vue.js开发了博客前台的vue版本(当然是spa),我打算基于我更熟悉的vue来做性能优化的尝试。

我的个人服务器配置:2M带宽、win2008Server。 博客前端vue版本问题:首屏加载很慢,大概在4~8s左右。 接口访问速度:部分接口需要4s左右才能请求到(此处不做接口优化)。

很显然,2M的带宽导致了前端资源加载慢,接口请求慢,但是这个改变不了(心疼票子)。所以,我需要从项目本身做优化。

开干

在我实际做优化的时候,我并没有去分析,所以导致我有点像无头苍蝇花费了不少时间。作为总结性的文章,这里我来根据项目来分析一下。 首先,这个项目是一个基于vue.js开发spa单页应用),这意味着它会在首屏加载绝大部分资源,导致首屏加载慢,但同时打开后续页面的时候会很快。 通过chrome可以很容易的看到,页面首次加载的时候会先请求以下的文件:

首屏图片

很显然,通过vue脚手架打包后项目里面的各种依赖都被打包到这2个文件里面了,当时从服务器请求它们压缩后的大小在400多kb,请求耗时4-8s。同时,在请求完成这2个文件后才回去请求剩余的资源。 这时候有2个思路:

  1. 把它们拆分成多个文件,异步下载多个文件小文件会更快;
  2. 通过cdn来加载项目里面使用到的依赖;

最后我的做法是,通过cdn加载项目里面使用到的资源(第一种做法没有实现成功)。在此之前,我通过在vue.config.js配置了插件BundleAanlyzerPlugin来分析打包后各个资源的情况;当然,vuecli3/vuecli4中都可以通过命令vue ui开启意图图形化界面 ,通过图形化界面打包的时候,通过界面里的仪表盘也可以看到各种资源打包后的大小以及加载速度。

vue图形化假面

通过BundleAanlyzerPlugin生成的分析界面或者vue ui生成的图形化界面都能找到项目里面比较大的依赖项,把这些依赖项通过cdn引入就可以解决问题。 具体来说,需要在配置文件里加上如下配置:

externals: {
  'vue': 'Vue',
  'axios': 'axios',
  'marked': 'marked',
  'vue-router': 'VueRouter',
  'ant-design-vue': 'antd',
  'moment': 'moment',
  'highlight.js': 'hljs'
}

其中前面的是依赖包名,后面是引入后使用的对象名。上面的依赖中moment是被ant-design-vue依赖的,所以尽管代码里面没使用到moment也需要把它配置到cdn里。 首先,我们要找到一些提供公共cdn服务的网站:这里使用的是 bootcdn.cn 。但是bootcdn.cn没有提供最新版本的ant-design-vue(此处是ant-design-vue@1.6.2)的cdn资源,所以我又通过了另一个提供公共cdn的网站 jsdelivr.com 获取了ant-design-vue的cdn资源。

bootcdn.cn 只需要通过搜索相关npm依赖名称即可,它会列出相关的主要资源。而jsdelivr.com 列出整个依赖包的所有资源并统一提供 cdn 服务,所以需要从找到 ant-design-vue 的主要依赖文件:位于dist目录下的antd.min.jsantd.min.css (我也是第一次使用,算是一个小插曲)。

取到各个依赖的cdn资源后(有js和css),我们需要在项目静态资源中引入,在public/index.html中引入,需要注意的是,要先引入vue的cdn资源,因为后面的依赖都是基于vue的,如果后引入可能导致依赖不生效。 其次,项目中通过import引入的相关依赖的代码也需要去掉,比如为上面ant-design-vue的按需加载的代码也没有用处了。

通过cdn引入后,可以通过外部提供的cdn资源快速地获取相关依赖,不在占用服务器的带宽资源,项目工程里的依赖打包后也变得很小:

cdn引入后资源加载截图

原本需要4-8s加载完毕的依赖资源在1s内就加载完毕了。可以看到上面30.7kb的资源从我的服务器上需要800ms才能加载完毕,通过cdn34kb的资源只需要53ms即可加载完毕。首屏加载速度得到了解决,至少首页白屏的时间大大缩短;接下来就需要处理项目里图片资源的处理了。

如果所有的资源都可以通过cdn加那自然是最好的,但是如果不能呢?首先是某些资源可能不允许这么做(客户不允许),其次是这些服务可能收费。网站上的图片可以通过七牛云等提供的图片cdn加速服务,有的服务商收费,也有免费的,这里请大家自己去尝试。 这里我们尝试在代码层次去做优化。我的博客中,首页会加载2张网站广告资源的图片(每张大小在240kb左右),通过我的服务器加载速度可想而知。由于图片是项目外部的网络资源,通过构建手段干涉不到,所以我们只能在代码层面去做优化。我这里使用了依赖vue-lazyload,通过它实现了图片的懒加载。 懒加载:当资源在浏览器可视区域的时候才进行加载,也就是延时加载资源,降低服务器压力。

在main.js以插件的方式全局注册vue-lazyload,然后就可以在全局使用它提供v-lazyload等指令,注册的代码为:

Vue.use(VueLazyload, {
  preLoad: 1.3,
  error: require('./assets/images/404.png'), // public目录下的可以不用require引入
  loading: require('./assets/images/lazyloading.gif'), // public目录下的可以不用require引入
  attempt: 1
})

在配置中,我配置了图片加载中的loading图片和加载失败的替代图片,这些需要开发者自己准备。 此处注意,如果开发者使用了一些处理图片的相关webpack插件可能导致loading和error图片加载不出来,比如:把图片会被转换成base64处理的。

vue-lazyload是国人开发的依赖库,功能强大,用法也比较多。这里简单举例下基本用法:

//  在img标签中使用
<img v-lazy="..." />
// 作为背景图片使用
<div v-lazy:backgound-image="..."> </div>

更多相关用法,一定要参考官方文档:vue-lazyload

最后我再啰嗦一句,如果接口请求比较慢,同时图片一开始就位于可视区域,这时候图片还没有开始被加载(接口也没有请求完毕)通过v-lazyload处理的图片会显示error的图片,在接口请求完毕后才会再次加载图片;这显然不符合预期,这时候就不适合使用v-lazyload

当然项目里的静态图片资源也可以通过webpack相关的插件或者loader进行处理,我这里用到了imagemin-webpack-plugin,它能够在项目构建的时候对图片进行处理,通过降低图片的质量减小图片资源的体积。我在项目中简单地使用了一下:

const ImageminPlugin = require('imagemin-webpack-plugin').default
// 配置在webpack的plugins中
plugins: [
  new ImageminPlugin({
    test: /\.(gif|png|jpe?g|svg)$/i,
    disable: false, // Disable during development
    pngquant: {
      quality: '80-90'
    }
  })
]

注意,当同时使用其他处理图片的插件时,应当把该插件置于其他处理图片的插件之后。

更多详细用法和配置,请参考官方文档:imagemin-webpack-plugin

其实我在修改这些配置的时候,也耗费了一些时间在其他相关处理上,接下来我有一些个人相关的教训,大家可以参考。 在项目中我使用了sass来编写样式代码,而处理sass相关的依赖库是node-sass。 其实能够处理sass编译问题的依赖有2个,一个是node-sass,一个是dart-sass。 尽管node-sass使用起来有很多问题,但是我选择它的原因有2:

  1. dart-sass的编译速度在dart中是很快的,但是在node.js中却要慢很多,比node-sass更慢;
  2. dart-sass,在 npmjs.com 上它的名字为sass,但是上一次提交是3年前,而上一次提交在一个月前,官方维护得比较勤;

但是使用node-sass也很麻烦,首先是安装上的问题,推荐使用cnpm。其次是它对nodejs的版本有要求,这一点一定要注意:

NodeJS Minimum node-sass version Node Module
Node 14 4.14+ 83
Node 13 4.13+ 79
Node 12 4.12+ 72
Node 11 4.10+ 67
Node 10 4.9+ 64
Node 8 4.5.3+ 57

所以如果本地的node.js版本和服务器node.js版本不匹配将不能安装成功。我的开发电脑是win10,所以不受影响,但是服务器是win2008 server,高版本的nodejs在这个服务器上不能安装。当然,Linux服务器不受影响。

####总结

由于我的项目是通过vue官方脚手架vue-cli最新版本搭建的,vue-cli默认做了许多基于webpack4的配置优化,同时webpack4本身也默认提供了很多优化能力,这些都无需个人配置;所以我个人能力范围能做的优化只有目前这些。 总结起来也就3个:

  1. 通过cdn资源替代依赖包,减小打包后的依赖包大小;
  2. 通过vue-lazy懒加载网络图片;
  3. 通过imagemin-webpack-plugin压缩项目中图片资源;

当然前面提到,网络图片也可以通过服务器厂商提供的cdn加速服务去处理。

仰望天空,脚踏实地;大道至简,不忘初心。知识在于一点点积累,大神也需要一砖一瓦的堆砌,要想在任何一个技术领域成为顶尖的人物,坚持和积累都是必须的,愿你我共勉。