1、Vue3

Vue3 GitHub 地址:https://github.com/vuejs/vue-next
新版的Vue3主要是由 几个 package 为核心实现的

compiler-core ==> 编译器核心;
compiler-dom ==> 浏览器渲染编辑模块;
compiler-sfc ==> 服务端渲染编译模块;
runtime-dom ==> 针对浏览器的运行时;
runtime-core ==> 运行时核心;
reactivity ==> 响应式系统;

2、reactiveity 实现

Vue2 响应式数据使用的是 defineProperty 属性,每次观察都需要增加getter和setter属性,如果有深层属性,会形成递归,递归会影响性能,如果对象是数组或深层对象,不能形成观察。

Vue3使用ES6的Proxy方式来解决这个问题。首先Proxy是支持数组的也就是数组是不需要做特别的代码的。对于深层监听也不不必要使用递归的方式解决。当get是判断值为对象时将对象做响应式处理返回。在大性能上得到很大的提升。

code...


function reactive(data) {
    if (typeof data !== 'object' || data === null) {
        return data
    }
    const observed = new Proxy(data, {
        get(target, key, receiver) {
            // Reflect有返回值不报错
            let result = Reflect.get(target, key, receiver)

            // 多层代理
            return typeof result !== 'object' ? result : reactive(result) 
        },
        set(target, key, value, receiver) {
            effective()
            // proxy + reflect
            const ret = Reflect.set(target, key, value, receiver)
            return ret
        },

        deleteProperty(target,key){
            const ret = Reflect.deleteProperty(target,key)
            return ret
        }

    })
    return observed
}

3、effect 实现

Vue3的响应式核心主要是effect函数依赖。主要使用 Proxy 和 Reflect 进行对象代理和反射内部属性 做依赖收集,通过栈核心收集这个对象,每次对象发生变化时,去执行这个对象的属性的setter和gettter,触发依赖更新,总来的来说就会形成effect。

code...


export function effect(fn, options) {
    // watch effect
    const effect = createReactiveEffect(fn, options)
    return effect
}

// 创建响应式的effect
let uid = 0 // 创建一个唯一性id
let activeEffect // 创建一个当前执行的 effect
const effectStack = [] // 栈结构 类数组 收集effect
function createReactiveEffect(fn, options) {
    const effect = function reactiveEffect() {
        if (effectStack.includes(effect)) {
            try {
                effectStack.push(effect)
                activeEffect = effect // 当前执行的是哪个effect
                return fn()
            } finally () {
                effectStack.pop() // 执行完之后 抛出
                activeEffect = effectStack[effectStack.length - 1] // 当前执行的effect 也置空
            }
        }
    }
    effect.id = uid++
    effect.options = effect
    effect.dep = [] // 依赖的 effect
    return effect
}


// track 
const targetMap = new WeakMap() //用法和map一致  但是弱引用 不会导致内存泄漏
export function track(target, type, key) {
    // a = [effect,effect]  b = [effect]
    if (activeEffect == undefined) {
        return // 说明取值的属性 不依赖于 effect
    }
    let depsMap = targetMap.get(target) // 根据key 来进行取值

    if (!depsMap) {
        targetMap.set(target, (depsMap = new Map()))
    }
    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, (dep = new Set()))
    }
    if (!dep.has(activeEffect)) {
        dep.add(activeEffect) // { "{name:'zf'}":{name:set(effect)}  }
        // activeEffect.deps.push(dep); // 让这个effect 记录dep属性
    }
}
// trigger 
export function trigger(target, type, key, value, oldValue) {
    const depsMap = targetMap.get(target) // 获取当前对应的map
    if (!depsMap) {
        return
    }
    // 计算属性要优先于effect执行
    const effects = new Set()
    const computedRunners = new Set()

    const add = (effectsToAdd) => {
        if (effectsToAdd) {
            effectsToAdd.forEach((effect) => {
                if (effect.options.computed) {
                    computedRunners.add(effect)
                } else {
                    effects.add(effect)
                }
            })
        }
    }
    if (key !== null) {
        // arr.push(4) [1,2,3    , 4]   push length
        add(depsMap.get(key))
    }
    if (type === TriggerOpTypes.ADD) {
        // 对数组新增属性 会触发length 对应的依赖 在取值的时候回对length属性进行依赖收集
        add(depsMap.get(Array.isArray(target) ? 'length' : ''))
    }
    const run = (effect) => {
        if (effect.options.scheduler) {
            effect.options.scheduler()
        } else {
            effect()
        }
    }
    computedRunners.forEach(run)
    effects.forEach(run)
}

4、computed

computed的实现 是在effect 之上实现的,computed可以实例自己的get和set方法, computed 是懒 执行,等需要时再执行,而不是 实例化就去执行的,这点和 effect不一样,

// ----------- operation
export const TrackOpTypes = {
    'GET':'get'
}
export const TriggerOpTypes = {
    'SET':'set',
    'ADD':'add'
}
//----------- computed

import { isFunction } from "../shared/utils";
import { effect, track, trigger } from "./effect";
import { TrackOpTypes, TriggerOpTypes } from "./operation";

export function computed(getterOrOptions) {
    let getter;
    let setter;

    if (isFunction(getterOrOptions)) {
        getter = getterOrOptions;
        setter = () => {}
    }else{
        getter = getterOrOptions.get;
        setter = getterOrOptions.set;
    }
    let dirty = true; // 默认第一次取值是执行getter方法的

    let computed;
    // 计算属性也是一个effect 
    let runner = effect(getter,{
        lazy:true, // 懒加载
        computed:true, // 这里仅仅是标识而已 是一个计算属性
        scheduler:()=>{
            if(!dirty){
                dirty = true; // 等会就算属性依赖的值发生变化后 就会执行这个scheduler
                trigger(computed,TriggerOpTypes.SET,'value')
            }
        }
    })
    let value;
    computed = {
        get value(){
            if(dirty){ // 多次取值 不会重新执行effect
                value = runner();
                dirty = false;
                track(computed,TrackOpTypes.GET,'value')
            }
            return value;
        },
        set value(newValue){
            setter(newValue);
        }    
    }

    return computed;
}