页面生命周期

地址栏输入url 发生了什么?

加载优化

  1. 判断输入的为搜索关键字还是请求的url,构建成完整的url请求
    2. 查找本地缓存,没找到资源继续(http缓存,PWA)
    3. DNS解析,获取IP地址(DNS缓存)
    4. https需要建立TLS连接
    5. 三次握手建立TCP连接(连接复用 http1.1 connection:keep-alive;http2)
    6. 浏览器构建请求头、请求行等,把域名相关的cookie带到请求头中(减小cookie体积)
    7. 服务器接收到信息后,返回响应信息,如果是301,302等重定向,会重新发送请求(减少重定向)
    8. 浏览器接收到文件,比如html,解析html,边接收边解析,不用等html加载完成
    9. 碰到js、css(外联的)等,请求js、css资源,js会阻塞加载,css不直接阻塞(css放头部,js放尾部;cdn)

减少请求数量(缓存,小图片转成base64,字体图标,雪碧图等)
减少请求体积(压缩,删除注释,tree shaking,拆包,gzip等)

渲染优化

  1. 构建DOM树 DOM Tree
    11. 样式计算 Recaculate Style
    12. 布局计算 Layout Tree
    13. 分层树 Layer Tree
    14. 图层绘制 Paint
    15. 栅格化 Raster
    16. 合成和显示

减少重排,重绘
合理使用合成
一个大的原则就是让单个帧的生成速度变快image.png

  1. 渲染进程将 HTML 内容转换为能够读懂的DOM 树结构。
  2. 渲染引擎将 CSS 样式表转化为浏览器可以理解的styleSheets,计算出 DOM 节点的样式。
  3. 创建布局树,并计算元素的布局信息。
  4. 对布局树进行分层,并生成分层树
  5. 为每个图层生成绘制列表,并将其提交到合成线程。
  6. 合成线程将图层分成图块,并在光栅化线程池中将图块转换成位图。
  7. 合成线程发送绘制图块命令DrawQuad给浏览器进程。
  8. 浏览器进程根据 DrawQuad 消息生成页面,并显示到显示器上。

重排(更新了元素的几何属性)

image.png

重绘(更新元素的绘制属性)


image.png

直接合成阶段

image.png

不同阶段的优化策略

打包优化

主要影响加载优化

vite

用于开发阶段

webpack

  • noParse

noParse表示不需要解析的文件,有的文件可能是来自第三方的文件,被 providePlugin引入作为windows上的变量来使用,这样的文件相对比较大,并且已经是被打包过的,所以把这种文件排除在外是很有必要的,配置如下

  1. module: {
  2. noParse: [/proj4\.js/]
  3. }
  • exclude

某些loader会有这样一个属性,目的是指定loader作用的范围,exclude表示排除某些文件不需要babel-loader处理,loader的作用范围小了,打包速度自然就快了,用babel-loader举一个简单例子

  1. {
  2. test: /\.js$/,
  3. loader: "babel-loader",
  4. exclude: path.resolve(__dirname, 'node_modules')
  5. }
  • devtool

这个配置是一个调试项,不同的配置展示效果不一样,打包大小和打包速度也不一样,比如开发环境下cheap-source-map肯定比source-map快,至于为什么,强烈推荐自己之前写的这一篇讲解devtool的文章:webpack devtools篇讲的非常详细。

  1. {
  2. devtool: 'cheap-source-map'
  3. }
  • .eslintignore

让不必要的文件禁止eslint,只对需要的文件eslint

  • cache-loader

