vue3 원본 학습 - 응답식reactive

문서 목록

  • 차단
  • 베이스
  • 객체 중복 방지 proxy
  • proxy 대상이 다시 proxy
  • 에 의해
  • 객체 중
  • 감청
  • 감청 함수
  • 삭제 보완
  • 전체 코드
  • 가로막다


    베이스


    의 원리
    어떻게 데이터의 변화를 감청합니까?
    Proxy、Reflect
    function reactive(target) {
         
        return new Proxy(target, handle)
    }
    const handle = {
          get, set };
    
    function get(target, propKey, receiver) {
         
        // console.log('get');
        // return target[propKey]
        return Reflect.get(target, propKey, receiver)
    }
    function set(target, propKey, value, receiver) {
         
        // console.log('set')
        // return target[propKey] = value
        return Reflect.set(target, propKey, value, receiver)
    }
    

    테스트
    let obj = reactive({
         a:1});
    console.log(obj.a)//  get  
    obj.a = 2//  set  
    

    대상이 proxy를 반복하는 것을 피하다


    테스트 코드
    let obj = {
         
      a: '111'
    }
    //     ,     proxy
    let rec1 = reactive(obj)
    let rec2 = reactive(obj)
    
    console.warn(rec1 == rec2)//false
    

    수정
    //WeakMap  key      
    
    // objectList       proxy
    const objectList = new WeakMap()//key: target, value: proxy
    
    function reactive(target) {
         
        let proxy = objectList.get(target);
        //    ,   
        if (proxy !== void 0) {
         
            return proxy
        }
    
        proxy = new Proxy(target, handle)
        objectList.set(target, proxy)
        
        return proxy
    }
    ...
    

    수정 후 테스트
    console.warn(rec1 == rec2)//true
    

    proxy 대상이 다시 proxy에 의해


    테스트
    let rec1 = reactive(obj)
    let rec2 = reactive(rec1)
    
    console.log(rec1 == rec2) //false
    

    수정
    // objectList       proxy
    const objectList = new WeakMap()//key: target, value: proxy
    
    // proxyList   proxy     proxy
    const proxyList = new WeakMap()//key: proxy, value: target
    
    function reactive(target) {
         
        let proxy = objectList.get(target);
        //    ,   
        if (proxy !== void 0) {
         
            return proxy
        }
        //   target proxy
        if(proxyList.has(target)){
         
            return target
        }
    
        proxy = new Proxy(target, handle)
        objectList.set(target, proxy)
        proxyList.set(proxy,target)
    
        return proxy
    
    }
    ...
    

    수정 후 테스트
    console.warn(rec1 == rec2)//true
    

    객체 중 객체


    테스트
    let obj = {
         
      a: '111',
      b: [1, 2, 3],
      c: {
         
        d: {
         
          e: '123'
        }
      }
    }
    let rec1 = reactive(obj)
    
    rec1.a = 1;//set
    rec1.b.push(4)//get
    rec1.c.d.e='===='//get
    

    세그먼트와 대상의 변경 사항이 set을 터치하지 않았습니다
    수정
    function get(target, propKey, receiver) {
         
        console.log('get')
        let proxy = Reflect.get(target, propKey, receiver)
        return isObject(proxy) ? reactive(proxy) : proxy;
        //      ,    ,    
    }
    function isObject(val) {
         
        return typeof val === 'object' && val !== null;
    }
    

    수정 후 테스트
    rec1.b.push(4)
    // get  [ get(obj,'b') ]
    // get  [ get(obj.b,'push') ]
    // get  [ get(obj.b,'length') ]
    // set  [ set(obj.b, 3) ]
    // set  [ set(obj.b,'length') ]
    
    rec1.c.d.e='===='
    // get
    // get
    // set
    

    위 문서는 reactive.js에 보관

    모니터


    데이터 변화 모니터링
    데이터를 가져올 때 (get) 수집하려면 track 방법
    데이터를 업데이트할 때 (set) 수집에서 페이지를 찾아서 업데이트해야 합니다. trigger 방법

    감청 함수


    우선 감청 함수를 써서 데이터가 변하면 이 방법을 실행합니다.
    테스트 코드
    let obj = {
         
      a: '111',
    }
    let rec1 = reactive(obj);
    // effect  :      ,       ,         
    //          
    effect(()=>{
         
      console.log('    ',rec1.a);
    })
    
    

    실행 과정:effect->fn->get->track,
    트랙 시 fn 방법 수집
    그러나track과effect는 두 가지 방법입니다.effect의fn은track에서targetMap에 어떻게 저장합니까?
    아이디어:effect이 호출될 때 즉시 한 번 촉발(호출)됩니다.
    터치할 때 fn을 임시 변수에 저장하고 트랙에서 꺼냅니다.
    코드
    // reactive.js
    function get(target, propKey, receiver) {
         
        // console.log('get')
    
        track(target, 'get', propKey)
    
        return Reflect.get(target, propKey, receiver)
    }
    
    // effect.js
    
    let targetMap = new WeakMap()//     key:obj
    
    //get       
    function track(target, type, key) {
         
        console.log('track-    ', type, target, key)
    
        let depsMap = targetMap.get(target);
        //targetMap target  ,   
        if (depsMap === void 0) {
         
            console.log(' depsMap')
            targetMap.set(target, (depsMap = new Map()))
        }
        //depsMap  key    ,   
        let dep = depsMap.get(key)
        if (dep === void 0) {
         
            console.log(' key')
            depsMap.set(key, (dep = new Set()))
        }
        //     
        if (!dep.has(effectFn)) {
         
            console.log(' effect,   effect')
            dep.add(effectFn)
        }
    }
    
    
    //     effect  fn  
    let  effectFn = null;
    
    function effect(fn){
         
        effectFn=fn;
        fn();
    }
    

    코드 실행 프로세스
    effect(()=>{
         
      console.log('    1',rec1.a)
    })
    effect(()=>{
         
      console.log('    2',rec1.a)
    })
    //     : effect->fn->get->track
        track ,   `effect  fn`    `targetMap`    
    
    targetMap:{
              //WeakMap
      key: {
         a:'111'},//  
      value:{
                 //Map
        key:'a',
        value:{
         
          0: ()=>{
          console.log('    1',rec1.a) }
          1: ()=>{
          console.log('    2',rec1.a) }
          //     effect,         。
        }//Set
      }
    }
    

    문제.
    effectFn 임시 변수는 트랙을 할 때 targetMap에 넣으면 effectFn이 쓸모가 없기 때문에 비워야 합니다.
    만약 비우지 않으면 발생하는 문제는 effectget 시 get->track 시 effectFntargetMapb 의 104591410 에 넣는다
    //    
    let obj = {
          
      a: '111',
      b: 'bbb'
    }
    let rec1 = reactive(obj);
    
    effect(() => {
          
      console.log('    1', rec1.a)
    })
    effect(() => {
          
      console.log('    2', rec1.a)
    })
    
    console.log('  get ', rec1.b)
    
    

    그래서
  • 트랙에서 effectFn이 있었고 targetMap을 통해 의존 관계를 맺었다.
  • 즉시 제거 effectFn
  • function track(target, type, key) {
         
        console.log('track-    ', type, target, key)
    
        if (effectFn) {
         
            ...
            //      effect    
            if (!dep.has(effectFn)) {
         
                dep.add(effectFn)
                effectFn = null//      
            }
        }
    }
    
    //  
    //    effectFn = null     
    //            
    function effect(fn) {
         
        try {
         
            effectFn = fn;
            fn();
        } finally {
         
            effectFn = null;
        }
    }
    
    

    ##set 시 종속 방법을 트리거합니다.
    테스트 코드
    effect(()=>{
         
      console.log('    ',rec1.a)
    })
    rec1.a='222';
    

    코드
    // reactive.js
    function set(target, propKey, value, receiver) {
         
        let proxy = Reflect.set(target, propKey, value, receiver)
        trigger(target, 'set', propKey)//trigger    set,         ,           。
        return proxy
    }
    
    // effect.js
    function trigger(target, type, key) {
         
        console.log('trigger-    ', type)
    
        let depsMap = targetMap.get(target)
    
        if (depsMap) {
         
            let deps = depsMap.get(key)
            if (deps) {
         
                //   key   effect    
                deps.forEach(effect => {
         
                    effect()
                })
            }
        }
    }
    

    완벽하다
    그룹이 변할 때 set을 여러 번 터치합니다.
    function set(target, propKey, value, receiver) {
          
        const oldvalue = target[propKey];
    
        let proxy = Reflect.set(target, propKey, value, receiver)
        //                  ,trigger
        if (!target.hasOwnProperty(propKey)) {
          
            trigger(target, 'add', propKey)
        } else if (oldvalue !== value) {
          
            trigger(target, 'set', propKey)
        }
        return proxy
    }
    

    삭제

    // reactive.js
    function deleteProperty(target, propKey) {
         
        console.log('  ')
        let proxy = Reflect.deleteProperty(target, propKey)
        trigger(target, 'delete', propKey)
        return proxy
    }
    //effect.js
    
    function trigger(target, type, key) {
         
        console.log('trigger-    ', type,key)
    
        let depsMap = targetMap.get(target)
    
        if (depsMap) {
         
            let deps = depsMap.get(key)
            if (deps) {
         
                //   key   effect    
                deps.forEach(effect => {
         
                    effect()
                })
                //   
                if(type == 'delete'){
         
                    console.log('delete')
                    depsMap.delete(key)
                }
            }
            
        }
    }
    

    전체 코드

    
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Documenttitle>
    head>
    
    <body>
        <h1>h1>
        <h2>h2>
        <button onclick="change()">Click mebutton>
        <script src="./reactive.js">script>
        <script src="./effect.js">script>
        <script>
    
            //    
            let obj = {
          
                a: '111',
                b: 'bbb',
                c: [1,2,3]
            }
            let rec1 = reactive(obj);
            effect(() => {
          
                document.getElementsByTagName('h2')[0].innerText = rec1.a
            })
            function change(){
          
                console.log('change')
                rec1.a = Math.random()
            }
    
        script>
    body>
    
    html>
    
    // reactive.js
    //WeakMap  key      
    
    // objectList       proxy
    const objectList = new WeakMap()//key: target, value: proxy
    
    // proxyList   proxy     proxy
    const proxyList = new WeakMap()//key: proxy, value: target
    
    function reactive(target) {
         
        let proxy = objectList.get(target);
        //    ,   
        if (proxy !== void 0) {
         
            return proxy
        }
        //   target proxy
        if (proxyList.has(target)) {
         
            return target
        }
        //        ,    
        if (!isObject(target)) {
         
            return target
        }
        proxy = new Proxy(target, handle)
        objectList.set(target, proxy)
        proxyList.set(proxy, target)
    
        return proxy;
    
    }
    const handle = {
          get, set, deleteProperty };
    
    function get(target, propKey, receiver) {
         
        console.log('get')
        let proxy = Reflect.get(target, propKey, receiver);
        track(target, 'get', propKey)
        return isObject(proxy) ? reactive(proxy) : proxy;
        //      ,    ,    
    }
    function set(target, propKey, value, receiver) {
         
        const oldvalue = target[propKey];
    
        let proxy = Reflect.set(target, propKey, value, receiver)
        //                  ,trigger
        if (!target.hasOwnProperty(propKey)) {
         
            trigger(target, 'add', propKey)
        } else if (oldvalue !== value) {
         
            trigger(target, 'set', propKey)
        }
        return proxy
    }
    function deleteProperty(target, propKey) {
         
        console.log('  ')
        let proxy = Reflect.deleteProperty(target, propKey)
        trigger(target, 'delete', propKey)
        return proxy
    }
    
    //  
    function isObject(val) {
         
        return typeof val === 'object' && val !== null;
    }
    
    
    //effect.js
    
    //    
    let targetMap = new WeakMap()//     key:obj
    
    
    //get       
    function track(target, type, key) {
         
        console.log('track-    ', type, target, key)
    
        if (effectFn) {
         
            let depsMap = targetMap.get(target);
            //targetMap target  ,   
            if (depsMap === void 0) {
         
                console.log(' depsMap')
                targetMap.set(target, (depsMap = new Map()))
            }
            //depsMap  key    ,   
            let dep = depsMap.get(key)
            if (dep === void 0) {
         
                console.log(' key')
                depsMap.set(key, (dep = new Set()))
            }
            //      effect    
            if (!dep.has(effectFn)) {
         
                console.log(' effect,   effect')
                dep.add(effectFn)
            }
        }
    }
    
    //set    
    function trigger(target, type, key) {
         
        console.log('trigger-    ', type,key)
    
        let depsMap = targetMap.get(target)
    
        if (depsMap) {
         
            let deps = depsMap.get(key)
            if (deps) {
         
                //   key   effect    
                deps.forEach(effect => {
         
                    effect()
                })
                //   
                if(type == 'delete'){
         
                    console.log('delete')
                    depsMap.delete(key)
                }
            }
            
        }
    }
    //     effect  fn  
    let effectFn = null;
    
    function effect(fn) {
         
        try {
         
            console.log('try')
            effectFn = fn;
            fn();
        } finally {
         
            console.log('finally')
            effectFn = null;
        }
    }
    
    

    vue3 원본 학습 - 컴퓨터

    좋은 웹페이지 즐겨찾기