Apa yang Baru di Vue 3?

Diterbitkan: 2022-03-10
Ringkasan cepat Vue 3 hadir dengan banyak fitur baru yang menarik dan perubahan pada beberapa fitur yang sudah ada yang ditujukan untuk membuat pengembangan dengan framework menjadi lebih mudah dan dapat dipelihara. Dalam artikel ini, kita akan melihat beberapa fitur baru ini dan cara memulainya. Kami juga akan melihat beberapa perubahan yang dilakukan pada fitur yang ada.

Dengan dirilisnya Vue 3, pengembang harus melakukan upgrade dari Vue 2 karena dilengkapi dengan beberapa fitur baru yang sangat membantu dalam membangun komponen yang mudah dibaca dan dipelihara serta cara yang lebih baik untuk menyusun aplikasi kita di Vue. Kami akan melihat beberapa fitur ini di artikel ini.

Di akhir tutorial ini, pembaca akan;

  1. Tahu tentang provide / inject dan cara menggunakannya.
  2. Memiliki pemahaman dasar tentang Teleport dan cara menggunakannya.
  3. Ketahui tentang Fragmen dan cara menggunakannya.
  4. Ketahui tentang perubahan yang dibuat pada Global Vue API.
  5. Ketahui tentang perubahan yang dibuat pada Events API.

Artikel ini ditujukan untuk mereka yang memiliki pemahaman yang tepat tentang Vue 2.x. Anda dapat menemukan semua kode yang digunakan dalam contoh ini di GitHub.

provide / inject

Di Vue 2.x, kami memiliki props yang memudahkan untuk melewatkan data (string, array, objek, dll) dari komponen induk langsung ke komponen turunannya. Tetapi selama pengembangan, kami sering menemukan contoh di mana kami perlu meneruskan data dari komponen induk ke komponen yang sangat bersarang yang lebih sulit dilakukan dengan props . Hal ini mengakibatkan penggunaan Vuex Store, Event Hub, dan terkadang melewatkan data melalui komponen yang sangat bersarang. Mari kita lihat aplikasi sederhana;

Penting untuk dicatat bahwa Vue 2.2.0 juga datang dengan provide / inject yang tidak direkomendasikan untuk digunakan dalam kode aplikasi generik.

 # parentComponent.vue <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png" /> <HelloWorld msg="Vue 3 is liveeeee!" :color="color" /> <select name="color" v-model="color"> <option value="" disabled selected> Select a color</option> <option :value="color" v-for="(color, index) in colors" :key="index">{{ color }}</option></select > </div> </template> <script> import HelloWorld from "@/components/HelloWorld.vue"; export default { name: "Home", components: { HelloWorld, }, data() { return { color: "", colors: ["red", "blue", "green"], }; }, }; </script>
 # childComponent.vue <template> <div class="hello"> <h1>{{ msg }}</h1> <color-selector :color="color"></color-selector> </div> </template> <script> import colorSelector from "@/components/colorComponent.vue"; export default { name: "HelloWorld", components: { colorSelector, }, props: { msg: String, color: String, }, }; </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style scoped> h3 { margin: 40px 0 0; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>
 # colorComponent.vue <template> <p :class="[color]">This is an example of deeply nested props!</p> </template> <script> export default { props: { color: String, }, }; </script> <style> .blue { color: blue; } .red { color: red; } .green { color: green; } </style>

Di sini, kami memiliki halaman arahan dengan dropdown yang berisi daftar warna dan kami meneruskan color yang dipilih ke childComponent.vue sebagai prop. Komponen anak ini juga memiliki prop msg yang menerima teks untuk ditampilkan di bagian template. Terakhir, komponen ini memiliki komponen anak ( colorComponent.vue ) yang menerima prop color dari komponen induk yang digunakan dalam menentukan kelas untuk teks dalam komponen ini. Ini adalah contoh melewatkan data melalui semua komponen.

