observe工厂函数

在之前的源码initdata函数中最后一句

observe(data, true /* asRootData */)复制代码

调用了observe函数观察数据

export function observe (value: any, asRootData: ?boolean): Observer | void { if (!isObject(value) || value instanceof VNode) { return //判断是否是一个对象或者VNode
}let ob: Observer | void  //定义变量ob 保存observe实例
if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { ob = value.__ob__ //检测目标对象是否含有_ob_,并且 __ob__ 属性应该是Observer的实例            作用是为了避免重复观察同一个数据对象} 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
}复制代码

看下else..if..第一个条件shouldObserve 必须为true

export let shouldObserve: boolean = true //初始化默认设置为true
export function toggleObserving (value: boolean) {// 接收一个布尔值 改变true false  shouldObserve = value}复制代码

第二个条件!isServerRendering() 必须ture

let _isServer //定义一个变量
export const isServerRendering = () => {if (_isServer === undefined) {/* istanbul ignore if */
if (!inBrowser && !inWeex && typeof global !== 'undefined') { // detect presence of vue-server-renderer and avoid     // Webpack shimming the process
_isServer = global['process'] && global['process'].env.VUE_ENV === 'server'  } else { _isServer = false
}
}  return _isServer}复制代码

这个函数作用是为了判断是否为服务端渲染

第三个条件判断是否为数组和纯对象

第四个判断对象是可拓展性的...接下来就不说了 相信应该能看懂

ob = new Observer(value)复制代码

最后创建一个observer实例

observe构造函数

export class Observer
{
value: any;
dep: Dep; vmCount: number; // number of vms that have this object as root $dataconstructor (value: any) {this.value = value //属性引用了数据对象   this.dep = new Dep() //保存了一个新创建的 Dep 实例对象    this.vmCount = 0 //实例属性设置为0    def(value, '__ob__', this) //为数据对象定义了一个 __ob__ 属性,这个属性的值就是当前 Observer 实例对象,其中 def 函数其实就是 Object.defineProperty 函数的简单封装,
之所以这里使用 def 函数定义 __ob__ 属性是因为这样可以定义不可枚举的属性,
这样后面遍历数据对象的时候就能够防止遍历到 __ob__ 属性
if (Array.isArray(value)) {
//判断是否为数组
if (hasProto) {protoAugment(value, arrayMethods)
} else {copyAugment(value, arrayMethods, arrayKeys)
}      this.observeArray(value)
} else {
//纯对象的情况      this.walk(value)
}
}/**   * Walk through all properties 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])
}
}/**   * Observe a list of Array items.   */
observeArray (items: Array<any>) {for (let i = 0, l = items.length; i < l; i++) {observe(items[i])
}
}
}
复制代码

walk 实例对象方法遍历了变量获得了可枚举的属性,

每个属性调用了defineReactive函数:

export function defineReactive ( obj: Object,key: string,val: any,  customSetter?: ?Function,  shallow?: boolean) {const dep = new Dep() //定义一个dep常量接收Dep实例对象
//getOwnPropertyDescriptor方法返回指定对象上一个自有属性对应的属性描述const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {return //判断是否可配置
}// cater for pre-defined getter/setters
const getter = property && property.get
//保存 property 对象的 get 和 set函数
const setter = property && property.set
if ((!getter || setter) && arguments.length === 2) {val = obj[key]//当只传递两个参数时,说明没有传递第三个参数 val,
那么此时需要根据 key 主动去对象上获取相应的值,即执行 if 语句块内的代码:val = obj[key]
}let childOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
//函数重新定义属性的 setter/getter,这会导致属性原有的 set 和 get 方法被覆盖,
所以要将属性原有的 setter/getter 缓存,并在重新定义的 set 和 get 方法中调用缓存的函数,
从而做到不影响属性的原有读写操作。
enumerable: true,
configurable: true,
get: function reactiveGetter () {const value = getter ? getter.call(obj) : val
//判断是否存在getter?直接调用该函数:使用val
if (Dep.target) {
//Dep.target 中保存的值就是要被收集的依赖(观察者)dep.depend()
//行 dep 对象的 depend 方法将依赖收集到 dep中
if (childOb) {
//大概意思是收集的依赖的触发时机是在使用 $set 或 Vue.set 给数据对象添加新属性时触发
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 //新旧值判断处理 后面判断应该是为了处理NaN
}      /* eslint-enable no-self-compare */     if (process.env.NODE_ENV !== 'production' && customSetter) { customSetter()//环境判断和customSetter函数判断(作用:用来打印辅助属性)
}     // #7981: for accessor properties without setter     if (getter && !setter) return     if (setter) {//正确设置属性值        setter.call(obj, newVal)
} else { val = newVal
}     childOb = !shallow && observe(newVal)     dep.notify()//深度观测 依赖收集
}
})
}复制代码

