【教程】Pastate.js 響應式 react 框架(八) 原理和API文檔

這是 Pastate.js 響應式 react state 管理框架系列教程,歡迎關注,持續更新。javascript

原理剖析

Pastate 的核心是一個管理不變式和響應式 state 的 pastore, 下面來詳細介紹 pastore 的內容。vue

帶路徑信息的 immutable state

Pastore 內部使用一種獨特的帶路徑信息且不可變(immutable)的 state 做爲應用的數據源, 掛載在 store 的 imState 屬性上。在實現上,Pastore 把非空數據源都轉化爲包裝類型,具體流程以下圖,以 string 類型的節點爲例:java

 state 包裝

其中 __store__ 爲 state 所在的 store 實例的引用,__xpath__ 爲該 state 節點在當前 store 的 state 的路徑,如 __xpath__ == '.foo.bar.baz'react

爲何要使用 immutable 數據?

Pastate 使用一種自定義的具備 immutable 特性的數據類型, 而不是 immutable.js 的數據類型。 Immutable 特性指的是數據是不可變的,新的數據只能根據老的不可變數據去從新組合而成,下面舉個例子:git

/** 可變數據 */
let mutableData = {
    foo: 'good',
    bar: 'very good'
}
// 可使用直接賦值的模式去改變數據
mutableData.foo = 'so good'
newData = mutableData

/** 不可變數據 */
let immutableData = {
    foo: 'good',
    bar: 'very good'
}
// 不可修改老數據,須要根據老數據去構建新數據
newData = Object.assgin({}, immutableData, {foo: 'so good'}) // 可使用 ES6 的 Object.assgin 函數
newData = {...immutableData, foo: 'so good'} // 也可使用 ES7 的對象擴展符

immutableData 實現了兩種效果:github

  1. 能夠把 state 變化過程的每一個狀態保存起來,這爲應用的調試帶來了不少新的方法,如時間旅行調試
  2. 對象內部的數據發生改變時,該對象的引用須要進行更新,這符合函數式編程的要求,能夠啓用 react 的 pure render ( PureComponent ) 按需快速渲染功能,使應用的性能獲得極大提升

爲何要帶路徑信息?

若是咱們手動實現 immutable state 的更新邏輯,會很是麻煩,假設有這樣一個 state :typescript

let state = {
    foo: {
        bar: {
            baz: {
                name: 'Peter'
            }
        }
    },
}

// 咱們手動來修改 name 屬性的值

// 1. 若是是 mutable data
state.foo.bar.baz.name = 'Tom'
newData = state

// 2. 若是是 immutable data 
newData = Object.assgin({}, state, 
    Object.assgin({}, state.foo, 
        Object.assgin({}, state.foo.bar, 
            Object.assgin({}, state.foo.bar.baz, {
                name: 'Tom'
            })
        )
    )
)

可見,在手動更新 immutble data 的深層嵌套數據時,很是麻煩!並且若是有多個相同或不一樣嵌套深度的 state 節點須要更改,且這些更改時在運行上連續進行的可是在代碼上是封裝開的,容易致使重複進行沒意義的引用更新:npm

let state = {
    foo: {
        bar: {
            baz: {
                name: 'Peter'
            },
            id: '1234'
        }
    },
}

// 咱們封裝 name 的修改邏輯
function changeName(){
    Object.assgin..., Object.assgin..., Object.assgin..., Object.assgin...
}
// 咱們封裝 age 的修改邏輯
function changeAge(){
    Object.assgin..., Object.assgin..., Object.assgin...
}

function handleClick(){
    shouldChangeName && changeName()
    shouldChangeAge && changeAge() 
}

上面的代碼,在 handleClick 的實現邏輯中,是否修改 name 或 age 是有可能單獨或同時發生的,通常咱們會把修改邏輯獨立封裝並按需調用。若是某個狀況下須要連續運行 changeName 和 changeAge, 那麼在 changeAge 中的 3 個 Object.assgin 是重複的,沒有實際意義。編程

你可能會考慮使用 immutable.js 庫的數據結構,immutable.js 提供完善的 immutable 數據類型和操做方式支持,但在 react 項目中使用 immutable.js 有如下不足:redux

  • immutable.js 的數據格式與原生 JS 格式互不兼容,在渲染時,常常須要在合適的位置使用 xxx.get() 或 xxx.toJS() 獲取原生 js 格式(使用 toJS() 會使全部子節點的引用更新,若是直接使用 fromJS(state).toJS() 只是對 state 進行 "深層複製", 不能實現用 immutable 數據來實現快速按需渲染的效果)
  • immutable.js 的數據操做模式是基於"節點名稱 / 節點名稱數組" 來索引節點的,如imState.setIn(['info', 'basic', 'name'], 'new Name'),而不是用原生 JS 對象屬性模式來索引節點, 如 state.info.basic.name。所以編輯器沒法分析可選屬性,也沒法索引結果的類型,這使得編程起來效率低,並且比較容易出錯。
  • immutable.js 確實完備,但過於龐大複雜,學習成本高,許多功能在通常的實際開發中不須要使用到,對初學者不太友好。

