浏览器工作原理和实践(三)——页面 (下)

简介:   《浏览器工作原理与实践》是极客时间上的一个浏览器学习系列,在学习之后特在此做记录和总结。

  执行 JavaScript 添加元素是在一个任务中执行的,重新计算样式布局是在另外一个任务中执行。

  所谓强制同步布局,是指 JavaScript 强制将计算样式和布局操作提前到当前的任务中。


function foo() {
    let main_div = document.getElementById("mian_div")
    let new_node = document.createElement("li")
    let textnode = document.createTextNode("time.geekbang")
    new_node.appendChild(textnode);
    document.getElementById("mian_div").appendChild(new_node);
    //由于要获取到offsetHeight,
    //但是此时的offsetHeight还是老的数据,
    //所以需要立即执行布局操作
    console.log(main_div.offsetHeight)
}


55.png


  为了避免强制同步布局,可以调整策略,在修改 DOM 之前查询相关值。


function foo() {
    let main_div = document.getElementById("mian_div")
    //为了避免强制同步布局,在修改DOM之前查询相关值
    console.log(main_div.offsetHeight)
    let new_node = document.createElement("li")
    let textnode = document.createTextNode("time.geekbang")
    new_node.appendChild(textnode);
    document.getElementById("mian_div").appendChild(new_node);   
}


  (3)避免布局抖动。

  所谓布局抖动,是指在一次 JavaScript 执行过程中,多次执行强制布局和抖动操作。


function foo() {
    let time_li = document.getElementById("time_li")
    for (let i = 0; i < 100; i++) {
        let main_div = document.getElementById("mian_div")
        let new_node = document.createElement("li")
        let textnode = document.createTextNode("time.geekbang")
        new_node.appendChild(textnode);
        new_node.offsetHeight = time_li.offsetHeight;
        document.getElementById("mian_div").appendChild(new_node);
    }
}


56.png

  (4)合理利用 CSS 合成动画。

  合成动画是直接在合成线程上执行的,这和在主线程上执行的布局、绘制等操作不同,如果主线程被 JavaScript 或者一些布局任务占用,CSS 动画依然能继续执行。

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

  (5)避免频繁的垃圾回收。

  要尽量避免产生那些临时垃圾数据。尽可能优化储存结构,尽可能避免小颗粒对象的产生。

3)虚拟DOM

  虚拟 DOM 解决的事情。

  (1)将页面改变的内容应用到虚拟 DOM 上,而不是直接应用到 DOM 上。

  (2)变化被应用到虚拟 DOM 上时,虚拟 DOM 并不急着去渲染页面,而仅仅是调整虚拟 DOM 的内部状态,这样操作虚拟 DOM 的代价就变得非常轻了。

  (3)在虚拟 DOM 收集到足够的改变时,再把这些变化一次性应用到真实的 DOM 上。

  把虚拟 DOM 看成是 DOM 的一个 buffer,和图形显示一样,它会在完成一次完整的操作之后,再把结果应用到 DOM 上,这样就能减少一些不必要的更新,同时还能保证 DOM 的稳定输出。


57.png


八、WebComponent


  WebComponent能提供给开发者组件化开发的能力。

  对内高内聚,对外低耦合。对内各个元素彼此紧密结合、相互依赖,对外和其他组件的联系最少且接口简单。

  WebComponent提供了对局部视图的封装能力,可以让 DOM、CSSOM 和 JavaScript 运行在局部环境中,这样就使得局部的 CSS 和 DOM 不会影响到全局。

1)使用

  WebComponent 是一套技术的组合,具体涉及到了 Custom elements(自定义元素)、Shadow DOM(影子 DOM)和HTML templates(HTML 模板)。


