yano3nora
7/2/2017 - 1:47 PM

[riot: SPA by riot-route] Implement SPA routing by riot with riot-route. #riot #js

[riot: SPA by riot-route] Implement SPA routing by riot with riot-route. #riot #js

Routing on Riot ?

表示するビューをJS側フレームワークで頑張る。親タグの下に複数の子タグをマウントする準備をしておいて、リクエスト(正確にはブラウザのアドレス変更検知)をルータが振り分けてマウントする子タグを選別するような感じ。


riot-route

riotjs の外部ライブラリ。2系では標準で入ってたのに3から外部あつかい。別途npmでインストールして app.js とかで import してやる。使うときは .tag ファイル内で requre('riot-route') 的な?

考え方として、エントリーJSにRouting情報(ベースURLやURLパターンによるマウントタグとマウント先DOM要素ID指定などなど...)を書いておき、表示するタグを切り替えるような使い方をする。SPAは通常アプリケーションのどあたまからJSで成り立つので、初回に全ビューのJSをimportして仮想DOMをエントリーJSでくみ上げておき、各タグは表示された時のことだけ考える。

GitHub

https://github.com/riot/route https://github.com/riot/route/tree/master/doc https://github.com/bourbonizable/riot-routing-sample

Install

$ npm install --save riot-route
// app.js 
import route from 'riot-route'
// ***.tag
const route = require('riot-route')

Sample Code

// app.js (entry)
import riot              from 'riot'
import route           from 'riot-route'
import content_a  from './content_a.tag'
import content_b  from './content_b.tag'

document.addEventListener('DOMContentLoaded', function(){
 
  riot.mount('*')  //mount static tags

  // common routing settings
  route.start()
  route.base('/')

  // routing each tags
  route('/content/a', (...args) => {  
    riot.mount('#content', 'content-a'); 
  })
  route('/content/b', (...args) => {  
    riot.mount('#content', 'content-b');
  })

})

Api

/**
 *  route()
 */
route(function(collection, id, action) {
  // URLが変更されると
  // 与えられたcallbackを実行
})
route('/fruit/*', function(name) {
  console.log('The detail of ' + name)
  // URLが`/fruit/apple`に変更した場合
  // 'apple'を`name`として渡す
})
route('/old..', function() {
  console.log('The pages under /old was moved.')
  // /oldと/old/**/**の両方に一致させたいときは .. でok
})
// リダイレクト的なこともできる
route('customers/267393/edit', 'エディットページ(タイトル)')


/**
 * route.start()
 */
route.stop()  // 古いコールバックを解除
route.start()  //URL変更の検知を開始(再起動)
// 通常は riot の読み込みと同時に自動で行われる...らしいよ


/**
 * route.create()
 */
var subRoute = route.create()
subRoute('/fruit/apple', function() { /* */ })
// http://riotjs.com/ja/api/route/#ルーティング・グループ
// http://riotjs.com/ja/api/route/#ルーティングの優先度


/**
 * subRoute.stop()
 */
var subRoute = route.create()
subRoute('/fruit/apple', function() { /* */ })
subRoute.stop()  //サブ・ルータを停止し全コールバックをクリア


/**
 * route.exec()
 * URLを変更せず別タグをその場で実行(非推奨)
 */
route.exec(function(collection, id, action) {
  //現在のURLを調べて、与えられたcallback
  //URL変更なしに「その場で」実行(非推奨だよ)
})


/**
 * route.query()
 * URLからパラメータを取り出す
 */
route('/search..', function() {
  var q = route.query()
  console.log('Search keyword: ' + q.keyword)
  console.log('Search limit: ' + q.limit)
})


/**
 * route.base()
 * ルーティングベースパスを変更
 * デフォルトは # 
 */
route.base('/app')
// http://riotexample.com/app/fruit/apple みたいなとき
// /fruit/appleの部分だけをルーティングに指定


/**
 * route.parser()
 * デフォルトパーサを変更
 */
// 例えばこんな !/user/activation?token=xyz のをパスに含みたい
route.parser(function(path) {
  var raw = path.slice(2).split('?'),
      uri = raw[0].split('/'),
      qs = raw[1],
      params = {}
  if (qs) {
    qs.split('&').forEach(function(v) {
      var c = v.split('=')
      params[c[0]] = c[1]
    })
  }
  uri.push(params)
  return uri
})