Tetapi dengan Vue 3, kita dapat melakukan ini dengan cara yang lebih bersih dan singkat dengan menggunakan pasangan Provide dan inject yang baru. Seperti namanya, kami menggunakan provide baik sebagai fungsi atau objek untuk membuat data tersedia dari komponen induk ke salah satu komponen bersarangnya terlepas dari seberapa dalam komponen tersebut bersarang. Kami menggunakan bentuk objek saat meneruskan nilai hard-code untuk provide seperti ini;

 # parentComponent.vue <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png" /> <HelloWorld msg="Vue 3 is liveeeee!" :color="color" /> <select name="color" v-model="color"> <option value="" disabled selected> Select a color</option> <option :value="color" v-for="(color, index) in colors" :key="index">{{ color }}</option></select > </div> </template> <script> import HelloWorld from "@/components/HelloWorld.vue"; export default { name: "Home", components: { HelloWorld, }, data() { return { colors: ["red", "blue", "green"], }; }, provide: { color: 'blue' } }; </script>

Tetapi untuk contoh di mana Anda harus melewati properti instance komponen untuk provide , kami menggunakan mode fungsi jadi ini mungkin;

 # parentComponent.vue <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png" /> <HelloWorld msg="Vue 3 is liveeeee!" /> <select name="color" v-model="selectedColor"> <option value="" disabled selected> Select a color</option> <option :value="color" v-for="(color, index) in colors" :key="index">{{ color }}</option></select > </div> </template> <script> import HelloWorld from "@/components/HelloWorld.vue"; export default { name: "Home", components: { HelloWorld, }, data() { return { selectedColor: "blue", colors: ["red", "blue", "green"], }; }, provide() { return { color: this.selectedColor, }; }, }; </script>

Karena kita tidak memerlukan props color baik di childComponent.vue dan colorComponent.vue , kita menyingkirkannya. Hal yang baik tentang menggunakan provide adalah bahwa komponen induk tidak perlu mengetahui komponen mana yang membutuhkan properti yang disediakannya.

Untuk memanfaatkan ini dalam komponen yang membutuhkannya dalam kasus ini, colorComponent.vue kami melakukan ini;

 # colorComponent.vue <template> <p :class="[color]">This is an example of deeply nested props!</p> </template> <script> export default { inject: ["color"], }; </script> <style> .blue { color: blue; } .red { color: red; } .green { color: green; } </style>

Di sini, kami menggunakan inject yang mengambil array variabel yang diperlukan yang dibutuhkan komponen. Dalam hal ini, kita hanya membutuhkan properti color jadi kita hanya meneruskannya. Setelah itu, kita bisa menggunakan color dengan cara yang sama seperti kita menggunakannya saat menggunakan alat peraga.

Kami mungkin memperhatikan bahwa jika kami mencoba untuk memilih warna baru menggunakan dropdown, warna tidak diperbarui di colorComponent.vue dan ini karena secara default properti yang provide tidak reaktif. Untuk memperbaikinya, kami menggunakan metode computed .

 # parentComponent.vue <template> <div class="home"> <img alt="Vue logo" src="../assets/logo.png" /> <HelloWorld msg="Vue 3 is liveeeee!" /> <select name="color" v-model="selectedColor"> <option value="" disabled selected> Select a color</option> <option :value="color" v-for="(color, index) in colors" :key="index">{{ color }}</option></select > </div> </template> <script> import HelloWorld from "@/components/HelloWorld.vue"; import { computed } from "vue"; export default { name: "Home", components: { HelloWorld, }, data() { return { selectedColor: "", todos: ["Feed a cat", "Buy tickets"], colors: ["red", "blue", "green"], }; }, provide() { return { color: computed(() => this.selectedColor), }; }, }; </script>

Di sini, kami mengimpor yang computed dan meneruskan Warna yang selectedColor sehingga dapat reaktif dan diperbarui saat pengguna memilih warna yang berbeda. Ketika Anda meneruskan variabel ke metode yang dihitung, ia mengembalikan objek yang memiliki value . Properti ini menyimpan nilai variabel Anda jadi untuk contoh ini, kita harus memperbarui colorComponent.vue agar terlihat seperti ini;

 # colorComponent.vue <template> <p :class="[color.value]">This is an example of deeply nested props!</p> </template> <script> export default { inject: ["color"], }; </script> <style> .blue { color: blue; } .red { color: red; } .green { color: green; } </style>

