Riot.js 中文文档(2)

自定义标签demo(Custom tag )

Riot.js 的自定义标签可以用来构造用户界面(UI)。他们是“View”部分,下面是几个例子:

<todo>

  <h3>{ opts.title }</h3>

  <ul>
    <li each={ items }>
      <label class={ completed: done }>
        <input type="checkbox" checked={ done } onclick={ parent.toggle }> { title }
      </label>
    </li>
  </ul>

  <form onsubmit={ add }>
    <input name="input" onkeyup={ edit }>
    <button disabled={ !text }>Add #{ items.length + 1 }</button>
  </form>

  <script>
    this.disabled = true

    this.items = opts.items    edit(e) {
      this.text = e.target.value    }

    add(e) {
      if (this.text) {
        this.items.push({ title: this.text })
        this.text = this.input.value = ''
      }
    }

    toggle(e) {
      var item = e.item
      item.done = !item.done      return true
    }
  </script></todo>

这些自定义标签会被编译成JS。可以在Riot.js 官网看到关于编译的说明。


  • 标签语法

一个Riot.js 的自定义标签是HTML(布局)和JS(逻辑)的组合。

  • 首先定义HTML,JS跟在后面,<script>标签包裹JS代码是可选的(注意,在外部的.tag里是不能用的)

  • 如果不写<script>,那JS只能跟在最后一个HTML标签结束后(以后的版本可能会完全支持混写)

  • 自定义标签可以是空的,也可以只用HTML或者只有JS

  • <foo bar={ baz }>或者 <foo bar="{ baz }">都可以,引号是可选的

  • 支持ES6的方法语法,比如 methodName() 会变成 this.methodName = function(),这个变量总是指向当前自定义标签的实例

  • 类名的简写语法可用了:如果那个done是true的话, class={ completed: done } 会被渲染成  class="completed"

  • 当表达式值是false时,逻辑属性(checked, selected 什么的)都将被忽略 <input checked={ undefined }> 会变成<input>

  • 所有的属性名称必须小写,这是W3C规范

  •  支持自结束标签:<div/> 等于 <div></div>,众所周知的"打开标签"如 <br>,<hr>,<img>或 <input>在编译之后永远不会关闭。

  • 自定义标签必须被关闭,不管是自结束标签还是普通标签

  • 标准的HTML标签也可以被自定义,不过不建议这么用


标签的定义要放在.tag文件中每一行的开头,不能有缩进

<!-- works -->
<my-tag></my-tag>
<!-- also works -->
<my-tag>
</my-tag>
      <!-- this fails, because of indentation -->
      <my-tag>
      </my-tag>

放在HTML文档里的tag定义(即内联JS里)必须正确的缩进,即所有的自定义标签保持一致的缩进等级,可以混用tab和空格


  • 无<script>的标签

你可以不要<script>标签的

<todo>
  <!-- layout -->
    <h3>{ opts.title }</h3>
   // logic comes here
  this.items = [1, 2, 3]
  </todo>

目前的版本中,这种不要<script>的形式只能是这样:JS只能跟在最后一个HTML标签结束后(不包括那个todo)。接下来的例子中将全部采用这种语法。


  • 预处理器(即JS的扩展语言)

你可以指定一个预处理器,即在<script>上加一个type属性,目前支持的有“coffee”“typescript”“es6”或者不写,当然你也可以在前面加个text/,比如text/coffee


  • 标签样式

你可以在自定义标签里加个<style>标签,Riot会自动把它放到一个外部文件并在head中引入

<todo>
<!-- layout -->
<h3>{ opts.title }</h3>

  <style>
    todo { display: block }
    todo h3 { font-size: 120% }   
     /** other tag specific styles **/  
  </style>
  </todo>


  • Scoped CSS

Scoped CSS可用啦,Scoped CSS

