参考 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. ONVIF学习-ONVIF开发框架搭建(C++)

    第一步.下载gsoap 从gsoap官网(http://www.genivia.com/products.html#notice)下载最新版gsoap(博主用的是gsoap_2.8.45).gsoap ...

  2. CSS Float(浮动)

    CSS Float(浮动) 一.CSS Float(浮动) CSS 的 Float(浮动),会使元素向左或向右移动,其周围的元素也会重新排列. Float(浮动),往往是用于图像,但它在布局时一样非常 ...

  3. 20145312 实验五 《Java网络编程》

    20145312 实验五<Java网络编程> 一. 实验内容及要求 实验内容: 运行下载的TCP代码,结对进行,一人服务器,一人客户端: 利用加解密代码包,编译运行代码,一人加密,一人解密 ...

  4. 20135320赵瀚青LINUX第一章读书笔记

    第一章-Linux内核简介 Unix的历史 依旧被认为是最强大和最优秀的系统 由一个失败的操作系统Multics中产生 被移植到PDP-11型机中 由其他组织进一步开发 重写了虚拟内存系统,最终官方版 ...

  5. Eclipse安装zylin[转]

    本文转载自:https://blog.csdn.net/dns888222/article/details/9263485 Eclipse安装zylin 在网上搜的是安装页为http://www.zy ...

  6. [BZOJ2733]永无乡

    Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以 ...

  7. ubuntu环境下nginx的编译安装以及相关设置

    一.基本的编译与安装 1.安装依赖项 sudo apt-get update sudo apt-get install build-essential zlib1g-dev libpcre3 libp ...

  8. OAuth客户端调用

    public class OAuthClientTest { private HttpClient _httpClient; public OAuthClientTest() { _httpClien ...

  9. tornado之WebSocket

    WebSocket WebSocket是HTML5规范中新提出的客户端-服务器通讯协议,协议本身使用新的ws://URL格式. WebSocket 是独立的.创建在 TCP 上的协议,和 HTTP 的 ...

  10. 解决 对路径bin\roslyn..的访问被拒绝

    使用visual studio开发,一重新编译就会报错: 对路径“bin\roslyn\System.Reflection.Metadata.dll”的访问被拒绝 一开始的解决办法就是把bin下的文件 ...