vue源码解读1

vue源码解读1

前言

vue是一个非常典型的MVVM框架,它的核心功能一是双向数据绑定系统,二是组件化开发系统。那么本文是以一种通俗易懂的的角度来实现一个简单
的双向数据绑定系统,如果你用过vue却对vue的实现原理不太清楚,或者没用过vue想学习vue那我相信看完本文你会的vue的实现有一个比较简单明确的了解。不过如果哪块有
错误,还望指出。

本文的实现目标:

input标签和{
{text}}的内容与data中的text值保持一致,实现双向绑定

 <div id="app"> <input type="text" v-model="text"> {
    {text}} </div>
 var vm=new Vue({ el:'app', data:{ text:'hello world!' } })

分解任务(三步)

  • model→view的初始化绑定
  • view→model的绑定
  • model→view的绑定

    看不太懂?不要着急,接下来先一步一步分析每一步都具体做了什么再回头看

    Step1 : model→view的初始化绑定.

    很简单,就是让v-mode=”text”和{
    {text}}绑定到的data中text的值。这里会有两个函数帮我们做事情,一个是node2Fragment函数,帮我们取到结点,
    一个是compile函数,操作我们取到的node结点的值去等于对应的data值,这样就完成了model到view的第一次初始化绑定。

 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=/\{\{(.*)\}\}/; //节点类型为元素 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 if(node.nodeType===3){ if(reg.test(node.nodeValue)){ var name=RegExp.$1; name=name.trim(); //将data的值赋给该node node.nodeValue=vm.data[name]; } } }
 //Vue对象 function Vue(options){ this.data=options.data; var id=options.el; var dom=node2Fragment(document.getElementById(id),this); //编译完成后,将dom片段添加到el挂载的元素上(app) document.getElementById(id).appendChild(dom) } //调用Vue var vm=new Vue({ el:'app', data:{ text:'hello world!' } })

最终达到的效果如下图:v-model绑定的input和{
{text}}的值和data中的text保持一致

1070235-20180518102023784-667626150.jpg

Step2 : view→model的绑定.

这一步的目标:当用户输入改变input的值(view层)时,反映到data中(model层)并改变对应的值

