vue双向数据绑定浣熊sky

1添加订阅,2触发订阅(循环数组中的每一项)

构造函数中挂载了一个更新数据方法回调函数cb,(在实例构造函数时需要传递进来),最新的数据对象vm,需要更新的属性key

并定义了一个更新函数,调用上面的回调函数cb

在编译器中当数据变化进行更新的时候,需要创建一个watchers实例

在watcher类的构造函数中添加如下三行代码:1.订阅器的target属性等于当前的watcher

2.对当前的值key通过第二行代码进行获取(此时会调用get函数)

3.另dep.target=null

4.在get函数中判断dep.target如果存在,则调用添加订阅者函数

获取vue实例中el的位置,将data中的数据填充进el中,然后将结果渲染出来

vue双向数据绑定原理

根据最新的数据渲染自身的结构

complie编译器:1.将dom中的节点放入,文档碎片中

2.2.对文本子节点进行正则匹配与提取,替换

3.将文档碎片重新移入dom节点,进行渲染

频繁对dom节点进行操作会不停的触发重绘和重排,所以需要使用文档碎片(将节点元素存入内存中,这样页面上就没有dom元素了,随意修改它也不会触发重绘和重排,

对文档碎片进行编译,再将结果重新渲染到页面上),

正则表达式中:空白字符用/s*,*表示0个或多个

目前几种主流的mvc(vm)框架都实现了单向数据绑定,而我所理解的双向数据绑定无非就是在单向绑定的基础上给可输入元素(input、textare等)添加了change(input)事件,来动态修改model和view,并没有多高深。所以无需太过介怀是实现的单向或双向绑定。

实现数据绑定的做法有大致如下几种:

脏值检查(angular.js)

数据劫持(vue.js)

这种方式现在毕竟太low了,我们更希望通过vm.property=value这种方式更新数据,同时自动更新视图,于是有了下面两种方式

脏值检查:angular.js是通过脏值检测的方式比对数据是否有变更,来决定是否更新视图,最简单的方式就是通过setInterval()定时轮询检测数据变动,当然Google不会这么low,angular只有在指定的事件触发时进入脏值检测,大致如下:

数据劫持:vue.js则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。

vardata={name:'kindeng'};observe(data);data.name='dmq';//哈哈哈,监听到值变化了kindeng-->dmqfunctionobserve(data){if(!data||typeofdata!=='object'){return;}//取出所有属性遍历,object.keys(data)得到对象的属性名数组,通过forEach()遍历属性名,属性名为参数key,data还是原来的对象Object.keys(data).forEach(function(key){defineReactive(data,key,data[key]);});};functiondefineReactive(data,key,val){observe(val);//监听子属性Object.defineProperty(data,key,{enumerable:true,//可枚举configurable:false,//不能再defineget:function(){returnval;},//newVal为改变后的值,set方法中默认传递的参数set:function(newVal){console.log('哈哈哈,监听到值变化了',val,'-->',newVal);val=newVal;}});}这样我们已经可以监听每个数据的变化了,那么监听到变化之后就是怎么通知订阅者了,所以接下来我们需要实现一个消息订阅器,很简单,维护一个数组,用来收集订阅者,数据变动触发notify,再调用订阅者的update方法,代码改善之后是这样:

//...val为data[key]对应的值functiondefineReactive(data,key,val){vardep=newDep();observe(val);//监听子属性Object.defineProperty(data,key,{//...省略set:function(newVal){if(val===newVal)return;console.log('哈哈哈,监听到值变化了',val,'-->',newVal);val=newVal;dep.notify();//通知所有订阅者}});}functionDep(){this.subs=[];}Dep.prototype={addSub:function(sub){this.subs.push(sub);},notify:function(){this.subs.forEach(function(sub){sub.update();});}};那么问题来了,谁是订阅者?怎么往订阅器添加订阅者?没错,上面的思路整理中我们已经明确订阅者应该是Watcher,而且vardep=newDep();是在defineReactive方法内部定义的,所以想通过dep添加订阅者,就必须要在闭包内操作,所以我们可以在getter里面动手脚:

因为遍历解析的过程有多次操作dom节点,为提高性能和效率,会先将跟节点el转换成文档碎片fragment进行解析编译操作,解析完成,再将fragment添加回原来的真实dom节点中

