vue2.0数据双向绑定,vue中数据双向绑定
本文主要介绍vue数据双向绑定的实现方法,帮助大家更好的理解和学习使用vue框架。感兴趣的朋友可以了解一下。
:
目录
1.介绍
2.代码实现
2.1目的分析
2.2实施过程
进入密码
页面初始化
2.2.3视图影响数据
2.2.4数据影响视图
3.未来计划
1. 前言
本文适合学习Vue源代码的初级学者。看完之后,你会对Vue的数据双向绑定原理有一个大致的了解,知道Observer、Compile、Wathcer三个角色(如下图所示)及其作用。
本文将带您一步步实现数据双向绑定的简单版本。每一步都会分析这一步要解决的问题,以及为什么要这样写代码。所以,看完这篇文章,希望你能自己实现一个简单版的数据双向绑定。
2. 代码实现
2.1 目的分析
本文要达到的效果如下图所示:
本文使用的HTML和JS的主要代码如下:
div id=应用程序
h1 v-text=msg/h1
输入类型=text v-model=msg
差异
h1 v-text=msg2/h1
输入类型=text v-model=msg2
/div
/div
让vm=new Vue({
埃尔: #app ,
数据:{
消息:“你好,世界”,
msg2:“你好,肖飞”
}
})
我们将遵循以下三个步骤来实现它:
第一步:将data中的数据同步到页面,实现M==V的初始化;
第二步:在输入框中输入一个值时,将新值同步到数据中,实现V==M的绑定;
第三步:数据更新时,触发页面变化,实现M==V的绑定。
2.2 实现过程
2.2.1 入口代码
首先,我们需要创建一个Vue类,它接收一个options对象,同时,我们需要将有效信息保存在options对象中。
然后,我们有三个主要模块:观察器,编译器和观察器。其中Observer用于数据劫持,Compile用于解析元素,Wathcer是一个观察者。可以写下面的代码:(Observer、Compile、Wathcer这三个概念不用详细研究,后面会详细讲解)。
Vue类{
//接收传入的对象
构造函数(选项){
//保存有效信息
这个。$ El=document . query selector(options . El);
这个。$ data=options.data
//容器:{属性1: [wathcer1,wathcer2.],属性2: [.]},用于保存每个属性观察者。
这个。$ watcher={ };
//解析元素:实现编译
this.compile(这个。$ El);//要解析元素,必须将元素传入
//劫持数据:实现观察者
观察(这个。$ data);//要劫持数据,必须传入数据
}
compile() {}
观察(){}
}
2.2.2 页面初始化
在这一步中,我们要初始化页面,也就是解析出v-text和v-model指令,并将数据中的数据渲染到页面上。
这一步的关键是实现compile方法,那么如何解析el元素呢?想法如下:
首先,我们需要获取el下的所有子节点,然后遍历这些子节点。如果有任何子节点,那么我们需要使用递归的思想。
遍历子节点,找到所有带有指令的元素,并将相应的数据呈现到页面上。
代码如下:(主要看编译部分)
Vue类{
//接收传入的对象
构造函数(选项){
//获取有用的信息
这个。$ El=document . query selector(options . El);
这个。$ data=options.data
//容器:{属性1: [wathcer1,wathcer2.],属性2: [.]}
这个。$ watcher={ };
//2.解析元素:实现编译
this.compile(这个。$ El);//要解析元素,必须将元素传入
//3.劫持数据:实现观察者
观察(这个。$ data);//要劫持数据,必须传入数据
}
编译(el) {
//解析元素下的每个子节点,所以得到el.children
//备注:children返回元素的集合,childNodes返回节点的集合。
let nodes=el.children
//解析每个子节点的指令
for (var i=0,length=nodes.lengthI长度;i ) {
设node=nodes[I];
//如果当前节点仍有子元素,递归解析该节点
if(node.children){
this.compile(节点);
}
//用v-text指令解析元素
if(node . has attribute( v-text ){
let attr val=node . get attribute( v-text );
node.textContent=this。$ data[attr val];//呈现页面
}
//用v-model指令解析元素
if(node . has attribute( v-model ){
let attr val=node . get attribute( v-model );
node.value=this$ data[attr val];
}
}
}
观察(数据){}
}
这样,我们就可以初始化页面了。
2.2.3 视图影响数据
因为input有v-model指令,所以我们需要实现这样一个功能:在输入框中输入字符,数据中绑定的数据会随之变化。
我们可以将一个input事件绑定到input元素,事件的作用是将data中对应的数据修改为input中的值。
这部分的实现代码比较简单。只要看看标着的地方,你就明白了。代码如下:
Vue类{
构造函数(选项){
这个。$ El=document . query selector(options . El);
这个。$ data=options.data
这个。$ watcher={ };
this.compile(这个。$ El);
观察(这个。$ data);
}
编译(el) {
let nodes=el.children
for (var i=0,length=nodes.lengthI长度;i ) {
设node=nodes[I];
if(node.children){
this.compile(节点);
}
if(node . has attribute( v-text ){
let attr val=node . get attribute( v-text );
node.textContent=this。$ data[attr val];
}
if(node . has attribute( v-model ){
let attr val=node . get attribute( v-model );
node.value=this$ data[attr val];
//看这里!只有三行代码!
node.addEventListener(input ,(ev)={
这个。$ data[attr val]=ev . target . value;
//可以尝试在这里执行:console.log(这个。$data),
//可以看到,每次在输入框中输入文本时,数据中的msg值也会发生变化。
})
}
}
}
观察(数据){}
}
2.2.4 数据影响视图
到目前为止,我们已经实现了:当我们在输入框中输入字符时,数据中的数据会自动更新;
该部分的主要任务是:当数据中的数据更新时,与数据绑定的元素会自动更新页面上的视图。具体思路如下:
1)我们要实现一个Wathcer类,它有一个更新页面的update方法。观察器的代码如下:
类监视器{
构造函数(节点,更新数据,虚拟机,表达式){
//保存传入的值,这些值是呈现页面时要使用的数据。
this.node=node
this.updatedAttr=updatedAttr
this.vm=vm
this.expression=expression
this . update();
}
update(){
this . node[this . updatedattr]=this . VM . $ data[this . expression];
}
}
2)试想一下,我们应该给哪些数据添加观察者?何时向数据中添加观察点?
解析一个元素的时候,解析v-text和v-model指令的时候,意味着这个元素需要双向绑定数据,所以我们在这个时候给容器添加了观察者。我们需要这样的数据结构:{属性1: [wathcer1,wathcer2.],属性2: [.]}.如果不是很清楚,可以看下图:
如您所见,vue实例中有一个$wathcer对象。$wathcer的每个属性对应每个要绑定的数据,值是一个数组,用来存储观察过数据的观察者。(注:Dep是在Vue源代码中专门创建的一个类,对应这里所说的数组。本文是简单版,就不多介绍了。)
3) 劫持数据:利用对象的访问器属性吸气剂和作曲者做到当数据更新的时候,触发一个动作,这个动作的主要目的就是让所有观察了该数据的观察者执行更新方法。
总结一下,在本小节我们需要做的工作:
实现一个沃特斯彻类;
在解析指令的时候(即在编制方法中)添加观察者;
实现数据劫持(实现观察方法)。
完整代码如下:
某视频剪辑软件类{
//接收传进来的对象
构造函数(选项){
//获取有用信息
这个. El=文档。查询选择器(选项。El);
这个. data=options.data
//容器: {属性1: [wathcer1,wathcer2.], 属性2: [.]}
这个. watcher={ };
//解析元素:实现编制
this.compile(这个$ El);//要解析元素,就得把元素传进去
//劫持数据:实现观察者
观察(这个$数据);//要劫持数据,就得把数据传入
}
编译(el) {
//解析元素下的每一个子节点,所以要获取埃尔。儿童
//拓展:儿童返回元素集合,子节点返回节点集合
let nodes=el.children
//解析每个子节点的指令
for (var i=0,长度=nodes.lengthI长度;i ) {
设node=nodes[I];
//如果当前节点还有子元素,递归解析该节点
if (node.children) {
this.compile(节点);
}
如果(节点。具有属性( v-text ){
设属性值=节点。get属性(“v-text”);
//node.textContent=this .$ data[属性值];
//观察器在实例化时调用更新,替代了这行代码
/**
* 试想沃特斯彻要更新节点数据的时候要用到哪些数据?
*例如p.innerHTML=vm .$data[msg]
* 所以要传入的参数依次是:当前节点节点,需要更新的节点属性,vue实例,绑定的数据属性
*/
//往容器中添加观察者:{邮件1:[观察者,观察者.],msg2: [.]}
如果(!这个. watcher[attrVal]) {
这个. watcher $ watcher[attr val]=[];
}
这个观察者[attrVal].推送(新观察器(node, innerHTML ,this,attrVal))
}
如果(节点。具有属性( v-model ){
设属性值=节点。get属性(“v-model”);
node . value=this $ data[attr val];
node.addEventListener(input ,(ev)={
这个$ data[attr val]=ev。目标。价值;
})
如果(!这个. watcher[attrVal]) {
这个. watcher $ watcher[attr val]=[];
}
//不同于上处用的innerHTML,这里投入用的是沃勒属性
这个观察者[attrVal].推送(新观察器(节点,值,这个,attrVal))
}
}
}
观察(数据){
对象.键(数据)。forEach((key)={
设val=data[key];//这个英国压力单位将一直保存在内存中,每次访问数据[键],都是在访问这个英国压力单位
Object.defineProperty(数据,键,{
get() {
返回val//这里不能直接返回数据[键],不然会陷入无限死循环
},
set(newVal) {
如果(瓦尔!==newVal) {
val=newVal//同理,这里不能直接对数据[关键字]进行设置,会陷入死循环
这个watcher[key].forEach((w)={
w。update();
})
}
}
})
})
}
}
类监视器{
构造函数(节点,更新数据,虚拟机,表达式){
//将传进来的值保存起来
this.node=节点
this.updatedAttr=updatedAttr
this.vm=vm
this.expression=表达式
这个。update();
}
update() {
这个。节点【这个。updatedattr]=this。VM。$ data【这个。表情];
}
}
让vm=新Vue({
埃尔: #app ,
数据:{
消息:"你好,世界",
msg2:"你好,肖飞"
}
})
至此,代码就完成了。
3. 未来的计划
结合设计模式的知识,分析上述源代码存在的问题,并与Vue源代码进行对比,可以看作是对Vue源代码的分析。
以上是vue数据双向绑定实现方法的详细内容。更多关于vue数据双向绑定的信息,请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。