该文档已经过期。新的组件反解基于数据和模板匹配的机制,代替原来的标记机制。
版本
:< 3.4.0
提示
:通过 San 进行服务端渲染,一定能通过相同版本的 San 在浏览器端进行反解。
概述
组件初始化时传入 el,其将作为组件根元素,并以此反解析出视图结构。
该特性的意图是:有时我们为了首屏时间,期望初始的视图是直接的 HTML,不由组件渲染。但是我们希望组件为我们管理数据、逻辑与视图,后续的用户交互行为与视图变换通过组件管理。
1 | var myComponent = new MyComponent({ |
以 el 初始化组件时,San 会尊重 el 的现时 HTML 形态,不会执行任何额外影响视觉的渲染动作。
- 不会使用预设的 template 渲染视图
- 不会创建根元素
- 直接到达 compiled、created、attached 生命周期
但是,el 可能需要一些额外的标记,来帮助 San 认识数据与视图的结构(插值、绑定、循环、分支、组件等),以便于后续的行为管理与视图刷新。
数据和视图是组件重要的组成部分,我们将从这两个方面说明组件反解的功能。
提示
:如果使用 NodeJS 做服务端,San 提供了 服务端渲染 的支持,能够天然输出标记好可被组件反解的 HTML,你无需了解组件反解的标记形式。如果你服务端使用其他的语言(比如PHP),请继续往下阅读。
视图
该章节介绍如何对视图的结构(插值、绑定、循环、分支、组件等)进行标记。
插值文本
插值文本的标记方式是:在文本的前后各添加一个注释。
- 文本前的注释以 s-text: 开头,紧跟着插值文本的声明。
- 文本后的注释内容为 /s-text,代表插值文本片段结束。
1 | <span><!--s-text:{{name}} - {{email}}-->errorrik - errorrik@gmail.com<!--/s-text--></span> |
提示
:s- 开头的 HTML Comment 是重要的标记手段,在循环与分支标记中也会用到它。
插值属性
插值属性的标记方式是:在 prop- 为前缀的属性上声明属性的内容。
1 | <span title="errorrik - errorrik@gmail.com" prop-title="{{name}} - {{email}}"></span> |
绑定
绑定属性的标记方式与插值属性完全一样:在 prop- 为前缀的属性上声明属性的内容。
1 | <ui-label prop-title="{{name}}" prop-text="{{jokeName}}"></ui-label> |
双向绑定用 {= expression =} 的形式。
1 | <input prop-value="{=name=}" value="errorrik"> |
循环
对于循环,我们需要以桩元素,分别标记循环的 起始 和 结束。
1 | <ul id="list"> |
起始 的桩元素标记是一个以 s-for: 开头的 HTML Comment,接着是声明循环的标签内容。
1 | <!--s-for:<li s-for="p,i in persons" title="{{p.name}}">{{p.name}} - {{p.email}}</li>--> |
结束 的桩元素标记是一个内容为 /s-for 的 HTML Comment。
1 | <!--/s-for--> |
对于循环的每个元素,按照普通元素标记,无需标记 for directive。通常它们在 HTML 输出端也是以循环的形式存在,不会带来重复编写的工作量。
1 | <li prop-title="{{p.name}}" title="otakustay"><!--s-text:{{p.name}} - {{p.email}}-->otakustay - otakustay@gmail.com<!--/s-text--></li> |
提示
:当初始没有数据时,标记循环只需要声明 起始 和 结束 桩即可。
分支
分支的标记比较简单,以 s-if 标记分支元素即可。
1 | <span s-if="condition" title="errorrik" prop-title="{{name}}"></span> |
当初始条件为假时,分支元素不会出现,此时以 HTML Comment 为桩,标记分支。在桩的内部声明分支的语句。
1 | <!--s-if:<span s-if="cond" title="{{name}}">{{name}}</span>--><!--/s-if--> |
一个包含完整 if-else 的分支,总有一个元素是具体元素,有一个元素是桩。
1 | <!--s-if:<span s-if="isErik" title="{{name}}">{{name}}</span>--><!--/s-if--> |
1 | <span s-if="isErik" title="errorrik" prop-title="{{name}}"><!--s-text:{{name}}-->errorrik<!--/s-text--></span> |
组件
1 | san.defineComponent({ |
组件的标记与视图模板中声明是一样的,在相应的自定义元素上标记绑定。San 将根据自定义元素的标签自动识别组件。
1 | <ui-label prop-title="{{name}}" prop-text="{{email}}"> |
我们可能因为样式、兼容性等原因不想使用自定义元素。当组件未使用自定义元素时,可以在元素上通过 s-component 标记组件类型。
1 | <label s-component="ui-label" prop-title="{{name}}" prop-text="{{email}}"> |
slot
slot 的标记与循环类似,我们需要以桩元素,分别标记循环的 起始 和 结束。
1 | <div id="main"> |
1 | var Tab = san.defineComponent({ |
起始 的桩元素标记是一个以 s-slot: 开头的 HTML Comment,接着是 slot 名称。
1 | <!--s-slot:title--> |
结束 的桩元素标记是一个内容为 /s-slot 的 HTML Comment。
1 | <!--/s-slot--> |
当 owner 未给予相应内容时,slot 的内容为组件内声明的默认内容,这时 slot 内环境为组件内环境,而不是组件外环境。对默认内容,需要在 起始 的桩元素 name 之前加上 ! 声明。
1 | <!--s-slot:!title--> |
数据
组件的视图是数据的呈现。我们需要通过在组件起始时标记 data,以指定正确的初始数据。初始数据标记是一个 s-data: 开头的 HTML Comment,在其中声明数据。
1 | <div id="wrap"> |
1 | var myComponent = new MyComponent({ |
警告
:如果 HTML 中只包含视图结果,不包含数据,组件无法从视图的 DOM 结构中解析出其代表数据,在后续的操作中可能会导致不期望的后果。
比如,对于列表数据应该在初始化时保证数据与视图的一致,因为列表的添加删除等复杂操作与视图更新上关系密切,如果一开始对应不上,视图更新可能产生难以预测的结果。
1 | <ul id="list"> |
1 | var myComponent = new MyComponent({ |
提示
:如果一个组件拥有 owner,可以不用标记初始数据。其初始数据由 owner 根据绑定关系灌入。
1 | <!-- ui-label 组件拥有 owner,无需进行初始数据标记 --> |
1 | var Label = san.defineComponent({ |