vue中数据响应式原理
vue作为一个MVVM的js前端框架,它有一个核心就是双向数据绑定作为一个MVVM的框架,可以拆分为三步来看它内部的实现原理,就是M→V,V→M,M→V首先来看第一步,<div id="app"><input type="text" v-model="text">{{text}}</div>var vm=new Vue({
vue作为一个MVVM的js前端框架,它有一个核心就是双向数据绑定
作为一个MVVM的框架,可以拆分为三步来看它内部的实现原理,就是M→V,V→M,M→V
首先来看第一步,
<div id="app">
<input type="text" v-model="text">
{{text}}
</div>
var vm=new Vue({
el:'app',
data:{
text:'hello world!'
}
})
要先保证input框中的value值绑定到实例中的data中相对应的值
function node2Fragment(node, vm) {
//这里是dom劫持,vue会新建一个文档片段来替换dom中本来的结点
var flag = document.createDocumentFragment();
//子节点
var child;
while(child = node.firstChild) {
//开始编译每个结点
compile(child, vm);
//appendchild方法会自动删除node对象的child结点
flag.appendChild(child)
}
return flag;
}
function compile(node, vm) {
var reg = /\{\{(.*)\}\}/;
//节点类型为元素,这里第一个节点是input,所以进入第一个判断条件
if(node.nodeType === 1) {
var attr = node.attributes;
for(var i = 0; i < attr.length; i++) {
//匹配v-model这个属性名称
if(attr[i].nodeName == 'v-model') {
var name = attr[i].nodeValue;
//将data的值赋给gainode
node.value = vm.data[name];
}
}
};
//节点类型为text,这里为{{}},使用正则进入替换,替换为相对应的value
if(node.nodeType === 3) {
if(reg.test(node.nodeValue)) {
var name = RegExp.$1; //指的是符合正则匹配的第一个
name = name.trim();
//将data的值赋给该node
node.nodeValue = vm.data[name];
}
}
}
先对节点进行劫持,检测到"v-model"属性所绑定的实例对象的data中所绑定的值,还有{{}}中绑定的值,对绑定的数据进行初始化,达到的效果为
第二步:
这一步是要达到用户改变input的值得时候,到同时改变绑定的Vue实例对象的data中的值
function defineReactive(obj, key, val) {
//定义一个主题
var dep = new Dep();
Object.defineProperty(obj, key, {
get: function() {
//添加订阅者watcher到主题对象Dep
if(Dep.target) dep.addSub(Dep.target)
return val
},
set: function(newVal) { //在set函数中检测到数据的值的变化
if(newVal === val) return;
val = newVal;
//看到数据改变
console.log("设置新的属性为" + val)
}
})
}
function observe(obj, vm) {
Object.keys(obj).forEach(function(key) {
defineReactive(vm, key, obj[key])
})
}
在Vue的官网文档中有介绍数据的响应的中,有提到,在Vue实例进行初始化的时候,将一个object赋值给Vue实例的data的时候,Vue内部会对data中的每个值遍历执行转换为getter/setter(使用Object.defineProperty()),在每个值的set函数中可以执行数据发生变化的操作,因为这个转换时Vue实例初始化的时候就已经完成的,所以一切绑定需要事先在实例的data中定义好,之后不能再次添加,但是Vue提供了this.$set(),属性可以对本来初始化好的数据的对象上面添加新的属性和值
第三步:
这一步最后因为Vue实例中的data对应的值修改了,要反应到{{}}绑定的文本中,需要使用观察者模式(n对个观察者同时观察一个主题对象,当这个主题对象发生改变的时候,通知到每个观察者,做出相应的操作)
function Watcher(vm, node, name) {
//Dep.target是一个Dep的静态属性,表示当前观察者。
Dep.target = this;
this.name = name;
this.node = node;
this.vm = vm;
//订阅者执行一次更新视图
this.update();
Dep.target = null;
}
Watcher.prototype = {
update: function() {
//触发对应data属性值的get函数
this.get();
this.node.nodeValue = this.value;
},
get: function() {
this.value = this.vm[this.name]
}
}
function Dep() {
//主题的订阅者们
this.subs = [];
}
Dep.prototype = {
//添加订阅者的方法
addSub: function(sub) {
this.subs.push(sub);
},
//发布信息的方法(让订阅者们全部更新view)
notify: function() {
this.subs.forEach(function(sub) {
sub.update();
})
}
}
最后,使用
//Vue对象
function Vue(options) {
this.data = options.data;
var id = options.el;
var data = this.data;
//将data的属性全部通过访问器属性赋给vm对象,使读写vm实例的属性转成读写了vm.data的属性值,达到鱼目混珠的效果
observe(data, this);
var dom = node2Fragment(document.getElementById(id), this);
//编译完成后,将dom片段添加到el挂载的元素上(app)
document.getElementById(id).appendChild(dom)
}
//调用Vue
var vm = new Vue({
el: 'app',
data: {
texts: 'hello1',
}
})
就可以基本达到一个Vue使用数据双向绑定的一个目的
希望可以帮助你理解Vue中的数据相应
本文参考coderzzp,GitHub地址https://github.com/coderzzp/vue-come-true/tree/master/vue-come-true-First#%E6%9C%AC%E6%96%87%E7%9A%84%E5%AE%9E%E7%8E%B0%E7%9B%AE%E6%A0%87
更多推荐
所有评论(0)