Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

by mizuki_r
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 が指定されて呼び出されます

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

mizuki_r
bengo4
「専門家をもっと身近に」を理念として、人々と専門家をつなぐポータルサイト「弁護士ドットコム」「弁護士ドットコムニュース」「税理士ドットコム」を提供。
https://corporate.bengo4.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away