博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Observer源码解析
阅读量:7119 次
发布时间:2019-06-28

本文共 4725 字,大约阅读时间需要 15 分钟。

前言

在Observer源码部分主要有三个角色:Observer、Dep、Watcher。

Observer

看这一部分,可以带着以下几个问题来看:

1、满足什么条件可以将其变成响应式的

2、Observer是如何去分别处理传入的数组或者对象的?

3、有两处new Dep,作用分别是什么?

4、核心代码defineReactive干了些什么?

5、Dep.target是什么?defineReactive中get时,为什么要判断Dep.target?

  • 满足什么条件可以将其变成响应式的
function observe (value, asRootData) {  if (!isObject(value) || value instanceof VNode) {    return  }  let ob  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}复制代码

其实上述代码中有很多判断,我们可以得出以下结论:

(1)必须是一个对象,且不能是vnode的类型的。

  • Observer是如何去分别处理传入的数组或者对象的
if (Array.isArray(value)) {  if (hasProto) {    protoAugment(value, arrayMethods)  } else {    copyAugment(value, arrayMethods, arrayKeys)  }  this.observeArray(value)} else {  this.walk(value)}复制代码

这段代码就是判断传入的值是不是数组,如果是数组,走observeArray方法,如果不是数组,那么走walk方法。

walk (obj) {    const keys = Object.keys(obj)    for (let i = 0; i < keys.length; i++) {      defineReactive(obj, keys[i])    }}observeArray (items) {    for (let i = 0, l = items.length; i < l; i++) {      observe(items[i])    }}复制代码

walk这个方法就是遍历对象,然后给对象中的属性值变成响应式的,遍历完之后,整个对象就是响应式对象了。 observeArray这个方法是遍历数组,然后对数组中每一个元素在走一遍响应式流程。

  • 有两处new Dep,作用分别是什么

第一处:

class Observer {  constructor (value) {    this.value = value    this.dep = new Dep()    this.vmCount = 0    def(value, '__ob__', this)    if (Array.isArray(value)) {      if (hasProto) {        protoAugment(value, arrayMethods)      } else {        copyAugment(value, arrayMethods, arrayKeys)      }      this.observeArray(value)    } else {      this.walk(value)    }  }复制代码

第二处:

function defineReactive (  obj,  key,  val,  customSetter,  shallow) {  const dep = new Dep()  // 省略中间代码  Object.defineProperty(obj, key, {    enumerable: true,    configurable: true,    get: function reactiveGetter () {      const value = getter ? getter.call(obj) : val      if (Dep.target) {        dep.depend()        // 省略中间代码      }      return value    },    set: function reactiveSetter (newVal) {      // 省略中间代码      dep.notify()    }  })}复制代码

第二处dep我们很好理解,是给get和set服务的,进行依赖收集和派发更新。

第一处dep,我们可以考虑一下,他是整个对象的一个属性,那么他何时进行依赖收集和派发更新。

我们可以全局搜一下dep.depend。发现会有三处。有两处是对ob属性进行操作的,也就是对整个对象进行依赖收集。

在全局搜一下dep.notify。发现有四处。有三处是对ob属性进行操作的。分别是set和del,数组的一个方法。

  • 核心代码defineReactive干了些什么

这一部分代码可以分为三部分看:定义一些变量、Object.defineProperty、对childOb操作。

// 生成一个新的dep。  const dep = new Dep()  // 判断这个对象这个属性是否可以修改  const property = Object.getOwnPropertyDescriptor(obj, key)  if (property && property.configurable === false) {    return  }  // 定义getter和setter方法  const getter = property && property.get  const setter = property && property.set  if ((!getter || setter) && arguments.length === 2) {    val = obj[key]  }复制代码
get: function reactiveGetter () {  // 获取值  const value = getter ? getter.call(obj) : val  // 当存在Dep.target的时候进行依赖收集  if (Dep.target) {    dep.depend()    // 省略中间代码  }  // 返回获取到的值  return value},set: function reactiveSetter (newVal) {  // 获取原来的值  const value = getter ? getter.call(obj) : val  // 将新的值和原值进行对比,如果没有发生改变,就直接返回  if (newVal === value || (newVal !== newVal && value !== value)) {    return  }  // 该属性不能set的情况也直接返回  if (getter && !setter) return  // 给该属性赋值  if (setter) {    setter.call(obj, newVal)  } else {    val = newVal  }  // 重新对这个值进行监听  childOb = !shallow && observe(newVal)  // 更新dep中的watcher  dep.notify()}复制代码
// 尝试将值转化成响应式对象let childOb = !shallow && 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) {      // 省略中间代码      //因为值发生了变动,所以再一次尝试将其变成一个响应式对象      childOb = !shallow && observe(newVal)    }  })复制代码
  • Dep.target是什么 其实Dep.target是一个全局变量,更是一个wathcer。在dep文件中的源码如下:
Dep.target = nullconst targetStack = []export function pushTarget (target: ?Watcher) {  targetStack.push(target)  Dep.target = target}export function popTarget () {  targetStack.pop()  Dep.target = targetStack[targetStack.length - 1]}复制代码
  • defineReactive中get时,为什么要判断Dep.target

因为所有定义在data中的值,都会被变成响应式对象,但是每一个不一定有watcher。watcher分为三种:render中生成的watcher、用户自定义的watcher、computed。

上述流程中,当生成一个新的renderWatcher的时候,便会走get流程,然后进行依赖收集,如果没有Dep.target,说明这个值并没有对应的watcher,所以不需要进行依赖收集。 当更新的是时候,又回进行一次依赖收集。

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

你可能感兴趣的文章
《大话设计模式》读书总结
查看>>
《悟透JavaScript》(3)
查看>>
java实现将字符串转化为二进制输出
查看>>
我的友情链接
查看>>
backtrack 5 基本配置
查看>>
Event, Alerts, Perf Data Flow in OpsMgr 2007
查看>>
linux下启用ip转发功能
查看>>
linux系统CPU、内存、硬盘、网络、lnmp服务整体监控邮件报警
查看>>
Git 远程仓库详解 ,Git分支管理详解
查看>>
ansible安装及配置使用
查看>>
Intel APIC Architecture
查看>>
AGL Specification简单记录
查看>>
使用vb将excel导入PowerDesigner,生成表结构
查看>>
烂泥:ubuntu安装KVM虚拟机管理virt-manager
查看>>
备份Oracle要注意的各事项
查看>>
MongoDB简单介绍以及基本命令
查看>>
httpd 安装配置
查看>>
Basic operations in OpenVZ environment
查看>>
隐藏nginx 版本等信息
查看>>
Web框架比较:JSF, Spring MVC, Stripes, Struts 2, Tapestry and Wicket
查看>>