You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
varexpress=require('express')varapp=express()// 开启gzip压缩,如果你想关闭gzip,注释掉下面两行代码,重新执行`node server.js`varcompression=require('compression')app.use(compression())app.use(express.static('dist'))app.listen(3000,function(){console.log('server is runing on http://localhost:3000')})
如果你的项目没有做路由懒加载,那么你大可放心的按上面所说的去实践了。但如果你的项目里用了,你应该会看到webpackJsonp is not defined的报错。这个因为prerender-spa-plugin渲染静态页面时,也会将类似于<script src="/static/js/0.9231fc498af773fb2628.js" type="text/javascript" async charset="utf-8"></script>这样的异步script标签注入到生成的html的head标签内。这会导致它先于app.js,vendor.js,manifest.js(位于body底部)执行。(async只是不会阻塞后面的DOM解析,这并不意味这它最后执行)。而且当这些js加载完毕后,又会在head标签重复创建这个异步的script标签。虽然这个报错不会对程序造成影响,但是最好的方式,还是不要把这些异步组件直接渲染到最终的html中。好在prerender-spa-plugin提供了postProcess选项,可以在真正生成html文件之前做一次处理,这里我使用一个简单的正则表达式,将这些异步的script标签剔除。本分支已经使用了路由懒加载,你可以直接查看git历史,比对文件和base分支的变化来对你的项目进行相应调整。
前言
和很多小伙伴一样,我在开发
Vue
项目时也是基于官方vue-cli@2
的webpack
模版,但随着项目越做越大,依赖的第三方npm
包越来越多,构建之后的文件也会越来越大,尤其是vendor.js
,甚至会达到2M
左右。再加上又是单页应用,这就会导致在网速较慢或者服务器带宽有限的情况出现长时间的白屏。为了解决这个问题,我做了一些探索,在几乎不需要改动业务代码的情况下,找到了三种有明显效果的优化方案 ——CDN
+Gzip
+Prerender
。我把这些方法整理了一下,放在了 Github仓库 上,意图通过不同的分支来展示不同的优化方式,对Vue
项目性能的影响。你可以直接克隆下来试一试,也得益于有git
历史,你也可以很方便的查看具体的改动细节。下面我将通过一个简单的项目来展示这三种优化方案的效果。一、首先准备一个简单的项目
通过
vue-cli@2
的webpack
模版生成,只包含最基础的Vue
三件套 ————vue
、vue-router
、vuex
以及常用的element-ui
和axios
。拆分两个路由——“首页”和“通讯录”,通过axios
异步获取一个通讯录名单,并利用element-ui
的表格展示。直接build
,不做任何优化处理,以作参照。1.1 构建后文件说明:
app.css
: 压缩合并后的样式文件。app.js
:主要包含项目中的App.vue
、main.js
、router
、store
等业务代码。vendor.js
:主要包含项目依赖的诸如vuex
,axios
等第三方库的源码,这也是为什么这个文件如此之大的原因,下一步将探索如何优化这一块,毕竟随着项目的开发,依赖的库也能会越来越多。数字.js
:以0、1、2、3等数字开头的js
文件,这些文件是各个路由切分出的代码块,因为我拆分了两个路由,并做了路由懒加载,所以出现了0和1两个js
文件。mainfest.js
:mainfest
的英文有清单、名单的意思,该文件包含了加载和处理路由模块的逻辑1.2 禁用浏览器缓存,网速限定为
Fast 3G
下的Network
图(运行在本地的nginx
服务器上可以看到未经优化的
base
版本在Fast 3G
的网络下大概需要7秒多的时间才加载完毕二、CDN 优化
vue
、vue-router
、vuex
、element-ui
和axios
这五个库,全部改为通过CDN
链接获取。借助HtmlWebpackPlugin
,可以方便的使用循环语法在index.html
里插入js
和css
的CDN
链接。这里的CDN
大部分使用的 jsDelivr 提供的。build/webpack.base.conf.js
中添加如下代码,这使得在使用CDN
引入外部文件的情况下,依然可以在项目中使用import
的语法来引入这些第三方库,也就意味着你不需要改动项目的代码,这里的键名是import
的npm
包名,键值是该库暴露的全局变量。 webpack文档参考链接。npm
包,npm uninstall axios element-ui vue vue-router vuex
main.js
里element-ui
相关代码。具体细节可以查看
git
的历史记录2.1 比对添加 CDN 前后构建的文件:
优化后:
![](https://camo.githubusercontent.com/7b4e2ec1808e317c792088871976d298c7ae8f8cf4bcb1dcd8a6c6b501891d36/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031382f392f33302f313636323631303232663431383737363f773d3137393826683d38303026663d706e6726733d323537303933)
![](https://camo.githubusercontent.com/a20c4e573c7832b5745a9b92b501d8cc709fdfea7f381dac1740979df2b7e879/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031382f392f32392f313636323564336331636466613236373f773d3138393026683d38343626663d706e6726733d333137373635)
优化前:
可以看出:
app.css
: 因为不再通过import 'element-ui/lib/theme-chalk/index.css'
,而是直接通过CDN
链接的方式引入element-ui
样式,使得文件小到了bytes
级别,因为它现在仅包含少量的项目的css
。app.js
:几乎无变化,因为这里面主要还是自己业务的代码。vendor.js
:将5个依赖的js
全部转为CDN
链接后,已经小到了不足1KB
,其实里面已经没有任何第三方库了。数字.js
和mainfest.js
:这些文件本来就很小,变化几乎可以忽略。2.2 同样,禁用浏览器缓存,网速限定为
Fast 3G
下的Network
图(运行在本地的nginx
服务器上可以看出相同的网络环境下,加载从原来的7秒多,提速到现在的3秒多,提升非常明显。而且更重要的一点是原本的方式,所有
![](https://camo.githubusercontent.com/8d5399c6db6ca51895c4db7f4318a9c139da7b95b10bc3ea5afc82566059fe77/68747470733a2f2f757365722d676f6c642d63646e2e786974752e696f2f323031382f392f33302f313636323630663938613138356263613f773d3332373626683d3135363226663d706e6726733d353431323834)
的
js
和css
等静态资源都是请求的我们自己的nginx
服务器,而现在大部分的静态资源都请求的是第三方的CDN
资源,这不仅可以带来速度上的提升,在高并发的时候,这无疑大大降低的自己服务器的带宽压力,想象一下原来首屏900多KB的文件
现在仅剩20KB是请求自己服务器的!
三、Gzip 优化
使用
Gzip
两个明显的好处,一是可以减少存储空间,二是通过网络传输文件时,可以减少传输的时间。3.1 如何开启
gzip
压缩开启
gzip
的方式主要是通过修改服务器配置,以nginx
服务器为例,下图是,使用同一套代码,在仅改变服务器的gzip
开关状态的情况下的Network
对比图未开启
gzip
压缩:开启
gzip
压缩:开启
gzip
压缩后的响应头从上图可以明显看出开启
gzip
前后,文件大小有三四倍的差距,加载速度也从原来的7秒多,提升到3秒多附上
nginx
的配置方式3.2 前端能为gzip做点什么
我们都知道
config/index.js
里有一个productionGzip
的选项,那么它是做什么用的?我们尝试执行npm install --save-dev compression-webpack-plugin@1.x
,并把productionGzip
设置为true
,重新build
,放在nginx
服务器下,看看有什么区别:我们会发现构建之后的文件多了一些
js.gz
和css.gz
的文件,而且vendor.js
变得更小了,这其实是因为我们开启了nginx
的gzip_static on;
选项,如果
gzip_static
设置为on
,那么就会使用同名的.gz
文件,不会占用服务器的CPU资源去压缩。3.3 前端快速搭建基于
node
的gzip
服务无法搭建
nginx
环境的前端小伙伴也可以按如下步骤快速启动一个带gzip
的express
服务器npm i express compression
serve.js
,并粘贴如下代码node server.js
下图是
express
开启gzip
的响应头:四、Prerender 预渲染
大家都是知道:常见的
Vue
单页应用构建之后的index.html
只是一个包含根节点的空白页面,当所有需要的js
加载完毕之后,才会开始解析并创建vnode
,然后再渲染出真实的DOM
。当这些js
文件过大而网速又很慢或者出现意料之外的报错时,就会出现所谓的白屏,相信做Vue
开发的小伙伴们一定都遇到过这种情况。而且单页应用还有一个很大的弊端就是对SEO
很不友好。那么如何解决这些问题呢?——SSR
当然是很好的解决的方案,但这也意为着一定的学习成本和运维成本,而如果你已经有了一个现成的vue
单页应用,转向SSR
也并不是一个无缝的过程。那么预渲染就显得更加合适了。只需要安装一个webpack
的插件 + 一些简单的webpack
配置就可以解决上述的两个问题。4.1 如何将单页应用转为预渲染
router
设为history
模式,并相应的调整服务器配置,这并不复杂。npm i prerender-spa-plugin --save-dev
build/webpack.prod.conf.js
下添加如下配置(没有路由懒加载的情况)。config/index.js
里build
中的assetsPublicPath
字段设置为'/'
,这是因为当你使用预渲染时,路由组件会编译成相应文件夹下的index.html
,它会依赖static
目录下的文件,而如果使用相对路径则会导致依赖的路径错误,这也要求预渲染的项目最好是放在网站的根目录下(这个坑我已经在prerender-spa-plugin
仓库提过ISSUE
了,不过借助postProcess
,自己再写一个正则表达式,也能实现,如果你有这方面的需求,可以参考下面 路由懒加载带来的坑)。main.js
执行
npm run build
,你会发现,dist
目录和以往不太一样,不仅多了与指定路由同名的文件夹而且index.html
早已渲染好了静态页面。4.2 效果如何?
和之前一样,我们依然禁用缓存,将网速限定为
Fast 3G
(运行在本地的nginx
服务器上)。可以看到,在vendor.js
还没有加载完毕的时候(大概有700多kB,此时只加载了200多kB),页面已经完整的呈现出来了。事实上,只需要index.html
和app.css
加载完毕,页面的静态内容就可以很好的呈现了。预渲染对于这些有大量静态内容的页面,无疑是很好的选择。4.3 路由懒加载带来的坑
如果你的项目没有做路由懒加载,那么你大可放心的按上面所说的去实践了。但如果你的项目里用了,你应该会看到
webpackJsonp is not defined
的报错。这个因为prerender-spa-plugin
渲染静态页面时,也会将类似于<script src="/static/js/0.9231fc498af773fb2628.js" type="text/javascript" async charset="utf-8"></script>
这样的异步script
标签注入到生成的html
的head
标签内。这会导致它先于app.js
,vendor.js
,manifest.js
(位于body
底部)执行。(async
只是不会阻塞后面的DOM
解析,这并不意味这它最后执行)。而且当这些js
加载完毕后,又会在head
标签重复创建这个异步的script
标签。虽然这个报错不会对程序造成影响,但是最好的方式,还是不要把这些异步组件直接渲染到最终的html
中。好在prerender-spa-plugin
提供了postProcess
选项,可以在真正生成html
文件之前做一次处理,这里我使用一个简单的正则表达式,将这些异步的script
标签剔除。本分支已经使用了路由懒加载,你可以直接查看git
历史,比对文件和base
分支的变化来对你的项目进行相应调整。除了这种解决方案,还有两种不推荐的解决方案:
HtmlWebpackPlugin
的inject
字段设置为'head'
,这样app.js,vendor.js,manifest.js
就会插入到head
里,并在异步的script
标签上面。但由于普通的
script
是同步的,在他们全部加载完毕之前,页面是无法渲染的,也就违背了prerender
的初衷,而且你还需要对main.js
作如下修改,以确保Vue
在实例化的时候可以找到<div id="app"></div>
,并正确挂载。总结
虽然官方的脚手架已经提供很多开箱即用的优化,比如
css
压缩合并,js
压缩与模块化,小图片转base64
等等,但我们能做的还很多。我没有提及代码级别的优化细节,也是希望给大家提供一些可实践的方案。上述三种方案或多或少都会给你项目带来一些收益。优化也是一门玄学,可研究的东西很多。也希望其他小伙伴可以在评论区提供宝贵意见,或者直接向我的这个项目 vue-optimization 的base
分支提交PR
,好的方案我会采纳并整理。目前三种方案整合的最终结果我已经放在 master 分支下,你可以克隆下来并在此基础上开发你的项目。The text was updated successfully, but these errors were encountered: