Riot.jsのRouteプラグインでSPAのルーティングを実装してみる

Vue.jsが人気の今ですが、あえてRiot.jsで開発しています。学習コストが非常に低く扱いやすいです。Riot.jsでSPA構築の基本であるルーティングの方法を紹介します。

ルーティングのデモ

右上の[全画面ボタン]をクリックして新規タブで見るとURLにハッシュがついて切り替わっているのが分かります。後述しますが、デフォルトでは#(ハッシュ)で、変更することもできます。

ルーティングの基本


<div data-is="navi"></div>
<div data-is="page"></div>

<script src="https://cdn.jsdelivr.net/npm/riot@3.7/riot+compiler.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/riot-route@3.1.2/dist/route.min.js"></script>
<script type="riot/tag">

<navi>
  <a each={ items } class="{ is-active: parent.path === url }" href="#{ url }">{ name }</a>
  
  var self = this
  
  this.items = [
    { url: '', name: 'トップ'},
    { url: 'one', name: '1ページ'},
    { url: 'two', name: '2ページ'}
  ]
  
  var router = route.create()
  
  router(function(path) {
    self.path = path
    self.update()
  })
  
  <style>
    :scope {
      background-color: #393c40;
    }
    :scope::after {
      display: table;
      content: '';
      clear: both;
    }
    a {
      display: block;
      padding: .5em 1.4em;
      float: left;
      color: #fff;
      text-decoration: none;
      font-size: .9em;
    }
    a.is-active {
      color: #fc3b5a;
      background-color: #333639;
    }
  </style>
</navi>

<page>
  <p>{ title }</p>
  
  var self = this,
      router = route.create()
  
  router('', function() {
    self.update({
      title: 'トップページのコンテンツが表示されます'
    })
  })
  
  router('/one', function() {
    self.update({
      title: '1ページのコンテンツが表示されます'
    })
  })
  
  router('/two', function() {
    self.update({
      title: '2ページのコンテンツが表示されます'
    })
  })
  
  // 404のときはトップへリダイレクト
  router('/..', function() {
    route('/')
  });
  
  <style>
  p {
    padding: .5em .7em;
    font-size: 1.5em;
  }
  </style>
</page>

</script>
<script>
  riot.mount('*')
  route.start(true)
</script>

非常にシンプルなので特に解説するところはありませんが、ver3.xからルーティング機能がコアから切り離されたので読み込む必要があります。

URLの#を無くす

ハッシュは少しかっこ悪い気がしたので、スラッシュに変更してみます。ディレクトリは http://localhost:8888 を想定しています。


<div data-is="navi"></div>
<div data-is="page"></div>

<script src="https://cdn.jsdelivr.net/npm/riot@3.7/riot+compiler.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/riot-route@3.1.2/dist/route.min.js"></script>
<script type="riot/tag">

<navi>
  <a each={ items } href="{ url }">{ name }</a>
  
  var self = this
  
  this.items = [
    { url: '/', name: 'トップ'},
    { url: '/one', name: '1ページ'},
    { url: '/two', name: '2ページ'}
  ]
</navi>

<page>
  <h1>{ title }</h1>
  
  var self = this,
      router = route.create()
  
  route.base('/')
  
  router('', function() {
    self.update({
      title: 'トップページのコンテンツが表示されます'
    })
  })
  
  router('/one', function() {
    self.update({
      title: '1ページのコンテンツが表示されます'
    })
  })
  
  router('/two', function() {
    self.update({
      title: '2ページのコンテンツが表示されます'
    })
  })
  
  // 404のときはトップへリダイレクト
  router('/..', function() {
    route('/')
  });
</page>

</script>
<script>
  riot.mount('*')
  route.start(true)
</script>

先ほどの例から3箇所変更します。また、route.base('/') でルーティングのベースディレクトリを指定します。デフォルトはハッシュ route.base('#') になっています。


<IfModule mod_rewrite.c>

RewriteEngine on

# 末尾にスラッシュがある場合は削除
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /$1 [L,R=301]

# ファイルやディレクトリが存在しなければindex.phpに転送
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]

# http://localhost:8888/index.phpにアクセスしたときindex.phpを省略
RewriteCond %{THE_REQUEST} /index\.php [NC]
RewriteRule ^(.*?)index\.php$ $1 [L,R=302,NC,NE]

</IfModule>

また、ブラウザのアドスバーから直接 http://localhost:8888/one などにアクセスされるとNot Foundとなってしまうので.htaccessファイルに index.php に転送する処理を書いておきます。

サブディレクトリの場合

ディレクトリが http://localhost:8888/demo/ のようにサブディレクトリである場合はまた少し注意が必要です。


<div data-is="navi"></div>
<div data-is="page"></div>

<script src="https://cdn.jsdelivr.net/npm/riot@3.7/riot+compiler.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/riot-route@3.1.2/dist/route.min.js"></script>
<script type="riot/tag">

<navi>
  <a each={ items } href="{ url }">{ name }</a>
  
  var self = this
  
  this.items = [
    { url: '/demo/', name: 'トップ'},
    { url: '/demo/one', name: '1ページ'},
    { url: '/demo/two', name: '2ページ'}
  ]
</navi>

<page>
  <h1>{ title }</h1>
  
  var self = this,
      router = route.create()
  
  route.base('demo/')
  
  router('', function() {
    self.update({
      title: 'トップページのコンテンツが表示されます'
    })
  })
  
  router('/one', function() {
    self.update({
      title: '1ページのコンテンツが表示されます'
    })
  })
  
  router('/two', function() {
    self.update({
      title: '2ページのコンテンツが表示されます'
    })
  })
  
  // 404のときはトップへリダイレクト
  router('/..', function() {
    route('/')
  });
</page>

</script>
<script>
  riot.mount('*')
  route.start(true)
</script>

<IfModule mod_rewrite.c>

RewriteEngine on

# ベースディレクトリを/demoに設定
RewriteBase /demo

# 末尾にスラッシュがある場合は削除
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)/$ /demo/$1 [L,R=301]

# ファイルやディレクトリが存在しなければindex.phpに転送
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . index.php [L]

# http://localhost:8888/demo/index.phpにアクセスしたときindex.phpを省略
RewriteCond %{THE_REQUEST} /index\.php [NC]
RewriteRule ^(.*?)index\.php$ $1 [L,R=302,NC,NE]

</IfModule>

サブディレクトリに配置する場合は RewriteBase でベースディレクトリを指定する必要があります。