摘要:本篇講解轉義標簽投影定義,這幾項與如何分離界面設計有關。找一個替代品如上一篇非正經入門之一所述,要克服漿糊的不利影響,要找一個替代品。本文完本專欄歷史文章介紹一項讓可以與抗衡的技術可視化開發工具非正經入門之一三宗罪
本系列博文從 Shadow Widget 作者的視角,解釋該框架的設計要點。本篇講解轉義標簽、json-x、投影定義,這幾項與 "如何分離界面設計" 有關。
?
1. 找一個 JSX 替代品如上一篇 "非正經入門(之一)" 所述,Shadow Widget 要克服 "JSX漿糊" 的不利影響,要找一個 JSX 替代品。
比如下面 JSX 表達方式:
returnDear {user.name}, {welcomeMsg(user)}
;
等效于:
return React.createElement( H1, {id:user.id}, "Dear ", user.name, ", ", welcomeMsg(user) );
創建一個 Element,需傳遞三項信息:ReactClass,props,children 列表。我們把這三項改造成一個 array 數組格式:
[ [ReactClass, props], child1, child2... ]
其中,child1, child2 是子節點定義,格式是 string 字串,或 array 數組。這種以 array 數組表達一個 Element 節點的格式叫 json-x 描述方式,與 JSX 完全等效。
2. 轉義標簽為了方便在 html 網頁文件中描述用戶界面,我們定義 "轉義標簽" 的表達方式,如下:
Referece: example.com
轉義標簽無非將所有 HTML 標簽劃分為行內標簽與 block 標簽,前者用 表達,后者用 在轉義標簽中定義的屬性,比如上面的 src="http://example.com",在掛載前先整理出 props 表(如 {src:"http://example.com"}),而轉義標簽的上下級節點的從屬關系,以及同級節點之間的前后關系,指明了 json-x 數據中的 children 定義。所以,ReactClass,props,children 三項信息都有了,轉義標簽能轉換成 json-x,所以它與 JSX 也是等效的。 轉義標簽具有良好可讀性,所以,它在 *.html 文件中可以直接書寫。另外,這種格式對搜索引擎也友好,若依賴 JSX 定義界面,搜索引擎無法分析 html 文件中定義了什么信息。 React 支持服務側渲染,這個特性似乎鼓勵了其生態鏈上若干工具額外拓展服務側功能。比如 react-router 中 Router 組件的 history 屬性,既可以是 browserHistory,也可以是 hashHistory。對于前者,客戶側路由(即 URL 路徑)決定服務側如何實現,將兩側的設計捆綁起來的,后者 hashHistory(即 #/some/path)完全在客戶側自主決定,與服務側無關。很顯然,后一方式優于前者,前者違背了軟件設計的 "關注點分離"(Separation of concerns, SOC)原則,并且在實踐上,服務側只有用 webpack-dev-server (加 --history-api-fallback 參數)才能玩得好,不只綁架用 JS 語言,而且綁架用特定工具。要命的是,react-router 官方居然推薦大家首選 browserHistory。 這個 browserHistory 就是充滿妖氣的特性,怪里怪氣,表面看起來有用,實則禁不起推敲。React 的 propTypes 也很妖,官方讓它存活了這么久,最終決定在 v15.5 之后棄用,連 context 也不建議用了,context 本是 React 為緩解跨節點數據共享不便,弄出的不倫不類的東西。 某種程度上 React 的服務側渲染也多少沾點 "妖氣",有些人僅為了解決 SEO 優化用它,仔細想想有點本末倒置了。它的初始需求源于 google 之類的搜索引擎不認 JSX,因為 JSX 服務于編程,編程腳本原不該由搜索引擎關注的,該關注的只是一些靜態文本。處理靜態文本沒必要拉上 React 一家子吧?但事實卻是,我們非得套用一個客戶側編程風格,用 JS 開發的服務側渲染工具,你說妖不妖? 在分離界面之前,我們還需建立路徑索引機制。 Shadow Widget 通過一顆樹(Widget 樹,R 樹)管理由它定義的界面,各節點都有 key 值作標識,既可以顯示指定一個 key 值,也可以缺省,缺省時由系統自動生成一個數字來表示。這果顆樹的根節點是 ".body",如果根節點下有一個 key 值為 "toolbar" 的 Panel 節點,它的絕對路徑就是 ".body.toolbar"。 有了路徑索引機制,我們能將界面描述與它的行為定義分離開了。比如這么定義界面: 這么定義 Test 按鈕的行為: 界面的轉義標簽在 *.html 文件中書寫,界面元素的行為定義在 *.js 文件進行,如此,界面設計分離出來了,界面描述與相關元素的行為定義通過該元素的絕對路徑實現關聯。如上例,用 javascript 編寫某元素的行為定義,也稱 "投影定義"。 json-x 數據與轉義標簽都與 JSX 對等,但傳遞 props 數據有若干限制,比如轉義標簽不支持傳遞函數對象,json-x 可傳函數對象,但也不鼓勵(主要因為不規范)。函數定義應在投影類中定義,就像上面舉例的 $onClick 函數,不通過轉義標簽的屬性來傳遞,只在轉義標簽掛載時,到 main 下找到相應投影定義,然后捆綁相應的函數定義。 除了函數,描述復雜的 props 數據時,json-x 的表達能力是完整的,因為它本來就是 javascript 數據,但轉義標簽受 html 標簽格式的影響,要改用 JSON 字串來表示,比如: 屬性值用 "{" 與 "}" 括起來,表示它是 JSON 字串,用 JSON.parse 前要先刪掉首尾兩個花括號,如上面 width 值為 JSON.parse("400")。另外,對于 string 類型的屬性值,可以直接傳遞(避開字串首尾是花括號的情形),不必按 JSON 字串的方式,如上面 title 屬性。 實施界面與底層分離除了投影定義,還有一種指定 idSetter 函數的方式,若簡單去理解,該方式是投影定義的一個變種,同樣實現特定界面元素的行為定義的動態綁捆。 舉例來說,界面這么描述: Javascript 這么定義: 這種書寫方式與上面投影定義的方式是等效的,投影類中該在 getInitialState() 中書寫的代碼,要挪到 idSetter 函數的 if (value == 1) 分支中,該在 componentDidMount() 中書寫的代碼移到 if (value == 2) 的分支中,該在 componentWillUnmount() 中書寫的代碼移到 if (value == 0) 的分支中。 使用 idSetter 函數的優點是,相應界面節點的絕對路徑不必完整定義,即路徑上各段不必顯式給出 key 值,系統由 $id__="xxx" 屬性值,自動找出 idSetter 函數。另一個優點是,編程風格更加函數式。 Flux 框架要求節點間數據流向要遵守嚴格的約束,React 不惜犧牲編程便利性,刻意隱藏了內建的那顆虛擬 DOM 樹,導致編程中跨節點調用非常不便,各節點都被一層黑墻包裹,無法探知周圍都有哪些節點存在,好在 React 為這個黑墻開了一扇單向玻璃窗:refs,讓父節點可以引用子節點,子節點引用不了父節點。克服引用不便的解藥是引入 redux 那樣的框架,把存在交叉影響的兩個或多個節點中的數據,提升到一個公共區域去編程。 既然 Shadow Widget 引入 MVVM 框架,在 Component 的 API 層面限制節點間互通已不合時宜,單向數據流應該在更高層面的設計去保證。所以,Shadow Widget 引入了 "W 樹" 的概念,也就是,所有符合規格的 Component 節點(即源于 WTC 類創建的節點)都串接在一顆樹上。樹中各節點都有唯一 "路徑" 標示,節點之間還可以用 "相對路徑" 或 "絕對路徑" 引用,比如: 有了 W 樹設計,router 規劃將變得簡單明了,比方下圖界面,把兩個可切換的頁 Article 與 Talk 裝到一個導航面板(NavPanel)中,若想切換到 Article 頁,按 "/article" 導航,切換另一頁用 "/talk" 導航。 ? (本文完) 本專欄歷史文章: 介紹一項讓 React 可以與 Vue 抗衡的技術 React 可視化開發工具 Shadow Widget 非正經入門(之一:React 三宗罪) 文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。 轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/87046.htmlmain[".body.toolbar.p.btn1"] = {
$onClick: function(event) {
alert("clicked");
},
};
idSetter["btn1"] = function(value,oldValue) {
if (value <= 2) {
if (value == 1) { // init process
this.setEvent( {
$onClick: function(event) {
alert("clicked");
},
});
// ...
}
else if (value == 2) { // did mount
// ...
}
else if (value == 0) { // will unmount
// ...
}
return;
}
// render process ...
};
this.componentOf("http://") // get parent component
this.componentOf("http://brother") // brother node
this.componentOf("sub.child") // child node
this.componentOf("./seg.child") // by relative path
this.componentOf(".body.top.toolbar") // by absolute path
摘要:前言非正經入門是相對正經入門而言的。不過不要緊,正式學習仍需回到正經入門的方式。快速入門建議先學會用拼文寫文檔注冊一個賬號,把庫到自己名下,然后用這個庫寫自己的博客,參見這份介紹。會用拼文寫文章,相當于開發已入門三分之一了。 本系列博文從 Shadow Widget 作者的視角,解釋該框架的設計要點,既作為用戶手冊的補充,也從更本質角度幫助大家理解 Shadow Widget 為什么這...
摘要:本篇解釋中類的控制指令,與指令式界面設計相關。本專欄歷史文章介紹一項讓可以與抗衡的技術可視化開發工具非正經入門之一三宗罪可視化開發工具非正經入門之二分離界面設計可視化開發工具非正經入門之三雙源屬性與數據驅動可視化開發工具非正經入門之四 本系列博文從 Shadow Widget 作者的視角,解釋該框架的設計要點。本篇解釋 Shadow Widget 中類 Vue 的控制指令,與指令式界面...
閱讀 2133·2023-04-26 03:06
閱讀 3580·2023-04-26 01:51
閱讀 2086·2021-11-24 09:38
閱讀 2452·2021-11-17 17:00
閱讀 2324·2021-09-28 09:36
閱讀 942·2021-09-24 09:47
閱讀 2587·2019-08-30 15:54
閱讀 1554·2019-08-30 15:44