所以,Pastate 在每一個 state 節點附加了 節點路徑信息 ,並實現 state 的自動化 immutable 且按需更新引用的功能!讓咱們能夠享受 immutable 數據的優點的同時,拋開使用 immutable 數據的複雜性。

pastate imState 內部操做機制

Pastore 內部實現了一個異步操做(operation)列表,並定義了一套 operation 壓入方法:

  • set: 設置值 set(imState.foo.bar, newValue)
  • merge: 合入新值 merge(imState.foo, {bar: newValue})
  • update: 更新值 update(imState.foo.bar, n => n+ '!')

這三個方法會向 store 提交一個 operation, store 把收到 operation 後,暫時存在一個 operation 隊列中,在下一個事件循環(event loop)時對 operation 隊列進行統一處理,再批量處理中實現按需引用更新,具體流程圖以下:

operation 生成和處理流程

在每一個 operation 被 push 的先後及在 operation 被 reduce 的先後,pastate 都設計了相應的生命週期函數,你能夠經過這些生命週期函數接收、過濾或控制operation,實現自定義邏輯。

  • stateWillAddOperation 將要增長 operation 時會被調用
  • stateWillReduceOperations 將要執行 operations 時會被調用
  • stateWillApplyOperation 將要執行一個 operation 時會被調用
  • stateDidAppliedOperation 執行完一個 operation 後會被調用
  • stateDidReducedOperations 執行完 operations 時執行

詳見 API文檔。

響應式 state 鏡像

直接使用 set(imState.foo.bar, newValue)merge(imState.foo, {bar: newValue}) 等來進行 immutable state 的操做雖然已經挺方便,但和咱們平時修改 js 變量的模式仍是不太同樣,所以 pastate 爲 immutable state 構建了一套響應式的鏡像 state 來自動調用 set、merge、或 update 來生成 operation, 使得咱們能夠用賦值符號 = 或數組操做參數如 pushpop 等來間接地對 immutable state 進行操做,對於處學者來講無需任何學習成本。 Pastate 內部使用 Object.defineProperty 的方式爲響應式 state 節點定義相應的 getter 和 setter, 以實現經過賦值或數組函數發起 set, merge 或 update operation,對接 pastate 異步 operation 處理引擎,流程圖以下:

 響應式流程

視圖響應引擎

當 pastore 完成一批 operations 的 reduce 過程時,會對外發出一個視圖更新信號,經過 pastore 的 dispatch 成員函數發出。Pastate 內部實現了 pastate-redux 鏈接器, 默認使用 redux 做爲 state 發生改變時的響應器,並由 react-redux 去鏈接 react 組件組件,從而實現 pastate-react。同時, pastate 使用 redux 的基本規則實現多模塊邏輯,所以,你可使用 redux 生態系統相關組件或開發工具類配合 pastate 應用,咱們很是擁抱 redux 生態系統。

另外一方面,你徹底能夠自行實現一套視圖響應邏輯, 甚至實現 pastore 與 vue 或 angular 對接,歡迎嘗試。

API 文檔

其餘信息

在 typescript 中使用

Pastate 使用 typescript 進行開發,具備完善的類型支持,能夠直接引入 pastate 並直接在 React Typescript 項目中使用。

在 react-native 中使用

咱們正嘗試在 react-native 中使用 pastate, 你也能夠加入嘗試,若是發現什麼問題,歡迎提交 issue 告訴咱們~

在 preact 或 nerv 中使用

Pastate 在原理上能夠與類 react (react alternative) 框架如 preactnerv 配合使用,但未進行全面測試,若是你嘗試了,歡迎提交 issue 告訴咱們~

相關資源

敬請期待,若是你基於 pastate 開發一些項目,歡迎提交 issue 告訴咱們~

貢獻指引

  1. fork pastate 的 github 倉庫: https://github.com/BirdLeeSCU...
  2. clone 並安裝 npm 包
$ git clone <your repo>
$ cd pastate
$ yarn install
  1. 運行 pastate 開發包
$ yarn start
  1. 實時測試 pastate 開發包
$ yarn test
  1. 編譯 pastate 開發包
$ yarn dist

目標代碼會編譯到 /dist 目錄下

  1. 提交代碼,發起 pull request。

英文翻譯

若是以爲 pastate 很是不錯,歡迎參與 pastate 文檔英文翻譯計劃,讓更多的國外友人也能嘗試 pastate 。若是你有意願,請在 github 聯繫。

相關文章
相關標籤/搜索