Skip to content

Commit 493a165

Browse files
committed
feat: proxyRefs
1 parent a946fd3 commit 493a165

File tree

5 files changed

+209
-0
lines changed

5 files changed

+209
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- [x] ref
1010
- [x] computed
1111
- [x] watch
12+
- [x] 自动脱 ref
1213
- 编译器
1314
- [x] 模板解析器 parse
1415
- [x] 转化器 transform

demo/14-ref.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@
7373
const wrapper = {
7474
value: val
7575
}
76+
// 定义不可枚举的属性 __v_isRef
77+
Object.defineProperty(wrapper, '__v_isRef', {
78+
value: true
79+
})
7680
return reactive(wrapper)
7781
}
7882
const obj = ref(1)

demo/15-ref-2.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@
7373
const wrapper = {
7474
value: val
7575
}
76+
// 定义不可枚举的属性 __v_isRef
77+
Object.defineProperty(wrapper, '__v_isRef', {
78+
value: true
79+
})
7680
return reactive(wrapper)
7781
}
7882
function toRef(obj, prop) {

demo/16-proxyRefs.html

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<body></body>
2+
3+
<script>
4+
let activeEffect
5+
const effectStack = []
6+
function effect(fn, options = {}) {
7+
const effectFn = () => {
8+
activeEffect = effectFn
9+
effectStack.push(effectFn)
10+
const result = fn()
11+
effectStack.pop()
12+
activeEffect = effectStack[effectStack.length - 1]
13+
return result
14+
}
15+
effectFn.options = options
16+
if (!options.lazy) { // 只有非 lazy 的时候,才执行
17+
effectFn()
18+
}
19+
return effectFn // 返回副作用函数
20+
}
21+
22+
function isObject(val) {
23+
return val && typeof val === 'object'
24+
}
25+
26+
const bucket = new WeakMap()
27+
28+
function reactive(data) {
29+
return new Proxy(data, {
30+
get(target, prop, receiver) {
31+
const result = Reflect.get(target, prop, receiver)
32+
track(target, prop)
33+
if (isObject(result)) {
34+
return reactive(result)
35+
}
36+
return result
37+
},
38+
set(target, prop, newVal, receiver) {
39+
const result = Reflect.set(target, prop, newVal, receiver)
40+
trigger(target, prop)
41+
return result
42+
}
43+
})
44+
}
45+
46+
function track(target, prop) {
47+
if (!activeEffect) return
48+
let depsMap = bucket.get(target)
49+
if (!depsMap) {
50+
bucket.set(target, (depsMap = new Map()))
51+
}
52+
let deps = depsMap.get(prop)
53+
if (!deps) {
54+
depsMap.set(prop, (deps = new Set()))
55+
}
56+
deps.add(activeEffect) // 收集副作用函数
57+
}
58+
function trigger(target, prop) {
59+
const depsMap = bucket.get(target)
60+
if (!depsMap) return
61+
const effects = depsMap.get(prop)
62+
effects && effects.forEach(effectFn => {
63+
if (effectFn.options.schedular) {
64+
// 如果存在调度器
65+
effectFn.options.schedular(effectFn)
66+
} else {
67+
effectFn()
68+
}
69+
})
70+
}
71+
72+
function ref(val) {
73+
const wrapper = {
74+
value: val
75+
}
76+
// 定义不可枚举的属性 __v_isRef
77+
Object.defineProperty(wrapper, '__v_isRef', {
78+
value: true
79+
})
80+
return reactive(wrapper)
81+
}
82+
function toRef(obj, prop) {
83+
const wrapper = {
84+
get value() {
85+
return obj[prop]
86+
},
87+
set value(newVal) {
88+
obj[prop] = newVal
89+
}
90+
}
91+
Object.defineProperty(wrapper, '__v_isRef', {
92+
value: true
93+
})
94+
return wrapper
95+
}
96+
function toRefs(obj) {
97+
const result = {}
98+
for (let key in obj) {
99+
result[key] = toRef(obj, key)
100+
}
101+
return result
102+
}
103+
104+
function proxyRefs(obj) {
105+
return new Proxy(obj, {
106+
get(target, prop, receiver) {
107+
const value = Reflect.get(target, prop, receiver)
108+
return value.__v_isRef ? value.value : value
109+
},
110+
set(target, prop, newVal, receiver) {
111+
const value = Reflect.get(target, prop, receiver)
112+
if (value.__v_isRef) {
113+
value.value = newVal
114+
return true
115+
}
116+
return Reflect.set(target, prop, newVal, receiver)
117+
}
118+
})
119+
}
120+
121+
const obj = reactive({ foo: 1, bar: 2 })
122+
const msg = ref('hello')
123+
const setupResult = {
124+
msg,
125+
...toRefs(obj)
126+
}
127+
const p = proxyRefs(setupResult)
128+
129+
console.log(p.msg) // hello
130+
console.log(p.foo) // 1
131+
console.log(p.bar) // 2
132+
</script>

slides.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,8 +1136,76 @@ obj.foo = 10 // 修改可以触发执行
11361136
// newObj.foo.value = 10
11371137
```
11381138

1139+
---
1140+
1141+
## 自动脱 ref
1142+
1143+
在 Vue.js 中,如下面代码所示:
1144+
1145+
```vue
1146+
<template>{{ msg }}</template>
1147+
1148+
<script>
1149+
export default {
1150+
setup() {
1151+
const msg = ref('hello')
1152+
return { msg }
1153+
}
1154+
}
1155+
</script>
1156+
```
1157+
1158+
可以看到,我们不需要在模板中使用 `{{ msg.value }}` 来获取属性值,而是直接使用 `{{ msg }}`,这是怎么实现的呢?
1159+
1160+
---
1161+
1162+
我们知道,在 `setup()` 函数中会返回所有的响应式数据,那么是不是可以对返回值做一个代理,当访问 `ref` 数据时自动脱 ref 呢?
1163+
1164+
```vue
1165+
<script>
1166+
export default {
1167+
setup() {
1168+
const obj = reactive({ foo: 1, bar: 2 })
1169+
const msg = ref('hello')
1170+
1171+
return proxyRefs({
1172+
msg,
1173+
...toRefs(obj)
1174+
})
1175+
}
1176+
}
1177+
<script>
1178+
```
1179+
1180+
接下来,看看 `proxyRefs` 函数是如何实现的。
1181+
1182+
---
1183+
1184+
> 前文中,如果使用的是 `ref()` 定义的响应式数据,那么其内部会创建一个 `__v_isRef` 属性,用来标识当前数据是一个 `ref` 类型的数据。
1185+
1186+
```js
1187+
// demo: 16-proxyRefs.html
1188+
function proxyRefs(obj) {
1189+
return new Proxy(obj, {
1190+
get(target, prop, receiver) {
1191+
const value = Reflect.get(target, prop, receiver)
1192+
return value.__v_isRef ? value.value : value
1193+
},
1194+
set(target, prop, newVal, receiver) {
1195+
const value = Reflect.get(target, prop, receiver)
1196+
if (value.__v_isRef) {
1197+
value.value = newVal
1198+
return true
1199+
}
1200+
return Reflect.set(target, prop, newVal, receiver)
1201+
}
1202+
})
1203+
}
1204+
```
1205+
11391206
---
11401207
layout: center
1208+
transition: fade-out
11411209
---
11421210

11431211
# 编译器初探

0 commit comments

Comments
 (0)