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 相关文章推荐
Mootools 1.2教程 类(一)
Sep 15 Javascript
jquery 插件开发方法小结
Oct 23 Javascript
javascript 基础篇3 类,回调函数,内置对象,事件处理
Mar 14 Javascript
写自已的js类库需要的核心代码
Jul 16 Javascript
JavaScript代码实现左右上下自动晃动自动移动
Apr 08 Javascript
JavaScript开发Chrome浏览器扩展程序UI的教程
May 16 Javascript
node.js 和HTML5开发本地桌面应用程序
Dec 13 Javascript
JS仿淘宝搜索框用户输入事件的实现
Jun 19 Javascript
Vue中引入样式文件的方法
Aug 18 Javascript
promise和co搭配生成器函数方式解决js代码异步流程的比较
May 25 Javascript
微信小程序实现美团菜单
Jun 06 Javascript
20道JS原理题助你面试一臂之力(必看)
Jul 22 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
西德产收音机
2021/03/01 无线电
PHP的substr_replace将指定两位置之间的字符替换为*号
2011/05/04 PHP
Yii入门教程之Yii安装及hello world
2014/11/25 PHP
Thinkphp搜索时首页分页和搜索页保持条件分页的方法
2014/12/05 PHP
php自定义函数实现二维数组排序功能
2016/07/20 PHP
Laravel 5.1 on SAE环境开发教程【附项目demo源码】
2016/10/09 PHP
PHP缓存工具XCache安装与使用方法详解
2018/04/09 PHP
PHP xpath提取网页数据内容代码解析
2020/07/16 PHP
JS函数this的用法实例分析
2015/02/05 Javascript
微信小程序实现图片自适应(支持多图)
2017/01/25 Javascript
浅析JS中的 map, filter, some, every, forEach, for in, for of 用法总结
2017/03/29 Javascript
AngularJS中filter的使用实例详解
2017/08/25 Javascript
vue+springboot实现项目的CORS跨域请求
2018/09/05 Javascript
对angularJs中ng-style动态改变样式的实例讲解
2018/09/30 Javascript
JQuery判断radio单选框是否选中并获取值的方法
2019/01/17 jQuery
纯javascript实现选择框的全选与反选功能
2019/04/08 Javascript
vue-router 前端路由之路由传值的方式详解
2019/04/30 Javascript
layui 实现加载动画以及非真实加载进度的方法
2019/09/23 Javascript
Vue项目中Api的组织和返回数据处理的操作
2019/11/04 Javascript
layui 弹出层值回传解决方式
2019/11/14 Javascript
javascript设计模式之迭代器模式
2020/01/30 Javascript
小程序接入腾讯位置服务的详细流程
2020/03/03 Javascript
js验证账户名是否重复
2020/05/26 Javascript
Python内置的字符串处理函数详细整理(覆盖日常所用)
2014/08/19 Python
Django静态资源URL STATIC_ROOT的配置方法
2014/11/08 Python
详解Python程序与服务器连接的WSGI接口
2015/04/29 Python
Python验证企业工商注册码
2015/10/25 Python
Python字典创建 遍历 添加等实用基础操作技巧
2018/09/13 Python
利用PyCharm操作Github(仓库新建、更新,代码回滚)
2019/12/18 Python
css3翻牌翻数字的示例代码
2020/02/07 HTML / CSS
世界上最大的在线汽车租赁预订平台:Rentalcars.com(支持中文)
2018/10/12 全球购物
入党积极分子思想汇报范文
2014/01/05 职场文书
大学同学十年聚会感言
2014/02/21 职场文书
义诊活动总结
2015/02/04 职场文书
发言稿之优秀教师篇
2019/09/26 职场文书
Nginx配置并兼容HTTP实现代码解析
2021/03/31 Servers