iPhoneで Grafanaの グラフを 参照できる アプリ Grafanizer 作ってます。 詳しくは こちらへ

目的や仕様など

最近 Vue.js を利用した開発を進めててなかなか気に入ってていろいろ試しているのだが、次に このマニュアル にある単一ファイルコンポーネント化を試そうとしたところ問題が発生...

「次はビルドツールの構築を覚えなきゃいけないの?」

Vue.js でも結構覚えることがあるのに次はJSのビルドツールを覚えなきゃいけないとなると、完成が一歩も二歩も下手したら十歩も遠のいてしまう。そもそもそのビルドツール覚えてもその知識何年使えるの?でもファイルを分割しておかないと後から絶対わけわからなくなるよね。

(実際にはそれほど悩まなくてもマニュアルの手順通りに進めれば手間なく導入できるんだろうけど、動きが理解できてないものを使うのってなんとなく抵抗あるよね。なるべくブラックボックスとかにはしたくない。)

ってことで、vue-loader にて .vue をロードする公式の方法を諦め、ビルドツール無しで同じようにコンポーネントを単一ファイルに分割する方法を考えてみた。

方針とか仕様とかは

  1. 1ファイルにHTML, JS, CSSが書ける
  2. Vue.js を拡張したりはしない
  3. ビルドツールは使わずにコードを書いたらすぐ反映される

あたりか。

とりあえずは、Vue.js のマニュアルにある 単一ファイルコンポーネント を参考にすすめてみる。

仕様の詳細

1ファイルにHTML, JS, CSSが書ける

これはそれほど面倒ではなく、ただ単にHTMLファイルを書けば良いだけ。むしろ .vue だとエディタによってはハイライト出来ないという問題も解決できると思われる。

Vue.jsを拡張したりはしない

マニュアルと同じように、分かりやすく HTML, JS, CSS を分離するように x-template を利用すると良い。

ビルドツールは使わずにコードを書いたらすぐ反映される

ビルドツールは使わないのでHTTPの仕様で複数ファイルを一つにまとめる必要があるのだが、HTML5には import構文があるのでそれを使えば良い。

ただ、import構文は単に import するだけなので、DOMに追加するにはJSで追加処理を行わなくてはいけない。これの詳細は後述するサンプルにて。

サンプル

img

上記の仕様を踏まえて実装すると以下の感じに分離出来る。

index.html

<html>
  <head>
    <script src="http://unpkg.com/vue"></script>

    <!-- TemplateをDOMに挿入する関数 -->
    <script>
    function addTemplate(elm) {
      var self = document.currentScript.ownerDocument;
      var template = self.querySelector(elm);
      document.querySelector("#templates").appendChild(template.cloneNode(true));
    }
    </script>
  </head>
  <body>
    <!-- 実際のDOM -->
    <div id="app">
      <todo-item v-for="todo in todos" v-bind:item="todo"></todo-item>
    </div>

    <!-- Template置き場 -->
    <div id="templates">
    </div>

    <!-- Importでロード -->
    <link rel="import" href="/static/import/todoItem.html">

    <!-- Vue化 -->
    <script src="/static/js/script.js"></script>
  </body>
</html>

todoItem.html

<!-- x-templateを使ったHTMLパート -->
<script type="text/x-template" id="todo-item">
  <span>{{ item.name }}</span>
</script>

<!-- スクリプトのパート-->
<script>
  // テンプレートの追加
  addTemplate("#todo-item");

  // ここはVueそのまま
  Vue.component("todoItem", {
    props: ["item"],
    template: "#todo-item"
  })
</script>

<!-- スタイルシートのパート -->
<style>
  span {
    padding: 10px;
  }
</style>

script.js

// ここはVueそのまま
var app = new Vue({
  el: "#app",
  data: {
    todos: [
      { name: "one" },
      { name: "two" },
      { name: "three" }
    ]
  }
});

問題点

面倒もないし分かりやすくもなったのだが、問題もある。

ビルドツールを使わないのでAltJSやCSSプリプロセッサが使えないというのは当然だが、他に Scoped CSS が使えないという点がある。しかし、これはクラス名を工夫すれば特に問題になることは無いだろう。

他には、既存のコンポーネントが使えないんだけど、そのコンポーネントを再開発するコストとビルドツールを覚えるコストを比較してその時に考えれば良いかなと。


新しい事を覚えるという行為を限られた時間の中で行うには非常に厳しい。新しい内容とすでに知ってる内容のバランスをとりながらうまく世の中を渡って行きたい。