Vue3 - 基本的模板語法

tags: Vue
category: Front-End
description: Vue3 - 基本的模板語法
created_at: 2022/07/15 16:30:00

cover image


回到 手把手開始寫 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 時會做的事,也就是說他只會做一次。

那麼上面這段會跑出下面的結果

v-for key

可以看到初始狀態都是正確的,但是當你戳下那顆按鈕

v-for key

你會看到,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 為唯一值就對了。





最後更新時間: 2022年07月15日.