reactive
reactive和ref的区别:
- ref支持传入所有类型,reactive只支持引用类型(Array、Object、Map、Set、WeakSet、WeakMap等)
- ref取值和赋值都要加.value,reactive不需要
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
| <template> <div> <form> <input v-model="form.name" type="text"> <br> <input v-model="form.age" type="text"> <br> <button @click.prevent="submit">提交</button> </form> </div> <br>
<!-- --------------------------------------------------------- -->
<div> <ul> <li v-for="(item, index) in list" :key="index">{{ item }}</li> <!-- <li v-for="(item, index) in list.arr" :key="index">{{ item }}</li> --> </ul> <button @click="add"></button> </div> </template>
<script setup lang="ts"> import { reactive } from "vue" type P = { name: string; age: number; }
// 只支持引用类型的参数,用基本类型就报错 // let form = reactive("")
// 和ref一样可以进行类型推断,也可以自己定义 // let form = reactive<P>({ // name: "yajue", // age: 24 // }) let form = reactive({ name: "yajue", age: 24 })
// 不需要.value // form.age = 114514 const submit = ()=> { console.log(form) }
// --------------------------------------------------------------
// reactive是proxy代理的对象,直接赋值会覆盖掉它 let list = reactive<string[]>([])
// let list = reactive<{ // arr: string[] // }>({ // arr: [] // })
const add = ()=> { // 假设它是接口返回的数据 setTimeout(() => { let res = ["www", "草", "kusa"] // 会破坏响应式对象,不能直接赋值 // list = res
// 解决方案1:可以用数组方法 list.push(...res)
// 解决方案2:把数组作为一个对象属性,并把对象赋给reactive // list.arr = res
console.log(list) }, 2000) } </script>
|
readonly
拷贝一份proxy对象,并将其设为只读。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| <template> <div> <button @click="show"></button> </div> </template>
<script setup lang="ts"> import { reactive, readonly } from 'vue'
let form = reactive({ name: "yajue", age: 24 })
// 把reactive所有属性变成只读 // 实际生产中用得不多,但源码里用得很多 const read = readonly(form)
const show = ()=> { // 会报错,因为read是只读的 // read.name = "野兽" // console.log(form, read) // -> {name:"yajue",age:24}, {name:"yajue",age:24}
// 不会报错,因为form不是只读的 // read会受原始对象影响 form.name = "野兽" console.log(form, read) // -> {name:"野兽",age:24}, {name:"野兽",age:24} } </script>
|
shallowReactive
只有浅层的数据(第一层)才会变成响应式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <template> <div> <div>reactive: {{ form }}</div> <div>shallowReactive: {{ obj }}</div> <button @click="edit"></button> </div> </template>
<script setup lang="ts"> import { reactive, readonly, shallowReactive } from 'vue'
let form = reactive({ name: "yajue", age: 24 })
// shallowReactive 作浅层响应式 const obj = shallowReactive<any>({ foo: { bar: { num: 114514 } } })
const edit = ()=> { // 改变第一层的属性可以响应 // obj.foo = { num = 1919810 }
// 改变深层嵌套的属性就不会引起视图的更新 // obj.foo.bar.num = 1919810 // console.log(obj)
// 和ref&shallowRef一样,reactive和shallowReactive同时写也会出现问题 form.name = "野兽" obj.foo.bar.num = 1919810 } </script>
|
源码
在Vue源码(/package/reactivity/src/reactive.ts
)中可以看到reactive的源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
| export function reactive<T extends object>(target: T): UnwrapNestedRefs<T> export function reactive(target: object) { if (isReadonly(target)) { return target } return createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap ) }
function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any>, proxyMap: WeakMap<Target, any> ) { if (!isObject(target)) { if (__DEV__) { console.warn(`value cannot be made reactive: ${String(target)}`) } return target } if ( target[ReactiveFlags.RAW] && !(isReadonly && target[ReactiveFlags.IS_REACTIVE]) ) { return target } const existingProxy = proxyMap.get(target) if (existingProxy) { return existingProxy } const targetType = getTargetType(target) if (targetType === TargetType.INVALID) { return target } const proxy = new Proxy( target, targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) proxyMap.set(target, proxy) return proxy }
|