# 页面渲染流程
浏览器渲染进程(浏览器内核)拿到内容后,渲染步骤大致可以分为以下几步:
**1.解析 HTML**:解析 HTML 并构建 DOM 树
**2.解析 CSS**:解析 CSS 并构建 CSSOM 树(样式树)
**3.合成渲染树**:将DOM与CSSOM合并成一个Render Tree(渲染树)
**4.布局计算**:根据渲染树的结构,计算每个节点在屏幕上的大小、位置等属性,生成布局信息 Layout。这个过程会发生回流和重绘
**5.分层**:创建复合图层,进行硬件加速,提高性能
**6.绘制页面**:将生成的布局信息交给浏览器的绘图引擎,通过 GPU 加速将像素Paint到屏幕上
**7.回流和重绘**:如果页面发生改变,浏览器需要重新计算布局和绘制,这可能导致性能问题。因此我们应尽量避免频繁的DOM操作和调整元素样式,以减少不必要的回流和重绘。

## 1、解析 HTML,构建 DOM 树
解析过程中遇到 CSS 解析 CSS、遇到 JS 执行 JS,为了提高解析效率,浏览器在开始解析前会启动一个预解析的线程,率先下载 HTML 中的外部CSS和外部JS文件。
如果主线程解析到 link 位置,此时外部 CSS 文件还没下载解析好,主线程不会等待,继续解析后续的 HTML。这是因为 CSS 的下载解析工作是在预解析线程中进行的,这就是 CSS 不会阻塞 HTML 解析的根本原因。
如果主线程解析到 script 位置,会停止解析 HTML。这是因为 JS 代码的执行过程可能会修改当前的 DOM 树,所以 DOM 树的生成必须暂停。这就是 JS 会阻塞 HTML 解析的根本原因。
**构建 DOM 树的过程**
**1)转换为字符**:浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成各个字符
**2)令牌化**:将字符串转换成 Token,例如:html、head、body等。Token 中会标识出当前 Token 是 “开始标签” 或是 “结束标签” 亦或是 “文本” 等信息
**3)词法分析**:转换过程中,不是等所有 Token 都转换完成后再去生成节点对象,而是一边生成 Token,一边消耗 Token 来生成节点对象。换句话说,每个 Token 被生成后,会立刻消耗这个 Token 创建出节点对象。注意:带有结束标签标识的 Token 不会再去创建节点对象。
**4)DOM树构建**:由于 HTML 标记定义不同标记之间的关系,创建的对象会链接在一个树数据结构内,此结构也会捕获原始标记中定义的父项-子项关系。
## 2、解析 CSS,构建 CSSOM 树
构建 CSSOM 树的过程与构建 DOM 树的过程非常相似,同样字节 -> 字符串 -> Token -> Node -> CSSOM
CSS匹配HTML元素是一个相当复杂且有性能问题的事情,浏览器要递归CSSOM树,确定每一个节点的样式到底是什么。所以 DOM 树要小,CSS 尽量用id和class,切勿过度层叠下去。
## 3、合成渲染树(样式计算)
* 当我们得到DOM树和CSSOM树后,就需要将这两棵树组合为Render tree
* 这一过程结束后,渲染树只会包含需要显示的节点和这些节点的样式信息,若某个节点是display: none的,那么就不会出现在渲染树中
* 主线程会遍历 DOM 树,依次为树中的每个节点计算出它最终的样式,这一过程称为Computed Style。在这一过程中很多预设值会变成绝对值:如red -> rgb(255, 0, 0)、em -> px等
* 在计算完样式后,可以通过 JS 的window.getComputedStyle(element)方法来获取计算后的样式,非常方便。
## 4、布局
* 浏览器生成渲染树以后,就会根据渲染树来进行布局(也叫回流 )。布局阶段浏览器会弄清楚各个节点在页面中的确切 位置和大小,这一行为也被称为自动重排
* 布局流程的输出是一个盒模型,所有相对测量值都将转换为屏幕上的绝对像素
* 大部分时候,DOM 树和布局树并非一一对应。 比如:


