性能优化

198 阅读6分钟
  • 工程化优化
  • 框架优化
  • 网络优化
  • 浏览器优化

渲染性能优化

服务端渲染 ssr

  • 传统的前后端分离(spa)渲染页面的过程复杂
  • ssr渲染页面过程简单,所以性能好
  • 如果是纯 h5 页面,ssr是性能优化的终极方案

虚拟列表

  • 用户滚动一个长列表时,虚拟列表技术仅渲染当前可见的部分,而不是整个列表。这种方法显著提高了性能,特别是当处理数千甚至数百万条记录时

利用时间切片进行优化

const app = document.querySelector('#app')
const btn: any = document.querySelector('.btn')

btn.onclick = () => {
  const add = (index: number) => {
    const div: any = document.createElement('div')
    div.innerText = index
    app.appendChild(div)
  }
  // 执行 add 三十万次
  performChunk(300000, add)
}

// 往requestIdleCallback队列添加异步任务
const scheduler = (task: Function) => {
  requestIdleCallback((idle) => {
    const hasTime = () => idle.timeRemaining() > 0
    task(hasTime)
  })
}

// 默认往requestIdleCallback队列添加异步任务
function performChunk(times: number, taskHandler: Function, push=scheduler) {
  let i = 0;
  
  function _run() {
    if(i >= times) return

    push(hasTime => {
      while(hasTime() && i < times) {
        taskHandler(i)
        i++
      }
      _run()
    })
  }

  _run()
}

图片优化

背景

最近在公司在写微信小程序,该小程序主要展示一些高清图片,所以图片数量非常多,而且图片尺寸也比较大,导致小程序的加载时间非常长。所以这里记录一下如何减少小程序图片的加载时间,优化用户体验。

使用webp格式图片

  • 在小程序中是支持webp格式的图片的,所以我们可以将图片转换为webp格式,
  • webp可以减少图片体积,提升加载速度。
  • 公司使用的阿里云oss进行图片存储
  • 只需要在图片url后面加一定的参数即可
// 原本图片路径:
let url =' https://.../50403470042.png'

// 转化为webp格式的图片路径:
let url =' https://.../50403470042.png?x-oss-process=image/format,webp'

根据需求设置适当的分辨率

  • 阿里云oss支持在图片后面加上参数来设置图片的分辨率,
  • 我们可以给图片url后面加上/resize,w_320即可,
  • 其中w_320表示图片宽度为320px
// 原本图片路径:
let url =' https://.../3%20%282%2.png?x-oss-process=image/format,webp'

// 设置宽度后的图片路径:
let url =' https://.../3%20%282%2.png?x-oss-process=image/format,webp/resize,w_320'

合理使用占位图片

  • 如果在网络慢的情况下,image加载图片的过程可能会非常慢,
  • 在请求完成之前页面都会因为没有数据而呈现一片空白,这是非常差的用户体验,
  • 这里我们可以借助小程序image标签上的@error @load事件来实现占位图片的展示。
  • 我们可以根据需求去封装一个LoadImage组件统一处理
<template>
    <view class="loadImage-wrapper">
        <image v-if="isLoading" :src="defaultImage" :mode="mode" :lazy-load="lazyLoad" />
        <image :class="[isLoading ? 'before-load' : '']" :src="imageUrl" :mode="mode" :lazy-load="lazyLoad"
            @load="imageLoad" />
    </view>
</template>
<script>
export default {
    props: {
        /**
         * 占位图
         * @default /static/images/load-image.png
         */
        defaultImage: {
            type: String,
            default: '/static/load-image.png',
        },
        /**
         * 是否使用webp
         * @default false
         */
        useWebp: {
            type: Boolean,
            default: false,
        },
        /**
         * 图片的显示模式
         * @default scaleToFill
         */
        mode: {
            type: String,
            default: 'scaleToFill',
        },
        /**
         * 图片加载分辨率-宽度
         * @default 
        */
        width: {
            type: String,
            default: '',
        },
        /**
         * 是否懒加载
         * @default true
         */
        lazyLoad: {
            type: Boolean,
            default: true,
        },
        /**
         * 图片地址
         * @default 
        */
        src: {
            type: String,
            default: '',
        },
    },
    data() {
        return {
            isLoading: true,
        }
    },

    methods: {
        imageLoad() {
            this.isLoading = false
        },
    },

    computed: {
        imageUrl() {
            let url = this.src + '?'
            this.useWebp && (url += 'x-oss-process=image/format,webp')
            this.width && (url += '/resize,w_' + this.width)
            return url
        }
    },
}
</script>
<style lang="scss" scoped>
.loadImage-wrapper {
    .before-load {
        width: 0;
        height: 0;
        opacity: 0;
    }
}
</style>

