面试官:Vue.observable你有了解过吗?说说看
  
一、Observable 是什么
Observable 翻译过来我们可以理解成可观察的
我们先来看一下其在Vue中的定义
Vue.observable,让一个对象变成响应式数据。Vue 内部会用它来处理 data 函数返回的对象
返回的对象可以直接用于渲染函数和计算属性内,并且会在发生变更时触发相应的更新。也可以作为最小化的跨组件状态存储器
1
   | Vue.observable({ count : 1})
  | 
 
其作用等同于
在 Vue 2.x 中,被传入的对象会直接被 Vue.observable 变更,它和被返回的对象是同一个对象
在 Vue 3.x 中,则会返回一个可响应的代理,而对源对象直接进行变更仍然是不可响应的
二、使用场景
在非父子组件通信时,可以使用通常的bus或者使用vuex,但是实现的功能不是太复杂,而使用上面两个又有点繁琐。这时,observable就是一个很好的选择
创建一个js文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   |  import Vue from 'vue // 创建state对象,使用observable让state对象可响应 export let state = Vue.observable({   name: '张三',   'age': 38 }) // 创建对应的方法 export let mutations = {   changeName(name) {     state.name = name   },   setAge(age) {     state.age = age   } }
 
  | 
 
在.vue文件中直接使用即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | <template>   <div>     姓名:{{ name }}     年龄:{{ age }}     <button @click="changeName('李四')">改变姓名</button>     <button @click="setAge(18)">改变年龄</button>   </div> </template> import { state, mutations } from '@/store export default {   // 在计算属性中拿到值   computed: {     name() {       return state.name     },     age() {       return state.age     }   },   // 调用mutations里面的方法,更新数据   methods: {     changeName: mutations.changeName,     setAge: mutations.setAge   } }
   | 
 
三、原理分析
源码位置:src\core\observer\index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
   | export function observe (value: any, asRootData: ?boolean): Observer | void {   if (!isObject(value) || value instanceof VNode) {     return   }   let ob: Observer | void      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 }
  | 
 
Observer类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   | export class Observer {     value: any;     dep: Dep;     vmCount: number; 
      constructor (value: any) {         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)         } }
  | 
 
walk函数
1 2 3 4 5 6 7
   | walk (obj: Object) {     const keys = Object.keys(obj)          for (let i = 0; i < keys.length; i++) {         defineReactive(obj, keys[i])     } }
  | 
 
defineReactive方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
   | export function defineReactive (   obj: Object,   key: string,   val: any,   customSetter?: ?Function,   shallow?: boolean ) {   const dep = new Dep()
    const property = Object.getOwnPropertyDescriptor(obj, key)   if (property && property.configurable === false) {     return   }
       const getter = property && property.get   const setter = property && property.set   if ((!getter || setter) && arguments.length === 2) {     val = obj[key]   }
    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) {       const value = getter ? getter.call(obj) : val              if (newVal === value || (newVal !== newVal && value !== value)) {         return       }              if (process.env.NODE_ENV !== 'production' && customSetter) {         customSetter()       }              if (getter && !setter) return       if (setter) {         setter.call(obj, newVal)       } else {         val = newVal       }       childOb = !shallow && observe(newVal)              dep.notify()     }   }) }
   | 
 
参考文献