defineReactive 函数的核心就是 将数据对象的数据属性转换为访问器属性,即为数据对象的属性设置一对 getter/setter,但其中做了很多处理边界条件的工作defineReactive 接收五个参数,但是在 walk 方法中调用 defineReactive 函数时只传递了前两个参数,即数据对象和属性的键名

转载于:https://juejin.im/post/5ca5b4555188251041397735

vue[源码]你不知道的observe!相关推荐

  1. vue源码解析之observe

    一. vue文档中有"由于 JavaScript 的限制,Vue 不能检测以下数组的变动",是否真是由于JavaScript的限制,还是出于其他原因考虑 当你利用索引直接设置一个数 ...

  2. 深入剖析Vue源码 - 响应式系统构建(上)

    从这一小节开始,正式进入Vue源码的核心,也是难点之一,响应式系统的构建.这一节将作为分析响应式构建过程源码的入门,主要分为两大块,第一块是针对响应式数据props,methods,data,comp ...

  3. vue源码-对于「计算属性」的理解

    vue源码-对于「计算属性」的理解 这是我最近学习vue源码的一个个人总结和理解,所以可能并不适合每一位读者 本文的整体脉络如下,首先尽可能去掉细节,对计算属性源码的大致实现有一个了解,然后举一例子, ...

  4. vue源码之响应式数据

    分析vue是如何实现数据响应的. 前记 现在回顾一下看数据响应的原因. 之前看了vuex和vue-i18n的源码, 他们都有自己内部的vm, 也就是vue实例. 使用的都是vue的响应式数据特性及$w ...

  5. vue 计算属性_lt;Vue 源码笔记系列6gt;计算属性 computed 的实现

    1. 前言 原文发布在语雀: <Vue 源码笔记系列6>计算属性 computed 的实现 · 语雀​www.yuque.com 上一章我们已经学习过 watch,这一章就来看一下计算属性 ...

  6. 源码解读_入口开始解读Vue源码系列(二)——new Vue 的故事

    作者:muwoo 转发链接:https://github.com/muwoo/blogs/blob/master/src/Vue/2.md 目录 入口开始解读Vue源码系列(一)--造物创世 入口开始 ...

  7. Vue源码学习: 关于对Array的数据侦听

    摘要 我们都知道Vue的响应式是通过Object.defineProperty来进行数据劫持.但是那是针对Object类型可以实现, 如果是数组呢? 通过set/get方式是不行的. 但是Vue作者使 ...

  8. Vue源码学习(三)——数据双向绑定

    在Vue中我们经常修改数据,然后视图就直接修改了,那么这些究竟是怎么实现的呢? 其实Vue使用了E5的语法Object.defineProperty来实现的数据驱动. 那么Object.defineP ...

  9. Vue源码分析 - observer.js

    观察者模式一般包含发布者(Publisher)和订阅者(Subscriber)两种角色:顾名思义发布者负责发布消息,订阅者通过订阅消息响应动作了. 回到Vue中,在Vue源码core/oberver目 ...

最新文章

  1. 8软件遇到的问题及解决方法_问题解决8步法
  2. .NET 将文本转换成语音 (转)
  3. Hive自定义UDF UDAF UDTF
  4. sqlserver免安装_SQL数据分析,如何免安装在线运行?
  5. python高级特性:迭代器与生成器
  6. 操作系统之内存管理:2、内存管理的功能(内存分配回收、扩充、地址转化、存储保护)
  7. 函数调用过程实例详解
  8. Android学习笔记---android平台中利用,SAX解析xml
  9. SaaS架构设计之高性能的Multi-Tenant最佳实践
  10. 十月微信小程序导航:官方文档+精品教程+demo集合(10月14日更新)
  11. Navicat操作数据库
  12. EAS BOS 序时簿做组织单元隔离
  13. 手把手教你十分钟学会使用小程序云存储
  14. SFP+光纤模块使用
  15. 把nasm集成到Visual studio 2013中
  16. macOS配置MAVEN环境变量执行source .bash_profile报错.bash_profile: not valid in this context: /xxx/xxx
  17. ajax判断成功状态码,Ajax返回状态码200,成功函数不触发(Ajax returns status code 200, success function not triggering)...
  18. UnityShader镜面反射计算与反射光向量推导
  19. storm风暴英雄 tempo_暴雪发布2018《风暴英雄》HGC战队实力排行榜
  20. Java+MySQL基于Springboot口腔牙科诊所管理系统#毕业设计

热门文章

  1. BZOJ 1711: [Usaco2007 Open]Dining吃饭
  2. 关于vs中代码生成的运行库
  3. MySQL 报错 1055
  4. Linux系统存储交换机日志
  5. 有了Windows Defender应用程序防护功能,再也不担心电脑免遭恶意***
  6. windows 安装
  7. Swift和OC文件间的相互调用
  8. Android TableLayout 常用的属性介绍及演示
  9. C++控制台读取和输出函数
  10. 就是一个人写代码做软件项目也建议用版本管理器也要考虑采用异地容灾手段...