<!DOCTYPE html>
<html>
<body>
    <!--
            一:定义模板
            二:定义内部CSS样式
            三:定义JavaScript行为
    -->
    <template id="geekbang-t">
        <style>
            p {
                background-color: brown;
                color: cornsilk
            }
            div {
                width: 200px;
                background-color: bisque;
                border: 3px solid chocolate;
                border-radius: 10px;
            }
        </style>
        <div>
            <p>time.geekbang.org</p>
            <p>time1.geekbang.org</p>
        </div>
        <script>
            function foo() {
                console.log('inner log')
            }
        </script>
    </template>
    <script>
        class GeekBang extends HTMLElement {
            constructor() {
                super()
                //获取组件模板
                const content = document.querySelector('#geekbang-t').content
                //创建影子DOM节点
                const shadowDOM = this.attachShadow({ mode: 'open' })
                //将模板添加到影子DOM上
                shadowDOM.appendChild(content.cloneNode(true))
            }
        }
        customElements.define('geek-bang', GeekBang)
    </script>
    <geek-bang></geek-bang>
    <div>
        <p>time.geekbang.org</p>
        <p>time1.geekbang.org</p>
    </div>
    <geek-bang></geek-bang>
</body>
</html>


  要使用 WebComponent,通常要实现下面三个步骤。

  (1)首先,使用 template 属性来创建模板。

  (2)其次,需要创建一个 GeekBang 的类。查找模板内容;创建影子 DOM;再将模板添加到影子 DOM 上。

  (3)最后,可以像正常使用 HTML 元素一样使用该元素。

2)影子 DOM

  WebComponent的核心就是影子 DOM。

  (1)影子 DOM 中的元素对于整个网页是不可见的;

  (2)影子 DOM 的 CSS 不会影响到整个网页的 CSSOM,影子 DOM 内部的 CSS 只对内部的元素起作用。


58.png


  从图中可以看出,使用了两次 geek-bang 属性,那么就会生成两个影子 DOM,并且每个影子 DOM 都有一个 shadow root 的根节点。

  可以将要展示的样式或者元素添加到影子 DOM 的根节点上,每个影子 DOM 都可以看成是一个独立的 DOM,它有自己的样式、自己的属性,内部样式不会影响到外部样式,外部样式也不会影响到内部样式。


九、安全沙箱


  在渲染进程和操作系统之间建一道墙,即便渲染进程由于存在漏洞被黑客攻击,但由于这道墙,黑客就获取不到渲染进程之外的任何操作权限。将渲染进程和操作系统隔离的这道墙就是安全沙箱。

  浏览器中的安全沙箱是利用操作系统提供的安全技术,让渲染进程在执行过程中无法访问或者修改操作系统中的数据,在渲染进程需要访问系统资源的时候,得通过浏览器内核来实现,然后将访问的结果通过 IPC 转发给渲染进程。

  了解了被安全沙箱保护的进程会有一系列的受限操作之后,接下来就可以分析渲染进程和浏览器内核各自都有哪些职责,如下图:


59.png


1)持久存储

  文件内容的读写都是在浏览器内核中完成的:

  (1)存储 Cookie 数据的读写。通常浏览器内核会维护一个存放所有 Cookie 的 Cookie 数据库,然后当渲染进程通过 JavaScript 来读取 Cookie 时,渲染进程会通过 IPC 将读取 Cookie 的信息发送给浏览器内核,浏览器内核读取 Cookie 之后再将内容返回给渲染进程。

  (2)一些缓存文件的读写也是由浏览器内核实现的,比如网络文件缓存的读取。

2)网络访问

  同样有了安全沙箱的保护,在渲染进程内部也是不能直接访问网络的,如果要访问网络,则需要通过浏览器内核。

  不过浏览器内核在处理 URL 请求之前,会检查渲染进程是否有权限请求该 URL,比如检查 XMLHttpRequest 或者 Fetch 是否是跨站点请求,或者检测 HTTPS 的站点中是否包含了 HTTP 的请求。

3)用户交互

  由于渲染进程不能直接访问窗口句柄,所以渲染进程需要完成以下两点大的改变。

  (1)第一点,渲染进程需要渲染出位图。为了向用户显示渲染进程渲染出来的位图,渲染进程需要将生成好的位图发送到浏览器内核,然后浏览器内核将位图复制到屏幕上。

  (2)第二点,操作系统没有将用户输入事件直接传递给渲染进程,而是将这些事件传递给浏览器内核。然后浏览器内核再根据当前浏览器界面的状态来判断如何调度这些事件,如果当前焦点位于浏览器地址栏中,则输入事件会在浏览器内核内部处理;如果当前焦点在页面的区域内,则浏览器内核会将输入事件转发给渲染进程。

  之所以这样设计,就是为了限制渲染进程有监控到用户输入事件的能力,所以所有的键盘鼠标事件都是由浏览器内核来接收的,然后浏览器内核再通过 IPC 将这些事件发送给渲染进程。