Di sini, kami mengubah color menjadi color.value untuk mewakili perubahan setelah membuat color reaktif menggunakan metode computed . Pada titik ini, class teks dalam komponen ini akan selalu berubah setiap kali warna yang selectedColor berubah di komponen induk.

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Teleportasi

Ada contoh di mana kita membuat komponen dan menempatkannya di satu bagian dari aplikasi kita karena logika yang digunakan aplikasi tetapi dimaksudkan untuk ditampilkan di bagian lain dari aplikasi kita. Contoh umum dari ini adalah modal atau popup yang dimaksudkan untuk menampilkan dan menutupi seluruh layar. Meskipun kita dapat membuat solusi untuk ini menggunakan properti position CSS pada elemen tersebut, dengan Vue 3, kita juga dapat melakukannya menggunakan Teleport.

Teleport memungkinkan kita untuk mengambil komponen dari posisi aslinya dalam dokumen, dari default #app container aplikasi Vue dibungkus dan memindahkannya ke elemen yang ada di halaman yang sedang digunakan. Contoh yang baik adalah menggunakan Teleport untuk memindahkan komponen header dari dalam #app div ke header . Penting untuk dicatat bahwa Anda hanya dapat melakukan Teleport ke elemen yang ada di luar Vue DOM.

pesan kesalahan di konsol saat Anda berteleportasi ke elemen yang tidak valid
Pesan kesalahan teleportasi di konsol: Pesan kesalahan target Teleportasi tidak valid di terminal. (Pratinjau besar)

Komponen Teleport menerima dua alat peraga yang menentukan perilaku komponen ini dan mereka adalah;

  1. to
    Prop ini menerima nama kelas, id, elemen, atau atribut data-*. Kita juga dapat membuat nilai ini dinamis dengan meneruskan :to prop sebagai to dan mengubah elemen Teleport secara dinamis.
  2. :disabled
    Prop ini menerima Boolean dan dapat digunakan untuk mengaktifkan fitur Teleport pada elemen atau komponen. Ini dapat berguna untuk mengubah posisi elemen secara dinamis.

Contoh ideal menggunakan Teleport terlihat seperti ini;

 # index.html** <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width,initial-scale=1.0" /> <link rel="icon" href="<%= BASE_URL %>favicon.ico" /> <title> <%= htmlWebpackPlugin.options.title %> </title> </head> <!-- add container to teleport to --> <header class="header"></header> <body> <noscript> <strong >We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong > </noscript> <div></div> <!-- built files will be auto injected --> </body> </html>

Dalam file index.html default di aplikasi Vue Anda, kami menambahkan elemen header karena kami ingin memindahkan komponen header kami ke titik itu di aplikasi kami. Kami juga menambahkan kelas ke elemen ini untuk gaya dan referensi mudah di komponen Teleport kami.

 # Header.vue** <template> <teleport to="header"> <h1 class="logo">Vue 3 </h1> <nav> <router-link to="/">Home</router-link> </nav> </teleport> </template> <script> export default { name: "app-header", }; </script> <style> .header { display: flex; align-items: center; justify-content: center; } .logo { margin-right: 20px; } </style>

Di sini, kami membuat komponen header dan menambahkan logo dengan tautan ke beranda di aplikasi kami. Kami juga menambahkan komponen Teleport dan memberikan to prop nilai header karena kami ingin komponen ini dirender di dalam elemen ini. Terakhir, kami mengimpor komponen ini ke dalam aplikasi kami;

 # App.vue <template> <router-view /> <app-header></app-header> </template> <script> import appHeader from "@/components/Header.vue"; export default { components: { appHeader, }, }; </script>

Dalam file ini, kami mengimpor komponen header dan menempatkannya di template sehingga dapat terlihat di aplikasi kami.

Sekarang jika kita memeriksa elemen aplikasi kita, kita akan melihat bahwa komponen header kita ada di dalam elemen header ;

Komponen header di DevTools
Komponen header di DevTools (Pratinjau besar)

Fragmen

