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に置き換えます。