十、Chrome性能工具

1)Audits

  性能指标的分数是由六项指标决定的,它们分别是:

  (1)首次绘制 (First Paint)。

  如果 FP 时间过久,那就是页面的 HTML 文件可能由于网络原因导致加载时间过久,可利用网络面板做性能分析。

  (2)首次有效绘制 (First Meaningfull Paint),由于 FMP 计算复杂,所以现在不建议使用该指标了;

  (3)首屏时间 (Speed Index),即 LCP;

  如果 FMP 和 LCP 消耗时间过久,那么有可能是加载关键资源花的时间过久,也有可能是 JavaScript 执行过程中所花的时间过久,所以可以针对具体的情况来具体分析。

  (4)首次 CPU 空闲时间 (First CPU Idle),也称为 First Interactive,对大部分用户输入做出响应即可;

  要缩短首次 CPU 空闲时长,就需要尽可能快地加载完关键资源,尽可能快地渲染出来首屏内容。

  (5)完全可交互时间 (Time to Interactive),简称 TTI,页面的内容已经完全显示出来了,所有的 JavaScript 事件已经注册完成,页面能够对用户的交互做出快速响应,通常满足响应速度在 50 毫秒以内。

  如果要解决 TTI 时间过久的问题,可以推迟执行一些和生成页面无关的 JavaScript 工作。

  (6)最大估计输入延时 (Max Potential First Input Delay),估计 Web 页面在加载最繁忙的阶段,窗口中响应用户输入所需的时间。

  为了改善该指标,可以使用 Web Worker 来执行一些计算,从而释放主线程。另一个有用的措施是重构 CSS 选择器,以确保它们执行较少的计算。


60.png


  在渲染进程确认要渲染当前的请求后,渲染进程会创建一个空白页面,把创建空白页面的这个时间点称为 First Paint,简称 FP。

  上图中,bundle.js 是关键资源,因此需要完成加载之后,渲染进程才能执行该脚本,然后脚本会修改 DOM,引发重绘和重排等一系列操作,当页面中绘制了第一个像素时,把这个时间点称为 First Content Paint,简称 FCP。

  接下来继续执行 JavaScript 脚本,当首屏内容完全绘制完成时,把这个时间点称为 Largest Content Paint,简称 LCP。

  接下来 JavaScript 脚本执行结束,渲染进程判断该页面的 DOM 生成完毕,于是触发 DOMContentLoad 事件。等所有资源都加载结束之后,再触发 onload 事件。

2)Performance

  Performance 可以记录站点在运行过程中的性能数据,有了这些性能数据,就可以回放整个页面的执行过程,这样就方便定位和诊断每个时间段内页面的运行情况

  下图区域 1,设置该区域中的“Network”来限制网络加载速度,设置“CPU”来限制 CPU 的运算速度。区域 2 和区域 3有两个按钮,黑色按钮是用来记录交互阶段性能数据的,带箭头的圆圈形按钮用来记录加载阶段的性能数据。


61.png


  无论采用哪种方式录制,最终所生成的报告页都是一样的。


62.png


  (1)概览面板

  引入了时间线,Performance 就会将几个关键指标,诸如页面帧速 (FPS)、CPU 资源消耗、网络请求流量、V8 内存使用量 (堆内存) 等,按照时间顺序做成图表的形式展现出来。

  除了以上指标以外,概览面板还展示加载过程中的几个关键时间节点,如 FP、LCP、DOMContentLoaded、Onload 等事件产生的时间点。这些关键时间点体现在了几条不同颜色的竖线上。

  (2)性能面板

  记录了非常多的性能指标项,比如 Main 指标记录渲染主线程的任务执行过程,Compositor 指标记录了合成线程的任务执行过程,GPU 指标记录了 GPU 进程主线程的任务执行过程。

  通过概览面板来定位问题的时间节点,然后再使用性能面板分析该时间节点内的性能数据。比如概览面板中的 FPS 图表中出现了红色块,那么点击该红色块,性能面板就定位到该红色块的时间节点内。


63.png


  (3)解读性能面板的各项指标

  先看最为重要的 Main 指标,它记录了渲染进程的主线程的任务执行记录。


