Options API(thisやdata()などを用いた記述)で書かれたVueコンポーネントをComposition API(setup()関数を用いた記述)に書き換えます。
簡便のため、<script setup>
シュガー(=糖衣構文)を使用します。
詳しく知りたい方は<script setup>
についてのvueのドキュメントを確認してください。
トップレベルで定義した変数や関数、importなどは、そのままtemplateタグ内で使用することができます。
ます、scriptタグにsetupを書き足し、もとのdefineComponentsはすべてコメントで囲います。 作業が終了した際は、コメントにした行は消去してください。
<template>...</template>
<script lang="ts" setup>
import { onMounted, ... } from 'vue';
import { defaultStore } from '@/store';
... // ここにコードを追加していく
/* (作業後削除する)
export default defineComponent({
...
*/
</script>
<style>...</style>
もし書き換え前のファイルでexport default defineComponent
の前に何らかの処理(コンポーネントを読み込んだ直後の1回きりしか実行しない処理)が書かれている場合、
次のように<script setup>
の上に<script>
タグを挿入し、その中に書いてください。
このとき、import行群はまとめて上のscriptの先頭に書きます。
<template>...</template>
<script lang="ts">
import { onMounted, ... } from 'vue';
import { defaultStore } from '@/store';
const hoge = /* nanka iroiro */
export default {
hoge,
}
</script>
<script lang="ts" setup>
... // ここにコードを追加していく
</script>
<style>...</style>
// Options APIでのpropsの例
export default defineComponent({
props: {
hoge: {
type: Array as PropType<{ foo: string; bar: string }[]>,
required: true,
},
fuga: {
type: String,
required: false,
default: 'DEFAULT'
},
},
...
propsを書き換えます。
まず、ファイル全体でthis.<prop名>
をprops.<prop名>
に置き換えます。
次のようなprops定義を、import行群の次に書き加えます。withDefaultsやdefinePropsは糖衣構文に定義されているのでimportは不要です。
const props = withDefaults(defineProps<{
hoge: { foo: string; bar: string }[];
fuga?: string;
}>(), {
fuga: 'DEFAULT',
});
definePropsのみを使用します。
const props = defineProps<{
hoge: { foo: string; bar: string }[];
fuga?: string;
}>();
<!-- Options APIでのemitsの例 -->
<template>
<div @click="$emit('close')">
</template>
<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({
emits: ['done', 'close'],
methods: {
hoge(result: any) {
this.$emit('done', result);
this.$emit('close');
}
},
});
</script>
ファイル全体でthis.$emit
/$emit
をemit
に置き換え、defineEmits
で以下の例のようにemitを定義します。
第2引数(v
)は使用状況に応じて定義してみてください。
<template>
<div @click="emit('close')">
</template>
<script lang="ts" setup>
const emit = defineEmits<{
(e: 'done', v: any): void;
(e: 'close'): void;
}>();
function hoge(v) {
emit('done', v);
emit('close')
}
</script>
<!-- Options APIでの$el, $refsの例 -->
<template>
<div class="root">
<div ref="foo" />
<XHoge ref="hoge" />
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import XHoge from '@/components/hoge.vue';
export default defineComponent({
components: {
XHoge
},
mounted() {
console.log(this.$el); // HTMLDivElement
console.log(this.$refs.foo); // HTMLDivElement
console.log(this.$refs.hoge.fuga); // XHoge["fuga"]
}
});
</script>
次のように書き換えます。後述する$refと同じ扱いになります。
<template>
<div ref="rootEl" class="root"> <!-- $elはrefにする -->
<div ref="foo" />
<XHoge ref="hoge" />
</div>
</template>
<script lang="ts" setup>
import { onMounted } from 'vue';
import XHoge from '@/components/hoge.vue';
let rootEl: HTMLDivElement = $ref();
let foo: HTMLDivElement = $ref();
let hoge: InstanceType<typeof XHoge> = $ref();
onMounted(() => { // ライフサイクルフックについては後述します
console.log(rootEl); // HTMLDivElement | undefined
console.log(foo); // HTMLDivElement | undefined
console.log(hoge.fuga); // XHoge["fuga"] | undefined
});
</script>
FYI: https://v3.ja.vuejs.org/guide/composition-api-template-refs.html
- ファイル全体で
this.$refs.<ref名>
→<ref名>
、this.$el
→rootEl
と置き換えます。 - 次に説明するdataなどと名前が被る可能性があるので、その場合は名前に
El
を付け足す(fooEl
などにする)ようにしてください(template内のref指定も変更します)。 - refはmount前に呼び出されてundefinedになる可能性があるで、そこを考慮してよしなに編集してください。
// Options APIでのdata()の例
import { defineComponent } from 'vue';
export default defineComponent({
data() {
return {
hogeList: [] as number[],
staticObj: {
foo: 'foo',
bar: 'bar',
},
}
},
computed: {
theFirst(): number | null {
return this.hogeList[0] || null;
},
}
methods: {
pushToHoge(item: number) {
this.hogeList.push(item);
},
},
});
- Reactivity Transform(ref sugar)を使用します。
- data()はletで定義します。リアクティブにしたい場合はref関数を使用します。
- computedは$computed関数を使用します。
- methodはfunctionとして定義します。
- ファイル全体でそれぞれ次のように置き換えます。
this.<静的なdata名>
→<静的なdata名>
this.<リアクティブなdata名>
→<リアクティブなdata名>
this.<computed名>
→<computed名>
this.<method名>(
→<method名>(
import { } from 'vue';
let hogeList: number[] = $ref([]);
const staticObj = {
foo: 'foo',
bar: 'bar',
};
const theFirst: number | null = $computed(() => hogeList[0] || null);
function pushToHoge(item: number) {
hogeList.push(item);
}
// Options APIでのライフサイクルフックの例(mounted)
import { defineComponent } from 'vue';
export default defineComponent({
mounted() {
console.log('MOUNTED!');
},
beforeUnmount() {
console.log('BEFORE_UNMOUNT!');
},
});
次のように置き換えます。
// vueパッケージからライフサイクルフック関数をインポート
import { onMouted, onBeforeUnmount } from 'vue';
/* もろもろの処理 */
onMounted(() => {
console.log('MOUNTED!');
});
onBeforeUnmount(() => {
console.log('BEFORE_UNMOUNT!');
});
// Options APIでの$watch
this.$watch('hoge', () => console.log('change', 'hoge'));
this.$watchはwatch関数に置き換えます。
FYI: https://v3.ja.vuejs.org/api/computed-watch-api.html#watch
import { watch } from 'vue';
const props = defineProps<{ bar: string }>();
const foo = $ref(true);
watch(foo, () => console.log('change', 'foo'));
watch(() => props.bar, () => console.log('change', 'bar'));
呼び出し先がデータや関数を使用している場合、外に公開するものをdefineExposeで明示する必要があります。
defineExpose({
foo,
hogeList,
});
FYI: https://v3.ja.vuejs.org/api/sfc-script-setup.html#defineexpose
万が一slotsやattrsが必要になった場合には、それぞれuseSlots
とuseAttrs
ヘルパーを使用します。
(コードが読みやすいようにpropsの前に定義するようにします。)
FYI: https://v3.ja.vuejs.org/api/sfc-script-setup.html#useslots-%E3%81%A8-useattrs
import { useSlots, useAttrs } from 'vue'
const slots = useSlots()
const attrs = useAttrs()
this
が使えなくなるため、グローバルプロパティ($i
, $store
, $instance
, $t
, $ts
)も使えません。
グローバルプロパティはもともと保守性の低下を招くため、廃止する方針となっています。
各グローバルプロパティの置き換え方法を次に示します(<template>
内に関しても編集してください)。
@/account
から$i
をインポートします。
import { $i } from '@/account';
@/instance
からインポートしたinstance
オブジェクトを使用します。
$instance
をinstance
に置き換えます。
import { instance } from '@/instance';
<!-- Options APIでの$store, defaultStoreの例 -->
<template>
<div v-if="fuga">{{ $store.state.bar }}</div>
<div v-if="$store.reactiveState.piyo.value">{{ $store.reactiveState.piyo.value }}</div>
<XSwitch v-model="foo">SWITCH!</XSwitch>
</template>
<script lang="ts">
import { defaultStore } from '@/store';
import XSwitch from '@/components/switch';
export default defineComponent({
components: {
XSwitch,
},
computed: {
foo: defaultStore.makeGetterSetter('foo'),
fuga: defaultStore.makeGetterSetter('hoge', v => !v, v => !v),
},
});
</script>
@/store
からインポートしたdefaultStore
を使用します。
通常はdefaultStore.reactiveState
から分割代入するのが簡単です。
fuga
のようにmakeGetterSetter
でカスタムのget/set関数を使う場合も例示しています。
defaultStore.state
を参照している場合、意図的に静的な値を参照することにした可能性があります(Issue)。
リアクティブが必要かどうか検討していただき、分割代入でリアクティブな値にするかそのまま$store
をdefaultStore
に置き換えるかを選択してください。
<template>
<div v-if="fuga">{{ defaultStore.state.bar }}</div>
<div v-if="piyo">{{ piyo }}</div>
<XSwitch v-model="foo">SWITCH!</XSwitch>
</template>
<script lang="ts" setup>
import { defaultStore } from '@/store';
import XSwitch from '@/components/switch';
const { foo, piyo } = defaultStore.reactiveState;
const fuga = computed(defaultStore.makeGetterSetter('hoge', v => !v, v => !v));
</script>
@/i18n
からi18n
をインポートし、$t
/this.$t
をi18n.t
、$ts
/this.$ts
をi18n.locale
に置き換えます。
import { i18n } from '@/i18n';
console.log(i18n.t('_hoge.fuga', { foo: 30 }));
console.log(i18n.locale._foo.bar);
@/router
からrouter
をインポートします。
$router
はrouter
、$route
はrouter.currentRoute.value
に置き換えます。