Skip to content

页面渲染层面

渲染时机相关

懒加载

懒执行

渐进式渲染

html dom 树,css cssom 树 defer async 等等 遇到 js,preload 以及 prefetch 并发加载 重绘和回流 浏览器缓存

页面渲染阶段

  • css 在上、js 在下
  • 加载 css 推荐用 link 少用 @import
    • link 属于 html,@import 属于 css,需写在样式表开头
    • @import 是 css2.1 才出现的概念
    • 当 html 文件被加载时,link 引用的文件会同时被加载,而@import 引用的文件则会等页面全部下载完毕再被加载
  • 不重要的外置引入的 js 使用 defer 或者 async 属性异步加载

script 特性(attribute)可以为我们解决延迟问题:deferasync

defer

defer 特性告诉浏览器不要等待脚本。相反,浏览器将继续处理 HTML,构建 DOM。脚本会“在后台”下载,然后等 DOM 构建完成后,脚本才会执行

换句话说:

  • 具有 defer 特性的脚本不会阻塞页面
  • 具有 defer 特性的脚本总是要等到 DOM 解析完毕,但在 DOMContentLoaded 事件之前执行

下面这个示例演示了上面所说的第二句话:

html
<p>...content before scripts...</p>

<script>
    document.addEventListener('DOMContentLoaded', () =>
        alert('DOM ready after defer!'),
    );
</script>

<script
    defer
    src="https://javascript.info/article/script-async-defer/long.js?speed=1"
></script>

<p>...content after scripts...</p>
  1. 页面内容立即显示
  2. DOMContentLoaded 事件处理程序等待具有defer 特性的脚本执行完成。它仅在脚本下载且执行结束后才会被触发

具有 defer 特性的脚本保持其相对顺序,就像常规脚本一样。

defer 特性仅适用于外部脚本

如果 <script> 脚本没有 src,则会忽略 defer 特性。

async

async 特性与 defer 有些类似。它也能够让脚本不阻塞页面,但是,在行为上二者有着重要的区别

async 特性意味着脚本是完成独立的

  • 浏览器不会因 async 脚本而阻塞(与 defer 类似)
  • 其他脚本不会等待 async 脚本加载完成,同样,async 脚本也不会等待其他脚本
  • DOMContentLoaded 可能会发生在异步脚本之前(如果异步脚本在页面完成后才加载完成)
  • DOMContentLoaded 也可能发生在异步脚本之后(如果异步脚本很短,或者是从 HTTP 缓存中加载的)

换句话说,async 脚本会在后台加载,并在加载就绪时运行。DOM 和其他脚本不会等待它们,它们也不会等待其他的东西。async 脚本就是一个会在加载完成时执行的完全独立的脚本

动态脚本

我们可以使用 JavaScript 动态地创建一个脚本,并将其插入(append)到文档(document)中:

javascript
let script = document.createElement('script');
script.src = '/article/script-async-defer/long.js';
document.body.append(script); // (*)

默认情况下,动态脚本的行为是“异步”的。

也就是说:

  • 它们不会等待任何东西,也没有上面东西会等它们
  • 先加载完成的脚本先执行(“加载优先”顺序)

总结

asyncdefer 有一个共同点:加载这样的脚本都不会阻塞页面的渲染。因此,用户可以立即圆度并了解页面内容。但是,它们之前也存在一些本质的区别:

顺序DOMContnetLoaded
async加载优先顺序。脚本在文档中的顺序不重要——先加载完成的先执行不相关。可能在文档加载完成前加载并执行完毕。如果脚本很小或者来自缓存,同时文档足够长,就会发生这种情况
defer文档顺序(它们在文档中的顺序)在文档加载和解析完成之后(如果需要,则会等待),即在DOMContentLoaded 之前执行

在实际开发中,defer 用于需要整个 DOM 的脚本,脚本的相对执行顺序很重要的时候使用

async 用于独立脚本,例如计数器或广告,这些脚本的相对执行顺序无关紧要

浏览器渲染原理

async / defer

实际工作中,用 defer,async 用的很少

JS 的下载和执行会阻塞 HTML 解析

CSS 的下载和执行会阻塞 JS 执行

页面渲染

DOM树

CSS 树

合成为渲染树

布局(Layout):大小、尺寸

  • reflow(回流)

绘制(Paint):颜色、阴影

  • repaint(重绘)

合成(Composite):层次

https://csstriggers.com

代码优化技巧

动态导入

suspense,lazy

lazy 传入

懒加载

对于 js,叫动态引入

对于图片,叫懒加载

<img src ="product.jpg" />

<img src="placeholder.png" data-src="product.jpg" />

// 伪代码
window on scroll
	findImgs().each img
		new Image()
			.src = img.dataset.src
			.onload img.src = img.dataset.src

预加载:在快加载前先加载

CSS 代码优化技巧

删除无用CSS

使用更高效的选择器

减少重排(reflow/relayout)

不要使用 @import url.css

启动 GPU 硬件加速:transform: translate3d(0,0,0)

使用缩写(#FFFFFF => #FFF; 0.1 =>.1 0px =>0)

砍需求

JS代码优化技巧

尽量不要全局变量:因为全局变量太多会使变量查找变慢

尽量少操作DOM:可以使用 Fragment 一次性插入多个 DOM 节点

不要往页面中插入大量的 HTML:一定会卡

  • Fiber 就是因为之前 React 会递归循环卡顿才提出的
  • 使用 requestAnimateFrame 做时间切片

尽量少触发重排:可以使用节流和防抖来降低重排频率

尽量少用闭包,避免内存泄露:实际上是浏览器的bug

面试专用:使用虚拟滚动列表

使用虚拟滚动列表

十万条数据的问题

最好的方法是分页

按需渲染

代码优化

  • loading/骨架屏
  • web worker
  • 虚拟列表
  • 懒加载
  • dom/style 批量更新

动画性能优化

参考资料

参考资料