Vue 3'teki Yenilikler Neler?
Yayınlanan: 2022-03-10Vue 3'ün piyasaya sürülmesiyle birlikte, geliştiricilerin Vue 2'den yükseltme yapmaları gerekiyor, çünkü bu, okunması kolay ve bakımı kolay bileşenler oluşturmada süper yardımcı olan bir avuç yeni özellik ve Vue'da uygulamamızı yapılandırmanın gelişmiş yolları ile birlikte geliyor. Bu yazıda bu özelliklerden bazılarına göz atacağız.
Bu eğitimin sonunda okuyucular;
-
provide / inject
ve nasıl kullanılacağını bilin. - Teleport ve nasıl kullanılacağı hakkında temel bir anlayışa sahip olun.
- Fragmanları ve bunları nasıl kullanacağınızı öğrenin.
- Global Vue API'sinde yapılan değişiklikleri öğrenin.
- Events API'de yapılan değişiklikleri öğrenin.
Bu makale, Vue 2.x'i doğru bir şekilde anlayanlara yöneliktir. Bu örnekte kullanılan tüm kodları GitHub'da bulabilirsiniz.
provide / inject
Vue 2.x'te, bir ana bileşenden doğrudan onun alt bileşenine veri (dize, diziler, nesneler, vb.) geçirmeyi kolaylaştıran props
vardı. Ancak geliştirme sırasında, ana bileşenden, props
ile yapılması daha zor olan, derinlemesine iç içe geçmiş bir bileşene veri aktarmamız gereken durumlarla sık sık karşılaştık. Bu, Vuex Store, Event Hub kullanılmasına ve bazen derin iç içe geçmiş bileşenlerden veri geçirilmesine neden oldu. Basit bir uygulamaya bakalım;
Vue 2.2.0'ın ayrıca genel uygulama kodunda kullanılması tavsiye edilmeyen provide / inject
etme ile geldiğini belirtmek önemlidir.
# 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>
Burada, bir renk listesi içeren bir açılır menüye sahip bir açılış sayfamız var ve seçilen color
bir destek olarak childComponent.vue
. Bu alt bileşen ayrıca, şablon bölümünde görüntülenecek bir metni kabul eden bir msg
desteğine sahiptir. Son olarak, bu bileşen, bu bileşendeki metnin sınıfını belirlemede kullanılan üst bileşenden bir color
desteği kabul eden bir alt bileşene ( colorComponent.vue
) sahiptir. Bu, tüm bileşenlerden veri geçirmenin bir örneğidir.
Ancak Vue 3 ile bunu yeni Sağla ve enjekte çiftini kullanarak daha temiz ve kısa bir şekilde yapabiliriz. Adından da anlaşılacağı gibi, bir üst bileşenden iç içe geçmiş bileşenlerinden herhangi birine veri sağlamak için, böyle bir bileşen ne kadar iç içe geçmiş olursa olsun, provide
bir işlev veya nesne olarak kullanırız. Bunun gibi provide
için sabit kodlanmış değerleri geçerken nesne biçiminden yararlanırız;
# 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>
Ancak, sağlamak için bir bileşen örneği özelliği iletmeniz gereken örnekler provide
, bunun mümkün olması için işlev modunu kullanırız;
# 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>
Hem childComponent.vue
hem de colorComponent.vue
içindeki color
desteklerine ihtiyacımız olmadığından, ondan kurtuluyoruz. Sağlamayı kullanmanın iyi yanı, ana bileşenin, provide
özelliğe hangi bileşenin ihtiyacı olduğunu bilmesine gerek olmamasıdır.
Bu durumda buna ihtiyaç duyan bileşende bunu kullanmak için colorComponent.vue
şunu yapıyoruz;
# 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>
Burada, bileşenin ihtiyaç duyduğu gerekli değişkenlerin bir dizisini alan inject
kullanıyoruz. Bu durumda, sadece color
özelliğine ihtiyacımız var, bu yüzden sadece bunu geçiyoruz. Bundan sonra, sahneyi kullanırken kullandığımız color
aynı şekilde kullanabiliriz.
Açılır menüyü kullanarak yeni bir renk seçmeye çalışırsak, rengin colorComponent.vue
içinde güncellenmediğini ve bunun nedeninin, varsayılan olarak, provide
özelliklerin reaktif olmaması olduğunu fark edebiliriz. Bunu düzeltmek için computed
yöntemi kullanıyoruz.
# 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>
Burada, kullanıcı farklı bir renk seçtiğinde reaktif olabilmesi ve güncellenebilmesi için computed
içe aktarır ve selectedColor
Renk'i iletiriz. Hesaplanan yönteme bir değişken ilettiğinizde, value
olan bir nesne döndürür. Bu özellik, değişkeninizin değerini tutar, bu nedenle bu örnek için colorComponent.vue
görünecek şekilde güncellememiz gerekir;
# 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>
Burada, computed
yöntemi kullanarak color
reaktif hale getirdikten sonraki değişikliği temsil etmek için color
color.value
olarak değiştiriyoruz. Bu noktada, üst bileşende selectedColor
değiştiğinde, bu bileşendeki metnin class
her zaman değişir.
Işınlanma
Uygulamanın kullandığı, ancak uygulamamızın başka bir bölümünde görüntülenmesi amaçlanan mantık nedeniyle bileşenler oluşturup uygulamamızın bir bölümüne yerleştirdiğimiz durumlar vardır. Bunun yaygın bir örneği, tüm ekranı görüntülemesi ve kaplaması amaçlanan bir mod veya açılır pencere olabilir. Bu tür elemanlar üzerinde CSS'nin position
özelliğini kullanarak bunun için bir geçici çözüm oluşturabilirken, Vue 3 ile Teleport kullanarak da yapabiliriz.
Teleport, Vue uygulamalarının sarıldığı varsayılan #app
kapsayıcısından bir bileşeni bir belgedeki orijinal konumundan çıkarmamıza ve onu, kullanıldığı sayfadaki mevcut herhangi bir öğeye taşımamıza olanak tanır. Bir başlık bileşenini #app
div'in içinden bir header
taşımak için Teleport kullanmak buna iyi bir örnek olabilir. Yalnızca Vue DOM'nin dışında var olan öğelere Işınlanabileceğinizi unutmamak önemlidir.