这个loader就是在第一次打包的时候会缓存打包的结果,在第二次打包的时候就会直接读取缓存的内容,从而提高打包效率。但是也需要合理利用,我们要记住一点你加的每一个loader,plugins都会带来额外的打包时间。这个额外时间比他带来的减少时间多,那么一味的增加这个loader就没意义,所以cache-loader最好用在耗时比较大的loader上,配置如下

  1. {
  2. rules: [
  3. {
  4. test: /\.vue$/,
  5. use: [
  6. 'cache-loader',
  7. 'vue-loader'
  8. ],
  9. include: path.resolve(__dirname, './src')
  10. }
  11. ]
  12. }
  • terser-webpack-plugin

    1. optimization: {
    2. minimizer: [
    3. new TerserPlugin({
    4. parallel: true,
    5. cache: true
    6. })
    7. ],
    8. }
  • DllPlugin,webpack.DllReferencePlugin

先把第三方依赖先打包一次生成一个js文件,然后真正打包项目代码时候,会根据映射文件直接从打包出来的js文件获取所需要的对象,而不用再去打包第三方文件。只不过这种情况打包配置稍微麻烦点,需要写一个webpack.dll.js。大致如下
webpack.dll.js

  1. const path = require('path');
  2. const webpack = require('webpack');
  3. module.exports = {
  4. entry: {
  5. library: ["vue", "moment"]
  6. },
  7. output: {
  8. filename: '[name].dll.js',
  9. path: path.resolve(__dirname, 'json-dll'),
  10. library: '[name]'
  11. },
  12. plugins: [
  13. new webpack.DllPlugin({
  14. path: './json-dll/library.json',
  15. name: '[name].json'
  16. })
  17. ]
  18. }

webpack.dev.js

  1. new AddAssetHtmlWebpack({
  2. filepath: path.resolve(__dirname, './json-dll/library.dll.js')
  3. }),
  4. new webpack.DllReferencePlugin({
  5. manifest: require("./json-dll/library.json")
  6. })
  • webpack-cdn-plugin
  • webpack-bundle-analyzer

分析打包后的文件,进行合适的优化

  • speed-measure-webpack-plugin

这个插件可以告诉我们打包时候每一个loader或者plugin花费了多少时间,从而对耗时比较长的plugin和loader做优化

代码优化

主要影响渲染优化

单帧速度变快

减少js脚本执行的时间

有时js执行时间过长,会影响主线程执行渲染任务的时间,表现就是页面反应很慢。针对这种情况,一般有两种策略:

  • 将一次执行的函数分解为多个任务,使得每次的执行时间不要过久(react time slicing)
  • 利用Web Workers,主线程外的线程,不会影响主线程 (CPU 密集型任务)


减少重排重绘

  • js 尽量减少对样式的操作,能用 css 完成的就用 css
  • 对 dom 操作尽量少,能用 createDocumentFragment 的地方尽量用
  • 如果必须要用 js 操作样式,能合并尽量合并不要分多次操作
  • resize 事件 最好加上防抖,能尽量少触发就少触发
  • 加载图片的时候,提前写好宽高

合理利用 CSS 合成动画

如果能提前知道对某个元素执行动画操作,那就最好将其标记为 will-change,这是告诉渲染引擎需要将该元素单独生成一个图层

避免频繁的垃圾回收

我们知道 JavaScript 使用了自动垃圾回收机制,如果在一些函数中频繁创建临时对象,那么垃圾回收器也会频繁地去执行垃圾回收策略。这样当垃圾回收操作发生时,就会占用主线程,从而影响到其他任务的执行,严重的话还会让用户产生掉帧、不流畅的感觉。
所以要尽量避免产生那些临时垃圾数据。那该怎么做呢?可以尽可能优化储存结构,尽可能避免小颗粒对象的产生。

其他

  • 删除不使用的代码和功能
  • 数据存在性判断,避免报错
  • 尽可能缓存 (computed,Vue组件复用,react memo等)
  • 避免内存泄漏
  • 框架使用中的优化
  • ……

体验优化

  1. 首屏优化
  2. 大量数据渲染优化
  3. 使用节流 throttle 和防抖 debounce
  4. 骨架图
  5. 分块加载,加载失败fallback
  6. 交互体验优化
  7. ……