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 相关文章推荐
JCalendar 日历控件 v1.0 beta[兼容IE&amp;Firefox] 有文档和例子
May 30 Javascript
Javascript变量作用域详解
Dec 06 Javascript
jquery的clone方法应用于textarea和select的bug修复
Jun 26 Javascript
jQuery基础知识小结
Dec 22 Javascript
JavaScript将一个数组插入到另一个数组的方法
Mar 19 Javascript
js正则表达式匹配数字字母下划线等
Apr 14 Javascript
简介JavaScript中Math.LOG10E属性的使用
Jun 14 Javascript
jQuery EasyUI Tab 选项卡问题小结
Aug 16 Javascript
jquery如何实现点击空白处隐藏元素
Dec 05 jQuery
详解基于Vue cli生成的Vue项目的webpack4升级
Jun 19 Javascript
微信小程序获取音频时长与实时获取播放进度问题
Aug 28 Javascript
详解微信小程序中var、let、const用法与区别
Jan 11 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
十大催泪虐心动漫电影,有几部你还没看
2020/03/04 日漫
PHP中对用户身份认证实现两种方法
2011/06/04 PHP
php实现水仙花数示例分享
2014/04/03 PHP
php微信开发之百度天气预报
2016/11/18 PHP
Js setInterval与setTimeout(定时执行与循环执行)的代码(可以传入参数)
2010/06/11 Javascript
style、 currentStyle、 runtimeStyle区别分析
2010/08/01 Javascript
jQuery 翻牌或百叶窗效果(内容三秒自动切换)
2012/06/14 Javascript
jquery实现动态画圆
2014/12/04 Javascript
Node.js的包详细介绍
2015/01/14 Javascript
深入理解JavaScript系列(38):设计模式之职责链模式详解
2015/03/04 Javascript
在JavaScript的正则表达式中使用exec()方法
2015/06/16 Javascript
基于JavaScript如何实现私有成员的语法特征及私有成员的实现方式
2015/10/28 Javascript
React Native之TextInput组件解析示例
2017/08/22 Javascript
node.js express框架实现文件上传与下载功能实例详解
2019/10/15 Javascript
javascript+Canvas实现画板功能
2020/06/23 Javascript
Python实现控制台中的进度条功能代码
2017/12/22 Python
python3.x实现发送邮件功能
2018/05/22 Python
pytorch 利用lstm做mnist手写数字识别分类的实例
2020/01/10 Python
python绘制封闭多边形教程
2020/02/18 Python
matplotlib实现数据实时刷新的示例代码
2021/01/05 Python
html5表单及新增的改良元素详解
2016/06/07 HTML / CSS
东芝官网商城:还原日式美学,打造美好生活
2018/12/27 全球购物
意大利顶级奢侈品电商:LUISAVIAROMA(支持中文)
2020/05/26 全球购物
土木工程毕业生自荐信
2013/09/21 职场文书
应届生妇产科护士求职信
2013/10/27 职场文书
预备党员思想汇报范文
2014/01/11 职场文书
教师节促销活动方案
2014/02/14 职场文书
《真想变成大大的荷叶》教学反思
2014/04/14 职场文书
学习型党组织建设经验材料
2014/05/26 职场文书
2014小学数学教研组工作总结
2014/12/06 职场文书
出国导师推荐信
2015/03/25 职场文书
2015年售票员工作总结
2015/04/29 职场文书
工作态度恶劣检讨书
2015/05/06 职场文书
2016年六一文艺汇演开幕词
2016/03/04 职场文书
Jupyter notebook 不自动弹出网页的解决方案
2021/05/21 Python
MySQL慢查询中的commit慢和binlog中慢事务的区别
2022/06/16 MySQL