Vue 3'teki Yenilikler Neler?

Yayınlanan: 2022-03-10
Kısa özet ↬ Vue 3, çerçeve ile geliştirmeyi çok daha kolay ve sürdürülebilir hale getirmeyi amaçlayan birçok ilginç yeni özellik ve mevcut olanlardan bazılarında değişikliklerle birlikte gelir. Bu yazıda, bu yeni özelliklerden bazılarına ve bunları nasıl kullanmaya başlayacağınıza bir göz atacağız. Ayrıca mevcut özelliklerde yapılan bazı değişikliklere de göz atacağız.

Vue 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;

  1. provide / inject ve nasıl kullanılacağını bilin.
  2. Teleport ve nasıl kullanılacağı hakkında temel bir anlayışa sahip olun.
  3. Fragmanları ve bunları nasıl kullanacağınızı öğrenin.
  4. Global Vue API'sinde yapılan değişiklikleri öğrenin.
  5. 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.

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

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.

geçersiz bir öğeye ışınlandığınızda konsolda hata mesajı
Konsolda Işınlanma hata mesajı: Terminalde geçersiz Işınlanma hedefi hata mesajı. (Büyük önizleme)

Teleport bileşeni, bu bileşenin davranışını belirleyen iki prop'u kabul eder ve bunlar;

  1. 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.
  2. :disabled
    Bu destek, bir Boolean 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;

DevTools'ta başlık bileşeni
DevTools'ta başlık bileşeni (Büyük önizleme)

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;

  1. Ana bileşende bu bileşene class eklerken, bu class 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;

nitelikler dağıtılmadığında terminalde hata mesajı
Nitelikler dağıtılmadığında terminalde hata mesajı (Büyük önizleme)

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

nitelik dağılımı olmayan bileşen
Nitelik dağıtımı olmayan bileşen (Geniş önizleme)
  1. 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.

nitelik dağılımına sahip bileşen
Öznitelik dağılımına sahip bileşen (Geniş önizleme)

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