之前有看过一次vue双向绑定原理实现相关的博客,看得似懂非懂的,然后也就搁浅了。
昨天脑海里又突然燃起了要不这块搞懂的冲动,于是乎又开始了一轮博客轰炸,综合研究了多位大神写得关于vue双向绑定的实现原理,然后结合自己的一些理解,就有了这篇文章了。
关于双向绑定,我所知的两类:(小的不才,目前只接触过vue和angular)
一是angular1的脏检查机制
二是vue的数据劫持配合观察者模式(网上有很多写的都是订阅-发布模式,我特意去查看了这两种模式有什么区别,大致是说观察者模式中观察者是被动接收统一的消息,而订阅-发布模式中是订阅者是可以自定义接收行为的,而vue中watcher对象中update方法都是一致的,就是同步数据,所有鄙人觉得用观察者模式来形容可能会更贴切些,如果理解的不对,欢迎大家轻吐~)
下面我先贴一张我整理的原理图


我的理解:
1.编译器会解析DOM元素,比如碰到v-model指令时,会对该DOM元素添加input监听事件,当事件发生时,给data属性值赋值,进而触发该属性的setter方法(这样就实现了从view到model的同步);除了增加监听事件外,还会实例化一个watch对象。
2.实例化一个watch对象主要有两个作用,一个是通过调用data元素的getter方法,触发Dep的add方法将watcher对象加入到订阅器中(为了防止在除watch对象中其他其他调用了getter方法,进而重复添加监听器,我们在watcher对象中给Dep添加一个不为空全局变量,当全局变量不为空时才添加,添加完后又将全局变量置为null),第二个作用是声明一个update方法,用来接收到订阅器的通知后同步数据给节点进行渲染。
3.上面我们已经在属性的getter方法中将watcher对象加入了订阅器,当model层属性值发生变化时会触发setter方法,我们在setter中再去触发订阅器的notify,并在notify中触发watcher对象的update方法(这样就实现了从model到view的同步)
从图中也可看出,主要有4大块,我们依照流程图的顺序来分析一下:

  • compile
    先撸一波