* display:none 的节点没有几何信息,因此不会生成到布局树;
* 伪元素选择器,虽然 DOM 树中不存在这些伪元素节点,但它们拥有几何信息,所以会生成到布局树中。
* 匿名行盒、匿名块盒等都会导致 DOM 树和布局树无法一一对应。
## 5、分层(普通图层、复合图层)
* 主线程会使用一套复杂的策略对整个布局树进行分层。 分层的好处在于,将来某一个层改变后,仅会对该层进行后续处理,从而提升效率。滚动条、堆叠上下文、transform、opacity 等样式都会或多或少的影响分层结果,也可以通过will-change属性更大程度的影响分层结果。
* 可通过f12 -> 更多工具 -> layers 查看分层情况
* 普通图层:
* 普通文档流内可以理解为一个复合图层(这里称为默认复合层,里面不管添加多少元素,其实都是在同一个复合图层中)
* absolute 布局(fixed 也一样),虽然可以脱离普通文档流,但它仍然属于默认复合层
* 复合图层:
* 可以通过硬件加速方式,声明一个新的复合图层,它会单独分配资源。(当然也会脱离文档流,这样一来,不管这个复合图层中怎么变化,也不会影响默认复合层里的回流重绘)
* 在 GPU 中,各个复合图层是单独绘制的,所以互不影响,这也是为什么某些场景硬件加速效果一级棒。
* 可以Chrome源码调试 -> More Tools -> Rendering -> Layer borders中看到,黄色的就是复合图层信息。
* 如何变成复合图层(硬件加速,提升性能)?
* 最常用的:translate3d、translateZ
* opacity属性/过渡动画:需要动画执行的过程中才会创建合成层,动画未开始、动画结束后元素还会回到之前的状态
* will-change 属性(这个比较偏僻):一般配合 opacity 和 translate 使用,作用是提前告诉浏览器要变化,这样浏览器会开始做一些优化工作(最好用完后就释放)
* video、iframe、canvas、webgl等元素
* 其他:如以前的 flash 插件
* 尽量不要大量使用复合图层,否则由于资源消耗过度,页面反而会变的更卡
* 使用硬件加速时,尽可能的使用 index,防止浏览器默认给后续的元素创建复合层渲染
* webkit CSS3中,如果这个元素添加了硬件加速,并且index层级比较低,那么在这个元素的后面其它元素(层级比这个元素高的,或者相同的,并且 releative 或 absolute 属性相同的),会默认变为复合层渲染,如果处理不当会极大的影响性能
* 隐式合成的概念:如果a是一个复合图层,而且b在a上面,那么b也会被隐式转为一个复合图层
## 6、绘制页面
**6.1、主线程会为每个层单独产生绘制指令集**,用于描述这一层的内容该如何画出来。

完成绘制后,主线程将每个图层的绘制信息提交给合成线程,剩余工作将由合成线程完成
**6.2、分块**:合成线程首先对每个图层进行分块,将其划分为更多的小区域(懒加载提升性能)。 它会从线程池中拿取多个线程来完成分块工作。

这些块的大小一般不会特别大,通常是 256 * 256 或 512 * 512 这个规格。这样可以大大加速页面的首屏展示。非视口内的图块数据要进入 GPU内存,考虑到浏览器内存上传到 GPU内存的操作比较慢,Chrome 采用了一个策略: 在首次合成图块时只采用一个低分辨率的图片,这样首屏展示的时候只是展示出低分辨率的图片,以提升首屏加载性能。
**6.3、栅格化:将图块转换为位图**
* 浏览器渲染进程中还专门维护了一个栅格化线程池,专门负责把图块转换为位图数据
* 合成线程会选择视口附近的图块(tile),把它交给栅格化线程池生成位图
* 生成位图的过程实际上都会使用 GPU 加速,生成的位图最后发送给合成线程。(GPU加速使用的是浏览器的GPU进程,是跨进程的,生成的位图会存储在GPU内存中)

