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 相关文章推荐
baidu博客的编辑友情链接的新的层窗口!经典~支持【FF】
Feb 09 Javascript
IE8中动态创建script标签onload无效的解决方法
Dec 22 Javascript
获取input标签的所有属性的方法
Jun 28 Javascript
简单分析javascript中的函数
Sep 10 Javascript
jQuery-mobile事件监听与用法详解
Nov 23 Javascript
微信公众平台开发教程(四) 实例入门:机器人回复(附源码)
Dec 02 Javascript
livereload工具实现前端可视化开发【推荐】
Dec 23 Javascript
JavaScript实现大图轮播效果
Jan 11 Javascript
微信小程序 下拉列表的实现实例代码
Mar 08 Javascript
JavaScript中的this原理及6种常见使用场景详解
Feb 14 Javascript
浅析JS中NEW的实现原理及重写
Feb 20 Javascript
解决Vue的项目使用Element ui 走马灯无法实现的问题
Aug 03 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
把从SQL中取出的数据转化成XMl格式
2006/10/09 PHP
动态新闻发布的实现及其技巧
2006/10/09 PHP
PHP 一个随机字符串生成代码
2010/05/26 PHP
php将金额数字转化为中文大写
2015/07/09 PHP
php微信公众号开发之秒杀
2018/10/20 PHP
Laravel jwt 多表(多用户端)验证隔离的实现
2019/12/18 PHP
用js实现下载远程文件并保存在本地的脚本
2008/05/06 Javascript
javascript天然的迭代器
2010/10/29 Javascript
javascript简单性能问题及学习笔记
2014/02/04 Javascript
jQuery 计算iframe 窗口大小的方法
2014/05/13 Javascript
JavaScrip常见的一些算法总结
2015/12/28 Javascript
fullpage.js全屏滚动插件使用实例
2016/09/06 Javascript
js中作用域的实例解析
2017/03/16 Javascript
.net MVC+Bootstrap下使用localResizeIMG上传图片
2017/04/21 Javascript
利用jQuery+localStorage实现一个简易的计时器示例代码
2017/12/25 jQuery
VUE+Element环境搭建与安装的方法步骤
2019/01/24 Javascript
微信小程序开发常见问题及解决方案
2019/07/11 Javascript
javascript实现函数柯里化与反柯里化过程解析
2019/10/08 Javascript
nodejs脚本centos开机启动实操方法
2020/03/04 NodeJs
python解析html开发库pyquery使用方法
2014/02/07 Python
urllib2自定义opener详解
2014/02/07 Python
python获得linux下所有挂载点(mount points)的方法
2015/04/29 Python
Python中单例模式总结
2018/02/20 Python
python SMTP实现发送带附件电子邮件
2018/05/22 Python
Python使用matplotlib 画矩形的三种方式分析
2019/10/31 Python
Series和DataFrame使用简单入门
2019/11/13 Python
利用pytorch实现对CIFAR-10数据集的分类
2020/01/14 Python
美国宠物美容和宠物用品购物网站:Cherrybrook
2018/12/07 全球购物
公司财务总监岗位职责
2013/12/14 职场文书
施工安全责任书
2014/04/14 职场文书
初三开学计划书
2014/04/27 职场文书
教师批评与自我批评材料
2014/10/16 职场文书
环卫工作个人总结
2015/03/04 职场文书
乡镇团委工作总结2015
2015/05/26 职场文书
公司考勤管理制度
2015/08/04 职场文书
干货:我将这样书写我的演讲稿!
2019/05/09 职场文书