浏览器渲染机制

首先介绍一些基本概念

一、浏览器的主要组成

1. user interFace 用户界面

地址栏、书签、前进/后退/主页等,不同浏览器不一样

2. browser engine 浏览器引擎

用来查询及操作渲染引擎的接口

3. rendering engine 渲染引擎

解析html(Parse HTML)、解析样式(Parse Stylesheet) 、发送请求(send Request)、解析javascript(Evaluate Javascript)、重新计算样式(Recalculate Style)、计算布局样式(Layout)、更新Layer Tree(Update Layer Tree)、绘制(Paint) 、混合layers(Composite Layers)等

4. network 网络

用来发送请求(send Request)

5. javascript interpreter

解析javascript(Evaluate Javascript), 由编译和执行阶段组成

6. UI-backend ui后端

绘制(Paint)节点

7. data persistence 数据存储

文件缓存: 离线缓存manifiest、http缓存
数据缓存:localstorage、sessionstorage、cookie、indexDB

二、在浏览器地址栏输入一个url, 到渲染出一个页面, 发生了什么?

浏览器中输入url,发生了什么.jpeg

下面用一个例子详细描述上面的过程, 前往

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>浏览器的渲染机制</title>
    <link rel="stylesheet" href="./css/main.css">
    <link href="https://cdn.bootcss.com/element-ui/2.4.0/theme-chalk/index.css" rel="stylesheet">
    <script src="./js/main.js"></script>
    <script src="./js/async.js" async></script>
    <script src="./js/defer.js" defer></script>
    <script>
        window.addEventListener('pageshow', function () {
            console.log('pageshow')
        })
        window.addEventListener('load', function () {
            console.log('load')
        })
    </script>
</head>

<body>
    <div>lalala</div>
    <ul></ul>
    <script>
        var $ul = document.querySelector('ul')
        var result = ''
        for (let i = 0; i < 10; i++) {
            result += '<li>列表项' + i + '</li>';
        }

        $ul.innerHTML = result
    </script>
    <div>北京欢迎你</div>
</body>
</html>
  1. 解析html
    浏览器解析html是从上往下执行的,渲染引擎 负责Parse HTML 、Parse Stylesheet、Recalculate Style、Layout、Update Layer Tree、Receive Response、Receive Data,network 负责 Send request, javascript interpreter负责Evaluate Javascript。下面分析 每一行代码,浏览器是如何处理的
# Parse HTML开始
# 定义文档类型, 告诉浏览器以什么标准解析html
<!DOCTYPE html>
<html lang="en">
# 发送请求, Parse HTML继续
<link rel="stylesheet" href="./css/main.css">
# 发送请求, Parse HTML继续
<link href="https://cdn.bootcss.com/element-ui/2.4.0/theme-chalk/index.css" rel="stylesheet">
# 发送请求, Parse HTML继续
<script src="./js/main.js"></script>
# 发送请求, Parse HTML继续
<script src="./js/async.js" async></script>
# 发送请求, Parse HTML继续
<script src="./js/defer.js" defer></script>

# Parse HTML停止, 等待请求完成

然后会等待上面的资源加载完成, 并按照顺序执行。async和defer是异步加载,defer在DOMContentLoaded之前按顺序执行, async加载完即执行。async和普通script的区别是async资源的加载不会阻塞Parse HTML.执行顺序如下

Parse Stylesheet(main.css) -->Parse Stylesheet(index.css)-->Evaluate Javascript(main.js)
async.js加载完就会执行, 没有顺序。
defer .js会在解析完</html>标签之后, 触发DOMContentLoaded事件之前按顺序执行。执行完所有的defer script, 然后重新计算样式 (recalcuate style)构建render tree, 然后触发DOMContentLoaded事件。

发送请求.jpeg

执行.jpeg

从上面两幅图中可以看到, main.css加载完后, 马上就执行了Parse Stylesheet; 而main.js 加载完后, 会等待index.css 加载完并执行完Parse Stylesheet, 才会执行Evaluate Javascript. 因为这里css、js执行顺序和定义的顺序需要保持一致。

# Parse HTML 开始
// script标签会阻塞Parse HTML, 执行完script里面的内容之后, 才会继续往下解析
<script>
    window.addEventListener('pageshow', function () {
        console.log('pageshow')
    })
    window.addEventListener('load', function () {
        console.log('load')
    })
</script>
# Parse HTML 继续
<body>
    <div>lalala</div>
    <ul></ul>
# Parse HTML继续
// script标签会阻塞Parse HTML, 执行完script里面的内容之后, 才会继续往下解析
<script>
    var $ul = document.querySelector('ul')
    var result = ''
    for (let i = 0; i < 10; i++) {
        result += '<li>列表项' + i + '</li>';
    }
    $ul.innerHTML = result
</script>
# Parse HTML继续
    <div>北京欢迎你</div>
</body>
# Parse HTML, DOM tree 构建完成
</html>
  1. Evaluate defer script, 执行异步defer脚本

  2. 触发DOMContentLoaded事件

  3. recalculate style, 构建render tree

  4. Layout、Update Layer tree

  5. paint、composite Layers


    rendering.jpeg

三、prefetch、preload、dns-prefetch

# DNS预解析
<link rel="dns-prefetch" href="//cdn.bootcss.com">
# 浏览器会在空闲时间加载prefetch中的内容, 存储在磁盘上, 不会执行里面的js; 
<link rel="prefetch" href="main.js">

# 如果prefetch已完成, 会从缓存中读取; 如果未完成,会再次发起请求。所以prefetch可能会造成资源的重复请求.
<script src="./js/main.js"></script>
# 浏览器会立即加载preload中的内容, 保持在内存中, 不会执行里面的js.
<link rel="preload" href="/main.js" as="script">

# 如果preload已经完成, 会从内存缓存中读取;如果不存在, 会等待preload完成。
<script src="./js/main.js"></script>

所以prefetch一般用于预加载下个页面即将用到的资源,preload用于提前加载当前页面用到的资源。

优先级: preload > 普通 > prefetch

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容