vue3 源码解析之Reactive实现的原理

第一节:vue3 响应式的API;
reactive(响应): shallowReactive(浅响应): readonly(只读): shallowReadonly(浅的只读), 浅相当于第一层。

import {reactive,readonly,shallowReactive,shallowReadonly} from 'vue'

const obj = reactive({name:'张三',list:;[{like:{live:'篮球'}]})
reactive: // 响应式的数据无论深浅整体会被进行代理;

readonly:// 只读的数据,无法对数据进行修改

shallowReactive: // 只代理浅层的数据 深层数据不会被代理

shallowReadonly:// 第一层是只读的,可以修改深层次的数据

第二节 封装以上响应式API的方法 :
以上的方法均采用了高阶函数的方法: 何为高阶函数, 函数的参数或者返回值为一个函数的时候。
在这里需要注意的点, 该API 分为是否为只读的 是否为深代理的 

const reactiveHandlers = {}

const shallowReactiveHandlers = {}

const readonlyHanlders = {}

const shallowReadonlyHandlers = {}


export const reactive(target){ // target 为一个对象

    return createReactObj(target,false,reactiveHandlers) //目标对象 是否可读 上面的对象

}

export const shallowReactive(target){ // target 为一个对象

    return createReactObj(target,false,shallowReactiveHandlers)

}

export const readonly(target){ // target 为一个对象

    return createReactObj(target,true,readonlyHandlers)

}

export const shallowReadonly(target){ // target 为一个对象

    return createReactObj(target,true,shallowReadonlyHandlers)

}

// 这里是实现proxy代理的核心代码

// 下面定义两个数据结构 用来进行优化 判断该对象是否已经被代理过了 防止重复代理的问题 

const reactiveMap = new WorkMap() // 这里面的key 是一个对象 定义的是非可读的
const readonlyMap = new WorkMap() // 这个定义的是可读的

function createRreactObj (target,isReadonly,baseHandlers){


    // 第一步判断 target是否为object对象 如果是的用proxy进行代理
    
    if(typeof target === 'object' && target != null ){

     // 第二步判断 该对象是否已经被进行代理过了 

      const proxymap = isReadonly?readonlyMap:reactiveMap //根据isReactive判断使用数据结构
    
          if(proxymap.get(target)){ // 这里代表已经被进行代理了 
        
                return proxtymap.get(target) // 将代理过的再返回出去 无需再代理

          }else {

              // 这里再做代理的工作
    
               const proxy =  new Proxy(target,baseHandlers)

                proxyMap.set(target,proxy) // 把代理对象和目标对象放到结构表中 
                
               return proxy // 返回的是代理后的对象

          }
        
    
      }else {

        return target
      }
    
    
    }

第三节 封装reactiveHandlers shallowReactiveHandlers  readonlyHandlers  shallowReadonlyHandlers ;
******这里有一个知识点: 懒代理 const obj = {name:'zs', list:{live:'apple'}}
当你不使用obj.list的时候不会对里面的数据进行代理,因为proxy默认只代理第一层,如果使用里面的数据 就会被重新代理了。

const get = createGetter() // 不是只读的 深的

const shallowGet = createGetter(false,true) //  不是只读的  浅的

const readonlyGet = creareGetter(true) // 只读的

const shallowReadonlyGet = creareGetter(true,true) // 只读的 浅的


// 定义 createGetter这个方法

function createGetter(isReadonly=false,isShallow=false){ // 默认的参数都为false 

    return function get(target,key,receiver){

        const res = Reflect.get(target,key,receiver) // target[key]


        if(!isReadonly){  // 不是只读的
        
            // 收集依赖
            
        }

        if(shallow){ // 是浅的

            return res 

        }
         
        // 这里使用了懒代理 const obj = {list:{name:'zs'}} 如果不obj.list里面数据不会被代理

        if(typeof res === 'object' && res !== null){ // 这里代表key是对象 需要递归

            return isReadonly?readonly(res):reactive(res) // 递归

        }

        return res 
    }

}

-------------------------------------------------------以上处理get方法的 


const set = createSetter()

const shallowSet = createSetter(true)

function createSetter(shallow=false){
    
    return function set(target,key,value,receiver){

        const result = Reflect.set(targer,key,value,receiver) // 获取最新的值

        return  result 

    }
}


--------------------------------------------------------这里处理set方法的

export const reactiveHandlers = {

    get:get,

    set:set

}

export const shallowReactiveHandlers = {//浅的{list:{a:b}} 里面的a不能被代理也就不是响应式
    
    get:shallowGet,

    set:shallowSet
}

export const readonlyHandlers = {

    get:readonlyGet,
    
    set:()=>{ // 这里set 是不允许被修改对象的值

        console.log('set on key is faild')

    }
}

export const shallowReadonlyHandlers  = {/

    get:shallowReadonlyGet,

    set:()=>{ // 这里set 是不允许被修改对象的值

        console.log('set on key is faild')

    }

}

第四节 effect用来收集依赖 相当于vue2中的watcher



    

手写effect这个方法:

// 定义effect 这个方法 返回值是里面的小函数 
function effect(fn,options={}) {

    const effect = createReactEffect(fn,options) {

        if(!options.lazt){ // 如果是不是懒加载 就立即执行  如果是懒加载就返回这个函数
    
            effect()   //  执行这个方法就相当于执行下面的函数 fn()     

        }

        return effect
    }
}

// 创建 createReactEffect这个方法

let uid = 0

let activeEffect // 保存当前的effect

const effectStack = []

 function createReactEffect (fn,optons){

    const  effect = function reactiveEffect(){

    if(!effectStack.includes(effect)){ // effect没有入栈 再执行下面的代码

        try{

         // 入栈
            
         effectStack.push(effect)

         activeEffect = effect

         fn()

        
        }finaly{

        //  出栈

        effectStack.pop()

        activeEffect =  effectStack[effectStack.length-1]

        }
    }
    

    }

    effect.id = uid++ // 区别effect

    effect.isEffect = true // 区别effect 是不是响应式的

    effect.raw = fn // 保存用户的方法

    effect.options = options // 保存用户的属性

    return effect
}


//  在获取数据的时候触发get 收集依赖 effect

let targetMap = new WeakMap()

export function Track(target,type,key){

if(activeEffect===undefined) {return} // 没有effect的使用

let depMap = targetMap.get(target)

if(!depMap) { // 没有  添加

    targetMap.set(targer,(depMap = new Map()))

}

let dep = depMap.get(key) // 有 

if(!dep){ // 没有属性

    depMap.set(key,(dep=new Set))
}

if(!dep.has(activeEffect)){ // 有没有收集effect 没有的话 收集

    dep.add(activeEffect)
}
    

}

这个在上面的函数中需要执行的收集依赖的方法

if(!isReadonly) { // 不是只读的收集依赖 

    Track(target,type,key) // target目标对象 type操作添加修改删除 key 属性名

    

}


第五节: 对象的属性的修改 需要触发set方法。

function createSetter(shallow=false){
    
    return function set(target,key,value,receiver){

        const oldValue = target[key] // 获取老值

        // 进行判断 目标对象是不是数组  索引是否为整数

  let haskey =  Array.isArray(target) && (key)=>{parseInt(key)+'' === key}?         
  Number(key) 
  {Object.prototype.hasOwnProperty.call(target,key)}

  const result = Reflect.set(targer,key,value,receiver) // 获取最新的值

        if(!haskey) { // 如果没有就是新增

            trigger(target,ADD,key,value)

        }else { // 修改 新值和原来的值一样

            if(value!==oldValue){ // 新值和老值不相同 在执行这个方法

                 trigger(target,Set,key,value,oldValue)

            }

           

        }

        return  result 

    }
}

export  function trigger(target,type,key?,newValue,oldValue){

    const depsMap = targetMap.get(target) 

    if(! depsMap){ // 是否存在依赖收集

        return 

    }

    // 有的情况下
    
    let effectSet = new Set() // 如果有多个修改同一个值 并且值 相同

    const add  = (effectAdd)=>{

        if(effectAdd){
        
            effectAdd.forEach(effect=>effectSet.add(effect))

        }
    }
    

    // 处理数组 就是key === length 

    if(key==='length' && Array.isArray(target)){

        depsMap.forEach(dep,key)=>{

            if(key === 'length' || key > newValue) {

                add(dep)

            }
        }

    }else{ // 可能是对象


        if(key != undefiend) {

            add(depsMap.get(key))
        }

    }

    // 执行 
    effectSet.froEach((effect:any)=>effect())

}



 

你可能感兴趣的:(vue.js,javascript,前端)