Vue核心之数据劫持
前瞻
当前前端界空前繁荣,各种框架横空出世,包括各类mvvm框架横行霸道,比如Anglar,Regular,Vue,React等等,它们最大的优点就是可以实现数据绑定,再也不需要手动进行DOM操作了,它们实现的原理也基本上是脏检查或数据劫持。那么本文就以Vue框架出发,探索其中数据劫持的奥秘(本文所选取的相关代码源自于Vue v2.0.3版本的源码)。
什么是数据劫持
首先我们应该搞清楚什么是数据劫持,说白了就是通过Object.defineProperty()来劫持对象属性的setter和getter操作,在数据变动时做你想要做的事情,举个栗子:
var data = {
name:'lhl'
}
Object.keys(data).forEach(function(key){
Object.defineProperty(data,key,{
enumerable:true,
configurable:true,
get:function(){
console.log('get');
},
set:function(){
console.log('监听到数据发生了变化');
}
})
});
data.name //控制台会打印出 “get”
data.name = 'hxx' //控制台会打印出 "监听到数据发生了变化"
上面的这个栗子可以看出,我们完全可以控制对象属性的设置和读取。在Vue中,作者在很多地方都非常巧妙的运用了defineProperty这个方法,具体用在哪里并且它又解决了哪些问题,下面做详细的介绍:
监听对象属性的变化
这个应该是Vue非常重要的一块,其主要思想是observer每个对象的属性,添加到订阅器dep中,当数据发生变化的时候发出notice通知。 相关源代码如下:(作者采用的是ES6+flow写的,代码在src/core/observer/index.js模块里面)
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: Function
) {
const dep = new Dep()//创建订阅对象
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
const getter = property && property.get
const setter = property && property.set
let childOb = observe(val)//创建一个观察者对象
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
//这里也是作者一个巧妙设计,在创建watcher实例的时候,通过调用对象的get方法往订阅器 dep上添加这个创建的watcher实例
if (Dep.target) {
dep.depend()
if (childOb) {
childOb.dep.depend()
}
if (Array.isArray(value)) {
dependArray(value)
}
}
return value
},
set: function reactiveSetter (newVal) {
const value = getter ? getter.call(obj) : val
if (newVal === value) {
return
}
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = observe(newVal)//继续监听新的属性值
dep.notify()//这个是真正劫持的目的,要对订阅者发通知了
}
})
}
以上是监听对象属性的变化,那么下面再看看如何监听数组的变化:
监听数组的变化
const arrayProto = Array.prototype
export const arrayMethods = Object.create(arrayProto)
;[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
.forEach(function (method) {
// cache original method
const original = arrayProto[method]
def(arrayMethods, method, function mutator () {
// avoid leaking arguments:
// http://jsperf.com/closure-with-arguments
let i = arguments.length
const args = new Array(i)
while (i--) {
args[i] = arguments[i]
}
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
inserted = args
break
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted)
// notify change
ob.dep.notify()
return result
})
})
...
/**
* Define a property.
*/
function def (obj, key, val, enumerable) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
});
}
通过上面的代码可以看出Vue是通过修改了数组的几个操作的原型来实现的。
实现对象属性代理
正常情况下我们是这样实例化一个Vue对象:
var VM = new Vue({
data:{
name:'lhl'
},
el:'#id'
})
按理说我们操作数据的时候应该是VM.data.name = ‘hxx’才对,但是作者觉得这样不够简洁,所以又通过代理的方式实现了VM.name = ‘hxx’的可能。 相关代码如下:
function proxy (vm, key) {
if (!isReserved(key)) {
Object.defineProperty(vm, key, {
configurable: true,
enumerable: true,
get: function proxyGetter () {
return vm._data[key]
},
set: function proxySetter (val) {
vm._data[key] = val;
}
});
}
}
表面上看起来我们是在操作VM.name,实际上还是通过Object.defineProperty()中的get和set方法劫持实现的。
总结
Vue框架很好的利用了Object.defineProperty()这个方法来实现了数据的监听和修改,同时也达到了很好的模块间解耦,在日常开发用好这个方法说不定会达到令人意想不到的结果。
本篇文章是我对Vue的浅薄之悟,如有理解不足之处,还请大家批评指正,Thank you ~
Vue核心之数据劫持的更多相关文章
- Vue 核心之数据劫持
前端界空前繁荣,各种框架横空出世,包括各类mvvm框架横行霸道,比如Angular.Regular.Vue.React等等,它们最大的优点就是可以实现数据绑定,再也不需要手动进行DOM操作了,它们实现 ...
- Vue框架核心之数据劫持
本文来自网易云社区. 前瞻 当前前端界空前繁荣,各种框架横空出世,包括各类mvvm框架横行霸道,比如Angular.Regular.Vue.React等等,它们最大的优点就是可以实现数据绑定,再也不需 ...
- Vue之九数据劫持实现MVVM的数据双向绑定
vue是通过数据劫持的方式来做数据绑定的,其中最核心的方法便是通过Object.defineProperty()来实现对属性的劫持,达到监听数据变动的目的. 如果不熟悉defineProperty,猛 ...
- vue双向绑定(数据劫持+发布者-订阅者模式)
参考文献:https://www.cnblogs.com/libin-1/p/6893712.html 实现mvvm主要包含两个方面,数据变化更新视图,视图变化更新数据. 关键点在于data如何更新v ...
- 手写vue observe数据劫持
实现代码: class Vue { constructor(options) { //缓存参数 this.$options = options; //需要监听的数据 this.$data = opti ...
- [Vue源码]一起来学Vue双向绑定原理-数据劫持和发布订阅
有一段时间没有更新技术博文了,因为这段时间埋下头来看Vue源码了.本文我们一起通过学习双向绑定原理来分析Vue源码.预计接下来会围绕Vue源码来整理一些文章,如下. 一起来学Vue双向绑定原理-数据劫 ...
- 直播课(1)如何通过数据劫持实现Vue(mvvm)框架
19.6.28更新: 这篇博客比较完善:将每一部分都分装在单独的js文件中: 剖析Vue原理&实现双向绑定MVVM 半个月前看的直播课,现在才自己敲了一遍,罪过罪过 预览: 思路: 简单实现V ...
- 搞懂:MVVM模型以及VUE中的数据绑定数据劫持发布订阅模式
搞懂:MVVM模式和Vue中的MVVM模式 MVVM MVVM : model - view - viewmodel的缩写,说都能直接说出来 model:模型,view:视图,view-Model:视 ...
- ES6入门:数据劫持、Proxy、Reflect
什么是数据劫持 Object数据劫持实现原理 Array数据劫持的实现原理 Proxy.Reflect 一.什么是数据劫持 定义:访问或者修改对象的某个属性时,在访问和修改属性值时,除了执行基本的数据 ...
随机推荐
- DNA sequence(映射+BFS)
Problem Description The twenty-first century is a biology-technology developing century. We know tha ...
- NPOI导出数值格式设置(我是保留四位小数,不足补0)
看了网上好多帖子,都是保留两位小数的,写法是: HSSFDataFormat.GetBuiltinFormat("0.00"); 于是想四位小数,就是多加两个00,变成: HSSF ...
- python小基础
1.计算机基础知识 中央处理器 CPU 人的大脑 内存 缓存数据 临时记忆 硬盘 储存数据 永久记忆 什么是操作系统 ? 控制计算机工作的流程 什么是应用程序? 安装在操作系统之上的软件 2.pyth ...
- [Advanced Python] 12 - Interview Quiz
第一步.大扫荡复习 Resource: https://www.liaoxuefeng.com/wiki/1016959663602400/1016959735620448 IDE:https://r ...
- redis实现网关限流(限制API调用次数1000次/分)
添加maven依赖,使用springboot2.x版本 <dependency> <groupId>org.springframework.boot</groupId&g ...
- jupyter编辑快捷键
Jupyter笔记本有两种不同的键盘输入模式. 编辑模式允许您将代码或文本输入到一个单元格中,并通过一个绿色的单元格来表示 命令模式将键盘与笔记本级命令绑定在一起,并通过一个灰色的单元格边界显示,该边 ...
- Debezium SQL Server Source Connector+Kafka+Spark+MySQL 实时数据处理
写在前面 前段时间在实时获取SQLServer数据库变化时候,整个过程可谓是坎坷.然后就想在这里记录一下. 本文的技术栈: Debezium SQL Server Source Connector+K ...
- 用 CocosCreator 快速开发推箱子游戏
游戏总共分为4个功能模块: - 开始游戏(menuLayer) - 关卡选择(levelLayer) - 游戏(gameLayer) - 游戏结算(gameOverLayer) Creator内组件效 ...
- 使用.net core3.0 正式版创建Winform程序
前阵子一直期待.net core3.0正式版本的出来,以为这个版本出来,Winform程序又迎来一次新生了,不过9.23日出来的马上下载更新VS,创建新的.net core Winform项目,发现并 ...
- layer.open打开一个新的jsp页面,如何关闭并刷新父页面问题
layer.open打开一个新的jsp页面弹框,如何关闭呢? 在新的页面提交完毕之后,关闭并刷新父页面列表. layer.closeAll(); parent.layer.closeAll(); wi ...