話題のAlpine.jsをVue.jsと比較しながら見ていく
UI開発者 板垣みなさんこんにちは、UI開発者の板垣です!
今月のGitHub Trending JavaScript部門で1位を獲得した、Alpine.jsをご存じでしょうか?
日本のフロントエンド界隈だとまだ耳にすることは少ないですが、じわじわとその名を浸透させていっています。
今回はそんなAlpine.jsをVue.jsと比較しつつ、簡単にご紹介いたします。
そもそもAlpine.jsとは
Vue.jsやReactの持つリアクティブかつ宣言的な性質を、簡単にマークアップに組み込むことができる軽量フレームワークです。
ディレクティブなどの基本構文はVue.jsを参考にしているため、Vue.jsを使ったことがある人であればすぐにでも始められます。
導入方法
Alpine.jsの導入方法は2通りあります。
CDNから読み込む
任意のHTMLファイルに下記script要素を読み込みます。
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.0.0/dist/alpine.js" defer></script>
※IE11のサポートが必要な場合は下記のIE11サポート版を読み込みましょう。
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.0.0/dist/alpine-ie11.js" defer></script>
NPMからインストールする
プロジェクトディレクトリで下記コマンドを実行。
npm i alpinejs
その後、任意のファイルにimport 'alpinejs'
と記述します。
構文について
現在、Alpine.jsには13のディレクティブと、5つのマジックプロパティと呼ばれるものが存在します。
- Directive
- x-data
- x-init
- x-show
- x-bind
- x-on
- x-model
- x-text
- x-html
- x-ref
- x-if
- x-for
- x-transition
- x-cloak
- Magic Properties
- $el
- $refs
- $event
- $dispatch
- $nextTick
Alpine.jsのディレクティブ = Vue.jsのディレクティブという認識で問題ありません。
マジックプロパティについては公式READMEで特に言及されていませんが、Vue.jsの接頭辞に$が付いたプロパティやメソッドにあたるものを、独自にマジックプロパティと定義しているようです。
つまり、マジックプロパティ = 便利な機能群くらいの認識で大丈夫でしょう。
これらの機能は接頭辞が異なるだけでVue.jsとほぼ同じ機能を提供しますが、いくつかAlpine.js独自の部分があるのでポイントを絞ってご紹介します。
x-data
Vue.jsのdata
にあたるのがx-data
です。
要素のx-data
ディレクティブにオブジェクト形式でデータを書き込むと、その要素およびその中にある要素内で記述したデータが扱えるようになります。
<div x-data="{show: false}">
<p x-show="show">このテキストは表示されません</p>
</div>
機能ごとにデータを分割したい場合は、オブジェクト形式のデータを関数の返り値に指定します。
<div x-data="dropdown()">
<button @click="open()">オープン</button>
<div x-show="isOpen" @click.away="clone()">
<p>テキスト</p>
</div>
</div>
<script>
const dropdown = () => {
return {
isOpen: false,
open () {
this.isOpen = true;
},
clone () {
this.isOpen = false;
}
}
};
</script>
x-init
x-init
にはコンポーネントが初期化されるときに実行したい処理を書きます。これはVue.jsでいうとcreated
に近いです。
x-init
の返り値として指定した関数は、Vue.jsのmounted
のようにDOMの初期更新を行った後に実行されます。
<div x-data x-init="init()"></div>
<script>
const mounted = () => {
alert('mounted');
};
const init = () => {
alert('init');
return mounted;
};
//
</script>
このx-init
はx-data
ディレクティブが存在しない場合は実行されないため注意が必要です。
x-on
イベントリスナーを登録できるディレクティブです。
このディレクティブ自体は、Vue.jsのv-on
とさほど代わりはなく、@on
という形で省略して書くことが可能です(今回紹介しないx-bindも同じく:
という形で省略できます)。
Vue.jsと異なる点はイベント修飾子です。x-on
には、以下の6つのイベント修飾子が存在します。
- keydown
- away
- prevent
- stop
- window
- once
away
やwindow
といった見慣れない修飾子があると思います。
away
修飾子を付けると、イベントが対象の要素もしくはその子要素以外で発生した場合にのみ指定の処理が実行されるようになります。(モーダルウィンドウを作るときなどに役立ちそうですね。)
また、window
修飾子を付けると、対象要素ではなくwindowオブジェクトにイベントリスナーが登録されます。
これは、ウィンドウの幅が変更されたり、スクロールが発生したときにコンポーネントの状態を変更したい場合に有用です。
x-text
x-text
は要素のinnerText
をリアクティブに更新するためのディレクティブです。
AlpineにはVue.jsのようなテンプレート構文({{}})が存在しないため、データをテキストとして反映するためにはこのx-text
を使用します。
使い方はx-text
ディレクティブの値に表示したいデータを入れるだけです。
x-transition
要素にアニメーションを加えるためのディレクティブです。Vue.jsでアニメーションを付けるには<transition>
コンポーネントを使用しましたが、Alpine.jsではディレクティブを使用することに注意してください。
このディレクティブを細かく分けると以下の6つなります。
- x-transition:enter
- x-transition:enter-start
- x-transition:enter-end
- x-transition:leave
- x-transition:leave-start
- x-transition:leave-end
これらのディレクティブにCSSのクラスを指定することで、対象要素に対して各ディレクティブのタイミングで指定したクラスを追加・削除するようになります。
各ディレクティブの適用される流れについて軽く触れると、Enter系は要素の挿入前にx-transition:enter
とx-transition:enter-start
が付与され、要素が挿入された1フレーム後にx-transition:enter-start
が削除されます。それと同時にx-transition:enter-end
が追加され、アニメーションが終了して1フレーム後にx-transition:enter
とx-transition:enter-end
が削除されるようになっています。
Leave系は上の文章の「要素の挿入されるタイミング」という部分が、「要素が消されるタイミング」に変わるだけです。
下記の例では、要素の表示非表示をx-transition
を使用して切り替えています。
<head>
<style>
.hide { opacity: 0; }
.show { opacity: 1; }
.transition { transition: 0.3s; }
</style>
</head>
<body>
<div x-data="{ open: false }">
<button x-on:click="open= ! open">表示・非表示の切り替え</button>
<div x-show="open"
x-transition:enter-start="hide"
x-transition:enter="transition"
x-transition:enter-end="show"
x-transition:leave-start="show"
x-transition:leave="transition"
x-transition:leave-end="hide">
<div>
x-transitionのサンプル
</div>
</div>
</div>
</body>
$dispatch
ここまでディレクティブを紹介してきましたが、こちらの$dispatch
はマジックプロパティです。
$dispatch
はカスタムイベントの作成とそのイベントをディスパッチできる機能で、内部的にはWeb標準のカスタムイベントを使用しているようです。
単純にカスタムイベントを簡単に発効するものなので、Vue.jsと比較するとどうこうというものではありませんが、強いていうならば$emit
に近いかもしれません。
次の例では、ボタンをクリック時に$dispatch
が実行され、イベント先でデータを更新しています。
<div x-data="{ msg: '' }" @hello="msg = $event.detail.msg">
<p x-text="msg"></p>
<button @click="$dispatch('hello', { msg: 'Hello' })">送信</button>
</div>
$dispatch
の第一引数には「カスタムイベント名」、第二引数には「送信したいデータ」を指定します。第二引数で指定したデータは、イベント先の「$event.detail」から参照できます。
このプロパティを活用することで、コンポーネント間でデータの受け渡しが容易にできそうです。
以上が、Alpine.jsの特徴的な構文です。
ちなみに各ディレクティブの接頭辞x-
は、v1リリース前までプロダクトネームが「Project-X」だったのでその名残でしょう。
実際に触ってみる
それでは最後に、Alpine.jsを使って作った簡単なディスクロージャとモーダルウィンドウをご覧ください。
コードのポイントとしては機能ごとにデータを分割している所です。分割したデータはエンドポイント要素のx-data
で、スプレッド演算子を使用して展開しています。
<div @modal-open="openModal()" x-data="{...disclosure(), ...modal()}">
...
</div>
<script>
const disclosure = () => {
...
};
const modal = () => {
...
};
</script>
おわりに
Vue.jsを触っていたからか、Alpine.jsの学習コストの低さにとても驚きました。
初回リリースからまだ3カ月ほどしかたっていない新しいフレームワークですが、完成度は高く、大規模サイトへ部分対応や小規模な診断コンテンツなどを作成するときに活用できそうです。