React 可视化开发工具 Shadow Widget 非正派入门(之二:星散界面设计)

本系列博文从 Shadow Widget 作者的视角,诠释该框架的设想要点。本篇解说转义标签、json-x、投影定义,这几项与 “怎样星散界面设想” 有关。

 

1. 找一个 JSX 替代品

如上一篇 “非正派入门(之一)” 所述,Shadow Widget 要战胜 “JSX浆糊” 的不利影响,要找一个 JSX 替代品。

比如下面 JSX 表达体式格局:

  return <h1 id={user.id}>Dear {user.name}, {welcomeMsg(user)}</h1>;

等效于:

  return React.createElement( H1, {id:user.id},
    "Dear ", user.name, ", ", welcomeMsg(user)
  );

竖立一个 Element,需通报三项信息:ReactClasspropschildren 列表。我们把这三项改形成一个 array 数组花样:

  [ [ReactClass, props],
    child1, child2...
  ]

个中,child1, child2 是子节点定义,花样是 string 字串,或 array 数组。这类以 array 数组表达一个 Element 节点的花样叫 json-x 形貌体式格局,与 JSX 完全等效。

2. 转义标签

为了方便在 html 网页文件中形貌用户界面,我们定义 “转义标签” 的表达体式格局,以下:

  <div $=Panel>
    <div $=P>
      <span $=Span>Referece:</span>
      <span $=A src='http://example.com'>example.com</span>
    </div>
  </div>

转义标签无非将一切 HTML 标签划分为行内标签与 block 标签,前者用 <span> 表达,后者用 <div> 表达,各标签顶用 $=XXX 属性定义的体式格局指导实际运用哪一个 React Class,如 $=Panel 示意用 T.Panel$=P 示意用 T.P。一切经 Shadow Widget 扩展出的构件模板类(也称 WTC,Widget Template Class)都应注册到 T 之下,如许,网页刚翻开时,由转义标签中 $=XXX 指导,能找到响应的 React class,完成一般挂载。

在转义标签中定义的属性,比如上面的 src='http://example.com',在挂载前先整理出 props 表(如 {src:"http://example.com"}),而转义标签的上下级节点的从属关联,以及同级节点之间的前后关联,指清楚明了 json-x 数据中的 children 定义。所以,ReactClasspropschildren 三项信息都有了,转义标签能转换成 json-x,所以它与 JSX 也是等效的。

转义标签具有优越可读性,所以,它在 *.html 文件中能够直接誊写。别的,这类花样对搜索引擎也友爱,若依靠 JSX 定义界面,搜索引擎没法剖析 html 文件中定义了什么信息。

3. 第一眼是妖孽,多数就是妖孽

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 开辟的效劳侧衬着东西,你说妖不妖?

4. 星散界面设想

在星散界面之前,我们还需竖立途径索引机制。

Shadow Widget 经由过程一颗树(Widget 树,R 树)管来由它定义的界面,各节点都有 key 值作标识,既能够显现指定一个 key 值,也能够缺省,缺省时由体系自动天生一个数字来示意。这果颗树的根节点是 ".body",假如根节点下有一个 key 值为 "toolbar" 的 Panel 节点,它的绝对途径就是 ".body.toolbar"

有了途径索引机制,我们能将界面形貌与它的行动定义星散开了。比如这么定义界面:

<div $=BodyPanel key='body'>
  <div $=Panel key='toolbar'>
    <div $=P key='p'>
      <span $=Button key='btn1'>Test</span>
    </div>
  </div>
</div>

这么定义 Test 按钮的行动:

main['.body.toolbar.p.btn1'] = {
  $onClick: function(event) {
    alert('clicked');
  },
};

界面的转义标签在 *.html 文件中誊写,界面元素的行动定义在 *.js 文件举行,云云,界面设想星散出来了,界面形貌与相干元素的行动定义经由过程该元素的绝对途径完成关联。如上例,用 javascript 编写某元素的行动定义,也称 “投影定义”。

5. 表达庞杂的 props 数据

