Riot.js入門-5


最後は応用編として、よりRiot.jsを使いこなすための各種機能・仕様を紹介していきます。


## LifeCycle:コンポーネントのライフサイクル
Riot.jsのカスタムタグは自身の状態をライフサイクルという流れの中で通知する仕様を持っています。

主に以下の4つのイベントがあります。
### mount
テンプレートからDOMを生成し、DOMツリーに追加された状態
### update
テンプレートが最新の値で更新される直前の状態（this.rootの状態は最新のテンプレートが設定される前）
### updated
テンプレートが最新の値で更新された直後の状態（this.rootの状態は最新のテンプレートが設定された後）
### unmount
DOMツリーから削除される直前の状態

この通知を購読（監視）するには、購読したいインスタンスの ``on`` メソッドを用います。

```
<script>
	// 自分自身のライフサイクルを購読する
	this.on('mount', function(){
		// マウント時に一度だけ行いたい処理を書く（グローバルイベントの購読開始など）
	});
	
	this.on('update', function(){
		// アップデート毎に事前に行いたい処理を書く（テンプレートに影響がある変数の更新など）
	});
	
	this.on('updated', function(){
		// アップデート毎に最新のDOMに対して何か行いたい処理を書く
	});
	
	this.on('unmount', function(){
		// メモリリークしないための掃除処理を書く
	});
</script>
```

#### 補足

``mount``と``unmount`` はライフサイクルの中で一度しか呼ばれませんが、``update ``と``updated ``と何回も呼ばれる可能性があります。

それらのイベントを1度だけ購読したい場合は ``one`` メソッドを使います（``this.one('update', function(){});``）;

購読をやめたい場合は ``off``、自身でカスタムイベントを発行したい場合は ``trigger`` を用います。


## mixin:ロジックの共有化／再利用
同じ処理を複数のコンポーネントで記述する必要がある場合、``mixin`` という機能を用いて一箇所で書いた処理を使いまわすことできます。

### 定義

```javascript
riot.mixin('sp-buttons', {
	init() {
		// common constructor
	},
	handleTouchStart() {
		// something to do
	},
	handleTouchMove() {
		// something to do
	},

	handleTouchEnd() {
		// something to do
	}
});
```

### 適用
```sample.tag
<sample>
	<div ontouchstart={handleTouchStart} ontouchmove={handleTouchMove} ontouchend={handleTouchEnd}></div>
	<script>
		// これで、this.handleTouchStart、this.handleTouchMove、this.handleTouchEnd が使えるようになります
		this.mixin('sp-buttons');		
	</script>
</sample>

```

また、mixin上で ``init`` という関数を定義すると、mixinが適用されたタイミング = インスタンス化したタイミングで自動で発火されるので、共通のコンストラクターとして活用できます。

#### 補足
同じページで使用するコンポーネントでの共有はもちろんですが、筆者はPCサイトとSPサイトで見た目（テンプレート部分）が異なるが、処理が同じ場合にクラス継承の要領でmixinを使っています。

※ 注意：コンポーネントのscriptタグ部分は、読み込む時に一度だけ実行される **のではなく** 、 **インスタンス化（マウント）される度に発火する** ので、``constructor`` として使えます。

```Javascript
riot.mixin('home', {
	init() {
		// common constructor
	}

	// common instance logic
});
```

```home-pc.tag
<home>
	<h1>PCサイト</h1>
	<script>
		// only pc constructor (before common constructor)
		
		this.mixin('home');
		
		// only pc constructor (after common constructor)
		
		onlyPCFunction() {
			// something to do
		}
	</script>
</home>

```

```home-sp.tag
<home>
	<h1>SPサイト</h1>
	<script>
		// only sp constructor (before common constructor)
		
		this.mixin('home');
		
		// only sp constructor (after common constructor)
		
		onlySPFunction() {
			// something to do
		}
	</script>
</home>

```

