?Reaction 響應(yīng)式追蹤的完整指南)
前端UI組件【免費下載鏈接】formily Cross Device High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3項目地址https://gitcode.com/gh_mirrors/fo/formily點擊查看免費下載導(dǎo)讀在 Formily 體系中formily/reactive提供了獨立于框架的響應(yīng)式內(nèi)核而formily/reactive-vue則負責把這一內(nèi)核接入 Vue 的組件生命周期。本文聚焦formily/reactive-vue的核心 API ——observer它能把一個 Vue 組件的渲染方法改造成一個Reaction使得組件每次重新渲染時自動收集所消費的響應(yīng)式依賴并在依賴變化時精準觸發(fā)重渲染。讀完本文你將掌握observer的簽名與參數(shù)語義、Vue 2 / Vue 3 雙版本下的底層實現(xiàn)原理、scheduler調(diào)度器的正確用法以及它與 Vue 原生響應(yīng)系統(tǒng)的共存方式。本文基于當前倉庫中 packages/reactive/docs/api/vue/observer.md 編寫并以 packages/reactive-vue 包的真實源碼與測試用例作為佐證。一、核心概念把渲染函數(shù)變成 Reactionobserver的官方描述非常凝練In Vue, the component rendering method is changed to Reaction, and dependencies are collected every time the view is re-rendered, and dependencies are updated automatically to re-render.翻譯過來即在 Vue 中將組件渲染方法變成 Reaction每次視圖重新渲染時收集依賴依賴更新時自動重渲染。要理解這句話需要先了解formily/reactive中兩個核心原語詳見 packages/reactive/src/index.ts 及文檔 packages/reactive/docs/api/reaction.mdobservable把普通對象包裝成可被追蹤的響應(yīng)式數(shù)據(jù)Tracker / reaction執(zhí)行一段函數(shù)effect在函數(shù)執(zhí)行期間讀取到的 observable 屬性會被自動登記為依賴此后任何依賴變化都會觸發(fā) effect 重新執(zhí)行。observer的工作就是把 Vue 組件的render邏輯放進這樣一個 Tracker 中執(zhí)行渲染期間讀到的observable屬性全部成為該組件的依賴此后只要任一依賴變化組件就會自動進入重渲染流程。這正是 Formily 表單元件能以高性能、細粒度方式響應(yīng)表單狀態(tài)變化的基礎(chǔ)設(shè)施。二、API 簽名與參數(shù)說明2.1 完整簽名原文檔給出了如下 TypeScript 簽名interface IObserverOptions { scheduler?: (updater: () void) void //調(diào)度器可以手動控制更新時機 name?: string //包裝后的組件的name } interface observerT extends VueComponent { (component: T, options?: IObserverOptions): T }observer接收一個 Vue 組件選項對象或類組件和可選配置返回一個包裝后的等價組件返回值的類型保持T不變因此對調(diào)用方而言 API 形狀完全透明。該簽名與倉庫中 packages/reactive-vue/src/types.ts 的定義一一對應(yīng)export interface IObserverOptions { name?: string scheduler?: (updater: () void) void }2.2 參數(shù)語義參數(shù)類型說明默認行為componentT extends VueComponent待包裝的 Vue 組件選項對象或 Vue 類必填options.namestring包裝后組件的名稱優(yōu)先取顯式傳入的name其次回退到組件自身name/_componentTag/ 構(gòu)造函數(shù)名最終回退到componentoptions.scheduler(updater: () void) void調(diào)度器在依賴變化時接管重渲染的時機控制不傳則依賴變化后立即同步重渲染關(guān)于name的回退優(yōu)先級源碼 packages/reactive-vue/src/observer/observerInVue2.ts 給出了精確順序const name observerOptions?.name || (Component as any).name || (Component as any)._componentTag || (Component.constructor Component.constructor.name) || component對應(yīng)的測試 packages/reactive-vue/src/tests/observer.spec.ts 驗證了兩種邊界無任何名稱時最終得到component顯式傳入{ name: abc }時得到abc。2.3 scheduler 的用途scheduler是IObserverOptions中最有實戰(zhàn)價值的參數(shù)當依賴更新發(fā)生時observer不會直接執(zhí)行重渲染而是把執(zhí)行重渲染這個動作即updater回調(diào)交給你手動編排。典型場景包括合并高頻更新在一幀內(nèi)多次修改響應(yīng)式數(shù)據(jù)時只調(diào)度一次渲染延遲渲染配合setTimeout/requestAnimationFrame把渲染推遲到合適時機異步批量刷新把更新任務(wù)推入自己的批處理隊列。測試 packages/reactive-vue/src/tests/observer.spec.ts 給出了一個完整的節(jié)流式 scheduler 示例100ms 內(nèi)的多次更新被合并為一次渲染。三、完整用例與 Vue 響應(yīng)系統(tǒng)共存原文檔提供了一段開箱即用的示例。一個值得強調(diào)的設(shè)計點是observer包裝后的組件不排斥 Vue 原生響應(yīng)系統(tǒng)——data()中既可以是普通 Vue 響應(yīng)式數(shù)據(jù)也可以混入formily/reactive的observable對象兩者可以在同一組件中并存。template div div input :style{ height: 28, padding: 0 8px, border: 2px solid #888, borderRadius: 3, } :valueobs.value input(e) { obs.value e.target.value } / /div div{{obs.value}}/div /div /template script import { observable } from formily/reactive import { observer } from formily/reactive-vue export default observer({ data() { // 能與 vue 的響應(yīng)系統(tǒng)共存 const obs observable({ value: Hello world, }) return { obs, } }, }) /script運行邏輯拆解observable({ value: Hello world })創(chuàng)建一個formily/reactive響應(yīng)式對象模板中:valueobs.value與{{obs.value}}在渲染階段被observer追蹤obs.value成為組件依賴輸入框input事件改寫obs.value依賴變化觸發(fā) Tracker 回調(diào)組件自動重渲染視圖同步更新。3.1 共存機制collectData 的過濾邏輯能與 Vue 響應(yīng)系統(tǒng)共存不是一句口號其底層實現(xiàn)位于 packages/reactive-vue/src/observer/collectData.ts。observer在 Vue 2 分支中會重寫data選項用collectData對data()的返回值做分流處理export default function collectData(vm: any, data?: any) { const dataDefinition typeof data function ? data.call(vm, vm) : data || {} const filteredData Object.keys(dataDefinition).reduce( (result: any, field) { const value dataDefinition[field] if (isObservable(value)) { Object.defineProperty(vm, field, { configurable: true, get() { return value }, }) } else { result[field] value } return result }, {} ) return filteredData }關(guān)鍵行為對data()返回的每個字段調(diào)用isObservable來自formily/reactive做判定若是observable對象則不交給 Vue 的響應(yīng)式系統(tǒng)而是直接在vm上定義 getter 透傳引用避免 Vue 對已代理對象再次遞歸代理帶來的性能損耗與行為沖突若是普通數(shù)據(jù)則保持原樣交給 Vue 處理與 Vue 原生響應(yīng)系統(tǒng)無縫銜接。對應(yīng)的測試 packages/reactive-vue/src/tests/observer.spec.ts 驗證了這一點經(jīng)過collectData過濾后modelobservable仍由formily/reactive追蹤并觸發(fā) autorun而普通字段target完全不受formily/reactive影響。四、雙版本實現(xiàn)Vue 2 與 Vue 3 的分流formily/reactive-vue借助vue-demi同時支持 Vue 2 與 Vue 3。入口 packages/reactive-vue/src/observer/index.ts 根據(jù)isVue2標志分發(fā)到兩套實現(xiàn)export function observerC(baseComponent: C, options?: IObserverOptions): C { if (isVue2) { return observerV2(baseComponent, options) } else { return observerV3(baseComponent, options) } }依賴關(guān)系見 packages/reactive-vue/package.json運行時依賴formily/reactive2.3.7與vue-demi0.13.6同時以vue^2.6.0 || 3.0.0-rc.0和vue/composition-apiVue 2 下使用 Composition API 時需要作為 peer 依賴。4.1 Vue 2重寫 $mount/$destroy接管原生渲染Vue 2 分支位于 packages/reactive-vue/src/observer/observerInVue2.ts其核心思路是復(fù)制原始組件選項避免修改原組件包裝data為collectData處理后的版本通過Super.extend(options)生成擴展組件其中Super取原組件的原型鏈構(gòu)造器規(guī)避Vue.extend時生命周期被重復(fù)調(diào)用的問題重寫原型上的$mount首次掛載時在batch包裹下用Tracker.track執(zhí)行原生渲染并把 Vue 的_watcher.getter替換為reactiveRender使組件后續(xù)每次渲染都重新進入 Tracker 收集依賴重寫$destroy組件銷毀前先disposeTracker杜絕銷毀后的無效追蹤。const reactiveRender () { batch(() { tracker.track(() { if (!mounted) { $mount.apply(this, args) mounted true nativeRenderOfVue this._watcher.getter // rewrite the native render method of vue with our reactive tracker render // thus if component updated by vue watcher, we could re track and collect dependencies by formily/reactive this._watcher.getter reactiveRender } else { nativeRenderOfVue.call(this, this) } }) }) return this }而 Tracker 的回調(diào)則負責在依賴變化時決定何時、以何種方式重渲染const tracker new Tracker(() { if ( reactiveRender.$vm._isBeingDestroyed || reactiveRender.$vm._isDestroyed ) { return tracker.dispose() } if ( observerOptions?.scheduler typeof observerOptions.scheduler function ) { observerOptions.scheduler(reactiveRender) } else { reactiveRender() } })這里有兩處值得注意的實現(xiàn)細節(jié)銷毀保護檢測到組件處于_isBeingDestroyed/_isDestroyed狀態(tài)時直接tracker.dispose()。測試 packages/reactive-vue/src/tests/observer.spec.ts 專門驗證了watcher 銷毀后不再觸發(fā)渲染這一行為scheduler 接管依賴變化時優(yōu)先調(diào)用用戶傳入的scheduler否則立即reactiveRender()。4.2 Vue 3setup 內(nèi)注入 useObserverVue 3 分支位于 packages/reactive-vue/src/observer/observerInVue3.ts實現(xiàn)更加輕量包裝返回一個帶setup的新組件在用戶setup執(zhí)行前先調(diào)用useObserver建立追蹤export const observer function (opts: any, options?: IObserverOptions): any { const name options?.name || opts.name || ObservableComponent return { name, ...opts, setup(props: Recordstring, any, context: any) { useObserver(options) return opts?.setup?.(props, context) }, } }useObserver定義在 packages/reactive-vue/src/hooks/useObserver.ts在組件實例上攔截effect屬性把組件更新函數(shù)包裝進Tracker.track并支持onBeforeUnmount時disposeTracker防止內(nèi)存泄漏依賴變化時優(yōu)先走options.scheduler否則調(diào)用vm.proxy.$forceUpdate()強制重渲染。需要說明的是這段實現(xiàn)中有一個對 Formily 官方 issue#2655的兼容性處理其細節(jié)隨 Vue 版本迭代可能有差異本文僅陳述源碼當前狀態(tài)。五、在 Formily 生態(tài)中的定位與典型用法5.1 使用入口formily/reactive-vue的公共導(dǎo)出見 packages/reactive-vue/src/index.ts包含三部分export * from ./observer export * from ./hooks export * from ./types即observer含collectData、useObserverhook 以及IObserverOptions類型。安裝后在項目中即可直接使用# 以 npm 為例 npm install formily/reactive-vue使用時注意 Vue 2 場景需自行安裝vue^2.6.0并視情況引入vue/composition-apiVue 3 則直接使用vue3。5.2 何時使用 observer從源碼與測試可以歸納出observer的典型適用場景組件內(nèi)消費了formily/reactive的 observable 數(shù)據(jù)例如 Formily 表單模型Form/Field的value、errors、loading等狀態(tài)希望渲染粒度精確到組件級只有真正讀取到的依賴變化才觸發(fā)本組件重渲染而不是整棵組件樹重渲染需要手動控制更新時機配合scheduler做渲染合并或延遲渲染。5.3 與 React 版本 observer 的差異當前倉庫同時提供了 React 版本文檔 packages/reactive/docs/api/react/observer.md兩者理念相同把渲染變成 Reaction但存在明顯差異維度formily/reactive-vueformily/reactive-react支持組件形態(tài)Vue 組件選項對象 / 類 / 帶 setup 的組件僅函數(shù)組件Function Component名稱配置項namedisplayName透傳 ref 配置無forwardRef附加能力collectData與 Vue 響應(yīng)系統(tǒng)共存Observer組件Render Props 局部精確渲染這一對照可以幫助你在跨框架復(fù)用 Formily 響應(yīng)式狀態(tài)時快速對齊 API 語義。六、常見問題與最佳實踐6.1 依賴收集每次渲染都發(fā)生是特性而非缺陷observer的設(shè)計是在每次渲染時重新收集依賴。這帶來兩個好處條件渲染下的依賴是動態(tài)的if (obs.a) return obs.b這種寫法下只有當obs.a為真時obs.b才成為依賴之后obs.b的變化不會在obs.a為假時誤觸發(fā)渲染依賴集合始終精確收斂到當前視圖真正消費的數(shù)據(jù)。代價是每次渲染都需要一次追蹤開銷但batch與細粒度收集保證了整體成本可控。6.2 scheduler 的正確姿勢scheduler的updater就是執(zhí)行重渲染的函數(shù)務(wù)必在合適時機調(diào)用且不要重復(fù)調(diào)用。參考測試中的節(jié)流寫法const Component observer( { data() { return { model } }, render(h) { return h(button, { on: { click: this.model.setAge }, domProps: { textContent: this.model.age }, }) }, }, { scheduler: (update) { clearTimeout(timer) timer setTimeout(update, 100) }, } )要點連續(xù)觸發(fā)時先clearTimeout再重新計時實現(xiàn)最后一次生效的合并效果組件銷毀后 Tracker 已 dispose不應(yīng)再調(diào)度渲染。6.3 組件銷毀后的安全性observer兩套實現(xiàn)都內(nèi)置了銷毀保護Vue 2Tracker 回調(diào)檢測_isBeingDestroyed/_isDestroyed后自動dispose$destroy也會顯式執(zhí)行 disposer見 observerInVue2.tsVue 3useObserver在onBeforeUnmount中 dispose Tracker見 useObserver.ts。因此無需在業(yè)務(wù)代碼里手動清理這也是測試 observer.spec.ts 覆蓋的核心保障之一。6.4 不要混用兩套響應(yīng)式代理同一對象盡管collectData已自動把observable對象從 Vue 響應(yīng)式系統(tǒng)中摘出但建議在業(yè)務(wù)層保持清晰邊界同一份狀態(tài)要么歸formily/reactive管要么歸 Vue 管避免對同一對象同時施加兩套代理導(dǎo)致的行為不確定性。七、小結(jié)observer是formily/reactive-vue的基石 API它把 Vue 組件的渲染生命周期與formily/reactive的響應(yīng)式追蹤機制打通每次渲染收集依賴、依賴更新自動重渲染實現(xiàn)組件級精確更新通過collectData與 Vue 原生響應(yīng)系統(tǒng)和平共存Vue 2 分支重寫$mount/$destroy并接管原生渲染Vue 3 分支通過setupuseObserver注入追蹤scheduler提供手動編排渲染時機的自由name幫助調(diào)試與組件樹識別。深入閱讀建議API 文檔packages/reactive/docs/api/vue/observer.md、packages/reactive/docs/api/vue/observer.zh-CN.md實現(xiàn)源碼packages/reactive-vue/src/observer/index.ts、observerInVue2.ts、observerInVue3.ts、collectData.ts、useObserver.ts測試用例packages/reactive-vue/src/tests/observer.spec.ts贊分享前端UI組件【免費下載鏈接】formily Cross Device High Performance Normal Form/Dynamic(JSON Schema) Form/Form Builder -- Support React/React Native/Vue 2/Vue 3項目地址https://gitcode.com/gh_mirrors/fo/formily點擊查看免費下載相關(guān)推薦Formily 響應(yīng)式渲染formily/reactive-react 的 observer 與 Observer 使用指南Formily 響應(yīng)式渲染formily/reactive react 的 observer 與 Observer 使用指南 本文是 Formily 官方文前端UI組件Formily 響應(yīng)式渲染指南深入理解 observer HOC 與 Observer 組件Formily 響應(yīng)式渲染指南深入理解 observer HOC 與 Observer 組件 導(dǎo)讀 本文以 Formily 官方文檔 observer.md前端UI組件formily Vue 響應(yīng)式組件observer API 用法與原理解讀formily Vue 響應(yīng)式組件observer API 用法與原理解讀 導(dǎo)讀 在 Formily 的 Vue 體系中 observer 是讓普通 Vue前端UI組件創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考