跳至主要内容

70 篇文章 含有標籤「frontend engineer」

檢視所有標籤

Vue.js 3 官方入門語法教學筆記 [10] - Components 元件 | 學習筆記

· 閱讀時間約 1 分鐘
kdchang

到目前為止,我們只使用了一個單一的元件。實際的 Vue 應用程式通常是由巢狀元件所組成的。

父元件可以在其模板中渲染另一個元件作為子元件。要使用子元件,我們需要先匯入它:

import ChildComp from './ChildComp.vue'

然後,我們可以在模板中使用該元件,如下所示:

<template>
<ChildComp />
</template>

我們現在試試看將匯入子元件並將其渲染到模板中。

<script setup>
import ChildComp from './ChildComp.vue'
</script>

<template>
<ChildComp />
</template>
<template>
<h2>A Child Component!</h2>
</template>

Vue.js 3 官方入門語法教學筆記 [9] - Watchers 觀察者 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

有時我們可能需要以反應性的方式執行「Side-effect 副作用」,例如,當一個數值改變時將其記錄到控制台。我們可以使用觀察者來實現這一點:

import { ref, watch } from 'vue'

const count = ref(0)

watch(count, (newCount) => {
// 是的,console.log() 是一種副作用
console.log(`新的計數是:${newCount}`)
})

watch() 可以直接監視一個 ref,每當 count 的值改變時,回調函數就會被觸發。watch() 也可以監視其他類型的數據來源——更多細節請參閱指南:觀察者(Watchers)。

比將訊息記錄到控制台更實用的例子,可能是當一個 ID 發生變化時,根據新 ID 獲取數據。我們的代碼目前是在元件掛載時,從一個模擬 API 獲取 todos 數據。此外,還有一個按鈕可以遞增應該被獲取的 todo ID。請嘗試實現一個觀察者,在按下按鈕時根據新 ID 獲取新的 todo 數據。

參考範例:

<script setup>
import { ref, watch } from 'vue'

const todoId = ref(1)
const todoData = ref(null)

async function fetchData() {
todoData.value = null
const res = await fetch(
`https://jsonplaceholder.typicode.com/todos/${todoId.value}`
)
todoData.value = await res.json()
}

fetchData()

watch(todoId, fetchData)
</script>

<template>
<p>Todo id: {{ todoId }}</p>
<button @click="todoId++" :disabled="!todoData">Fetch next todo</button>
<p v-if="!todoData">Loading...</p>
<pre v-else>{{ todoData }}</pre>
</template>

Vue.js 3 官方入門語法教學筆記 [8] - Lifecycle and Template Refs 生命週期與模板引用 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

Lifecycle and Template Refs 生命週期與模板引用

到目前為止,Vue.js 透過響應式資料與聲明式渲染,幫助我們處理了所有的 DOM 更新。然而,無可避免地會有需要手動操作 DOM 的情況。

我們可以使用特殊的 ref 屬性來請求模板引用 (template ref),也就是模板中某個元素的引用:

範例

<template>
<p ref="pElementRef">hello</p>
</template>

定義引用

為了訪問這個引用,我們需要宣告一個名稱匹配的 ref

import { ref } from 'vue';

const pElementRef = ref(null);

請注意,這個 ref 在初始化時會是 null,因為當 <script setup> 被執行時,對應的 DOM 元素還不存在。模板引用只有在元件掛載 (mounted) 後才能被訪問。

在掛載後執行程式碼

我們可以使用 onMounted() 函式在掛載後執行程式碼:

import { onMounted } from 'vue';

onMounted(() => {
// 元件已掛載完成
});

生命週期鉤子

這稱為生命週期鉤子 (lifecycle hook),它允許我們在元件生命週期的特定時間點註冊回呼函式。其他的生命週期鉤子還包括 onUpdatedonUnmounted 等。更多細節請參考官方文件 生命週期圖示

試試看

現在,我們可以嘗試添加一個 onMounted 鉤子,透過 pElementRef.value 訪問 <p> 元素,並對其進行一些直接的 DOM 操作(例如更改 textContent)。

<script setup>
import { ref, onMounted } from 'vue'

const pElementRef = ref(null)

onMounted(() => {
pElementRef.value.textContent = 'mounted!'
})
</script>

<template>
<p ref="pElementRef">Hello</p>
</template>

Vue.js 3 官方入門語法教學筆記 [7] - Computed Property 計算屬性 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

讓我們在前一個待辦清單的範例基礎上繼續改進。這裡,我們已經為每個待辦項目新增了切換功能 (toggle),這是透過在每個待辦物件中新增一個 done 屬性,並使用 v-model 綁定到核取方塊來實現的:

<template>
<li v-for="todo in todos">
<input type="checkbox" v-model="todo.done">
...
</li>
</template>

