Vue.jsでcomputed以外のプロパティでcomputedっぽいことをする
標題のとおりです。
したいこと
export default { name: 'hoge-component', computed: { hoge ( ) { return 'xxx'; } }, meta: { // これがcomputedのように動いて欲しい reactiveComputed ( ) { // => 'xxx' return this.hoge; } } }
普段はcomputed内だけに定義できる算出ゲッターをmeta内に定義できるようにします。
動的に変わりまくるtitleやpageContext的なものを扱うためにcomputedではないレイヤーにcomputedがほしかった。
やりかた
- globalMixinを使い,createdでmetaが入ってきたコンポーネントが作られたことを検出します。
- metaのそれぞれの関数にコンポーネントをbindして作った関数で別のVueインスタンスを作ります
- 新しく作るVueインスタンスのcomputedに登録してWatcherで追跡してもらいます
watcher = undefined; Vue.mixin({ created() { if (this.$options.meta) { const self = this; const meta = this.$options.meta; const computed = Object.entries(meta).reduce((acc, current) => { if (typeof current[1] === "function") { acc[current[0]] = current[1].bind(self); } return acc; }, {}); watcher = new Vue({ computed, watch: { hoge(to, from) { console.log(to); } } }); } }, beforeDestroy () { if (watcher) { watcher.$destroy(); } } });
これでok. リアクティブな値を好きなスコープに作るのはVue.util.defineReactive
で作れますが, computedを作るにはWatcherをnewするひつようがあるので別のVueインスタンスを作るしかなさげかなと。
Vue.prototype._init
をいじればcomputedにmetaの中身をマージできますが, そうすると同名でcomputedに置けなくなってしまう。
こうすればインスタンスが増えてしまうけれどcomputedを作れます。
コード
さらに
computedと同時に同名のwatchを定義して, 別につくったVueインスタンスのdataを置き換えるようにします。 そうすると散らばったコンポーネントのmetaが一箇所にまとまります.
このVueインスタンスのdataを元に色々と処理をします。 そうすると各ページコンポーネントにmetaってプロパティで定義した算出ゲッターが返す値をdocument.titleに設定できるようになります。
べんり!