微型框架 Riot.js 特性一览

注:本文基于Riot.js v2.5.0版本

Riot.js简介

  • 类似 React 的 微型 UI 库
  • 自定义标签/虚拟 DOM
  • 适合编写独立组件
  • MVP 架构
  • 支持 IE9+ ??

1.自定义标签

  1. 布局与逻辑耦合,可重用组件
  2. 实际上的语法糖—>编译为 JS
  3. 虚拟 DOM
    • 单向的数据传输: update 或 unmount 都是从 父亲->孩子
    • 预编译和缓存表达式,解析更加高效
    • 预处理器
    • 可用于服务器端
  4. 语法友好
    • 强大的属性缩写: class={enable: true, hidden: false}
    • 不需要额外的手动绑定,无需记忆 render,state,constructor 等
    • 可插值使用: Add #{items.length + 1} 或 class="item {selectd: true}"
    • 逻辑代码,可不放在 script 标签内
    • 可使用部分 ES6(完全使用需结合 Babel)

2.mixin

Mixin 可以将公共代码在不同标签之间方便地共享,可以混入 Object 和 new function(){}。

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>

声明式 mixin 可在文件之间和项目之间共享 mixin:

riot.mixin("defaultData", {
    author: "ddfe",
    email: "shield@didichuxing.com"
});

// in custom tag
this.mixin("defaultData"); 

3.事件

  • 自定义标签创建过程:
    1. 创建标签实例
    2. 标签定义中的 JavaScript 被执行
    3. HTML 中的表达式被首次计算并首次触发 “update” 事件
    4. 标签被加载 (mount) 到页面上,触发 “mount” 事件
  • 监听生命周期事件
    • before-mount
    • mount
    • update(改写上下文数据)
    • updated(操作 DOM)
    • before-mount
    • unmount
  • 表达式更新方式:
    1. 当一个事件处理器被调用后自动更新。可以在事件处理器中设置 e.preventUpdate = true 来禁止这种行为。
    2. 当前标签实例的 this.update() 方法被调用时
    3. 当前标签的任何一个祖先的 this.update() 被调用时. 更新从父亲到儿子单向传播。
    4. 当 riot.update() 方法被调用时, 会更新页面上所有的表达式。

    ​ ​

4.表达式

100%纯 JavaScript:

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

可放在 html 节点中,也可作为文本节点嵌入:

<h3 id={ /* 属性表达式 */ }>
  { /* 嵌入表达式 */ }
</h3>

注:可通过riot.settings.brackets自定义花括号

5.杂烩

  • 嵌套标签 <account> <subscription plan={ opts.plan } show_details="true" /> </account> <subscription> <h3>{ opts.plan.name }</h3> // 取得标签选项 var plan = opts.plan, show_details = opts.show_details // 访问父标签实例 var parent = this.parent </subscription> 父标签的参数通过 riot.mount 方法的参数设置,而子标签的选项通过标签属性来传递。 <script> riot.mount('account', { plan: { name: 'ddfe', age: '4' } }) </script> ​
  • 嵌套HTML <!--definition--> <my-tag> <p>Hello <yield/></p> this.text = 'world' </my-tag> <!--apply--> <my-tag> <b>{ text }</b> </my-tag> <!--result--> <my-tag> <p>Hello <b>world</b><p> </my-tag>
  • 带有 name 或 id 属性的 DOM 元素将自动被绑定到上下文中,可直接访问
  • 事件处理器 <login> <form onsubmit={ submit }> </form> // 上面的表单提交时调用此方法 submit(e) { } </login> //e.currentTarget 事件处理器的所属元素 //e.target 发起事件的元素 //e.which 键盘事件中的键值 //e.item 循环中的当前元素 ​
  • 渲染条件:if = {expression} show hide
  • 循环:each = {items}(对象数组)or each = {name, i in items}(非对象数组) or each = {name,value in items}(对象)(不建议使用) 循环中的每一项将建立一个新的上下文,子标签通过 parent 访问父标签定义的方法和属性。
  • 使用标准 HTML 元素作为标签 <ul riot-tag="my-tag"></ul> riot.mount('my-tag')
  • 服务端渲染 var riot = require('riot') var timer = require('timer.tag') var html = riot.render(timer, { start: 42 }) console.log(html) // <timer><p>Seconds Elapsed: 42</p></timer>

6. 编译