function Compile(node, vm) {if(node) {this.$frag = this.nodeToFragment(node, vm);return this.$frag;}}Compile.prototype = {nodeToFragment: function(node, vm) {var self = this;var frag = document.createDocumentFragment();var child;while(child = node.firstChild) {self.compileElement(child, vm);frag.append(child); // 将所有子节点添加到fragment中}return frag;},compileElement: function(node, vm) {var reg = /\{\{(.*)\}\}/;//节点类型为元素if(node.nodeType === 1) {var attr = node.attributes;// 解析属性for(var i = 0; i < attr.length; i++ ) {if(attr[i].nodeName == 'v-model') {var name = attr[i].nodeValue; // 获取v-model绑定的属性名node.addEventListener('input', function(e) {// 给相应的data属性赋值,进而触发该属性的set方法vm[name]= e.target.value;});// node.value = vm[name]; // 将data的值赋给该nodenew Watcher(vm, node, name, 'value');}};}//节点类型为textif(node.nodeType === 3) {if(reg.test(node.nodeValue)) {var name = RegExp.$1; // 获取匹配到的字符串name = name.trim();// node.nodeValue = vm[name]; // 将data的值赋给该nodenew Watcher(vm, node, name, 'nodeValue');}}},}

代码分析:
(1)使用 DocumentFragment 处理节点,速度和性能远远优于直接操作 DOM。Vue 进行编译时,就是将挂载目标的所有子节点劫持(真的是劫持,通过 append 方法,DOM 中的节点会被自动删除)到 DocumentFragment 中,经过一番处理后,再将 DocumentFragment 整体返回插入挂载目标。
(2)node.nodeType判断节点类型,如果是元素的话,判断该元素有没有v-model
属性,有则监听input事件,将输入框的值同步到变量中,同时实例化一个watcher。如果是文本的话,看是不是符合{{}}正则表达式,符合则是我们需要加入订阅器的对象。

  • watcher-观察者
function Watcher(vm, node, name, type) {Dep.target = this;this.name = name;this.node = node;this.vm = vm;this.type = type;this.update();Dep.target = null;}Watcher.prototype = {update: function() {this.get();this.node[this.type] = this.value; // 订阅者执行相应操作},// 获取data的属性值get: function() {this.value = this.vm[this.name]; //触发相应属性的get}}

代码分析:
(1)实例化watcher对象是有获取对象的值this.vm[this.name],这对出发该对象的get方法,而在对象的get方法中就可以把这个观察者加入到Dep中了,后面oberver可以看到
(2)update()方法就是观察者接收到通知后用来同步数据给节点进行渲染的作用。

  • Observe-数据监测器
function defineReactive (obj, key, val) {var dep = new Dep();Object.defineProperty(obj, key, {get: function() {//添加订阅者watcher到主题对象Depif(Dep.target) {// JS的浏览器单线程特性,保证这个全局变量在同一时间内,只会有同一个监听器使用dep.addSub(Dep.target);}return val;},set: function (newVal) {if(newVal === val) return;val = newVal;console.log(val);// 作为发布者发出通知dep.notify();}})}function observe(obj, vm) {Object.keys(obj).forEach(function(key) {defineReactive(vm, key, obj[key]);})}

代码分析:
(1)数据监测器用到的知识点就是ES5的Object.defineProperty方法,改写对象的set和get方法,在set方法中通知观察该对象的所有watcher更新,在get方法中实现的是当watcher第一次调用get方法的时候把自己绑定给订阅器进行管理

  • Dep-订阅器
function Dep() {this.subs = [];}Dep.prototype = {addSub: function(sub) {this.subs.push(sub);},notify: function() {this.subs.forEach(function(sub) {sub.update();})}}

(1)它的addsub方法在observer的get方法中被调用,notify方法在observer的set方法中被调用
至此,四大块就分析完了,回过头来再去看那张图,你理解的会更深刻一点。
下面终极大boss出场了MVVM.js

function Vue(options) {this.data = options.data;var data = this.data;observe(data, this);var id = options.el;var dom =new Compile(document.getElementById(id),this);// 编译完成后,将dom返回到app中document.getElementById(id).appendChild(dom);}

index.html

<!DOCTYPE html><head></head><body><div id="app"><input type="text" id="a" v-model="text">{{text}}</div><script src="src/Dep.js"></script><script src="src/Observe.js"></script><script src="src/Watcher.js"></script><script src="src/Compile.js"></script><script src="src/MVVM.js"></script><script>var vm = new Vue({el: 'app',data: {text: 'hello world'}});</script></body>
</html>

代码分析:
这里自定义了vue对象,解析包含的html片段,实现的双向绑定。

··························分隔符···························
参考资料:
http://www.cnblogs.com/kidney/p/6052935.html?utm_source=gold_browser_extension

记一次对vue双向绑定的理解相关推荐

  1. 对双向绑定的理解和总结

    主流的mvc(vm)框架都实现了单向数据绑定,即数据(model)变化主动触发ui(view)变化 而双向绑定则是,数据(model)变化主动触发ui(view)变化,同时ui(view)变化主动触发 ...

  2. 梳理vue双向绑定的实现原理

    Vue 采用数据劫持结合发布者-订阅者模式的方式来实现数据的响应式,通过Object.defineProperty来劫持数据的setter,getter,在数据变动时发布消息给订阅者,订阅者收到消息后 ...

  3. vue双向绑定原理源码解析

    当我们学习angular或者vue的时候,其双向绑定为我们开发带来了诸多便捷,今天我们就来分析一下vue双向绑定的原理. 简易vue源码地址:https://github.com/maxlove123 ...

  4. 前端技巧|vue双向绑定原理,助你面试成功

    在面试一些大厂的时候,面试官可能会问到你vue双向数据绑定的原理是什么?有些小伙伴不知道是什么东西,这样你在面试官的眼里就大打折扣了.今天小千就来给大家介绍一下vue的双向绑定原理,千万不要错过啦. ...

  5. Vue双向绑定:原理篇(详细)

    文章目录 前言 什么是响应式 数据劫持 发布者-订阅者模式 模式简介 发布者 Observer 订阅器 dep 订阅者 Watcher 整体流程 初始化data data变为响应式数据 解析模板 收集 ...

  6. vue双向绑定原理及实现

    vue双向绑定原理及实现 一.MVC模式 二.MVVM模式 三.双向绑定原理 1.实现一个Observer 2.实现一个Watcher 3.实现一个Compile 4.实现一个MVVM 四.最后写一个 ...

  7. 【vue双向绑定原理浅析】

    vue双向绑定原理浅析 1.什么是双向绑定? ​ 所谓双向绑定,指的是vue实例中的data与其渲染的DOM元素的内容保持一致,无论谁被改变,另一方会相应的更新为相同的数据.(数据变化更新视图,视图变 ...

  8. vue双向绑定有时候不生效处理办法

    vue双向绑定有时候不生效处理办法 问题:当我们给响应式的对象新增属性时,新增的属性并不会显示到页面中:对于响应式的数组,增加元素.修改数组长度时,数组的这些变化也不会反映到页面中 <templ ...

  9. vue 双向绑定 getter 和 setter

    它的每个属性都有两个相对应的get和set方法,我觉的这是多此一举的,于是去网上查了查Vue双向绑定的实现原理,才发现它和Angular.js双向绑定的实现原理完全不同,Angular是用的数据脏检测 ...

最新文章

  1. 工单系统的设计与实现(3)
  2. Spring 学习教程(一): 认识 Spring 框架
  3. for in for of区别_Python 第4课:for…in循环黄金搭档之range()函数
  4. centos8 apache php,centos6.8安装php7 for Apache2
  5. hacker_Hacker Public Radio上的免费软件
  6. 关于MyEclips新导入项目报错问题,解决方法!(基础)
  7. 正规表达式与有限自动机
  8. logback实现日志按天和大小切分
  9. java实战 ——分类模块的开发
  10. 【转(比较全面)】CSS3弹性盒模型之Flexbox是布局模块box-sizing box-orient box-direction box-ordinal-group
  11. 简明python教程电子书下载_简明Python教程PDF
  12. 软考高项--项目管理概述
  13. 德国自动驾驶与电动出行趋势进展
  14. Excel根据快递单号自动识别快递公司
  15. 程序语言翻译: 2.1在以阶段划分的编译器中,贯穿于编译器工作始终的是( )。2.2 对高级语言程序进行翻译时,源程序中的变量不可能映射到( )
  16. 视频教程-达芬奇DAVINCI 14 影视调色大师全套零基础学习到调色综合案例实战视频教程-其他
  17. 情商高的人所看透的四种人生真相
  18. 一、注册功能怎么测试
  19. Windows11 Windows安全中心无法打开 Windows Defender无法打开
  20. python turtle隐藏画笔_Python turtle库的画笔控制说明

热门文章

  1. 联想小新笔记本尝鲜win11时遇到无wifi按钮的问题及解决办法
  2. css3 中dispaly:none 动画处理
  3. 第六届山东省大学生网络安全技能大赛决赛Writeup
  4. python中指定最后一个字符_如何从Python字符串中删除最后一个字符?
  5. thinkphp源码剖析
  6. spring.profiles.active 配置未生效
  7. 【Active Learning - 03】Adaptive Active Learning for Image Classification
  8. 【数据挖掘】期末复习模拟题(暨考试题)
  9. 简单认识 cookie
  10. 江湖求生如何用电脑玩 江湖求生PC电脑版教程