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 相关文章推荐
浏览器解析js生成的html出现样式问题的解决方法
Apr 16 Javascript
js判断选择时间不能小于当前时间的示例代码
Sep 24 Javascript
jQuery实现信息提示框(带有圆角框与动画)效果
Aug 07 Javascript
Js实现简单的小球运动特效
Feb 18 Javascript
使用递归遍历对象获得value值的实现方法
Jun 14 Javascript
jQuery设置聚焦并使光标位置在文字最后的实现方法
Aug 02 Javascript
jquery实现垂直和水平菜单导航栏
Aug 27 Javascript
js微信扫描二维码登录网站技术原理
Dec 01 Javascript
微信小程序 简单教程实例详解
Jan 13 Javascript
vue router路由嵌套不显示问题的解决方法
Jun 17 Javascript
使用js获取伪元素的content实例
Oct 24 Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【矩形情况】
Dec 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
PHP在字符串中查找指定字符串并删除的代码
2008/10/02 PHP
PHP中基本符号及使用方法
2010/03/23 PHP
PHP面向对象三大特点学习(充分理解抽象、封装、继承、多态)
2012/05/07 PHP
PHP表单递交控件名称含有点号(.)会被转化为下划线(_)的处理方法
2013/01/06 PHP
PHP接收json 并将接收数据插入数据库的实现代码
2015/12/01 PHP
thinkPHP5框架auth权限控制类与用法示例
2018/06/12 PHP
JQuery toggle使用分析
2009/11/16 Javascript
js中top、clientTop、scrollTop、offsetTop的区别 文字详细说明版
2011/01/08 Javascript
jQuery使用andSelf()来包含之前的选择集
2014/05/19 Javascript
node.js中的http.get方法使用说明
2014/12/14 Javascript
Javascript writable特性介绍
2015/02/27 Javascript
JavaScript实现的简单烟花特效代码
2015/10/20 Javascript
基于jQuery仿淘宝产品图片放大镜特效
2020/10/19 Javascript
js时间戳格式化成日期格式的多种方法介绍
2017/02/16 Javascript
AngularJS 防止页面闪烁的方法
2017/03/09 Javascript
HTML5实现微信拍摄上传照片功能
2017/04/21 Javascript
详解angularJS+Ionic移动端图片上传的解决办法
2017/09/13 Javascript
vue.js实现只弹一次弹框
2018/01/29 Javascript
jQuery阻止事件冒泡实例分析
2018/07/03 jQuery
express如何解决ajax跨域访问session失效问题详解
2019/06/20 Javascript
利用JavaScript模拟京东按键输入功能
2020/12/01 Javascript
python多线程操作实例
2014/11/21 Python
Python数据操作方法封装类实例
2017/06/23 Python
python3判断url链接是否为404的方法
2018/08/10 Python
Python3利用Dlib实现摄像头实时人脸检测和平铺显示示例
2019/02/21 Python
Python中bisect的使用方法
2019/12/31 Python
python离线安装外部依赖包的实现
2020/02/13 Python
python生成word合同的实例方法
2021/01/12 Python
html5 input输入实时检测以及延时优化
2018/07/18 HTML / CSS
印尼最大的在线购物网站:MatahariMall.com
2016/08/26 全球购物
Tory Burch英国官方网站:美国时尚生活品牌
2017/12/06 全球购物
DBA数据库管理员JAVA程序员架构师必看
2016/02/07 面试题
户外用品商店创业计划书
2014/01/29 职场文书
国培远程培训感言
2014/03/08 职场文书
Nginx+Tomcat负载均衡集群的实现示例
2021/10/24 Servers
nginx配置指令之server_name的具体使用
2022/08/14 Servers