Vue2.x双向数据绑定
1.vue双向绑定原理
vue.js 则是采用数据劫持结合发布者-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。我们先来看Object.defineProperty()这个方法:

var obj = {};
Object.defineProperty(obj, 'name', {
get: function() {
console.log('我被获取了')
return val;
},
set: function (newVal) {
console.log('我被设置了')
}
})
obj.name = 'fei';//在给obj设置name属性的时候,触发了set这个方法
var val = obj.name;//在得到obj的name属性,会触发get方法

已经了解到vue是通过数据劫持的方式来做数据绑定的,其中最核心的方法便是通过Object.defineProperty()来实现对属性的劫持,那么在设置或者获取的时候我们就可以在get或者set方法里假如其他的触发函数,达到监听数据变动的目的,无疑这个方法是本文中最重要、最基础的内容之一。
2.实现最简单的双向绑定
我们知道通过Object.defineProperty()可以实现数据劫持,是的属性在赋值的时候触发set方法,

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="demo"></div>
<input type="text" id="inp">
<script>
var obj = {};
var demo = document.querySelector('#demo')
var inp = document.querySelector('#inp')
Object.defineProperty(obj, 'name', {
get: function() {
return val;
},
set: function (newVal) {//当该属性被赋值的时候触发
inp.value = newVal;
demo.innerHTML = newVal;
}
})
inp.addEventListener('input', function(e) {
// 给obj的name属性赋值,进而触发该属性的set方法
obj.name = e.target.value;
});
obj.name = 'fei';//在给obj设置name属性的时候,触发了set这个方法
</script>
</body>
</html>

当然要是这么粗暴,肯定不行,性能会出很多的问题。
3.讲解vue如何实现
先看原理图

3.1 observer用来实现对每个vue中的data中定义的属性循环用Object.defineProperty()实现数据劫持,以便利用其中的setter和getter,然后通知订阅者,订阅者会触发它的update方法,对视图进行更新。
3.2 我们介绍为什么要订阅者,在vue中v-model,v-name,{{}}等都可以对数据进行显示,也就是说假如一个属性都通过这三个指令了,那么每当这个属性改变的时候,相应的这个三个指令的html视图也必须改变,于是vue中就是每当有这样的可能用到双向绑定的指令,就在一个Dep中增加一个订阅者,其订阅者只是更新自己的指令对应的数据,也就是v-model='name'和{{name}}有两个对应的订阅者,各自管理自己的地方。每当属性的set方法触发,就循环更新Dep中的订阅者。
4.vue代码实现
4.1 observer实现,主要是给每个vue的属性用Object.defineProperty(),代码如下:

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

4.2实现compile:
compile的目的就是解析各种指令称真正的html。

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) {
console.log([child])
self.compileElement(child, vm);
frag.append(child); // 将所有子节点添加到fragment中
}
return frag;
},
compileElement: function(node, vm) {
var reg = /\{\{(.*)\}\}/;
//节点类型为元素(input元素这里)
if(node.nodeType === 1) {
var attr = node.attributes;
// 解析属性
for(var i = 0; i < attr.length; i++ ) {
if(attr[i].nodeName == 'v-model') {//遍历属性节点找到v-model的属性
var name = attr[i].nodeValue; // 获取v-model绑定的属性名
node.addEventListener('input', function(e) {
// 给相应的data属性赋值,进而触发该属性的set方法
vm[name]= e.target.value;
});
new Watcher(vm, node, name, 'value');//创建新的watcher,会触发函数向对应属性的dep数组中添加订阅者,
}
};
}
//节点类型为text
if(node.nodeType === 3) {
if(reg.test(node.nodeValue)) {
var name = RegExp.$1; // 获取匹配到的字符串
name = name.trim();
new Watcher(vm, node, name, 'nodeValue');
}
}
}
}

4.3 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() {
console.log(1)
this.value = this.vm[this.name]; //触发相应属性的get
}
}

4.4 实现Dep来为每个属性添加订阅者

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