<todo>
<!-- layout -->
<h3>{ opts.title }</h3>

 <style scoped>
   :scope { display: block }
   h3 { font-size: 120% }    
   /** other tag specific styles **/ 
 </style>
 </todo>

这只会发生一次,不管标签被初始化多少次

为了更方便的重载CSS,你可以在<head>中指定Riot应注入的标签的样式

<style type="riot"></style>

demo用了个在normalize.css之后但在你样式之前的组件库中插入标签的样式,以便允许你覆盖库之前的样式


Mounting

(笔者注:意为为装载、编译)

一旦定义了个自定义标签你就可以在页面上mount它

<body>
<!-- place the custom tag anywhere inside the body -->
<todo></todo>
<!-- include riot.js -->
<script src="riot.min.js"></script>
<!-- include the tag -->
<script src="todo.js" type="riot/tag"></script>
<!-- mount the tag -->
<script>riot.mount('todo')</script>
</body>


在页面里的(笔者注,即内联的)自定义标签需要需要被正常关闭,但暂不支持自关闭的标签<todo/>

这是mount的集中用法:

// mount all custom tags on the page
riot.mount('*')
// mount an element with a specific id
riot.mount('#my-element')
// mount selected elements
riot.mount('todo, forum, comments')

一个页面中可以包含同种标签的多个实例


选项(options)

在第二个参数里面你可以传递options

<script>
riot.mount('todo', { title: 'My TODO app', items: [ ... ] })
</script>

不管是一个简单的对象还是某网站API,或者作为Flux架构的存储,这取决与你设计的体系结构


在标签里面,options能和ops变量一起被引用:

<my-tag>
<!-- Options in HTML -->
<h3>{ opts.title }</h3>
// Options in JavaScript
var title = opts.title
</my-tag>


‍‍Mixins

Mixins提供了一种很简单的方案用来在标签间共享函数。但一个标签被Riot编译完成后,所有定义的Mixins会被添加到那个标签中:

var OptsMixin = {
    init: function() {
      this.on('updated', function() { console.log('Updated!') })
    },

    getOpts: function() {
        return this.opts    },

    setOpts: function(opts, update) {
        this.opts = opts
        if(!update) {
            this.update()
        }

        return this
    }}

    <my-tag>
    <h1>{ opts.title }</h1>
    this.mixin(OptsMixin)
    </my-tag>

在这个demo中,你给了<my-tag>所有实例以OptsMixin提供的getOpts 和setOpts 方法。init 方法是特殊的能在当他加载标签时初始化Mixin的方法。init方法在它混合进的方法中是不可访问的。

var my_tag_instance = riot.mount('my-tag')
[0]console.log(my_tag_instance.getOpts())
//will log out any opts that the tag has

标签将接受任何对象,{'key': 'val'} var mix = new function(...)——当传递给他其他类型就会报错

现在除了init方法之外,<my-tag>已经有了一个getId 方法以及其他被OpsMixins一起定义进去的东西

function IdMixin() {
    this.getId = function() {
        return this._id
    }
}
     var id_mixin_instance = new IdMixin()
<my-tag>
    <h1>{ opts.title }</h1>
    this.mixin(OptsMixin, id_mixin_instance)
</my-tag>

通过在标签级别上被定义,Mixin不仅能扩展你自定义标签的功能,还允许用在一个可重复的界面里。每次mount一个标签,甚至是指标签,每个实例将拥有混进去的代码


  • 共享Mixin

如果要在不同文件和项目中共享Mixin,Riot提供了个叫 riot.mixin 的API。你可以用下面这种方式在全局范围内注册你的Mixin:

riot.mixin('mixinName', mixinObject)

如果要把Mixin加载到标签里去,有个叫 mixin() 的方法:

<my-tag>
    <h1>{ opts.title }</h1>
    this.mixin('mixinName')
</my-tag>


标签的生命周期

每个标签都是按下面的顺序创建的:

  1. 标签被构造

  2. 执行标签的JS逻辑

  3. 计算HTML的表达式再触发 ”update“ 事件

  4. 标签被mount到页面上,然后触发mount事件


