React 可视化开发工具 shadow-widget 最佳实践(下)

本文介绍 "React + Shadow Widget" 应用于通用 GUI 开发的最佳实践,只聚焦于典型场景下最优开发方法。分上、下两篇讲解,下篇讲述正交框架分析模式与常用调测方法。

查阅上篇 请点击这里,Shadow Widget 开源项目 在这里。

React 可视化开发工具 shadow-widget 最佳实践(下)_第1张图片

1. Controller View 与 View

典型的 Flux 框架中,Store 与 View 之间的关系如下:

React 可视化开发工具 shadow-widget 最佳实践(下)_第2张图片

本图摘自 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 定义各节点行为。把相关节点的投影定义写一起,很大程度消除了节点间隔阂,因为,你能随时可以定义一个变量用来传数据。

2. 正交框架分析模式

接本文上篇的例子,假定我们在原功能基础上,再增加 “全局配置” 的功能,提供 3 个可选项:“自动选 Celsius 还是 Fahrenheit 格式”、“固定用 Celsius”、“固定用 Fahrenheit”。其中,第一个选项 Auto(自动选格式)依据浏览器特性推断国别信息,然后智能选择 Celsius 或 Fahrenheit。

新增如下界面设计:

React 可视化开发工具 shadow-widget 最佳实践(下)_第3张图片

相应的,增加一个 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 的分布如下图:

React 可视化开发工具 shadow-widget 最佳实践(下)_第4张图片

当我们整体设计 GUI 时,应以 MVVM 方式思考。结合本例,也就是规划 config 与 calculator 两个 “功能块”,确定各功能块的入口节点,以及它的上下层串接关系。而处理各个功能块之间联动关系时,应切换到 Flux 单向数据流思考方式。

总结一下,整个 HTML 页面是一颗 DOM 树,是纵向的,在这颗树划分若干功能块的过程,是基于 MVVM 为主的设计过程;而处理各功能块之间横向联系,则以 FRP 思路为主导。这一纵一横的思考方式,我们称为 “正交框架” 分析模式

说明,Flux 是 FRP(Functional Reactiv Programming)开发思想的一种实现,对于 React 开发,上面所提 Flux 与 FRP 基本等效。

至于 FB 之间的功能如何交互,如果处理逻辑简单,不妨在相关 FB 代码块中直接写代码,如果逻辑复杂,不妨取相关 FB 的共有父节点作为入口节点,新设一个 FB 功能块。取共有父节点的主要作用是,该父节点从创建到 unmount,可以覆盖其下所有节点的生存周期,在它的 idSetter 函数中编程会方便一些。

3. 挂载数据来驱动调测

在可视设计器中开发界面的过程,可能存在破坏式重构,比如你在某个 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 文件可构造不同的调测场景,然后用

你可能感兴趣的:(wysiwyg,react.js,html5,javascript)