64.png


  光栅化线程池 (Raster),用来让 GPU 执行光栅化的任务。因为光栅化线程池和 GPU 进程中的任务执行也会影响到页面的性能,所以性能面板也添加了这两个指标,分别是 Raster 指标和 GPU 指标。

  渲染进程中除了有主线程、合成线程、光栅化线程池之外,还维护了一个 IO 线程。

  除此之外,性能面板还添加了其他一些比较重要的性能指标。

  a、第一个是 Network 指标,网络记录展示了页面中的每个网络请求所消耗的时长,并以瀑布流的形式展现。

  b、第二个是 Timings 指标,用来记录一些关键的时间节点在何时产生的数据信息,诸如 FP、FCP、LCP 等。

  c、第三个是 Frames 指标,帧记录就是用来记录渲染进程生成所有帧信息,包括了渲染出每帧的时长、每帧的图层构造等信息。

  d、第四个是 Interactions 指标,用来记录用户交互操作,比如点击鼠标、输入文字等交互信息。

  (4)详情面板

  通过上面的图形只能得到一个大致的信息,如果想要查看这些记录的详细信息,就需要引入详情面板了。

相关实践学习
基于阿里云DeepGPU实例,用AI画唯美国风少女
本实验基于阿里云DeepGPU实例,使用aiacctorch加速stable-diffusion-webui,用AI画唯美国风少女,可提升性能至高至原性能的2.6倍。
相关文章
|
1月前
|
缓存 JavaScript
vue阻止浏览器刷新和关闭页面提示
使用场景:在使用vuex进行缓存管理时,页面的缓存会随着页面关闭而消失,如果缓存动作仍在进行中,关闭页面会导致数据丢失,此时需要阻止页面关闭
50 3
|
2月前
|
存储 监控 安全
360 企业安全浏览器基于阿里云数据库 SelectDB 版内核 Apache Doris 的数据架构升级实践
为了提供更好的日志数据服务,360 企业安全浏览器设计了统一运维管理平台,并引入 Apache Doris 替代了 Elasticsearch,实现日志检索与报表分析架构的统一,同时依赖 Doris 优异性能,聚合分析效率呈数量级提升、存储成本下降 60%....为日志数据的可视化和价值发挥提供了坚实的基础。
360 企业安全浏览器基于阿里云数据库 SelectDB 版内核 Apache Doris 的数据架构升级实践
|
2月前
|
数据采集 Web App开发 JSON
浏览器插件:WebScraper基本用法和抓取页面内容(不会编程也能爬取数据)
本文以百度为实战案例演示使用WebScraper插件抓取页面内容保存到文件中。以及WebScraper用法【2月更文挑战第1天】
124 2
浏览器插件:WebScraper基本用法和抓取页面内容(不会编程也能爬取数据)
|
2月前
|
存储 缓存 前端开发
浏览器缓存工作原理是什么?
浏览器缓存工作原理是什么?
|
18天前
【超实用】Angular如何修改当前页面网页浏览器url后面?param1=xxx&param2=xxx参数(多用于通过浏览器地址参数保存用户当前操作状态的需求),实现监听url路由切换、状态变化。
【超实用】Angular如何修改当前页面网页浏览器url后面?param1=xxx&param2=xxx参数(多用于通过浏览器地址参数保存用户当前操作状态的需求),实现监听url路由切换、状态变化。
|
18天前
|
搜索推荐 前端开发 UED
html页面实现自动适应手机浏览器(一行代码搞定)
html页面实现自动适应手机浏览器(一行代码搞定)
19 0
|
1月前
|
Web App开发 缓存 网络协议
|
2月前
|
存储 安全 前端开发
浏览器跨窗口通信:原理与实践
浏览器跨窗口通信:原理与实践
44 0
|
2月前
|
消息中间件 JavaScript 前端开发
前端秘法进阶篇----这还是我们熟悉的浏览器吗?(浏览器的渲染原理)
前端秘法进阶篇----这还是我们熟悉的浏览器吗?(浏览器的渲染原理)
|
3月前
|
搜索推荐 前端开发 UED
html页面实现自动适应手机浏览器(一行代码搞定)
html页面实现自动适应手机浏览器(一行代码搞定)
62 0

热门文章

最新文章