vue.js的双向绑定,我们用原生js来实现
Posted 夕水
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue.js的双向绑定,我们用原生js来实现相关的知识,希望对你有一定的参考价值。
前面讲到原型的时候,说过vue.js框架处理数据的核心原理,实际上就是通过Object.defineProperty()方法将访问器函数getter和setter函数重写.接下来,我也来根据这个原理,使用原生js实现vue.js的数据双向绑定.
使用vue.js写过数据的双向绑定就应该知道页面的结构应该是怎么样的,不过vue.js在绑定数据的时候用的是模板引擎语法,这里暂时不写这个,就用指令代替即可.如下:
html:
<div id="#app-1">
<input type="text" v-model="testData">
<p v-bind="testData"></p>
</div>
首先,需要定义一个构造函数.代码如下:
//参数为构造函数需要配置的属性,因为vue.js就是通过构造函数创建一个vue实例的.
function eveningWater(option){
//初始化该构造函数的基础属性也就是实例传入的对象
this.init(option);
}
//将初始化函数添加到构造函数的原型上
eveningWater.prototype.init = function(option){
this.option = option;
//重写构造实例属性
this.$el = document.querySelector(option.el);
this.$data = option.data;
//定义一个对象,用于保存自定义指令属性
this.binding = {};
//观察对象的变化
this.observe(this.$data);
//观察dom元素,响应页面视图变化
this.readElement(this.$el);
}
eveningwater.prototype.observe = function(dataobj){
//定义一个变量保存数据的属性名
var value;//如构造函数实例的data对象中为test,则为test,这里是testData
//遍历data对象
for(var key in dataobj){
//判断如果属性名存在于对象实例中
if(dataobj.hasOwnProperty(key)){
//自定义指令用一个数组来保存
this.binding[key] = {
directivesArr:[]
};
value = dataobj[key];
//判断如果value里面数据还是对象则通过递归原理再往里面遍历
if(typeof value === 'object' && value !== null){
this.observe(value);
};
//新定义变量赋值为自定义指令保存对象,以方便之后访问
var _binding = this.binding[key];
//利用Object.defineProperty()重写访问器函数
Object.defineProperty(dataobj,key,{
enumberable:true,//属性特性,表示允许循环遍历
configurable:true,//属性特性,表示可以通过delete删除属性
get:function(){
return value;
},//get访问器函数,读取数据
set:function(newVal){
如果数据不一致,则调用数据更新函数
if(value !== newVal){
value = newVal;
_binding.directivesArr.forEach(function(dir){
dir.update();
})
}
}
})
}
}
}
//读取dom元素
eveningWater.prototype.readElement = function(root){
//this对象指向构造函数初始化的属性,即前面的this.option对象
var _self = this;
//获取根元素的子元素,刚好是一个数组
var nodes = root.children;//[input,p]
for(var i = 0,len = nodes.length;i < len;i++){
var node = nodes[i];
//判断如果根元素的子元素还有嵌套的子元素,则通过递归原理继续遍历
if(node.children.length){
this.readElement(node);
}
//开始判断含有指令的元素
if(node.hasAttriBute('v-mode') && node.tagName.toLocaleCase() === 'input'){
//取得指令属性
var attrModel = node.getAttribute('v-mode');
//输入值事件监听,注意这里要用函数表达式形成一个块级作用域,因为要修改前面的data对象里的值
node.addEventListener('input',(function(key){
//这里是一个难点,由于我们把数据值都添加到一个监听函数中去 ,传入的参数有标签名,标签dom元素,构造实例函数的属性对象,获取的指令属性,与input标签值获取的value
_self.binding.directivesArr.push(new WatchListener(node.tagName.toLocaleCase(),node,_self,attrmodel,'value'));
//当值改变时,就改变构造函数实例中的data对象中的对象属性值,即testData的值,并且这里要返回一个函数,这是一个回调函数
return function(){
_self.$data[attrmodel] = nodes[key].value;
}
})(i))
}
//开始判断包含有v-bind指令的标签元素
if(node.hasAttribute('v-bind')){
var attrBind = node.getAttribute('v-bind');
_self.binding.directivesArr.push(new WatchListener(node.tagName.toLocaleCase(),node,_self,attrBind,'textContent'));
}
}
}
//监听函数,参数标签名,dom元素,构造函数实例对象,指令属性,以及对应的值改变属性
function WatchListener(name,el,vm,data,attr){
this.name = name;
this.el = el;
this.vm = vm;
this.data = data;
this.attr = attr;
this.update();
}
//数据更新时函数
WatchListener.prototype.update = function(){
//注意this对象指向watchListener构造函数
this.el[this.attr] = this.vm.$data[this.data];
}
//页面加载时运行
window.onload = function(){
//初始化构造函数
new eveningWater({
el:'#app-1',
data:{
testData:'测试数据'
}
})
}
//至此,一个简单的数据双向绑定示例就完成了,完整效果如以下动图显示:
以上是关于vue.js的双向绑定,我们用原生js来实现的主要内容,如果未能解决你的问题,请参考以下文章