◆ v-model で input とのデータ連動が簡単
◆ data オブジェクトを直接書き換えていい
◆ HTML Imports 感のある .vue ファイル

私は lit-html や hyperhtml や elm を React や Vue より先に使ってたので React か Vue かで言えば近い考え方と書き方で作れる React 派です
ただ ある程度使ってると不満を感じるというか面倒な部分があって それが前記事でも書いた 2 点です

React で input と state の関連付けを楽にしたい
React でネストしたオブジェクトの state 更新を楽にしたい

input の値と state の関連付けが面倒なのと ネストしたオブジェクトの更新が面倒なところです
ネストしたオブジェクトは一応追加ライブラリで immer を入れることで解決はしたものの 追加ライブラリが必要なことや そもそもオブジェクトまでイミュータブルにしないといけないせいで immer が必要になってくると考えればイマイチなところとも言えます

この点では Vue のほうが優れていて どちらも React より簡単に扱えます

v-model

Vue の場合は oninput などのリスナをつけなくても input タグに v-model を指定すると指定した名前のプロパティと自動で連動します

<!doctype html>
<script type="module">
import Vue from "https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.esm.browser.js"

new Vue({
el: "#app",
data: {
text: "abcd",
},
})
</script>

<div id="app">
<input v-model="text">
<p>{{text}}</p>
</div>

イベントをリッスンして状態を更新する処理を設定してみたいなものの記述は必要ありません
逆に React に比べてどこにリスナがあって どのタイミングで処理が呼び出されて どこが変わるのかが隠蔽されてわかりづらくはあります
とは言っても そんなところまで意識しなくても問題なく動くようにはなってるので 短く楽に書きたいというときは便利です

nested data

Vue でコンポーネントの状態を表す data オブジェクトは初期化時に再帰的に getter や setter に変換されます
プロパティを書き換えると setter で処理されるので React のように新しいオブジェクトを作ってとか考えずに変えたいところを単純に変えるだけです
再帰的に変換されてるのでネストしていても気にせず変えたいところを変えれば良いです

配列の場合は Array を継承して独自の push メソッドなどを追加したクラスのインスタンスになっているので push や sort や reverse など破壊的にメソッドを使った場合でも問題なく動きます
初期化時以外でもプロパティに代入した値がオブジェクトや配列なら変換されてプロパティに設定されるので末端プロパティに限らず途中からオブジェクト全体を置き換えることもできます

ただし Proxy じゃないので 追加する予定のプロパティはすべて最初にデフォルト値を用意しておかないと setter が準備されず更新しても何も起きません

<!doctype html>
<script type="module">
import Vue from "https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.esm.browser.js"

new Vue({
el: "#app",
data: {
obj1: {
obj2: [
null,
{
value: 1
},
],
},
},
methods: {
up() {
this.obj1.obj2[1].value++
}
}
})
</script>

<div id="app">
<p>{{obj1.obj2[1].value}}</p>
<button @click="up">UP</button>
</div>

Vue ファイル

これは一長一短ですが 昔あった HTML Imports に近い見た目なのは好きなところです
.vue ファイルはコンポーネントを定義するもので HTML Imports でインポートする HTML ファイルとほぼ同じ感じで HTML ファイルのように template タグと script タグを書きます

<template>
<div>{{ x }}</div>
</template>

<script>
export default {
data: {
x: 1,
},
}
</script>

このファイルを import すると Vue のコンポーネント定義の形式のオブジェクトとして取得できます

JavaScript で書くなら

export default {
template: `<div>{{ x }}</div>`,
data: {
x: 1
}
}

このモジュールを import しても結果は同じはずです
ですが template プロパティに文字列で書くと コードハイライトなどエディタサポートが使えず見やすさや扱いづらさで劣ります
しかし .vue ファイルの場合も 独自のファイル形式なので Webpack で vue-loader 通して変換しないといけません
ES Modules で使えないのは不便なところです

