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 相关文章推荐
jQuery操作checkbox选择(list/table)
Apr 07 Javascript
js中传递特殊字符(+,&amp;)的方法
Jan 16 Javascript
给js文件传参数(详解)
Jul 13 Javascript
JavaScript DOM操作表格及样式
Apr 13 Javascript
js绘制圆形和矩形的方法
Aug 05 Javascript
AngularJS过滤器详解及示例代码
Aug 16 Javascript
jQuery图片缩放插件smartZoom使用实例详解
Aug 25 jQuery
node.js使用免费的阿里云ip查询获取ip所在地【推荐】
Sep 03 Javascript
Angular CLI 使用教程指南参考小结
Apr 10 Javascript
详解关于表格合并span-method方法的补充(表格数据由后台动态返回)
May 21 Javascript
vue进入页面时不在顶部,检测滚动返回顶部按钮问题及解决方法
Oct 30 Javascript
JS函数基本定义与用法示例
Jan 15 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
PHP用swoole+websocket和redis实现web一对一聊天
2019/11/05 PHP
可输入的下拉框
2006/06/19 Javascript
JS代码格式化和语法着色V2
2006/10/14 Javascript
JQuery魔力之$(&quot;tagName&quot;)与selector
2012/03/05 Javascript
js跨域访问示例(客户端/服务端)
2014/05/19 Javascript
JS在onclientclick里如何控制onclick的执行
2016/05/30 Javascript
javascript实现的全国省市县无刷新多级关联菜单效果代码
2016/08/01 Javascript
jQuery实现花式轮播之圣诞节礼物传送效果
2016/12/25 Javascript
微信小程序 实战实例开发流程详细介绍
2017/01/05 Javascript
通过vue提供的keep-alive减少对服务器的请求次数
2018/04/01 Javascript
详解javascript replace高级用法
2019/02/17 Javascript
vue+elementUi图片上传组件使用详解
2019/08/20 Javascript
Jquery属性的获取/设置及样式添加/删除操作技巧分析
2019/12/23 jQuery
Node快速切换版本、版本回退(降级)、版本更新(升级)
2021/01/07 Javascript
用python登录Dr.com思路以及代码分享
2014/06/25 Python
python爬虫入门教程之糗百图片爬虫代码分享
2014/09/02 Python
Python中遍历字典过程中更改元素导致异常的解决方法
2016/05/12 Python
Django数据库表反向生成实例解析
2018/02/06 Python
解决python 输出是省略号的问题
2018/04/19 Python
django进阶之cookie和session的使用示例
2018/08/17 Python
解决python opencv无法显示图片的问题
2018/10/28 Python
python3使用matplotlib绘制散点图
2019/03/19 Python
给keras层命名,并提取中间层输出值,保存到文档的实例
2020/05/23 Python
python新手学习可变和不可变对象
2020/06/11 Python
python脚本使用阿里云slb对恶意攻击进行封堵的实现
2021/02/04 Python
Python面试题集
2012/03/08 面试题
大专毕业生简历的自我评价
2013/10/20 职场文书
中文师范生自荐信
2014/01/30 职场文书
迟到早退检讨书
2014/02/10 职场文书
授权委托书样本
2014/04/03 职场文书
《画家乡》教学反思
2014/04/22 职场文书
自我推荐信范文
2014/05/09 职场文书
西安大雁塔导游词
2015/02/10 职场文书
校园安全主题班会
2015/08/12 职场文书
使用pandas或numpy处理数据中的空值(np.isnan()/pd.isnull())
2021/05/14 Python
vue实现Toast组件轻提示
2022/04/10 Vue.js