谈谈Vue.observable
面试官: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() } }) }
|
参考文献