Vue3 - 基本的模板語法
tags: Vue
category: Front-End
description: Vue3 - 基本的模板語法
created_at: 2022/07/15 16:30:00
回到 手把手開始寫 Vue3
前言
還是假設你已經做好了第一篇做的事 開新專案與環境設定
我自己還是覺得基本上大多都跟 Vue2
差不多。(模板語法的部分)
本篇會介紹到
- 插入變數
- 插入
html
- 屬性綁定
- 一次綁定多個屬性
- 動態綁定屬性
- 指令
if
相關v-show
for
最基本的 - 插入變數
使用兩個大括號包住,就可以輸出上方 js
的變數,又或者說是 javascript expression
<script setup>
const count = 100;
</script>
<template>
{{ count }}
</template>
你也可以使用另一種方式帶入文字(text
)
<template>
<div v-text="count"></div>
</template>
只要是 Vue
定義的屬性,他在雙引號中間的字串,就會去解析他(後面會更有感)
另外以兩個大括號輸出 javascript expression
的範例
<div>{{ "hello world".split("").reverse().join("") }}</div>
這樣子你會看到畫面上輸出
dlrow olleh
插入 html
因為上面使用兩個大括號輸出的東西是文字(text
),那如果要插入的是 html
那就必須使用 v-html
這個屬性
先看一下錯誤的範例:
<script setup>
const htmlText = `<div>Hello</div>`;
</script>
<template>
<div>{{ htmlText }}</div>
</template>
這樣子你會在網頁上看到 <div>Hello</div>
的字串,而不是你想要的內容
你應該改成下面這樣
<template>
<div v-html="htmlText"></div>
</template>
這樣的話,你就會在畫面上只看到 Hello
然後因為這個東西會直接把 html
做執行,所以你應該小心的使用他,像是如果你把變數改成下面這一段,你會發現畫面上的文字變成了紅色。
<script setup>
const htmlText = `
<div>
Hello
<style>
div {
color: red;
}
</style>
</div>`;
</script>
屬性綁定
上面定義出來的變數不只可以輸出成文字(text
)、html
外,還可以動態的帶入到屬性(attribute
)中
<script setup>
const className = "card";
</script>
<template>
<div v-bind:class="className">Hello</div>
</template>
上面這樣做,這個 div
,就會擁有一個 class
,而他的值為 card
,就像是下面這樣
<div class="card">Hello</div>
而這個功能因為很常用到,所以也有簡短的寫法,就是去掉 v-bind
,變成下面這樣
<div :class="className">Hello</div>
而上面有提到,只要是 Vue
定義的屬性,他在雙引號中間的字串,就會去解析他,也就是一個 javascript expression
,例如我也可以去組裝字串,像是下面這樣
<div :class="`my-class-${className}`">Hello</div>
那他就會生成
<div class="my-class-card">Hello</div>
注意: 如果是字串,記得要加單雙引號或「`」包起來,因為他是一個 javascript expression
。
不過既然他是一個 javascript expression
,那麼其實你要塞 function
也不是不能,但通常應該不會這樣做。
<script setup>
const className = "card";
const f = (text) => {
return `my-class-${text}`;
};
</script>
<template>
<div :class="f(className)">Hello</div>
</template>
像是上面這種需求,你可能會直接用 computed
然後去監控 className
這個變數。(之後會提到)
一次綁定多個屬性
可以像是上面一個一個慢慢綁定,你也可以直接綁定一個物件到屬性中。
<script setup>
const attrs = {
id: "hello",
class: "world",
};
</script>
<template>
<div v-bind="attrs">Hello</div>
</template>
這樣子他會生成下面這一段 HTML
<div id="hello" class="world">Hello</div>
動態綁定屬性
雖然可以像上面使用物件一次綁定多個屬性,但有時候屬性的 key
可能是動態產生的,又不想要多生成一個物件,你可以這樣做
<script setup>
const attrKey = "custom-key";
</script>
<template>
<div v-for="i in 5" :[attrKey]="i" :key="i">{{ i }}</div>
</template>
上面這段就會生出五個 div
<div custom-key="1">1</div>
<div custom-key="2">2</div>
<div custom-key="3">3</div>
<div custom-key="4">4</div>
<div custom-key="5">5</div>
主要就是透過 :[javascript expression]
這一段冒號+中括號裡面放入東西
指令
像是一些 if
for
之類的東西,或是乾脆把他想成 屬性的 key
值(?)
if 相關
if
基本上就是 if
(廢話),反正條件 true
才會渲染出來
<template>
<div v-if="true">true</div>
<div v-if="false">false</div>
</template>
這樣你的畫面上只會有 true
,因為他 v-if
成立。
比較完整的範例
<script setup>
const age = 18;
</script>
<template>
<div v-if="age < 18">你未成年</div>
<div v-else-if="age < 100">你成年了</div>
<div v-else>真的嗎? 你也活太久了</div>
</template>
可以去改變上方定義的 age
,這樣畫面上輸出的文字就會根據條件改變。
v-show
跟 if
滿像的,也是根據條件是否顯示元素
<template>
<div v-show="true">true</div>
<div v-show="false">false</div>
</template>
他會輸出下面這樣
<div>true</div>
<div style="display: none;">false</div>
注意: 他跟 v-if
不太一樣,在 v-if
,如果條件不成立,會整個不渲染,完全不會出現在 DOM
上,但是 v-show
只是依靠 display
讓他隱藏。
for
就...迴圈,廢話,一樣看範例
<script setup>
const items = [
{
id: 1,
name: "Item A",
},
{
id: 2,
name: "Item B",
},
{
id: 3,
name: "Item C",
},
{
id: 4,
name: "Item D",
},
{
id: 5,
name: "Item E",
},
];
</script>
<template>
<ul>
<li v-for="item in items" :key="item.id">{{ item.id }}. {{ item.name }}</li>
</ul>
</template>
上面先做了一個要拿來跑迴圈的陣列 (雖然不只可以跑陣列)
v-for
能用的方式有滿多,可以直接去看官方
而這邊只提一個重點,就是那個 :key
是要幹嘛的,為什麼要加,不加或亂加會怎樣。
在 vue
裡面 ,因為他要提高渲染的效率,所以如果 key
一樣(或是沒有),他就會直接拿來用,就是更新現有的 DOM
內容,而不是重新渲染。
就是說他會想盡可能重複利用已經在畫面上的 DOM
就是了。
所以比較適合帶的是唯一的值,而不是帶陣列的索引(index
),除非你要迭代的東西很單純(不會變)
舉個例子:
src/App.vue
<script setup>
import { reactive } from "vue";
import Child from "./components/Child.vue";
const nums = reactive([1, 2, 3, 4, 5]);
const insert = () => {
nums.splice(2, 0, 100);
};
</script>
<template>
<h1>key 帶 index</h1>
<ul>
<Child v-for="(num, index) in nums" :num="num" :key="index" />
</ul>
<hr />
<h1>key 帶 num</h1>
<ul>
<Child v-for="num in nums" :num="num" :key="num" />
</ul>
<button class="bg-blue-500 text-white px-4 py-2" @click="insert">
insert 100 to index 2
</button>
</template>
src/components/Child.vue
<script setup>
import { onMounted, ref } from "vue";
const props = defineProps({
num: {
type: Number,
required: true,
},
});
const num = ref(null);
onMounted(() => {
num.value = props.num;
});
</script>
<template>
<div>prop = {{ props.num }}, local = {{ num }}</div>
</template>
可以先不要管語法細節,先知道說 onMounted
是當元件被掛上 DOM
時會做的事,也就是說他只會做一次。
那麼上面這段會跑出下面的結果
可以看到初始狀態都是正確的,但是當你戳下那顆按鈕
你會看到,WOW,為什麼上面的結果錯了?!
事情是這樣的,想像一下原本他渲染了5個,可能長得像這樣
<Child :num="1" :key="0" />
<Child :num="2" :key="1" />
<Child :num="3" :key="2" />
<Child :num="4" :key="3" />
<Child :num="5" :key="4" />
然後當你插入 100 之後
<Child :num="1" :key="0" />
<Child :num="2" :key="1" />
<Child :num="100" :key="2" />
<Child :num="3" :key="3" />
<Child :num="4" :key="4" />
<Child :num="5" :key="5" />
所以他從 num = 100
,之後直到最新的那個之前都會是錯的,因為他重複利用了舊的元件,只是改了 num
,但是 onMounted()
,還是只被執行一次,並不是新的元件。
所以他其實只會新增了最後一個 key=5
的這一個,因為他確實是新的。
總結: 講了這麼多,如果不想理解那麼多,總之你就乖乖設 key
為唯一值就對了。