注:本文基于Riot.js v2.5.0版本
Riot.js简介
-
类似 React 的 微型 UI 库
-
自定义标签/虚拟 DOM
-
适合编写独立组件
-
MVP 架构
- 支持 IE9+ ??
1.自定义标签
-
布局与逻辑耦合,可重用组件
-
实际上的语法糖—>编译为 JS
-
虚拟 DOM
- 单向的数据传输: update 或 unmount 都是从 父亲->孩子
- 预编译和缓存表达式,解析更加高效
- 预处理器
- 可用于服务器端
-
语法友好
- 强大的属性缩写: 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.事件
-
自定义标签创建过程:
- 创建标签实例
- 标签定义中的 JavaScript 被执行
- HTML 中的表达式被首次计算并首次触发 “update” 事件
- 标签被加载 (mount) 到页面上,触发 “mount” 事件
-
监听生命周期事件
- before-mount
- mount
- update(改写上下文数据)
- updated(操作 DOM)
- before-mount
- unmount
-
表达式更新方式:
- 当一个事件处理器被调用后自动更新。可以在事件处理器中设置
e.preventUpdate = true
来禁止这种行为。 - 当前标签实例的
this.update()
方法被调用时 - 当前标签的任何一个祖先的
this.update()
被调用时. 更新从父亲到儿子单向传播。 - 当
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}
(对象数组)oreach = {name, i in items}
(非对象数组) oreach = {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.路由
一个最小化的路由器实现
功能:
- 修改 URL 的 hash 部分
- hash 变化时进行通知
- 查看当前 hash
API:
-
riot.route(callback)
riot.route(function(collection, id, action) { }) //如果 url 变为 customers/987987/edit,则 //collection = 'customers' //id = '987987' //action = 'edit'
-
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) })
-
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>
-
riot.route(to[, title, shouldReplace])
在内部实现中,
- 如果没有
shouldReplace
, 将使用history.pushState()
. - 如果有
shouldReplace
, 将使用history.replaceState()
.
- 如果没有
-
riot.route.start()
开始监听路由变化,需要手动调用
-
riot.route.start(autoExec)
riot.route.start(true) <=> riot.route.start()+riot.route.exec()
-
riot.route.stop()
-
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) })
-
riot.route.base(base)
修改基础路径 -
riot.route.parser(parser[, secondParser])
-
其他,路由优先级等
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)
坚持原创技术分享,您的支持将鼓励我继续创作!
扫描二维码,分享此文章