Observable
?翻譯過來我們可以理解成可觀察的
Vue.js2.6
?新增
Vue.observable
,讓一個對象變成響應式數據。Vue
?內部會用它來處理?data
?函數返回的對象 。
返回的對象可以直接用于渲染函數和計算屬性內,并且會在發生變更時觸發相應的更新。也可以作為最小化的跨組件狀態存儲器
Vue.observable({ count : 1})
其作用等同于
new vue({ count : 1})
?在?Vue 2.x
?中,被傳入的對象會直接被?Vue.observable
?變更,它和被返回的對象是同一個對象
在?Vue 3.x
?中,則會返回一個可響應的代理,而對源對象直接進行變更仍然是不可響應的
Observable 是什么
Vue.observable 是 Vue.js 提供的一個 API,它可以將一個普通的 JavaScript 對象轉換為響應式對象。通過使用 Vue.observable,可以在任何地方創建一個響應式對象,而不僅僅是在 Vue 實例中使用。它在 Vue.js 的響應式系統中扮演了很重要的角色,可以用于管理狀態、狀態共享等場景。
Observable 作用
使用 Vue.observable 可以將一個普通對象轉換為響應式對象,從而使其成為 Vue.js 的響應式系統的一部分。在轉換之后,可以對該對象進行讀取、修改等操作,并且在修改后,Vue.js 會自動進行重新渲染。
解決了什么問題
Vue.observable 的出現解決了在 Vue.js 中管理狀態的問題。在 Vue.js 中,通過將狀態保存在 Vue 實例的 data 屬性中,可以實現狀態管理,但是這種方式只能在 Vue 實例中使用,無法在其他地方使用。通過使用 Vue.observable,我們可以在任何地方創建一個響應式對象,從而更加靈活地管理狀態。
適用場景
在非父子組件通信時,可以使用通常的bus或者使用vuex,但是實現的功能不是太復雜,而使用上面兩個又有點繁瑣。這時,observable就是一個很好的選擇。
舉個例子:
// utils.js 文件
import Vue from 'vue'
// 創建響應式對象
export const state = Vue.observable({count: 1
})export const mutations = Vue.observable({increase() {state.count++}
})
// 在 vue 文件中使用
<template><div id="app"><button @click="increaseCount">+</button>{{ num }}</div>
</template><script>
import { state, mutations } from '@/utils/index'
export default {name: 'App',computed: {num() {return state.count}},methods: {increaseCount: mutations.increase}
}
</script>
原理分析
源碼位置:src\core\observer\index.js
export function observe (value: any, asRootData: ?boolean): Observer | void {if (!isObject(value) || value instanceof VNode) {return}let ob: Observer | void// 判斷是否存在__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) {// 實例化Observer響應式對象ob = new Observer(value)}if (asRootData && ob) {ob.vmCount++}return ob
}
Observer
類
export class Observer {value: any;dep: Dep;vmCount: number; // number of vms that have this object as root $dataconstructor (value: any) {this.value = valuethis.dep = new Dep()this.vmCount = 0def(value, '__ob__', this)if (Array.isArray(value)) {if (hasProto) {protoAugment(value, arrayMethods)} else {copyAugment(value, arrayMethods, arrayKeys)}this.observeArray(value)} else {// 實例化對象是一個對象,進入walk方法this.walk(value)}
}
walk
函數
walk (obj: Object) {const keys = Object.keys(obj)// 遍歷key,通過defineReactive創建響應式對象for (let i = 0; i < keys.length; i++) {defineReactive(obj, keys[i])}
}
defineReactive
方法
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}// cater for pre-defined getter/settersconst getter = property && property.getconst setter = property && property.setif ((!getter || setter) && arguments.length === 2) {val = obj[key]}let childOb = !shallow && observe(val)// 接下來調用Object.defineProperty()給對象定義響應式屬性Object.defineProperty(obj, key, {enumerable: true,configurable: true,get: function reactiveGetter () {const value = getter ? getter.call(obj) : valif (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/* eslint-disable no-self-compare */if (newVal === value || (newVal !== newVal && value !== value)) {return}/* eslint-enable no-self-compare */if (process.env.NODE_ENV !== 'production' && customSetter) {customSetter()}// #7981: for accessor properties without setterif (getter && !setter) returnif (setter) {setter.call(obj, newVal)} else {val = newVal}childOb = !shallow && observe(newVal)// 對觀察者watchers進行通知,state就成了全局響應式對象dep.notify()}})
}