vue.js
Vue.js #3Day 23

初めてのVue Plugin開発 〜 vue-syntagme

More than 1 year has passed since last update.

この記事はVue.js #3 Advent Calendar 2017 – Qiita 23日目の記事です。

拙作であるFlux Framework、syntagmeのVuePluginを書く機会があり、

それを通じてVueの仕組みの理解を深めたのでそのお話です。

vue-syntagmeはvue-routerの実装を参考にさせていただきました。


環境


  • Vue 2.5


構造


プラグインの登録 - install

公式のドキュメントでは如何のように説明されています。


Vue.js プラグインは install メソッドを公開する必要があります。このメソッドは第 1 引数は Vue コンストラクタ、第 2 引数は任意で options が指定されて呼び出されます


https://jp.vuejs.org/v2/guide/plugins.html#%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%AE%E8%A8%98%E8%BF%B0

今回、syntagmeではこのinstallを使って2つの機能を提供しました。



  • vm.$state ... stateオブジェクトへのアクセサ。


  • vm.$action ... actionCreatorの実行。


mixinの定義

今回行う対応は3つあります。

1. $state というリアクティブなプロパティの定義

2. $action というsyntagmeのインスタンスにプロキシするメソッドの定義

3. 継承していれば子でも上記機能を利用できるようにする仕組み

これらは、install関数内でmixinを定義します。


vue-syntagme/install.js

export function install (Vue) {

if (install.installed && _Vue) return
install.installed = true

Vue.mixin({
})
}



初期化処理

syntagmeの実行を初期化するため、ルートとなるcomponentではsyntagmeをラップしたVueSyntagmeのインスタンスを初期化します。


vue-syntagme/install.js

Vue.mixin({

beforeCreate () {
this._syntagme = this.$options.syntagme
this._syntagme.init()
}
})

このとき、 this はrootのvmであり、this.$options.syntagme はVueSyntagmeのインスタンスを想定しています。


App.vue

Vue.use(VueSyntagme)

const syntagme = new VueSyntagme(options)
export { syntagme }

こんなイメージで利用します


vm.$stateプロパティの定義

vm.$state はVueのライフサイクルとは異なるsyntagmeのライフサイクルで更新させます。ただsyntagmeのstateを参照したとしても、そこで起きた更新をVueは検知できません。

そこで初期化時にVue.util.defineReactiveを用いてsyntagmeのstateの更新をVueに伝えられるようにします。

defineReactiveについてはVue.jsでリアクティブな独自データスコープを定義するの記事を参考にさせていただきました。


vue-syntagme/install.js

Vue.mixin({

beforeCreate () {
this._syntagme = this.$options.syntagme
this._syntagme.init()
Vue.util.defineReactive(this, '_state', this._syntagme._state)
}
})

これでstateを参照できるように... おっと、_state直接見るっていうのはお行儀が良くないですね。参照用のプロパティを定義します。


vue-syntagme/install.js

export default install (Vue) {

Vue.mixin({
beforeCreate () {
this._syntagme = this.$options.syntagme
this._syntagme.init()
Vue.util.defineReactive(this, '_state', this._syntagme._state)
}
})

Object.defineProperty(Vue.prototype, '$state', {
get () { return this._syntagmeRoot._state }
})
}



vm.$actionメソッドの定義

こちらは単純に関数の追加なので、Vueのprototypeに直接機能を追加します。


vue-syntagme/install.js

export default install (Vue) {

Vue.mixin({
beforeCreate () {
this._syntagmeRoot = this
this._syntagme = this.$options.syntagme
this._syntagme.init()
Vue.util.defineReactive(this, '_state', this._syntagme._state)
}
})

Object.defineProperty(Vue.prototype, '$state', {
get () { return this._syntagmeRoot._state }
})

Vue.prototype.$action = function (action_type, args) {
return this._syntagmeRoot._syntagme.action(action_type, args)
}
}



機能を継承する

想定では、Rootでsyntagmeのインスタンスを渡し、それを子のContainerComponentで利用します。

なので、Rootのときは初期化を、それ意外の場合は、親からsyntagmeのプロパティを参照します。


vue-syntagme/install.js

  Vue.mixin({

beforeCreate () {
if (isDef(this.$options.syntagme)) {
this._syntagmeRoot = this
this._syntagme = this.$options.syntagme
this._syntagme.init(this)
Vue.util.defineReactive(this, '_state', this._syntagme._state)
} else {
this._syntagmeRoot = (this.$parent && this.$parent._syntagmeRoot)
}
},
})


VueSyntagmeクラス

ここまで度々登場してきた、処理の本体であるところのVueSyntagmeクラスですが、詳細はsyntagmeの実装に依存するので割愛します。

今回プラグインとして公開するaction, プラグイン内で利用するinit _state, そしてスタティックメソッドとして定義されたinstallを持ちます。


vue-syntagme/index.js

import install from './install'

export default class VueSyntagme {
init () { ... }
action (type, parms) { ... }
actionCreator(type, ac) { ... }
}

VueSyntagme.install = install
VueSyntagme.version = '__VERSION__'


このクラスはインスタンス化され、プラグイン外から呼び出すことも想定しています。


Vue.use

useします。この辺はおなじみですね。


App.vue

import Vue         from 'vue'

import VueSyntagme form 'vue-syntagme'

Vue.use(VueSyntagme)

const syntagme = new VueSyntagme()

syntagme.actionCreator('TAP', () => {
return { action: 'tap' }
})

export {
syntagme,
computed () {
count () { return this.$state.tapCount }
},
methods: {
tap () { this.$action('TAP') }
}
}



まとめ

本記事では、Vueのプラグイン機構を用いて、プロパティ、関数などを実際に登録する手順を示しました。

Vueは公式ドキュメントが充実しており、ほとんどの場合公式ドキュメントで問題が解決するのですが、それはそれとして、すでにVueには多くのプラグインが提供され、軽く目を通すだけでも勉強になります。興味がある方はぜひ読んでみるとよいです。

このように、まあ誰が使うかも定かではありませんが、試しにプラグインを書いてみるとより理解が深まりますのでぜひ一度書いてみるとVueの世界をしるのでおすすめです。

※なお、Vueは公式にFluxフレームワークとしてvuexを推奨しています。僕の場合は事情がある程度特殊なため、自前フレームワークとそれを利用するプラグインを使っています。