前言
前几天开会的时候,被老板质问为何打开网址等了8、9秒?当时我既然语塞,因为安卓和IOS打开也很慢,但不会像PC网页这么慢到接近10秒才打开。而且更要命的是服务器在新加坡,老板就是在新加坡打开这么慢,我在国内打开往往花了一分钟以上,最慢的时候达到3分40秒,这速度是要命的!因为3秒定律,过了3秒用户是没有耐心直接关闭,如果是国外的网址愿意多等一会,但也不可能愿意等这么久!
于是乎这周除了修复几个bug和新增了一些功能之外,我首要的任务就是优化网站加载!
当然以下优化都是建立在网速正常的情况下。
优化vue项目
第一步:增加页面加载动画
国际网站给多个国家使用,因为再怎么优化,首次进入都一定有几秒的等待时间,这个时候加一个炫酷的动画往往能使用户分心,不会过于焦躁,而网站在第一次加载之后,后面更新了也不需要再等待那么久,利用缓存基本能秒更新。
有兴趣的伙伴可以到我的github下载: https://github.com/13632756286/Loading-
第二步:vue-router 路由懒加载
见名知意,路由懒加载既是组件的延迟加载,通常vue的页面在运行后进入都会有一个默认的页面,而其他页面只有在点击后才需要加载出来。使用懒加载可以将页面中的资源划分为多份,从而减少第一次加载的时候耗时。
相关代码:
1. router/index.js
const Home = resolve => require(['@/views/Home/HomePage'], resolve); const Login = resolve => require(['@/views/Login/LoginPage'], resolve); const Error = resolve => require(['@/components/Error/Error'], resolve); const PrintPage = resolve => require(['@/views/Print/printPage'], resolve); const PayNow = resolve => require(['@/views/Print/PayNow'], resolve); const HomePage = resolve => require(['@/components/Home/homePage'], resolve); const router = new Router({ routes: [ { path: '/home', name: 'home', component:Home, children:[ { path: "",name: 'homePage',component: HomePage, props:true }, { path: "printPage",name: 'printPage',component: PrintPage, props:true }, { path: "payNow",name: 'payNow',component: PayNow, props:true } ], }, { path: '/login', name: 'login', component:Login, }, { path: '/register', component:Login, }, { path:'/*', component:Error } ] });
2. 父组件引入子组件时:
components: { // 组件的按需懒加载 navMenu: resolve => require(["@/components/Util/Menu/navMenu.vue"], resolve), TransactionsBox: resolve => require(["@/components/Home/transactions.vue"], resolve), topUp: resolve => require(["@/components/Home/topUp.vue"], resolve), DebitCard: resolve => require(["@/components/Home/debitCard.vue"], resolve), SettingsBox: resolve => require(["@/components/Home/settings.vue"], resolve), ProfileSelect: resolve => require(["@/components/Util/Select/profileSelect.vue"], resolve), headerBox: resolve => require(["@/components/Util/Head/header.vue"], resolve), AddCardDialog: resolve => require(['@/components/Util/CardBox/addCardDialog.vue'],resolve) }
3. 关闭vuecli 3默认开启的prefetch(预先加载模块)
首屏加载的时候,默认会把分割的十几个路由文件全部下载,提前获取用户未来可能访问的内容,在这里建议关闭,可以在vue.config.js中设置:
module.exports = { chainWebpack: config => { // 移除 prefetch插件 config.plugins.delete('prefetch') } }
或者修改其选项:
module.exports = { config.plugin('prefetch').tap(options => { options[0].fileBlacklist = options[0].fileBlacklist || []; options[0].fileBlacelist.push(/myasyncRoute(.)+?.js$/); return options; }) }
当prefetch插件被禁用时,你可以通过webpack的内联注释手动选定要提前获取的代码:
import(/* webpackPrefetch: true */ './someAsyncComponent.vue');
设置完毕之后,首屏就只会加载当前页面的路由组件了。
第三步:使用CDN减小代码体积加快请求速度
CDN的好处:
1.CDN采取的是就近原则下载,速度较快;
2.减少打包体积,这也意味着请求该网址的下载速度会更快;
相关代码
1. public/index.html
<script src="https://cdn.bootcss.com/vue/2.6.11/vue.min.js"></script> <link href="https://cdn.bootcss.com/element-ui/2.13.0/theme-chalk/index.css" rel="stylesheet"> <script src="https://cdn.bootcss.com/element-ui/2.13.0/index.js"></script> <script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script> <script src="https://cdn.bootcss.com/axios/0.19.2/axios.min.js"></script> <script src="https://cdn.bootcss.com/vue-i18n/8.17.0/vue-i18n.min.js"></script> <script src="https://cdn.bootcss.com/vuex/3.1.3/vuex.min.js"></script>
2. 删除main.js当中的相关代码,例:import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css';Vue.use(ElementUI); 等等,如果这些代码没有删除,那么打包的时候还是会自动在node_modules中引入下载,打包的体积还是会很大!
3. vue.config.js中设置禁止引入上述的包进行打包(如果没有vue.config.js就自己在根目录创建)
configureWebpack: config => { return { externals: { //externals 里的库不会被webpack打包 'echarts': 'echarts', 'vue': 'Vue', 'vue-router': 'VueRouter', 'ElementUI': 'ELEMENT', 'axios': 'axios', 'vue-resource': 'VueResource', 'vuex': 'Vuex', 'vue-i18n': 'VueI18n', "qs": "qs", "jquery": "$" }, } } }
第四步:去掉编译文件中map文件
在编译好后,我们会看到文件夹下有特别多的.map文件,这些文件主要是帮助我们线上调试代码,查看样式。所以为了避免部署包过大,通常都不生成这些文件。
在 config/index.js 文件中将productionSourceMap 的值设置为false. 再次打包就可以看到项目文件中已经没有map文件;
第五步--重点:开启gzip压缩(压缩率最高可达70%)
1. cnpm i compression-webpack-plugin 下载compression-webpack-plugin插件;
2. vue.config.js中添加下面的代码:
configureWebpack: config => { return { plugins: [ new CompressionPlugin({ filename: '[path].gz[query]', algorithm: 'gzip', // test: /.js$|.html$|.css/, //匹配文件名 test: /.(js|css|woff|ttf|json|txt|html|ico|svg)(?.*)?$/i, threshold: 10240, //对超过10k的数据进行压缩 minRatio: 0.8, // 压缩比小于这个才压缩 deleteOriginalAssets: false //是否删除源文件 }), ], // plugins: plugins, externals: { //externals 里的库不会被webpack打包 'echarts': 'echarts', 'vue': 'Vue', 'vue-router': 'VueRouter', 'ElementUI': 'ELEMENT', 'axios': 'axios', 'vue-resource': 'VueResource', 'vuex': 'Vuex', 'vue-i18n': 'VueI18n', "qs": "qs", "jquery": "$" }, } }
注意:有些浏览器并不支持.gz文件,因此deleteOriginalAssets: false,就会同时生成.js和.gz文件,只要通过浏览器请求头知道支持.gz文件,那么就自动使用.gz,否则使用.js。
3. 要使服务器返回.gz文件,还需要对服务器进行配置,根据Request Headers的Accept-Encoding标签进行鉴别,如果支持gzip就返回.gz文件,如果后端用SpringBoot ,还需要我们手动开启,在配置文件中添加两行:
server: compression: enabled: true mime-types: application/json,application/xml,text/html,text/plain,text/css,application/x-javascript
如果后端用的不是SpringBoot,那么可以参考这篇文章来进行配置: https://blog.csdn.net/qq_40999917/article/details/107936750
第六步: 使用阿里巴巴矢量图标库
我们使用elementUI图标时,往往不能满足项目的要求,有时候产品就要我们使用他们设计的logo,图标等,这个时候如果使用图片或者整合成雪碧图,其实都不太理想,像素可能不够清晰不说,定位、放大失真、加载速度慢或多次请求、同一类型图片颜色不一样就得两个图等等,以上缺点对于网站性能要求高的有时候是致命的!因此这里强烈推荐大家使用阿里巴巴矢量图标库:https://www.iconfont.cn/home/index?spm=a313x.7781069.1998910419.2
使用步骤一:新建项目
使用步骤二:上传.svg图片
如果上传失败先检查该svg是不是矢量图做的?如果是图片转为矢量图是不成功的,渐变的svg也是不行,可以看上面六个注意事项,如果都符合但是不成功,可以看我这篇文章: https://www.cnblogs.com/zxd66666/p/13425205.html
使用步骤三:
1. 在vue-cli项目中引入
2. 注意在iconfont.css加上下面的代码:
[class^="el-icon-bs"], [class*=" el-icon-bs"] { font-family:"fontFamily" !important; /* 以下内容参照第三方图标库本身的规则 */ font-size: 18px; font-style:normal; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
3. 在main.js中引入
// 引入icon import './assets/icon/iconfont.js' import "./assets/icon/iconfont.css";
4.使用方式
//如果用原本icon颜色 <svg class="icon svg-icon" aria-hidden="true"> <use href="#el-icon-bs-UnionPay"></use> </svg>
//如果想要自己设置颜色 <i class="el-icon-arrow-right"></i>
可以看到这里用法和原先elementUI的icon用法完全一致,我们可以设置font-size改变大小和color改变颜色,完全不会失真!
第七步:Vue SSR
解决首页js未加载完页面不出来的问题:
1. vue-server-renderer服务端渲染
2. prerender-spa-plugin客户端的静态页面生成
3. Nuxt.js 一个vue的服务端渲染框架,容易上手。
这里的思路无法在于解决vue-cli要在首页加载的时候把js都下载完之后渲染页面的问题,优先渲染页面,让用户不需要一直看空白页,像许多大型网站如京东、淘宝等都是一开始由服务器返回静态页面。参考文章: https://www.jianshu.com/p/0e882b8937a2
其实我个人是建议可以把public/index.html里面的加载动画写成首页或部分的样子先展示,等到window.onload()加载完毕之后,再移除该dom元素,这要比Vue SSR更好把控,且不需要劳烦后端的同事。
其他优化方式
1. 手动懒加载--较大的图片最后加载,一开始我们给一个空的地址,等到window.onload()之后再替换回去;
2. 预加载公用数据并放全局--像我公司项目中的卡列表,此时如果不做增删改那么列表数据是不会改变的,而渲染卡列表的子页面可能有好几个,此时我们可以在父组件页的时候就异步获取,然后用sessionStorage保存,如果执行了增删改的方法,成功的时候我们只需要修改本地数据,就无需再次请求后台接口了。但是注意如果用户可以通过其他渠道例如app增删改卡数据,此时还是建议用户每查看一次卡列表就获取一次,不然用户在浏览器打开页面,离开一会的时候在app删除了,回来继续查看发现卡列表没改变,就会引起不必要的投诉了,因此大家注意看项目而定;
3. 如果不是多个页面而仅仅是两三个页面使用的公用js、css,就在该相关页面单独引用,不要在main.js全局引入;
最终效果
可以看到了,压缩完之后js体积才351.36KB,快的时候加载速度3.09s,要知道服务器在新加坡,这种速度可以说是惊人的!而第二次再加载因为浏览器有缓存的功能,就只需要1s左右就可以加载完毕,Nice!
网站优化参考文章:
https://segmentfault.com/a/1190000019499007#articleHeader4
另外关于服务器优化方式参考文章:
http://www.hwsem.com/server/yunzj/201918076.html
https://www.jianshu.com/p/b358a91bdf2d
https://jingyan.baidu.com/article/6d704a13c1b3c268da51ca26.html