Dengan Vue 2.x, tidak mungkin memiliki beberapa elemen root dalam template file Anda dan sebagai solusinya, pengembang mulai membungkus semua elemen dalam elemen induk. Meskipun ini tidak terlihat seperti masalah serius, ada beberapa contoh di mana pengembang ingin merender komponen tanpa wadah yang membungkus elemen tersebut tetapi harus puas dengan itu.

Dengan Vue 3, fitur baru yang disebut Fragmen diperkenalkan dan fitur ini memungkinkan pengembang untuk memiliki banyak elemen dalam file template root mereka. Jadi dengan Vue 2.x, seperti inilah tampilan komponen wadah bidang masukan;

 # inputComponent.vue <template> <div> <label :for="label">label</label> <input :type="type" : :name="label" /> </div> </template> <script> export default { name: "inputField", props: { label: { type: String, required: true, }, type: { type: String, required: true, }, }, }; </script> <style></style>

Di sini, kita memiliki komponen elemen formulir sederhana yang menerima dua props, label dan type , dan bagian template dari komponen ini dibungkus dalam div. Ini tidak selalu menjadi masalah tetapi jika Anda ingin label dan bidang input berada langsung di dalam elemen form Anda. Dengan Vue 3, pengembang dapat dengan mudah menulis ulang komponen ini agar terlihat seperti ini;

 # inputComponent.vue <template class="testingss"> <label :for="label">{{ label }}</label> <input :type="type" : :name="label" /> </template>

Dengan simpul akar tunggal, atribut selalu dikaitkan ke simpul akar dan mereka juga dikenal sebagai Atribut Non-Prop . Mereka adalah peristiwa atau atribut yang diteruskan ke komponen yang tidak memiliki properti terkait yang ditentukan dalam props atau emits . Contoh atribut tersebut adalah class dan id . Namun, diperlukan untuk secara eksplisit menentukan elemen mana dalam komponen simpul multi-root yang harus dikaitkan.

Inilah artinya menggunakan inputComponent.vue dari atas;

  1. Saat menambahkan class ke komponen ini di komponen induk, harus ditentukan komponen mana yang akan dikaitkan dengan class ini, jika tidak, atribut tidak akan berpengaruh.
 <template> <div class="home"> <div> <input-component class="awesome__class" label="name" type="text" ></input-component> </div> </div> </template> <style> .awesome__class { border: 1px solid red; } </style>

Saat Anda melakukan sesuatu seperti ini tanpa menentukan ke mana atribut harus dikaitkan, Anda mendapatkan peringatan ini di konsol Anda;

pesan kesalahan di terminal ketika atribut tidak didistribusikan
Pesan kesalahan di terminal saat atribut tidak didistribusikan (Pratinjau besar)

Dan border tidak berpengaruh pada komponen;

komponen tanpa distribusi atribut
Komponen tanpa distribusi atribut (Pratinjau besar)
  1. Untuk memperbaikinya, tambahkan v-bind="$attrs" pada elemen tempat Anda ingin atribut tersebut didistribusikan;
 <template> <label :for="label" v-bind="$attrs">{{ label }}</label> <input :type="type" : :name="label" /> </template>

Di sini, kami memberi tahu Vue bahwa kami ingin atribut didistribusikan ke elemen label yang berarti kami ingin kelas awesome__class diterapkan padanya. Sekarang, jika kita memeriksa elemen kita di browser, kita akan melihat bahwa kelas sekarang telah ditambahkan ke label dan karenanya batas sekarang ada di sekitar label.

komponen dengan distribusi atribut
Komponen dengan distribusi atribut (Pratinjau besar)

API Global

Tidak jarang melihat Vue.component atau Vue.use dalam file main.js dari aplikasi Vue. Jenis metode ini dikenal adalah API Global dan ada cukup banyak di Vue 2.x. Salah satu tantangan dari metode ini adalah tidak memungkinkan untuk mengisolasi fungsionalitas tertentu ke satu instance aplikasi Anda (jika Anda memiliki lebih dari satu instance di aplikasi Anda) tanpa memengaruhi aplikasi lain karena semuanya terpasang di Vue. Inilah yang saya maksud;

 Vue.directive('focus', { inserted: el => el.focus() }) Vue.mixin({ /* ... */ }) const app1 = new Vue({ el: '#app-1' }) const app2 = new Vue({ el: '#app-2' })

