2023前端面试八股文-浏览器和性能优化(个人汇总自用)

265 阅读8分钟

关键渲染路径

关键渲染路径的定义

[关键渲染路径 - Web 性能 | MDN (mozilla.org)]

关键渲染路径是浏览器将 HTML,CSS 和 JavaScript 转换为屏幕上的像素所经历的步骤序列,关键渲染路径包含了 文档对象模型(DOM),CSS 对象模型 (CSSOM),渲染树和布局。

关键渲染路径总共分为以下几步:

  1. 处理 HTML 标记并构建 DOM 树。
  2. 处理 CSS 标记并构建 CSSOM 树。
  3. 将 DOM 与 CSSOM 合并成一个渲染树。
  4. 根据渲染树来布局(Layout),以计算每个节点的几何信息。
  5. 将各个节点绘制到屏幕上。

image.png

DOM树和渲染树区别

  1. DOM树与HTML标签——对应,包括head和隐藏元素
  2. 渲染树不包括head和隐藏元素,大段文本的每一个行都是独立节点,每一个节点都有对应的css属性

CSS会阻塞DOM解析吗

由浏览器的渲染流程图可知,DOM 解析和 CSS 解析是两个并行的进程,所以 CSS 加载不会阻塞 DOM 树的解析

Render Tree是依赖于 DOM Tree 和 CSSOM Tree 的,所以无论 DOM Tree 是否已经完成,它都必须等待到 CSSOM Tree 构建完成,即 CSS 加载完成(或 CSS 加载失败)后,才能开始渲染。

CSS 加载会阻塞其后的 JS 执行

JS 的加载、解析与执行会阻塞 DOM 的构建,也就是说,在构建 DOM 时,HTML 解析器若遇到了 JS,那么它会暂停构建 DOM ,将控制权移交给JS引擎,等 JS 引擎运行完毕,浏览器再从中断的地方恢复 DOM 构建。

这也是建议将 script 标签放在 body 标签底部的原因。

回流和重绘

参考文档[回流和重绘 - 掘金 (juejin.cn)]

什么是回流

当我们对 DOM 的修改引发了 DOM 几何尺寸的变化(比如修改元素的宽、高或隐藏元素等)时,浏览器需要重新计算元素的几何属性(其他元素的几何属性和位置也会因此受到影响),然后再将计算的结果绘制出来。这个过程就是回流(也叫重排)。【重新排列布局,即打碎重组】

由本身的大小宽高位置等的几何属性改变,引发 局部全局 的排版,会引发回流或局部回流

  • 全局范围:从根节点 html 开始对整个渲染树进行重新布局。
  • 局部范围:对渲染树的某部分或某一个渲染对象进行重新布局

什么是重绘

当我们对 DOM 的修改导致了样式的变化、却并未影响其几何属性(比如修改了颜色或背景色)时,浏览器不需重新计算元素的几何属性、直接为该元素绘制新的样式。这个过程叫做重绘。

只改变外观、风格,不影响布局,会引发重绘

怎么理解回流必将引起重绘,重绘不一定会引起回流

  1. 在回流过程中,浏览器会检查元素的新位置是否与之前的位置有重叠,如果有重叠,那么浏览器需要重新调整元素的位置,这个过程就是重绘。
  2. 回流会导致页面的布局发生变化,从而影响到其他元素的显示。为了保持页面的一致性,浏览器需要对受到影响的元素进行重绘,以更新它们的外观。

怎样减少回流和重绘

  • 如果想设定元素的样式,通过改变元素的 class 类名 (尽可能在 DOM 树的最里层)
  • 避免设置多项内联样式
  • 应用元素的动画,使用 position 属性的 fixed 值或 absolute 值
  • 避免使用 table 布局,table 中每个元素的大小以及内容的改动,都会导致整个 table 的重新计算
  • 使用css3硬件加速,可以让transformopacityfilters这些动画不会引起回流重绘
  • 避免使用 CSS 的 JavaScript 表达式

前端优化方案

优化思路: 浏览器 -〉资源 -〉图片 -〉代码层面

浏览器

  1. 减少HTTP请求
  2. 使用HTTP2.0
  3. 设置浏览器缓存策略
  4. 白屏时间做加载动画,增强用户体验

资源

  1. 静态资源cdn, 静态css/js/img等资源可以做cdn缓存,这样把资源同步到全国全球各地,用户就能更快访问到
  2. gzip压缩,服务端配置,如nginx可配置支持gzip压缩资源传输的方式
  3. 做服务端渲染(SSR)
  4. 将CSS放在文件头部,JavaScript文件放在底部

图片:

  1. 字体图标代替图片图标 :些通用的小图标,如箭头,叉,可以使用字体图标,减少请求,渲染更快
  2. 使用雪碧图
  3. 图片预加载, 可以在window.onload之后请求一些其他地方需要的图片资源
    比如我们有一个活动页使用了高清图,我们可以在它的入口前的首页就加载它,当我们进去页面时,浏览器就会从缓存里读取这张图片
  4. 小于10k的图片可以打包为base64格式 , 可以使用webpack url-loader处理
  5. 大图片切割成多个小图片再组合起来使用

