Vue3 筆記 | 專案建立與響應式

閱讀時間約 15 分鐘

當初在找工作的時候在 104 上就發現台灣的公司貌似比較常用 Vue 來開發前端專案,React 和 Angular 的職缺較少,而且 Angular 也逐漸被 Vue 和 React 給甩到後面去了。

現在開始逐步學習 Vue、Pinia、Nuxt,我會把學習筆記以部落格的方式放出來分享給大家,與有興趣的各位一同學習。



專案建立

Vue3 釋出之後改變了許多 Vue 原本的生態,包括 Vue2 的選項式 API 寫法改成推薦使用組合式 API 寫法,在專案的建立上也不再使用舊版的 CLI 而是直接使用 Vite。

網路上查詢用 Vite 建立專案會有複數種寫法,但最簡便的方式其實是 Vue 官網上開宗明義使用的 :

npm create vue@latest

這串打下去就直接默認用 Vite 來幫忙建立 Vue 專案了,可以說是非常的迅速。接下來r進到專案後按照下列步驟:

npm install
npm run dev

當你在瀏覽器看到類似下面這張圖時,就代表成功建立一個 Vue 專案了。

raw-image



語法

講語法前,先來說一下 Vue3 的結構。Vue3 推薦的架構跟 Vue2 不太一樣,當然還是可以用 Vue2 的那種架構寫法,但 Vue3 的架構會更方便易懂,尤其如果你是像我一樣先寫 React 才來學 Vue 的。Vue3 的架構會像下面這樣:

<script setup>
/** 你的變數、方法通通都在這裡,如果想用 Vue2 寫法,可以不在 script 處加 setup **/
</script>

<template>
/** 你的 HTML 架構 **/
</template>

<style scoped>
/** 你的樣式,添加 scope 讓這個樣式只生效於此元件下 **/
</style>

接下來講語法。與 React 使用 JSX 語法不同,Vue 使用模板語法進行開發,使用上會更像 Express + Handlebars。

使用上有兩個重點:

  1. {{…}} 包裹變數:如 <span>Message: {{ msg }}</span> 中,msg 代表的可能是個響應式變數,當他變動時渲染也會跟著更新。
  2. v-html 包裹 HTML 語法:因為 {{…}} 會把數據解釋為純文本,如果你的變數代表的是一段 HTML,那必須為你的 tag 添加 v-html 屬性並掛上的 HTML 變數,這裡我直接放官網範例:
raw-image

v-bind

如果今天變數擺放的區域是在 HTML tag 上,比如說屬性要依照某變數來決定是否掛上 tag,那在這時不能使用 {{…}},而是要改用 v-bind

<h1 v-bind:class="{addColor: changed}">I will change my color</h1>

比如這裡可以假定為當 changed 為 true 時掛上一個 class 叫 addColor,false 時就把 class 移除。

因為 v-bind 很常用,所以可以簡寫成下列方式,用 : 代替 v-bind

 <h1 :class="{addColor: changed}">I will change my color</h1>

動態參數

還有一個神奇的玩意兒叫動態參數,長這樣:

<a :[attributeName]="url"> ... </a>

他的意思是,比如今天attributeName 被更動為 href,那這串就等同 v-bindhref 這個屬性。



響應式基礎 (Vue 中的狀態管理)

在 React 中,我們會學到最基本的響應資料型態是用 useState 這個 hook 做管理。但 Vue 不一樣,他靠 ref reactive 以及 computed 做狀態儲存與計算。

關於 ref reactive 真的會讓初學者一開始很難摸清楚到底在幹嘛,為什麼就不能像 useState那樣一個東西決所有問題呢?

ref

ref 可以存取的所有型態的資料,但通常不會把物件和陣列的資料交給它存。

而且 ref 的操作稍微沒那麼直觀,為何這樣說呢?那是因為如果是在<script setup>或調用 ref 的值,會需要添加 refItem.value,但如果只是單純 HTML 模板,直接 {{refItem}} 就可以取到值了。

比方說,我定義一個變數 num ref 並且預設 0,然後我寫了一個方法來使每次點擊都把它的值加 1,來看看 code 和畫面渲染吧:

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

const num = ref(0)

const changeNum = () => num.value += 1
</script>

<template>
<h1>{{ num }}</h1>
<button @click="changeNum">Add one</button>
</template>


實際上我把按鈕改成 <button @click="num++">Add one</button> 也是做一樣的事,但在<script setup>中定義的方法取值必須用.value

提醒一下,定義 ref 請使用 const 宣告為常數,不然用 let 可能會一不小心被覆蓋掉。

reactive

他只能存陣列或物件,跟 ref 不同的地方,他無論在哪裡取值都不需要添加 .value

我們來創個玩玩:

<script setup>
import { reactive } from 'vue';

const people2 = reactive({
name: 'Jeremy',
gender: 'Male',
age: 20
})

const changePeople2 = () => {
people2.name = 'Lucy'
console.log('people2 changed')
}
</script>

