通俗的说下浏览器的渲染过程

最初的模型:

  • 浏览器下载 htmljavascript

  • 开始解析 htmlcss

    • 碰见外链资源, 保存起来, 而且继续解析html

    • html 解析结束java

    • 开始下载外链浏览器

      • 下载结束dom

        • 开始处理async

          • css 处理google

          • js 处理线程

            • 处理完毕, 开始渲染code

            • 用户看到界面

这个模型的基础是: 浏览器是单线程的.

可是实际上: 浏览器不是单线程, 是多个线程.
浏览器有以下几个线程:
1 javascript引擎线程
2 界面渲染线程
3 浏览器事件触发线程
4 http请求线程

也就是说: 下载和解析是能够同步的, 碰见外链就开始下载.

更改以后的模型

  • 浏览器下载 html

  • 开始解析 html

    • 碰见外链资源, 开始下载, 而且继续解析

    • html 解析结束

    • 下载结束

      • 开始处理

        • css 处理

        • js 处理

          • 处理完毕, 开始渲染

          • 用户看到界面

这个模型的基础是:
资源下载和 html 解析是同步的, 全部的资源下载结束, 才开始进行下一步:渲染.
实际情景是:
资源大体能够分红
css
js
imgs
others

imgs以及 others 这种, 若是一个资源过大, 好比说一个媒体文件100M, 非要等到用户下载结束
才开始下一步, 这显然是不合理的.
而 css 和 js 是能够对页面产生修改和效果的, 因此必需要等待它们的参与才能进行下一步操做,
好比说 css,js 都没有下载解析执行结束, 就开始下一步渲染, 最终渲染的结果是一个没有样式的
页面.

看浏览器是否是这么想的?
实验:
在demo 中加一个 p 标签, 在底部加一个 css 外链
若是 css 都没有加载完毕, p 标签就显示出来了, 说明浏览器就没有等 css 文件
也就是说: html 解析结束以后, 什么都无论, 就开始下一步渲染了.

demo:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>demo</title>
</head>
<body>
    <p>hello world</p>
    <link rel="stylesheet" href="https://www.google.com.hk/test.css">
</body>
</html>

结果:
css 没有加载以前, 页面空白, 说明 html 解析结束以后, 会等到css加载出来再开始渲染.

更一步的实验:
资源变成 js, 页面先渲染出来, js 还在加载.
资源变成 img, 页面会先渲染出来, img 还在加载.

结论:
**html 解析结束以后, 会先等到 css 下载和解析结束以后(经过 link 标签知道是 css 文件)
再开始下一步, 并不会等全部的非 js 资源.**

因此模型变成:

  • 浏览器下载 html

  • 开始解析 html

    • 碰见外链资源, 开始下载, 而且继续解析

    • html 解析结束

    • 等到css下载结束

      • 开始处理 css

        • 处理完毕, 开始渲染

        • 用户看到界面

如今考虑一种状况:
js 是有能力去改变 DOM, 那么若是都渲染结束了, js 这个时候开始执行了,而后把页面从新干掉了.
这个时候怎么办?

只能将修改应用到已经渲染好的页面上.

考虑一种极端状况, 页面里面有成千上万的节点, 好比说1万个节点, css 文件都 一两百k
辛辛苦苦浏览器把页面渲染出来了, 而后这个时候, js 下载结束开始执行, 啪的一下把页面

document.write('中奖啦');

这种状况确定不能容许发生.

为了不这种状况, 能够这样
先全局检测下是否是有 script 标签, 若是有的话, html 就等着 script 加载执行以后,
再开始渲染.

这种方式有一个很差: 也就是说, 即便咱们如今有了 html 和 css, 其实均可以把页面渲染出来了
可是仍是要等 script 下载执行以后才敢进行渲染, 有点投鼠忌器的感受.
有时候等半天, 可能 script 返回的就一句话:

console.log('逗你玩');

并且全局检测 script 标签, 这句话说的简单, 其实是要创建在你已经把 html 解析结束了以后才知道
到底有没有 script 标签.

因此实际的状况是:

浏览器不知道页面里面有没有 script 标签
不知道script 里面会不会有 DOM 操做, 是 '中奖啦' 仍是 '逗你玩'

面对这种状况, 实际上只能赌, 或者说博弈.

浏览器拿到 html 和 css 以后依旧开始解析渲染
为了减少万一中奖以后全盘都输的状况, 当碰见 script 以后

中止解析, 专心下载 js 文件
    这样即便中奖, 我也就渲染了前面了一点内容, 后面的尚未渲染, 输少点
    可是这样后面若是还有资源要加载。。。

因此更新策略:

浏览器碰见 script, 开始下载, 把后面的html 解析掉, 全部的资源都开始下载
而后回头安心等这个 script, 看看到底中奖不中奖.

因此模型变成:

  • 浏览器下载 html

  • 开始解析 html

    • 碰见外链, 开始下载

      • 发现 script 外链, 继续html解析

        • 将页面分红两部分, script 标签以前, 以后

          • 处于 script 以前的页面

            • css 下载结束

              • 渲染

              • 处于 script 以后的页面

                • script 下载完毕, 执行

                  • css 下载完毕

                    • 渲染

浏览器不是等全部的资源都下载结束才开始渲染
浏览器也不是等到全部的 js 都执行结束以后才会渲染

具体的渲染过程

当 html 解析成 DOM tree, css 解析成 CSSOM, 两者合并成
Render Tree, 就能够开始渲染了.

首先要先计算这棵树上面的全部的节点的位置, 这一步叫作 layout
而后要给每一个节点上色, 这一步叫作 paint
layout 和 paint 统称为 render.

当页面的元素的位置修改以后, 就会出现 relayout (重绘)
relayout 一定会形成 repaint.

附录: defer, async 之间的区别.

defer
以前说了, 浏览器碰见 js 以后会始终等着它执行, 后面的内容都不渲染了
而后常常就报错了, 我草获取某个节点怎么没有, 想要获取后面的节点可是节点尚未
渲染出来, 因此呢, 因此就有了 defer
至关于说, 我等你所有渲染以后再执行吧, 要否则我总是出错, 烦人.
执行要在全部元素解析完成以后,DOMContentLoaded 事件触发以前完成。

async还有一种状况, 就是说, 浏览器辛辛苦苦等到 js 下载执行结束发现哎吆我草这个玩意对dom屁改动都没有, 我等她干吗啊, 我草草, 那就出来 async这个东西就是说, 不须要等我, 也不须要关心个人执行, 我不会干扰你的, 你作本身的事情就好.同时也解决了多个js之间的依赖, 加上这个就表示, 我是孤立的, 我不依赖任何其余js也不给任何其余js 依赖.

相关文章
相关标签/搜索