摘要:本篇講解雙源屬性不可變數據事件驅動等。可被偵聽,被偵聽后源頭若發生變化,相應的偵聽函數將自動被調起。本文完本專欄歷史文章介紹一項讓可以與抗衡的技術可視化開發工具非正經入門之一三宗罪可視化開發工具非正經入門之二分離界面設計
本系列博文從 Shadow Widget 作者的視角,解釋該框架的設計要點。本篇講解雙源屬性、不可變數據、事件驅動等。
1. React 中的隱式雙源var mainComp = null; function Welcome(props) { returnHello, {props.name}
; } class DivText extends React.Component { constructor(props) { super(props); this.state = {name:"Wayne"}; mainComp = this; } render() { return (); } } ReactDOM.render(, document.getElementById("root") ); setTimeout( function() { mainComp.setState({name:"George"}); },5000);
這個例子創建的 component 樹如下圖,main 節點的 state.name 傳遞給 txt 節點用作 props.name。txt 節點初始顯示 "Hello, Wayne",過 5 秒后切換為 "Hello, George"。
+-- main // div | +-- txt // h1
我們研究一下 5 秒后切換都發生了什么,mainComp.setState({name:"George"}) 一句更改 main 節點的 state.name,然后系統觸發下級 txt 節點的 props.name 變化,再驅動 txt 節點內容刷新。
本處 React 技術實現讓初學者很費解,main 節點的 render 函數用 JSX 返回 Element,并非每次渲染都用
render() { return (); }
而是首次渲染時創建一次,其后 render() 調用只對已存在的節點做更新,由 props.name 變化驅動子節點內容刷新。所以,上面 txt 節點的 props.name 對節點自身來說,是不變量,但對父節點來說,是可變量。由 state.xxx 驅動刷新與 props.xxx 驅動刷新本質是一回事,只不過 React 編程模型在表面弄了一點限制。
props.xxx 驅動的刷新是一個源頭,state.xxx 驅動的刷新是另一個源頭,合起來是 "雙源驅動"。
2. 改造雙源驅動由于 React 限定本節點 props.xxx 是只讀的,我們通過改造,讓一個節點既接受 props.xxx 驅動,也接受 state.xxx 驅動。讓 React 隱式的雙源驅動,變成顯式的雙源驅動,如下:
var txtComp = null; class Welcome extends React.Component { constructor(props) { super(props); this.state = {name:props.name}; this.oldName = props.name; txtComp = this; } render() { var name = this.state.name; if (this.oldName !== this.props.name) name = this.state.name = this.oldName = this.props.name; returnHello, {name}
; } }
這樣,在 txt 節點,既可用 txtComp.setState({name:"George"}) 驅動刷新,也可由父節點傳入的 props.name 變化來驅動刷新。我們額外要做的是,在 txt 節點用 this.oldName 記錄 props.name 舊值,由 this.oldName !== this.props.name 來識別傳入 props.name 是否變化了。
這么改造的意義在于:
自身狀態變遷與父級驅動變遷,是兩種普遍存在的現象,我們引用正規的 "雙源驅動" 概念,便于將兩種源頭歸一,如后面敘述,用 this.duals.xxx 表達,歸一后才能構造事件發布與訂閱的機制。
React 讓 props 屬性只讀的設計有點尷尬,有違普遍認知。
如前面介紹,它不是不可變,而是限定本級與下級不可修改,這個規則對保障單向數據傳遞有利。但大眾對 DOM 節點的認知是這樣的,以 為例,type="button" 這個屬性可以用 props.type 表達,因為生存周期里它不該有變化,而 title="for test" 屬性應讓本節點參與管理,生存期內可變。
讓自身節點管理類似 props.name, props.title 的屬性,大致有兩種方法,其一,采取上面介紹的方法,讓兩個源頭歸一,再驅動本節點輸出。其二,按嚴格的單向數據流要求,把代碼寫成下面樣子:
class Welcome extends React.Component { constructor(props) { super(props); } setName(newName) { mainComp.setState({name:newName}); } render() { returnHello, {this.props.name}
; } }
也就是借助父節點的 setState() 實現刷新,理論上,這也是單向數據流,理解有點別扭,自身節點的屬性不能直接管理,非要到父節點跑一圈。
Shadow Widget 雙源驅動的優點在于 "讓 DOM 節點功能回歸本原",讓 props.xxx 服務于生存周期中不變量,讓 duals.xxx 服務于可變量,state.xxx 也服務于可變量,但傾向于用來表達自身節點的私有狀態。
reflux 為實現 React flux 機制,仿 component 接口設計了 store,如果沒有上述 props.xxx 限制,我相信把 component 與 store 合一遠優于現有設計。回歸原本的設計好處是潛在的,因為傾斜的地基會導致上層建筑更加傾斜。
3. 數據偵聽機制Shadow Widget 將雙源驅動歸一后,用 duals.attr 存取屬性,而且系統內部對讀寫 duals.attr 做了封裝,"讀屬性" 自動轉從 state.attr 讀值,"寫屬性" 則封裝成事件驅動機制,等效于調用 comp.setState({attr:value}),但它所做的事遠不止這個,還包括:
用戶可以調用 comp.defineDual(attr,setterFunc) 注冊自定義的 setter 函數,甚至對同一 duals.attr 多次注冊不同 settrer 函數,比如基類定義一個 setter 函數,繼承類中再定義另一個 setter,兩個 setter 會依順被調用。即 duals.attr 的 setter 也具有一種可繼承的機制。
duals.attr 可被偵聽,被偵聽后源頭 duals.attr 若發生變化,相應的偵聽函數將自動被調起。
多個 component 節點的雙源屬性可以串接,源頭更改其它地方會自動更新。
對某節點的 duals.attr 賦值,會導致多種聯動響應,如果導致本節點其它雙源屬性更新,更新將在同一周期立即進行,如果導致其它節點的雙源屬性更新,將在下一周期在其它節點 render() 時進行,如果觸發偵聽事件,也在下一周期調用偵聽函數。Shadow Widget 對 duals.attr 賦值的設計,已兼顧考慮了本節點內雙源屬性遞歸回調的效率,也保證了數據流傳遞的單向性。
在各節點注冊 duals.attr 的 setter 函數、偵聽函數,能自動適應它的生存周期。比如 B 節點偵聽 A 節點的 duals.attr,無論 A 節點,還是 B 節點先被卸載,偵聽鏈都會自動斷開。
偵聽源節點的雙源屬性,常見代碼有這么兩種寫法:
sourceComp.listen("attr",targetComp,"attrMethod"); sourceComp.listen("attr",function(value,oldValue){});
第 1 行寫法的效果是:sourceComp.duals.attr 發生變化后,自動觸發 targetComp["attrMethod"] 的函數調用。第 2 行則觸發由參數指定的回調函數。
4. 數據更新的判斷依據Shadow Widget 采用 "恒等比較" 的方式判斷兩個數值是否更改為,在 comp.duals.attr = value 與 comp.setState({attr:value}) 語句中,當所賦新值(value)與舊值恒等(即 ===),則視作數據未更新,也就不會觸發相應的 setter 調用或 listen 調用。
Shadow Widget 已為各構件配置 shouldComponentUpdate() 與 componentWillReceiveProps() 缺省處理,除非有特別理由,您不應改變缺省 "以各屬性新舊值是否恒等" 的判斷方式。
至于如何對 Array 或 Object 快速構造新數據,以便被系統判斷為 "非恒等",我們建議用 React addon 提供的 update 接口,Shadow Widget 已缺省內置該函數,即 ex.update(),請參考 Shadow Widget 的 API 手冊。
5. 自動定義的雙源屬性雙源屬性一般要調用 comp.defineDual() 注冊后才使用,但對于 DOM 節點內置屬性是例外,如 title, id, name 等,這些屬性只要節點在創建時,傳入的 props 用到了,就會被系統自動注冊為雙源屬性。
另外,命名為 data-*, aria-*, dual-* 的屬性,也自動注冊為雙源屬性。
自動注冊雙源屬性的設計目的是為了簡化編程,如果遇到不想變成雙源屬性卻自動注冊了的情況,不使用 duals.xxx 即可。
(本文完)
本專欄歷史文章:
介紹一項讓 React 可以與 Vue 抗衡的技術
React 可視化開發工具 Shadow Widget 非正經入門(之一:React 三宗罪)
React 可視化開發工具 Shadow Widget 非正經入門(之二:分離界面設計)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87077.html
摘要:本篇解釋中類的控制指令,與指令式界面設計相關。本專欄歷史文章介紹一項讓可以與抗衡的技術可視化開發工具非正經入門之一三宗罪可視化開發工具非正經入門之二分離界面設計可視化開發工具非正經入門之三雙源屬性與數據驅動可視化開發工具非正經入門之四 本系列博文從 Shadow Widget 作者的視角,解釋該框架的設計要點。本篇解釋 Shadow Widget 中類 Vue 的控制指令,與指令式界面...
摘要:前言非正經入門是相對正經入門而言的。不過不要緊,正式學習仍需回到正經入門的方式。快速入門建議先學會用拼文寫文檔注冊一個賬號,把庫到自己名下,然后用這個庫寫自己的博客,參見這份介紹。會用拼文寫文章,相當于開發已入門三分之一了。 本系列博文從 Shadow Widget 作者的視角,解釋該框架的設計要點,既作為用戶手冊的補充,也從更本質角度幫助大家理解 Shadow Widget 為什么這...
閱讀 2211·2019-08-30 15:54
閱讀 1947·2019-08-30 13:49
閱讀 666·2019-08-29 18:44
閱讀 824·2019-08-29 18:39
閱讀 1104·2019-08-29 15:40
閱讀 1524·2019-08-29 12:56
閱讀 3134·2019-08-26 11:39
閱讀 3094·2019-08-26 11:37