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 相关文章推荐
一段利用WSH修改和查看IP配置的代码
May 11 Javascript
制作高质量的JQuery Plugin 插件的方法
Apr 20 Javascript
Node.js实现在目录中查找某个字符串及所在文件
Sep 03 Javascript
jQuery检查事件是否触发的方法
Jun 26 Javascript
jQuery插件EasyUI校验规则 validatebox验证框
Nov 29 Javascript
小程序实现悬浮搜索框
Jul 12 Javascript
React Native 混合开发多入口加载方式详解
Sep 23 Javascript
关于layui的下拉搜索框异步加载数据的解决方法
Sep 28 Javascript
使用vscode快速建立vue模板过程详解
Oct 10 Javascript
vue实现随机验证码功能(完整代码)
Dec 10 Javascript
JavaScript利用键盘码控制div移动
Mar 19 Javascript
JS数组去重详情
Nov 07 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针对中英文混合字符串长度判断及截取方法示例
2017/03/31 PHP
PHP进阶学习之反射基本概念与用法分析
2019/06/18 PHP
php函数式编程简单示例
2019/08/08 PHP
Nigma vs Liquid BO3 第一场2.13
2021/03/10 DOTA
javascript 写的一个简单的timer
2009/07/30 Javascript
jquery 屏蔽一个区域内的所有元素,禁止输入
2009/10/22 Javascript
js动态添加onload、onresize、onscroll事件(另类方法)
2012/12/26 Javascript
基于JavaScript实现前端文件的断点续传
2016/10/17 Javascript
深入理解js中的加载事件
2017/02/08 Javascript
纯JS实现图片验证码功能并兼容IE6-8(推荐)
2017/04/19 Javascript
js实现简单数字变动效果
2017/11/06 Javascript
vue.js中实现登录控制的方法示例
2018/04/23 Javascript
javascript中的event loop事件循环详解
2018/12/14 Javascript
vue-cli3 karma单元测试的实现
2019/01/18 Javascript
Python松散正则表达式用法分析
2016/04/29 Python
Ubuntu安装Jupyter Notebook教程
2017/10/18 Python
django js实现部分页面刷新的示例代码
2018/05/28 Python
flask/django 动态查询表结构相同表名不同数据的Model实现方法
2019/08/29 Python
Python基于QQ邮箱实现SSL发送
2020/04/26 Python
python爬取网易云音乐热歌榜实例代码
2020/08/07 Python
html5超简单的localStorage实现记住密码的功能实现
2017/09/07 HTML / CSS
英国最大的电子产品和家电零售企业:Currys PC World
2016/09/24 全球购物
英国领先的男装设计师服装购物网站:Mainline Menswear
2018/02/04 全球购物
安德玛比利时官网:Under Armour比利时
2019/08/28 全球购物
四种会话跟踪技术
2015/05/20 面试题
化学教师自荐信范文
2013/12/28 职场文书
留学推荐信中文范文三篇
2014/01/25 职场文书
分层教学实施方案
2014/03/19 职场文书
教师一岗双责责任书
2014/04/16 职场文书
基层党员公开承诺书
2014/05/29 职场文书
十佳青年事迹材料
2014/08/21 职场文书
2014年幼儿园园务工作总结
2014/12/05 职场文书
2015年社区工作总结
2015/04/08 职场文书
春风化雨观后感
2015/06/11 职场文书
pytorch常用数据类型所占字节数对照表一览
2021/05/17 Python
MySQL Innodb索引机制详细介绍
2021/11/23 MySQL