Open16

Alpine.js やる

おーみーおーみー

高校の卒業研究でWebアプリを作っている。技術について学習コストを考慮に入れるのはあまり好きではないのだが、メンバーの環境にBabelをセットアップしたりするのがあまりやりたくないという気持ちがあり、HTMLにテンプレート書いてライブラリを読み込めばサクッと動くというようなものが欲しい。

個人的にはReactでJSXを使って式をガチャガチャするのが好きなのだが、ライブラリ直に読み込むような場面ではJSXが使えずReact.createElementを手書きすることになって心が温まるのであまり向いていないと思う。そこでVueやAlpineはHTMLにテンプレート書いてライブラリ読み込んで<script>にふるまい書けば動くっぽそうなのでそれを使ってみたいなあという気がしています。ゆえにAlpineをやる。

おーみーおーみー

とりあえず開発サーバーはどうしても必要になってしまう。@web/dev-server に全幅の信頼を置いている。

  "name": "alpine-de-asobu",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "start": "web-dev-server --watch --root-dir dist/"
  },
  "license": "CC0",
  "devDependencies": {
    "@web/dev-server": "^0.1.24",
    "prettier": "^2.4.1"
  }
}
おーみーおーみー

Alpine本体をCDNから入れた方がいいか、npmから入れてしまうかには考える余地があるかも。

おーみーおーみー

ふむ。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Alpine test</title>
    <script
      defer
      src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"
    ></script>
  </head>
  <body>
    <script type="module"></script>
    <h1 x-data="{ message: 'Hello Alpine.js!' }" x-text="message"></h1>
  </body>
</html>

インテリセンスあるとはいえちょっと文字列ベタ書きはあまりうれしくない。

おーみーおーみー

Alpine公式サイトにはちゃんとシンタックスハイライト効いてるのずるい。おれのVSCodeにもくれ。

おーみーおーみー

x-data="{ count:0 }" とするとその要素以下のディレクティブでcountが使えるようになる。count が変更されるとそれを使っているディレクティブみんな反映される。

おーみーおーみー

x-on:click="count++" とするとその要素がクリックされたとき count++ する。この count は上で定義したやつ。省略形として @click="count++" と書くこともできる。

おーみーおーみー

x-text="count" と書くとJavaScript式を評価した結果がその要素の子のテキストノードになる。

おーみーおーみー
<div x-data="{ open: false }">
      <button @click="open = !open">Toggle</button>
      <div x-show="open" @click.outside="open = false">hidden content</div>
    </div>
おーみーおーみー

x-show="expr" とするとexprがtrueのとき表示、falseのとき非表示。ずいぶん高レイヤーな機能だ。

おーみーおーみー

@click.outside とすると その要素の外側をクリックしたときのイベントを使える。すげえ。

おーみーおーみー

getterを使う例。

  <div
      x-data="{
      search: '',
      items: ['foo', 'bar', 'baz'],
      get filteredItems () {
        return this.items.filter(item => item.startsWith(this.search))
      }
    }"
    >
      <input x-model="search" placeholder="search..." />
      <ul>
        <template x-for="item in filteredItems" :key="item">
          <li x-text="item"></li>
        </template>
      </ul>
    </div>
おーみーおーみー

<input x-model="search">とするとsearchとinputの双方向バインディングができる。

おーみーおーみー

<template x-for="束縛する変数 in 配列"> でループできる。 <template> を使うのが面白い。

        <template x-for="item in filteredItems" :key="item">
          <li x-text="item"></li>
        </template>