接下來我們可以進一步改進功能,新增一個按鈕來隱藏已完成的待辦項目。我們已經有一個按鈕可以切換 hideCompleted 狀態。但要如何根據這個狀態來動態渲染不同的待辦清單項目呢?

這裡引入了 computed()。我們可以建立一個計算屬性,基於其他的響應式數據來源來計算它的 .value 值:

import { ref, computed } from 'vue';

const hideCompleted = ref(false);
const todos = ref([
/* ... */
]);

const filteredTodos = computed(() => {
// 根據 `todos.value` 和 `hideCompleted.value`
// 返回篩選後的待辦項目
});

我們將 v-for 的數據來源從原本的 todos 改為 filteredTodos

- <li v-for="todo in todos">
+ <li v-for="todo in filteredTodos">

計算屬性會自動追蹤其計算邏輯中使用的其他響應式數據作為依賴項目。它會快取計算結果,並在其依賴項目改變時自動更新。

現在,嘗試新增一個 filteredTodos 計算屬性,並實現其計算邏輯!如果實現正確,當隱藏已完成項目時,勾選一個待辦項目應會立即將其隱藏。

<script setup>
import { ref, computed } from 'vue'

let id = 0

const newTodo = ref('')
const hideCompleted = ref(false)
const todos = ref([
{ id: id++, text: 'Learn HTML', done: true },
{ id: id++, text: 'Learn JavaScript', done: true },
{ id: id++, text: 'Learn Vue', done: false }
])

const filteredTodos = computed(() => {
return hideCompleted.value
? todos.value.filter((t) => !t.done)
: todos.value
})

function addTodo() {
todos.value.push({ id: id++, text: newTodo.value, done: false })
newTodo.value = ''
}

function removeTodo(todo) {
todos.value = todos.value.filter((t) => t !== todo)
}
</script>

<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo" required placeholder="new todo">
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in filteredTodos" :key="todo.id">
<input type="checkbox" v-model="todo.done">
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
<button @click="hideCompleted = !hideCompleted">
{{ hideCompleted ? 'Show all' : 'Hide completed' }}
</button>
</template>

<style>
.done {
text-decoration: line-through;
}
</style>

Vue.js 3 官方入門語法教學筆記 [6] - List Rendering 表單綁定 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

List Rendering 列表渲染
我們可以使用 v-for 指令根據一個來源陣列來渲染元素列表:

<template>
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
</template>

在這裡,todo 是一個局部變數,代表目前被迭代的陣列元素。它的作用域僅限於 v-for 元素內部,類似於函式的作用域。


請注意,我們為每個 todo 對象指定了一個唯一的 id,並將其綁定為每個 <li> 的特殊 key 屬性。key 允許 Vue 精準地移動每個 <li>,以匹配陣列中對應對象的位置。


有兩種方式可以更新列表:

  1. 對來源陣列調用可變方法:

    todos.value.push(newTodo)
  2. 使用新的陣列替換:

    todos.value = todos.value.filter(/* ... */)

以下是一個簡單的待辦事項列表範例,您可以試著實現 addTodo()removeTodo() 方法,使其正常運作!

範例程式碼:

<template>
<div>
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
<button @click="removeTodo(todo.id)">Remove</button>
</li>
</ul>
<input v-model="newTodoText" placeholder="Add a new todo" />
<button @click="addTodo">Add Todo</button>
</div>
</template>

<script setup>
import { ref } from 'vue'

// 定義待辦事項列表和新增的文字
const todos = ref([
{ id: 1, text: 'Learn Vue.js' },
{ id: 2, text: 'Build a project' }
])

const newTodoText = ref('')

// 新增待辦事項
function addTodo() {
if (newTodoText.value.trim()) {
todos.value.push({
id: Date.now(), // 使用當前時間作為唯一 ID
text: newTodoText.value.trim()
})
newTodoText.value = '' // 清空輸入框
}
}

// 移除待辦事項
function removeTodo(id) {
todos.value = todos.value.filter(todo => todo.id !== id)
}
</script>

更多關於 v-for 的詳細內容,請參閱官方指南 - 列表渲染

<script setup>
import { ref } from 'vue'

// give each todo a unique id
let id = 0

const newTodo = ref('')
const todos = ref([
{ id: id++, text: 'Learn HTML' },
{ id: id++, text: 'Learn JavaScript' },
{ id: id++, text: 'Learn Vue' }
])

function addTodo() {
todos.value.push({ id: id++, text: newTodo.value })
newTodo.value = ''
}

function removeTodo(todo) {
todos.value = todos.value.filter((t) => t !== todo)
}
</script>

<template>
<form @submit.prevent="addTodo">
<input v-model="newTodo" required placeholder="new todo">
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in todos" :key="todo.id">
{{ todo.text }}
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
</template>

