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


Posted in Javascript onOctober 23, 2019

proxy方法

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

Vue 3.0与Vue 2.0的区别仅是数据劫持的方式由Object.defineProperty更改为Proxy代理,其他代码不变。可查看Vue 2.0双向绑定原理的实现

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

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、定义观察者(区别在这一块代码)

Liu.prototype.observer = function (data) {
 const that = this;
 for(var key in data){
  that.deps[key] = []; //初始化所有订阅者对象{msg: [订阅者], info: []}
 }
 let handler = {
  get(target, property) {
   return target[property];
  },
  set(target, key, value) {
   let res = Reflect.set(target, key, value);
   var watchers = that.deps[key];
   watchers.map(item => {
    item.update();
   });
   return res;
  }
 }
 this.$data = new Proxy(data, handler);
}

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面象对象设计
Apr 28 Javascript
WordPress 照片lightbox效果的运用几点
Jun 22 Javascript
formValidator3.3的ajaxValidator一些异常分析
Jul 12 Javascript
json原理分析及实例介绍
Nov 29 Javascript
jquery text(),val(),html()方法区别总结
Nov 04 Javascript
js清理Word格式示例代码
Feb 13 Javascript
iScroll中事件点击触发两次解决方案
Mar 11 Javascript
jQuery插件MixItUp实现动画过滤和排序
Apr 12 Javascript
jQuery实现的给图片点赞+1动画效果(附在线演示及demo源码下载)
Dec 31 Javascript
Angular中使用ui router实现系统权限控制及开发遇到问题
Sep 23 Javascript
canvas实现环形进度条效果
Mar 23 Javascript
JavaScript 中的无穷数(Infinity)详解
Feb 13 Javascript
JavaScript判断数组类型的方法
Oct 23 #Javascript
Vue 2.0双向绑定原理的实现方法
Oct 23 #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
You might like
php5.3中连接sqlserver2000的两种方法(com与ODBC)
2012/12/29 PHP
2个自定义的PHP in_array 函数,解决大量数据判断in_array的效率问题
2014/04/08 PHP
wamp服务器访问php非常缓慢的解决过程
2015/07/01 PHP
php基于session实现数据库交互的类实例
2015/08/03 PHP
php简单创建zip压缩文件的方法
2016/04/30 PHP
thinkPHP连接sqlite3数据库的实现方法(附Thinkphp代码生成器下载)
2016/05/27 PHP
js之WEB开发调试利器:Firebug 下载
2007/01/13 Javascript
jQuery Animation实现CSS3动画示例介绍
2013/08/14 Javascript
当json键为数字时的取值方法解析
2013/11/15 Javascript
jquery中子元素和后代元素的区别示例介绍
2014/04/02 Javascript
原生js实现fadein 和 fadeout淡入淡出效果
2014/06/05 Javascript
javascript中定义类的方法汇总
2014/12/28 Javascript
jQuery判断指定id的对象是否存在的方法
2015/05/22 Javascript
jQuery UI Bootstrap是什么?
2016/06/17 Javascript
Vue实例简单方法介绍
2017/01/20 Javascript
nodejs中art-template模板语法的引入及冲突解决方案
2017/11/07 NodeJs
原生JS实现网页手机音乐播放器 歌词同步播放的示例
2018/02/02 Javascript
spirngmvc js传递复杂json参数到controller的实例
2018/03/29 Javascript
解决vue中监听input只能输入数字及英文或者其他情况的问题
2018/08/30 Javascript
JS实现的Object数组去重功能示例【数组成员为Object对象】
2019/02/01 Javascript
Node爬取大批量文件的方法示例
2019/06/28 Javascript
javascript移动端 电子书 翻页效果实现代码
2019/09/07 Javascript
keep-Alive搭配vue-router实现缓存页面效果的示例代码
2020/06/24 Javascript
JavaScript实现表单验证功能
2020/12/09 Javascript
python anaconda 安装 环境变量 升级 以及特殊库安装的方法
2017/06/21 Python
Django url,从一个页面调到另个页面的方法
2019/08/21 Python
使用 Python 合并多个格式一致的 Excel 文件(推荐)
2019/12/09 Python
pyecharts绘制中国2020肺炎疫情地图的实例代码
2020/02/12 Python
如何使用Python处理HDF格式数据及可视化问题
2020/06/24 Python
爱尔兰灯和灯具网上商店:Lights.ie
2018/03/26 全球购物
同步和异步有何异同,在什么情况下分别使用他们?举例说明
2014/02/27 面试题
学校献爱心活动总结
2014/07/08 职场文书
乱丢垃圾袋检讨书
2014/10/08 职场文书
六一儿童节新闻稿
2015/07/17 职场文书
Python爬虫之爬取二手房信息
2021/04/27 Python
Win11应用商店打开闪退怎么解决? win11应用商店打不开的多种解决办法
2022/04/05 数码科技