【搬砖百宝箱】前端性能优化点总结

309 阅读8分钟

从构建优化,网络层面和代码层优化总结。

构建优化

1.  tinypng.com在线压缩工具压缩大图。

这个工具挺好用,但是压缩大的图和批量压缩要收费。

2. webpack配置image-webpack-loader,压缩本地图片体积。

这个其实比较鸡肋,npm安装总是不成功,要用cnpm。当然这是其次,主要是压缩的效果不是很好呀。

3. webpack配置compressionPlugin插件把大文件预先压缩为gzip,与nginx配合传输。

这个效果杠杠的,能把资源压缩近70%呢。

原理可查看这篇文章(www.cnblogs.com/style-hyh/p…

const CompressionPlugin = require('compression-webpack-plugin');

chainWebpack: config => {    
    if (process.env.NODE_ENV === 'production') {      
        config.plugin('compressionPlugin') 
         .use(new CompressionPlugin({          
            test: /\.js$|\.html$|\.css/, // 匹配文件名          
            threshold: 10240, // 对超过10k的数据压缩          
            deleteOriginalAssets: false // 不删除源文件     
           }))    
      }  
}

    #开启gzip模式
    gzip on;

    #nginx对于静态文件的处理模块,开启后会寻找以.gz结尾的文件,直接返回,不会占用cpu进行压缩,如果找不到则不进行压缩
    gzip_static on;    
    #gizp压缩起点,文件大于1k才进行压缩
    gzip_min_length 1k;
    
    # gzip 压缩级别,1-9,数字越大压缩的越好,也越占用CPU时间
    gzip_comp_level 1;
    
    # 进行压缩的文件类型。
    gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript ;
    

    
    # 是否在http header中添加Vary: Accept-Encoding,建议开启
    gzip_vary on;

    # 设置压缩所需要的缓冲区大小,以4k为单位,如果文件为7k则申请2*4k的缓冲区 
    gzip_buffers 2 4k;

    # 设置gzip压缩针对的HTTP协议版本
    gzip_http_version 1.0;

4. 生产环境不生产Map文件

module.exports = {  productionSourceMap: false}

map文件相当于是查看源码的一个东西。如果不需要定位问题,并且不想被看到源码,就把productionSourceMap 置为false,既可以减少包大小,也可以加密源码。

5.prerender-spa-plugin 预渲染

未实践

6. 利用webpack的splitChunk分包

7. 利用import()按需加载

这里介绍两个场景使用场景:

  A. 路由懒加载。广为人知的应用场景,不做赘述。

  B. 在业务逻辑中使用。比如现在有一个混合开发app,我需要在h5模块装安装一个类似eruda这样的控制台调试工具。当然这个只是在测试环境才需要,生产环境无需引入。这时候,就可以通过桥协议获取app的环境,根据环境判断是否调用import()函数动态引入这个工具,然后再初始化,而不是先把工具引入,再根据环境判断是否初始化。

8. 打包es5和es6两个版本的js,根据浏览器版本动态引入对应版本js。

9. 使用webpack的Tree shaking

webpack官网对Tree shaking的介绍webpack.docschina.org/guides/tree…

其实在最新的babel-loader中,已经默认关闭对import和export相关的转化,所以不会影响webpack的tree shaking。

网络层优化

1. 浏览器缓存机制

网页的静态资源,并不是实时更新的,所以可以请求到最新版本之后,把它放入缓存中,下次访问网页时,如果没有更新资源,可以直接从缓存里获取。

因为资源更新之后,要从服务器中获取最新资源,那就无法使用强制缓存,只能使用协商缓存判断是否要获取最新数据。

我们使用的时nginx,所以只说下nginx怎么配置。

当我们使用nginx默认配置时,nginx在返回请求时,会带着etag和last-modified的,但是刷新页面再次请求后,请求头里并没带有if-none-match,而是默认强制缓存了。缓存的时间,也是根据浏览器自己定的缓存时间策略。

那么我们如何设置为协商缓存呢?

经过查阅nginx官网文档,在nginx配置文件中把Expires设置为-1,就可以解决这个问题了。

但是,如果有负载均衡的话,每台服务器就都会产生一个etag,这样单靠etag来判断是否资源更新就不准了,这时,If-Modified-Since就派上用场了。

还有一个极端的情况,万一这些服务器不是同一时间启动的,这样时间也不准了,那怎么办呢?只能修改etag的生成算法来解决吗? 

2. 开启HTTP2.0

HTTP2.0的出现目的就是为了优化性能。虽然HTTP1.1也是长连接了,但是不能并行发送请求。2.0就解决了这个问题呀,而且服务端可以往客户端发送请求。也就是说静态文件js, css, html不用必须向服务器发送三次请求。如果是nginx服务器,版本要1.9.5以上,并且是https协议。

3. 离线包缓存

离线包方式其实就是把H5代码打包压缩成zip包形式,上传的后台服务器。App在打开此应用时,下载此资源。当然下载前会先判断是否有更新,无更新就用本地缓存的资源。这么看来倒是很像http的协商缓存了。

下载后打开静态资源,当然就比在线打开要快一些了。

但是,这里会出现跨域的问题,解决办法可以有两种:

   A. 使用CROS解决跨域问题。

   B. 添加拦截HTTP请求方法,用原生端做代理发送请求,等获得响应后再返回给H5。这样对         H5是无感的。推荐!

CDN图片存储

CDN存储图片、pdf等资源,多节点,提高用户访问速度。

4. nginx配置浏览器强制缓存静态资源案例

如果你问我,强制缓存和协商缓存,优先用哪个,我的答案是,在条件允许的情况下,用强制缓存,如果条件不允许,先想办法创建条件使用强制缓存。

可能要问,为什么优先强制缓存?因为强制缓存相比协商缓存来说,是不需要发起网络请求的。

起初在我的项目中,也是使用的协商缓存。静态资源的获取,每次都要发送请求向后端确认是否更新,当返回304时,证明没有更新,直接在本地获取缓存数据即可。随着项目不断完善,发版次数也在减少,每个资源每次加载都向后台发送这个请求就感觉有点浪费了。

我想达到的效果是:只有静态文件有更新了,我才向后台发请求获取资源,不更新就直接获取本地的数据(强制缓存)。

问题是:每次访问页面时,如何知道静态资源是否更新了呢?

我的项目使用的是webpack,webpack最终会把打包后的js文件,创建

那么是否可以,html文件不缓存,每次都是向服务器获取最新数据,只强制缓存静态资源呢?

因为我的项目是用nginx存放前端代码的,所以看了下nginx是否支持分文件设置缓存策略的。答案是肯定的。

// nginx.configserver {    listen       80;    server_name  localhost;    location /my-app/assets/ {                try_files $uri $uri/ /index.html last;          alias /app/html/assets/;
         # 配置强制缓存
          add_header Cache-Control max-age=90000;    }    location /my-app/libs/ {                try_files $uri $uri/ /index.html last;          alias /app/html/libs/;
         # 配置强制缓存          add_header Cache-Control max-age=90000;    }    location /my-app/ {            try_files $uri $uri/ /index.html last;            alias /app/html/;            index index.html index.htm;
            # 配置无需缓存            add_header ETag "";            add_header Last-Modified "";            add_header Cache-Control "no-store";            proxy_cache_revalidate on;    }}

我们强制缓存了assets,libs文件夹下的静态文件,html未缓存。这样,每次访问我们项目时,都会从服务器获取html文件,在获取js等文件时,会判断html中的主js已存在在本地浏览器缓存(根据js文件名的hash值后缀),直接取出,没有,去服务器获取并存入本地缓存。

经验证,异步组件代码变更后,打包生成的主js文件,也会改变hash值,所以不需要担心不是直接引用的异步组件获取不到最新代码的问题。

渲染层面

部分引自:

juejin.im/post/684490…

  • 动态引入模块代码。

  • 组件库中组件按需引入。

  • 巧用computed和watch

  • HTML文档结构层次尽量少,最好不深于六层

  • JS 脚本尽量后放

  • 样式结构层次尽量简单

  • 少量首屏样式使用内联方式放在标签内

  • 在脚本中尽量减少DOM操作,尽量访问离线DOM样式信息,避免过度触发回流

  • 减少通过 JS 代码修改元素样式,尽量使用修改 class 名方式操作样式或动画

  • 尽量减少浏览器重排和重绘的一些情况发生

  • 2020年了!就不要使用 table 布局了

  • CSS 动画中尽量只使用 transformopacity ,不会发生重排和重绘

  • 隐藏在屏幕外,或在页面滚动时,尽量停止动画

  • 尽可能只使用 CSS 做动画,CSS动画肯定比 JS 动画要好很多

  • 避免浏览器的隐式合成

  • 改变复合层的尺寸

首屏加提速优化点

减小包体积

  1. 页面分包进行懒加载
  2. 组件库按需加载
  3. 按条件动态引入工具库
  4. 压缩图片资源
  5. 开启gzip压缩

网络请求优化

  1. 利用http2
  2. 图片懒加载
  3. 利用CDN缓存静态资源或图片

利用浏览器缓存

  1. 强制缓存
  2. 协商缓存

开启服务端渲染

  1. ssr