What's New on Vue 3.3 ?
What's New on Vue 3.3 ?
Minggu lalu (11 Maret), Vue.js merilis versi minor terbaru yaitu Vue 3.3 "Rurouni Kenshin" dengan membawa beberapa improvement pada developer experience khususnya pada penggunaan TypeScript di fitur SFC <script setup>
. Bersamaan dengan rilis Volar versi 1.6 yang kini bernama Vue Language Tools, Vue juga telat menyelesaikan banyak long-standing pain points saat menggunakan Vue dengan TypeScript. Apa saja update-nya? Cekidot!
Improvement DX pada penggunaan TypeScript di <script setup
Imported and Complex Types Support in Macros
Sebelumnya, types
yang digunakan pada parameter defineProps
dan defineEmits
terbatas hanya pada lingkup lokal SFC saja, dan hanya mendukung type literals
dan interfaces
.
Kini di Vue 3.3, kita dapat menggunakan type
yang diimpor dari file lain, dan mendukung complex types
yang terbatas:
<script setup lang="ts">
import type { Props } from './types';
// imported + intersection type
defineProps<Props & { extraProp?: string }>();
</script>
Perlu dicatat bahwa dukungan complex types ini AST-based dan karenanya tidak 100% komprehensif. Beberapa tipe yang lebih kompleks memerlukan actual type analysis, misalnya conditional types ini tidak didukung. Kita bisa menggunakan conditional types untuk satu tipe props, tetapi tidak seluruh objek props.
Generic Components
Component yang menggunakan syntax <script setup>
sekarang bisa menerima parameter generic melalui attribute generic
:
<script setup lang="ts" generic="T">
defineProps<{
items: T[];
selected: T;
}>();
</script>
Value-nya sama dengan parameter list pada <...>
di TypeScript. Misalnya, Kita bisa menggunakan beberapa parameter, extends constraints, default types, dan referensi tipe yang diimpor:
<script setup lang="ts"
generic="T extends string | number, U extends Item"
>
import type { Item } from './types';
defineProps<{
id: T;
list: U[];
}>();
</script>
More Ergonomic defineEmits
Sebelumnya, tipe parameter untuk defineEmits
hanya mendukung sintaks call signature:
// BEFORE
const emit = defineEmits<{
(e: 'foo', id: number): void;
(e: 'bar', name: string, ...rest: any[]): void;
}>();
Ini terkesan agak bertele-tele dan canggung untuk ditulis. Di Vue 3.3 sekarang kita bisa menulisnya dengan cara yang lebih ergonomis dan simple:
// AFTER
const emit = defineEmits<{
foo: [id: number];
bar: [name: string, ...rest: any[]];
}>();
Key adalah nama event dan Value-nya adalah array yang menentukan parameter/payload event tersebut.
Sintaks lama masih tetap didukung dan sintaks baru ini tidak menggantikan sintaks lama.
Typed Slots with defineSlots
Vue 3.3 memperkenalkan macro baru yaitu defineSlots
yang digunakan untuk mendeklarasikan expected slots dan expected props slots-nya:
<script setup lang="ts">
defineSlots<{
default?: (props: { msg: string }) => any;
item?: (props: { id: number }) => any;
}>();
</script>
defineSlots()
hanya menerima types
parameter dan tidak menerima argument
runtime. Tipe parameter harus berupa type literal di mana key adalah nama slot, dan value adalah slot function. Argumen pertama dari slot function adalah props
yang diharapkan diterima oleh slot, dan types
-nya akan digunakan untuk props slot di template
. Return value dari defineSlots
adalah objek slots yang sama yang dikembalikan dari useSlots
.
Ada beberapa batasan pada defineSlots
untuk saat ini:
- Type checking belum diimplementasikan di Volar / vue-tsc.
- Return type dari slot function saat ini diabaikan dan dapat
any
Ada juga opsi slots
yang sesuai untuk penggunaan defineComponent
. Kedua API tidak memiliki implikasi runtime dan hanya berfungsi sebagai type hints untuk IDE dan vue-tsc
.
Experimental Features
Vue 3.3 ini hadir dengan dua experimental features yaitu Reactive Props Destructure dan defineModel
.
Reactive Props Destructure
Fitur ini sebelumnya merupakan bagian dari Reactivity Transform yang kini telah dihapus, reactive props destructure kini telah dipecah menjadi fitur terpisah.
Fitur ini memungkinkan kita untuk melakukan destructure pada props untuk tetap mempertahankan reactivity-nya, dan juga menyediakan cara yang lebih ergonomis untuk mendeklarasikan default value pada props:
<script setup>
import { watchEffect } from 'vue';
const { msg = 'hello' } = defineProps(['msg']);
watchEffect(() => {
// accessing `msg` in watchers and computed getters
// tracks it as a dependency, just like accessing `props.msg`
console.log(`msg is: ${msg}`);
});
</script>
<template>{{ msg }}</template>
Fitur ini sekarang masih bersifat eksperimental dan jika ingin menggunakannya harus diaktifkan secara explisit. Untuk mengaktifkan fitur ini pada Vite project contohnya seperti berikut:
// vite.config.js
export default {
plugins: [
vue({
script: {
propsDestructure: true,
},
}),
],
};
defineModel
Sebelumnya, agar sebuah komponen mendukung two-way binding dengan v-model
, kita perlu (1) mendeklarasikan sebuah prop dan (2) mendeklarasikan event update:propName
yang sesuai ketika ia ingin memperbarui nilai prop:
<!-- BEFORE -->
<script setup>
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
console.log(props.modelValue);
function onInput(e) {
emit('update:modelValue', e.target.value);
}
</script>
<template>
<input :value="modelValue" @input="onInput" />
</template>
Kini Vue 3.3 ini menjadi lebih simple berkat macro baru defineModel
. Makro ini secara otomatis mendaftarkan prop, dan mengembalikan ref
yang dapat langsung dimutasi:
<!-- AFTER -->
<script setup>
const modelValue = defineModel();
console.log(modelValue.value);
</script>
<template>
<input v-model="modelValue" />
</template>
Fitur ini juga masih bersifat eksperimental dan jika ingin menggunakannya harus diaktifkan secara explisit. Untuk mengaktifkan fitur ini pada Vite project contohnya seperti berikut:
// vite.config.js
export default {
plugins: [
vue({
script: {
defineModel: true,
},
}),
],
};
Fitur lainnya
defineOptions
Makro defineOptions
baru memungkinkan mendeklarasikan component options secara langsung di <script setup>
, tanpa memerlukan blok <script>
terpisah:
<script setup>
defineOptions({ inheritAttrs: false });
</script>
Ada juga beberapa fitur lainnya seperti Better Getter Support with toRef
and toValue
dan JSX Import Source Support yang bisa kita cek disini.
Kesimpulan
Vue 3.3 membawa banyak perbaikan yang signifikan dalam penggunaan TypeScript dengan fitur SFC <script setup>
. Dukungan import types dan complex types dalam <script setup>
menjadi salah satu yang ditunggu oleh Vue developer. Dukungan TypeScript juga mendapat banyak peningkatan dengan kehadiran Generic Components, sintaks baru di defineEmits
dan juga defineSlots
. Saya pribadi sebagai TypeScript user sangat senang dengan improvement ini.
Selain itu, Vue 3.3 juga fitur eksperimental seperti Reactive Props Destructure, defineModel
, dan defineOptions
ini akan sangat membantu developer dalam menulis komponen yang jadi lebih ringkas dan ergonomis.
Rilis ini memberikan DX yang lebih baik, meningkatkan produktivitas, dan memungkinkan pengembangan aplikasi Vue yang lebih kuat dan type-safety. Meskipun beberapa fitur masih eksperimental dan memerlukan pengaktifan eksplisit, Vue 3.3 menunjukkan komitmen dalam terus meningkatkan framewerk Vue untuk memenuhi kebutuhan developer.
Sekian dan happy coding! 🥳