被
readonly
代理过的,在被reactive
进行代理的时候直接返回readonly
的结果已经被代理过的对象再次代理则返回上次代理的结果
Reactivity模块基本使用
<div id="app"></div>
<script src="./reactivity.global.js"></script>
<script>
const { reactive, effect, shallowReactive, shallowReadonly, readonly } = VueReactivity;
// let state = reactive({ name: 'lyp', age: 30 });
// const state = shallowReactive({ name: 'lyp', age: 30 })
// const state = readonly({ name: 'lyp', age: 30 })
const state = reactive({ name: 'lyp', age: 30})
</script>
reactive
方法会将对象变成proxy对象,effect
中使用reactive
对象时会进行依赖收集,稍后属性变化时会重新执行effect
函数~。
编写reactive函数
- 1、将数据变成响应式的,
reactive
只能做对象的代理 - 2、在
set
和get
中 用Reflect
设置值和取值 - 3、响应式映射表 防止一个对象多次代理:
state1 = reactive(data
);state2 = reactive(data)
- 4、
ReactiveFlages
防止 代理对象再次被代理state1 = reactive(data)
;state2 = reactive(state1)
- 5、
get
的时候 对effect
进行依赖收集 - 6、
set
的时候 比对新旧值 根据依赖收集更新effect
函数 - 7、
get
时如果得到的是对象 递归深度代理
reactive接受的参数必须是一个对象类型
import { isObject } from "@vue/shared"
function createReactiveObject(target: object, isReadonly: boolean) {
if (!isObject(target)) {
return target
}
}
// 常用的就是reactive方法
export function reactive(target: object) {
return createReactiveObject(target, false)
}
已经被代理过对象处理__v_isReactive
const enum ReactiveFlags {
IS_REACTIVE = '__v_isReactive'
}
const mutableHandlers: ProxyHandler<object> = {
get(target, key, receiver) {
// 在get中增加标识,当获取IS_REACTIVE时返回true
if(key === ReactiveFlags.IS_REACTIVE){
return true;
}
}
}
function createReactiveObject(target: object, isReadonly: boolean) {
// 在创建响应式对象时先进行取值,看是否已经是响应式对象
if(target[ReactiveFlags.IS_REACTIVE]){
return target
}
}
要使用Reflect
进行操作,保证this
指向永远指向代理对象
解决以下问题
let target = {
name:'zf',
get alias(){
return this.name
}
}
const proxy = new Proxy(target,{
get(target,key,receiver){
// 去代理对象上取值 就走get
// return target[key];
console.log(key);
return Reflect.get(target,key,receiver)
},
set(target,key,value,receiver){
// 去代理上设置值 执行set
return Reflect.set(target,key,value,receiver);
}
});
proxy.alias; // 取alais值时,也取了name,但是没有监控到name
完整版
// __v_isReactive 为代理过对象的标识
export const enum ReactiveFlags {
IS_REACTIVE = '__v_isReactive'
}
export const mutableHandlers = {
get(target, key, receiver) {
// 第一次普通对象代理,我们通过 new Proxy代理
// 下一次传递的是proxy 可以看一下有没有代理过
// 如果访问这个proxy有get方法的时候 说明代理过了
if(key === ReactiveFlags.IS_REACTIVE){
return true
}
// 依赖收集 取值的时候 activeEffect 会和 key关联起来 执行完就没了’
// 调用的是effect中的方法
track(target,'get', key) // 调用的是effect中的方法
// 去代理对象上取值
// 可以监控到取值操作
let res=Reflect.get(target, key, receiver)
// 递归深度代理
if(isObject(res)){
return reactive(res);
}
return res
},
set(target, key, value, receiver) {
let oldValue = target[key]
let result = Reflect.set(target, key, value, receiver)
if(oldValue !== value){ // 值变化了
// 要更新有依赖的 effect
// 调用的是effect中的方法
trigger(target,'get', key, value, oldValue)
}
// 可以监控到设置值操作
return result
}
}
// 响应式映射表 防止一个对象多次代理:state1 = reactive(data) state2 = reactive(data)
// key只能是对象
const reactiveMap = new WeakMap()
export function reactive(target) {
//只对对象进行代理(除了null)
if (!isObject(target)) {
return
}
// 如果目标是一个代理过的对象 会触发 proxy中的get 会返回true,
//没代理过的就不会走到proxy中的get
if(target[ReactiveFlags.IS_REACTIVE]){
return target
}
// 并没有重新定义属性 只是代理 在取值的时候会调用get
let exisitingProxy = reactiveMap.get(target)
if(exisitingProxy) return exisitingProxy;
const proxy = new Proxy(target, mutableHandlers)
reactiveMap.set(target,proxy)
return proxy;
}
toRaw将响应式对象转成非响应式的
let obj={name:'lyp'}
let proxy = reactive(obj) // target -> proxy
console.log(obj === toRaw(proxy)) // proxy -> target
markRaw 标记对象不可被代理
let obj={name:'lyp'}
let proxy = reactive(markRaw(obj)) // 标记对象不可被代理
console.log(proxy) //__v_skip:true 表示不能被代理 使用Object.defineProperty实现
在代理数组的时候
如果是数组 对
includes
indexOf
lastIndexOf
pop
shift
push
splice
unshift
方法进行重写
const data = {name: 'lyp'}
const arr = reactive([data]) // 对象中嵌套对象 在取值的时候会进行递归处理
console.log(data === arr[0]) // 这里访问arr[0]的时候获取的是代理对象
// 这里调用includes vue内部会将代理对象转换成原始对象,在处理arr.includes时 原始的不行再去调用代理过的
console.log(arr.includes(data)) // 在正常情况下不应该为 true,所以要重写includes方法
在访问symbol属性和__proto__的时候 不做依赖收集处理直接返回属性值
// 下边的访问不会做依赖收集
console.log(state[Symbol.iterator])
console.log(state[Symbol.__proto__)