14
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue.js #3Advent Calendar 2017

Day 23

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

Last updated at Posted at 2017-12-22
1 / 2

この記事は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 が指定されて呼び出されます

今回、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を推奨しています。僕の場合は事情がある程度特殊なため、自前フレームワークとそれを利用するプラグインを使っています。

14
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?