Skip to content

Commit

Permalink
docs:fiber
Browse files Browse the repository at this point in the history
Signed-off-by: xsahxl <[email protected]>
  • Loading branch information
xsahxl committed May 25, 2024
1 parent a04dea1 commit 58f87a4
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .dumirc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default defineConfig({
},
{
title: '面试题',
link: '/learn/debounce-and-throttle',
link: '/learn/debounce-throttle',
activePath: '/learn',
},
{
Expand Down
14 changes: 8 additions & 6 deletions docs/learn/apply-call-bind.md → docs/learn/bind.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# apply call bind 区别?如何实现一个bind?
# 如何实现一个 bind?

## apply call bind 区别?

主要作用是改变 函数执行时的上下文,换句话说是改变函数运行时的 `this` 指向

Expand Down Expand Up @@ -49,7 +51,7 @@ steven.charge(90);
console.log(steven);
```

现在tom的手机也没电了,但是又不想买充电宝,想借用 steven的充电宝给手机充电
现在 tom 的手机也没电了,但是又不想买充电宝,想借用 steven 的充电宝给手机充电

```js
const steven = {
Expand Down Expand Up @@ -131,10 +133,10 @@ console.log(tom);

## 区别

- 都可以改变this指向
- 第一个参数都是this要指向的对象,如果不传递或者为null,undefined,则默认指向全局的window
- apply 接收的参数是数组,call是参数列表
- apply 和 call 会立即执行,bind则是返回绑定this之后的函数
- 都可以改变 this 指向
- 第一个参数都是 this 要指向的对象,如果不传递或者为 null,undefined,则默认指向全局的 window
- apply 接收的参数是数组,call 是参数列表
- apply 和 call 会立即执行,bind 则是返回绑定 this 之后的函数

## 手写 call

Expand Down
File renamed without changes.
File renamed without changes.
67 changes: 67 additions & 0 deletions docs/learn/diff.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# diff 和 fiber

## 是什么?

diff 算法是通过对比新旧虚拟 dom 精确的找出变化之处,然后同步到真实的 dom 上。

## 原理

react diff 算法 主要遵循三个层级的策略

- tree 层级
- component 层级
- element 层级

### tree 层级

DOM 节点跨层级的操作不做优化,只会对相同层级的节点进行比较
![](https://static.vue-js.com/ae71d1c0-ec91-11eb-85f6-6fac77c0c9b3.png)

只有删除、创建操作,没有移动操作,如下图:
![](https://static.vue-js.com/b85f2bb0-ec91-11eb-ab90-d9ae814b240d.png)

react 发现新树中,R 节点下没有了 A,那么直接删除 A,在 D 节点下创建 A 以及下属节点

上述操作中,只有删除和创建操作

### component 层级

如果是同一个类的组件,则会继续往下 diff 运算,如果不是一个类的组件,那么直接删除这个组件下的所有子节点,创建新的

![](https://static.vue-js.com/c1fcdf00-ec91-11eb-ab90-d9ae814b240d.png)

当 component D 换成了 component G 后,即使两者的结构非常类似,也会将 D 删除再重新创建 G

### element 层级

对于比较同一层级的节点们,每个节点在对应的层级用唯一的 key 作为标识

提供了 3 种节点操作,分别为 INSERT_MARKUP(插入)、MOVE_EXISTING (移动)和 REMOVE_NODE (删除)

## fiber

React 16 以前递归对比虚拟 DOM 树的方案有一个明显的问题:`阻塞主线程`。旧的 React 架构中,Diff 算法和组件更新都是同步执行的。这意味着一旦更新开始,React 会一直占用主线程直到整个更新过程完成,这就可能导致浏览器无法响应用户操作,界面卡顿等现象

React 16 为了优化性能,会先把虚拟 DOM 树转换成 Fiber,也就是从树转换成链表,再基于 Fiber 进行渲染。这个过程分成两个阶段:

- reconcile(可中断) :从虚拟 DOM 转换成 Fiber,并给需要操作的节点打上标记。
- commit(不可中断) :对有标记的 Fiber 节点进行操作。

### 建立 fiber

第一次渲染不需要 diff,直接讲虚拟 dom 转为 fiber

![](https://cdn.jsdelivr.net/gh/xsahxl/blog-images/create-fiber.drawio.png)

### 更新 fiber

尽可能复用旧的 fiber 来更新本次的 fiber

![](https://cdn.jsdelivr.net/gh/xsahxl/blog-images/update-fiber.drawio.png)

具体实现分为两次遍历

- 第一次遍历:`复用 位置和内容都相同的节点`。方式是对比虚拟 dom 和旧的 fiber,如果可以复用就继续处理下一个节点,否则就结束遍历。
如上图,相比初始的 fiber,A,B,C 都是完全没变的,直接复用,在往下走原本是 E,但现在变成了 D,发现不能复用,直接返回

- 第二次遍历:`把剩下的内容填上`, 方法是把剩余的旧 fiber 节点做成一个 map,然后遍历新 dom 树,构建新 fiber 的时候查查 map,能复用的就复用,用不了就新建。如上图,构建 D、F、H 的时候发现旧 Fiber 里有,那么可以拿过来复用,M 以前没有,那就新建一个。
10 changes: 5 additions & 5 deletions docs/learn/promise.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# 手写promise
# 手写 promise

## 基本使用

Expand Down Expand Up @@ -78,7 +78,7 @@ console.log(p2.result); // fail

## then 第一节

- 首先明确的是需要返回一个promise
- 首先明确的是需要返回一个 promise
- 应该根据当前状态做相关处理

```js
Expand Down Expand Up @@ -587,9 +587,9 @@ p3.then(
## Promise.resolve 和 Promise.reject

- Promise.resolve(value):
- 如果value是promise,则value的执行结果就是Promise.resolve的返回结果
- 如果value不是promise,则返回成功的promise,其结果就是value
- Promise.reject(reason): 返回一个失败的promise,原因就是reason
- 如果 value 是 promise,则 value 的执行结果就是 Promise.resolve 的返回结果
- 如果 value 不是 promise,则返回成功的 promise,其结果就是 value
- Promise.reject(reason): 返回一个失败的 promise,原因就是 reason

```js
class MyPromise {
Expand Down
47 changes: 47 additions & 0 deletions docs/learn/v-dom.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# 虚拟 dom

## 虚拟 dom 是什么

组件 render 返回的对象就是 vdom,一般包含标签名称,标签属性,事件监听和子元素等。

## 虚拟 dom 有什么优点

- 能减少不必要的 dom 操作

- 减少 dom 更新的次数
你用传统的原生 api 或 jQuery 去操作 DOM 时,浏览器会从构建 DOM 树开始从头到尾执行一遍流程
当你在一次操作时,需要更新 10 个 DOM 节点,浏览器没这么智能,收到第一个更新 DOM 请求后,并不知道后续还有 9 次更新操作,因此会马上执行流程,最终执行 10 次流程
而通过 VNode,同样更新 10 个 DOM 节点,虚拟 DOM 不会立即操作 DOM,而是将这 10 次更新的 diff 内容保存到本地的一个 js 对象中,最终将这个 js 对象一次性 attach 到 DOM 树上,避免大量的无谓计算

- 减少 dom 更新的范围
通过 diff 算法,能够只更新被修改的 DOM 节点,而非全部重绘

- 能跨平台渲染,比如 react native
虚拟 DOM 最大的优势在于抽象了原本的渲染过程,实现了跨平台的能力,而不仅仅局限于浏览器的 DOM,可以是安卓和 IOS 的原生组件,可以是近期很火热的小程序,也可以是各种 GUI

## 虚拟 dom 怎么实现

- 虚拟 dom 一般用 js 对象来表示,比如

```js
{
tag: 'div',
attrs: {
id: 'container',
className: 'container'
},
children: [
{
tag: 'span',
attrs: {
className: 'red'
},
children: ['Hello, world!']
}
]
}
```

## 虚拟 dom 有什么缺点

需要额外的创建函数,比如 CreateElement 或 h,但可以通过 jsx 来简化成 xml 写法

0 comments on commit 58f87a4

Please sign in to comment.