2-3-3 Vue3 Reactive的值:Ref和Reactive
一个是Reactive,那么:- 它可以通知(trigger)- vue更新- vue做其他标准行为- 完成自定义的行为- 它可以监听(track)- vue发生的变化(依赖)ref - reference`ref` 是一个工厂方法,它本质是创造一个`Ref`对象。`ref`的目标是管理值。首先,`ref` 可以像一个正常值一样被使用:import { ref } from "vue"export
一个是Reactive,那么:
- 它可以通知(trigger)
- vue更新
- vue做其他标准行为
- 完成自定义的行为
- 它可以监听(track)
- vue发生的变化(依赖)
ref - reference
`ref` 是一个工厂方法,它本质是创造一个`Ref`对象。`ref`的目标是管理值。
首先,`ref` 可以像一个正常值一样被使用:
import { ref } from "vue"
export default {
setup() {
const msg = ref("hello") // RefImpl
return () => {
return <div>{msg.value}</div>
}
}
}
上面程序中:
- setup是一个vue3新特性(帮助初始化组件)
- setup可以返回一个render函数,render函数返回的是VNode
jsx的语法`div` 会被babel翻译成`createVNode` 。
`ref` 和一个正常值看上去很像。
ref是值的代理
ref是一个`setter` 和`getter` ,如下面这段代码很好的栓释了ref的内部实现。
当然仅仅有`setter` 和`getter` 是不够的,还需要一些`reactive` 的机制:
function track(){
}
function trigger(){
}
function createRef<T>(val : T){
let _val : T = val
const refObj = {
set value(v : T){
console.log('setter called')
if(_val !== v) {
trigger()
_val = v
}
},
get value() {
console.log('getter called')
track()
return _val
}
}
return refObj
}
const a = createRef(0)
a.value = 10
a.value ++
console.log(a.value)
当set的时候trigger,get的时候track
- trigger:驱动vue更新
- track:跟踪vue的更新(Why?)
- 一个ref可以给多个vue组件用(因此依赖是不确定的)
- 为什么叫做依赖?(Deps)
- 因为vue组件依赖ref,因此是ref的依赖
- ref的依赖应该是一个数组(集合似乎更好!)
- 为什么不能在ref的构造函数中确定依赖……?
- 为什么不直接操作依赖而是封装一个`track`方法?
- 为什么不是vue来检查依赖,而是ref track更新依赖?——发现能力更出色、更Reactive。
Ref驱动更新的示例
import { ref } from "vue"
export default {
setup() {
const counter = ref(0)
return () => (
<div>
{counter.value}
<button
onClick={() => {
counter.value++
}}
>
add
</button>
</div>
)
},
}
Reactive
Reactive和Ref类似,都是代理模式的Reactive值。代理一个值用`getter` 和`setter` 很方便,代理一个对象呢?——JS提供了Proxy类。
代理一个对象
function createReactive(obj : any) {
return new Proxy(obj, {
// get trap
get : (target, name, receiver) => {
console.log('get value', name)
if(name === 'c') {
return "this is a proxy value"
}
return Reflect.get(target, name, receiver)
},
// set trap
set : (target, name, value, receiver) :boolean => {
if(!(name in target)) {
return false
}
Reflect.set(target, name, value, receiver)
console.log('set value to', value, receiver)
return true
}
})
}
const o = createReactive({
a : 1,
b : 2,
foo : function() {
console.log('a is', this.a)
}
})
o.a = 100
console.log(o.c)
o.foo()
- 为什么用Reflect.set、Reflect.get而不用target[name]这种形式?
- 可以在getter和setter间同步receiver(this指针)
一个坑:
const p = new Proxy({
a : 1
}, {
get(target, property, receiver) {
console.log("get trap", ...arguments)
return Reflect.get(receiver, property,receiver)
}
})
console.log(p.a)
上面程序会`Stack Overflow`
Reative是一个代理对象
const state = reactive({
counter : 0
})
state.counter ++
上面的程序会触发代理对象的`getter` 然后`setter` ,因为`++` 不是`atomic` 原子操作(记住这个单词:atomic)。
具体的和`ref` 一致, Reactive也会在getter中track,在setter中trigger
Reactive实现的Counter
import { reactive} from "vue"
export default {
setup() {
const state = reactive({
counter : 0
})
return () => (
<div>
{state.counter}
<button
onClick={() => {
state.counter++
}}
>
add
</button>
</div>
)
},
}
Ref和Reactive
它们都是`vue` 提供的`reactive` 值。 Ref维护一个值/对象,Reactive维护一个对象的所有成员。
例如:
const obj = ref({
a : 1,
b : 2
})
obj.value.a ++ //不会触发重绘
obj.value = {...obj.value, a : 1} // 触发重绘
const obj1 = reactive({
a : 1,
b : 2
})
obj1.a ++ // 触发重绘
有一个函数叫做`toRef` ,还有一个函数叫做`toRefs` ,这两个函数可以将值转换为`ref` ,举个例子:
import { defineComponent, reactive, toRef, toRefs } from "vue"
export default defineComponent({
setup() {
const state = reactive({
a : 1,
b : '-'
})
const aRef = toRef(state, 'a')
const bRef = toRef(state, 'b')
// 等价于
//const refs = toRefs(state)
//const aRef === refs.a
//const bRef === refs.b
return () => {
return <>
<h1>aRef : {aRef.value}</h1>
<h1>aRef : {aRef}</h1>
<h1>bRef : {bRef.value}</h1>
<h1>bRef : {bRef}</h1>
<button onClick={() => state.a++}>state.a++</button>
<button onClick={() => aRef.value++}>aRef.a++</button>
</>
}
}
})
另外reactive会自动将ref拆包。
const r = reactive({
a : ref(0)
})
// 等价于:
const r = reactive({a : 0})
Reactive 和 Ref的类型
在Reactive和Ref中,通过UnwrapRef的定义配合infer关键字,做到了两种类型合一。 因此从类型的角度看`ref` 和`reactive` 是同类东西。
export declare function ref<T>(value: T): Ref<UnwrapRef<T>>;
export declare function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export declare type UnwrapNestedRefs<T> = T extends Ref ? T : UnwrapRef<T>;
export declare type UnwrapRef<T> = T extends Ref<infer V> ? UnwrapRefSimple<V> : UnwrapRefSimple<T>;
declare type UnwrapRefSimple<T> =
T extends
Function |
CollectionTypes |
BaseTypes |
Ref | RefUnwrapBailTypes[keyof RefUnwrapBailTypes] ?
T :
T extends Array<any> ?
{
[K in keyof T]: UnwrapRefSimple<T[K]>;
} :
T extends object ? UnwrappedObject<T> : T
1
更多推荐
所有评论(0)