面试官问我vue双向数据绑定,我能跟他聊一个小时【建议收藏】

导语:
在面试中,我们经常能够被问到,vue的双向数据绑定是怎么实现的?又或者你对vue的源码深入分析,想要了解它的双向数据绑定时怎么实现的,这一篇教会你。

一,实现原理

vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现。
在这里插入图片描述就是以上的这个视图,原理就是当视图层数据发生变化的时候,就会更新data,如果data改变了,就会更新view。

详细过程是这样:

1,当data 有变化的时候它通过Object.defineProperty()方法中的set方法进行监控和更新,然后调用定义好data 和view的关系的回调函数,来通知view进行数据的改变,从而实现view视图的改变。

2,如果view 发生改变,则是通过底层的input 事件来进行data的响应更改。

二,实现过程

1,视图层改变view比较容易实现,只要通过底层的input事件就可以对data的响应更改。

2,主要难的,当data改变的时候,怎么检测到,还有怎么去更改视图层里面的数据呢?

我们先来看一个图。
在这里插入图片描述data更新view的难点是如何知道数据变了,只要知道数据变了,那么接下去的事都好处理。只要通过Object.defineProperty( )对属性设置一个set函数,当数据改变了就会来触发这个函数,就可以实现data更新view了。

现在我们已经知道要实现vue的双向绑定,首先要对data进行劫持监听,所以我们需要设置一个监听器Observer,用来监听所有data。如果data发生变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep(容器)来专门收集这些订阅者,然后在监听器Observer和订阅者Watcher之间进行统一管理的。因此接下去我们执行以下2个步骤,实现数据的双向绑定:

1.实现一个监听器Observer,用来劫持并监听所有data,如果有变动的,就通知订阅者。

2.实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新view。

流程图如下:
在这里插入图片描述

1,实现一个observer

function mvvm(data, key, val) {
    
    
    observe(val); 
    Object.defineProperty(data, key, {
    
    
        get: function() {
    
    
            return val;
        },
        set: function(newVal) {
    
    
            val = newVal;
        }
    });
}
 
function observe(data) {
    
    
	// 如果data不是对象,或者为空就不用观察
    if (!data || typeof data !== 'object') {
    
    
        return;
    }
    // 对每一个data对象都设置观察,可以执行get和set函数
    Object.keys(data).forEach(function(key) {
    
    
        mvvm(data, key, data[key]);
    });
};

2,实现一个dep消息订阅器和订阅者

function mvvm(data, key, val) {
    
    
    observe(val); 
    var dep = new Dep(); 
    Object.defineProperty(data, key, 
        get: function() {
    
    
            if (Dep.self) {
    
    .  // 判断是否需要添加订阅者
                dep.additem(Dep.self); // 在这里添加一个订阅者
            }
            return val;
        },
        set: function(newVal) {
    
    
            if (val === newVal) {
    
    
                return;
            }
            val = newVal;
            dep.notify(); // 如果数据变化,通知所有订阅者
        }
    });
}
 
function Dep () {
    
    
	// 这里的消息订阅是可以是一个数组
    this.items = [];
}
Dep.prototype = {
    
    
    addItem: function(item) {
    
    
        this.items.push(item);
    },
    notify: function() {
    
    
        this.items.forEach(function(item) {
    
    
            item.update();
        });
    }
};
// 订阅者
function Watcher(_this, key, fun) {
    
    
    this.fun = fun;			//订阅者要触发的回调函数
    this._this =_this;		//订阅者订阅的对象
    this.key = key;			//订阅者订阅的key
    this.value = this.get();  // 将自己添加到订阅器
}
 
Watcher.prototype = {
    
    
    update: function() {
    
    
        this.run();
    },
    run: function() {
    
    
        var value = this._this.data[this.key];
        var oldVal = this.value;
        if (value !== oldVal) {
    
    
            this.value = value;
            this.fun.call(this._this, value, oldVal);
        }
    },
    get: function() {
    
    
        Dep.self = this;  
        var value = this._this.data[this.key]  
        Dep.self = null;  
        return value;
    }
};

3,将Observer和Watcher联系起来

function myVue (data, el, exp) {
    
    
    this.data = data;
    observe(data);
    el.innerHTML = this.data[exp];
    new Watcher(this, exp, function (value) {
    
    
        el.innerHTML = value;
    });
    return this;
}

4,跟视图层联系

<body>
    <h1 id="text">{
   
   {text}}</h1>
</body>
<script type="text/javascript">
    var ele = document.querySelector('#text');
    var myVue = new myVue({
     
     
        name: 'hello world'
    }, ele, 'text');
 
</script>

补充
在这里插入图片描述
微信搜索【web小馆】,回复全栈博客项目,即可获取项目源码和后续的实战文章教程。每天用最简单朴实的语言,潜移默化的提升你的计算机基础知识和前端技术。小米粥,一个专注的web全栈工程师,我们下期再见!

在这里插入图片描述
node后台

猜你喜欢

转载自blog.csdn.net/gitchatxiaomi/article/details/108808083