[重構倒數第29天] — Vue2 Option API轉換 Vue3 Composition API
Vue2 重構 Vue3 的時候,我們可以從以下幾個地方著手!
前言
該系列是為了讓看過Vue官方文件或學過Vue但是卻不知道怎麼下手去重構現在有的網站而去規畫的系列文章,在這邊整理了許多我自己使用Vue重構很多網站的經驗分享給讀者們。
Vue3 的 Composition Api 可以說是改變了原本的 Option Api 撰寫上面的方式,還有撰寫 Vue 上面的一些思維的變動。
所以今天如果我們接手了一個 Vue2 的專案要升級成 Veu3 Composition Api 的話,我們可以先從甚麼地方下手,會需要注意那些地方,然後要怎麼去改寫 Option Api,我們馬上來看一下。
1. 先定義 setup 函式
跟以往的 Option Api 不同,我們可以在 setup 函式裡面去定義我們所有的東西,包含data
、methods
、computed
、lifecycle
等方法。
<script>
export default {
setup(){
return {}
}
}
</script>
2. 檢查有多少 data
我們在改寫的時候會先來看一下有多少被定義的資料,這邊是我們在 Option Api 裡面定義的資料的部分。
<script>
export default {
data(){
return {
userData: {
name: '',
age: '',
address: '',
},
isOpen: false,
products: []
}
}
}
</script>
我們接下來來用 Vue3 的方式來改寫 ,就會像這樣。
<script>
import { ref, reactive } from "vue";
export default {
setup() {
const isOpen = ref(false)
const products = ref([])
const userData = reactive({
name: '',
age: '',
address: '',
})
return {}
},
};
</script>
在這邊你會看到我定義資料是用 ref
跟 reactive
來定義的,關於我們該如何選用 ref
跟 reactive
呢 ? 在大多數的情況下兩者皆可以互相替換使用,主要取決於個人喜好或是團隊習慣,根據具體情況來決定要使用哪個好,關於 ref
跟 reactive
的差異我們來看一下 :
ref
: 可以使用任何型態的資料,但是不會對Object
或是Array
內部的屬性做監聽。reactive
: 只接受Object
或Array
,可以做深層的監聽,以及取資料的時候不用使用.value
,但是如果對 reactive 的 Object 使用解構的方式取得內容,就會失去了 Vue 更新綁定的機制。
我自己個人一般的情況下能盡量使用 ref
就使用ref
,可以更精準地去控制資料的更新,很多人不習慣用 .value
的方式,但是 .value
的方式可以讓開發者更明確知道這段 code 再進行資料的更新。
更多具體的細節可以參考我之前寫的這篇文章 ref 跟 reactive 我該怎麼選 !?
3. methods 與 this
一般我們要定義 methods 會在 methods 這個欄位裡面去定義它的 function,然後要 get
、set
可以透過 this
來使用,這個 this
就是指向 Vue 本身。
<script>
export default {
data(){
return {
userData: {
name: '',
age: '',
address: '',
},
isOpen: false,
products: []
}
},
methods: {
handleTroggle(){
this.isOpen = !this.isOpen
},
setProducts(prod){
this.products = prod
},
showUserKey(key){
this.userData.name = 'mike'
console.log(this.userData.name)
}
}
}
</script>
但是改成 Composition Api 的方式就不需要寫在 methods 裡面,也不需要管 this 指向的部分,整體下來看起來乾淨很多。
<script>
import { ref, reactive } from "vue";
export default {
setup() {
const isOpen = ref(false)
const products = ref([])
const userData = reactive({
name: '',
age: '',
address: '',
})
const handleTroggle = () => {
isOpen.value = !isOpen.value
}
const setProducts = (prod) => {
products.value = prod
}
const showUserKey = (key) => {
userData.name = 'mike'
console.log(userData.name)
}
return {}
},
};
</script>
4. 換掉 mixins,把共用邏輯抽出來
以前我們會把共用的 function 或是 data 給放到 mixins,但是 mixins 最讓人詬病的是對於來源的查找很不明確,只能靠 coding style 來做區別,所以我們要透過 Composition Api 的方式來處理共用的 function 。
· 使用 mixin
先新增一個 myMixin.vue
的檔案
// myMixin.vue
<script>
export default {
data(){
return {
point: 0
}
},
methods: {
addPoint() {
this.point += 1
}
}
}
</script>
然後再我們的 Component 去載入 myMixin.vue
// app.vue
<script>
import myMixin from "./myMixin.vue";
export default {
mixins: [myMixin],
data(){
...
},
methods: {
...
}
}
</script>
<template>
<div id="app">
<h1>Point: {{ point }}</h1>
<button @click="addPoint">click</button>
</div>
</template>
上面就是一般我們使用 mixins
的方式,雖然方便,但是如果多個 Component 的引入跟使用也會造成管理上的不方便,所以接下來我們使用 composition Api 來包裝它。
· 使用 Composition Api
先新增一個名叫 useAddPoints.js
的檔案
import { ref, readonly } from "vue"
export function useAddPoints(){
const point = ref(0)
const addPoint = () => {
point.value += 1
}
return {
point: readonly(point),
addPoint
}
}
這邊使用了 readonly
最主要是希望這個傳出去的值是只可以get
讀取的,所以在這邊把它給用 readonly
包起來,關於 readonly
的細節可以參考官方的文件
官方文件 : https://v3.vuejs.org/api/basic-reactivity.html#readonly
然後要使用的話就像是這樣,非常的簡單明瞭,完全解決了使用 mixins
上面來源不清楚的問題。
<script>
import { useAddPoints } from "../composition/useAddPoints.js"
export default {
setup() {
const { point, addPoint } = useAddPoints();
return {
point,
addPoint
}
},
};
</script>
<template>
<div id="app">
<h1>Point: {{ point }}</h1>
<button @click="addPoint">click</button>
</div>
</template>
5. lifecycle (生命週期) 的更新
我們在 Option Api 裡面有許多的 lifecycle
可以用,但是到了 composition Api
裡面就有了一些變化,所以今天讓我們來稍微看一下這些變化,也好在轉移程式碼的時候避免發生一些問題。
<script>
export default {
beforeCreate(){},
created(){},
beforeMount(){},
mounted(){},
updated(){},
destroyed(){}
}
</script>
但是在 composition Api
裡面我們要從 vue 裡面把 lifecycle
取出來在setup
使用。
<script>
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onUnmounted } from "../composition/useAddPoints.js"
export default {
setup() {
onBeforeMount(()=> {})
onMounted(()=> {})
onBeforeUpdate(()=> {})
onUpdated(()=> {})
onUnmounted(()=> {})
return {}
},
};
</script>
不過這邊要特別注意,原本 Veu2 裡面 destroyed
這個生命週期函式再 Vue3 裡面 改名叫做 onUnmounted
,然後原本的 beforeCreate
、created
沒了, 現在的 setup
這個函式就等同於 beforeCreate
、created
這兩個效果一樣,合再一起了。
更多完整的生命週期函式可以參考官方文件的部份
https://v3.vuejs.org/guide/composition-api-lifecycle-hooks.html#lifecycle-hooks
所以我今天如果是要在 created
裡面去打 API,我就可以直接在 setup
裡面去做操作。
<script>
import { ref } from 'vue'
import axios from "axios"
export default {
setup() {
const userData = ref([])
// 等同於再 created 執行
axios.get("https://test.demo.com/api/getUser").then((res)=> {
userData.value = res.data
})
return {
userData
}
},
};
</script>
或是今天你要移除原生 JS 監聽的時候,就可以改用 onUnmounted
<script>
import { onMounted, onUnmounted } from 'vue'
import axios from "axios"
export default {
setup() {
const handleWinReSize = (e) => {
console.log(e)
}
onMounted(()=> {
window.addEventListener("resize", ,handleWinReSize)
})
// 等同於 Vue2 的 destroyed
onUnmounted(()=> {
window.removeEventListener("resize", ,handleWinReSize)
})
return { }
},
};
</script>
6. Props 跟 Emit 的使用
以前在 Option Api 上面的時候都要透過 this 的方式來取得 props 還有使用 emit,但是在 Composition Api 裡面可以統一透過 setup 函式來取得這兩個物件。
props
定義好 props 傳入的內容,我們就可以從 setup 正確的取得 props ,它會從 setup 的第一個參數傳入。
<script>
import { onMounted } from 'vue'
export default {
props: {
userType: {
type: String,
default: "user"
}
},
setup(props) {
onMounted(()=> {
console.log(props.userType)
})
return { props }
},
};
</script>
<template>
<h1>{{ props.userType }}</h1>
</template>
emits
emit 再 setup 的第二個參數的物件裡面,我們可以透過解構的方式直接取出,然後使用。
<script>
export default {
emits: ['userName'],
setup(props, { emit }) {
const getUserName = (name) => {
emit("userName", name)
}
return { getUserName }
},
};
</script>
<template>
<button @click="getUserName('mike')">mike</button>
<button @click="getUserName('jacky')">jacky</button>
<button @click="getUserName('andy')">andy</button>
<button @click="getUserName('scars')">scars</button>
<button @click="getUserName('ash')">ash</button>
</template>
先告一個段落
重構這件事情不是一個小工程,它裡面有很多需要注意的細節在裡面,包括如何讀懂人家的邏輯以及理解這個功能開發上面的前因後果,還有就是你的經驗的展現,各種問題、各種情境,除了程式碼語法版本上面的差異外,還有很多都是要下功夫的,再後面的章節,我會再一一的跟大家介紹關於重構專案上面其他需要注意的地方,還有我的思考規劃的方式,以及選用技術的考量等。
此文章同步更新於 第 13 屆 iT 邦幫忙鐵人賽
https://ithelp.ithome.com.tw/articles/10259305
那如果對於Vue3不夠熟的話呢?
Ps. 購買的時候請登入或註冊該平台的會員,然後再使用下面連結進入網站點擊「立即購課」,這樣才可以讓我獲得更多的課程分潤,還可以幫助我完成更多豐富的內容給各位。
我有開設了一堂專門針對Vue3從零開始教學的課程,如果你覺得不錯的話,可以購買我課程來學習
https://hiskio.com/packages/AYR5m7VR3
如果對於JS基礎不熟的朋友,我也有開設JS的入門課程,可以參考這個課程
https://hiskio.com/packages/Q9R4OYoyD
訂閱Mike的頻道享受精彩的教學與分享
Mike 的 Youtube 頻道
Mike的medium
MIke 的官方 line 帳號,好友搜尋 @mike_cheng