本文介紹 "React + Shadow Widget" 應用於通用 GUI 開發的最佳實踐,只聚焦於典型場景下最優開發方法。分上、下兩篇講解,下篇講述正交框架分析模式與常用調測方法。
查閱上篇 請點選這裡,Shadow Widget 開源專案 在這裡。
典型的Flux 框架中,Store 與View 之間的關係如下:
本圖摘自fluxxor.com
的「What is Flux?」 一文,Store 中的資料傳遞給一個Component,這個Component 又透過props 屬性驅動多層Component 子節點來展示介面。在這種資料傳遞關係中,多個 Component 都是 View,但從 Store 取得資料的那個 View 比較特殊,稱為 "Controller View"(見上圖)。將 Controller View 與 View 對應到 Shadow Widget 的 MVVM 框架,Controller View 是 VM(ViewModel),由 VM 驅動的子級 Component 就是 V (View)。
然而現實程式設計並非上圖那麼簡單,Controller View 的子節點,也即 View 節點,有時很複雜,其外部若只依賴從上級 props 傳遞下來的資料來驅動渲染,會很彆扭。開發者常不由自主的放棄「純淨」 的程式模式,突破限制,讓View 也從全域變數讀數據,即,變相的把部分數據從Store 分離出去,改用全域變數表達,或乾脆讓View 也直接從Store 讀取數據,而不是只用Controller View 代傳的數據。
Shadow Widget 將問題簡化,既然Store 主要用於存貯數據,那就還原它的數據特性,作為數據,在哪裡定義關係不大,直接拿Component 的屬性存貯數據就好,將Store 併入Component 沒有不可逾越的障礙,當然,前提是我們已設計了「雙源屬性」 與「W 樹」 機制。然後,Controller View 及其下級多個 View,合起來視為一個 FB(Functionarity Block),在同一 Functionarity Block Namespace 下以 javascript 定義各節點行為。把相關節點的投影定義寫一起,很大程度消除了節點間隔閡,因為,你能隨時可以定義一個變數用來傳遞資料。
接本文上篇的例子,假定我們在原始功能基礎上,再增加「全域配置」 的功能,提供3 個可選項:「自動選Celsius 或Fahrenheit 格式」、「固定用Celsius」、「固定用Fahrenheit」。其中,第一個選項 Auto(自動選格式)依據瀏覽器特性推斷國別訊息,然後智慧選擇 Celsius 或 Fahrenheit。
新增以下介面設計:
#對應的,增加一個Functionarity Block,JS 編碼如下:
(function() { // functionarity block var configComp = null; idSetter['config'] = function(value,oldValue) { // ... }; })();
該FB 的入口節點是configComp
。再接著細化設計,我們該將 configComp
定義移到全域變數定義區,因為節點在兩個 FB 功能塊都用到。
為方便講述起見,我們將這兩個FB 分別稱為config 與calculator,以FB 分佈為橫軸,以W 樹為縱軸,W 樹中的節點是層層串聯的,繪製這兩個FB 的分佈如下圖:
當我們整體設計GUI 時,應以MVVM 方式思考。結合本例,也就是規劃 config 與 calculator 兩個 “功能塊”,確定各功能塊的入口節點,以及它的上下層串接關係。 而處理各個功能塊之間連動關係時,應切換到 Flux 單向資料流思考方式。
總結一下,整個HTML 頁面是一顆DOM 樹,是縱向的,在這顆樹劃分若干功能塊的過程,是基於MVVM 為主的設計過程;而處理各功能塊之間橫向聯繫,則以FRP 思路為主導。這縱一橫的思考方式,我們稱為 「正交框架」 分析模式。
說明,Flux 是 FRP(Functional Reactiv Programming)開發想法的一種實現,對於 React 開發,上面所提 Flux 與 FRP 基本等效。
至於FB 之間的功能如何交互,如果處理邏輯簡單,不妨在相關FB 程式碼區塊中直接寫程式碼,如果邏輯複雜,不妨取相關FB 的共有父節點作為入口節點,新設一個FB 功能塊。取共有父節點的主要作用是,該父節點從創建到 unmount
,可以覆蓋其下所有節點的生存週期,在它的 idSetter 函數中編程會方便一些。
在可视设计器中开发界面的过程,可能存在破坏式重构,比如你在某个 FB 的入口节点指定 data
属性值,然后它的子节点根据 data
取值自动生成子节点,如果你给定的 data
初始值格式不对,其下子节点会无法生成。所给初值不对可能因为设计变化了,你的 data
格式还没来得及调整。
为了最大幅度减少上述破坏式重构带来错误,在设计界面时,我们建议用作驱动源头的数据初值应取 “空” 值,比如赋给 null
或 []
之类的值。
界面设计过程中,若想查看不同数据初值会驱动什么样的界面形态,不妨启用 W.$dataSrc
定义,比如前面例子界面缺省显示 Celsius 温度格式,因为 '.body.calculator.field'
节点的 scale
属性初值是 'c'
,现在我们想检查 scale='f'
界面是否正确。按如下方式使用两行代码即可:
if (!window.W) { window.W = new Array(); W.$modules = [];} W.$modules.push( function(require,module,exports) { var W = require('shadow-widget'); var dataSrc = W.$dataSrc = {}; dataSrc['.body.calculator.field'] = { 'scale': 'f' }; });
其中,var dataSrc = W.$dataSrc = {}
表示启用 W.$dataSrc
,缺省是不启用的。另一句 dataSrc['.body.calculator.field'] = { 'scale': 'f' }
,用来预定义哪个节点要附加哪些属性的初值。
上面代码可以写入独立的 js 文件,多个此类 js 文件可构造不同的调测场景,然后用 <script>
标签按需把某一个 js 文件导入到被测页面。
shadow-bootstrap 新近推出 v1.1 版本,Bootstrap 设计方式在 Shadow Widget 体系得到完整支持了。
Bootstrap 提供了优秀的前端控件库,在 shadow-widget 上用 bootstrap 堪称完美,毕竟上百个 class 类谁都记不住,大家做开发时,要不停的查手册。用 shadow-widget 的可视化设计器,只需从右侧样板页拖一个样板扔进来,就创建你想要的构件了,然后选择相应节点,把相关属性配置一下,你的界面很快就做好。
上图是其中一个样板页,该拖入哪个样板,看一眼就能区分,不再受那么多 class 类名困扰了。
刚开始使用 Shadow Widget 时,大家可能不适应它的可视化设计,容易忘掉几项设计约束,简单举几个例子:
在根节点(即 ".body"
节点)下只能摆放面板与绝对定位的构件(如 ScenePage 等),即首层节点要么是 Panel 类构件,要么是指定 position='absolute'
的非行内构件。
绝对定位的构件,应挂到根节点,不宜在其它地方出现。(注:此项为建议,不强制)
面板之下不能直接放行内构件,要在面板下放置 P 类构件后,才能放 Span 类构件。
一个构件要么启用 "html."
属性,要么使用它的若干子节点,两者只能二选一,若定义子节点了,以 "html."
表示文本将被忽略。
总之,与界面设计打交道,设计总是具体的,你要面对各类封装好的构件,不少构件有特殊要求,《Shadow Widget 用户手册》有全面介绍,有必要通读该手册。
Shadow Widget 最佳实践还建议团队成员要按技能分工,至少有两个工种,其一是能运用他人已封装好的 WTC 类或库化 UI,进行 GUI 开发;其二是为他人封装 WTC 类或库化 UI。前者对技能要求不高,后者则要求深入掌握 React 与 Shadow Widget 知识。
对于微型团队,也应按上述分工的思路规划您的产品开发,因为这两种分工的目标不同,前者着眼短期目标,尽快把产品做出来,把界面弄漂亮些,后者着眼中长期目标,用封装库提高开发复用率。即使是单人团队,同样需要分工,无非在时间上错开,一段时间承担一种角色,另一段时间换一个角色。
Shadow Widget 当前的 WTC 类与界面库还不够丰富,但它的架子已搭好,起点不低。它的定制扩展能力出色,已通过一些上规模代码的产品检验,如 shadow-bootstrap
, pinp-blogs
等,具备一定成熟度。我们有理由相信,这个产品会随着扩展库逐渐增多,前景越来越光明。
以上是React 視覺化開發工具 shadow-widget介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!