标签被mount后按照这些步骤更新:

  1. 自动调用事件处理器(除非你在事件处理器里单独设置 e.preventUpdate = true)比如在前文中说到的toggle

  2. 在当前标签的实例上调用 this.update()

  3. 在父级和父级以上的标签实例上调用 this.update(),再更新从父到子的单向数据流

  4. 当 riot.update() 被调用时,就会在全局范围内更新所有的表达式


当标签更新时,这个”更新“事件也会被触发

所有的变量都会在mount除了 <img src={ src }>这种会失败的调用之前被计算


  • 监听生命周期事件

你可以像下面这样监听各种生命周期事件:

<todo>

  this.on('mount', function() {    
  // right after tag is mounted on the page
  })

  this.on('update', function() {    
  // allows recalculation of context data before the update
  })

  this.on('unmount', function() {    
  // when the tag is removed from the page
  })  
  // curious about all events ?
  this.on('mount update unmount', function(eventName) {
    console.info(eventName)
  })
  </todo>

对同一个事件你有很多个监听器可以用,后面会详细讲解


表达式

HTML中可以混入用花括号包裹起来的表达式:

{ /* my_expression goes here */ }

表达式可被设置属性或嵌套文本节点:

<h3 id={ /* attribute_expression */ }>
  { /* nested_expression */ }
 </h3>

所谓表达式就是JS,这是最常见的用法:

{ title || 'Untitled' }
{ results ? 'ready' : 'loading' }
{ new Date() }
{ message.length > 140 && 'Message is too long' }
{ Math.round(rating) }

Riot的目标是让表达式尽可能迷你,也希望你的HTML更干净,如果以后这些表达式越来越复杂庞大,你可以试试把一部分逻辑代码放到”更新“事件上:

<my-tag>  
<!-- the `val` is calculated below .. -->  
<p>{ val }</p>  
// ..on every update
this.on('update', function() {
    this.val = some / complex * expression ^ here  
})
</my-tag>


  • 逻辑属性

像checked、selected这类逻辑属性的值是false时会被忽略的

W3C 说过如果存在该属性那么就该是true— 就算变量为false的空

下面这个就是无效的:

<input type="checkbox" { true ? 'checked' : ''}>

因为只有属性和嵌套的文本表达式是有效的。Riot能检测44中不同的逻辑属性


  • Class ShortHand

对于写CSS的Class,Riot提供了种简洁的语法:

<p class={ foo: true, bar: 0, baz: new Date(), zorro: 'a value' }></p>

这段{}的计算结果为 "foo baz zorro" ,值是true的属性名会被追加到class里。当然如果你有其他需要用的地方,比如id和你自定义的属性什么的,也是可以直接用的


  • 打印花括号

你可以通过转义符号输入无需计算的表达式:

\{ this is not evaluated \} 
//输出如下,但里面未被计算过
{ this is not evaluated }


  • 自定义括号

你可以自由地把括号改成你要的:

riot.settings.brackets = '${ }'
riot.settings.brackets = '{{ }}'

开头结尾之间要加个空格

你要用预编译器的话必须要设计括号选项


  • Etc

注意,在 <style> 标签里的表达式会被无视的


  • 渲染非转义HTML

Riot表达式可以只渲染文本值而无视HTML格式。不过呢,你可以自定义标签来完成这项工作

<raw>
  <span></span>

  this.root.innerHTML = opts.content
</raw>

这步之后就可以在其他标签里用了

<my-tag>
  <p>Here is some raw content: 
  <raw content="{ html }"/> 
  </p>

  this.html = 'Hello, <strong>world!</strong>'
</my-tag>

这里有个demo:demo

不过这可能会被贱人用作XSS攻击,所以请确保你不会从不受信任的来源加载数据

你可能感兴趣的:(js,Riot.js)