Vue.observable你有了解过吗?说说看 - 图1

一、Observable 是什么

Observable 翻译过来我们可以理解成可观察的

我们先来看一下其在Vue中的定义

Vue.observable,让一个对象变成响应式数据。Vue 内部会用它来处理 data 函数返回的对象

返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器

  1. Vue.observable({ count : 1})

其作用等同于

  1. new vue({ count : 1})

Vue 2.x 中,被传入的对象会直接被 Vue.observable 变更,它和被返回的对象是同一个对象

Vue 3.x 中,则会返回一个可响应的代理,而对源对象直接进行变更仍然是不可响应的

二、使用场景

在非父子组件通信时,可以使用通常的bus或者使用vuex,但是实现的功能不是太复杂,而使用上面两个又有点繁琐。这时,observable就是一个很好的选择

创建一个js文件

  1. // 引入vue
  2. import Vue from 'vue
  3. // 创建state对象,使用observable让state对象可响应
  4. export let state = Vue.observable({
  5. name: '张三',
  6. 'age': 38
  7. })
  8. // 创建对应的方法
  9. export let mutations = {
  10. changeName(name) {
  11. state.name = name
  12. },
  13. setAge(age) {
  14. state.age = age
  15. }
  16. }

.vue文件中直接使用即可

  1. <template>
  2. <div>
  3. 姓名:{{ name }}
  4. 年龄:{{ age }}
  5. <button @click="changeName('李四')">改变姓名</button>
  6. <button @click="setAge(18)">改变年龄</button>
  7. </div>
  8. </template>
  9. import { state, mutations } from '@/store
  10. export default {
  11. // 在计算属性中拿到值
  12. computed: {
  13. name() {
  14. return state.name
  15. },
  16. age() {
  17. return state.age
  18. }
  19. },
  20. // 调用mutations里面的方法,更新数据
  21. methods: {
  22. changeName: mutations.changeName,
  23. setAge: mutations.setAge
  24. }
  25. }

三、原理分析

源码位置:src\core\observer\index.js

  1. export function observe (value: any, asRootData: ?boolean): Observer | void {
  2. if (!isObject(value) || value instanceof VNode) {
  3. return
  4. }
  5. let ob: Observer | void
  6. // 判断是否存在__ob__响应式属性
  7. if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
  8. ob = value.__ob__
  9. } else if (
  10. shouldObserve &&
  11. !isServerRendering() &&
  12. (Array.isArray(value) || isPlainObject(value)) &&
  13. Object.isExtensible(value) &&
  14. !value._isVue
  15. ) {
  16. // 实例化Observer响应式对象
  17. ob = new Observer(value)
  18. }
  19. if (asRootData && ob) {
  20. ob.vmCount++
  21. }
  22. return ob
  23. }

Observer

  1. export class Observer {
  2. value: any;
  3. dep: Dep;
  4. vmCount: number; // number of vms that have this object as root $data
  5. constructor (value: any) {
  6. this.value = value
  7. this.dep = new Dep()
  8. this.vmCount = 0
  9. def(value, '__ob__', this)
  10. if (Array.isArray(value)) {
  11. if (hasProto) {
  12. protoAugment(value, arrayMethods)
  13. } else {
  14. copyAugment(value, arrayMethods, arrayKeys)
  15. }
  16. this.observeArray(value)
  17. } else {
  18. // 实例化对象是一个对象,进入walk方法
  19. this.walk(value)
  20. }
  21. }

walk函数

  1. walk (obj: Object) {
  2. const keys = Object.keys(obj)
  3. // 遍历key,通过defineReactive创建响应式对象
  4. for (let i = 0; i < keys.length; i++) {
  5. defineReactive(obj, keys[i])
  6. }
  7. }

defineReactive方法

  1. export function defineReactive (
  2. obj: Object,
  3. key: string,
  4. val: any,
  5. customSetter?: ?Function,
  6. shallow?: boolean
  7. ) {
  8. const dep = new Dep()
  9. const property = Object.getOwnPropertyDescriptor(obj, key)
  10. if (property && property.configurable === false) {
  11. return
  12. }
  13. // cater for pre-defined getter/setters
  14. const getter = property && property.get
  15. const setter = property && property.set
  16. if ((!getter || setter) && arguments.length === 2) {
  17. val = obj[key]
  18. }
  19. let childOb = !shallow && observe(val)
  20. // 接下来调用Object.defineProperty()给对象定义响应式属性
  21. Object.defineProperty(obj, key, {
  22. enumerable: true,
  23. configurable: true,
  24. get: function reactiveGetter () {
  25. const value = getter ? getter.call(obj) : val
  26. if (Dep.target) {
  27. dep.depend()
  28. if (childOb) {
  29. childOb.dep.depend()
  30. if (Array.isArray(value)) {
  31. dependArray(value)
  32. }
  33. }
  34. }
  35. return value
  36. },
  37. set: function reactiveSetter (newVal) {
  38. const value = getter ? getter.call(obj) : val
  39. /* eslint-disable no-self-compare */
  40. if (newVal === value || (newVal !== newVal && value !== value)) {
  41. return
  42. }
  43. /* eslint-enable no-self-compare */
  44. if (process.env.NODE_ENV !== 'production' && customSetter) {
  45. customSetter()
  46. }
  47. // #7981: for accessor properties without setter
  48. if (getter && !setter) return
  49. if (setter) {
  50. setter.call(obj, newVal)
  51. } else {
  52. val = newVal
  53. }
  54. childOb = !shallow && observe(newVal)
  55. // 对观察者watchers进行通知,state就成了全局响应式对象
  56. dep.notify()
  57. }
  58. })
  59. }

参考文献