自定义标签会被编译为 JavaScript

  • 浏览器内编译 <script src="todo.tag" type="riot/tag"></script> ​
  • 预编译:riot 命令 npm install riot -g # 编译到当前目录 riot some.tag # 编译到目标目录 riot some.tag some_folder # 编译到目标路径 riot some.tag some_folder/some.js # 将源目录下的所有文件编译至目的目录 riot some/folder path/to/dist # 将源目录下的所有文件编译(合并)到单个js文件 riot some/folder all-my-tags.js 参数: -w watch 目录 有变化自动编译 -ext html 指定后缀名 --config config 使用config.js作配置文件 --type 指定 js 处理器 --template 指定 HTML 模板 ​
  • 预处理器 <script type="coffee"></script> ​

7.观察者 Observable(事件触发器)

Riot 提供 Observable 以便组件间通信,实现模块化。

// 方法1,创建一个观察者,返回一个实例,之后该对象便可以触发和监听事件
var ddfe = riot.observable({
});

//方法2,使 ddfe 成为观察者
riot.observable(ddfe);


// 监听事件
ddfe.on("event1", function(data1, data2){
    // 监听event1事件
    // data1 和 data2 是trigger传入的参数
    // data1 = 1, data2 = 2
    console.log(data1, data2);
});

// 发布一个事件
// 该事件带有 1 和 2 作为参数
// 上面的on("event1")的回调fn将会执行
ddfe.trigger("event1", 1, 2);

// 解除 event1 的所有监听,第二个参数可选
// 如果有第二个参数 [function],则只解绑该函数
ddfe.off("event1");

// one 与 on 类似,只是 one 如果执行过一次,就自动解除绑定
ddfe.one("event1", function(data1){
    console.log(data1);
});
ddfe.trigger("event1", 1, 2);

//删除所有事件的所有监听器
ddfe.off('*')

//对所有的事件删除指定的回调函数
ddfe.off('*',fn)

8.路由

一个最小化的路由器实现

功能:

  1. 修改 URL 的 hash 部分
  2. hash 变化时进行通知
  3. 查看当前 hash

API:

  1. riot.route(callback) riot.route(function(collection, id, action) { }) //如果 url 变为 customers/987987/edit,则 //collection = 'customers' //id = '987987' //action = 'edit'
  2. riot.route(filter, callback) // 精确匹配 `/fruit` riot.route('/fruit', function(name) { console.log('The list of fruits') }) // 如果 url 变成 `/blog/2015-09/01`, // 回调的参数将被捕捉成 '2015', '09' 和 '01' riot.route('/blog/*-*/*', function(year, month, date) { console.log('The page of ' + year + '-' + month + '-' date) })
  3. riot.route.create() 返回一个新的路由上下文 <first-tag> <p>First tag</p> <script> var subRoute = riot.route.create() // 创建新的路由上下文 subRoute('/fruit/*', function(name) { /* 公用的部分 */ }) </script> </first-tag> <second-tag> <p>Second tag</p> <script> var subRoute = riot.route.create() // 创建新的路由上下文 subRoute('/fruit/apple', function(name) { /* 个别的部分 */ }) </script> </second-tag>
  4. riot.route(to[, title, shouldReplace]) 在内部实现中,
    • 如果没有 shouldReplace, 将使用 history.pushState().
    • 如果有 shouldReplace, 将使用history.replaceState().
  5. riot.route.start() 开始监听路由变化,需要手动调用
  6. riot.route.start(autoExec) riot.route.start(true) <=> riot.route.start()+riot.route.exec()
  7. riot.route.stop()
  8. riot.route.query() // 如果 url 变成 `/search?keyword=Apple&limit=30` 将会匹配 riot.route('/search..', function() { var q = riot.route.query() console.log('Search keyword: ' + q.keyword) console.log('Search limit: ' + q.limit) })
  9. riot.route.base(base)修改基础路径
  10. riot.route.parser(parser[, secondParser])
  11. 其他,路由优先级等
   riot.route('/fruit/apple', function() { /* */ }) // 路由-B (1)
   riot.route('/fruit/orange', function() { /* */ }) // 路由-C (2)
   riot.route('/fruit/*', function(name) { /* */ }) // 路由-A (3)

   riot.route(function() { /* */ }) // 路由-X (3)
   riot.route('/fruit/*', function() { /* */ }) // 路由-Y (1)
   riot.route('/sweet/*', function() { /* */ }) // 路由-Z (2)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券