浏览器页面渲染

当我们打开一个网站我们期待什么?

1.希望页面内容能尽快加载完,不要让我一直等
2.网站交互是流畅的
3.能刷到感兴趣的内容

进程和线程

进程:进程是计算机中程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位。
线程:线程是操作系统能够进行运算调度的最小单位。

进程=火车,线程=车厢

  • 线程在进程下行进 (单纯的车厢无法运行)
  • 一个进程可以包含多个线程 (一辆火车可以包含多个车厢)
  • 不同进程间数据很难共享 (一辆火车上的乘客很难换乘到另外一辆火车,比如站点换乘)
  • 同一进程下不同线程间数据很容易共享 (A车厢很容易换乘到B车厢)
  • 进程间不会相互影响,一个线程挂掉将导致整个进程挂掉

当一个程序启动时,一个进程就被创建了。应用程序可能会创建一些线程帮助它完成某些工作。操作系统会划分出一部分
内存给这个进程,当前应用程序的所有状态都将保存在这个私有的内存空间中。

浏览器是多进程

以Chrome 为例,主要的进程有4个
1.浏览器进程(Browser Process)负责浏览器的Tab的前进、后退、地址栏、书签栏的工作和处理浏览器一些不可见的底层操作,比如网络请求和文件访问。
2.渲染进程(Renderer Process) 负责一个Tab内的显示相关的工作,也称渲染引擎。
3.插件进程(Plugin Process)负责控制网页使用到的插件
4.GPU进程 (GPU Process) 负责处理整个应用程序的GPU任务

浏览器渲染进程

  1. GUI渲染线程
  • 解析HTML,CSS,构建DOM树、构建render树,布局和绘制等
  • 界面重绘(Repaint)或回流(reflow)
  1. JS引擎线程
  • 处理JavaScript程序
  • JS引擎一直等待着任务队列中任务的到来,然后加以处理
  1. 事件触发线程
  • 当JS引擎执行如setTimeOut代码块时,或者是其他的如网络异步请求、鼠标点击等时,会将对应任务添加到事件线程中。当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理的事件队列中,等待JS引擎空闲时处理。
  1. 定时器线程
  • setInterval与setTimeout所在线程。计时完毕后,将对应事件添加到事件队列中,等待JS引擎空闲时处理。
  1. 异步网络请求线程
  • 处理网络请求,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。等待JS引擎空闲时处理。

GUI渲染线程和JS引擎线程是互斥的,当JS引擎线程执行时,GUI渲染线程会被挂起。这是因为JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。

导航

1.dns 查找
2.tcp 三次握手
3.服务端响应

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!doctype HTML>
<html>
<head>
<meta charset="UTF-8"/>
<title>My simple page</title>
<link rel="stylesheet" src="styles.css"/>
<script src="myscript.js"></script>
</head>
<body>
<h1 class="heading">My Page</h1>
<p>A paragraph with a <a href="https://example.com/about">link</a></p>
<div>
<img src="myimage.jpg" alt="image description"/>
</div>
<script src="anotherscript.js"></script>
</body>
</html>

解析

一旦浏览器收到数据的第一块,它就可以开始解析收到的信息。“推测性解析”,“解析”是浏览器将通过网络接收的数据转换为DOM和CSSOM的步骤,通过渲染器把DOM和CSSOM在屏幕上绘制成页面。

构建DOM树

dom树

子资源加载

除了HTML文件,网站通常还会使用到一些诸如图片,CSS样式以及JavaScript脚本等子资源。这些文件会从缓存或者网络上获取。
1.当遇到script标签时,会阻塞解析html, 添加async或者defer 属性可以避免阻塞解析html

script 脚本加载和执行的区别(红线代表Javascript 执行,绿线代表HTML解析, 蓝色线代表Javascript加载)

script加载和解析

defer脚本会在DOMContentLoaded事件前执行完,有序
async脚本会在load事件前完成,无序

构建CSSOM树

构建CSSOM并不需要等待所有DOM都构建完毕。而是在解析HTML构建DOM时,若遇见CSS会立刻构建CSSOM。即DOM 和 CSSOM 是并行构建的。
构建CSSOM和构建DOM是可以同时进行的。不过进入下一个构建渲染树阶段必须要等待CSSOM构建完毕后才能进行。因为CSS的每个属性都可以改变CSSOM,所以会存在这样一个问题:假设前面几个字节的CSS将字体大小设置为16px,后面又将字体大小设置为14px,那么如果不把整个CSSOM构建完整,最终得到的CSSOM其实是不准确的。所以必须等CSSOM构建完毕才能进入到下一个阶段,哪怕DOM已经构建完,它也得等CSSOM,然后才能进入下一个阶段。
所以,CSSOM 会阻塞渲染,只有当 CSSOM 构建完毕后才会进入下一个阶段构建渲染树。

