Apa yang Baru di Vue 3?
Diterbitkan: 2022-03-10Dengan 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;
- Tahu tentang
provide / inject
dan cara menggunakannya. - Memiliki pemahaman dasar tentang Teleport dan cara menggunakannya.
- Ketahui tentang Fragmen dan cara menggunakannya.
- Ketahui tentang perubahan yang dibuat pada Global Vue API.
- 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.
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.
Komponen Teleport menerima dua alat peraga yang menentukan perilaku komponen ini dan mereka adalah;
-
to
Prop ini menerima nama kelas, id, elemen, atau atribut data-*. Kita juga dapat membuat nilai ini dinamis dengan meneruskan:to
prop sebagaito
dan mengubah elemen Teleport secara dinamis. -
:disabled
Prop ini menerimaBoolean
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
;
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;
- Saat menambahkan
class
ke komponen ini di komponen induk, harus ditentukan komponen mana yang akan dikaitkan denganclass
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;
Dan border
tidak berpengaruh pada komponen;
- 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.
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