## router と observer
Riot.jsはWebComponentsの機能以外に、url周りを制御する ``riot.route`` と、オブザーバーオブジェクトを手軽に作れる ``riot. observer`` を提供していますが、今回は割愛させていただいます。

* [Riot.js API・Router](http://riotjs.com/api/route/)
* [Riot.js API・Observable](http://riotjs.com/api/observable/)


## 番外編：Riot.jsのちょっとした癖の紹介

### アトリビュート更新のコンフリクト（競合）
Riot.jsやReactに代表される仮想DOMをもつライブラリは、極力ユーザー自身でDOMを直接触らないことを推奨しています（DOMを操作する作業はコストが高くパフォーマンス低下の要因になりやすいため）。

しかし、背に腹は代えられず、やむなくDOMを直接いじることはあると思います。

その際に気をつけるのは、テンプレート部分で代入している値をユーザー自身で変更すると、更新する値が予期せぬタイミングで上書きされてしまい、思い通りの挙動をしない可能性があります。

```sample.tag
<sample>
	<h1 id="title" class={isFlag ? 'templete-add-class' : ''}>sample</h1>
	
	<script>
		this.on('mount', function(){
			this.title.classList.add('direct-add-class');
		});
	</script>
</sample>
```

1. mount直後は``templete-add-class``というクラスだけ持っている
2. mountイベントのコールバックで'direct-add-class'というクラスが追加される
3. なにかのタイミングでupdateが呼ばれ、再びテンプレートが更新されると``templete-add-class``だけになってしまう

といった流れです。

やむなくDOMを直接いじる場合は、必ずテンプレートと競合しない部分だけにしましょう。

### 入れ子に定義するアトリビュートはキャメルケースを使わず、ハイフンで区切る
```
<sample custom-att="test" /> -> this.opts.customAtt
```
```
<sample customAtt="test" /> -> this.opts.customatt
```

Riot.jsの仕様として、optsに代入されるkeyは原則小文字にされ、-（ハイフン）で区切ったものだけ大文字にします。

なので、アトリビュート名は **小文字とハイフンのみ** で定義しましょう。


### イベントハンドラーの細かい制御
```
// 設定されたeventHandlerにeventObjectを渡し、その戻り値を得る
var flag = customTag.eventHandler(eventObject);

// 1）戻り値が false の場合、preventDefault を発火
if (!flag) {
	eventObject.preventDefault();
}

// 2）eventHandler内で eventObject.preventUpdate に true が設定されていなければ カスタムタグを update させる
if (eventObject.preventUpdate !== true) {
	customTag.update();
}

```
こちらはRiot.js内のイベントハンドラーが発火した時の処理を簡易的にしたものです。
``on~`` で定義したイベントハンドラーの引数には ``addEventListener`` と同様 EventObject が渡されます。

#### 1）戻り値に true を設定
デフォルトのイベントの挙動を取り消す ``e.preventDefault()`` は、ライブラリ側で勝手に発火してくれるため、ユーザー側で明記する必要がありません。
しかし e.preventDefault を発火させたくない場合は、イベントハンドラーの戻り値に ``true`` を設定することで可能になります。

#### 2）preventUpdate を使いこなす
イベントハンドラーが発火するとRiot.jsは自動でテンプレートを更新します（update関数を呼びます）。

しかし、テンプレートに影響がある変数を更新していなかったり、イベントハンドラーをフックに非同期処理が走り、そのコールバックを待って更新させたい場合など、即時的にupdateを呼びたくないことはよくあります。

その場合、eventObject の中の ``preventUpdate `` というプロパティに ``true`` を設定することで、自動更新を回避できます。

preventUpdate の細かい説明については [【RiotJS】preventUpdate を使いこなせ！](http://qiita.com/naota70/items/8fb5fe135f7058a0e149)を一読ください。


## 以上

というわけで、全5回に渡った Riot.js入門（入門というより公式ドキュメントの意訳をしただけな気も...） は以上になります。

ぜひRiotユーザーの増える一端を担えたら幸いです。

それではまた。