参考 vue 2.2.6版本

/* @flow */
//引入订阅者模式
import Dep from './dep'

import { arrayMethods } from './array'
import {
def,
isObject,
isPlainObject,
hasProto,
hasOwn,
warn,
isServerRendering
} from '../util/index' const arrayKeys = Object.getOwnPropertyNames(arrayMethods) /**
* By default, when a reactive property is set, the new value is
* also converted to become reactive. However when passing down props,
* we don't want to force conversion because the value may be a nested value
* under a frozen data structure. Converting it would defeat the optimization. 默认新的值也会被definePropty, 但是有些情况是不希望被转换的, 比如 frozen的属性, 实际不需要监听, 如果强制转换, 会破坏做的性能优化, 所以这里监听做成可配置
*/
export const observerState = {
shouldConvert: true,
isSettingProps: false
} /**
* Observer class that are attached to each observed
* object. Once attached, the observer converts target
* object's property keys into getter/setters that
* collect dependencies and dispatches updates. 每个监听对象在Observer里面被监听, 对象属性进入 getter setter方法后, 会收集依赖项 和 触发更新也的回调方法
*/
export class Observer {
value: any;
dep: Dep;
vmCount: number; // number of vms that has this object as root $data constructor (value: any) {
this.value = value
 
  // 监听的是 obj = {a:{}}, 这里的观察是整个对象 obj, 而不是某个属性的变化

   /*
    举个列子 data: { a:{b:{c:1}} } var w1 = this.$watch('a.b.c', fn1); 初始化的时候, 会把这个data生成 6 个dep, 其中三个是这个方法生成的, 另外三个是下面那个defineReact方法生成的
    前三个对应的值分别是 {a:{b:{c:1}}} {b:{c:1}} {c:1}
    后三个对应的值分别是 data.a a.b b.c     上面的 w1 这个watcher收集依赖的时候, 会有5个依赖, 分别是 data.a || {b:{c:1}} || a.b || {c:1} || b.c 对应的dep
    
在wathc a.b.c 的时候就会依次去取值 data.a => a.b => b.c, 收集这个三个属性依赖就可以了, 至于为什么要收集前三个对象依赖,
还没有搞清楚, 可能在一些地方会在这三个对象ob中挂载一些东西, 以便发布一些东西.
*/
  this.dep = new Dep() 
  this.vmCount = 0 //value增加一个 属性 __ob__ , 值就是 observer
def(value, '__ob__', this)
if (Array.isArray(value)) {    //有__proto__的浏览器才能监控数组的push pop 方法, 如果没有, 则不能监控
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], obj[keys[i]])
}
} /**
* Observe a list of Array items.
监控数组每个元素, 如果元素也是数组, 又走数组监控的分支
*/
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
} // helpers /**
* Augment an target Object or Array by intercepting
* the prototype chain using __proto__
利用 __proto__ 截断原有的原型链
  相当于
arrA.__proto__ = arrayMethods
这样的话 , 用户使用 arrA.push('haha');
就会沿着 __proto__找到 arrayMethods 属性 __proto__ 的push方法, 因为这个方法被检测了, 所以会触发set方法, 最终触发 notify()
*/
function protoAugment (target, src: Object) {
/* eslint-disable no-proto */
target.__proto__ = src
/* eslint-enable no-proto */
} /**
* Augment an target Object or Array by defining
* hidden properties.
*通过定义隐藏属性来加强一个目标对象 或者 数组
/* istanbul ignore next */
function copyAugment (target: Object, src: Object, keys: Array<string>) {
for (let i = 0, l = keys.length; i < l; i++) {
const key = keys[i]
def(target, key, src[key])
}
} /**
* Attempt to create an observer instance for a value,
* returns the new observer if successfully observed,
* or the existing observer if the value already has one.
如果监控的值已经存在, 返回这个订阅者
*/
export function observe (value: any, asRootData: ?boolean): Observer | void {
if (!isObject(value)) {
return
}
let ob: Observer | void //如果这个值有__ob__, 直接返回
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
ob = value.__ob__
} else if (
   //如果不是服务器 而且 能被监控, 而且是一个数组或者对象, 而且这个对象不是vue实例
observerState.shouldConvert &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
//根据值来创建ob
ob = new Observer(value)
}
if (asRootData && ob) {
ob.vmCount++
}
return ob
} /**
* Define a reactive property on an Object.
为属性添加监控主方法
*/
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: Function
) { //而这个dep是 observe下的某一个属性, 比如监听的是 obj = {a:{}}, 这里的dep是obj的属性 a
const dep = new Dep()
 
//获取Object的get set函数 
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

//如果val也是一个对象, 而且监控成功, 返回 一个observe实例
let childOb = observe(val)
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 = observe(newVal)
dep.notify()
}
})
} /**
* Set a property on an object. Adds the new property and
* triggers change notification if the property doesn't
* already exist.
对象添加一个属性, 如果新的属性, 触发通知, 数组的$set 方法 其实就是通过splice方法触发的页面变化
*/
export function set (target: Array<any> | Object, key: any, val: any): any {
if (Array.isArray(target) && typeof key === 'number') {
target.length = Math.max(target.length, key)
target.splice(key, 1, val)
return val
}
if (hasOwn(target, key)) {
target[key] = val
return val
}
const ob = (target : any).__ob__
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid adding reactive properties to a Vue instance or its root $data ' +
'at runtime - declare it upfront in the data option.'
)
return val
}
if (!ob) {
target[key] = val
return val
}
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
} /**
* Delete a property and trigger change if necessary.
Vue.$remove 方法触发页面变化的来源
*/
export function del (target: Array<any> | Object, key: any) {
//判断如果是数组
if (Array.isArray(target) && typeof key === 'number') {
target.splice(key, 1)
return
}
const ob = (target : any).__ob__
//避免删除vue实例
if (target._isVue || (ob && ob.vmCount)) {
process.env.NODE_ENV !== 'production' && warn(
'Avoid deleting properties on a Vue instance or its root $data ' +
'- just set it to null.'
)
return
}
if (!hasOwn(target, key)) {
return
}
//如果是对象
delete target[key]
if (!ob) {
return
}
//触发通知
ob.dep.notify()
}

