Vue3.2 で正式に導入された SFC 用の新構文
7/1にリリースされた Vue2.7 にも導入されました。
<script setup> import { ref } from "vue"; import MyComponent from "./MyComponent.vue"; const count = ref(0); function handleClick() { count.value++; } </script> <template> <button @click="handleClick"> {{ count }} </button> <MyComponent /> </template>
<script setup> import { ref } from "vue"; import MyComponent from "./MyComponent.vue"; const count = ref(0); function handleClick() { count.value++; } </script> <template> <button @click="handleClick"> {{ count }} </button> <MyComponent /> </template>
<script setup> const msg = "Hello!"; function handleClick() { alert(msg); } </script> <template> <button @click="handleClick"> {{ msg }} </button> </template>
<script setup> const msg = "Hello!"; function handleClick() { alert(msg); } </script> <template> <button @click="handleClick"> {{ msg }} </button> </template>
別の方法ではこのように書いていました。
<script> export default { setup() { const msg = "Hello!"; function handleClick() { alert(msg); } return { msg, handleClick, }; }, }; </script> <template> <button @click="handleClick"> {{ msg }} </button> </template>
<script> export default { setup() { const msg = "Hello!"; function handleClick() { alert(msg); } return { msg, handleClick, }; }, }; </script> <template> <button @click="handleClick"> {{ msg }} </button> </template>
<script> export default { data() { return { msg: "Hello!", }; }, methods: { handleClick() { alert(this.msg); }, }, }; </script> <template> <button @click="handleClick"> {{ msg }} </button> </template>
<script> export default { data() { return { msg: "Hello!", }; }, methods: { handleClick() { alert(this.msg); }, }, }; </script> <template> <button @click="handleClick"> {{ msg }} </button> </template>
import
するだけで使用できます<script setup> import { ref } from "vue"; import MyButton from "./MyButton.vue"; const count = ref(0); function handleClick() { count.value++; } </script> <template> <MyButton @click="handleClick"> {{ count }} </MyButton> </template>
<script setup> import { ref } from "vue"; import MyButton from "./MyButton.vue"; const count = ref(0); function handleClick() { count.value++; } </script> <template> <MyButton @click="handleClick"> {{ count }} </MyButton> </template>
別の方法ではこのように書いていました。
<script> import { ref } from "vue"; import MyButton from "./MyButton.vue"; export default { components: { MyButton, }, setup() { const count = ref(0); function handleClick() { count.value++; } return { count, handleClick, }; }, }; </script> <template> <MyButton @click="handleClick"> {{ count }} </MyButton> </template>
<script> import { ref } from "vue"; import MyButton from "./MyButton.vue"; export default { components: { MyButton, }, setup() { const count = ref(0); function handleClick() { count.value++; } return { count, handleClick, }; }, }; </script> <template> <MyButton @click="handleClick"> {{ count }} </MyButton> </template>
<script> import MyButton from "./MyButton.vue"; export default { components: { MyButton, }, data() { return { count: 0, }; }, methods: { handleClick() { this.count++; }, }, }; </script> <template> <MyButton @click="handleClick"> {{ count }} </MyButton> </template>
<script> import MyButton from "./MyButton.vue"; export default { components: { MyButton, }, data() { return { count: 0, }; }, methods: { handleClick() { this.count++; }, }, }; </script> <template> <MyButton @click="handleClick"> {{ count }} </MyButton> </template>
v
で始まる変数を使用しますawait
も使用できます<script setup> import vMyDirective from "./my-directive.js"; const post = await fetch(`/api/post/1`).then((r) => r.json()); </script> <template> <p v-my-directive> {{ post }} </p> </template>
<script setup> import vMyDirective from "./my-directive.js"; const post = await fetch(`/api/post/1`).then((r) => r.json()); </script> <template> <p v-my-directive> {{ post }} </p> </template>
別の方法ではこのように書いていました。
<script> import vMyDirective from "./my-directive.js"; export default { directives: { "my-directive": vMyDirective, }, async setup() { const post = await fetch(`/api/post/1`).then((r) => r.json()); return { post, }; }, }; </script> <template> <p v-my-directive> {{ post }} </p> </template>
<script> import vMyDirective from "./my-directive.js"; export default { directives: { "my-directive": vMyDirective, }, async setup() { const post = await fetch(`/api/post/1`).then((r) => r.json()); return { post, }; }, }; </script> <template> <p v-my-directive> {{ post }} </p> </template>
<script> import vMyDirective from "./my-directive.js"; export default { directives: { "my-directive": vMyDirective, }, data() { return { post: "", // 非同期はスマートな書き方ができない }; }, async mounted() { this.post = await fetch(`/api/post/1`).then((r) => r.json()); }, }; </script> <template> <p v-my-directive> {{ post }} </p> </template>
<script> import vMyDirective from "./my-directive.js"; export default { directives: { "my-directive": vMyDirective, }, data() { return { post: "", // 非同期はスマートな書き方ができない }; }, async mounted() { this.post = await fetch(`/api/post/1`).then((r) => r.json()); }, }; </script> <template> <p v-my-directive> {{ post }} </p> </template>
defineProps
というコンパイラマクロを使用しますdefineEmits
というコンパイラマクロを使用します<script setup> import { ref } from "vue"; const props = defineProps({ modelValue: String, }); const emit = defineEmits(["update:modelValue", "send"]); const inputRef = ref(); function handleInput() { emit("update:modelValue", inputRef.value.value); } function handleClick() { emit("send", props.modelValue); } </script> <template> <input ref="inputRef" :value="modelValue" @input="handleInput" /> <button @click="handleClick">send</button> </template>
<script setup> import { ref } from "vue"; const props = defineProps({ modelValue: String, }); const emit = defineEmits(["update:modelValue", "send"]); const inputRef = ref(); function handleInput() { emit("update:modelValue", inputRef.value.value); } function handleClick() { emit("send", props.modelValue); } </script> <template> <input ref="inputRef" :value="modelValue" @input="handleInput" /> <button @click="handleClick">send</button> </template>
別の方法ではこのように書いていました。
<script> import { ref } from "vue"; export default { props: { modelValue: String, }, emits: ["update:modelValue", "send"], setup(props, { emit }) { const inputRef = ref(); function handleInput() { emit("update:modelValue", inputRef.value.value); } function handleClick() { emit("send", props.modelValue); } return { inputRef, handleInput, handleClick, }; }, }; </script> <template> <input ref="inputRef" :value="modelValue" @input="handleInput" /> <button @click="handleClick">send</button> </template>
<script> import { ref } from "vue"; export default { props: { modelValue: String, }, emits: ["update:modelValue", "send"], setup(props, { emit }) { const inputRef = ref(); function handleInput() { emit("update:modelValue", inputRef.value.value); } function handleClick() { emit("send", props.modelValue); } return { inputRef, handleInput, handleClick, }; }, }; </script> <template> <input ref="inputRef" :value="modelValue" @input="handleInput" /> <button @click="handleClick">send</button> </template>
<script> export default { props: { modelValue: String, }, emits: ["update:modelValue", "send"], methods: { handleInput() { this.$emit("update:modelValue", this.$refs.inputRef.value); }, handleClick() { this.$emit("send", this.modelValue); }, }, }; </script> <template> <input ref="inputRef" :value="modelValue" @input="handleInput" /> <button @click="handleClick">send</button> </template>
<script> export default { props: { modelValue: String, }, emits: ["update:modelValue", "send"], methods: { handleInput() { this.$emit("update:modelValue", this.$refs.inputRef.value); }, handleClick() { this.$emit("send", this.modelValue); }, }, }; </script> <template> <input ref="inputRef" :value="modelValue" @input="handleInput" /> <button @click="handleClick">send</button> </template>
defineProps
とdefineEmits
は TypeScript を使用する場合、型のみの定義を使用できます defineProps
では Prop の型を定義します withDefaults
を併用しますdefineEmits
では関数の型を定義します<script setup lang="ts"> // ... const props = withDefaults( defineProps<{ modelValue: string; message?: "Hello!" | "Goodby!"; }>(), { message: "Hello!", } ); const emit = defineEmits<{ (e: "update:modelValue", newValue: string): void; (e: "send", sendValue: string): void; }>(); // ... </script>
<script setup lang="ts"> // ... const props = withDefaults( defineProps<{ modelValue: string; message?: "Hello!" | "Goodby!"; }>(), { message: "Hello!", } ); const emit = defineEmits<{ (e: "update:modelValue", newValue: string): void; (e: "send", sendValue: string): void; }>(); // ... </script>
型のみ定義方法を使用しない場合はこのように書いていました。
<script setup lang="ts"> // ... const props = defineProps({ modelValue: { type: String, required: true, }, message: { type: String, // ユニオン型にはできません。 default: "Hello!", }, }); const emit = defineEmits({ "update:modelValue": (newValue: string) => true, send: (sendValue: string) => true, }); // ... </script>
<script setup lang="ts"> // ... const props = defineProps({ modelValue: { type: String, required: true, }, message: { type: String, // ユニオン型にはできません。 default: "Hello!", }, }); const emit = defineEmits({ "update:modelValue": (newValue: string) => true, send: (sendValue: string) => true, }); // ... </script>
defineExpose
を使用して外部から参照できるインスタンスプロパティを定義しますuseSlots
とuseAttrs
を使用して$slots
や$attrs
のような情報にアクセスできます<script setup>
とは別に<script>
ブロックを定義できます export default {}
を定義することで、inheritAttrs
などのコンポーネントオプションを定義できます.vue
がexport
する情報を定義できます<script setup> import { useSlots, useAttrs } from "vue"; const slots = useSlots(); const attrs = useAttrs(); // ... </script>
<script setup> import { useSlots, useAttrs } from "vue"; const slots = useSlots(); const attrs = useAttrs(); // ... </script>
<script> // module が呼び出されたときに一度だけ実行される runSideEffectOnce(); // オプションの定義 export default { inheritAttrs: false, }; </script> <script setup> // ... </script>
<script> // module が呼び出されたときに一度だけ実行される runSideEffectOnce(); // オプションの定義 export default { inheritAttrs: false, }; </script> <script setup> // ... </script>
Vue3.3 で正式に導入予定の新構文
(Vue3.2 でも実験的機能として使用可能)
<script setup> import MyComponent from "./MyComponent.vue"; const count = $ref(0); function handleClick() { count++; } </script> <template> <button @click="handleClick"> {{ count }} </button> <MyComponent /> </template>
<script setup> import MyComponent from "./MyComponent.vue"; const count = $ref(0); function handleClick() { count++; } </script> <template> <button @click="handleClick"> {{ count }} </button> <MyComponent /> </template>
Ref オブジェクトの.value
を消し去れます。
以前:
<script setup> import { ref, toRefs, computed } from "vue"; import { useMouse } from "@vueuse/core"; const count = ref(0); function handleClick() { count.value++; } const { x, y } = toRefs(useMouse()); const mousePosition = computed(() => `x: ${x.value}, y: ${y.value}`); </script>
<script setup> import { ref, toRefs, computed } from "vue"; import { useMouse } from "@vueuse/core"; const count = ref(0); function handleClick() { count.value++; } const { x, y } = toRefs(useMouse()); const mousePosition = computed(() => `x: ${x.value}, y: ${y.value}`); </script>
Reactivity Transform 使用:
<script setup> import { useMouse } from "@vueuse/core"; let count = $ref(0); function handleClick() { count++; } let { x, y } = $(useMouse()); const mousePosition = $computed(() => `x: ${x}, y: ${y}`); </script>
<script setup> import { useMouse } from "@vueuse/core"; let count = $ref(0); function handleClick() { count++; } let { x, y } = $(useMouse()); const mousePosition = $computed(() => `x: ${x}, y: ${y}`); </script>
詳細は Reactivity Transform のドキュメントを参照してください。
https://vuejs.org/guide/extras/reactivity-transform.html
現在、実験的機能であるため、このスライドでは詳細は割愛します。
また、このスライドに載せた記述方法や機能は変更になる可能性があります。
Support me ❤ or follow me!!
GitHub: https://github.com/ota-meshi
Twitter: https://twitter.com/omoteota
Qiita: https://qiita.com/ota-meshi