雪碧图

  • 针对http1的优化, 对头阻塞
  • 雪碧图,也叫Sprite,是将多个小图片合并成一张大图,然后在页面中使用background-imagebackground-position属性来显示其中的某一张图片。这样可以减少图片的加载次数,减少图片的大小,同时减少图片的加载时间。在项目中难免会有很多小图标,我们就可以使用雪碧图的方式来使用,减少请求次数。这里我就不做展示了。

CDN

生产环境下我们不会直接用 OSS 的 URL 访问,而是会开启 CDN,用网站域名访问,最终回源到 OSS 服务:

image.png

image.png

网络性能优化

请求缓存

  • 请求缓存是指创建一个带有缓存的请求,
  • 当没有命中缓存时发送请求并缓存结果,
  • 当有缓存时直接返回缓存。

请求幂等

  • 连续调用同一个api接口n次, 只有第一次才发送http请求
  • 调用接口后, 查看缓存状态
    • pendding 状态直接发起http请求
    • loading 状态
      • 使用发布订阅模式
      • 将 函数 存入数组中
      • 当状态变为 successfail 执行数组中的函数
    • success 状态: 直接返回本地缓存
    • fail 状态: 重新发送http请求

对头阻塞(针对http1的优化)

  • 减少文件数量: 雪碧图
  • 增加TCP通道
    • 同一个域名最多只能同时加载6个资源
    • 使用不同的域名加载资源, 突破6个限制

image.png

首页白屏优化&打包体积优化

可通过两个方向进行优化: 代码压缩, 代码分包

代码压缩

  • 使用webpack压缩 js css html 图片
  • 代码复用: 提取公共代码
  • treeShaking
    • 充分利用webpacke打包机制 treeShaking
    • treeShaking: 就是打包时把没用的代码去掉
// 这样写不好, 会将没使用的组件也打包进去
import UI form 'antd'
// 这样写,如果Button未被使用, Button将被tree shaking 掉
import { Button } from 'antd'

代码分割

  • 将不会立马使用的第三方库分包出去
  • 将动态导入的代码分包出去: import('./tool.js')
  • 等到需要时再加载(懒加载)
  • 也可以在空闲时预加载

分页

  • 针对列表页
  • 默认只展示第一页内容
  • 上滑加载更多

延迟加载

  • 当首屏有上万dom需要渲染时
  • 将首屏页面分层可见部分和不可见部分
  • 渲染第1帧加载可见部分
  • 渲染第10帧加载不可见部分
const frame = useFrame() // 可通过requestAnimation()实现
return <>
    { frame>1 && <Part1/> }
    { frame>10 && <Part2/> }
    { frame>20 && <Part3/> }
    ...
</>

懒加载

  • import('./a.js')

图片懒加载 lazyload

  • 针对详情页
  • 默认只展示文本内容,然后触发图片懒加载
  • 注意:提前设置图片尺寸,尽量只重绘不重排

路由懒加载

  • 使用与单页面应用, 不适用于多页面应用
  • 路由拆分, 优先保证首页加载

代码懒加载

  • 将不会立马使用的第三方库分包出去
  • 将动态倒入的代码分包出去: import('./tool.js')
  • 等到需要时再加载(懒加载)

预加载

代码预加载

  • 将不会立马使用的第三方库分包出去
  • 将动态倒入的代码分包出去: import('./tool.js')
  • 等到需要时再加载(懒加载)
  • 也可以在空闲时预加载

APP预加载

打开页面前提前获取数据 预取

  • 如果H5在APP WebView中展示,可使用App预取
  • 用户访问列表页时,App预加载文章首屏内容
  • 用户进入H5页,直接从APP中获取内容,瞬间展示首屏

Hybrid

  • 提前将html js css下载到App内部
  • 在App webview中使用file://协议加载页面文件
  • 再用Ajax获取内容并展示(也结合App预取)