webpack优化打包体积

参考webpack高级配置-优化产出代码 - 知乎 (zhihu.com) 1)小图片base64编码;
2)bundle加hash;
3)懒加载;
4)提取公共代码;
5)使用CDN加速;
6)IgnorePlugin;
7)使用Production模式默认开启tree-shaking;
8)Scope Hosting;

  1. 小图片base64编码 webpack配置中使用url-loader来将符合配置的文件转换成 Base64 方式引入
{
    test: /.(png|svg|jpg|gif)$/,
    use: {
        loader: 'url-loader',
        options: {
            limit: 3*1024 // 3k
        }
    }
}

bundle是webpack打包之后的各个文件,一般就是和chunk(代码块)是一对一的关系,bundle就是对chunk进行编译压缩打包等处理之后的产出。 我们在 Webpack 中使用chunkhash实际使用的是占位符语法,类似如下:

module.exports = {
    entry: './index.js',
    output: {
        filename: 'bundle.[chunkhash:8].js'
        // → bundle.8e0d62a3.js
    }
};

2.3 懒加载(按需加载) Webpack 可以帮我们按需加载代码模块——遵循 ES 标准的动态加载语法dynamic-import来编写代码即可,webpack 会自动处理使用该语法编写的模块。 使用import()实现按需加载

import(/* webpackChunkName: "lodash" */ 'lodash').then((_) => { 
  console.log(_.lash([1, 2, 3])) // 打印 3
})

2.4提取公共代码 而 webpack 4.x 则是把相关的功能包到了optimization.splitChunks中,直接使用该配置就可以实现代码分离。

2.6 IgnorePlugin

IgnorePlugin 是一个 webpack 内置的插件,可以直接使用webpack.IgnorePlugin 来获取。

2.7 tree-shaking

框架优化

  1. 路由懒加载, React.lazy
  2. 按需引入, React 支持多种按需加载组件的方式,包括使用 React.lazy 和 Suspense API、使用高阶组件和使用动态导入。

代码层面优化

  1. 慎用全局变量
  2. 缓存全局变量
  3. 适当的使用防抖和节流
  4. 如果首页过长,可以做图片懒加载(只展示视窗内图片,其他图片滚动时再加载)

SSR

SSR定义: Service-Side-Rendering(服务器端渲染)-将客户端或通用应用程序渲染到服务器上的HTML。

SSR适用场景

  • 需更好的支持 SEO 优势在于同步。搜索引擎爬虫是不会等待异步请求数据结束后再抓取信息的,如果 SEO 对应用程序至关重要,但你的页面又是异步请求数据,那 SSR 可以帮助你很好的解决这个问题。

  • 需更快的到达时间 优势在于慢网络和运行缓慢的设备场景。传统 SPA 需完整的 JS 下载完成才可执行,而SSR 服务器渲染标记在服务端渲染 html 后即可显示,用户会更快的看到首屏渲染页面。如果首屏渲染时间转化率对应用程序至关重要,那可以使用 SSR 来优化。

VUE实现SSR

通过 vue-server-renderer

React SSR

参考文档手把手从零实现 React SSR - 掘金 (juejin.cn) 简单的 SSR 其实实现很简单,只需要在服务端导入要渲染的组件,然后调用 react-dom/server 包中提供的 renderToString 方法将该组件的渲染内容输出为字符串后返回客户端即可。

下面是一个简单的例子:

import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';

import App from '../ui/App';

const app = express();

app.get('/', (_: unknown, res: express.Response) => {
   res.send(renderToString(<App />));
});

app.listen(4000, () => {
   console.log('Listening on port 4000');
});

白屏优化方案

所谓白屏时间,一般是当用户打开一个页面,从开始等待到页面第一个字符出现的时间。我们可以基于影响白屏时间长短的两个主要因素来解决——DNS 查询和首字符展示

前端侧,可以通过在页面中加入 dns-prefetch,在静态资源请求之前对域名进行解析,从而减少用户进入页面的等待时间。如下所示:

<meta http-equiv="x-dns-prefetch-control" content="on" />
​
<link rel="dns-prefetch" href="https://s.google.com/" >

首字符展示优化

方案一是使用loading图,但是体验稍差。 方案二可以使用骨架屏

长列表优化方案

  1. 懒加载,就是只展示部分内容, 等滚动到下面时,再加载10条, 继续滚动就继续加载, 但是这样加载的多了,比如上千条的时候,页面滚动就会变卡。
  2. 做虚拟列表,只展示前面10条, 滚动时,前10条放到另外的变量中, 显示11~20条, 以此类推。核心思路就是页面只显示固定数量的内容, 其他内容放缓存中或者请求API获取。