API · 自定义标签

加载

riot.mount(customTagSelector, [opts])

其中

// 选中并加载页面上所有的<pricing> 标签
var tags = riot.mount('pricing')

// 加载所有class名称是 .customer 的自定义标签
var tags = riot.mount('.customer')

// 加载 <account> 标签并传递一个 API 对象作为参数
var tags = riot.mount('account', api)

@返回值: 加载成功的标签实例的数组

注意:使用 浏览器内编译 的用户需要将 riot.mount 调用放在 riot.compile 中才能获得返回的 标签实例. 不这么做的话 riot.mount 将返回 undefined

<script>
riot.compile(function() {
  // here tags are compiled and riot.mount works synchronously
  var tags = riot.mount('*')
})
</script>

riot.mount(‘*’, [opts])

Riot使用特殊选择器 “*” 来加载页面上所有自定义标签:

riot.mount('*')

@返回值: 加载成功的标签实例的数组

riot.mount(selector, tagName, [opts])

其中

// 加载自定义标签 "my-tag" 到 div#main ,将 api 作为参数
var tags = riot.mount('div#main', 'my-tag', api)

@返回值: 加载成功的 标签实例数组

riot.mount(domNode, tagName, [opts])

将名为 tagName 的自定义标签加载到指定的 domNode 上,将可选的 opts 作为参数. 示例:

// 加载 "users" 标签到 #slide 结点,api参数放在opts里
riot.mount(document.getElementById('slide'), 'users', api)

@返回值: 加载成功的 标签实例 数组

渲染

riot.render(tagName, [opts])

将标签渲染成 html. 只在 服务端渲染 (Node/io.js) 时可用. 例如:

// 将 "my-tag" 渲染成 html
var mytag = require('my-tag')
riot.render(mytag, { foo: 'bar' })

@返回值: html

标签实例

每一个标签实例有如下属性:

你可以在HTML和JavaScript中使用这些变量,例如:

<my-tag>
  <h3>{ opts.title }</h3>

  var title = opts.title
</my-tag>

你可以随意添加数据到一个实例(也称作 “上下文”)中,添加后在HTML表达式中可用。:

<my-tag>
  <h3>{ title }</h3>

  this.title = opts.title
</my-tag>

更新

this.update()

更新当前标签实例及所有子标签实例的所有表达式。此方法在每次用户与应用交互导致事件处理器被调用时会自动调用。

除此之外,riot不会自动更新视力,所以你需要手动调用此方法。通常这发生在某些非UI的事件中: setTimeout, AJAX 调用或服务端消息到达后,例如:

<my-tag>

  <input name="username" onblur={ validate }>
  <span class="tooltip" show={ error }>{ error }</span>

  var self = this

  validate() {
    $.get('/validate/username/' + this.username.value)
      .fail(function(error_message) {
        self.error = error_message
        self.update()
      })
  }
</my-tag>

上例中错误信息在调用 update() 方法后才显示. 我们将 this 赋值给 self 是因为在 AJAX 回调函数内部 this 将指向 response 对象,而不是标签实例。

this.update(data)

设置当前实例的值,并更新表达式。 效果与 this.update() 等价,但可以同时指定要更新的数据。所以,与其写:

self.error = error_message
self.update()

写成:

self.update({ error: error_message })

更简短干净。

riot.update() | #update

更新页面上所有加载的标签实例和它们的表达式。

@返回值: 加载成功的 标签实例 数组.

卸载

this.unmount(keepTheParent)

将当前标签实例及其子孙从页面上移除。会触发 “unmount” 事件. 如果希望移除标签但保留最顶级的实例,需要给unmount方法一个 true 参数

从 DOM 中移除标签:

mytag.unmount()

保留顶级实例,移除所有的子标签:

mytag.unmount(true)

嵌套标签

tags 变量来访问嵌套的标签实例:

<my-tag>

  <child></child>

  // 访问子标签
  var child = this.tags.child

</my-tag>

如果使用了同一个子标签的多个实例,则它们是作为一个数组来访问的 this.tags.child[n] 也可以用 name 属性为嵌套标签实例取一个其它的名称。

<my-tag>

  <child name="my_nested_tag"></child>

  // 访问子标签
  var child = this.tags.my_nested_tag

</my-tag>

子标签实例的初始化发生在父标签实例初始化之后,所以在 “mount” 事件中子标签实例的方法和属性才可用。

<my-tag>

  <child name="my_nested_tag"></child>

  // 访问子标签方法
  this.on('mount', function() {
    this.tags.my_nested_tag.someMethod()
  })

</my-tag>

使用 <yield> 标签来包含内部HTML

<yield> 标签是riot的特殊核心功能,可以在运行时将自定义标签的内部模板进行编译和插入,这个技术使你可以用从服务端渲染生成的html内容来扩展你的标签模板

例如使用下面的 riot 标签 my-post

<my-post>
  <h1>{ opts.title }</h1>
  <yield/>
  this.id = 666
</my-post>

你可以在应用中随时包含 <my-post> 标签

<my-post title="What a great title">
  <p id="my-content-{ id }">My beautiful post is just awesome</p>
</my-post>

riot.mount('my-post') 加载后渲染成:

<my-post>
  <h1>What a great title</h1>
  <p id="my-content-666">My beautiful post is just awesome</p>