functionCompile(el){this.$el=this.isElementNode(el)el:document.querySelector(el);if(this.$el){this.$fragment=this.node2Fragment(this.$el);this.init();this.$el.appendChild(this.$fragment);}}Compile.prototype={init:function(){this.compileElement(this.$fragment);},node2Fragment:function(el){varfragment=document.createDocumentFragment(),child;//将原生节点拷贝到fragmentwhile(child=el.firstChild){fragment.appendChild(child);}returnfragment;}};compileElement方法将遍历所有节点及其子节点,进行扫描解析编译,调用对应的指令渲染函数进行数据渲染,并调用对应的指令更新函数进行绑定,详看代码及注释说明:

Watcher订阅者作为Observer和Compile之间通信的桥梁,主要做的事情是:1、在自身实例化时往属性订阅器(dep)里面添加自己2、自身必须有一个update()方法3、待属性变动dep.notice()通知时,能调用自身的update()方法,并触发Compile中绑定的回调,则功成身退。如果有点乱,可以回顾下前面的思路整理

MVVM作为数据绑定的入口,整合Observer、Compile和Watcher三者,通过Observer来监听自己的model数据变化,通过Compile来解析编译模板指令,最终利用Watcher搭起Observer和Compile之间的通信桥梁,达到数据变化->视图更新;视图交互变化(input)->数据model变更的双向绑定效果。

一个简单的MVVM构造器是这样子:

functionMVVM(options){this.$options=options;vardata=this._data=this.$options.data;observe(data,this);this.$compile=newCompile(options.el||document.body,this)}但是这里有个问题,从代码中可看出监听的数据对象是options.data,每次需要更新视图,则必须通过varvm=newMVVM({data:{name:'kindeng'}});vm._data.name='dmq';这样的方式来改变数据。

显然不符合我们一开始的期望,我们所期望的调用方式应该是这样的:varvm=newMVVM({data:{name:'kindeng'}});vm.name='dmq';

所以这里需要给MVVM实例添加一个属性代理的方法,使访问vm的属性代理为访问vm._data的属性,改造后的代码如下:

functionMVVM(options){this.$options=options;vardata=this._data=this.$options.data,me=this;//属性代理,实现vm.xxx->vm._data.xxxObject.keys(data).forEach(function(key){me._proxy(key);});observe(data,this);this.$compile=newCompile(options.el||document.body,this)}MVVM.prototype={_proxy:function(key){varme=this;Object.defineProperty(me,key,{configurable:false,enumerable:true,get:functionproxyGetter(){returnme._data[key];},set:functionproxySetter(newVal){me._data[key]=newVal;}});}};这里主要还是利用了Object.defineProperty()这个方法来劫持了vm实例对象的属性的读写权,使读写vm实例的属性转成读写了vm._data的属性值,达到鱼目混珠的效果,哈哈

本文主要围绕“几种实现双向绑定的做法”、“实现Observer”、“实现Compile”、“实现Watcher”、“实现MVVM”这几个模块来阐述了双向绑定的原理和实现。并根据思路流程渐进梳理讲解了一些细节思路和比较关键的内容点,以及通过展示部分关键代码讲述了怎样一步步实现一个双向绑定MVVM。文中肯定会有一些不够严谨的思考和错误,欢迎大家指正,有兴趣欢迎一起探讨和改进~

THE END
1.浣熊绒的是一种什么面料?为什么爱掉毛还有这么多人喜欢,并且那么贵!浣熊毛衣掉毛吗? 浣熊毛衣掉毛,一般毛衣都会掉毛,这是一种比较常见的现象。刚买回来的浣熊毛衣需要清洗一下,因为浣熊毛衣表面会有一些浮毛,毛衣里面再穿别的衣服就会沾在上面,通常是很难清洗的,所以买回来一定要清洗一下。 这里小编要说一下,浣熊绒的的掉毛程度是貂绒的2.3倍,是最适合裸穿的一种面料,所以对于浣...https://www.meipian.cn/2p6gkc3c
2.日本有浣熊吗(浣熊和狸猫是一种动物吗)日本狸猫不是浣熊。日本狸猫指的是一种叫犬科动物在中文中常被翻译成狸猫,但在中文中与狸猫不同。另外,狸猫和浣熊很像,所以经常被搞错。但是,如果你仔细观察,你会发现它们的尾巴、脚趾和毛色都不一样。 日本灵猫是浣熊吗 在日本动画中,我们经常可以看到小动物有礼貌的形象。它们的眼睛是黑色的,尾巴非常蓬松,看起...https://m.edu.iask.sina.com.cn/jy/hBAyDPIlZl.html
3.小浣熊:独特的宠物品种及其照料指南宠物品种小浣熊有9种已知的品种,分布在世界各地。最常见的品种包括: 普通浣熊:最常见的品种,分布于北美、中美和南美北部。 蟹食浣熊:以食用螃蟹和其他甲壳类动物而闻名,主要分布在南美。 巴拿马浣熊:分布于巴拿马和哥伦比亚,以其长而蓬松的尾巴而闻名。 哥斯达黎加浣熊:分布于哥斯达黎加,体型较小,面部条纹不明显。 https://www.qinpa.cn/25034.html
1.浣熊简介浣熊形态特征浣熊分布范围→MAIGOO百科浣熊 0 赞数:0 #宠物# 浣熊,是哺乳纲真兽亚纲食肉目浣熊科浣熊属的一种动物,原产自北美洲,现为无危物种。浣熊特征为眼睛周围有一圈深色皮毛,体型较小,体长约40到70厘米。因其常在河边捕食鱼类并在水中浣洗食物,故名浣熊。浣熊是食肉目动物,但浣熊偏于杂食。春天和初夏的饮食主要由昆虫、蠕虫等。夏末、...https://m.maigoo.com/citiao/229159.html
2.浣熊(食肉目浣熊科动物)浣熊(拉丁学名:Procyon lotor),浣熊科浣熊属动物。 1分钟 野生动物生物安全科普系列动画⑨|浣熊篇 视频解读 12.8万观看 浣熊眼睛周围有一圈深色皮毛,体长约40到70厘米;耳朵略圆,上方为白色毛;前后脚有五趾,脚趾常分,能抓住东西。原产自北美洲,喜欢栖息在靠近河流、湖泊或池塘的树林中。看形态特征 浣熊列入《世...https://baike.sogou.com/m/fullLemma?lid=122974
3.童年美食大搜索!你吃过几种?——坐上时光机,回到小辰光有益思在薯片还没有那么火的年代,咪咪虾条风行大江南北。5角钱一包,每天放学后买一包,一点点咸味儿,香脆可口,嘎巴嘎巴。咪咪虾条作为一种以淀粉为主的经典零食,是很多人的童年回忆。 小浣熊干脆面 一种可以干吃的方便面,把袋子里的面饼压碎,撒上调味粉,用手抓着吃,那叫一个香。小浣熊干脆面之所以能人们留下深刻印象...https://www.jfdaily.com/sgh/detail?id=649460
4.哄女朋友睡觉故事(精选50篇)一路上都怔怔的,回家后发了一条手机短信给她:马上洗脸,别忘了多涂点蛤蜊油,否则会皲裂。坐在那里看着打好的行囊,有种叫伤感的情绪汹涌而来。有些东西舍不下,却带不走。 现在QQ里常有小小的留言,比如:“胖了没有?严禁囤积脂肪!”“干吗呢你?别以为我想你了,我的手闲着没事敲字玩。”“注意劳动保护,尽量...https://mip.ruiwen.com/word/hongnvpengyoushuijiaogushi.html
5.森林驿站今日答案是什么森林驿站今日答案汇总1月5日:浣熊喜欢用双手感应物体你知道是什么有效增强了手部感应能力吗? 答案:须毛 1月4日:以下哪种动物的前爪很灵活,常用前爪把食物放在水中洗濯? 答案:浣熊 12月31日:蝙蝠是如何传播植物种子的? 答案:排泄粪便 12月30日:全世界6000多种哺乳动物中有多少种是蝙蝠? https://app.ali213.net/gl/489761.html
6.推荐儿童童话故事可是这一走,可把小猪给害惨了,猎人拿起枪,往小猪的臀部打了几枪,痛得它嗷嗷直叫,差点把猪屎打出来了。没过多久,小猪只能束手就擒。 为了把那几个猎人尽快赶走,小猴便密秘进行钻研,终于,功夫不负有心人,一声爆炸的声音,把猴子炸成了“猴肉”。成功的发明出“无敌大炮”。可以把敌人打得落花流水。“这下终...https://www.jy135.com/ertonggushi/tonghua/855947.html
7.浣熊介绍浣熊属哺乳纲食肉目浣熊科的一种动物。源自北美洲,因其食前要将食物在水中洗濯,故名浣熊,以下是小编整理的浣熊介绍相关资料,希望对大家有所帮助。 浣熊,浣熊原产自北美洲,现为无危物种。浣熊特征为眼睛周围有一圈深色皮毛,体型较小,体长约40到70cm。因其进食前要将食物在水中浣洗,故名浣熊。 https://www.unjs.com/xuexi/quanke/20140601000000_1078600.html