前端性能优化总结

351 阅读6分钟

前言

本文整理了前端性能优化方面方法总结,如果对答案有不一样见解的同学欢迎评论区补充讨论,当然有问题,也欢迎在评论区指出。

一、什么是性能?

对于前端而言,从网站开始生成时,到代码运行中间,消耗浏览器以及服务器的所有资源

二、分析下前端加载速度慢原因

  1. 首先安装webpack的可视化资源分析工具
npm i webpack-bundle-analyzer -D
  1. 然后在webpack的dev开发模式配置中,引入插件
const BundleAnalyzerPlugin = require('webpack-bundle-plugin').BundleAnalyzerPlugin plugins: [ new BundleAnalyzerPlugin() ]
  1. 最后命令行执行npm run build --report , 浏览器会自动打开分析结果

image.png


其它性能监控方式

  • (1)最简单的性能监控
<!DOCTYPE html>
<html>
<head>
    <title></title>
    <script type="text/javascript">
        // 记录页面加载开始时间
        var timerStart = Date.now();
    </script>
    <!-- 加载静态资源,如样式资源 -->
</head>
<body>
<!-- 加载静态JS资源 -->
<script type="text/javascript">
    document.addEventListener('DOMContentLoaded', function() {
        console.log("DOM 挂载时间: ", Date.now() - timerStart);
        // 性能日志上报
    });
    window.addEventListener('load', function() {
        console.log("所有资源加载完成时间: ", Date.now()-timerStart);
        // 性能日志上报
    });
</script>
</body>
</html>
  • (2)浏览器的Performance

image.png

三、如何优化性能

1、缓存策略

image.png

2、精炼js代码

主要是用最优算法,来降低时间和空间复杂度

3、静态资源加载

(1)CDN

如:vue项目引入Element组件库

<!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"> 
<!-- 引入组件库 --> 
<script src="https://unpkg.com/element-ui/lib/index.js"></script>

(2)加载图片等资源

  • 精灵图或雪碧图:将所有小图放置在一张大图上,使用时,直接引用大图定位即可

    • 可以使用webpack插件:webpack-spritesmith
    • 程序自动读取雪碧图目录下的文件,并将所有图片组合成一张大图
  • 图片懒加载:按需加载

    • 可以使用vue-lazy-load插件
    //main.js
    import Vue from 'vue'
    import App from './App.vue'
    import VueLazyload from 'vue-lazyload'
    
    Vue.use(VueLazyload)
    
    // or with options
    Vue.use(VueLazyload, {
      preLoad: 1.3,
      error: 'dist/error.png',
      loading: 'dist/loading.gif',
      attempt: 1
    })
    
    new Vue({
      el: 'body',
      components: {
        App
      }
    })
    
    • vue文件中将需要懒加载的图片绑定 v-bind:src 修改为 v-lazy
    <ul>
      <li v-for="img in list">
        <img v-lazy="img.src" >
      </li>
    </ul>
    

4、vue框架代码优化

  • 合理使用 v-ifv-show
  • 合理使用 computedwatchfilter
  • 尽量减少使用watch监听相应的数据,监听数据量过多,会导致性能消耗,系统出现卡顿。采用事件中央总线或者vuex进行数据的变更操作。
  • v-for 遍历为 item 添加 keyv-for 遍历避免同时使用 v-if
  • 细分组件:把所有的组件的布局写在一个组件中,当数据变更时,由于组件代码比较庞大,vuejs的数据驱动视图更新比较慢,造成渲染比较慢
  • 定时器和监听器,在不使用时,需要在组件销毁阶段(beforeDestory)清除定时器和监听器removeEventListener或者clearInterval等
  • keep-alive缓存组件,Props:
    • include - 字符串或正则表达式。只有名称匹配的组件会被缓存。
    • exclude - 字符串或正则表达式。任何名称匹配的组件都不会被缓存。
    • max - 数字。最多可以缓存多少组件实例。
  • 路由懒加载:三种方式
  • SSR服务端渲染:局限性就是目前仅支持Koa、express等Nodejs的后台框架
  • tree-shaking:移除无用代码

5、webpack层面优化

(1)开发环境性能优化

  • 优化打包构建速度:

    • HMR:hot module replacement 模块热替换,当一个模块变化,只会打包这一个模块
  • 优化代码调试

    • 优化代码调试source-map:能定位到代码错误的地方

(2)生产环境性能优化

  • tree-shaking之webpack:去除无用代码
前提:1. 必须使用ES6模块化  2. 开启production环境
作用: 减少代码体积