渲染

渲染步骤包括样式、布局、绘制,在某些情况下还包括合成。在解析步骤中创建的CSSOM树和DOM树组合成一个Render树,然后用于计算每个可见元素的布局,然后将其绘制到屏幕上。

样式 Style

第三步是将DOM和CSSOM组合成一个Render树,计算样式树或渲染树从DOM树的根开始构建,遍历每个可见节点。

布局 Layout

渲染树上运行布局以计算每个节点的几何体。

绘画 Paint

在绘制或光栅化阶段,浏览器将在布局阶段计算的每个框转换为屏幕上的实际像素。绘画包括将元素的每个可视部分绘制到屏幕上,包括文本、颜色、边框、阴影和替换的元素(如按钮和图像)。

合成

合成是将页面分成若干层,然后分别对他们进行光栅化,最后在一个单独的线程(合成线程)里面合成一个页面的技术。

重绘

当 render tree 中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会影响布局的,比如 background-color,这个过程叫做重绘(repaint) 重绘时机有

  • 当一个元素的外观的可见性 visibility 发生改变的时候,但是不影响布局
  • 回流会引起重绘

回流

当 render tree 中的一部分(或全部)因为元素的尺寸、布局、显示/隐藏等改变而需要重新构建,这个过程称作回流(reflow)。页面第一次加载的时候,至少发生一次回流。 回流时机有

  • 页面渲染初始化。
  • 调整窗口大小。
  • 改变字体,比如修改网页默认字体。
  • 增加或者移除样式表。
  • 内容变化,比如文本改变或者图片大小改变而引起的计算值宽度和高度改变。
  • 激活 CSS 伪类,比如 :hover
  • 操作 class 属性。
  • 脚本操作 DOM,增加删除或者修改 DOM
  • 节点,元素尺寸改变——边距、填充、边框、宽度和高度。
  • 计算 offsetWidth 和 offsetHeight 属性。
  • 设置 style 属性的值。

在回流的时候,浏览器会使 render tree 中受到影响的部分失效,并重新构造这部分渲染树,完成回流后,浏览器会重新绘制受影响的部分到屏幕中,该过程成为重绘。因此回流必将引起重绘,而重绘不一定会引起回流。
Reflow 的成本比 Repaint 高得多的多。DOM Tree 里的每个结点都会有 reflow 方法,一个结点的 reflow 很有可能导致子结点,甚至父点以及同级结点的 reflow。

load和DOMContentLoaded

  1. 当load事件触发时,页面上所有的DOM,样式表,脚本,图片,flash都已经加载完成了。
  2. 当 DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash。

参考链接

Javascript设计模式

为什么需要了解设计模式

在实际工作中很多时候一个js或者ts文件,写着写着就大几百行了,当你后面接受到新需求时,需要再在里面修改代码时,这是一个特别痛苦的过程,自己写的代码维护起来都很难,其他同事再来接手这种都是直接懵的,估计改的时候都是崩溃的。回顾自己写的代码,代码乱糟糟的一团,代码块没有明确的分类,函数和变量命名比较随意,函数里面充斥着if else 条件判断,但是你真准备动手重构代码的时候,又发现太多代码里面有太多业务逻辑判断,功能耦合特别严重,光凭自己发现无从下手,这时候就想到需要看设计模式的书籍了。

多态- 把不变的部分隔离出来,把可变的部分封装起来

看完《JavaScript设计模式与开发实践》后,书中开篇提到一个概念,把设计拆分成可变的与不可变的。多态:同一操作作用于不同对象,可以产生不同的解释和不同的执行结果。多态最根本的作用就是通过多态把过程化的条件分支语句转化为对象的多态性,从而消除这些条件分支。

16种设计模式

  • 单例模式
  • 策略模式
  • 代理模式
  • 迭代器模式
  • 发布订阅模式
  • 命令模式
  • 组合模式
  • 模板方式模式
  • 享元模式
  • 职责链模式
  • 中介者模式
  • 装饰者模式
  • 状态模式
  • 适配器模式
  • 外观模式

代码重构

1.提炼函数
2.合并重复的条件判断
3.把条件分支语句提炼成函数
4.提前让函数退出代替嵌套条件分支
5.尽量减少参数数量
6.用return 退出多重循环

react 基础知识

prop 和 state

  1. prop 不可改变,从父组件传递到子组件, state是组件内部状态,state改变,页面也会重新渲染,如果state是对象,需要改变引用地址。

react class 组件

  1. class组件 具有生命周期钩子函数,可以在钩子函数改变state,例如componentDidMount()
  2. class组件 新手更容易掌握,render() 函数返回jsx, 组件嵌套可以通过prop,从上往下传递,如瀑布流,每层都可以添加prop

react hooks

  1. hooks