Teleport bileşeni, bu bileşenin davranışını belirleyen iki prop'u kabul eder ve bunlar;
-
to
Bu destek, bir sınıf adını, bir kimliği, bir öğeyi veya bir data-* niteliğini kabul eder. Teleport öğesini dinamik olarak değiştirmek yerine bir:to
to
ileterek de bu değeri dinamik hale getirebiliriz. -
:disabled
Bu destek, birBoolean
kabul eder ve bir öğe veya bileşende Işınlanma özelliğini değiştirmek için kullanılabilir. Bu, bir elemanın konumunu dinamik olarak değiştirmek için faydalı olabilir.
Teleport kullanmanın ideal bir örneği şuna benzer;
# 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>
Vue uygulamanızdaki varsayılan index.html
dosyasına, başlık bileşenimizi uygulamamızdaki o noktaya Işınlamak istediğimiz için bir header
öğesi ekleriz. Ayrıca, Teleport bileşenimizde stil ve kolay referans için bu öğeye bir sınıf ekledik.
# 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>
Burada başlık bileşenini oluşturuyoruz ve uygulamamızdaki ana sayfaya bağlantı içeren bir logo ekliyoruz. Ayrıca Teleport bileşenini ekliyoruz ve to
prop öğesine bir header
değeri veriyoruz çünkü bu bileşenin bu öğenin içinde işlenmesini istiyoruz. Son olarak, bu bileşeni uygulamamıza aktarıyoruz;
# App.vue <template> <router-view /> <app-header></app-header> </template> <script> import appHeader from "@/components/Header.vue"; export default { components: { appHeader, }, }; </script>
Bu dosyada, başlık bileşenini içe aktarıyoruz ve uygulamamızda görünebilmesi için şablona yerleştiriyoruz.
Şimdi, uygulamamızın öğesini incelersek, başlık bileşenimizin header
öğesinin içinde olduğunu fark ederiz;

