riotjs一款小型的10000star mvp框架。目前進化至3.x版本了。讀者注意,本篇文章介紹的是2.2.4哦。爲啥介紹這款啊,是由於那個啥,preact面向現代瀏覽器,對我來講不咋好使。css
riotjs從出生到如今總共經歷了3個大版本,基本上每一個都不同,1.x最爲簡陋,能夠視之就一個簡單的mvc框架哦,模板引擎也是簡單的不要不要的,2.x版本完善了各項功能,而且強化了controller的做用,使之成爲一個真正的MVP框架。3.x版本使用了大量es6,es5新增方法進行重構,對svg支持,模板引擎,事件系統,內存使用等進行了必定程度的優化。(實際從2.3開始就往現代瀏覽器上靠了)html
因爲riotjs小,容易和其餘框架混合使用react
小,但經不起強渲染es6
riotjs 2.2.4是最後一個支持ie8的版本。(然而事實上,代碼中使用了一些es5新增的方法,這些方法要ie9才支持,以致於咱們不得不使用es5shiv/es5sham來進行兼容);算法
riot的事件系統,全部事件通知方式都基於該模塊,可全局使用npm
// 發送全局事件 var window.eventBus = riot.observable(); eventBus.on('test', function (e) { console.log(e); }); eventBus.trigger('test', 123);
包含方法數組
做用是向內部對象mixins添加屬性或方法,該對象無保護,因此必需要人爲保證命名時不衝突.瀏覽器
// 在外部使用 riot.mixin('testfunction', function () { console.log(2) }); var c = riot.mixin('testfunction'); c() // print 2
該方法通常提供給riot tag初始化實例的時候使用。當在tag類中使用this.mixin混入方法的時候,會將內部對象mixins上的方法或屬性混合到tag類上緩存
2.2.4版本的riot.route是一個功能超弱的路由管理器,經過監聽hashchange事件來觸發註冊的路由回調。該路由模塊是自動啓動的。並且它的實現上是有缺陷的。它本質上是個事件分發器。mvc
// 使用示例 riot.route(function (path, module, action, params) { console.log(path, module, action, params) }); riot.route('search/index/search/1234');
包含方法
riot.route(arg)
riot.route.exec(fn)
riot.route.parser(fn)
riot.route.stop()
riot.route.start()
包含兩個內容,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標籤, 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.mountTo只是riot.mount的別名。該方法顧名思義,掛在riot標籤(組件)。會返回一個tag的實例。
參數
selector
tagName
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>
更新全部的tag實體,實質是調用每一個實體的update方法。
上文說到riot中所用經過riot.tag聲明的custom tag都只是緩存了,而沒有馬上產生tag實例。實際上tag實例是在執行riot.mount的時候被建立的。全部的riot tag實例都是由內部構造器Tag實例化而來的。而對於一個多tag嵌套的組件,實際上是遞歸先將子tag從底部實例化完,當實例化完成,會從根部到底部依次觸發mount事件~
true | false, 指示tag是否完成安裝
一個自增的id,用於惟一表明該實例
若一個tag實例有父實例,這個parent指向父實例
this.on('mount', function () { this.parent && this.parent.trigger('child-mounted'); }.bind(this))
該屬性指向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}
哦,這個的構造器是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,你玩表單的時候當心一點。
這裏面放了子tag的實例的引用,上面的示例中有相似用法。多個同名子tag會放在數組裏,我記不得在哪一個版本里了,即便是一個子tag也會放在數組裏。
這個操做分爲幾個步驟:
一:源碼裏明確寫到,執行該方法先判斷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回收和事件回收上有問題,那你更新的時候就至關捉急了,在最新的版本里把這個泄漏點給堵上了)
接受無數多個字符串參數,內部運行 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
將該實例強制從新裝載一遍
傳入參數,若是爲true,會把初始化用的那個根節點也刪球掉。該方法用於卸載實例,釋放內存。
經過riot.observable混入的事件方法,而後咱們能夠在不一樣tag實例上處處傳播事件了,建議使用一個集線器把事件管理起來,或者使用其餘玩意,好比riot-flux什麼的來玩。
to be continue
這個框架跟虛擬dom沒撒關係,由於徹底沒有diff算法。。。在更新視圖的時候用了文檔碎片湊~,效果還湊合