<template>
<h1>{{ people2.name }} / {{ people2.gender }}</h1>
<button @click="changePeople2">Change!</button>
</template>

可以很清楚看到名字改變了!這樣就可以輕輕鬆鬆修改值啦!

ref 和 reactive 差異

所以 ref reactive 差在哪裡?

除了儲存類型的主要差異,和 ref 貌似被一大包東西包起來的感覺 (還需要 .value 取值),實際上操作好像就只差在要不要用 .value 取值誒。

實際上這兩個的差異在於監聽上。當有用到 watch 去監聽響應式數值是否變動時,如果 ref 今天存的是個物件,而我們變動了其中一部分的值,watch 是監聽不到的,因為這個變動太深層了!但反之 reactive所儲存的物件內部若有變動卻是監聽得到的。我們實際來看看:

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

// 用 ref 創個物件​
const people1 = ref({
name: 'Mike',
gender: 'Female',
age: 30
})
//​ 用 reactive 創個物件
const people2 = reactive({
name: 'Jeremy',
gender: 'Male',
age: 20
})

const changePeople1 = () => {
people1.value.name = 'Leo'
}

const changePeople2 = () => {
people2.name = 'Lucy'
}

// 監聽響應式的改變​
watch(people1, () => {
console.log('people1 changed')
})

watch(people2, () => {
console.log('people2 changed')
})

</script>

<template>
<div class="main-area">
<div>
<h1>Here is ref Object. Name: {{ people1.name }}. Gender: {{ people1.gender }}. Age: {{ people1.age }}</h1>
<button @click="changePeople1">click</button>
</div>
<div>
<h1>Here is reactive object Name: {{ people2.name }}. Gender: {{ people2.gender }}. Age: {{ people2.age }}</h1>
<button @click="changePeople2">click</button>
</div>
</div>
</template>

我們會發現畫面上 people1 的名字的確被更動成 Leo 了,但 watch 裡的console.log 並不會被觸發,但 people2 的 watch中的 console.log 會被觸發。

但如果現在把 watch 掛上第三個參數讓它可以執行深層監聽呢?讓我們調整一下 people1 的 watch 監聽器:

watch(people1, () => {
console.log('people1 changed')
}, {deep : true})
raw-image

嘿,可以看到 people1 的 watch中的 console.log 也被執行了,表示變動被監聽到了!

根據 Mike Chen 老師說的,其實 reactive 中包的每個項目其實都是單獨一個 ref 項目,具備這個想法後對於 Vue 官方文件的解讀,無論是這裡監聽的差異還是解構都會清楚很多。

computed

computed 顧名思義是拿來做計算的,計算什麼?計算響應式資料的變動然後來幫助畫面渲染。什麼意思?我們來看這個例子:

<script setup>
import { ref } from 'vue';
const num1 = ref(1)
const num2 = ref(2)

</script>

<template>
<h1>Num1: {{ num1 }}</h1>
<h1>Num2: {{ num2 }}</h1>
<h1>Num1 + Num2 = {{ num1 + num2 }}</h1>
</template>
raw-image

So?就是在 HTML template 中添加了一個式子嘛,計算正確啊!

對,我們可以在 HTML template 中寫各種判斷式或各式式子,實際上我之前在 React 的撰寫中也幹過這樣把邏輯寫在 HTML 架構中的事 (突然想起我還有被助教提醒...),但這樣其實程式碼結構一大會很難維護。所以 Vue 的 computed 出現了!它可以為我們做到監聽響應式變化並即時做出反應。

<script setup>
import { ref, computed } from 'vue';
const num1 = ref(1)
const num2 = ref(2)

const numPlus = computed(()=>{
return num1.value + num2.value
})
</script>

<template>
<h1>Num1: {{ num1 }}</h1>
<h1>Num2: {{ num2 }}</h1>
<h1>Num1 + Num2 = {{ numPlus }}</h1>
</template>

這樣我們可以得到跟第一段一樣的結果,但邏輯被我們抽出去了。

在學習 computed 時會有一個疑惑,所以它跟 method 差在哪裡?主要差在兩個地方:

  1. computed 只有當響應式資料變動時才會執行。
  2. computed 默認不能傳參數進去。

關於第二點,在 Vue 官方文件裡有說,computed 的使用默認只有 getter,所以你只能拿到由響應式資料計算出的回應結果,但無法透過 computed 去更動響應式資料。

但如果今天你說,我就是想在計算的時候也順便把我的資料做更動啊!好吧,可以,的確可以為 computed 加上 setter 。而至於具體操作和應用情境等我搞懂再來更新 www



參考資料

  1. Vue.js 官方網站
  2. Vue3 + Vite 快速上手 Get Startrd EP1 - 初探 Vite 專案 / Vue3 初學者應該要先知道的東西 / 剛開始寫 Composition API 會犯的錯誤 !
  3. Vue3 + Vite 快速上手 Get Startrd EP2 - 定義資料 ref、reactive、computed 深度探討
18會員
37內容數
這個專題用來存放我在學習網頁開發時的心得及知識。
留言0
查看全部
發表第一個留言支持創作者!