Untuk kode di atas, tidak mungkin untuk menyatakan bahwa Vue Directive dikaitkan dengan app1 dan Mixin dengan app2 tetapi sebaliknya, keduanya tersedia di dua aplikasi.

Vue 3 hadir dengan API Global baru dalam upaya untuk memperbaiki jenis masalah ini dengan pengenalan createApp . Metode ini mengembalikan instance baru dari aplikasi Vue. Instance aplikasi memperlihatkan subset dari API global saat ini. Dengan ini, semua API (komponen, mixin, directive, use, dll) yang mengubah Vue dari Vue 2.x sekarang akan dipindahkan ke masing-masing instance aplikasi dan sekarang, setiap instance aplikasi Vue Anda dapat memiliki fungsionalitas yang unik untuk mereka tanpa mempengaruhi aplikasi lain yang ada.

Sekarang, kode di atas dapat ditulis ulang sebagai;

 const app1 = createApp({}) const app2 = createApp({}) app1.directive('focus', { inserted: el => el.focus() }) app2.mixin({ /* ... */ })

Namun dimungkinkan untuk membuat fungsionalitas yang ingin Anda bagikan di antara semua aplikasi Anda dan ini dapat dilakukan dengan menggunakan fungsi pabrik.

API Acara

Salah satu cara paling umum yang diadopsi pengembang untuk meneruskan data di antara komponen yang tidak memiliki hubungan induk ke anak selain menggunakan Vuex Store adalah penggunaan Bus Acara. Salah satu alasan mengapa metode ini umum adalah karena betapa mudahnya memulainya;

 # eventBus.js const eventBus = new Vue() export default eventBus;

Setelah ini, hal berikutnya adalah mengimpor file ini ke main.js agar tersedia secara global di aplikasi kita atau mengimpornya ke file yang Anda perlukan;

 # main.js import eventBus from 'eventBus' Vue.prototype.$eventBus = eventBus

Sekarang, Anda dapat memancarkan peristiwa dan mendengarkan peristiwa yang dipancarkan seperti ini;

 this.$eventBus.$on('say-hello', alertMe) this.$eventBus.$emit('pass-message', 'Event Bus says Hi')

Ada banyak sekali basis kode Vue yang diisi dengan kode seperti ini. Namun, dengan Vue 3, itu tidak mungkin dilakukan karena $on , $off , dan $once semuanya telah dihapus tetapi $emit masih tersedia karena komponen anak-anak diperlukan untuk memancarkan peristiwa ke komponen induknya. Alternatif untuk ini akan menggunakan provide / inject atau salah satu perpustakaan pihak ketiga yang direkomendasikan.

Kesimpulan

Dalam artikel ini, kami telah membahas bagaimana Anda dapat meneruskan data dari komponen induk ke komponen turunan yang sangat bersarang menggunakan pasangan provide / inject . Kami juga telah melihat bagaimana kami dapat memposisikan ulang dan mentransfer komponen dari satu titik di aplikasi kami ke yang lain. Hal lain yang kami lihat adalah komponen node multi-root dan bagaimana memastikan kami mendistribusikan atribut agar berfungsi dengan baik. Terakhir, kami juga membahas perubahan pada Events API dan Global API.

Sumber Daya Lebih Lanjut

  • “Fungsi Pabrik JavaScript dengan ES6+,” Eric Elliott, Medium
  • “Menggunakan Bus Acara untuk Berbagi Alat Peraga Antar Komponen Vue,” Kingsley Silas, CSS-Tricks
  • Menggunakan Beberapa Teleportasi Pada Target yang Sama, Vue.js Docs
  • Atribut Non-Prop, Dokumen Vue.js
  • Bekerja Dengan Reaktivitas, Vue.js Documents
  • teleport , Dokumen Vue.js
  • Fragmen, Dokumen Vue.js
  • 2.x Sintaks, Dokumen Vue.js