/**
* Collect dependencies on array elements when the array is touched, since
* we cannot intercept array element access like property getters.
*/
function dependArray (value: Array<any>) {
for (let e, i = 0, l = value.length; i < l; i++) {
e = value[i]
e && e.__ob__ && e.__ob__.dep.depend()
if (Array.isArray(e)) {
dependArray(e)
}
}
}

vue.js 源代码学习笔记 ----- observe的更多相关文章

  1. vue.js 源代码学习笔记 ----- 工具方法 env

    /* @flow */ /* globals MutationObserver */ import { noop } from 'shared/util' // can we use __proto_ ...

  2. vue.js 源代码学习笔记 ----- html-parse.js

    /** * Not type-checking this file because it's mostly vendor code. */ /*! * HTML Parser By John Resi ...

  3. vue.js 源代码学习笔记 ----- instance state

    /* @flow */ import Dep from '../observer/dep' import Watcher from '../observer/watcher' import { set ...

  4. vue.js 源代码学习笔记 ----- 工具方法 lang

    /* @flow */ // Object.freeze 使得这个对象不能增加属性, 修改属性, 这样就保证了这个对象在任何时候都是空的 export const emptyObject = Obje ...

  5. vue.js 源代码学习笔记 ----- helpers.js

    /* @flow */ import { parseFilters } from './parser/filter-parser' export function baseWarn (msg: str ...

  6. vue.js 源代码学习笔记 ----- instance render

    /* @flow */ import { warn, nextTick, toNumber, _toString, looseEqual, emptyObject, handleError, loos ...

  7. vue.js 源代码学习笔记 ----- instance event

    /* @flow */ import { updateListeners } from '../vdom/helpers/index' import { toArray, tip, hyphenate ...

  8. vue.js 源代码学习笔记 ----- instance init

    /* @flow */ import config from '../config' import { initProxy } from './proxy' import { initState } ...

  9. vue.js 源代码学习笔记 ----- instance index

    import { initMixin } from './init' import { stateMixin } from './state' import { renderMixin } from ...

随机推荐

  1. Apache 部署HTTPS

    Apache 部署HTTPS 系统:Linux Centos 7.4 x64 应用:Apache 2.4.6 需要安装:mod_ssl 注:确认开启 Include conf/extra/httpd- ...

  2. 关于MVC 中EF调用存储过程

    Entity Framework 4.3 中使用存储过程 分类:ASP.NET MVC 3, ASP.NET                  0                   尽管 Entit ...

  3. cl.exe 命令行编译sqlite3 sqlite3.dll及sqlite3.exe

    有点被宇宙最强的ide惯坏了,封装的太好,不能像gcc那样一步步了解其原理,其实强大的vs背后也有类似gcc的cl.exe 看到How To Compile SQLite http://sqlite. ...

  4. 20145324 Java实验四

    在IDEA上操作 由于不会创建安卓模拟器失败 选择老师给的插件 成功 实验总结 开始开发安卓,感觉更难了,这次实验完全是看运气拼电脑的实验! 步骤 耗时 百分比 需求分析 10m 17% 设计 20m ...

  5. linux 第三周

    linux内核目录结构 arch目录包括了所有和体系结构相关的核心代码.它下面的每一个子目录都代表一种Linux支持的体系结构,例如i386就是Intel CPU及与之相兼容体系结构的子目录.PC机一 ...

  6. 网络攻防工具介绍——Metasploit

    Metasploit 简介 Metasploit是一款开源的安全漏洞检测工具,可以帮助安全和IT专业人士识别安全性问题,验证漏洞的缓解措施,并管理专家驱动的安全性进行评估,提供真正的安全风险情报.这些 ...

  7. MR案例:小文件合并SequeceFile

    SequeceFile是Hadoop API提供的一种二进制文件支持.这种二进制文件直接将<key, value>对序列化到文件中.可以使用这种文件对小文件合并,即将文件名作为key,文件 ...

  8. rocketmq总结(消息的高可用、中间件选型)

    rocketmq总结(消息的高可用.中间件选型) 参考: https://blog.csdn.net/meilong_whpu/article/details/76922456 http://blog ...

  9. pt-table-sync修复mysql主从不一致的数据

    pt-table-sync简介 顾名思义,它用来修复多个实例之间数据的不一致.它可以让主从的数据修复到最终一致,也可以使通过应用双写或多写的多个不相关的数据库实例修复到一致.同时它还内部集成了pt-t ...

  10. 初识numpy的多维数组对象ndarray

    PS:内容来源于<利用Python进行数据分析> 一.创建ndarray 1.array :将一个序列(嵌套序列)转换为一个数组(多维数组) In[2]: import numpy as ...