Önceki yazımda üzerine inşa edeceğimiz temelleri atmıştım; şimdi "gerçekten" başlamanın zamanı.
Vue.js hakkında çok şey duydum. Ayrıca, geliştiriciden yöneticiliğe geçen bir arkadaşım bana Vue hakkında güzel şeyler söyledi, bu da ilgimi daha da çekti. Bir göz atmaya karar verdim: Bu, bir acemi olarak, ki ben de öyleyim, inceleyeceğim ilk "hafif" JavaScript framework'ü olacak.
WebJars ve Thymeleaf'ı son yazımda açıklamıştım. İşte kurulum, sunucu ve istemci tarafı.
İşte ikisini de POM'a nasıl entegre ettiğim:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!--1--> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <!--2--> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator</artifactId> <!--3--> <version>0.52</version> </dependency> <dependency> <groupId>org.webjars.npm</groupId> <artifactId>vue</artifactId> <!--4--> <version>3.4.34</version> </dependency> </dependencies>
Spring Boot tarafında Kotlin Router ve Bean DSL'leri kullanıyorum:
fun vue(todos: List<Todo>) = router { //1 GET("/vue") { ok().render("vue", mapOf("title" to "Vue.js", "todos" to todos)) //2-3 } }
Todo
nesneleri listesi geçirin
API geliştirmeye alışkınsanız, body()
fonksiyonuna aşinasınızdır; bu fonksiyon, yükü doğrudan, muhtemelen JSON formatında döndürür. render()
akışı görünüm teknolojisine, bu durumda Thymeleaf'e iletir. İki parametre kabul eder:
/templates
ve önek .html
; bu durumda, Thymeleaf /templates/vue.html
bir görünüm beklerİşte HTML tarafındaki kod:
<script th:src="@{/webjars/axios/dist/axios.js}" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/axios.min.js"></script> <!--1--> <script th:src="@{/webjars/vue/dist/vue.global.js}" src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.js"></script> <!--2--> <script th:src="@{/vue.js}" src="../static/vue.js"></script> <!--3--> <script th:inline="javascript"> /*<![CDATA[*/ window.vueData = { <!--4--> title: /*[[${ title }]]*/ 'A Title', todos: /*[[${ todos }]]*/ [{ 'id': 1, 'label': 'Take out the trash', 'completed': false }] }; /*]]>*/ </script>
Geçtiğimiz haftaki makalede açıklandığı gibi, Thymeleaf'in avantajlarından biri hem statik dosya işleme hem de sunucu tarafı işleme izin vermesidir. Sihrin işe yaraması için, bir istemci tarafı yolu, yani src
ve bir sunucu tarafı yolu, yani th:src
belirtiyorum.
Şimdi Vue kodlarına dalalım.
Birkaç özelliği hayata geçirmek istiyoruz:
Todo
öğelerini görüntülemelidirTodo
tamamlandı onay kutusuna tıklandığında, completed
niteliği ayarlanmalı/ayarlanmamalıdırTodo
silinirTodo
listesine aşağıdaki değerlere sahip bir Todo
eklenmelidir:id
: Sunucu tarafında hesaplanan, diğer tüm ID'lerin maksimum değeri artı 1label
: label
için Etiket alanının değericompleted
: false
olarak ayarlandı İlk adım framework'ü başlatmaktır. Yukarıda özel vue.js
dosyamız için referansı zaten ayarladık.
document.addEventListener('DOMContentLoaded', () => { //1 // The next JavaScript code snippets will be inside the block }
Bir sonraki adım Vue'nun sayfanın bir kısmını yönetmesine izin vermektir. HTML tarafında, Vue'nun hangi üst düzey kısmı yöneteceğine karar vermeliyiz. İsteğe bağlı bir <div>
seçebilir ve gerekirse daha sonra değiştirebiliriz.
<div id="app"> </div>
JavaScript tarafında, önceki HTML <div>
seçicisini CSS seçicisine geçirerek bir uygulama oluşturuyoruz.
Vue.createApp({}).mount('#app');
Bu noktada sayfa yüklendiğinde Vue'yu başlatıyoruz ancak görünürde hiçbir şey olmuyor.
Bir sonraki adım bir Vue şablonu oluşturmaktır. Bir Vue şablonu, Vue tarafından yönetilen normal bir HTML <template>
udur. Vue'yi Javascript'te tanımlayabilirsiniz, ancak bunu HTML sayfasında yapmayı tercih ediyorum.
Başlığı görüntüleyebilecek bir kök şablonla başlayalım.
<template id="todos-app"> <!--1--> <h1>{{ title }}</h1> <!--2--> </template>
title
özelliğini kullanın; kurulumu yapılması gerekiyor
JavaScript tarafında ise yönetim kodunu oluşturmamız gerekiyor.
const TodosApp = { props: ['title'], //1 template: document.getElementById('todos-app').innerHTML, }
title
özelliğini bildirin
Son olarak, uygulamayı oluştururken şu nesneyi iletmemiz gerekiyor:
Vue.createApp({ components: { TodosApp }, //1 render() { //2 return Vue.h(TodosApp, { //3 title: window.vueData.title, //4 }) } }).mount('#app');
render()
fonksiyonunu bekliyorh()
nesneden ve onun özelliklerinden sanal bir düğüm oluştururtitle
özelliğini sunucu tarafında oluşturulan değerle başlatın
Bu noktada Vue başlığı görüntüler.
Bu noktada, kullanıcı bir onay kutusuna tıkladığında eylemi uygulayabiliriz: sunucu tarafındaki durumda güncellenmesi gerekir.
Öncelikle, Todo
görüntüleyen tablo için yeni bir iç içe geçmiş Vue şablonu ekledim. Yazıyı uzatmamak için, ayrıntılı olarak açıklamaktan kaçınacağım. İlginizi çekerse, kaynak koduna bir göz atın.
Başlangıç satırı şablonunun kodu sırasıyla JavaScript ve HTML olarak şöyledir:
const TodoLine = { props: ['todo'], template: document.getElementById('todo-line').innerHTML }
<template id="todo-line"> <tr> <td>{{ todo.id }}</td> <!--1--> <td>{{ todo.label }}</td> <!--2--> <td> <label> <input type="checkbox" :checked="todo.completed" /> </label> </td> </tr> </template>
Todo
kimliğini gösterTodo
etiketini görüntülecompleted
niteliği true
kutuyu işaretleyin
Vue, @
sözdizimi aracılığıyla olay işleme olanağı sağlar.
<input type="checkbox" :checked="todo.completed" @click="check" />
Kullanıcı satıra tıkladığında Vue şablonun check()
fonksiyonunu çağırır. Bu fonksiyonu bir setup()
parametresinde tanımlarız:
const TodoLine = { props: ['todo'], template: document.getElementById('todo-line').innerHTML, setup(props) { //1 const check = function (event) { //2 const { todo } = props axios.patch( //3 `/api/todo/${todo.id}`, //4 { checked: event.target.checked } //5 ) } return { check } //6 } }
props
dizisini kabul edin, böylece daha sonra erişebilirizevent
geçirirÖnceki bölümde iki hata yaptım:
Bunu, tamamlanan görevlerin temizlenmesi olan bir sonraki özelliği uygulayarak yapacağız.
Artık Vue üzerinden olayların nasıl işleneceğini biliyoruz:
<button class="btn btn-warning" @click="cleanup">Cleanup</button>
TodosApp
nesnesine aynı isimli bir fonksiyon ekliyoruz:
const TodosApp = { props: ['title', 'todos'], components: { TodoLine }, template: document.getElementById('todos-app').innerHTML, setup() { const cleanup = function() { //1 axios.delete('/api/todo:cleanup').then(response => { //1 state.value.todos = response.data //2-3 }) } return { cleanup } //1 } }
state
modeli depoladığımız yerdir
Vue'nun semantiğinde, Vue modeli reaktif olmasını istediğimiz verilerin etrafında bir sarmalayıcıdır. Reaktif, görünüm ve model arasında iki yönlü bağlama anlamına gelir. Mevcut bir değeri ref()
metoduna geçirerek reaktif hale getirebiliriz:
Composition API'sinde reaktif durumu bildirmenin önerilen yolu
ref()
fonksiyonunu kullanmaktır.
ref()
argümanı alır ve onu .value özelliğine sahip bir ref nesnesi içinde sarılı olarak döndürür.
Bir bileşenin şablonundaki referanslara erişmek için, bunları bileşenin
setup()
fonksiyonundan bildirin ve döndürün.
Hadi yapalım:
const state = ref({ title: window.vueData.title, //1-2 todos: window.vueData.todos, //1 }) createApp({ components: { TodosApp }, setup() { return { ...state.value } //3-4 }, render() { return h(TodosApp, { todos: state.value.todos, //5 title: state.value.title, //5 }) } }).mount('#app');
title
ayarlama şeklimizi değiştiriyoruz. İki yönlü bir bağlama olmadığı için bu gerekli değil - başlığı istemci tarafında güncellemiyoruz, ancak tüm değerler arasında işlemeyi tutarlı tutmayı tercih ediyorumstate
yapılandırın
Bu noktada reaktif bir istemci tarafı modelimiz var.
HTML tarafında ilgili Vue niteliklerini kullanıyoruz:
<tbody> <tr is="vue:todo-line" v-for="todo in todos" :key="todo.id" :todo="todo"></tr> <!--1-2--> </tbody>
Todo
nesnelerinin listesi üzerinde döngüis
niteliği, tarayıcının HTML'yi ayrıştırma biçimiyle başa çıkmak için çok önemlidir. Daha fazla ayrıntı için Vue belgelerine bakınYukarıda ilgili şablonu anlattım.
Artık yeni bir özellik uygulayabiliriz: istemciden yeni bir Todo
ekleyin. Ekle düğmesine tıkladığımızda, Etiket alan değerini okuruz, verileri API'ye göndeririz ve modeli yanıtla yenileriz.
İşte güncellenmiş kod:
const TodosApp = { props: ['title', 'todos'], components: { TodoLine }, template: document.getElementById('todos-app').innerHTML, setup() { const label = ref('') //1 const create = function() { //2 axios.post('/api/todo', { label: label.value }).then(response => { state.value.todos.push(response.data) //3 }).then(() => { label.value = '' //4 }) } const cleanup = function() { axios.delete('/api/todo:cleanup').then(response => { state.value.todos = response.data //5 }) } return { label, create, cleanup } } }
create()
fonksiyonu uygunTodo
listesine ekleyin
HTML tarafında bir buton ekliyoruz ve create()
fonksiyonuna bağlıyoruz. Aynı şekilde Label alanını da ekliyoruz ve onu modele bağlıyoruz.
<form> <div class="form-group row"> <label for="new-todo-label" class="col-auto col-form-label">New task</label> <div class="col-10"> <input type="text" id="new-todo-label" placeholder="Label" class="form-control" v-model="label" /> </div> <div class="col-auto"> <button type="button" class="btn btn-success" @click="create">Add</button> </div> </div> </form>
Vue, create()
fonksiyonunu HTML düğmesine bağlar. Bunu eş zamanlı olarak çağırır ve çağrı tarafından döndürülen yeni öğeyle reaktif Todo
listesini yeniler. Aynısını, işaretli Todo
nesnelerini kaldırmak için Cleanup düğmesi için de yaparız.
Kodu gereğinden fazla karmaşık hale getirmekten kaçınmak için bilerek herhangi bir hata işleme kodu uygulamadığımı unutmayın. İlk deneyim için yeterli içgörü elde ettiğimizden burada duracağım.
Bu gönderide, Vue ile bir SSR uygulamasını zenginleştirmeye yönelik ilk adımlarımı attım. Oldukça basitti. Karşılaştığım en büyük sorun, Vue'nin satır şablonunu değiştirmesiydi: Belgeleri kapsamlı bir şekilde okumadım ve is
niteliğini kaçırdım.
Ancak HTTP çağrılarında Axios'u kullanmama rağmen bir hayli satır JavaScript yazmam gerekti ve hataları yönetemedim.
Bir sonraki yazımda aynı özellikleri Alpine.js ile uygulayacağım.
Bu yazının tam kaynak koduna GitHub'dan ulaşabilirsiniz:
Daha ileri gidelim: