Vue 2.0双向绑定原理的实现方法


Posted in Javascript onOctober 23, 2019

Object.defineProperty方法

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

具体实现过程的代码如下:

1、定义构造函数

function Vue(option){
 this.$el = document.querySelector(option.el); //获取挂载节点
 this.$data = option.data;
 this.$methods = option.methods;
 this.deps = {}; //所有订阅者集合 目标格式(一对多的关系):{msg: [订阅者1, 订阅者2, 订阅者3], info: [订阅者1, 订阅者2]}
 this.observer(this.$data); //调用观察者
 this.compile(this.$el); //调用指令解析器
}

2、定义指令解析器

Vue.prototype.compile = function (el) {
 let nodes = el.children; //获取挂载节点的子节点
 for (var i = 0; i < nodes.length; i++) {
 var node = nodes[i];
 if (node.children.length) {
 this.compile(node) //递归获取子节点
 }
 if (node.hasAttribute('l-model')) { //当子节点存在l-model指令
 let attrVal = node.getAttribute('l-model'); //获取属性值
 node.addEventListener('input', (() => {
 this.deps[attrVal].push(new Watcher(node, "value", this, attrVal)); //添加一个订阅者
 let thisNode = node;
 return () => {
  this.$data[attrVal] = thisNode.value //更新数据层的数据
 }
 })())
 }
 if (node.hasAttribute('l-html')) {
 let attrVal = node.getAttribute('l-html'); //获取属性值
 this.deps[attrVal].push(new Watcher(node, "innerHTML", this, attrVal)); //添加一个订阅者
 }
 if (node.innerHTML.match(/{{([^\{|\}]+)}}/)) {
 let attrVal = node.innerHTML.replace(/[{{|}}]/g, ''); //获取插值表达式内容
 this.deps[attrVal].push(new Watcher(node, "innerHTML", this, attrVal)); //添加一个订阅者
 }
 if (node.hasAttribute('l-on:click')) {
 let attrVal = node.getAttribute('l-on:click'); //获取事件触发的方法名
 node.addEventListener('click', this.$methods[attrVal].bind(this.$data)); //将this指向this.$data
 }
 }
}

3、定义观察者

Vue.prototype.observer = function(data){
 for(var key in data){
 (function(that){
 let val = data[key]; //每一个数据的属性值
 that.deps[key] = []; //初始化所有订阅者对象{msg: [订阅者], info: []}
 var watchers = that.deps[key];
 Object.defineProperty(data, key, { //数据劫持
 get: function(){
  return val;
 },
 set: function(newVal){ //设置值(说明有数据更新)
  if(val !== newVal){
  val = newVal;
  }
  // 通知订阅者
  watchers.forEach(watcher=>{
  watcher.update()
  })
 }
 })
 })(this)
 }
}

4、定义订阅者

function Watcher(el, attr, vm, attrVal) {
 this.el = el;
 this.attr = attr;
 this.vm = vm;
 this.val = attrVal;
 this.update(); //更新视图
}

5、更新视图

Watcher.prototype.update = function () {
 this.el[this.attr] = this.vm.$data[this.val]
}

以上代码定义在一个Vue.js文件中,在需要使用双向绑定的地方引入即可。

尝试使用一下:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>Document</title>
 <script src="./vue.js"></script>
</head>
<body>
 <!--
 实现mvvm的双向绑定,是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。就必须要实现以下几点:
 1、实现一个数据监听器Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
 2、实现一个指令解析器Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
 3、实现一个Watcher,作为连接Observer和Compile的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
 4、mvvm入口函数,整合以上三者
 -->
 <div id="app">
 <input type="text" l-model="msg" >
 <p l-html="msg"></p>
 <input type="text" l-model="info" >
 <p l-html="info"></p>
 <button l-on:click="clickMe">点我</button>
 <p>{{msg}}</p>
 </div>

 <script>
 var vm = new Vue({
 el: "#app",
 data: {
 msg: "恭喜发财",
 info: "好好学习, 天天向上"
 },
 methods: {
 clickMe(){
  this.msg = "我爱敲代码";
 }
 }
 })
 </script>
</body>
</html>

更多教程点击《Vue.js前端组件学习教程》,欢迎大家学习阅读。

关于vue.js组件的教程,请大家点击专题vue.js组件学习教程进行学习。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
百度 popup.js 完美修正版非常的不错 脚本之家推荐
Apr 17 Javascript
JavaScript可否多线程? 深入理解JavaScript定时机制
May 23 Javascript
JS.findElementById()使用介绍
Sep 21 Javascript
ie中js创建checkbox默认选中问题探讨
Oct 21 Javascript
js传中文参数controller里获取参数乱码问题解决方法
Jan 03 Javascript
jQuery中:lt选择器用法实例
Dec 29 Javascript
jQuery制作可自定义大小的拼图游戏
Mar 30 Javascript
原生JS实现网络彩票投注效果
Sep 25 Javascript
jquery实现拖动效果(代码分享)
Jan 25 Javascript
微信小程序 wepy框架与iview-weapp的用法详解
Apr 10 Javascript
8个有意思的JavaScript面试题
Jul 30 Javascript
JS画布动态实现黑客帝国背景效果
Nov 08 Javascript
p5.js绘制旋转的正方形
Oct 23 #Javascript
p5.js实现简单货车运动动画
Oct 23 #Javascript
p5.js实现故宫橘猫赏秋图动画
Oct 23 #Javascript
vue父组件给子组件的组件传值provide inject的方法
Oct 23 #Javascript
p5.js临摹旋转爱心
Oct 23 #Javascript
JavaScript 作用域scope简单汇总
Oct 23 #Javascript
node.js使用fs读取文件出错的解决方案
Oct 23 #Javascript
You might like
关于PHP的相似度计算函数:levenshtein的使用介绍
2013/04/15 PHP
PHP不用递归实现无限分级的例子分享
2014/04/18 PHP
简单谈谈php中的unicode和utf8编码
2015/06/10 PHP
php session_decode函数用法讲解
2019/05/26 PHP
Prototype使用指南之dom.js
2007/01/10 Javascript
javascript 鼠标滚轮事件
2009/04/09 Javascript
指定位置如果有图片显示图片,无图片显示广告的JS
2010/06/05 Javascript
jquery 之 $().hover(func1, funct2)使用方法
2012/06/14 Javascript
JavaScript动态创建div属性和样式示例代码
2013/10/09 Javascript
防止jQuery ajax Load使用缓存的方法小结
2014/02/22 Javascript
推荐5 个常用的JavaScript调试技巧
2015/01/08 Javascript
jquery实现美观的导航菜单鼠标提示特效代码
2015/09/06 Javascript
AngularJs Modules详解及示例代码
2016/09/01 Javascript
如何通过setTimeout理解JS运行机制详解
2019/03/23 Javascript
详解在Javascript中进行面向切面编程
2019/04/28 Javascript
十条建议帮你提高Python编程效率
2016/02/16 Python
浅析Python中else语句块的使用技巧
2016/06/16 Python
TensorFlow损失函数专题详解
2018/04/26 Python
python pytest进阶之fixture详解
2019/06/27 Python
详解Python中字符串前“b”,“r”,“u”,“f”的作用
2019/12/18 Python
spyder 在控制台(console)执行python文件,debug python程序方式
2020/04/20 Python
pycharm全局搜索的具体步骤
2020/07/28 Python
《小壁虎借尾巴》教学反思
2014/02/16 职场文书
质量月活动策划方案
2014/03/10 职场文书
协议书格式
2014/04/23 职场文书
大专生自我鉴定怎么写
2014/09/16 职场文书
工作检讨书怎么写
2014/10/10 职场文书
党的群众路线教育实践活动调研报告
2014/11/03 职场文书
银行优秀员工推荐信
2015/03/24 职场文书
幼儿园六一主持词开场白
2015/05/28 职场文书
法制教育观后感
2015/06/17 职场文书
七年级英语教学反思
2016/02/15 职场文书
导游词之桂林山水
2019/09/20 职场文书
JavaScript中的LHS和RHS分析详情
2022/04/06 Javascript
Consul在linux环境的集群部署
2022/04/08 Servers
Spring JPA 增加字段执行异常问题及解决
2022/06/10 Java/Android