導入

Riot.js >=4 は完全に書き直されています。(詳しくはこちらを御覧ください

Riot.js 3 で書かれた古いアプリケーションを移行することは推奨されていません。なぜなら、Riot.js の古いバージョンにもセキュリティパッチが適用され、十分に安定しているからです。

このガイドを使用して、Riot.js 3 および 2 から Riot.js 4 のコンポーネントを作成する方法を学ぶことができます。

コンポーネントの構文と API

コンポーネントの構文は可能な限りあいまいさを避けるために、最新の JavaScript 標準に合わせて更新されました。 魔法が少ないということは、より明確で相互運用性があることを意味します。Riot.js 4 コンポーネントは完全に将来のために設計されています!

script タグ

以前の Riot.js のバージョンでは、<script> タグ内の this というキーワードを介してコンポーネントのインスタンスをただ継承するのみでした。この糖衣構文は、ES2018 という標準の JavaScript の構文に依存したよりクリーンな API のために削除されました。

<my-component>
  <p onclick={onClick}>{message}</p>

  <!-- <script> タグは任意 -->

  onClick() {
    this.message = 'hello'
  }
</my-component>

<my-component>
  <p onclick={onClick}>{message}</p>

  <!-- <script> タグは省略不可能 -->

  <script>
    export default {
      onClick() {
        this.message = 'hello'
        this.update()
      }
    }
  </script>
</my-component>

このようにして、エディタや TypeScript のような他のコンパイラは、コンポーネントの JavaScript ロジックに惑わされることなく、特別な心配をせずに使用できます。

この変更は Riot.js の新しい哲学によって推進されたことに言及する価値があります:

…あいまいさに直面したときには、推測の誘惑を断ろう。
それをするための一つの、そしてできればただ一つだけの明確な方法があるべきだ。
しかしオランダ人でない限り、その方法は最初は明白ではないかもしれないが…

テンプレートのショートカット

テンプレートショートカットは完全に削除され、純粋でより明示的な JavaScript の式が採用されました。Riot.js 4 で、同じ結果をどれほどよりクリーンな方法で簡単に得ることができるか見てみてください。

<my-component>
  <p class={ active: isActive, disabled: isDisabled }>hello</p>
</my-component>

// riot-class-names-plugin.js
/**
 * Convert object class constructs into strings
 * @param   {Object} classes - class list as object
 * @returns {string} return only the classes having a truthy value
 */
function classNames(classes) {
  return Object.entries(classes).reduce((acc, item) => {
    const [key, value] = item

    if (value) return [...acc, key]

    return acc
  }, []).join(' ')
}

// classNames プラグインをインストール
riot.install(function(component) {
  // すべての riot コンポーネントに classNames ヘルパーを追加
  component.classNames = classNames

  return component
})
<my-component>
  <p class={
    classNames({
      active: isActive,
      disabled: isDisabled
    })
  }>hello</p>
</my-component>

さらに良いことに、classNames をコンポーネントのロジックで直接使用することで riot.install の使用を避け、テンプレートをクリーンな状態に保つことができます。例:

<my-component>
  <p class={getParagraphClasses()}>hello</p>

  <script>
    import classNames from 'classnames'

    export default {
      getParagraphClasses() {
        return classNames({
          active: this.isActive,
          disabled: this.isDisabled
        })
      }
    }
  </script>
</my-component>

同じショートカットが style 属性にも使用できましたが、今の Riot.js 4 では style 属性を自分で処理する必要があります:


<my-component>
  <p style={{ color: 'red', 'font-size': `${fontSize}px` }}>hello</p>
</my-component>

// riot-style-attributes-plugin.js
/**
 * Convert object attributes constructs into strings
 * @param   {Object} attributes - style attributes as object
 * @returns {string} a string with all the attributes and their values
 */
function styleAttribute(attributes) {
  return Object.entries(attributes).reduce((acc, item) => {
    const [key, value] = item

    return [...acc, `${key}: ${value}`]
  }, []).join(';')
}

// styleAttribute プラグインをインストール
riot.install(function(component) {
  // すべての riot コンポーネントに styleAttribute ヘルパーを追加
  component.styleAttribute = styleAttribute

  return component
})
<my-component>
  <p style={
    styleAttribute({
      color: 'red',
      'font-size': `${fontSize}px`
    })
  }>hello</p>
</my-component>

これでコードとヘルパーは、組み込まれたフレームワークに依存せず完全にカスタマイズ可能になります。 これは、サードパーティのコードから来るバグが少なく、より自由になることを意味します…次のことを忘れないでください:

…明示は暗黙よりも優れています…

オブザーバブル

オブザーバブルパターンは以前の Riot.js のバージョンに完全に統合されました。これは独断的な決定であり、すべてのユーザーに有効なわけではありません。Riot.js 4 では、アプリケーションでどのプログラミングパターンを使用するかを決めることができます。このため、観察可能なヘルパーはソースコードから完全に削除され、より一般的なアプローチが採用されています。

<my-component>
  <p>{message}</p>

  this.on('mount', function() {
    this.message = 'hello'
    this.update()
  })
</my-component>

<my-component>
  <p>{message}</p>

  <script>
    export default {
      onMounted() {
        this.message = 'hello'
        this.update()
      }
    }
  </script>
</my-component>

新しいコンポーネントのライフサイクルイベントも確認してください。

この変更は、観察可能なライフサイクルパターンを好むノスタルジックなユーザーに門戸を開いたまま、アプリケーションの状態を管理する多くの新しい可能性を開きます。

// riot-observable-plugin.js

import observable from 'riot-observable'

/**
 * Trigger the old observable and the new event
 * @param   {RiotComponent} component - riot component
 * @param   {Function} callback - lifecycle callback
 * @param   {string} eventName - observable event name
 * @param   {...[*]} args - event arguments
 * @returns {RiotComponent|undefined}
 */
function triggerEvent(component, callback, eventName, ...args) {
  component.trigger(eventName, ...args)
  return callback.apply(component, [...args])
}

riot.install(function(componentAPI) {
  const {
      onBeforeMount,
      onMounted,
      onBeforeUpdate,
      onUpdated,
      onBeforeUnmount,
      onUnmounted
  } = componentAPI

  // riot コンポーネントを観測可能にする
  const component = observable(componentAPI)
  // 新しいイベントを古いイベントに再マップする
  const eventsMap = {
    onBeforeMount: ['before-mount', onBeforeMount],
    onMounted: ['mount', onMounted],
    onBeforeUpdate: ['before-update', onBeforeUpdate],
    onUpdated: ['updated', onUpdated],
    onBeforeUnmount: ['before-unmount', onBeforeUnmount],
    onUnmounted: ['unmount', onUnmounted]
  }

  Object.entries(eventsMap).forEach(([eventName, value]) => {
    const [oldObservableEvent, newCallback] = value
    component[eventName] = (...args) => triggerEvent(
      component, newCallback, oldObservableEvent, ...args
    )
  })

  return component
})
<my-component>
  <p>{message}</p>

  <script>
    export default {
      onMounted() {
        this.on('update', () => {
          console.log('I got updated!')
        })
      }
    }
  </script>
</my-component>

Opts vs props と state

以前のバージョンの Riot.js は各コンポーネントに opts キーを提供していました。このキーは props という名前に変更され、不変になりました ー これは Object.freeze によって固定された読み取り専用のプロパティです。 props オブジェクトは、それを読み取るコンポーネントの外部でのみ更新できますが、新しい state オブジェクトが update のコール毎に更新されます。

<my-component>
  <p>{opts.message}</p>
</my-component>

<my-component>
  <p>{props.message}</p>
</my-component>

Refs 属性

ref 属性は、可変プロパティよりも関数型アプローチを優先する $ および $$ というコンポーネントヘルパーに置き換えられました。

<my-component>
  <p ref='paragraph'>{message}</p>

  this.on('mount', function() {
    this.refs.paragraph.innerHTML = '<b>hello</b>'
  })
</my-component>

<my-component>
  <p>{message}</p>

  <script>
    export default {
      onMounted() {
        const paragraph = this.$('p')

        paragraph.innerHTML = '<b>hello</b>'
      }
    }
  </script>
</my-component>

親と子

Riot.js ユーザーは parent キーと tags キーを過度に乱用していました。それらが多くの副作用や明らかなバッドプラクティスの原因となっていました。このため、Riot.js 4 で作成された子/親コンポーネントは内部APIを公開せず、コンポーネントは props を介してのみ通信し、外部と直接やり取りしません。

<my-component>
  <my-child ref='child'/>

  this.on('mount', function() {
    this.refs.child.update({ message: 'hello' })
  })
</my-component>

<my-component>
  <my-child message={childMessage}/>

  <script>
    export default {
      onMounted() {
        this.childMessage = 'hello'
        this.update()
      }
    }
  </script>
</my-component>

もちろん独自の riot プラグインを作成して ref の振る舞いを追加することもできますが、お勧めできません:

// riot-ref-plugin.js
riot.install(function(component) {
  const { onBeforeMount } = component

  component.onBeforeMount = (props, state) => {
    if (props.ref) {
      props.ref(component)
    }

    onBeforeMount.apply(component, [props, state])
  }

  return component
})
<my-component>
  <my-child ref={onChildRef}/>

  <script>
    export default {
      onChildRef(child) {
        this.child = child
      },
      onMounted() {
        this.child.update()
      }
    }
  </script>
</my-component>

独自イベントのディスパッチング

子コンポーネントにディスパッチされた独自イベントを検知するには、プロパティを介するとシンプルに行うことができます:

<my-component>
  <child on-loaded={onChildLoaded}></child>

  <script>
    export default {
      onChildLoaded() {
        console.log('the child component dispatched the on loaded event!')
      }
    }
  </script>
</my-component>
<my-child>
  <figure>
    <img src="path/to/the/image.jpg" onloaded={props.onLoaded}/>
  </figure>
</my-child>

Virtual タグ

<virtual> タグは削除され、使用できなくなりました。代わりに <template> タグを使用してください。詳しくはこちらを参照してください。

Yield タグ

<yield> タグは、より予測可能な動作を持つ <slot> タグに置き換えられました。どのように動くのかを理解するためにスロット API を確認してください。

CLI

新しい CLI は、単一のタグをコンパイルできるだけでなく、Riot.js アプリケーション全体をバンドルすることもできるので、以前のCLIよりはるかに強力です。

迅速なプロトタイプやデモのためにコンポーネントのバンドルを簡略化するように設計されていますが、より大きなアプリケーションのために、高度にカスタマイズ可能な JavaScript のバンドラを riot ローダーと一緒に使用することをお勧めします。

インストール

Riot.js v3 では、npm i -g riot というコマンドで CLI をインストールすることが可能でした。Riot.js 4 では、riot という npm package から削除されているため、npm i -g @riotjs/cli というコマンドで個別にインストールする必要があります。

コンポーネントの登録

以前は Riot.js のコンポーネントが自動的に登録されていたということに注意する必要があります。しかし現在は、JavaScript モジュールとして出力されるようになりました。JavaScript の出力を自分で登録する必要があります:

// コンパイルされた Riot.js コンポーネント
import MyComponent from './my-component.js'
import {register} from 'riot'

register('my-component', MyComponent)