Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

理解vue响应式原理(1)--reactive #13

Open
conan1992 opened this issue Jun 12, 2020 · 0 comments
Open

理解vue响应式原理(1)--reactive #13

conan1992 opened this issue Jun 12, 2020 · 0 comments

Comments

@conan1992
Copy link
Owner

conan1992 commented Jun 12, 2020

reactive

在js引擎执行渲染函数的途中,突然读到了data.msg,data已经被定义成了响应式数据,读取data.msg时所触发的get函数已经被我们劫持,这个get函数中我们去记录下data.msg被这个渲染函数所依赖,然后再返回data.msg的值。

处理对象,将对象定义为响应式;修改data每个属性的get函数,进行拦截;

import Dep from "./dep"
import { isObject } from "./utils"

export default function reactive( data ){
    if(isObject(data)){
        Object.keys(data).forEach(key => {
            defineReactive(data, key);
        })
    }
    return data;
};
function defineReactive(data, key){
    let val = data[key];
    var dep = new Dep();
    Object.defineProperty(data, key,{
        get(){
            dep.depend();
            return val;
        },
        set( newVal ){
            val = newVal;
            console.log(dep)
            dep.notify();//触发更新
        }
    });
    //如果值也是引用类型对象,也要处理为响应式
    if(isObject(val)){
        reactive( val )
    }
}

Dep

那么,get函数里的dep又是什么呢?

可以把dep看成一个收集依赖的小筐,每当运行渲染函数读取到data的某个key的时候,就把这个渲染函数丢到这个key自己的小筐中,在这个key的值发生改变的时候,去key的筐中找到所有的渲染函数再执行一遍。

就是说,我们在遍历data属性修改get函数的时候,我们把有涉及到当前属性的渲染函数的watcher添加到dep这个“小筐”中;

export default class Dep {
    constructor(){
        this.deps = new Set();
    }
    depend(){
        if(Dep.target){
            this.deps.add(Dep.target)
        }
    }
    notify(){
        this.deps.forEach(watcher => {
            watcher.update();
        })
    }
}
Dep.target = null

Dep.target这个概念也是Vue中所引入的,它是一个挂在Dep类上的全局变量

watcher

Dep.target 存储了渲染函数的watcher,接下来就是watcher的实现,Watcher在实例化的时候,将当前watcher存储到Dep.target;

new Watcher(() => {
  document.getElementById('app').innerHTML = `msg is ${data.msg}`
})

Dep.target存储

const targetStack = [];

// 将上一个watcher推到栈里,更新Dep.target为传入的_target变量。
export function pushTarget(_target){
    if(Dep.target) targetStack.push(Dep.target);
    Dep.target = _target
}
// 取回上一个watcher作为Dep.target,并且栈里要弹出上一个watcher。
export function popTarget(){
    Dep.target = targetStack.pop();
}

Watcher实现:

import Dep, {pushTarget, popTarget } from "./dep"

export default class Watcher {
    constructor( getter ){
        this.getter = getter;
        this.get();
    }
    get(){
        pushTarget(this);
        this.value = this.getter();
        popTarget();
        return this.value
    }
    update(){
        this.get();
    }
}

所以,当data属性值在发送改变是,set函数中会执行dep.notify();遍历deps中的每个watcher,执行update;
就这样,响应式就实现了。

参考

@conan1992 conan1992 changed the title 理解vue响应式原理 理解vue响应式原理(1)--reactive Jun 13, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant