reactive
- 只支持引用类型
import { reactive } from 'vue'
const data = reactive('童渊') // 报错
const data2 = reactive({}) // 正确
- 访问属性不需要
.value
import { reactive, onMounted } from 'vue'
const data = reactive({
name: "童渊"
})
onMounted(() => {
console.log(data.name); // 童渊
})
- 使用数组例子
import { reactive } from 'vue'
const data = reactive(["诸葛亮", "黄月英", "诸葛果"])
<div>
<select name="select" id="select">
<option :value="index" v-for="(item,index) in data" :key="index">{{ item }}</option>
</select>
</div>
- 使用对象例子
const data = reactive({
nickName: "",
pwd: ""
})
const submit = function () {
console.log(data); // reactive 对象 Proxy {nickName: '', pwd: ''}
}
<div>
<form>
<input type="text" v-model="data.nickName"><br />
<input type="text" v-model="data.pwd"><br />
<button @click.prevent="submit">登录</button>
</form>
</div>
- 不可以给
reactive
对象直接赋值,会破坏响应式数据。模拟异步操作:
import { reactive, onMounted } from 'vue'
let data = reactive({}) // <--- 传的是一个对象 {}
const getData = function (): any {
setTimeout(() => {
let res = {
code: 200,
data: [
{ name: '吕布' },
{ name: "关羽" },
{ name: "张飞" },
{ name: "刘备" }
]
}
data = res.data
console.log(data);
}, 2000)
}
onMounted(getData)
<ul>
<li v-for="(item, index) in data" :key="index">{{ item }}</li>
</ul>
像上面这样写,页面中是不会有任何渲染的,尽管控制台已经打印了数据
解决方案:
如果你传递的是一个对象的话,那么就再新增一个属性,把数据赋值给属性
import { reactive, onMounted } from 'vue'
interface IList {
name: string
}
let data = reactive({
// list: [] as any,
// list: <any>[],
list: [] as IList[] // <------------- 新增的 list 属性
})
const getData = function (): any {
setTimeout(() => {
let res = {
code: 200,
data: [
{ name: '吕布' },
{ name: "关羽" },
{ name: "张飞" },
{ name: "刘备" }
]
}
data.list = res.data // <---------- 把数据赋值给属性
console.log(data);
}, 2000)
}
onMounted(getData)
<div>
<ul>
<li v-for="(item, index) in data.list" :key="index">{{ item.name }}</li>
</ul>
</div>
你如果传的是一个数组,可以利用数组push
方法+解构赋值
来实现:
import { reactive, onMounted } from 'vue'
interface IData {
name: string
}
let data = reactive(
// [] as any
// <any>[]
[] as IData[] // < ---------- 传的是数组
)
const getData = function (): any {
setTimeout(() => {
let res = {
code: 200,
data: [
{ name: '吕布' },
{ name: "关羽" },
{ name: "张飞" },
{ name: "刘备" }
]
}
data.push(...res.data)
console.log(data);
}, 2000)
}
onMounted(getData)
<div>
<ul>
<li v-for="(item, index) in data" :key="index">{{ item.name }}</li>
</ul>
</div>
上面这种方法,同样可以实现保持响应式
shallowReactive
跟shallowRef
差不多
- 浅层响应式
import { reactive, shallowReactive } from 'vue'
const data = reactive({
hero: {
name: '小乔',
}
})
const shallowData = shallowReactive({
food: {
name: "苹果",
price: {
p1: '4'
}
}
})
const change = function () {
console.log(data, shallowData);
shallowData.food.price.p1 = "888"
console.log(shallowData.food.price.p1);
}
<div>
<div>{{ shallowData }}</div>
<button @click="change">修改</button>
</div>
像上面这种写法,虽然值确实是发生了改变,但是页面却不会更新
shallowReactive
响应式只到第一个属性,需要改成下面这种写法
const change = function () {
shallowData.food = { name: '香蕉', price: { p1: "888" } }
}
如果只传一个基础数据类型,那么就会报错:
let shallowData = shallowReactive({
name: "郭嘉"
})
const change = function () {
shallowData = '张角'
}
报错信息:
let shallowData: ShallowReactive<{
name: string;
}>
不能将类型“string”分配给类型“ShallowReactive<{ name: string; }>”。
不能将类型“string”分配给类型“{ name: string; }”。
- 和
reactive
放在一起修改值时,会受reactive
的影响
import { reactive, shallowReactive } from 'vue'
const data = reactive({ name: "司马徽" })
let shallowData = shallowReactive({
sObj: {
name: "于吉"
}
})
const change = function () {
console.log('修改前: ', data.name); // 修改前: 司马徽
console.log('修改前: ', shallowData.sObj.name); // 修改前: 于吉
data.name = "张宝"
shallowData.sObj.name = "孙策"
console.log('修改后: ', data.name); // 修改后: 张宝
console.log('修改后: ', shallowData.sObj.name); // 修改后: 孙策
}
<div>
<button @click="change">修改</button>
</div>
当修改reactive
的值发生改变时,shallowReactive
的值也同样会被修改
readonly
- 把
reactive
对象的值,变成只读的
import { reactive, readonly, onMounted } from 'vue'
const data = reactive({ name: "周瑜" })
const readonlyData = readonly(data)
onMounted(() => {
readonlyData.name = "左慈" // 无法为“name”赋值,因为它是只读属性。
})
- 也是会被
reactive
所影响
import { reactive, readonly, onMounted } from 'vue'
const data = reactive({ name: "周瑜" })
const readonlyData = readonly(data)
onMounted(() => {
console.log('修改前', data.name); // 修改前 周瑜
console.log('修改前', readonlyData.name); // 修改前 周瑜
data.name = "贾诩"
console.log('修改后', data.name); // 修改后 贾诩
console.log('修改后', readonlyData.name); // 修改后 贾诩
})
源码解析
文件位置: \core-main\packages\reactivity\src\reactive.ts
// <T extends object> // 泛型约束
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
// 如果它是只读属性,直接返回
if (isReadonly(target)) {
return target
}
// 创建 reactive 对象
return createReactiveObject(
target,
false,
mutableHandlers,
mutableCollectionHandlers,
reactiveMap
)
}
至于reactive
为什么只接收引用数据类型,可以看到reactive
它是有泛型约束
的,它继承于object
createReactiveObject
函数:
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
}
// target is already a Proxy, return it.
// exception: calling readonly() on a reactive object
// 如果传的对象已经被代理过 并且 不是要把响应式对象变成只读的, 直接返回
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
// target already has corresponding Proxy
// 不了解,有待学习 WeakMap 是个啥?
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// only specific value types can be observed.
// 英文翻译: 只能观察到特定的值类型
// 意思是类似于白名单、通行证之类?
// 如果我们传的对象,在这个白名单中,就直接返回,没有就直接创建 Proxy 代理
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
}
关于reactive
的源码暂时就看到这里吧
总结
reactive
- 创建响应式对象(深层)
- 只支持引用类型
- 访问值不需要
.value
- 不可以直接给
reactive
对象赋值,会破坏响应式
shallowReactive
- 创建响应式对象(浅层)
- 会被
reactive
对象影响
readonly
- 把
reactive
对象的值变成只读的 - 会被
reactive
对象影响