Skip to content

Commit d208170

Browse files
张东张东
张东
authored and
张东
committed
add
1 parent 5660dcf commit d208170

File tree

4 files changed

+221
-2
lines changed

4 files changed

+221
-2
lines changed

.vitepress/config.mts

+11-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export default defineConfig({
7474
{
7575
text: "框架",
7676
items: [
77-
{ text: "Vue", link: "/frame/vue/start" },
77+
{ text: "Vue", link: "/frame/vue/reactivity" },
7878
{ text: "React", link: "/frame/react/start" },
7979
],
8080
},
@@ -213,6 +213,16 @@ export default defineConfig({
213213
link: "/frame/react/manage",
214214
},
215215
],
216+
"/frame/vue/": [
217+
{
218+
text: "响应式原理",
219+
link: "/frame/vue/reactivity",
220+
},
221+
{
222+
text: "api源码",
223+
link: "/frame/vue/apis",
224+
},
225+
],
216226
"/Web-Api/": [
217227
{
218228
text: "Web-Api",

browser/http/crossorigin.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ layout: doc
33
outline: deep
44
---
55
## 什么是跨域
6-
浏览器有一个同源策略,要求协议,域名,端口号都相同,当请求url与当前页面url违背了同源策略,即为跨域
6+
浏览器有一个同源策略,协议,域名,端口号都相同是同一个源,出于安全性考虑,浏览器会对不同源之间的资源访问进行限制.当请求url与当前页面url违背了同源策略,即为跨域
77
## 如何解决跨域
88
### cors
99
浏览器将cors请求分为简单请求和非简单请求

frame/vue/apis.md

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
outline: deep
3+
layout: doc
4+
---
5+
## ref
6+
通过ref注册的响应式数据,有一个访问器属性value,当getter时收集依赖,setter时派发更新
7+
8+
而对于set的value,使用toReactive进行处理,首先判断数据类型是不是object,如果不是原样返回,反之使用reactive使value也变成响应式数据
9+
## reactive
10+
:::tip :rocket:代理
11+
允许我们拦截并重新定义对一个对象的基本操作
12+
:::
13+
基于proxy进行代理,他接受两个参数,第一个参数是被代理对象,第二个参数是一组traps,例如get set,通过这些trap可以控制被代理对象的基本操作
14+
15+
16+
对于reactive来说,有get set deleteProperty has ownKeys这些traps
17+
18+
在get中进行track依赖收集,如果value是一个对象,会递归调用reactive实现深度监听。如果value是一个ref类型,会进行自动解包
19+
20+
在set中处理新增和修改属性,进行trigger派发更新
21+
22+
在deleteProperty处理删除属性,触发trigger
23+
24+
在has拦截in操作符,触发track
25+
26+
在ownKeys拦截forin,触发track
27+
28+
通过配置这些traps可以进一步实现shallowreactive,readonly等api
29+
30+
## computed
31+
使用computed注册的响应式数据使用方法与ref差不多,都要使用.value,实际上computed会返回一个对象,里面有个访问器属性value
32+
33+
初始化会自动执行一遍里面的回调函数,收集依赖,并且还有两个属性dirty和value,dirty初始化是true,代表需要重新执行回调,value则是执行完回调return的值
34+
35+
当computed依赖的响应式数据发生改变,dirty被设置为true,当触发getter时需要重新执行回调,将return的值缓存到value,并把新值返回
36+
37+
如果依赖没有改变,也就是dirty为false,就会直接返回缓存的value
38+
39+
## nextTick
40+
::: tip :rocket:
41+
我们通过js修改dom时 dom树在内存中是同步发生更新的,但是此时的最新状态并不会立即反应到屏幕上 而是要等待浏览器的渲染周期和帧率有关 一般在16.6ms 当渲染完成后 才能在屏幕观测到最新的页面
42+
:::
43+
44+
这跟vue的异步更新队列有关,vue会同步将任务放入任务队列,在微任务promise.then中执行任务队列
45+
46+
因此要获取最新的dom,需要在上面提到的微任务执行完之后再执行
47+
48+
nexttick的原理就是通过promise的链式调用,将nexttick里的回调放在上面的微任务.then里执行
49+
50+
51+
52+

frame/vue/reactivity.md

+157
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
---
2+
outline: deep
3+
layout: doc
4+
---
5+
## 响应式原理
6+
通过proxy拦截一个对象的读取,设置,当读取时将副作用函数存储到桶上,设置时将副作用从桶取出再执行
7+
8+
## 完善的响应式系统
9+
### 提供一个注册副作用函数的函数
10+
```js
11+
01 // 用一个全局变量存储被注册的副作用函数
12+
02 let activeEffect
13+
03 // effect 函数用于注册副作用函数
14+
04 function effect(fn) {
15+
05 // 当调用 effect 注册副作用函数时,将副作用函数 fn 赋值给activeEffect
16+
06 activeEffect = fn
17+
07 // 执行副作用函数
18+
08 fn()
19+
09 }
20+
```
21+
### 桶结构
22+
桶是用来保存被操作字段与对应副作用函数的映射,最外层是weakmap,键名是代理对象target,键值是map,它的键名是target下的key,这个map的键值是一个set,用来保存副作用函数
23+
### 分支切换
24+
可能会产生遗留的副作用函数,解决方法是执行副作用之前,先从与他相关的依赖对应的副作用函数集合去除
25+
26+
当副作用函数执行完毕后,会重新建立联系,但在新的联系中不会包含遗留的副作用函数
27+
28+
也就需要明确知道哪些`依赖集合`中包含它(deps)
29+
```js
30+
01 function track(target, key) {
31+
02 // 没有 activeEffect,直接 return
32+
03 if (!activeEffect) return
33+
04 let depsMap = bucket.get(target)
34+
05 if (!depsMap) {
35+
06 bucket.set(target, (depsMap = new Map()))
36+
07 }
37+
08 let deps = depsMap.get(key)
38+
09 if (!deps) {
39+
10 depsMap.set(key, (deps = new Set()))
40+
11 }
41+
12 // 把当前激活的副作用函数添加到依赖集合 deps 中
42+
13 deps.add(activeEffect)
43+
14 // deps 就是一个与当前副作用函数存在联系的依赖集合
44+
15 // 将其添加到 activeEffect.deps 数组中
45+
16 activeEffect.deps.push(deps) // 新增
46+
17 }
47+
```
48+
49+
而调用副作用函数之前要清除
50+
```js
51+
01 // 用一个全局变量存储被注册的副作用函数
52+
02 let activeEffect
53+
03 function effect(fn) {
54+
04 const effectFn = () => {
55+
05 // 调用 cleanup 函数完成清除工作
56+
06 cleanup(effectFn) // 新增
57+
07 activeEffect = effectFn
58+
08 fn()
59+
09 }
60+
10 effectFn.deps = []
61+
11 effectFn()
62+
12 }
63+
```
64+
clean函数
65+
```js
66+
01 function cleanup(effectFn) {
67+
02 // 遍历 effectFn.deps 数组
68+
03 for (let i = 0; i < effectFn.deps.length; i++) {
69+
04 // deps 是依赖集合
70+
05 const deps = effectFn.deps[i]
71+
06 // 将 effectFn 从依赖集合中移除
72+
07 deps.delete(effectFn)
73+
08 }
74+
09 // 最后需要重置 effectFn.deps 数组
75+
10 effectFn.deps.length = 0
76+
11 }
77+
```
78+
## 嵌套的effect
79+
例如
80+
```js
81+
01 effect(() => {
82+
02 Foo.render()
83+
03 // 嵌套
84+
04 effect(() => {
85+
05 Bar.render()
86+
06 })
87+
07 })
88+
```
89+
所以需要一个effect栈
90+
```js
91+
01 // 用一个全局变量存储当前激活的 effect 函数
92+
02 let activeEffect
93+
03 // effect 栈
94+
04 const effectStack = [] // 新增
95+
05
96+
06 function effect(fn) {
97+
07 const effectFn = () => {
98+
08 cleanup(effectFn)
99+
09 // 当调用 effect 注册副作用函数时,将副作用函数赋值给 activeEffect
100+
10 activeEffect = effectFn
101+
11 // 在调用副作用函数之前将当前副作用函数压入栈中
102+
12 effectStack.push(effectFn) // 新增
103+
13 fn()
104+
14 // 在当前副作用函数执行完毕后,将当前副作用函数弹出栈,并把
105+
activeEffect 还原为之前的值
106+
15 effectStack.pop() // 新增
107+
16 activeEffect = effectStack[effectStack.length - 1] // 新增
108+
17 }
109+
18 // activeEffect.deps 用来存储所有与该副作用函数相关的依赖集合
110+
19 effectFn.deps = []
111+
20 // 执行副作用函数
112+
21 effectFn()
113+
22 }
114+
```
115+
## 调度执行
116+
有能力决定副作用函数执行的时机、次数以及方式
117+
118+
连续多次修改响应式数据但只会触发一次更新
119+
120+
这些任务同步加进去,同步执行完执行微任务
121+
```js
122+
01 // 定义一个任务队列
123+
02 const jobQueue = new Set()
124+
03 // 使用 Promise.resolve() 创建一个 promise 实例,我们用它将一个任务添加到微任务队列
125+
04 const p = Promise.resolve()
126+
05
127+
06 // 一个标志代表是否正在刷新队列
128+
07 let isFlushing = false
129+
08 function flushJob() {
130+
09 // 如果队列正在刷新,则什么都不做
131+
10 if (isFlushing) return
132+
11 // 设置为 true,代表正在刷新
133+
12 isFlushing = true
134+
13 // 在微任务队列中刷新 jobQueue 队列
135+
14 p.then(() => {
136+
15 jobQueue.forEach(job => job())
137+
16 }).finally(() => {
138+
17 // 结束后重置 isFlushing
139+
18 isFlushing = false
140+
19 })
141+
20 }
142+
21
143+
22
144+
23 effect(() => {
145+
24 console.log(obj.foo)
146+
25 }, {
147+
26 scheduler(fn) {
148+
27 // 每次调度时,将副作用函数添加到 jobQueue 队列中
149+
28 jobQueue.add(fn)
150+
29 // 调用 flushJob 刷新队列
151+
30 flushJob()
152+
31 }
153+
32 })
154+
33
155+
34 obj.foo++
156+
35 obj.foo++
157+
```

0 commit comments

Comments
 (0)