riotjs 2.2.4
riotjs
riotjs一款小型的10000star mvp框架。目前进化至3.x版本了。读者注意,本篇文章介绍的是2.2.4哦。为啥介绍这款啊,是因为那个啥,preact面向现代浏览器,对我来说不咋好使。
riotjs从出生到现在总共经历了3个大版本,基本上每个都不一样,1.x最为简陋,可以视之就一个简单的mvc框架哦,模板引擎也是简单的不要不要的,2.x版本完善了各项功能,并且强化了controller的作用,使之成为一个真正的MVP框架。3.x版本使用了大量es6,es5新增方法进行重构,对svg支持,模板引擎,事件系统,内存使用等进行了一定程度的优化。(实际从2.3开始就往现代浏览器上靠了)
为何选用
由于riotjs小,容易和其他框架混合使用
特点
小,但经不起强渲染
支持ie8吗
riotjs 2.2.4是最后一个支持ie8的版本。(然而事实上,代码中使用了一些es5新增的方法,这些方法要ie9才支持,以至于我们不得不使用es5shiv/es5sham来进行兼容);
静态方法
riot.observable
riot的事件系统,所有事件通知方式都基于该模块,可全局使用
// 发送全局事件 var window.eventBus = riot.observable(); eventBus.on('test', function (e) { console.log(e); }); eventBus.trigger('test', 123);
包含方法
- on(events, fn)
- off(events, fn)
- trigger(name[,arguments])
- one(name, fn)
riot.mixin
作用是向内部对象mixins添加属性或方法,该对象无保护,所以必须要人为保证命名时不冲突.
// 在外部使用 riot.mixin('testfunction', function () { console.log(2) }); var c = riot.mixin('testfunction'); c() // print 2
该方法一般提供给riot tag初始化实例的时候使用。当在tag类中使用this.mixin混入方法的时候,会将内部对象mixins上的方法或属性混合到tag类上
riot.route
2.2.4版本的riot.route是一个功能超弱的路由管理器,通过监听hashchange事件来触发注册的路由回调。该路由模块是自动启动的。而且它的实现上是有缺陷的。它本质上是个事件分发器。
// 使用示例 riot.route(function (path, module, action, params) { console.log(path, module, action, params) }); riot.route('search/index/search/1234');
包含方法
riot.route(arg)
- 2.2.4版本里arg接受2种类型,字符串和function,上面已经给出示例。需要自己去分出路径,模块,行为和参数。你没看错,就是这么弱
riot.route.exec(fn)
- 解释当前哈希路径,并把参数传递到fn里
riot.route.parser(fn)
- 指定哈希路径解释器,如果未调用该方法,固定解释方法是 path.split('/');如示例所示
riot.route.stop()
- 销毁监听hashchange事件,销毁路由事件
riot.route.start()
- 监听。默认是开启的
riot.util
包含两个内容,brackets和tmpl, brackets是tmpl的辅助函数,单独使用意义不大,该辅助函数可以通过正则或者索引制造我们需要匹配的部分。tmpl是riotjs的模板引擎核心,html字符串拼接完全通过该引擎,可独立使用(在npm上有独立维护的模块名为riot-tmpl)
// 设置模板占位符(默认是{ }) riot.settings.brackets = '{{ }}'; // 使用 var html = riot.util.tmpl('<div>{{a}}</div>', { a:1 }); console.log(html); //print <div>1</div>
riot.tag(name, html, css, attrs, fn)
全局注册一个riot标签, css attrs参数可省略。其实质是向一个内部对象tagImpl上创建了一个名为name的属性,其值是{name,html,css,attrs,fn}。此时该缓存并没有被使用,tag的实例并没有建立。
riot.tag( 'ri-root', [ '<ri-login if={showLogin}></ri-login>', '<ri-error if={showError}></ri-error>' ].join(''), function () { var self = this; this.showLogin = false; this.showError = false; this.on('mount', function () { var device_id = window.Qutils.getParams('device_id'); if (!device_id) { alert('参数device_id缺失!'); } else { setTimeout(function () { bridge.isInApp( function () { self.showLogin = true; self.tags['ri-login'].trigger('login-init', device_id); self.update(); }, function () { self.showError = true; self.update(); } ) }, 100); } }); } );
riot.mount & riot.mountTo
riot.mountTo只是riot.mount的别名。该方法顾名思义,挂在riot标签(组件)。会返回一个tag的实例。
参数
selector
- 接受'*'(mount所有), string split with ',' , string(使用原生的Selectors API,获取一个NodeList),或者接受一个NodeList,Element
tagName
- 接受'*'(mount所有), object(当为Object类型时,即为opts),string(等同mount所有selector上下文下的tagName匹配tag)
opts
- Object,传入的参数对象,可直接混合在tag实体的opts对象上
<!-- example --> <div id="test"></div> <script> var tag = riot.mount('#test', '*', {a:1,b:2}); console.log(tag[0].opts.a) // print 1 </script>
riot.update
更新所有的tag实体,实质是调用每个实体的update方法。
riot的tag实例方法
上文说到riot中所用通过riot.tag声明的custom tag都只是缓存了,而没有立刻产生tag实例。实际上tag实例是在执行riot.mount的时候被创建的。所有的riot tag实例都是由内部构造器Tag实例化而来的。而对于一个多tag嵌套的组件,其实是递归先将子tag从底部实例化完,当实例化完成,会从根部到底部依次触发mount事件~
this.isMounted
true | false, 指示tag是否完成安装
this._id
一个自增的id,用于唯一代表该实例
this.parent
若一个tag实例有父实例,这个parent指向父实例
this.on('mount', function () { this.parent && this.parent.trigger('child-mounted'); }.bind(this))
this.root
该属性指向tag实例所表示的真实dom元素,另外root._tag同样挂载了tag实例的引用,所以当你的个自定义标签实例化以后,你还可以通过这样的姿势找到tag实例
var tag = riot.mount('custom-tag'); console.log(tag[0].root); // print <custom-tag></custom-tag> console.log(document.querySelector('custom-tag')._tag) // print Tag {xxx}
this.opts
哦,这个的构造器是Child,不过特的原型指向你传入的opts的引用。所以如果不想自己配置被改动,请乖乖深度克隆
var tag = riot.mount('custom-tag', {a:1,b:2,c:3}); console.log(tag[0].opts.a); // print 1
另外如果要在父子tag间传递参数也是很好玩的.
riot.tag( 'hhh', '<zzz myoptions={this.opts.test}></zzz>', function () {} ); riot.tag( 'zzz', '<div>{this.opts.myoptions}</div>', function () {} ); var new_custom_tag = document.createElement('hhh'); document.body.appendChild(new_custom_tag); var custom_tag = riot.mount('hhh', {test:1}); console.log(custom_tag[0].tags['zzz'].opts.myoptions); // print: 1
// 注: riot没有state机制,要通过attributes传值,小心undefined
需要注意的是,由于他遍历的是dom.attributes,你玩表单的时候小心一点。
this.tags
这里面放了子tag的实例的引用,上面的示例中有类似用法。多个同名子tag会放在数组里,我记不得在哪个版本里了,即使是一个子tag也会放在数组里。
this.update(data)
这个操作分为几个步骤:
一:源码里明确写到,执行该方法先判断data对象里有没有可能覆盖tag实例属性的属性,如果有,丢弃。注意,该处过滤数据使用的是浅复制,如果是对象套对象,你要小心了。
二:如果是循环产生的tag(注意,如果不是在custom-tag上使用each循环产生,不会去继承,因为此时isLoop为undefined),重新从父tag获取需要继承的值
riot.tag( 'hhh', '<zzz each={eee} myoptions={opts.test}></zzz>', function () { this.eee = [{a:1,b:2},{a:1,b:2}]; this.eeeeeeeeee = 43214321; this.fdsafsdf = 3253425432 } ); riot.tag( 'zzz', '<div>{a} {b}</div>', function () { this.fffff = 4325345342543} ); var new_custom_tag = document.createElement('hhh'); document.body.appendChild(new_custom_tag); var custom_tag = riot.mount('hhh', {test:1}); console.log(custom_tag[0].tags.zzz[0].fdsafsdf) // print 3253425432
riot.tag( 'hhh', '<div each={eee}><zzz myoptions={opts.test}></zzz></div>', function () { this.eee = [{a:1,b:2},{a:1,b:2}]; this.eeeeeeeeee = 43214321; this.fdsafsdf = 3253425432 } ); riot.tag( 'zzz', '<div>{a} {b}</div>', function () { this.fffff = 4325345342543} ); var new_custom_tag = document.createElement('hhh'); document.body.appendChild(new_custom_tag); var custom_tag = riot.mount('hhh', {test:1}); console.log(custom_tag[0].tags.zzz[0].fdsafsdf) // print undefined
该特性可能会对您造成困扰,啥时候误操作都可能一头雾水不造为什么。
三:混合其他数据和属性到当前tag上
四:更新视图,删除dom传导属性,重置事件。(所以说如果你浏览器在dom回收和事件回收上有问题,那你更新的时候就相当捉急了,在最新的版本里把这个泄漏点给堵上了)
this.mixin()
接受无数多个字符串参数,内部运行 riot.mixin[arguments[i]]将需要混入的属性或方法混入到实例上~~前面介绍过了。在2.2.4以前的版本没有使用.bind(this)来参入作用域,略蛋疼的说,2.2.4 对混入的方法都bind了当前作用域。另外,注意init是很特殊的,在混入时会自动执行。
riot.mixin({ init: function () { this.a = 1; console.log('init') } }); riot.tag('test', '<div></div>', function () { this.mixin('init'); }); var a = document.createElement('test'); document.body.appendChild(a); var custom_tag = riot.mount('test'); console.log(custom_tag[0].a) // print 1
this.mount()
将该实例强制重新装载一遍
this.unmount(keepRootTag)
传入参数,如果为true,会把初始化用的那个根节点也删球掉。该方法用于卸载实例,释放内存。
on, off, trigger, one
通过riot.observable混入的事件方法,然后我们可以在不同tag实例上到处传播事件了,建议使用一个集线器把事件管理起来,或者使用其他玩意,比如riot-flux什么的来玩。
生命周期
to be continue
后记
这个框架跟虚拟dom没撒关系,因为完全没有diff算法。。。在更新视图的时候用了文档碎片凑~,效果还凑合