这样一来整个数据的双向绑定就完成了。
5.梳理
首先我们为每个vue属性用Object.defineProperty()实现数据劫持,为每个属性分配一个订阅者集合的管理数组dep;然后在编译的时候在该属性的数组dep中添加订阅者,v-model会添加一个订阅者,{{}}也会,v-bind也会,只要用到该属性的指令理论上都会,接着为input会添加监听事件,修改值就会为该属性赋值,触发该属性的set方法,在set方法内通知订阅者数组dep,订阅者数组循环调用各订阅者的update方法更新视图。
Vue2.x双向数据绑定的更多相关文章
- Vue2.0双向数据绑定原理
Object.defineProperty(objectName, key, option); Object.defineProperty()可以直接在一个对象上定义一个新属性, 或者修改一个属性, ...
- vue2组件之间双向数据绑定问题
最近在使用element-ui的dialog组件二次封装成独立组件使用时,子组件需要将关闭dialog状态返回给父组件,简单的说就是要实现父子组件之间的数据双向绑定问题. 大致代码如下: 1,父组件 ...
- vue2.* 双向数据绑定 Vue事件介绍 以及Vue中的ref获取dom节点 04
<template> <div id="app"> <!-- 双向数据绑定(必须在表单里面使用) m:model v:view mvvm:model改 ...
- Vue: 一个简单的Vue2.0 v-model双向数据绑定的实现,含源代码,小白也能看懂
首先说一下原理吧 View层(dom元素)的变动如何响应到Model层(Js变量)呢? 通过监听元素的input事件来动态的改变js变量的值,实际上不是改变的js变量的值,而是改变的js变量的gett ...
- 【vue2】Style和Class,条件,列表渲染,双向数据绑定,事件处理
目录 1.style和class 2. 条件渲染 2.1 指令 2.2 案例 3. 列表渲染 3.1 v-for:放在标签上,可以循环显示多个此标签 3.2 v-for 循环数组,循环字符串,数字,对 ...
- javascript基础修炼(9)——MVVM中双向数据绑定的基本原理
开发者的javascript造诣取决于对[动态]和[异步]这两个词的理解水平. 一. 概述 1.1 MVVM模型 MVVM模型是前端单页面应用中非常重要的模型之一,也是Single Page Appl ...
- Vue学习之路第九篇:双向数据绑定 v-model指令
1.学习准备: ①:双向数据绑定可以简单理解为:后端定义的数据改变,前端页面展示的时候会自动改变,数据通过前端页面修改的时候,后端定义的数据内容也会随之改变. ②:指令中只有v-model可以实现双向 ...
- vue3.0中的双向数据绑定方法
熟悉vue的人都知道在vue2.x之前都是使用object.defineProperty来实现双向数据绑定的 而在vue3.0中这个方法被取代了 1. 为什么要替换Object.definePrope ...
- Vue指令之`v-model`和`双向数据绑定
v-bind 只能实现数据的单向绑定,从 M 自动绑定到 V, 无法实现数据的双向绑定 <input type="text" v-bind:value="msg& ...
随机推荐
- #P2341 [HAOI2006]受欢迎的牛 题解
题目描述 每头奶牛都梦想成为牛棚里的明星.被所有奶牛喜欢的奶牛就是一头明星奶牛.所有奶 牛都是自恋狂,每头奶牛总是喜欢自己的.奶牛之间的“喜欢”是可以传递的——如果A喜 欢B,B喜欢C,那么A也喜欢C ...
- Linux进程管理(二)
目录 Linux进程管理(二) 参考 vmstat命令 top命令 Linux进程管理(二)
- Java 实现简单的人机猜拳游戏
import java.util.Scanner; import java.util.Random; public class TestGuess{ public static void main(S ...
- 10.MongoDB
1.安装(1.1):去官方下载最新的包,http://www.mongodb.org/downloads(1.2):然后tar zvxf 解压(1.3):拷贝到相应的文件夹即可2.在Shell里面启动 ...
- keep-alive的使用
<keep-alive>是Vue的内置组件,能在组件切换过程中将状态保留在内存中,防止重复渲染DOM. <router-view>中间为组件</router-view&g ...
- mysql判断数据库或表是否存在
(1) 判断数据库存在, 则删除: drop database if exists db_name;(2) 判断数据表存在, 则删除: drop table if exists table ...
- Acwing779 最长公共字符串后缀
题目大意:给定n个字符串,让你找到他们的最长公共字符串后缀是什么,可能为空. 分析:题目数据范围比较小,可以O(n*n)暴力匹配,即可解决这道问题.之所以写这道题的题解还是因为写字符串的题还不够多啊, ...
- SpringBoot之Feign调用方式比较
一:事发原因 两个东家都使用SpringCloud,巴拉巴拉用上了Spring全家桶,从eureka到ribbon,从ribbon到feign,从feign到hystrix,然后在使用feign的时候 ...
- JS截取腾讯视频和去除视频广告
一:腾讯视频截取 H5视频播放除了video标签以外,还有iframe嵌套视频 项目需求是用户输入腾讯视频的html链接,如 https://v.qq.com/x/page/y0116k2vspw.h ...
- CSS元素和文本垂直居中
div居中 1.使用绝对定位和负外边距让块级元素垂直居中 要点:必须提前知道被居中块级元素的尺寸,否则无法准确实现垂直居中. <div id="box"> <di ...