后台管理系统焕然一新的(上) -性能优化__Vue.js
发布于 3 年前 作者 banyungong 1754 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

原文地址 版权地址在这,我看过的都要摘抄一遍

前言

在这个项目中你有遇到什么技术难点,你是怎么解决的?

不能做 让面试官认为你只是一个API Caller

是因为:

   而很多人会说我的项目不大,并没有什么难点,或者说并不算难点,只能说是一些坑,只要google一下就能解决,实在不行请教我同事,这些问题并没有困扰我很久。其实我也遇到过相同的情况,和面试官说如何通过搜索引擎解决这些坑的吧不太好,让面试官认为你只是一个API Caller,但是又没有什么值得一谈的项目难点

你可以

试着封装几个常用的组件,同时尝试分析项目的性能瓶颈,寻找一些优化的方案,同样也能让面试官对你有一个整体的了解

接下来, 作者会分享在我目前公司的项目里,是如何在满足业务需求的基础上,让整个系统焕然一新的过程.

因为代码高度的耦合,所以我准备把整个项目重写

得益于花裤衩的vue-element-admin,我决定新开一个工程把之前的代码全部重写

项目结构

项目目录结构优化

├─api                                 //api接口
├─assets                              //项目运行时使用到的图片和静态资源
├─components                          //组件
│  ├─BaseEllipsis                     //业务组件 (Base开头都是全局组件)
│  ├─BasePagination                   //分页器组件   
│  ├─BaseIcon                         //svg图标组件
│  ├─BaseToggle                       //业务组件
│  ├─BaseTable                        //表格组件
│  ├─FormPanel                        //业务组件(Form开头是围绕表单相关的小组件)
│  ├─TableOptions                     //业务组件(Table开头是围绕表格相关的小组件)
│  ├─TheBreadcrumb                    //面包屑组件(The开头是每个页面组件只会引入一次的无状态组件)
|  ├─TheSidebar                       //侧边栏组件
│  ├─TransitionSildeDown              //业务组件(Transition开头是动画组件)
│  └─index.js                         //全局组件自动注册的脚本
│  
├─directives                          //自定义指令
├─element                             //elementui
├─errorLog                            //错误捕获
├─filters                             //全局过滤器
├─icons                               //svg图标存放文件夹
├─interface                           //TypeScript接口
├─mixins                              //局部混入
├─router                              //vue-router
│  ├─modules                      
│  └─index.js           
├─store                                //vuex
│  ├─modules                      
│  └─index.js                      
├─style                                //全局样式/局部页面可复用的样式
├─util                                //公共的模块(axios,cookie封装,工具函数)
├─vendor                              //类库文件
└─views                               //页面组件(所有给用户显示的页面)

一个良好的项目分层在业务迭代的时候能够快速找到对应的模块进行修改,而不是在茫茫的代码海中找到其中的某一行代码

性能优化

1.从缩短打包角度出发.

2.从4个方面入手,让系统"步履如飞"

网络请求相关 构建相关 静态资源优化 编码相关

网络请求相关

CDN

​ 将第三方的类库放到CDN上,能够大幅度减少生产环境中的项目体积,另外CDN能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上

​ 另外因为CDN和服务器的域名一般不是同一个,可以缓解同一域名并发http请求的数量限制,有效分流以及减少多余的cookie的发送(CDN上面的静态资源请求时不需要携带任何cookie)

externals只适用于ES Module的默认导入

这里还是建议尽量放到公司专用的CDN上,不推荐使用公共的CDN,因为容易挂,生产环境还是以稳定为主吧

合理的缓存策略

将长时间不会改变的第三方类库或者静态资源设置为强缓存,将max-age设置为一个非常长的时间,再将访问路径加上哈希达到哈希值变了以后保证获取到最新资源(vue-cli3会自动构建,自己搭建的webpack脚手架需要自行配置contentHash,chunkHash)

CDN上的缓存策略,可以看到max-age的值非常大,这样下次访问就只会读取本地磁盘/内存中缓存的资源:

对于index.html和一些图片等多媒体资源,可以选择协商缓存(max-age<=0,Last-Modified,ETag),保证返回服务器最新的资源

一个好的缓存策略,有助于减轻服务器的压力,并且显著的提升用户的体验

gzip

为你的文件开启gzip压缩是一个不错的选择,通常开启gzip压缩能够有效的缩小传输资源的大小,如果你的项目是用nginx作为web服务器的话,只需在nginx的配置文件中配置相应的gzip选项就可以让你的静态资源服务器开启gzip压缩

    #开启和关闭gzip模式
    gzip on;
    #gizp压缩起点,文件大于1k才进行压缩
    gzip_min_length 1k;
    # gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时间
    gzip_comp_level 6;
    # 进行压缩的文件类型。
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript ;
    #nginx对于静态文件的处理模块,开启后会寻找以.gz结尾的文件,直接返回,不会占用cpu进行压缩,如果找不到则不进行压缩
    gzip_static on
    # 是否在http header中添加Vary: Accept-Encoding,建议开启
    gzip_vary on;
    # 设置gzip压缩针对的HTTP协议版本
    gzip_http_version 1.1;

但是我们这里要说的是前端输出gzip文件,利用compression-webpack-plugin让webpack在打包的时候输出.gz后缀的压缩文件