方法:

  • 在complie编译的时候监听node,并改变data中的值为node.value;
  • 通知data中的数据改变(这里会用到访问器属性,即Object.defineProperty

    这里我们先完成第二个点,通知数据改变,在全局中新添加两个函数

 function defineReactive(obj,key,val){ Object.defineProperty(obj,key,{ get:function(){ return val }, set:function(newVal){ 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对象 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) }

监听node(修改complie函数)

 function compile(node,vm){ var reg=/\{\{(.*)\}\}/; //节点类型为元素 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; node.addEventListener('input',function(e){ //给对应的data属性赋值,并触发该属性的set函数 vm[name]=e.target.value; }); //将data值赋给该node,注意这里本来是vm.data[name] node.value=vm[name] } } }; //节点类型为text if(node.nodeType===3){ if(reg.test(node.nodeValue)){ var name=RegExp.$1; name=name.trim(); //将data的值赋给该node,注意这里本来是vm.data[name] node.nodeValue=vm[name]; } } }

那么step2完成了,当用户在input中输入值,data属性值也会发生改变,这样一来就完成了model→view的一个实现过程

1070235-20180518102110402-855085874.jpg

Step3 : model→view的绑定

诶不是之前已经绑定过一次model→view,怎么还要绑定?

第一次绑定是初始化绑定,我们现在要完成的是,当用户改变data值,再回过头去改变view层,这里刚好可以用到一个设计模式:
观察者模式-让多个观察者同时监听某一个主题对象,这个主题对象的状态发生改变时就会通知所有观察者对象。

1070235-20180518102119569-32326473.png

放到这里就是:每个data属性值在defineReactive函数监听处理的时候,添加一个主题对象,当data属性发生改变,通过set函数去通知所有的观察者们,
那么如何添加观察者们呢,就是在complie函数编译node时,通过初始化value值,触发set函数,在set函数中为主题对象添加
观察者。有点难理解?直接看代码就明白了。

 function compile(node,vm){ var reg=/\{\{(.*)\}\}/; //节点类型为元素(这块在这里并没有修改) //节点类型为text if(node.nodeType===3){ if(reg.test(node.nodeValue)){ var name=RegExp.$1; name=name.trim(); //初始化数据,并给对应的data属性值添加观察者 new Watcher(vm,node,name); } } }

我们注意到新建了一个Watcher对象,这个对象的作用就是初始化数据(step1做的工作),以及触发get函数,添加这个node到观察者

 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] } }

观察者设置好了,现在设置主题,在defineReactive函数里

 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){ if(newVal===val)return ; val=newVal; //作为发布者发出通知(更新所有订阅了这个属性的view) dep.notify(); } }) }

主题的结构:

 function Dep(){ //主题的订阅者们 this.subs=[]; } Dep.prototype={ //添加订阅者的方法 addSub:function(sub){ this.subs.push(sub); }, //发布信息的方法(让订阅者们全部更新view) notify:function(){ this.subs.forEach(function(sub){ sub.update(); }) } }

如此一来,一个简单的MVVM就实现了,思维导图如下:

1070235-20180518102128583-106487127.png

不过这只是Vue的冰山一角,只是实现了一个v-model,陆陆续续会更新其他的操作和一些细节,敬请期待,如果你
看完了并且有所收获不妨点个star(滑稽脸)

转载于:https://www.cnblogs.com/souleigh-hong/p/9054776.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/101654.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • AppFabric_fabric安装

    AppFabric_fabric安装安装后,两种方式存储:XML,sqlserver建立缓存集New-CacheCluster-ProviderSystem.Data.SqlClient-ConnectionString “DataSource=[ServerName];InitialCatalog=[DBName];IntegratedSecurity=True”-SizeSmallRegiste

    2022年10月17日
    0
  • “大数据管理局”让大数据共用共享

    “大数据管理局”让大数据共用共享近日,广州市政府官方网站公布了工信委、商务委和国资委3个部门的“三定方案”。三个部门共“定编”339名,其中商务委编制最多,占比超4成。机构设置方面,工信委下设的广州市大数据管理局(正处级)颇具创新,其承载着建设工业大数据库等9项重要职责。城市发展到了今天这么大的体量,社会治理模式也需要不断升级。大数据,无疑是一个重要的发展方向。随着网络的普及…

    2022年6月8日
    44
  • Batch Normalization批量归一化[通俗易懂]

    Batch Normalization批量归一化[通俗易懂]深度学习捷报连连、声名鹊起,随机梯度下降成了训练深度网络的主流方法。尽管随机梯度下降法对于训练深度网络简单高效,但是它有个毛病,就是需要我们人为的去选择参数,比如学习率、参数初始化、权重衰减系数、Dropout比例等。这些参数的选择对训练结果至关重要,以至于我们很多时间都浪费在这些的调参上。那么学完这篇文献之后,你可以不需要那么刻意的慢慢调整参数。批量标准化一般用在非线性…

    2022年8月31日
    9
  • MySql必知必会实战练习(二)数据检索

    在上篇博客MySql必知必会实战练习(一)表创建和数据添加中完成了各表的创建和数据添加,下面进行数据检索和过滤操作。1.Select子句使用顺序select>DISTINCT>f

    2021年12月29日
    31
  • pandas数据清洗详细教程_excel数据清洗工具

    pandas数据清洗详细教程_excel数据清洗工具Pandas数据清洗常见方法01读取数据df=pd.read_csv(‘文件名称’)02查看数据特征df.info()03查看数据量df.shape04查看各数字类型的统计量df.describe()05去除重复值df.drop_duplicates(inplace=True)06重置索引data.reset_index(inplace=True,drop=True)07查看缺失值信息data.loc[data[‘列名’].isnull()]01

    2022年9月7日
    0
  • linux查看光纤卡光衰_光衰大如何解决

    linux查看光纤卡光衰_光衰大如何解决ethtool-g网卡名称,显示网卡的接收/发送环形参数。lldptool-t-n-i网卡名称收光:ethtool-meth0|grep”Receiversignal”|head-1|awk-F’/”{print$2}’发光:ethtool-meth0|grep”Laseroutputpower”|head-1|awk-F’/”{print$2}’…

    2022年8月31日
    3

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号