🐕

VanJS 試してみた

2023/06/11に公開

VanJS?

VanJS is an ultra-lightweight, zero-dependency, and unopinionated Reactive UI framework based on pure vanilla JavaScript and DOM. Programming with VanJS feels like building React apps in a scripting language, without JSX.

VanJS は、純粋な Vanilla JavaScript と DOM に基づいた、超軽量、依存関係なし、固定観念のない Reactive UI フレームワークです。 VanJS を使用したプログラミングは、JSX を使用せずにスクリプト言語で React アプリを構築するような感じです。

ということで、猫も杓子も Node.js なこの時代にあえて「超軽量、依存関係なし、純粋な Vanilla JavaScript」で構築した UI フレームワーク とのことです。
公式のサンプルコードを拝見するとなかなか興味深い内容になっていまして、

VanJS Example
// Reusable components can be just pure vanilla JavaScript functions.
// Here we capitalize the first letter to follow React conventions.
const Hello = () => div(
  p("👋Hello"),
  ul(
    li("🗺️World"),
    li(a({href: "https://vanjs.org/"}, "🍦VanJS")),
  ),
)

van.add(document.body, Hello())
// Alternatively, you can write:
// document.body.appendChild(Hello())

これは・・・タグのようでタグじゃない・・・タグと同じ名前の、全部関数??

というわけで、気になったので早速試してみます!

やってみた

VanJS の利用には Node.js が不要で、公式の Getting Started によりますと latest version の VanJS をダウンロードして普通にインポートしなさいと書かれてあります。
今回のお試しでは、以下のようなファイル構成を作ってみました。

tree
├── app.html
└── script
    ├── main.js
    └── van-0.12.0.min.js

サンプル実装

公式のこちらを参考に、シンプルなサンプルを作って動かしてみました。

main.js
import van from "./van-0.12.0.min.js"

const { span, button } = van.tags

const Counter = () => {
  const counter = van.state(0)
  return span(
    "❤️ ", counter, " ",
    button({onclick: () => ++counter.val}, "👍"),
    button({onclick: () => --counter.val}, "👎"),
  )
}

export const addCounter = (id) => {
  const target = document.querySelector("#"+id)
  van.add(target, Counter())
}
app.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width" />
        <script type="module">
            import { addCounter } from "./script/main.js"
            addCounter("web")
        </script>
        </head>
    <body>
        <main id="web"></main>
    </body>
</html>

結果

result

「👍」をクリックするとカウンターがインクリメントされ、「👎」をクリックするとデクリメントされます。DOMを操作していることを意識させず、Reactっぽい宣言型に近い記述で要素の組み立てができている様子がわかります。

また VanJS は標準でstateの仕組みを提供しています。サンプルのようにvan.state(初期値)で生成したstateの値をイベントハンドラで更新するように書いておくと、イベントの発火に応じて影響する要素を更新してくれます。

ReactでいうuseStateの仕組みに近いですが、軽量なライブラリのインポートだけでイージーにstate管理ができるのはありがたいです。

Svelteコンポーネントに VanJS を組み込んでみる

個人的に Svelte が好きでよく使うので、Svelteのコンポーネントで VanJS を使う方法を考えてみました。

VanJS で組み立てた要素を反映させるには、document オブジェクトを経由して目的の要素にaddする必要があります。ところがSvelteのコンポーネントは基本的にはサーバーサイドでレンダリングされてしまうので、そのままではdocumentオブジェクトにアクセスできず、ReferenceError: document is not definedなどと悲しいエラーを吐かれることになります。

Counter.svelte
<script>
    import { onMount } from 'svelte';

    onMount(async () => {
        const module = await import('../script/van-0.12.0.min.js');
        const { span, button } = module.default.tags;

        const c = () => {
            const counter = module.default.state(0);
            return span(
                '❤️ ',
                counter,
                ' ',
                button({ onclick: () => ++counter.val }, '👍'),
                button({ onclick: () => --counter.val }, '👎')
            );
        };

        const target = document.querySelector('#target');
        module.default.add(target, c());
    });
</script>

<div id="target" />

一連の処理をonMountでくくってあげます。onMountでくくられた処理はコンポーネントのレンダリング後に、クライアントサイドで実行されるので、documentにも問題なくアクセスできます。

まとめ

以上です。

VanJSは「超軽量、依存関係なし、純粋な Vanilla JavaScript」を謳う、既存のフレームワークに対するアンチテーゼ的な思いを感じるライブラリでした。

その性質上、ReactやSvelteなど既存のJavaScriptライブラリと併用するには少しコツが要りそうですが、ちょっとした動的なコンポーネントを作りたいような場合で便利な使い方ができそうですし、stateの仕組みがあるのでなんならこれひとつでSPAを作ったりもできちゃうのでは・・・?と夢がふくらみます。

ぜひ、ご参考いただければ幸いです。

ではまた!

参考記事

Discussion