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 相关文章推荐
javascript确认框的三种使用方法
Dec 17 Javascript
jquery制作 随机弹跳的小球特效
Feb 01 Javascript
jQuery实现定时读取分析xml文件的方法
Jul 16 Javascript
jQuery实现宽屏图片轮播实例教程
Nov 24 Javascript
layui.js实现的表单验证功能示例
Nov 15 Javascript
讲解vue-router之命名路由和命名视图
May 28 Javascript
javascript闭包的使用之按钮切换功能
Aug 30 Javascript
JavaScript使用Math.random()生成简单的验证码
Jan 21 Javascript
vue中添加与删除关键字搜索功能
Oct 12 Javascript
js实现盒子滚动动画效果
Aug 09 Javascript
React实现阿里云OSS上传文件的示例
Aug 10 Javascript
Vue全家桶入门基础教程
May 14 Vue.js
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
全国FM电台频率大全 - 3 河北省
2020/03/11 无线电
php生成WAP页面
2006/10/09 PHP
php学习笔记之 函数声明
2011/06/09 PHP
laravel 5异常错误:FatalErrorException in Handler.php line 38的解决
2017/10/12 PHP
phpcms实现验证码替换及phpcms实现全站搜索功能教程详解
2017/12/13 PHP
JavaScript打字小游戏代码
2011/12/26 Javascript
jQuery.validate 常用方法及需要注意的问题
2013/03/20 Javascript
获取表单控件原始(初始)值的方法
2013/08/21 Javascript
node.js中的fs.createReadStream方法使用说明
2014/12/17 Javascript
TinyMCE提交AjaxForm获取不到数据的解决方法
2015/03/05 Javascript
JavaScript中点击事件的写法
2016/06/28 Javascript
JavaScript中在光标处插入添加文本标签节点的详细方法
2017/03/22 Javascript
微信小程序实现拖拽 image 触摸事件监听的实例
2017/08/17 Javascript
vue使用axios跨域请求数据问题详解
2017/10/18 Javascript
vue2.0+koa2+mongodb实现注册登录
2018/04/10 Javascript
详解webpack打包时排除其中一个css、js文件或单独打包一个css、js文件(两种方法)
2018/10/26 Javascript
ES6 Generator函数的应用实例分析
2019/06/26 Javascript
layui前端时间戳转化实例
2019/11/15 Javascript
vscode+gulp轻松开发小程序的完整步骤
2020/10/18 Javascript
node中短信api实现验证码登录的示例代码
2021/01/20 Javascript
Python struct模块解析
2014/06/12 Python
python爬取m3u8连接的视频
2018/02/28 Python
Python I/O与进程的详细讲解
2019/03/08 Python
python与pycharm有何区别
2020/07/01 Python
开发人员所需要知道的HTML5性能分析面面观
2012/07/05 HTML / CSS
基于HTML5 Canvas的3D动态Chart图表的示例
2017/11/02 HTML / CSS
Zadig&Voltaire官网:法国时装品牌
2018/01/05 全球购物
社区护士演讲稿
2014/08/27 职场文书
教师群众路线教育实践活动个人对照检查材料
2014/11/04 职场文书
2014年反腐倡廉工作总结
2014/12/05 职场文书
2014年学校后勤工作总结
2014/12/06 职场文书
工商行政处罚决定书
2015/06/24 职场文书
交通事故责任认定书
2015/08/06 职场文书
2016国培学习心得体会
2016/01/08 职场文书
详解python中[-1]、[:-1]、[::-1]、[n::-1]使用方法
2021/04/25 Python
python自动化测试之Selenium详解
2022/03/13 Python