前言
vue3也用了很久了一直说要去看源码的,一直拖到了现在,怎么说呢现在开始也不晚。
vue3的响应模块主要就是在reactive里面,reactive看懂了看其他模块也就照猫画虎了
vue3依赖于pnpm下的monorepo和ts进行源码的管理和开发,所以主要的代码包文件在packages文件夹下,对源码打包后可以在packages/vue/examples下新建文件调试,本次解析文件在packages/reactivity下
/packages/reactivity/index.ts
主要导出常用的API看源码时以此文件作为入口文件查看
reactive、readonly、shallowReactive、shallowReadonly
这四个API都基于同一个高阶函数createReactiveObject,
下面从reactive实例上进入
/packages/vue/examples下新建reactive.html右键with Live Server执行调试
<script src="../dist/vue.global.js"></script>
<div id="app"></div>
<script>
const { reactive,readonly,effect,ref } = Vue
//使用reactive声明复杂数据类型
const obj = reactive({
name: '张三',
})
effect(() => {
document.querySelector('#app').innerText = obj.name
})
setTimeout(() => {
obj.name = '李四'
}, 2000)
</script>
reactive的源码/packages/reactivity/src/reactive.ts
export const reactiveMap = new WeakMap<Target, any>()
export function reactive(target:object){
return createReactiveObject(
target,
false,
mutableHandlers,// 从baseHandlers导入
mutableCollectionHandlers,
reactiveMap
)
}
createReactiveObject(
target: Target, //要代理的数据
isReadonly: boolean, // 是否只读
baseHandlers: ProxyHandler<any>, // Proxy代理的第二个参数,主要包含get和set
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>, //存储以target为key value为depsMap=>(可以理解为target的key和depEffect的Map结构)的WeakMap结构
/*baseHandlers中主要是对proxy代理数据的操作
包含get\set\deleteProperty\has\ownKeys等操作
*/
/*proxyMap:在实例中对应的结构
WeakMap{
key:obj,
value:Map{
key:'name',
value:Set[Effect,Effect]
}
}*/
){
// 判断是否是object
// 判断是否已代理过
...
const proxy = new Proxy(target,baseHandlers)
proxyMap.set(target,proxy)
return proxy
}
/packages/reactivity/src/baseHandlers.ts
export const mutableHandlers: ProxyHandler<object> = {
get,//可拦截对象属性的访问
set, //拦截对象属性的赋值
deleteProperty,
has,
ownKeys
}
const get = /*#__PURE__*/ createGetter() // createGetter同样也是高阶函数柯里化
export const createGetter(isReadonly = false, shallow = false){
return get(target: Target, key: string | symbol, receiver: object){
// 一系列判断条件
...
const res = Reflect.get(target, key, receiver);
// 收集依赖
track(target,'get',key);
if(isObject(res)){//此处有优化只有在使用时才对属性做代理
return isReadonly?readonly(res):reactive(res);
}
return res;
}
}
const set = /*#__PURE__*/ createSetter()
function createSetter(shallow = false){
return set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
):boolean {
let oldValue = (target as any)[key]//存储原始数据
....
Reflect.set(target,key,value,receiver);
// hasChanged数据有变化
// 触发依赖
trigger(target,'set',key,value,oldValue)
}
}
走了第一步,以后的每一步都会越来越轻松