Parça
Vue 2.x ile, dosyanızın template
birden fazla kök öğeye sahip olmak imkansızdı ve geçici bir çözüm olarak geliştiriciler tüm öğeleri bir üst öğeye sarmaya başladılar. Bu ciddi bir sorun gibi görünmese de, geliştiricilerin bu tür öğeleri kapsayıcı olmadan bir bileşen oluşturmak istedikleri ancak bununla yetinmek zorunda oldukları durumlar vardır.
Vue 3 ile Fragments adlı yeni bir özellik tanıtıldı ve bu özellik geliştiricilerin kök şablon dosyalarında birden çok öğeye sahip olmalarını sağlıyor. Yani Vue 2.x ile bir girdi alanı kapsayıcı bileşeni şöyle görünür;
# 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>
Burada, label
ve type
olmak üzere iki props kabul eden basit bir form elemanı bileşenimiz var ve bu bileşenin şablon bölümü bir div ile sarılmış. Bu mutlaka bir sorun değildir, ancak etiket ve giriş alanının doğrudan form
öğenizin içinde olmasını istiyorsanız. Vue 3 ile geliştiriciler bu bileşeni şöyle görünecek şekilde kolayca yeniden yazabilirler;
# inputComponent.vue <template class="testingss"> <label :for="label">{{ label }}</label> <input :type="type" : :name="label" /> </template>
Tek bir kök düğümde, öznitelikler her zaman kök düğüme atfedilir ve bunlar ayrıca Prop Olmayan Öznitelikler olarak da bilinir . emits
props
tanımlanan karşılık gelen özelliklere sahip olmayan bir bileşene geçirilen olaylar veya niteliklerdir. Bu tür özniteliklere örnek olarak class
ve id
verilebilir. Bununla birlikte, çok köklü bir düğüm bileşenindeki öğelerden hangisine atfedileceğini açıkça tanımlamak gerekir.
Yukarıdan inputComponent.vue
kullanmanın anlamı şudur;
- Ana bileşende bu bileşene
class
eklerken, buclass
hangi bileşene atfedileceği belirtilmelidir, aksi takdirde özniteliğin hiçbir etkisi olmaz.
<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>
Niteliklerin nereye atfedileceğini tanımlamadan böyle bir şey yaptığınızda konsolunuzda şu uyarıyı alırsınız;

Ve border
bileşen üzerinde hiçbir etkisi yoktur;

- Bunu düzeltmek için, bu tür niteliklerin dağıtılmasını istediğiniz öğeye bir
v-bind="$attrs"
ekleyin;
<template> <label :for="label" v-bind="$attrs">{{ label }}</label> <input :type="type" : :name="label" /> </template>
Burada, Vue'ya niteliklerin label
öğesine dağıtılmasını istediğimizi söylüyoruz, bu da buna awesome__class
öğesinin uygulanmasını istediğimiz anlamına geliyor. Şimdi, öğemizi tarayıcıda incelersek, sınıfın şimdi label
eklendiğini ve dolayısıyla artık etiketin etrafında bir kenarlık olduğunu görürüz.