Vue.js 3 官方入門語法教學筆記 [5] - Conditional Rendering 表單綁定 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

Conditional Rendering 條件渲染
在 Vue 中,我們可以使用 v-if 指令來條件式渲染元素:

<template>
<h1 v-if="awesome">Vue is awesome!</h1>
</template>

這個 <h1> 只有在 awesome 的值為真值時才會被渲染。如果 awesome 的值變為假值,它將從 DOM 中被移除。


我們還可以使用 v-elsev-else-if 表示條件的其他分支:

<template>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
</template>

目前的範例同時顯示了兩個 <h1>,按鈕也沒有作用。請嘗試為 <h1> 添加 v-ifv-else 指令,並實作一個 toggle() 方法,使我們可以透過按鈕來切換顯示的內容。


示例程式碼範例:

<template>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
<button @click="toggle">Toggle</button>
</template>

<script setup>
import { ref } from 'vue'

const awesome = ref(true)

function toggle() {
awesome.value = !awesome.value
}
</script>

更多關於 v-if 的詳細內容請參閱官方指南 - 條件渲染

<script setup>
import { ref } from 'vue'

const awesome = ref(true)

function toggle() {
awesome.value = !awesome.value
}
</script>

<template>
<button @click="toggle">Toggle</button>
<h1 v-if="awesome">Vue is awesome!</h1>
<h1 v-else>Oh no 😢</h1>
</template>

Vue.js 3 官方入門語法教學筆記 [4] - Form Bindings 表單綁定 | 學習筆記

· 閱讀時間約 2 分鐘
kdchang

Form Bindings 表單綁定
使用 v-bindv-on 結合,我們可以對表單輸入元素創建雙向綁定:

<template>
<input :value="text" @input="onInput">
<p>{{ text }}</p>
</template>

<script setup>
import { ref } from 'vue'

const text = ref('')

function onInput(e) {
// v-on 處理器接收原生 DOM 事件作為參數
text.value = e.target.value
}
</script>

當您在輸入框中輸入時,您應該會看到 <p> 中的文字隨之更新。


為了簡化雙向綁定,Vue 提供了 v-model 指令,它本質上是上述代碼的語法糖:

<template>
<input v-model="text">
<p>{{ text }}</p>
</template>

<script setup>
import { ref } from 'vue'

const text = ref('')
</script>

v-model 會自動同步 <input> 的值與綁定的狀態,因此我們不再需要為此使用事件處理器。


v-model 不僅適用於文本輸入,還適用於其他輸入類型,如復選框 (checkbox)、單選按鈕 (radio button) 和下拉選單 (select dropdown)。有關更多細節,請參閱官方指南 - 表單綁定


現在,我們試著將代碼重構為使用 v-model

SFC/Composition 版本:

<script setup>
import { ref } from 'vue'

const text = ref('')
</script>

<template>
<input v-model="text" placeholder="Type here">
<p>{{ text }}</p>
</template>

SFC/Options 版本:

<script>
export default {
data() {
return {
text: ''
}
},
methods: {
onInput(e) {
this.text = e.target.value
}
}
}
</script>

<template>
<input :value="text" @input="onInput" placeholder="Type here">
<p>{{ text }}</p>
</template>

HTML/Options 版本:

<script type="module">
import { createApp } from 'vue'

createApp({
data() {
return {
text: ''
}
},
methods: {
onInput(e) {
this.text = e.target.value
}
}
}).mount('#app')
</script>

<div id="app">
<input :value="text" @input="onInput" placeholder="Type here">
<p>{{ text }}</p>
</div>

HTML/Composition 版本:

<script type="module">
import { createApp, ref } from 'vue'

createApp({
setup() {
const text = ref('')

function onInput(e) {
text.value = e.target.value
}

return {
text,
onInput
}
}
}).mount('#app')
</script>

<div id="app">
<input :value="text" @input="onInput" placeholder="Type here">
<p>{{ text }}</p>
</div>

Vue.js 3 官方入門語法教學筆記 [3] - Event Listeners 事件監聽器 | 學習筆記

· 閱讀時間約 1 分鐘
kdchang

Event Listeners 事件監聽器
在 Vue 中,我們可以使用 v-on 指令監聽 DOM 事件:

<template>
<button v-on:click="increment">{{ count }}</button>
</template>

由於 v-on 的使用頻率很高,Vue 提供了一個簡寫語法:

<template>
<button @click="increment">{{ count }}</button>
</template>

在這裡,increment 是在 <script setup> 中定義的一個函式:

<script setup>
import { ref } from 'vue'

// 定義響應式狀態
const count = ref(0)

// 定義函式來更新狀態
function increment() {
// 更新組件的狀態
count.value++
}
</script>

