composition-apiのスコープが理解できていなくて、同じコンポーネントを複数表示しているときに、scriptタグ直下の変数やオブジェクトが連動することがわかりました。
#何があった?
気が付くと今年にはVue.js 3.0が出るらしい。
しかも、今までの書式とは結構変わってしまうらしい…。
何と言う事でしょう、巧1の仕業か!?
Composition APIを調べてみて思った事
Vue.js 3.0から導入される新機能の大玉はComposition APIと言う仕組みらしいです。
なんでもTypeScriptとの相性を解消するためだったり2とか、高速化の為だったりとか。
当初、Vue.js 2.xでも完璧超人3に感じてたので、わざわざ新しい書式なんて要らないんじゃないかな?
…と思ったのですが、Composition APIを少し触ってみて結構いいんじゃないと思ったのと、自分なりに思う所が出てきたので、そういった部分をメモしてみたいと思います。
setupについて
Composition APIではテンプレートで使用する要素をsetupでreturnする仕様になっているらしい。
また、いくつかの解説(それは公式も)ではsetup内に関数を入れているみたいだけど、setupと言う名前だし、あんまり業務ロジック的な関数は中に入れたくない気がします。
出来ればdefineComponent
の外に配置したいな思いました。
逆にreactive
やref
、computed
などは、setupの外でも使用可能だけど、これらはsetup()の中で指定するほうが見渡しが良くなるような感じもする…。
この部分は大きな間違いで、defineComponent
の外で関数や変数を定義すると他コンポーネントと干渉してしまいます。検証不足でした…。
<script>
import { reactive } from '@vue/composition-api'
export default defineComponent({
setup() {
const state = reactive({
name: 'てすと',
price: 1000,
text: ''
})
const method = {
taxCalculator(price){
const taxRate = 1.1
return Math.floor(price * taxRate)
}
}
return {
state,
...method
}
}
})
</script>
例えば、こんな感じ?
つまるところ、setup関数の中に自由に変数や関数類かけるので、今までのように決められたobject
に記述しなくてよいので、連動している機能などをまとめて記述できるというのが一つの売りなのでしょうか。
setup関数の第二引数について
setup関数の第二引数内には、「root, parent, refs, attrs, listeners, isServer, ssrContext, emit, slots」など含まれているけど、今までmethodの中でバンバンthis.$emit
なんて書いてきたので、なんか違和感を感じました。
なんかこの第二引数の内容って、ちょっと隠したい物の置き方みたいに感じます4。
それにemitを使うためにsetup内に関数を書くのは、ちょっとやりたくないかなって思いました。
でも、emitは割とよく使うし…どうすればいいんだろうと考えました。
emitの使い方を考える
↑で書いたみたいに、setup内でemitを含んだ関数は書きたくない!!!
それでふと、他にemit出来る場所って結局templateの中しかないと思った時に、なんとなくもともとemitはtemplateから直接呼びたいものなのではないかと思いました5。
<template>
<dl>
<dt>名前入力</dt>
<dd>
<input
type="text"
@input="$emit('input', method.extractionValue($event))"
/>
</dd>
</dl>
</template>
こういう感じにemitは直接templateから指定して、引数からメソッドを呼んで必要に応じて成型した戻り値を親コンポーネントへ送ります。
分岐からのemitの場合
ただ時々ある、ボタン押してからstateの条件に応じてemitの選り分け等を行う場合にはtemplateからのemitでは辛いかもと思う時があります。
自分の考えとしては、選り分け処理は親コンポーネントに任せてとりあえず値を親に送り付けるという方法が良いのではないかと思います。
今までは、↓のように分岐してからemitしてた。
<template>
<button @click="submit()">送信</button>
</template>
submit () {
if (this.flag > 1) {
this.$emit('submit')
}
}
でもこれからはtemplateでとりあえず、親へ送っちゃう
<template>
<button @click="$emit('submit', status)">送信</button>
</template>
// これは親コンポーネントの処理
function submit(status){
if (this.status > 1) {
// 条件に応じての処理
}
}
routeについて
routerじゃなくてrouteの方もemitとよく似てます。
これも結局templateから普通に呼べるので、メソッドを呼ぶときに引数に加えてあげればいいかなと思います。
<input
type="number"
@input="$emit('input', hoge($route.name))"
>
ただ、ページの一番最初にAPI呼ぶときのパラメータとかで必要だったりするので、そういう場合はsetupの第二引数を使って呼ぶ必要があるかもしれません。
setup(props, { root }){
cosnt pageName = root.$route.name
axios.get(`/api/hogehoge/${pageName}`).then(...)
}
storeとかrouterについて
これらは普通にimportしちゃうのが良い感じだと思いました。
import store from '@/store'
import router from '@/router'
function hoge () {
store.dispatch('fugafuga')
router.push('hogehoge')
}
axiosはどうする?
vueの公式サイトでもaxiosをvueのインスタンスの中に突っ込んで、this.$axios
みたいな呼び方をするのがメジャーかなって思いましたが、これも普通にimportして使うのが良いかもしれません。
Componentの参照方法について
store
やrouter
、axios
をimportするようになると、コンポーネントのためのimportと混在してちょっと見通しが悪い気がします。
なので、コンポーネントはrouter.jsみたいな感じに書いちゃった方が、さっぱりするかもしれません。
※これは完全に個人的な思いつきでやっていますので、特に参考になりません。
import { defineComponent } from '@vue/composition-api'
import axios from '@/axios'
export default defineComponent({
components: {
Hoge: () => import('./Hoge.vue'),
Fuga: () => import('./Fuga.vue'),
},
setup() {
// ...
}
}
…と言う夢を見た
と言う感じにComposition APIを学んだ結果、こんな夢を見てしまったわけですが、特にemitなどのあしらい方はそのままvue.js 2.xでも実践できるなと思いました。
もしかしたら、vue.jsは自分が思ってる以上にTemplateに焦点を当てて考えるべきなのかもと思いました。
そして、この夢が良いのかどうかを検証するためにも実践していきたいと思ったのでした…。
ここまで、駄文を読んでくれた人がいましたら、ありがとうございました。
2020年3月9日追記:思いっきりスコープの概念を勘違いしていたので、一部編集いたしました。
結果、composition-apiと言うのはsetup関数内に自由に変数・関数を書けるという事なのかなと思っています。
ただ、あまりにも多くの機能になる場合は、適宜コンポーネントなどに分離する必要があるかなと思います。
現状、ちょっぴり玄人風と言う事と今後進むであろうtypescriptへの対応と言う点以外では、慌てて採用する必要もないのかもしれません。
注意事項
この記事はある種の夢でありチラシの裏でありポエムの様なものです。
参考にならないなと思った場合は、そっ閉じしてくれると嬉しいです。