genel API
Bir Vue uygulamasının main.js
dosyasında Vue.component
veya Vue.use
görmek alışılmadık bir durum değildi. Bu tür yöntemler, Global API'ler olarak bilinir ve bunlardan Vue 2.x'te oldukça fazla sayıda bulunur. Bu yöntemin zorluklarından biri, diğer uygulamaları etkilemeden uygulamanızın bir örneğine (uygulamanızda birden fazla örneğin varsa) belirli işlevleri yalıtmayı imkansız hale getirmesidir, çünkü bunların tümü Vue'ya monte edilmiştir. Demek istediğim bu;
Vue.directive('focus', { inserted: el => el.focus() }) Vue.mixin({ /* ... */ }) const app1 = new Vue({ el: '#app-1' }) const app2 = new Vue({ el: '#app-2' })
Yukarıdaki kod için, Vue Yönergesinin app1
ve app2
ile ilişkilendirildiğini belirtmek imkansızdır, ancak bunun yerine her ikisi de iki uygulamada mevcuttur.
Vue 3, createApp
tanıtılmasıyla bu tür bir sorunu çözme girişiminde yeni bir Global API ile birlikte gelir. Bu yöntem, bir Vue uygulamasının yeni bir örneğini döndürür. Bir uygulama örneği, geçerli global API'lerin bir alt kümesini ortaya çıkarır. Bununla, Vue'yu Vue
2.x'ten mutasyona uğratan tüm API'ler (bileşen, karıştırma, yönerge, kullanım vb.) artık bireysel uygulama örneklerine taşınacak ve şimdi, Vue uygulamanızın her bir örneği benzersiz işlevlere sahip olabilir. diğer mevcut uygulamaları etkilemeden bunları.
Şimdi, yukarıdaki kod şu şekilde yeniden yazılabilir;
const app1 = createApp({}) const app2 = createApp({}) app1.directive('focus', { inserted: el => el.focus() }) app2.mixin({ /* ... */ })
Bununla birlikte, tüm uygulamalarınız arasında paylaşmak istediğiniz işlevleri oluşturmak mümkündür ve bu, bir fabrika işlevi kullanılarak yapılabilir.
Etkinlikler API'sı
Geliştiricilerin, Vuex Store'u kullanmak dışında bir ebeveyn-çocuk ilişkisi olmayan bileşenler arasında veri iletmek için benimsediği en yaygın yollardan biri, Event Bus'ın kullanılmasıdır. Bu yöntemin yaygın olmasının nedenlerinden biri, onunla başlamanın ne kadar kolay olmasıdır;
# eventBus.js const eventBus = new Vue() export default eventBus;
Bundan sonra, uygulamamızda global olarak kullanılabilir hale getirmek için bu dosyayı main.js
içe aktarmak veya ihtiyacınız olan dosyalara aktarmak olacaktır;
# main.js import eventBus from 'eventBus' Vue.prototype.$eventBus = eventBus
Artık olayları yayınlayabilir ve bunun gibi yayılan olayları dinleyebilirsiniz;
this.$eventBus.$on('say-hello', alertMe) this.$eventBus.$emit('pass-message', 'Event Bus says Hi')
Bunun gibi kodlarla dolu çok sayıda Vue kod tabanı var. Bununla birlikte, Vue 3 ile bunu yapmak imkansız olurdu çünkü $on
, $off
ve $once
tümü kaldırılmış, ancak $emit
hala kullanılabilir çünkü alt bileşenlerin kendi ana bileşenlerine olay yayması gerekiyor. Buna bir alternatif, provide / inject
etme veya önerilen üçüncü taraf kitaplıklarından herhangi birini kullanmak olabilir.
Çözüm
Bu makalede, provide / inject
etme çiftini kullanarak bir üst bileşenden derinlemesine yuvalanmış bir alt bileşene nasıl veri aktarabileceğinizi ele aldık. Bileşenleri uygulamamızdaki bir noktadan diğerine nasıl yeniden konumlandırabileceğimizi ve aktarabileceğimizi de inceledik. İncelediğimiz diğer bir şey de çok köklü düğüm bileşeni ve niteliklerin düzgün çalışması için dağıttığımızdan nasıl emin olacağımızdır. Son olarak, Events API ve Global API'deki değişiklikleri de ele aldık.
Diğer Kaynaklar
- “ES6+ ile JavaScript Fabrika İşlevleri,” Eric Elliott, Medium
- Kingsley Silas, CSS-Tricks, "Vue Bileşenleri Arasında Donanımları Paylaşmak için Event Bus'ı Kullanma"
- Aynı Hedefte Birden Fazla Teleport Kullanma, Vue.js Docs
- Prop Dışı Nitelikler, Vue.js Belgeleri
- Reaktivite ile Çalışma, Vue.js Docs
-
teleport
, Vue.js Belgeleri - Parçalar, Vue.js Belgeleri
- 2.x Sözdizimi, Vue.js Belgeleri