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 相关文章推荐
javascript import css实例代码
Jul 18 Javascript
JavaScript创建类/对象的几种方式概述及实例
May 06 Javascript
jquery $.each 和for怎么跳出循环终止本次循环
Sep 27 Javascript
JavaScript sup方法入门实例(把字符串显示为上标)
Oct 20 Javascript
JavaScript数据结构与算法之栈详解
Mar 12 Javascript
jQuery往返城市和日期查询实例讲解
Oct 09 Javascript
AngularJS初始化静态模板详解
Jan 14 Javascript
require.js 加载 vue组件 r.js 合并压缩的实例
Oct 14 Javascript
iview中Select 选择器多选校验方法
Mar 15 Javascript
vue init webpack myproject构建项目 ip不能访问的解决方法
Mar 20 Javascript
jQuery实现鼠标滑过商品小图片上显示对应大图片功能【测试可用】
Apr 27 jQuery
Vue.js中关于侦听器(watch)的高级用法示例
May 02 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浮点数知识
2015/05/13 PHP
10个超级有用的PHP代码片段果断收藏
2015/09/23 PHP
分享五个PHP7性能优化提升技巧
2015/12/07 PHP
Laravel自定义 封装便捷返回Json数据格式的引用方法
2019/09/29 PHP
PHP使用Http Post请求发送Json对象数据代码解析
2020/07/16 PHP
JQuery 构建客户/服务分离的链接模型中Table分页代码效率初探
2010/01/22 Javascript
ExtJs 3.1 XmlTreeLoader Example Error
2010/02/09 Javascript
JavaScript获取路径设计源码
2014/05/22 Javascript
javascript操作字符串的原生方法
2014/12/22 Javascript
jquery实现在光标位置插入内容的方法
2015/02/05 Javascript
nodejs 日志模块winston的使用方法
2018/05/02 NodeJs
Node.js使用Angular简单示例
2018/05/11 Javascript
如何在Vue项目中添加接口监听遮罩
2021/01/25 Vue.js
Python greenlet实现原理和使用示例
2014/09/24 Python
简单介绍Python中的decode()方法的使用
2015/05/18 Python
通过python+selenium3实现浏览器刷简书文章阅读量
2017/12/26 Python
Python基于列表list实现的CRUD操作功能示例
2018/01/05 Python
Python常用特殊方法实例总结
2019/03/22 Python
Python为何不能用可变对象作为默认参数的值
2019/07/01 Python
pycharm配置当鼠标悬停时快速提示方法参数
2019/07/31 Python
Python文件操作方法详解
2020/02/09 Python
python下载的库包存放路径
2020/07/27 Python
HTML5拖放API实现自动生成相框功能
2020/04/07 HTML / CSS
美特斯邦威官方商城:邦购网
2016/10/13 全球购物
德国街头和运动文化高品质商店:BSTN Store
2017/08/26 全球购物
学校司机岗位职责
2013/11/14 职场文书
个人自我鉴定写法
2013/11/30 职场文书
迟到检讨书300字
2014/02/14 职场文书
《曹刿论战》教学反思
2014/03/02 职场文书
颁奖晚会主持词
2014/03/25 职场文书
2014年党课学习心得体会
2014/07/08 职场文书
2014年军人思想汇报范文
2014/10/12 职场文书
社区环境卫生倡议书
2015/04/29 职场文书
2015年支教教师工作总结
2015/07/22 职场文书
导游词之河北野三坡
2019/12/11 职场文书
springboot如何初始化执行sql语句
2021/06/22 Java/Android