當初在找工作的時候在 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 專案了。
講語法前,先來說一下 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。
使用上有兩個重點:
{{…}}
包裹變數:如 <span>Message: {{ msg }}</span>
中,msg 代表的可能是個響應式變數,當他變動時渲染也會跟著更新。v-html
包裹 HTML 語法:因為 {{…}}
會把數據解釋為純文本,如果你的變數代表的是一段 HTML,那必須為你的 tag 添加 v-html
屬性並掛上的 HTML 變數,這裡我直接放官網範例:如果今天變數擺放的區域是在 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-bind
上 href
這個屬性。
在 React 中,我們會學到最基本的響應資料型態是用 useState
這個 hook 做管理。但 Vue 不一樣,他靠 ref
和 reactive
以及 computed
做狀態儲存與計算。
關於 ref
和 reactive
真的會讓初學者一開始很難摸清楚到底在幹嘛,為什麼就不能像 useState
那樣一個東西決所有問題呢?
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
可能會一不小心被覆蓋掉。
他只能存陣列或物件,跟 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
貌似被一大包東西包起來的感覺 (還需要 .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})
嘿,可以看到 people1 的 watch
中的 console.log
也被執行了,表示變動被監聽到了!
根據 Mike Chen 老師說的,其實 reactive
中包的每個項目其實都是單獨一個 ref
項目,具備這個想法後對於 Vue 官方文件的解讀,無論是這裡監聽的差異還是解構都會清楚很多。
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>
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 差在哪裡?主要差在兩個地方:
computed
只有當響應式資料變動時才會執行。computed
默認不能傳參數進去。關於第二點,在 Vue 官方文件裡有說,computed
的使用默認只有 getter
,所以你只能拿到由響應式資料計算出的回應結果,但無法透過 computed
去更動響應式資料。
但如果今天你說,我就是想在計算的時候也順便把我的資料做更動啊!好吧,可以,的確可以為 computed
加上 setter
。而至於具體操作和應用情境等我搞懂再來更新 www