Dalam catatan saya sebelum ini , saya meletakkan asas untuk membina; sekarang adalah masa untuk memulakan "sebenarnya".
Saya mendengar banyak Vue.js . Selain itu, rakan yang beralih daripada pembangun kepada pengurus memberitahu saya perkara yang baik tentang Vue, yang seterusnya menarik minat saya. Saya memutuskan untuk melihatnya: ia akan menjadi rangka kerja JavaScript "ringan" pertama yang akan saya kaji - dari sudut pandangan seorang pemula, iaitu saya.
Saya menerangkan WebJars dan Thymeleaf dalam jawatan terakhir. Berikut ialah persediaan, bahagian pelayan dan pelanggan.
Inilah cara saya mengintegrasikan kedua-duanya dalam POM:
<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>
Saya menggunakan Penghala Kotlin dan DSL Bean pada bahagian Spring Boot:
fun vue(todos: List<Todo>) = router { //1 GET("/vue") { ok().render("vue", mapOf("title" to "Vue.js", "todos" to todos)) //2-3 } }
Todo
Jika anda biasa membangunkan API, anda sudah biasa dengan fungsi body()
; ia mengembalikan muatan secara langsung, mungkin dalam format JSON. render()
menghantar aliran ke teknologi paparan, dalam kes ini, Thymeleaf. Ia menerima dua parameter:
/templates
dan awalan ialah .html
; dalam kes ini, Thymeleaf menjangkakan paparan di /templates/vue.html
Berikut ialah kod di sebelah HTML:
<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>
Seperti yang dijelaskan dalam artikel minggu lepas, salah satu faedah Thymeleaf ialah ia membenarkan pemaparan fail statik dan pemaparan sebelah pelayan. Untuk membuat keajaiban berfungsi, saya menentukan laluan sisi klien, iaitu , src
dan laluan sisi pelayan, iaitu , th:src
.
Sekarang, mari kita selami kod Vue.
Kami ingin melaksanakan beberapa ciri:
Todo
Todo
complete, ia harus menetapkan/menyahset atribut completed
Todo
yang telah selesaiTodo
ke senarai Todo
dengan nilai berikut:id
: ID pengiraan sebelah pelayan sebagai maksimum semua ID lain tambah 1label
: nilai medan Label untuk label
completed
: ditetapkan kepada false
Langkah pertama ialah bootstrap rangka kerja. Kami telah menyediakan rujukan untuk fail vue.js
tersuai kami di atas.
document.addEventListener('DOMContentLoaded', () => { //1 // The next JavaScript code snippets will be inside the block }
Langkah seterusnya ialah membenarkan Vue mengurus sebahagian halaman. Dari segi HTML, kita mesti memutuskan bahagian peringkat teratas yang Vue uruskan. Kita boleh memilih <div>
sewenang-wenangnya dan mengubahnya kemudian jika perlu.
<div id="app"> </div>
Di sisi JavaScript, kami mencipta apl , melepasi pemilih CSS bagi HTML sebelumnya <div>
.
Vue.createApp({}).mount('#app');
Pada ketika ini, kami melancarkan Vue apabila halaman dimuatkan, tetapi tiada apa yang kelihatan berlaku.
Langkah seterusnya ialah membuat templat Vue . Templat Vue ialah <template>
HTML biasa yang diuruskan oleh Vue. Anda boleh menentukan Vue dalam Javascript, tetapi saya lebih suka melakukannya pada halaman HTML.
Mari kita mulakan dengan templat akar yang boleh memaparkan tajuk.
<template id="todos-app"> <!--1--> <h1>{{ title }}</h1> <!--2--> </template>
title
; ia masih perlu disediakan
Di sisi JavaScript, kita mesti mencipta kod pengurusan.
const TodosApp = { props: ['title'], //1 template: document.getElementById('todos-app').innerHTML, }
title
, yang digunakan dalam templat HTML
Akhir sekali, kita mesti lulus objek ini apabila kita mencipta aplikasi:
Vue.createApp({ components: { TodosApp }, //1 render() { //2 return Vue.h(TodosApp, { //3 title: window.vueData.title, //4 }) } }).mount('#app');
render()
h()
untuk hiperskrip mencipta nod maya daripada objek dan sifatnyatitle
dengan bahagian pelayan yang dijana nilai
Pada ketika ini, Vue memaparkan tajuk.
Pada ketika ini, kami boleh melaksanakan tindakan apabila pengguna mengklik pada kotak pilihan: ia perlu dikemas kini dalam keadaan sebelah pelayan.
Mula-mula, saya menambah templat Vue bersarang baharu untuk jadual yang memaparkan Todo
. Untuk mengelak daripada memanjangkan siaran, saya akan mengelak daripada menerangkannya secara terperinci. Jika anda berminat, sila lihat kod sumber .
Berikut ialah kod templat baris permulaan, masing-masing JavaScript dan HTML:
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
Todo
completed
adalah true
Vue membenarkan pengendalian acara melalui sintaks @
.
<input type="checkbox" :checked="todo.completed" @click="check" />
Vue memanggil fungsi check()
templat apabila pengguna mengklik pada baris. Kami mentakrifkan fungsi ini dalam parameter setup()
:
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
, supaya kami boleh mengaksesnya kemudianevent
yang mencetuskan panggilanDalam bahagian sebelumnya, saya membuat dua kesilapan:
Kami akan melakukannya dengan melaksanakan ciri seterusnya, iaitu pembersihan tugasan yang telah selesai.
Kami kini tahu cara mengendalikan acara melalui Vue:
<button class="btn btn-warning" @click="cleanup">Cleanup</button>
Pada objek TodosApp
, kami menambah fungsi dengan nama yang sama:
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
adalah tempat kami menyimpan model
Dalam semantik Vue, model Vue ialah pembalut di sekeliling data yang kita mahu reaktif . Reaktif bermaksud pengikatan dua hala antara pandangan dan model. Kita boleh membuat nilai sedia ada reaktif dengan menghantarnya ke kaedah ref()
:
Dalam API Komposisi, cara yang disyorkan untuk mengisytiharkan keadaan reaktif adalah menggunakan fungsi
ref()
.
ref()
mengambil hujah dan mengembalikannya dibalut dalam objek ref dengan sifat .value.
Untuk mengakses rujukan dalam templat komponen, isytiharkan dan kembalikannya daripada fungsi
setup()
komponen.
Jom buat:
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
. Ia tidak perlu kerana tiada pengikatan dua hala - kami tidak mengemas kini tajuk bahagian klien, tetapi saya lebih suka mengekalkan pengendalian yang koheren merentas semua nilaistate
Pada ketika ini, kami mempunyai model sisi pelanggan yang reaktif .
Di bahagian HTML, kami menggunakan atribut Vue yang berkaitan:
<tbody> <tr is="vue:todo-line" v-for="todo in todos" :key="todo.id" :todo="todo"></tr> <!--1-2--> </tbody>
Todo
is
adalah penting untuk mengatasi cara pelayar menghuraikan HTML. Lihat dokumentasi Vue untuk butiran lanjutSaya telah menerangkan templat yang sepadan di atas.
Kami kini boleh melaksanakan ciri baharu: tambah Todo
baharu daripada klien. Apabila mengklik pada butang Tambah , kami membaca nilai medan Label , menghantar data ke API, dan menyegarkan model dengan respons.
Berikut ialah kod yang dikemas kini:
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()
betulTodo
Di sebelah HTML, kami menambah butang dan mengikat kepada fungsi create()
. Begitu juga, kami menambah medan Label dan mengikatnya pada model.
<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 mengikat fungsi create()
pada butang HTML. Ia memanggilnya secara tidak segerak dan menyegarkan senarai Todo
reaktif dengan item baharu yang dikembalikan melalui panggilan. Kami melakukan perkara yang sama untuk butang Pembersihan , untuk mengalih keluar objek Todo
yang diperiksa.
Ambil perhatian bahawa saya tidak sengaja melaksanakan sebarang kod pengendalian ralat untuk mengelakkan membuat kod lebih kompleks daripada yang diperlukan. Saya akan berhenti di sini kerana kami memperoleh cukup cerapan untuk pengalaman pertama.
Dalam siaran ini, saya mengambil langkah pertama saya dalam menambah apl SSR dengan Vue. Ia agak mudah. Isu terbesar yang saya temui ialah untuk Vue menggantikan templat baris: Saya tidak membaca dokumentasi secara meluas dan terlepas atribut is
.
Walau bagaimanapun, saya terpaksa menulis beberapa baris JavaScript, walaupun saya menggunakan Axios untuk membantu saya dengan panggilan HTTP dan tidak menguruskan ralat.
Dalam siaran seterusnya, saya akan melaksanakan ciri yang sama dengan Alpine.js.
Kod sumber lengkap untuk siaran ini boleh didapati di GitHub:
Pergi lebih jauh: