最全Vue双向绑定及其讲解 一步一步debug包教包会

在这里插入图片描述

实现mvvm的双向绑定,是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()【defineProperty】来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。就必须要实现以下几点:

1.监听器Observer:监听变化
2.解析器Compile: 根据指令模板替换数据,以及绑定相应的更新函数
3.Watcher: 订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数
4.mvvm入口函数,整合以上三者

简易版实现:

observer.js
function defineReactive(data, key, val) {
    observe(val);//遍历选出可枚举的数据值
    var dep = new Dep();
    Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get: function () {
            if (Dep.target) {
                //dep添加订阅
                dep.addSub(Dep.target);
            }
            return val;
        },
        set: function (newVal) {
            if (val === newVal) {
                return;
            }
            val = newVal;
            console.log('属性' + key + '已经被监听了,现在值为:“' + newVal.toString() + '”');
            //数据变化则通知订阅者
            dep.notify();
        }
    })
};
Dep.target = null;
function observe(data) {
    if (!data || typeof data !== 'object') {
        return;
    }
    Object.keys(data).forEach(function (key) {
        //key方法选出可枚举对象
        defineReactive(data, key, data[key]);
    })
};

function Dep() {
    this.subs = [];
}
Dep.prototype = {
    addSub: function (sub) {
        this.subs.push(sub);
    },
    notify: function () {
        this.subs.forEach(function (sub) {
            sub.update();
        });
    }
};
// var library = {
//     book1: {
//         name: ''
//     },
//     book2: ''
// };
// observe(library);
// library.book1.name = '我是第一本书';//属性name已经被监听了,现在值为:“我是第一本书”
// library.book2 = '我是第二本书';  //属性book2已经被监听了,现在值为:“我是第二本书”
watcher
function Watcher(vm, exp, cb) {
    this.cb = cb;
    this.exp = exp;
    this.vm = vm;
    this.value = this.get();
}

Watcher.prototype = {
    update() {
        this.run();
    },
    run: function () {
        var value = this.vm.data[this.exp];
        var oldVal = this.value;
        if (value !== oldVal) {
            this.value = value;
            this.cb.call(this.vm, value, oldVal);
        }
    },
    get: function () {
        Dep.target = this;
        var value = this.vm.data[this.exp]
        Dep.target = null;
        return value
    }
}
index.js
function SelfVue(data, el, exp) {
    this.data = data;
    observe(data);
    el.innerHTML = this.data[exp];
    new Watcher(this, exp, function (value) {
        el.innerHTML = value;
    });
    return this
}
index.html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vueText</title>
</head>
<script src="js/observer.js"></script>
<script src="js/watcher.js"></script>
<script src="js/index.js"></script>

<body>
    <h1 id="name">{{name}}</h1>
</body>
<script>
    var ele = document.querySelector('#name');
    var selfVue = new SelfVue({
        name: 'hello world'
    }, ele, 'name');
    window.setTimeout(function () {
        console.log('name值改变了');
        selfVue.data.name = 'canfoo';
    }, 2000);
</script>

</html>

便于理解执行顺序如下:

首先先走一遍变量:Dep.target = null;以及watcher dep等对象的原型继承
然后开始执行 index.html中获取节点==》创建selfVue
在这里插入图片描述
变量值如下
进入observe函数
在这里插入图片描述
递归找出可枚举对象
创建dep 进入defineproperty函数
在这里插入图片描述
获取初始属性 赋值给html
在这里插入图片描述
这里会触发get 并且此时页面会发生改变 “name”==》helloworld
这里的涉及很巧妙因为初始target值为空 故会直接返回val(helloworld)只有当watcher初始化的时候才会压栈
之后按顺序实例化watcher
在这里插入图片描述
在这里插入图片描述
之后进入watcher中的get
在这里插入图片描述

get这个时候target被赋值 不再是空了并且因为get方法需要获取所以进入defineproperty中
在这里插入图片描述
把被感染的watcher放入dep中之后再将target其置为空
(平时都是空 只有当watcher初始化时才会变成watcher进而可以对dep进行压栈操作)
都构建完成了 开始更改值 设置一个定时器 给name一个新的值
在这里插入图片描述
此时会进入definedeprotype中的set方法在这里插入图片描述
把新值给val 并执行notify函数
在这里插入图片描述
此时需要获取data值 再次进入de的get函数中
此时为空会直接返回val (上一步的set已经把他设置成辛德勒)
然后进行比对 之前获取的value是oldvaluehello(get获取pro的get)
然后给一个新的value
然后执行之前存在cb中的index.js的函数 将页面更改
至此一个简易的双向绑定已经完成
简易版总结:::getpro劫持数据 watcher劫持数据(有一个藏watcher监听的过程dep) 当发生改变时通过回调函数修改数据

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值