この記事をおすすめしたい人
- VueやNuxtは触ったことがある人
- にも関わらず生のJavaScriptはあまり触ったことがない人
- つまりオレ
はい、今回は、普通のJavaScriptには苦手意識があるけど、Nuxt.jsの経験値はそこそこある人間が、WordPressの1ページだけにJavaScriptをゴリゴリ組み込んで開発したい。
そんなあなたを助けます!
いやいや、そんな奴おらんやろ!?
いるんですねぇ。
というわけで、ChatGPTくんが教えてくれたVueライクな軽量ライブラリ「Alpine.js」について自分のメモを残しておきます。
前提とか条件とか
さっき言ったように、NuxtやVueは書けるけど、JavaScriptはコピペでちょろっと実装する程度の経験しかない。
そんな愚か者がWordPressにJavaScriptで動くツールを作りたい。 ビルドとかだるいから、Nuxtで作ってiframeで埋め込むことはしない。
該当者いますか?
そんな変な条件じゃなくても、ビルドなしでお手軽にVueみたいな感じでJavaScriptの実装をしたいケースにも使えるみたいです。
Alpine.jsでほぼ解決
いやね…。
ほんとは普通のJavaScriptを触れるようになろうと思って始めた企画で、いや、企画? そうお勉強を始めて、JSON整形ツールまでは作れたんですよ。
んで、Nuxtで作ってたちょっと規模が大きめのツールを移植しようと思って、ChatGPTくんに投げてみたところ、
無理ムリ無理ムリ無理!!
みたいな複雑なコードが返ってきて、しかも量が数倍に膨れ上がってる。 これを理解して自前で作れるようになるのはオレには無理!ってなって。
そんな事情もChatGPTくんに投げたところ、返ってきたのがAlpine.jsというライブラリでした。
簡単に特徴を書くとこんな↓感じ。
- headでライブラリを読み込むだけで導入可能
- Nuxt(というかVue)に似た形式で記述可能
- 「v-if」とかは「x-if」に変更
細かいところではNuxtとけっこう違うので、完全に同じとまではいかず不便なところもありますが、生のJavaScriptでやるよりは遥かに楽で、記述も少なくて済みました。
最高!
基本的な書き方は公式を見てください。 ここでも簡単に流れを書くと、headでライブラリを読み込んで、
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
ページ内で使う変数や関数を定義して、
<script>
Alpine.data('app', () => ({
var1: 0,
var2: 1,
func1() {
return 2
},
...
}))
</script>
さっき付けた名前を、Alpine.jsで処理するルートのタグに「x-data」で指定して、
<div x-data="app">
これで終わりです。
これだけで、「x-data」を指定した要素以下では、NuxtやVueに近い感じで開発が可能になります。
Alpine.jsの注意点とか
あくまで、Nuxt.js(v2とv3を経験済み)視点での注意点です。
Nuxtのdata、computed、methodsの代わり
残念ながらAlpine.jsにはそのような構造はなくて、この↓ようにすべて同じ階層で定義していくしかないみたい。
Alpine.data('app', () => ({
var1: 0,
var2: 1,
func1() {
return 2
},
...
}))
computedに近い機能だけは用意されていて、
Alpine.data('app', () => ({
get computed1() {
return 1+1
},
}))
変数と違って代入はできず、中で処理をさせることができる値です。
computed同様に、処理に使っている変数が変化した場合は値がちゃんと自動で更新されるようになっていて、まんまcomputedのように使えました。 ちなみにこの「get」を使った定義は読み取り専用なので、代入して値を変えることはできません。
階層を持たせる場合の注意点
階層というかグループ分けはできないと言いましたが、オブジェクトとしてまとめることで、こんな↓書き方もできます。
Alpine.data('app', () => ({
data: {
var1: 0,
var2: 1,
},
methods: {
func1() {
return 2
},
func2() {
return 3
},
}
...
}))
これでかなりNuxtっぽい管理・記述スタイルに近づけることができます。
ただ、非常にやっかいな点があって、Alpine.jsでも上のデータ内の他の要素へアクセスするのにthisを使うんですが(this.data.var1とか)、そのスコープの範囲がグループ内だけに限定されてしまうんです。
どういうことかというと、methods内のfunc1の中のthisはmethods以下の範囲になっていて、func2には「this.func2」でアクセスできるけど、var1やvar2にはどうやってもアクセスできません。 なので他要素へアクセスする関数はルート直下に置かないとダメみたい。 ルートに置くと「this.data.var1」でアクセスできます。
暫定的に、最終的にこんな↓構造にすることにしました。
Alpine.data('app', () => ({
data: {
var1: 0,
var2: 1,
},
//引数だけで完結する関数
methods: {
func1() {
return 2
},
func2() {
return 3
},
},
//他要素にアクセスする関数
func3() {
return this.data.var1 + this.methods.func1()
},
...
}))
{{ }}で変数を出力できない
NuxtだとHTML内でこんな↓風に{{ }}で囲めば変数を出力できていましたが、
{{data.var1}}
Alpine.jsでは「x-text」を使って出力するようです。
<span x-text="data.var1"></span>
Nuxtの「v-text」や「v-html」と同じ感じですね。
v-forで回数ループができない
Nuxtだとこんな↓書き方で 10 回ループができていましたが、
<option v-for="n in 10" :value="n">{{n}}倍</option>
どうもAlpine.jsではできないっぽい…。 公式を見ると…
できるやないかい!
ChatGPTの嘘つき!
こんな↓書き方ができるみたいです。
<ul>
<template x-for="i in 10">
<li x-text="i"></li>
</template>
</ul>
Nuxtもv3 以降では、templateタグでループ部分を囲むのが推奨されていました。
selectタグの動的生成に注意
こんな↓風に、定義したデータに従って動的にselectのoptionを定義することがあると思います。
<select id="type" name="type" x-model.number="state.type">
<template x-for="row in data.types" :key="row.id">
<option :value="row.id" x-text="row.name"></option>
</template>
</select>
初期値は「state.type」に入っているのに、のままだと初期選択されません。 おそらく、Alpineの初期化とDOMの生成タイミングのズレが原因です。
対策として、ループ内でselectedを動的に付与すれば、ちゃんと選択されました。
<select id="type" name="type" x-model.number="state.type">
<template x-for="row in data.types" :key="row.id">
<option :value="row.id" x-text="row.name" :selected="row.id === state.type"></option>
</template>
</select>