json-x 数据与转义标签都与 JSX 对等,但通报 props 数据有若干限定,比如转义标签不支撑通报函数对象,json-x 可传函数对象,但也不勉励(重要由于不范例)。函数定义应在投影类中定义,就像上面举例的 $onClick 函数,不经由过程转义标签的属性来通报,只在转义标签挂载时,到 main 下找到响应投影定义,然后绑缚响应的函数定义。

除了函数,形貌庞杂的 props 数据时,json-x 的表达能力是完全的,由于它原本就是 javascript 数据,但转义标签受 html 标签花样的影响,要改用 JSON 字串来示意,比如:

  <div $=Panel title='tool bar' width='{400}'>
  </div>

属性值用 '{''}' 括起来,示意它是 JSON 字串,用 JSON.parse 前要先删掉首尾两个花括号,如上面 width 值为 JSON.parse('400')。别的,关于 string 范例的属性值,能够直接通报(避开字串首尾是花括号的情况),没必要按 JSON 字串的体式格局,如上面 title 属性。

6. idSetter 函数

实行界面与底层星散除了投影定义,另有一种指定 idSetter 函数的体式格局,若简朴去明白,该体式格局是投影定义的一个变种,一样完成特定界面元素的行动定义的动态绑捆。

举例来说,界面这么形貌:

<div $=BodyPanel key='body'>
  <div $=Panel key='toolbar'>
    <div $=P>
      <span $=Button $id__='btn1'>Test</span>
    </div>
  </div>
</div>

Javascript 这么定义:

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 ...
};

这类誊写体式格局与上面投影定义的体式格局是等效的,投影类中该在 getInitialState() 中誊写的代码,要挪到 idSetter 函数的 if (value == 1) 分支中,该在 componentDidMount() 中誊写的代码移到 if (value == 2) 的分支中,该在 componentWillUnmount() 中誊写的代码移到 if (value == 0) 的分支中。

运用 idSetter 函数的长处是,响应界面节点的绝对途径没必要完全定义,即途径上各段没必要显式给出 key 值,体系由 $id__='xxx' 属性值,自动找出 idSetter 函数。另一个长处是,编程作风越发函数式。

7. 竖立 W 树供随时节点定位

Flux 框架请求节点间数据流向要恪守严厉的束缚,React 不惜牺牲编程便利性,锐意隐蔽了内建的那颗假造 DOM 树,致使编程中跨节点挪用异常不方便,各节点都被一层黑墙包裹,没法探知四周都有哪些节点存在,幸亏 React 为这个黑墙开了一扇单向玻璃窗:refs,让父节点能够援用子节点,子节点援用不了父节点。战胜援用不方便的解药是引入 redux 那样的框架,把存在交织影响的两个或多个节点中的数据,提升到一个大众地区去编程。

既然 Shadow Widget 引入 MVVM 框架,在 Component 的 API 层面限定节点间互通已不合时宜,单向数据流应该在更高层面的设想去保证。所以,Shadow Widget 引入了 “W 树” 的观点,也就是,一切相符规格的 Component 节点(即源于 WTC 类竖立的节点)都串接在一颗树上。树中各节点都有唯一 “途径” 标示,节点之间还能够用 “相对途径” 或 “绝对途径” 援用,比如:

  this.componentOf('//')   // get parent component
  this.componentOf('//brother')   // brother node
  this.componentOf('sub.child')   // child node
  this.componentOf('./seg.child') // by relative path
  this.componentOf('.body.top.toolbar') // by absolute path

有了 W 树设想,router 计划将变得简朴清楚明了,比如下图界面,把两个可切换的页 ArticleTalk 装到一个导航面板(NavPanel)中,若想切换到 Article 页,按 "/article" 导航,切换另一页用 "/talk" 导航。

《React 可视化开发工具 Shadow Widget 非正派入门(之二:星散界面设计)》

 

(本文完)

本专栏历史文章:

  1. 引见一项让 React 能够与 Vue 对抗的手艺

  2. React 可视化开辟东西 Shadow Widget 非正派入门(之一:React 三宗罪)

    原文作者:程序强
    原文地址: https://segmentfault.com/a/1190000009742514
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