.vue ファイルを Webpack する場合は React の JSX と同じように ビルド時に HTML 表現から関数呼び出しなどの JavaScript 表現に置き換えられます
その分 ブラウザ側での処理は減って初期表示は速くなりますし Vue 自体にパースして変換する処理がいらなくなるのでそれらを除いた軽量版で済みます

Vue のイマイチなところ

そんな感じで良いところも多いですが これまでほとんど使ってなかった理由はテンプレートの書き方です
HTML 互換なので HTML としてパース可能なテンプレート表現になっています
そこを重視しすぎている感じがして テンプレート自体が見づらいです

本文中の埋め込みは {{}} が使えるのでいいのですが 属性には使えません
属性の値部分は "" で囲みます
ただのテキストであろうと JavaScript のコードであろうと "" なので区別がつきにくいです
JSX のように {} を使うか本文と同じように {{}} を使ってくれれば見やすいのですけど
また "" が使われているので中で JavaScript の文字列を使いたいときは '' か `` を使わざるを得ません
やっぱり "" での囲みはやめてほしいと思います

イベントハンドラの設定では @click="" 形式で設定しますが この値の部分も曖昧で見づらいです
methods で定義している関数名を指定するのも HTML のように処理を書くのもどっちも可能です

<div @click="add"></div>
<div @click="add(1)"></div>
<div @click="num += 1"></div>

add も add(1) も使えるのが嫌です
add(1) を実行すると関数が返ってきてそれをセットするようにしか見えないです

あとは v-if みたいなオプションです
hidden 属性付ける感じで非表示にできて一見便利で嬉しい機能のはずなのに あまり見やすくないです
JSX で 「cond ? value1 : value2」 ってするのよりは見やすそうに思うのですが 属性に紛れてるのはわかりづらく感じます
サーバサイドのテンプレートで多い if と else のブロックの中で書いてくれるのがベストかなと思います
一応 template タグを使って

<div>
<template v-if="already_clicked">
<div class="large">Clicked</div>
</template>
<template v-else>
<button class="btn">Click me</button>
</template>
</div>

のようにもできます
通常のタグなので目立ちにくさはありますが v-if しか書かないようにしてれば 見やすくなります
ですが template を省略もできるので

<div>
<div class="large" v-if="already_clicked">Clicked</div>
<button class="btn" v-else>Click me</button>
</div>

とも書けて 基本見るのはこっちです
独立してるタグなのに直前のタグの属性に依存するような v-else が書けてしまうのは タグを移動させたら条件まで変わってしまったとかありそうで見ていて不安になるものです
HTML なのに HTML のような感じでは読み書きできないあたり 変に HTML 互換な記法じゃないほうが良かったかもしれないくらいです

v-for も同じ感じで せめて制御構文くらいは HTML 記法から外してほしかったです

.vue ファイルや template プロパティでのテンプレート指定なら HTML 互換である必要もないので たぶん HTML ファイルにテンプレートを書いておいてマウント先の要素の innerHTML をテンプレートに使えるという仕組みのせいで すべて HTML で書けるようにしている気はするのですが その機能のおかげでロード中に初期状態が見えているならともかく SSR してるわけじゃないので見えているのはあくまでテンプレートです
ロードが終わるまでに見えているのは 「{{title}}」 みたいなもので 見えてないほうが良く 隠す方法が用意されてるくらいです
この機能は要らないと思うので なくして HTML 互換よりも見やすいものにしてくれると 使うことが増えそうなんですけどねー

……逆に HTML であることを生かして 独自記法を作ってそれを HTML に変換さえすれば後は Vue に任せられると考えると便利かも?
作るものが HTML なら比較的かんたんですし 機能追加や変換したもののチェックもしやすいです
.vue ファイルなら template タグに lang 属性指定するとその loader で変換できたと思いますし 変換処理の loader 版も作っておけば独自記法をそのまま .vue ファイル書くこともできそうです

とは言ってもこれまで自作した条件分岐や繰り返しなどが使えて HTML を出力するテンプレート系はどれも作ってみたけど結局使ってない状態なので常に使いたいと言えるくらいの記法を思いつけばやってみようかなくらいです

続いた