2-3-3 Vue3 Reactive的值:Ref和Reactive
Posted 沿着路走到底
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2-3-3 Vue3 Reactive的值:Ref和Reactive相关的知识,希望对你有一定的参考价值。
一个是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
以上是关于2-3-3 Vue3 Reactive的值:Ref和Reactive的主要内容,如果未能解决你的问题,请参考以下文章
vue3的ref和reactive以及toRef和toRefs的区别。
Vue3通透教程Vue3中的响应式数据 reactive函数ref函数