vue 数据劫持 响应式原理 Observer Dep Watcher
1、vue响应式原理流程图概览
2、具体流程
(1)vue示例初始化(源码位于instance/index.js)
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index' function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
} initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue) export default Vue
响应式相关的是“stateMixin”。
(2)、state.js(源码位于instance/state.js)
与响应式有关的是:
function initData (vm: Component) {
let data = vm.$options.data
data = vm._data = typeof data === 'function'
? getData(data, vm)
: data || {}
if (!isPlainObject(data)) {
data = {}
process.env.NODE_ENV !== 'production' && warn(
'data functions should return an object:\n' +
'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
vm
)
}
// proxy data on instance
const keys = Object.keys(data)
const props = vm.$options.props
const methods = vm.$options.methods
let i = keys.length
while (i--) {
const key = keys[i]
if (process.env.NODE_ENV !== 'production') {
if (methods && hasOwn(methods, key)) {
warn(
`Method "${key}" has already been defined as a data property.`,
vm
)
}
}
if (props && hasOwn(props, key)) {
process.env.NODE_ENV !== 'production' && warn(
`The data property "${key}" is already declared as a prop. ` +
`Use prop default value instead.`,
vm
)
} else if (!isReserved(key)) {
proxy(vm, `_data`, key)
}
}
// observe data
observe(data, true /* asRootData */)
}
在initData中实现了2个功能:
(2).1 将data中的对象代理(proxy)到_data上
说明proxy函数也是使用的Object.defineProperty,
export function proxy (target: Object, sourceKey: string, key: string) {
sharedPropertyDefinition.get = function proxyGetter () {
return this[sourceKey][key]
}
sharedPropertyDefinition.set = function proxySetter (val) {
this[sourceKey][key] = val
}
Object.defineProperty(target, key, sharedPropertyDefinition)
}
也就是说vm._data.变量都是响应式数据(即vm.变量)。
(2).2 将data中的数据变为响应式数据,即
// observe data
observe(data, true /* asRootData */)
(3)observe类
第(2)步的observe函数:
export function observe (value: any, asRootData: ?boolean): Observer | void {
if (!isObject(value) || value instanceof VNode) {
return
}
let ob: Observer | void
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
shouldObserve &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
}
调用了Observer类:
现在看Observer的构造函数和walk方法:
constructor (value: any) {
this.value = value
this.dep = new Dep()
this.vmCount = 0
def(value, '__ob__', this)
if (Array.isArray(value)) {
const augment = hasProto
? protoAugment
: copyAugment
augment(value, arrayMethods, arrayKeys)
this.observeArray(value)
} else {
this.walk(value)
}
} /**
* Walk through each property and convert them into
* getter/setters. This method should only be called when
* value type is Object.
*/
walk (obj: Object) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
需要说明的是,并不是data中的所有数据都会变成响应式的。
请看例子:
new Vue({
template:
`<div>
<span>text1:</span> {{text1}}
<span>text2:</span> {{text2}}
<div>`,
data: {
text1: 'text1',
text2: 'text2',
text3: 'text3'
}
});
data中text3并没有被模板实际用到,为了提高代码执行效率,我们没有必要对其进行响应式处理,因此,依赖收集简单点理解就是收集只在实际页面中用到的data数据,即text1和text2。
2019.3.8 修正
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {
const value = getter ? getter.call(obj) : val
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
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (process.env.NODE_ENV !== 'production' && customSetter) {
customSetter()
}
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
childOb = !shallow && observe(newVal)
dep.notify()
}
defineReactive函数中会用到Dep类来收集依赖(dep.depend())以及当数据变化时触发更新(dep.notify())。
(4)Dep类
export default class Dep {
static target: ?Watcher;
id: number;
subs: Array<Watcher>; constructor () {
this.id = uid++
this.subs = []
} addSub (sub: Watcher) {
this.subs.push(sub)
} removeSub (sub: Watcher) {
remove(this.subs, sub)
} depend () {
if (Dep.target) {
Dep.target.addDep(this)
}
} notify () {
// stabilize the subscriber list first
const subs = this.subs.slice()
if (process.env.NODE_ENV !== 'production' && !config.async) {
// subs aren't sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort((a, b) => a.id - b.id)
}
for (let i = 0, l = subs.length; i < l; i++) {
subs[i].update()
}
}
}
Dep类的构造函数中的subs是Watcher(观察者)类。vue实例中data的一个值,可以添加多个Watcher,同时这个值变化的时候也是触发这多个Watcher的更新。
(5)Watcher类
Watcher类主要用来收集依赖和触发更新。
Watcher类也是实现了$watch(),即:https://cn.vuejs.org/v2/api/#watch
(6)Observer、Dep和Watcher类关系
Observer类是书店(vue实例的data对象),里面有好多书(Dep类),每本书可以被订阅(Watcher类)。
当某一本书更新时,订阅的Watcher类会收到通知,进而更新书店内容(vue实例的data对象)。
Dep类是Observer类和Watcher类链接的桥梁。
vue 数据劫持 响应式原理 Observer Dep Watcher的更多相关文章
- Vue数据绑定和响应式原理
Vue数据绑定和响应式原理 当实例化一个Vue构造函数,会执行 Vue 的 init 方法,在 init 方法中主要执行三部分内容,一是初始化环境变量,而是处理 Vue 组件数据,三是解析挂载组件.以 ...
- Vue 2.0 与 Vue 3.0 响应式原理比较
Vue 2.0 的响应式是基于Object.defineProperty实现的 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 prop ...
- 手写实现vue的MVVM响应式原理
文中应用到的数据名词: MVVM ------------------ 视图-----模型----视图模型 三者与 Vue 的对应:view 对应 te ...
- 学习 vue 源码 -- 响应式原理
概述 由于刚开始学习 vue 源码,而且水平有限,有理解或表述的不对的地方,还请不吝指教. vue 主要通过 Watcher.Dep 和 Observer 三个类来实现响应式视图.另外还有一个 sch ...
- vue学习之响应式原理的demo实现
Vue.js 核心: 1.响应式的数据绑定系统 2.组件系统. 访问器属性 访问器属性是对象中的一种特殊属性,它不能直接在对象中设置,而必须通过 defineProperty() 方法单独定义. va ...
- vue核心之响应式原理(双向绑定/数据驱动)
实例化一个vue对象时, Observer类将每个目标对象(即data)的键值转换成getter/setter形式,用于进行依赖收集以及调度更新. Observer src/core/observer ...
- vue2双向绑定原理:深入响应式原理defineProperty、watcher、get、set
响应式是什么?Vue 最独特的特性之一- 就是我们在页面开发时,修改data值的时候,数据.视图页面需要变化的地方变化. 主要使用到哪些方法? 用 Object.defineProperty给watc ...
- Vue.js响应式原理
写在前面 因为对Vue.js很感兴趣,而且平时工作的技术栈也是Vue.js,这几个月花了些时间研究学习了一下Vue.js源码,并做了总结与输出. 文章的原地址:answershuto/learnV ...
- Vue 数据响应式原理
Vue 数据响应式原理 Vue.js 的核心包括一套“响应式系统”.“响应式”,是指当数据改变后,Vue 会通知到使用该数据的代码.例如,视图渲染中使用了数据,数据改变后,视图也会自动更新. 举个简单 ...
随机推荐
- struts2对拦截器使用带实例
拦截器是struts2的核心.拦截器可以拦截请求,控制视图的走向.那么怎么来实现自定义的拦截器呢? 这里我们做一个例子. 首先假现在做了两个jsp页面一个是登陆的信息的(用session来模拟),一个 ...
- CSS之BFC、IFC、FFC and GFC
CSS之BFC.IFC.FFC and GFC 什么是FC? BFC(Block Formatting Contexts) BFC的布局规则: 如何生成BFC: IFC(Inline Formatti ...
- 图片碎片化mask动画
图片碎片化mask动画 效果 源码 https://github.com/YouXianMing/Animations // // TransformFadeViewController.m // A ...
- pgm转jpg
clc;clear all;for i=1:40for j=1:10image=imread(strcat('N:\FACE\orl_faces\s',...int2str(i),'\',int2st ...
- P值(P-value),“差异具有显著性”和“具有显著差异”
郑冰刚提到P值,说P值的定义(着重号是笔者加的,英文是从WikiPedia摘来的): P值就是当原假设为真时,比所得到的样本观察结果更极端的结果出现的概率. The P-value is the pr ...
- Nginx缓存使用官方教程及常见问题解答
原文地址:http://www.kuqin.com/shuoit/20150804/347388.html 我们都知道,应用程序和网站一样,其性能关乎生存.但如何使你的应用程序或者网站性能更好,并没有 ...
- Asp.Net Core App 部署故障示例 2
相关阅读:Windows + IIS 环境部署Asp.Net Core App 1. HTTP Error 502.5 – Process Failure 环境 Windows Server 201 ...
- cocos2d js ScrollView的使用方法
游戏中非常多须要用到ScrollView的情况,也就是须要滚动一片区域. 这里有两种实现方法,一种是使用cocos studio的方式,另外一种是手写代码.先看第一种 第一种记得在设置滚动区域时选取裁 ...
- OpenCV学习(35) OpenCV中的PCA算法
PCA算法的基本原理可以参考:http://www.cnblogs.com/mikewolf2002/p/3429711.html 对一副宽p.高q的二维灰度图,要完整表示该图像,需要m = ...
- OTL翻译(6) -- otl_connect类
otl_connect 这个类封装了连接的功能,如连接.断开连接.提交.回滚等.otl_connect也就是一个用来创建连接对象并进行管理的类. 序号 方法.变量 说明 1 int connected ...