SPA ダケジャナイ Vue.js!汎用ライブラリを Vue で作る

最終更新日
2017.10.31

CMS のアドオン用ライブラリを Vue で作ったら最高に楽になった。Vue のプラグイン機能も使う。

はじめに

少し前に自分で使っている CMS 用の CodeMirror エディタアドオンを jQuery で作ってたんですがだんだんつらくなってきたので Vue で作りなおしました。その時汎用ライブラリとして作成したんですけどちょっと面白かったので記事にしておこうと思います。

ビルドファイルをライブラリにする

外から呼び出せるようにして、マウントする要素は不特定で作成できるようにします。

Webpackの設定

Webpackの設定は output に library を追加するだけ簡単。

  output: {
    library: 'sortTable',
    libraryTarget: 'umd'
  }

これで main.js からエクスポートした値を「sortTable」で使用できるようになります。

いくつかライブラリを作りたい場合その数だけグローバルを汚染してしまうのもあれなので、以下のように library を配列にすると全て VueLib (名前は適当)オブジェクトのプロパティで作成されます。

  entry: {
    example: './src/main.js'
  },
  output: {
    library: ['VueLib', '[name]'],
    libraryTarget: 'umd',
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'VueLib.[name].js'
  },

運用モードのビルドだけ Vue を含めないようにする

externals: {
  'vue': 'Vue'
}

production モードだけ externals オプションに Vue を指定します。

main.jsの書き方

main.js はこんな感じで外から使用するための関数やオブジェクトを返すようにします。

export function init(selector) {
  var el = document.querySelector(selector)
  new Vue({
    el: el,
    render: h => h(App, {
      attrs: {
        id: el.getAttribute('id')
      }
    })
  })
}

インスタンスの作り方

Webpack でバンドルしたファイルをスクリプトタグで読み込み、以下のように関数を実行してアプリの作成を開始できます。

VueLib.example.init('#app')

テーブルをソート出来るようにするライブラリ

サンプルで簡単なライブラリを作ってみました。

jQueryと似たような感覚で読み込んでセレクタを指定するだけでテーブルをソート出来るようにします。

<table id="vue-menu">
  <tr><th>Name</th><th>Power</th></tr>
  <tr><td>Jackie Chan</td><td type="int">7000</td></tr>
  <tr><td>Jet Li</td><td type="int">8000</td></tr>
  <tr><td>Bruce Lee</td><td type="int">9000</td></tr>
  <tr><td>Chuck Norris</td><td type="int">10000</td></tr>
</table>
<script>
  VueLib.sortTable.init('#vue-menu')
</script>

Vue をスクリプトタグで読み込んでいれば他の Vue アプリの中の要素にも使えます。

ソースコード全体は GitHub にあります。

CMS 用のライブラリを作るのでとりあえず考えること

これ以下は上で書いた CMS 用で作ったライブラリについてです。

今までのように SPA やページ全体を一つの Vue アプリにするのではなくてページ内の一部(エディタ部分)だけ対応させます。あとエディタは画面内に複数存在します。アプリケーションを作成開始するのはフォーム内ですが、モーダルウィンドウはオーバーレイを全画面にするためにはポジションの影響を受けない基本 body の直下に置かないといけないので、モーダルウィンドウ用のマウント場所が必要です。

ちなみに、CMS本体でjQueryを使っているのでこのライブラリでもちょこちょこ使ってます。

  • エディタ部分をひとつのアプリにして複数のエディタを使用できるように
  • メインアプリをマウントする要素外にモーダルウィンドウを設置する
  • テンプレートは.vueファイルしか使わないのでランタイム限定モードで良い

ビルドファイルを Vue 同梱のライブラリにする

他のアドオンでも Vue を使うなら Vue のビルドファイルをスクリプトタグで読み込んでグローバルに使うことも出来ますが、他で使う予定がないのでモジュールは全部 Webpack で一つのファイルにバンドルする事にしました。

プロジェクトテンプレートは vue-cli の webpack-simple を使用しました。読み込んだらすぐ実行ではなくて、特定のオプションを使った Vue インスタンスを作成するためにライブラリでビルドするようにします。

テンプレートは単一ファイルコンポーネントとレンダー関数しか使わないので Vue をランタイム限定にするとちょっとコンパクトになります。

  resolve: {
    alias: {
      'vue$': 'vue/dist/vue.runtime.esm.js',
    }
  }

Vue プラグインを作る

今回 Vuex は使用しなかったので、各コンポーネント共通で使うプロパティを楽にするために Vue プラグインを使いました。

初めてプラグイン作ってみたんですけど思ってたよりだいぶ簡単そうです。要は install メソッドを渡せばいいだけですね!

一番簡単なものならきっとこんな感じでしょう。

var MyPlugin = {
  install: function () {
    // インストール時の処理
    // このプラグインで使うカスタムディレクティブや Mixin などまとめて登録
  }
}
// 上で作ったプラグインを Vue.use で登録
Vue.use(MyPlugin)

インスタンス毎に別のプロパティを使うプラグイン

install のみだと全ての Vue インスタンスでプロパティを共有するので、Vue-router のソースを参考にして Vue インスタンス毎に別のプロパティを参照するようにしました。

予めプラグインのインスタンスを作成して、Vue のインスタンスを作成する時にオプションで持たせるようにします。少し簡略してますが、実際のコードは下のまとめのリポジトリにあります。

// プラグインを読み込んで登録
import ModalPlugin from './plugins/ModalPlugin'
Vue.use(ModalPlugin)
export function init(id) {
  // プラグインインスタンスを作成
  // この init で作成する Vue インスタンス専用のプロパティ
  // init した時に受け取った id を持たせる
  const modal = new ModalPlugin(id)
  // Vue インスタンスを作成
  new Vue({
    modal: modal, // 専用のプラグインインスタンスを持たせる
    render: h => h(App, { ...
  })
}

プラグインはインスタンスごとのプロパティを持てるように書きます。

import Vue from 'vue'
class ModalPlugin {

  // インスタンスを作る毎にする処理
  constructor(id) {
    // new で独自のプロパティ id を作成 
    this.id = id
  }

  // インストール時の処理 @1回しか実行されない
  // クラスを使ってるので install メソッドは static で作成
  static install(Vue) {
    Vue.mixin({
      // インスタンス作成時に modal オプションを持っていれば this._modal に置き換える
      beforeCreate() {
        if (typeof this.$options.modal !== 'object') { return }
        this._modal = this.$options.modal
      }
    })
    // $modal プロパティを定義して常にルートから取得する
    // 例で modal オブジェクトの id プロパティを返すことにする
    Object.defineProperty(Vue.prototype, '$modal', {
      get() { return this.$root._modal.id }
    })
  }
}
export default ModalPlugin

これでどこのコンポーネントからでも this.$modalmodal.id を参照するようになります。実際はコンストラクタでモーダル用のビューモデルや CodeMirror のインスタンスを作成してそれを使えるようにしました。

まとめ

現状のアドオンのソース全体は GitHub においてあります。

Vue はコンパクトで軽量なので、jQuery のように気軽に「前提で Vue.js を読み込んでね☆」というのでも良いですね。今回は MODX のアドオンで使いましたが、ユーザーが「読み込んで使うだけ」の色んな汎用ライブラリを Vue で作る事が出来そうです。

WPでは今フロントのフレームワークにもしかしたら Vue が使われるかも?みたいな話が持ち上がってますね。羨ましいです(。→ω←。)