在package.json中配置 
      "sideEffects": false 所有代码都没有副作用(都可以进行tree shaking)
        问题:可能会把css / @babel/polyfill (副作用)文件干掉
      "sideEffects": ["*.css", "*.less"]  这样就会保留css、less文件
  • 开启gizp压缩         gizp压缩是一种http请求优化方式,通过减少文件体积来提高加载速度。html、js、css文件甚至json数据都可以用它压缩,可以减小60%以上的体积。前端配置gzip压缩,并且服务端使用nginx开启gzip,用来减小网络传输的流量大小。(compression-webpack-plugin)

  • 压缩css文件

使用mini-xss-extract-plugin提取CSS 到单独的文件, 并使用optimize-css-assets-webpack-plugin来压缩CSS文件

  • webpack的模块懒加载/预加载
console.log('index.js文件被加载了~');

// import { mul } from './test';

document.getElementById('btn').onclick = function() {
  // 懒加载~:当文件需要使用时才加载~ ---- 但当使用某个模块,模块又非常大时,加载的就会慢
  // 预加载 prefetch:会在使用之前,提前加载js文件,使用时读取缓存 ---- 兼容性较差
  // 正常加载可以认为是并行加载(同一时间加载多个文件)  
  // 预加载 prefetch:等其他资源加载完毕,浏览器空闲了,再偷偷加载资源
  import(/* webpackChunkName: 'test', webpackPrefetch: true */'./test').then(({ mul }) => {
    console.log(mul(4, 5));
  });
};
  • PWA 渐进式网络开发应用程序(离线可访问)
    • 在离线情况下,会从serviceWorker中和CacheStorage中获取资源

在真实项目中,当路由已经跳转,而上一页的请求还在pending状态,如果数据量小还好,数据量大时,跳到新页面,旧的请求依旧没有停止,这将会十分损耗性能,这时我们应该先取消掉之前还没有获得相应的请求,再跳转页面。这里axios给我们提供了一个方法:

cancelToken

官网方法一:

var CancelToken = axios.CancelToken;
var source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // 处理错误
  }
});

// 取消请求(message 参数是可选的)
source.cancel('Operation canceled by the user.');

如果我要跳转页面的话,我调用source.cance()方法就可以干掉之前这个没有请求完的请求了。
但是这个方法有个弊端,就是比较麻烦,每次都要手动去调用source.cance()方法。怎么做到全局统一管理呢?

官网方法二:

还可以通过传递一个 executor 函数到 CancelToken 的构造函数来创建 cancel token:

var CancelToken = axios.CancelToken;
var cancel;

axios.get('/user/12345', {
  cancelToken: new CancelToken(function executor(c) {
    // executor 函数接收一个 cancel 函数作为参数
    cancel = c;
  })
});

// 取消请求
cancel();

根据这个方法:我们一步一步来实现它:

1、在main.js里写一个全局httpRequestList的空数组,用来装我们的cancel函数:

// main.js
Vue.$httpRequestList = []

2、再在我们封装好的post,get请求里面,将每一个请求里面都做一个将cancel函数推入的httpRequestList数组的动作:这样我们的每一个请求里面,都包含了一个cancelToken对象。

POST (url, data, errMsg) {
    const CancelToken = axios.CancelToken
    return axios.post(url, data, {
      timeout: 30000,
      cancelToken: new CancelToken(function executor (c) {
        Vue.$httpRequestList.push(c)
      })
    }).then(checkStatus).then(res => checkCode(res, errMsg))
  },
  GET (url, params, errMsg) {
    const CancelToken = axios.CancelToken
    return axios.get(url, {
      params: {
        _t: +(new Date()),
        ...params
      },
      timeout: 30000,
      cancelToken: new CancelToken(function executor (c) {
        Vue.$httpRequestList.push(c)
      })
    }).then(checkStatus).then(res => checkCode(res, errMsg))
  }

3、在这之后我们要写一个执行cancel方法的方法:

import Vue from 'vue'

export const clearHttpRequestingList = () => {
  if (Vue.$httpRequestList.length > 0) {
    Vue.$httpRequestList.forEach((item) => {
        //item就是之前每一个请求装进httpRequestList数组的cancel方法,item()执行后,
        //如果该请求是pending状态,那么可以直接取消掉。执行完后记得清空httpRequestList数组。
      item()
    })
    Vue.$httpRequestList = []
  }
}

4、最后我们回到main.js,在每次跳转之前执行clearHttpRequestingList()函数。

router.beforeEach((to, from, next) => {
  clearHttpRequestingList()
  ..........这下面是你的路由验证代码..........
})

这样就实现了每次路由跳转之前,就清空之前出于pending状态的请求,优化了性能。

总结

觉得写得好的,对你有帮助的,可以分享给身边人,知识越分享越多,千万不要吝啬呀

后续更新其它前端小知识总结,请关注我,整理好,分享给你们,我们一起学前端