在函式內,我們可以透過修改 ref 的值來更新組件的狀態。


事件處理器也可以使用內聯表達式,並透過修飾符簡化常見任務。這些細節在指南 - 事件處理中有詳細說明。


現在,我們可以試著自己實作 increment 函式,並使用 v-on 將它綁定到按鈕。

<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
count.value++
}
</script>

<template>
<button @click="increment">Count is: {{ count }}</button>
</template>

Vue.js 3 官方入門語法教學筆記 [2] - Attribute Bindings 屬性綁定 | 學習筆記

· 閱讀時間約 1 分鐘
kdchang

Attribute Bindings 屬性綁定
在 Vue 中,Mustache 語法只能用於文字插值。要將屬性綁定到動態值,我們需要使用 v-bind 指令:

<template>
<div v-bind:id="dynamicId"></div>
</template>

指令是一種特殊的屬性,以 v- 前綴開頭,屬於 Vue 的模板語法的一部分。與文字插值類似,指令的值是 JavaScript 表達式,可以訪問組件的狀態。有關 v-bind 和指令語法的完整細節,請參閱官方說明指南 - 模板語法

冒號之後的部分(:id)是指令的「參數」。在這裡,元素的 id 屬性將與組件狀態中的 dynamicId 屬性同步。

由於 v-bind 的使用頻率很高,Vue 提供了專用的簡寫語法:

<template>
<div :id="dynamicId"></div>
</template>

我們可以試著將動態類名綁定到 <h1>,使用 titleClassref 作為值。如果綁定正確,文字應該會變成紅色!

<script setup>
import { ref } from 'vue'

const titleClass = ref('title')
</script>

<template>
<h1 :class="titleClass">Make me red</h1> <!-- add dynamic class binding here -->
</template>

<style>
.title {
color: red;
}
</style>

Vue.js 3 官方入門語法教學筆記 [1] - Declarative Rendering 聲明式渲染 | 學習筆記

· 閱讀時間約 3 分鐘
kdchang

在 Vue3 我們常看到的是 Vue 單文件元件 (SFC)。SFC 是一個可重用的、獨立的程式碼區塊,它將相關的 HTML、CSS 和 JavaScript 封裝在一起,並寫在一個 .vue 文件中。

Vue 的核心特性是聲明式渲染:使用擴展 HTML 的模板語法,我們可以根據 JavaScript 的狀態描述 HTML 應該如何呈現。當狀態發生變化時,HTML 會自動更新。

可以在狀態變化時觸發更新的狀態被認為是響應式的。我們可以使用 Vue 的 reactive() API 來聲明響應式狀態。通過 reactive() 創建的對象是 JavaScript 的 Proxy,它們的行為與普通物件相同:

SFC 版本:

import { reactive } from 'vue'

const counter = reactive({
count: 0
})

console.log(counter.count) // 0
counter.count++

html 版本:

<script type="module">
import { createApp, ref } from 'vue'

createApp({
setup() {
// component logic
// declare some reactive state here.

return {
// exposed to template
}
}
}).mount('#app')
</script>

<div id="app">
<h1>Make me dynamic!</h1>
</div>

reactive() 只能作用於物件(包括陣列和內建類型如 Map 和 Set)。另一方面,ref() 可以接受任何類型的值並創建一物件,其內部值通過 .value 屬性暴露出來:

import { ref } from 'vue'

const message = ref('Hello World!')

console.log(message.value) // "Hello World!"
message.value = 'Changed'

有關 reactive()ref() 的更多細節,可以參考官方教學指南 - 響應式基礎

在組件的 <script setup> 區塊中聲明的響應式狀態可以直接在模板中使用。我們可以基於 counter 對象和 message 的值,使用 Mustache 語法渲染動態文字:

<template>
<h1>{{ message }}</h1>
<p>Count is: {{ counter.count }}</p>
</template>

注意,當在模板中訪問 messageref 值時,我們不需要使用 .value:它會自動取值,以提供更簡潔的用法。

Mustache {{ }} 中的內容不限於標識符或路徑 —— 我們可以使用任何有效的 JavaScript 表達式:

<template>
<h1>{{ message.split('').reverse().join('') }}</h1>
</template>

現在,試著自己創建一些響應式狀態,並使用它來為模板中的 <h1> 渲染動態文本內容吧!

<script setup>
import { reactive, ref } from 'vue'

const counter = reactive({ count: 0 })
const message = ref('Hello World!')
</script>

<template>
<h1>{{ message }}</h1>
<p>Count is: {{ counter.count }}</p>
</template>

總結

Vue.js 3 提供了更好的性能、更靈活的 API 和更簡潔的開發體驗。無論是使用 Composition API 還是 Options API,都能快速上手並構建強大的前端應用。