**6.4、合成和显示**
* 栅格化操作完成后,合成线程会生成一个绘制命令,即DrawQuad,并发送给浏览器主进程。
* 浏览器主进程中的viz组件接收到这个命令,把页面内容绘制到内存,也就是生成了页面,然后把这部分内存发送给显卡。
* 显示器有一个固定的刷新频率,一般是 60 HZ,即 60 帧,也就是一秒更新 60 张图片,一张图片停留的时间约为 16.7 ms。而每次更新的图片都来自显卡的前缓冲区。而显卡接收到浏览器传来的页面后,会合成相应的图像,并将图像保存到后缓冲区,然后系统自动将前缓冲区和后缓冲区对换位置,如此循环更新。
* 因此:假设某个动画大量占用内存时,浏览器生成图像的时候会变慢,图像传送给显卡就会不及时,而显示器还是以不变的频率刷新,因此会出现卡顿,也就是明显的掉帧现象。
## 7、回流和重绘
* 回流的本质是重新计算layout树。当进行了影响布局树的操作后,会引发布局计算。为避免连续的多次操作导致布局树反复计算,浏览器会合并这些操作,待JS代码全部执行完成后再进行统一计算。所以改动属性造成的回流是异步完成的。
* 因此,当 JS 获取布局属性时,就可能无法获取最新的布局信息。浏览器在反复权衡下,最终决定获取属性(如dom.clientWidth)立即回流
* 重绘的本质是重新根据分层信息计算了绘制指令。当改动了可见样式后,就需要重新计算,引发重绘
* 由于元素的布局信息也属于可见样式,所以回流一定会引起重绘
**总结:**
* 回流也叫重排,当 DOM结构发生变化 或者 元素样式发生改变时,浏览器需要重新计算样式和渲染树,这个过程比较消耗性能
* 重绘,指元素的外观样式发生变化(比如改变 背景色,边框颜色,文字颜色 color 等 ),但是布局没有变,此时浏览器只需要应用新样式绘制元素就可以了,比回流消耗的性能小一些
* 什么情况会引起回流?
* 页面的首次渲染、浏览器的窗口大小发生变化、元素内容发生变化、元素的尺寸或位置发生变化、元素的字体大小发生变化、添加或删除可见的 DOM元素、激活 CSS伪类、查询某些属性或者调用某些方法
**优化方案:**
* 使用 CSS 动画代替 JavaScript 动画:CSS 动画利用 GPU 加速,在性能方面通常比 JavaScript 动画更高效。使用 CSS 的 transform 和 opacity 属性来创建动画,而不是改变元素的布局属性,如宽度、高度等。
* 使用 translated3d 开启硬件加速:将元素的位移属性设置为 translated3d( 0,0,0 ),可以强制使用 GPU 加速。有助于避免回流,并提高动画流畅度。
* 避免频繁操作影响布局的样式属性:当需要对元素进行多次样式修改时,可以考虑将这些修改合并为一次操作。通过添加/移除 css 类来一次性改变多个样式属性,而不是逐个修改。
* 使用 requestAnimationFrame:通过使用 requestAnimationFrame 方法调度动画帧,可以确保动画在浏览器的重绘周期内执行,从而避免不必要的回流。这种方式可确保动画在最佳时间点进行渲染。
* 使用文档片段(Document Fragment):当需要在 DOM 中插入大量新元素时,可以先将这些元素添加到文档片段中,然后再将整个文档片段一次性插入到 DOM 中。这样可以减少回流和重绘的次数。(vue 虚拟 dom 的做法)
* 使元素脱离文档流:position: absolute/position: fixed/float:left(只是减少回流,不是避免回流)
* 使用 visibility:hidden 代替 display: none:visibility:hidden 不会触发回流,因为元素仍然占据空间,只是不可见。而 display: none 会将元素从渲染树中移除,引起回流。
## 8、注意事项
* CSSOM 会阻塞渲染,只有当 CSSOM 构建完毕后才会进入下一个阶段构建渲染树。(这点与浏览器优化有关,防止 css 规则不断改变,避免了重复的构建)(CSS 加载虽然不会阻塞 DOM 树解析,但会阻塞 Render 树的渲染)
* 通常情况下 DOM 和 CSSOM 是并行构建的,但是当浏览器遇到一个 script 标签时,DOM 构建将暂停,直至 JS 脚本下载完成并执行后才会继续解析 HTML。因为 JavaScript 可以使用诸如 document.write() 更改整个 DOM 结构之类的东西来更改文档的形状,因此 HTML 解析器必须等待 JavaScript 运行才能恢复 HTML 文档解析。
* 如果你想首屏渲染的越快,就越不应该在首屏就加载 JS 文件,建议将 script 标签放在 body 标签底部。
* 如果主线程解析到 link 位置,此时外部的 CSS 文件还没有下载解析好,主线程不会等待,继续解析后续的 HTML。这是因为下载和解析 CSS 的工作是在预解析线程中进行的。这就是CSS 不会阻塞 HTML 解析的根本原因。
- JavaScript面试题
- 1. 原始类型有哪几种?null 是对象吗?
- 2. 对象(Object)类型
- 3. typeof vs instanceof
- 4. 类型转换
- 5. this
- 6. == vs ===
- 7. 闭包
- 8. 深浅拷贝
- 9. 原型
- 10. JS 异步编程及常考面试题
- 10.1 并发(concurrency)和并行(parallelism)区别
- 10.2 回调函数(Callback)
- 10.3 Generator
- 10.4 Promise
- 10.5 async 及 await
- 10.6 常用定时器函数
- 11. Event Loop(事件循环)
- 11.1 进程与线程
- 11.2 堆和栈的概念
- 11.3 浏览器中的 Event Loop
- 11.4 Node 中的 Event Loop
- 11.5 浏览器js与node事件循环的区别
- 12. new
- 13. instanceof 的原理
- 14. 0.1 + 0.2 != 0.3
- 一、词法结构
- 1.1 ajax请求webservice接口及Unicode转义
- 1.2 javascript中的保留字
- 二、类型、值和变量
- 2.1 javascript中的数据类型有哪些?
- 2.2 Javascript中所支持的数字函数
- 2.3 二进制浮点数和四舍五入错误
- 2.4 日期计算函数
- 2.5 字符串常用的方法
- 2.6 布尔值的转化
- 2.7 null和undefined
- 2.8 比较两个数组
- 2.9 JavaScript类型转换
- 2.10 变量声明
- 2.11 变量作用域
- 2.11.1 变量作用域优先级
- 2.11.2 函数作用域和声明提前
- 2.11.3 作用域链
- 三、表达式和运算符
- 3.1 JavaScript运算符
- 3.2 运算符优先级
- 3.3 == 和 ===
- 3.4 in运算符
- 3.5 instanceof运算符
- 3.6 eval()
- 3.7 三元运算符
- 3.8 typeof运算符
- 3.9 delete运算符
- 四、语句
- 4.1 枚举对象的属性和属性值
- 4.2 跳转语句
- 4.2.1 break与contiinue
- 4.2.2 return语句
- 4.2.3 throw语句
- 4.2.4 try/catch/finally语句 问题一:try/catch/finally语句
- 4.3 with、debugger和use strict
- 4.4 for循环
- 五、对象
- 5.1 对象基础
- 5.2 对象直接量与new创建对象
- 5.3 原型与原型链
- 5.4 Object.create()
- 5.5 属性的查询和设置
- 5.6 删除属性
- 5.7 检测属性
- 5.8 枚举属性
- 5.9 属性getter和setter
- 5.9.1 存取器属性实例一
- 5.9.2 存取器属性实例二
- 5.9.3 存取器属性实例三
- 5.10 属性的特性
- 5.10.1 如何实现属性特性的查询和设置操作
- 5.10.2 修改不允许创建后修改的属性
- 5.10.3 复制属性的特性
- 5.11 对象的三个属性
- 5.11.1 原型属性
- 5.11.2 类属性
- 5.11.3 可扩展性
- 5.12 序列化对象
- 5.13 对象方法
- 六、数组
- 6.1 JS数组基础
- 6.2 创建数组
- 6.3 数组元素的读和写
- 6.4 稀疏数组
- 6.5 数组长度
- 6.6 数组元素的添加和删除
- 6.7 数组遍历
- 6.8 多维数组
- 6.9 数组方法
- 6.10 数组类型
- 6.11 类数组对象
- 6.12 作为数组的字符串
- 七、函数
- 7.1 函数定义
- 7.2 函数调用
- 7.2.1 函数调用
- 7.2.2 方法调用
- 7.2.3 构造函数调用
- 7.2.4 间接调用
- 7.3 函数的实参和形参
- 7.3.1 可选形参
- 7.3.2 可变长的实参列表:实参对象
- 7.3.3 将对象的属性用做实参
- 7.3.4 实参类型
- 7.4 作为值的函数
- 7.5 作为命名空间的函数
- 7.6 闭包
- 7.7 函数的属性、方法和构造函数
- 7.7.1 length属性
- 7.7.2 prototype 属性
- 7.7.3 call()方法和apply()方法
- 7.7.4 bind()方法
- 7.7.5 toString()方法
- 7.7.6 Function()构造函数
- 7.7.7 可调用的对象
- 7.8 函数式编程
- 7.8.1 使用函数处理数组
- 7.8.2 高阶函数
- 7.8.3 不完全函数
- 7.8.4 记忆
- 八、类和模板
- 8.1 类和原型
- 8.2 类和构造函数
- 8.2.1 构造函数和类的标识
- 8.2.2 constructor 属性
- 8.3 JS中Java式的类继承
- 8.4 类的扩充
- 8.5 类和类型
- 8.5.1 instanceof 运算符
- 8.5.2 constructor属性
- 8.5.3 构造函数的名称
- 8.5.4 鸭式辩型
- 8.6 JS中的面向对象
- 8.6.1 集合类
- 8.6.2 枚举类型
- 8.6.3 标准转换方法
- 8.6.4 比较方法
- 8.6.5 方法借用
- 8.6.6 私有状态
- 8.6.7 构造函数的重载和工厂方法
- 8.7 子类
- 8.7.1 定义子类
- 8.7.2 构造函数和方法链
- 8.7.3 组合 VS 子类
- 8.7.4 类的层次结构和抽象类
- 8.8 ES5中的类
- 8.8.1 让属性不可枚举
- 8.8.2 定义不可变的类
- 8.8.3 封装对象状态
- 8.8.4 防止类的扩展
- 8.8.5 子类和ES5
- 8.8.6 属性描述符
- 8.9 模块
- 8.9.1 用做命名空间的对象
- 8.9.2 作为私有命名空间的函数
- 九、正则表达式
- 9.1 正则表达式的定义
- 9.1.1 直接量字符
- 9.1.2 字符类
- 9.1.3 重复
- 9.1.4 选择、分组和引用
- 9.1.5 指定匹配位置
- 9.1.6 修饰符
- 9.2 用于模式匹配的String方法
- 9.3 RegExp对象
- 9.3.1 RegExp的属性
- 9.3.2 RegExp的方法
- 十、JavaScript的子集和扩展
- 10.1 JS的子集
- 10.1.1 精华
- 10.1.2 子集的安全性
- 10.2 常量和局部变量
- 10.3 解构赋值
- 10.4 迭代
- 10.4.1 for/each循环
- 10.4.2 迭代器
- 10.4.3 生成器
- 10.4.4 数组推导
- 10.4.5 生成器表达式 问题一 生成器表达式?
- 10.5 函数简写
- 10.6 多catch从句
- Vue面试题
- 一、vue-cli依赖的 Node.js 环境安装
- 二、vue-cli 工程技术集合介绍
- 三、vue-cli 工程目录结构介绍
- 四、vue.js 核心知识点高频试题一
- 4.1 对于Vue是一套渐进式框架的理解
- 4.2 请说出vue几种常用的指令
- 4.3 请问 v-if 和 v-show 有什么区别?
- 4.4 vue常用的修饰符
- 4.5 v-on可以监听多个方法吗?
- 4.6 vue中 key 值的作用
- 4.7 vue-cli工程升级vue版本
- 4.8 vue事件中如何使用event对象?
- 4.9 $nextTick的使用
- 4.10 Vue 组件中 data 为什么必须是函数
- 4.11 v-for 与 v-if 的优先级
- 五、vue.js 核心知识点高频试题二
- 5.1 vue中子组件调用父组件的方法
- 5.2 vue中 keep-alive 组件的作用
- 5.3 vue中编写可复用的组件
- 5.4 vue生命周期有关的试题
- 5.5 vue如何监听键盘事件中的按键?
- 5.6 vue更新数组时触发视图更新的方法
- 5.7 vue中对于对象的更改检测
- 5.8 解决非工程化项目初始化页面闪动问题
- 5.9 v-for产生的列表,实现active的切换
- 5.10 v-model语法糖的使用
- 六、vue.js 核心知识点高频试题三
- 6.1 十个常用的自定义过滤器
- 6.2 vue等单页面应用及其优缺点
- 6.3 vue的计算属性
- 6.4 vue提供的几种脚手架模板
- 6.5 vue父组件向子组件通过props传递数据
- 6.6 vue-cli开发环境使用全局常量
- 6.7 vue-cli生产环境使用全局常量
- 6.8 vue弹窗后如何禁止滚动条滚动?
- 6.9 计算属性的缓存和方法调用的区别
- 6.10 vue-cli中自定义指令的使用
- 6.11 父组件异步获取动态数据传递给子组件
- 6.12 mixin优缺点
- 6.13 组合式和选项式API的优缺点
- 6.14 vue2与vue3的区别
- 七、vue-router 路由高频考题
- 7.1 vue-router响应 路由参数 的变化
- 7.2 完整的 vue-router 导航解析流程
- 7.3 vue-router有哪几种导航钩子( 导航守卫 )?
- 7.4 vue-router的几种实例方法以及参数传递
- 7.5 vue-router的动态路由匹配以及使用
- 7.6 vue-router如何定义嵌套路由?
- 7.7 router-link组件及其属性
- 7.8 vue-router实现路由懒加载( 动态加载路由 )
- 7.9 vue-router路由的两种模式
- 八、vuex 状态管理高频试题
- 8.1 什么是vuex?
- 8.2 使用vuex的核心概念
- 8.3 vuex在vue-cli中的应用
- 8.4 如何在组件中去使用vuex的值和方法?
- 8.5 在vuex中使用异步修改
- 九、axios、fetch、ajax等请求高频试题
- 9.1 Promise对象是什么
- 9.2 axios、fetch与ajax有什么区别?
- 9.3 JS的同源策略和跨域问题
- 9.4 如何解决跨域问题?
- 9.5 vue-cli中使用JSON数据模拟
- 9.6 axios有哪些特点?
- 十、前端 UI 样式高频试题
- 10.1 组件样式属性 scoped 问题及解决方法
- 10.2 vue常用的UI组件库
- 10.3 vue组件适配移动端【经典】
- 10.4 移动端适配常用媒体查询源码
- 10.5 垂直居中对齐
- 10.6 vue-cli中如何使用背景图片?
- 10.7 使用表单禁用时移动端样式问题
- 10.8 多种类型文本超出隐藏问题
- 10.9 vue-cli工程引入全局less变量
- 十一、vue中常用功能实现与详解
- 11.1 vue中如何实现tab切换功能?
- 11.2 利用<keep-alive></keep-alive>标签实现某个组件缓存功能
- 11. 3 vue中实现切换页面时为左滑出效果
- 11.4 vue无痕刷新
- 11.5 多种情景的数组去重
- 11.6 vue保留滚动条位置
- 11.7 vue组件中使用定时器
- 11.8 组件slot使用示例
- 11.9 使用递归组件实现级联选择器
- 11.10 使用递归组件实现树形控件
- 11.11 几种常见的自定义指令详解
- 11.11.1 实现一个表单自动聚焦的自定义指令
- 11.11.2 实现一个可从外部关闭的下拉菜单
- 11.11.3 实现一个实时时间转换指令v-time
- 11.12 两种类型的循环单选选择、取消功能
- 十二、vue各种情况的组件间通信
- 12.1 关于vue组件间通信问题的回答
- 12.2 万金油:vue中央事件总线的使用
- 12.3 props:最基础的父子组件间传递数据
- 12.4 v-model语法糖:子组件向父组件传递数据
- 12.5 .sync 修饰符:进行父子组件间相互传递数据
- 12.6 $attrs属性:父孙组件间传递数据
- 12.7 $on属性:子组件调用父组件方法
- 12.8 $listeners属性:孙组件调用父组件的方法
- 12.9 $refs属性:
- 12.10 $parent属性:
- 12.11 $children属性:
- 十三、混合开发:vue工程与app交互
- 十四、vue开发命令的执行过程【拓展】
- 十五、vue-cli工程打包后.js文件详解
- 15.1 vue打包命令是什么?
- 15.2 vue打包后会生成哪些文件?
- 15.3 配置 vue 打包生成文件的路径
- 15.4 vue如何优化首屏加载速度?
- 十六、MVVM设计模式高频试题
- 16.1 MVC、MVP与MVVM模式
- 16.2 MVC、MVP与MVVM的区别
- 16.3 常见的实现MVVM几种方式
- 16.4 Object.defineProperty()方法
- 十七、实现一个自己的MVVM
- 17.1 准备知识
- 17.1.1 ES6中类和定义
- 17.1.2 JS中的文档碎片
- 17.1.3 解构赋值
- 17.1.4 Array.from与Array.reduce
- 17.1.5 递归的使用
- 17.1.6 Obj.keys()与Obj.defineProperty
- 17.1.7 发布-订阅模式
- 17.2 效果示例
- 17.3 思路分析
- 17.4 项目地址
- 十八、vue-cli中的配置详解
- 18.1 webpack的特点
- 十九、源码剖析
- 19.1 剖析 Vue 内部运行机制
- 19.1.1 Vue.js 全局运行机制
- 19.1.2 响应式系统的基本原理
- 19.1.3 响应式系统的依赖收集追踪原理
- 19.1.4 实现 Virtual DOM 下的一个 VNode 节点
- 19.1.5 template 模板是怎样通过 Compile 编译的
- 19.1.6 数据状态更新时的差异 diff 及 patch 机制
- 19.1.7 批量异步更新策略及 nextTick 原理
- 19.1.8 proxy代理过程
- 19.2 剖析 vue-router 内部运行机制
- 19.2.1 重要函数思维图解
- 19.3 剖析 vuex 内部运行机制
- 19.3.1 准备知识
- 1. Vue.mixin
- 2. Vue.use
- 19.3.2 插件安装
- 19.3.3 Store
- 19.3.4 使用源码
- 19.4 剖析 axios 内部运行机制
- 二十、发布一个 npm 资源包
- 二十一、vue的服务器端渲染
- 二十二:资料分享
- React面试题
- React高频面试题
- 1.React设计思想
- 2.Hooks的作用&原理
- 3.Vue和React数据驱动原理区别
- 4.React虚拟DOM Diff算法
- 5.React 的生命周期方法有哪些?
- 6.React 中的 useState() 是什么?
- 7.什么是纯函数?
- 8.useMem和useCallback的区别
- 9.useEffect和useLayoutEffect区别
- 10.useState与useRef主要区别
- 11.什么时JSX?
- 12.状态管理&组件通信方式有哪些?
- 13.React-Router工作原理
- 14.setState是同步还是异步的
- 15.什么是高阶组件(HOC)?
- 16.防止子组件不必要的重新渲染
- 17.React 组件性能优化
- 18.为什么React要Fiber架构,而Vue没有
- 19.React Fiber可中断更新机制原理
- 20.Fiber架构原理
- 21.React中的setState批量更新的过程是什么?
- 22.简述为什么 useState 要使⽤数组⽽不是对象 ?
- 23.简述 React key 是⼲嘛⽤的 为什么要加?
- 24.请简述当调用setState时,React render 是如何工作的?
- 25.哪些方法会触发 React 重新渲染?重新渲染 render 会做些什么?
- 26.React 并发模式是如何执行的?
- 27.React setState 调⽤之后发⽣了什么?是同步还是异步?
- Webpack面试题
- 1. 减少 Webpack 打包时间
- 2. 减少 Webpack 打包后的文件体积
- 一、Webpack基础介绍
- 1.1 模块化
- 1.2 构建的作用及常见功能
- 1.3 常见的构建工具及其优缺点
- 1.4 webpack
- 1.5 Loader
- 1.6 Plugin
- 1.7 DevServer
- 1.8 webpack的核心概念
- 二、Webpack配置
- 2.1 Entry
- 2.2 Output
- 2.2.1 filename配置
- 2.2.2 chunkFilename配置
- 2.2.3 path配置
- 2.2.4 publicPath配置
- 2.2.5 crossOriginLoading配置
- 2.2.6 libraryTarget 和 library配置
- 2.2.7 var (默认)配置
- 2.2.8 commonjs配置
- 2.2.9 commonjs2配置
- 2.2.10 this配置
- 2.2.11 window配置
- 2.2.12 global配置
- 2.2.13 libraryExport配置
- 2.3 Module
- 2.3.1 Loader配置
- 2.3.2 noParse配置
- 2.3.3 parser配置
- 2.4 Resolve
- 2.4.1 alias配置
- 2.4.2 mainFields配置
- 2.4.3 extensions配置
- 2.4.4 modules配置
- 2.4.5 descriptionFiles配置
- 2.4.6 enforceExtension配置
- 2.4.7 enforceModuleExtension配置
- 2.5 Plugin
- 2.6 DevServer
- 2.6.1 hot配置
- 2.6.2 inline配置
- 2.6.3 historyApiFallback配置
- 2.6.4 contentBase配置
- 2.6.5 headers配置
- 2.6.6 host配置
- 2.6.7 port配置
- 2.6.8 allowedHosts配置
- 2.6.9 disableHostCheck配置
- 2.6.10 https配置
- 2.6.11 clientLogLevel配置
- 2.6.12 compress配置
- 2.6.13 open配置
- 2.7 其他配置
- 2.7.1 Target配置
- 2.7.2 Devtool配置
- 2.7.3 Watch 和 WatchOptions
- 2.7.4 Externals配置
- 2.7.5 ResolveLoader配置
- 2.8 如何整体配置结构
- 2.9 判断如何配置 Webpack
- 三、Webpack实战
- 3.1 如何接入ES6
- 3.2 如何接入SCSS
- 3.3 React的使用
- 3.4 Vue的使用
- 3.5 使用 Angular2 框架
- 3.6 为单页应用生成 HTML
- 3.7 离线应用
- 3.7.1 使用 Service Workers 实现离线缓存
- 3.7.2 接入 Webpack
- 3.8 检查代码
- 四、Webpack优化
- 4.1 缩小文件搜索范围
- 4.1.1 优化 loader 配置
- 4.1.2 优化 resolve.modules 配置
- 4.1.3 优化 resolve.mainFields 配置
- 4.1.4 优化 resolve.alias 配置
- 4.1.5 优化 resolve.extensions 配置
- 4.2 构建动态链接库
- 4.2.1 接入 Webpack
- 4.2.2 如何实现构建动态链接库
- 4.3 如何把任务分解给多个子进程去并发的执行
- 4.4 多进程压缩代码
- 4.5 使用自动刷新
- 4.6 热模块替换
- 4.6.1 优化模块热替换
- 4.7 区分环境
- 4.7.1 实现原理
- 4.8 CDN加速
- 4.8.1 用 Webpack 实现 CDN 的接入
- 4.9 Tree Shaking优化
- 4.9.1 接入 Tree Shaking
- 4.10 提取公共代码
- 4.10.1 如何通过 Webpack 提取公共代码
- 4.11 按需加载
- 4.11.1 用 Webpack 实现按需加载
- 4.11.2 实例强化(按需加载与 ReactRouter)
- 4.12 优化代码在运行时的效率
- 4.12.1 接入 Webpack
- 4.13 开启 Scope Hoisting
- 4.13.1 接入webpack
- 4.14 输出分析
- 五 . Webpack原理
- 5.1 流程概括
- 5.2 流程细节
- 5.3 详细原理
- 5.4 打包流程
- 六、Vite原理
- 1.webpack与vite打包差异
- 七、高频面试题
- 1.Vue-loader 是什么?
- 2.webpack精选面试题
- 3.Tree shaking实现原理
- 4.什么是Scope hoisting
- 5.HMR热更新原理
- Nodejs面试题
- 1.Nodejs是单线程还是多线程?
- 2.为什么Nodejs单线程却能够支持高并发?
- 3.Nodejs事件循环机制
- 4.Nodejs中libuv库的作用是什么?
- 5.请简述一下node的多进程架构
- Http协议面试题
- 重点:输入 URL 到页面渲染的整个流程
- 一、网络基础
- 1.1 TCP/IP协议族
- 1.1.1 什么是TCP/IP协议族
- 1.1.2 TCP/IP协议族按层次划分了哪几次?
- 1.1.3 HTTP请求时,TCP/IP通信传输流
- 1.2 网络层:IP协议
- 1.3 传输层:TCP协议、UDP协议
- 1.3.1 TCP协议位于哪一层
- 1.3.2 TCP头部有哪些重要字段?
- 1.3.3 TCP协议的三次握手
- 1.3.4 为什么 TCP 建立连接需要三次握手?
- 1.3.5 UDP 与 TCP 的区别是什么?
- 1.3.6 ARQ协议
- 1.3.7 滑动窗口协议
- 1.3.8 拥塞处理
- 1.4 应用层:DNS服务
- 1.5 URL和URI
- 1.5.1 URI
- 1.5.2 绝对URI的格式
- 1.6 RFC
- 二、Http协议
- 2.1 HTTP请求报文组成
- 2.2 HTTP协议的状态
- 2.3 告知服务器意图的HTTP方法
- 2.4 TCP的持久连接
- 2.5 使用Cookie的状态管理
- 三、HTTP报文内的HTTP信息
- 3.1 HTTP报文类型和结构
- 四、HTTP状态码
- 五、与HTTP协作的 Web 服务器
- 5.1 通信数据转发程序:代理
- 5.2 通信数据转发程序:网关
- 5.3 通信数据转发程序:隧道
- 六、HTTP首部
- 6.1 HTTP首部字段类型
- 6.2 通用首部字段
- 6.3 请求首部字段
- 6.4 响应首部字段
- 6.5 实体首部字段
- 6.6 非HTTP/1.1 首部字段
- 6.7 为Cookie服务的首部字段
- 6.8 其他首部字段
- 七、HTTPS
- 7.1 HTTP的缺点
- 7.2 HTTP与HTTPS
- 7.3 HTTPS的加密方式
- 7.4 HTTPS的通信步骤
- 7.5 HTTPS的缺点
- 八、基于HTTP的功能追加协议
- 8.1 SPDY协议
- 8.2 webSocket协议
- 8.3 HTTP/2.0
- Web安全面试题
- 1.XSS原理及防范
- 2.CSRF原理及防范
- 3.如何保证你的HTTP cookies安全不受XSS攻击
- 浏览器相关面试题
- 1.浏览器渲染html流程
- 2.浏览器是多进程还是单进程?
- 3.DOM Tree 与 CSSOM
- 4.哪些CSS属性会触发硬件加速?
- 5.硬件加速有什么副作用?如何优化?
- 微前端&设计模式
- 1.什么是微前端
- 2.沙箱(sandbox)技术
- 3.qiankun沙箱机制
- 4.微前端框架对比