vue-next学习笔记

481 阅读3分钟

前言:前段时间拜读了vue-next部分源码(v3.0.0-alpha.8),想在vue3.0正式版发布前了解下vue3.0的一些新特性,本文仅作为自己的学习记录和总结,欢迎拍砖探讨。

准备工作:

1、TypeScript学习,2019年是TS的一年,Vue 3全部采用TS构建,学会TS很有必要。

2、ES6+相关知识,如 Proxy 、 Reflect 等。

正文:

目录:

├── packages
│   ├── compiler-core // 编译器核心
│   ├── compiler-dom // dom解析&编译
│   ├── compiler-sfc // 文件编译系统│   ├── compiler-ssr // 服务端渲染
│   ├── reactivity // 数据响应
│   ├── runtime-core // 虚拟DOM渲染-核心
│   ├── runtime-dom // dom即时编译
│   ├── runtime-test // 测试runtime
│   ├── server-renderer // ssr
│   ├── shared // 帮助
│   ├── size-check // runtime包size检测
│   ├── template-explorer
│   └── vue // 构建vue

主要模块解析-响应式

众所周知,在 Vue 3 中使用了 Proxy 替换了原先的 Object.defineproperty 来实现数据响应,本文主要截取部分源码,按照个人理解添加相关注释,有注释不清晰的地方望指正。

数据响应模块

使用方法:

const obj = reactive({ num: 0 })
    // 当给obj.num 赋值会触发回调
    effect(() => {
        console.log(obj.num)
    })
obj.num = 1

reactive原理解析:

export function reactive(target: object) {  
    // 如果代理的对象是readonly的则直接return readonly对象的代理    
    if (readonlyToRaw.has(target)) {    
        return target  
    }   
    // 如果代理的对象被标记为readonly则调用readonly返回其代理    
    if (readonlyValues.has(target)) {        
        return readonly(target)    
    }    
    //如果是ref对象直接返回    
    if (isRef(target)) {        
        return target   
    }    
    // 调用createReactiveObject创建reactive对象    
    return createReactiveObject(        
        target,        
        rawToReactive,        
        reactiveToRaw,        
        mutableHandlers,        
        mutableCollectionHandlers    
    )
}

接着分析createReactiveObject方法-该方法主要用于创建响应式代理对象

function createReactiveObject(  
    target: unknown,  
    toProxy: WeakMap<any, any>,  
    toRaw: WeakMap<any, any>,  
    baseHandlers: ProxyHandler<any>,  
    collectionHandlers: ProxyHandler<any>
) {  

    // 如果非对象 直接return并且dev环境下打印警告  
    if (!isObject(target)) {    
        if (__DEV__) {      
            console.warn(`value cannot be made reactive: ${String(target)}`)    
        }    
        return target  
    }  

    // 目标对象可观察,直接返回已创建的代理  
    let observed = toProxy.get(target)  
    if (observed !== void 0) {    
        return observed  
    }  

    // 目标对象已经有代理,直接返回Proxy代理  
    if (toRaw.has(target)) {    
        return target  
    }  

    // 目标对象不可观察,直接返回目标对象  
    if (!canObserve(target)) {    
        return target  
    }  
    // 创建响应式代理,Set、Map、WeakMap、WeakSet的响应式对象handler与Object、Array的响应式对象handler不同,要分开处理  
    const handlers = collectionTypes.has(target.constructor) 
                     ? collectionHandlers : baseHandlers  
    // 创建代理  
    observed = new Proxy(target, handlers)  
    // 更新rawToReactive、reactiveToRaw的映射  
    toProxy.set(target, observed)  
    toRaw.set(observed, target)  
    return observed
}

ObjectArray的响应式对象handler,也就是baseHandlers.ts文件

主要分析createGetter()和createSetter()

function createGetter(isReadonly = false, shallow = false) {  
    return function get(target: object, key: string | symbol, receiver: object) { 
   
        // 对Array单独处理    
        if (isArray(target) && hasOwn(arrayInstrumentations, key)) {      
            return Reflect.get(arrayInstrumentations, key, receiver)    
        }    

        // 通过Reflect拿到原始对象的的get    
        const res = Reflect.get(target, key, receiver) 
   
        // 如果是内置方法,不处理    
        if (isSymbol(key) && builtInSymbols.has(key)) {      
            return res    
        } 
   
        // 官方标记TODO 应该是严格模式下返回只读对象    
        if (shallow) {      
            // track方法用于收集依赖      
            track(target, TrackOpTypes.GET, key)      
            return res    
        }    
        // 如果是ref对象-展开,只用于判断对象,不用于判断数组    
        if (isRef(res) && !isArray(target)) {      
            return res.value    
        }
        // 收集依赖    
        track(target, TrackOpTypes.GET, key)    
        // 拿到结果,判断是否嵌套对象,如果嵌套则递归调用reactive,避免循环调用    
        return isObject(res) ? isReadonly
                ? readonly(res) : reactive(res)      
                : res  
    }
}

Set、Map、WeakMap、WeakSet的代理暂且不表。

小结:

本文主要对响应式核心原理进行理解,笔者也将持续关注Vue3.x后续版本的更新,欢迎大家讨论。