</my-post>

多点yield

>=2.3.12

<yield>标签还支持将模板中不同位置的html内容插入到指定的位置。

例如,使用下面的自定义标签 my-other-post

<my-other-post>
  <article>
    <h1>{ opts.title }</h1>
    <h2><yield from="summary"/></h2>
    <div>
      <yield from="content"/>
    </div>
  </article>
</my-other-post>

在应用中可以这样使用<my-other-post>标签:

<my-other-post title="What a great title">
  <yield to="summary">
    My beautiful post is just awesome
  </yield>
  <yield to="content">
    <p>And the next paragraph describes just how awesome it is</p>
    <p>Very</p>
  </yield>
</my-other-post>

使用 riot.mount('my-other-post') 加载后渲染结果是这样的:

<my-other-post>
  <article>
    <h1>What a great title</h1>
    <h2>My beautiful post is just awesome</h2>
    <div>
      <p>And the next paragraph describes just how awesome it is</p>
      <p>Very</p>
    </div>
  </article>
</my-other-post>

yield 与 循环

<yield> 标签可以用在循环中或子标签中,但你必须知道 它总是使用子标签的数据进行解析和编译

看下面的 blog.tag riot 组件


<blog>
  <h1>{ title }</h1>
  <my-post each={ posts }>
    <a href={ this.parent.backToHome }>Back to home</a>
    <div onclick={ this.parent.deleteAllPosts }>Delete all the posts</div>
  </my-post>

  this.backToHome = '/homepage'
  this.title = 'my blog title'

  this.posts = [
    { title: "post 1", description: 'my post description' },
    { title: "post 2", description: 'my post description' }
  ]

  // 本例中需要这个bind来在子标签里也保留父上下文
  deleteAllPosts() {
    this.posts = []

    // 我们需要手动调用 update 函数,因为当前函数由子标签触发,不会自动冒泡到父一级
    this.update()
  }.bind(this)

</blog>

<my-post>
  <h2>{ title }</h2>
  <p>{ description }</p>
  <yield/>
</my-post>

将编译成:


<blog>
  <h1>my blog title</h1>
  <my-post>
      <h2>post 1</h2>
      <p>my post description</p>
      <a href="/homepage">Back to home</a>
      <div>Delete all the posts</div>
  </my-post>
  <my-post>
      <h2>post 2</h2>
      <p>my post description</p>
      <a href="/homepage">Back to home</a>
      <div>Delete all the posts</div>
  </my-post>
</blog>

事件

每个标签实例都是一个 可观察 所以你可以使用 onone 方法来监听发生在标签实例上的事件. 以下是内置支持的事件:

例如:

// 当标签实例不再在DOM树中后,释放资源
this.on('unmount', function() {
  clearTimeout(timer)
})

保留字

上面提到的方法和属性名都是Riot标签的保留字. 不要使用这些作为你的实例变量或方法的名字: opts, parent, root, update, unmount, on, off, onetrigger. 局部变量可以随意命名,例如:

<my-tag>

  // 允许
  function update() { } 

  // 不允许
  this.update = function() { }

  // 不允许
  update() {

  }

</my-tag>

手动创建标签实例

riot.tag(tagName, html, [css], [attrs], [constructor])

不使用编译器“手动”定义一个新的自定义标签.

示例

riot.tag('timer',
  '<p>Seconds Elapsed: { time }</p>',
  'timer { display: block; border: 2px }',
  'class="tic-toc"',
  function (opts) {
    var self = this
    this.time = opts.start || 0

    this.tick = function () {
      self.update({ time: ++self.time })
    }

    var timer = setInterval(self.tick, 1000)

    this.on('unmount', function () {
      clearInterval(timer)
    })

  })

参阅 timer demoriot.tag API 文档了解更多细节和 限制.

警告 使用 riot.tag 就无法利用编译器带来的好处,以下功能不支持:

  1. 自关闭标签
  2. 不带引号的表达式. 只能写 value="{ val }" 不能写 value={ val }
  3. 布尔属性. 只能写 __checked="{ flag }" 不能写 checked={ flag }
  4. ES6 方法简写形式
  5. <img src={ src }> 必须写成 <img riot-src={ src }> 以避免非法的服务器请求
  6. style="color: { color }" 必须写成 riot-style="color: { color }" 才能支持IE

可以象下面这样利用 <template><script> :

<script type="tmpl" id="my_tmpl">
  <h3>{ opts.hello }</h3>
  <p>And a paragraph</p>
</script>

<script>
riot.tag('tag-name', my_tmpl.innerHTML, function(opts) {

})
</script>

riot.Tag(impl, conf, innerHTML)

试验性api

在 riot 2.3 中我们允许开发者访内部的 Tag 实例,以便使开发者能够以更加创新的方式来创建自定义标签。

例如,用ES2015的写法:


class MyTag extends riot.Tag {
  constructor(el) {
    super({ tmpl: MyTag.template() }, { root: el })
    this.msg = 'hello'
  }
  bye() {
    this.msg = 'goodbye'
  }
  static template() {
    return `<p onclick="{ bye }">{ msg }</p>`
  }
}

new MyTag(document.getElementById('my-div')).mount()

一般情况下,不建议使用 riot.Tag 方法。只有在使用前面的riot方法无法满足你的特殊需求的时候才考虑用它。