一个是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

Logo

前往低代码交流专区

更多推荐