这样不需要服务器主动压缩我们就已经可以得到gzip文件,在上面的nginx配置项中可以发现这一行

#nginx对于静态文件的处理模块,开启后会寻找以.gz结尾的文件,直接返回,不会占用cpu进行压缩,如果找不到则不进行压缩
gzip_static on

只要把.gz的文件放到服务器上,开始gzip_static就可以让服务器优先返回.gz文件,在面对高流量时,也能一定程度减轻对服务器的压力,属于用空间来换时间(.gz文件会额外占有服务器的空间)

资源嗅探

对于现代浏览器来说,可以给link标签添加preload,prefetch,dns-prefetch属性

preload

对于SPA应用来说,当浏览器解析完script脚本才会生成DOM节点,如果你的项目中没有使用服务端渲染的话且需要加载一个比较耗时的首屏图片时,可以考虑将这个首屏图片放在preload标签中让浏览器预先请求并加载执行,这样当script脚本执行完毕后就会瞬间加载图片(否则需要等脚本执行完毕后再向后台请求图片)

另外使用preload预加载首屏需要的css样式也是一个不错的选择,类似的库有critical

没有使用preload

prefetch

prefetch可以让浏览器提前加载下个页面可能会需要的资源,vue-cli3默认会给所有懒加载的路由添加prefetch属性,这样可以在你访问使用到懒加载的路由页面时能够获得更快的加载速度

preload和prefetch的区别在于,preload的资源会和页面需要的静态资源并行加载,而prefetch则会等到浏览器加载完必要的资源后,在空闲时间加载被标记为prefetch的资源

dns-prefetch

dns-prefetch可以让浏览器提前对域名进行解析,减少DNS查找的开销,如果你的静态资源和后端接口不是同一个服务器的话,可以将考虑你后端的域名放入link标签加入dns-prefetch属性

京东首页也使用到了dns-prefetch技术

http2

http2从2015年问世以来已经走过了4个年头,如今在国内也有超过50%的覆盖率,得益于http2的分帧传输,它能够极大的减少http(s)请求开销

http2和http1.1的性能差异对比

如果系统首屏同一时间需要加载的静态资源非常多,但是浏览器对同一域名的tcp连接数量是有限制的(chrome为6个)超过规定数量的tcp连接,则必须要等到之前的请求收到响应后才能继续发送,而http2则可以在一个tcp连接中并发多个请求没有限制,在一些网络较差的环境开启http2性能提升尤为明显

这里极力推荐在支持https协议的服务器中使用http2协议,可以通过web服务器Nginx配置,或是直接让服务器支持http2

nginx开启http2非常简单,在nginx.conf中只需在原来监听的端口后面加上http2就可以了,前提是你的nginx版本不能低于1.95,并且已经开启了https

listen 443 ssl http2;
复制代码

在network中通过protocol可以查看到当前的资源是通过哪个版本的http协议传输的

h2代表http2

构建相关

构建方面通过合理的配置构建工具,达到减少生产环境的代码的体积,减少打包时间,缩短页面加载时间

路由懒加载

传统的路由组件是通过import静态的打包到项目中,这样做的缺点是因为所有的页面组件都打包在同一个脚本文件中,导致生产环境下首屏因为加载的代码量太多会有明显的卡顿(白屏)

通过import()使得ES6的模块有了动态加载的能力,让url匹配到相应的路径时,会动态加载页面组件,这样首屏的代码量会大幅减少,webpack会把动态加载的页面组件分离成单独的一个chunk.js文件

预渲染

由于浏览器在渲染出页面之前,需要先加载和解析相应的html,css和js文件,为此会有一段白屏的时间,如何尽可能的减少白屏对用户的影响,目前我选择的是在html模版中,注入一个loading动画,这里我拿D2-Admin中的loading动画举例

在打包完成后,在这个index.html下方还会注入页面的脚本,当用户访问你的项目时,脚本还没有执行,但是可以显示loading动画,因为它是直接注入在html中的,等到脚本执行完毕后,Vue会新生成一个app的节点然后将旧的同名节点删除,这样可以有效的过渡白屏的时间

loading动画只是一个让用户感知到你程序正在启动的效果,只是一个静态页面没有任何的功能

另外预渲染还可以使用服务端渲染(SSR),通过后端输出一个首页的模版,或者使用骨架屏的方案,这里本人没有深入的了解过,有兴趣的朋友可以去实践一下

开发过程中小技巧

使用require.context这个webpack的api可以避免每次引入一个文件都需要显式的用import导入,它可以扫描你指定的文件,然后全部导入到指定文件,可以用在

  • vue-router的路由自动导入
  • vuex的模块自动导入
  • svg图标的自动导入
  • 全局组件的自动导入

vuex:

这样在创建一个新的模块时,不需要在index.js中用import引入,在modules中再声明一遍了

全局组件和svg图标:

避免了每创建一个全局组件都需要引入,在调用一次Vue.component的过程,而当加载到Svg组件会自动扫描icons文件夹,将所有svg图标导入进来

源代码

部分优化的方案放在我的github上,有兴趣可以看看

源码地址

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 金利源 原文链接:https://juejin.im/post/6844904025939410951

回到顶部