[TOC]
# Vue 3 composition API
## Vue2 vs Vue3
### 一、以 Vue source code 為切入點 談兩個版本的差異
:::danger
此區段提到的內容博大精深,喜歡燒腦的勇士歡迎至vue mastery學習或是google其他高手寫的文章,因為資源很多,就不附上特定連結惹,這邊先預祝大家越挫越勇 :fire:
:::
>Evan You 本人表示:
>> (1) ==**因應瀏覽器開始支援最新JS語法(ES6)**==:[color=#907bf7]
>> ex vue2用defineProperty,vue3用proxy來撰寫reative objects( = IE掰餔)
>> (2) ==**在渲染DOM的效能上仍有很多改善空間**==
>> vue3在渲染虛擬DOM節點的function上進行了重構,讓佔用CPU的效能不到vue2的十分之一
>> (3) ==**跟typescript真的太不合**==
>> vue2的寫法完全沒有考慮要兼容ts檔案,但typescript已經大流行,所以必須跟進
>> (4) ==**打包後體積超大**==
>>ex vue2會直接把透過npm安裝的所有dependencies打包,導致沒用到的也會一起打包進去,改善後,vue3打包體積小於vue2的一半
### 二、以 使用Vue的開發者為切入點 談vue3提供的新設計模式
::: info
即使將專案建置在vue3底下,我們仍然可以使用熟悉的Vue2 API撰寫程式碼,只是透過Vue3新增的API,讓開發者可以根據情境選擇不同的設計模式,以下會先介紹Vue3新增了哪些API,再介紹Vue3 API帶來的新設計模式-composition
:::
#### 1. Vue3 新增的API
```javascript
//跟reative有關的api
import {ref, reactive, computed ,watchEffect,watch} from "vue";
```
```javascript
//跟life cycle有關的api
import {
onMounted,
onBeforeMount,
onBeforeUpdate,
onUpdated,
onUnmounted,
onBeforeUnmount,
} from "vue";
```
>以上新增的API都需要在 ==**vue3提供的全新生命週期setup(等於原先的beforeCreate和created)內使用**== ,在setup以外的地方,都還是使用原來的vue2 API[color=#907bf7]
> **computed和watch** 是原本vue2就有的用法,只是在vue3可以透過引入的方式,在setup內使用[color=#907bf7]
> **ref和reactive** 是vue3在setup內創建reactive object的api[color=#907bf7]
> **watchEffect** 和watch一樣具有監聽效果,不過watchEffect可以監聽整個funciton,只要function內任一個reactive object更動,watchEffect就會自己重跑一次![color=#907bf7]
>生命週期的部分 **多了setup**(等於beforeCreate和created), **如果要在setup內調用生命週期,要記得加上'on'** [color=#907bf7]
#### 2. vue3 新API帶來了什麼樣的設計模式? 如何善用vue3 新API讓程式碼更好維護?
>上面介紹新API時有提到,==**新API都必須放在setup裡面使用**==,也就是不同於Vue2主打 **「拆成data/methods/computed/watch等區段」** 的設計模式,Vue3讓我們可以 **「把所有的東西一次組好塞進setup」** 裡面,請見下面這張圖[color=#907bf7]

>看到這張圖大家可能會有個疑惑[color=#907bf7]
::: danger
修但幾勒!啊把一大坨程式碼塞進去setup裡面??這個新設計模式也太可怕了ㄅ ??!!
:::
```!
我只能說,這根據情況及工程師喜好有所不同,不過有個絕對的好處是,當程式碼拖到像下圖這~麼~長~的時候,光是想到在code review和開發時,明明都是同一個功能,卻要上上下下跳來跳去的講/寫code,setup就有足夠的使用價值惹(拭淚)
```

)
>上圖中,大家可以看到剛剛提到的vue的兩個設計模式名稱:「options」和「composition」[color=#907bf7]
>vue2希望開發者將元件切成適當的大小,然後「自行選用」需要用到的官方api, **「以官方api分區塊」**,方便程式碼閱讀[color=#907bf7]
>vue3希望開發者可以將同一功能的東西「組合」在一起, **「以功能分區塊」**,結合vue2的設計模式,達到 **方便共用及閱讀** 的效果
[color=#907bf7]
```!
*官方api = data / methods / computed / life cycle ...
```
:::success
了解vue3帶來的 **以功能分區塊的「組合」設計模式** 後,接下來告訴大家在什麼情境下使用這個設計模式可能會好於vue2的options api設計模式
:::
:::spoiler 1. 單一component過度肥胖時
```!
基本上好的component切分是vue設計中很重要的一環,所以這種情況應該是幾乎不會發生才對,但還是會有遇到的時候,比如說,麻醉系統的remark表格(再度拭淚)
```

```!
大家可以看到它其實就是一個三列的表格,只以表格的渲染來看,它的確該被放在一個component裡面,但偏偏這三列各自有不同的複雜功能,因此造成remark元件非常非常非常的臃腫,為了提高易讀性,開發時在這個元件裡寫了很多註解,不過有了setup以後!就可以使用composition api,讓開發者可以非常清楚的看出:
```
###### ==「什麼是註記要用的」、「什麼是手術擺位要用的」、「什麼是兩者共用的」==
:::
:::spoiler 2. 有多個元件共用function、data時
```!
因為可以在setup引入任意一個我們寫好的composition function,所以共用方便性及易讀性大大大大大大幅提升惹!!!
```
:::
vue2 to vue3
[https://v3.vuejs.org/guide/migration/introduction.html#overview]
## 實作 Vue3 Composition API
:::info
再來要透過一個簡單的小範例,讓大家知道composition api究竟怎麼用,順便比較與vue2寫法的差異!
:::
>這邊要提醒的是,此次的範例完全可以只用vue2的設計模式設計,但為了讓大家簡易入門,所以才使用了vue3 composition api,正常情況下,這個簡單範例應該是使用vue2設計模式,程式碼就夠好懂了喔~
[color=#907bf7]
>先來個作品預覽圖 希望各位等等都會有水噹噹的todolist可以用[color=#907bf7]

:::success
耶嘿! 那麻煩大家幫我git clone專案下來!我們要來開始實作囉!!!!!!
:::
>git clone https://github.com/TingYuWong/vue2-to-vue3.git [color=#907bf7]
```javascript
// 專案剛下載下來時 要記得用 npm install 把 dependencies 都載下來
npm install
```
```javascript
// 用@vue/cli建立的專案 運行vue專案的指令
npm run serve
```
>成功run起來以後,vue會在terminal給網址,請ctrl+click點擊url,網頁就會咻地跳出來了喔[color=#907bf7]
### 第一步:製作composition function
:::info
以下教學為個人研究過後覺得最好維護的寫法,有興趣的人們可以自己去查詢其他種設計方法~
:::
>請大家在src路徑點右鍵,新增use資料夾[color=#907bf7]
>在use資料夾裡面新增todo.js

>點兩下todo.js,我們要在這裡製作composition function[color=#907bf7]
>首先import我們需要用到的api: **reactive 和 toRefs** [color=#907bf7]
```javascript
import { reactive, toRefs } from 'vue'
```
>再來命名我們要製作的function,慣例為依照 ==**use<功能名稱>**== 取名,所以我們要寫一個叫作 ==**useTodos**== 的function [color=#907bf7]
```javascript
// 等等要輸出使用 所以前面幫我多加個export default喔
export default function useTodos(){
// 這兩行代表 我們會從瀏覽器的儲存空間抓資料 有抓到就用瀏覽器的
// 沒抓到就用預設的defaultData
const defaultData = [{
done: false,
content: 'Write a blog post'
}]
const todosData = JSON.parse(localStorage.getItem('todos')) || defaultData
}
```
>使用reactive,把所有我們需要的東西都丟進去![color=#907bf7]
>使用reactive時,其實就是在創建一個響應式object,因此reactive要這樣用: ==reactive **({** 我們要放的東西 **})**==[color=#907bf7]
```javascript
// 建立reative object 存到變數todolist裡
const todolist = reactive({
})
```
>首先讓我們把需要的 ==data== 放進去[color=#907bf7]
```javascript
const todolist = reactive({
//data
newTodo: '',
todos: todosData,
})
```
>再來把需要的 ==function== 放進去[color=#907bf7]
>這邊要注意的是,以往在vue2我們都是用 ==this.<data/methods名稱>==,但在這邊因為我們自行創建了響應式物件,所以要變成 ==**<reactive obj variable name>** .<data/methods名稱>== [color=#907bf7]
>大家可以看到addTodo func裡面,我們是寫 ==**todolist**== .newTodo和 ==**todolist**== .saveTodo() [color=#907bf7]
```javascript
// 建立reative object 存到變數todolist裡
const todolist = reactive({
//data
newTodo: '',
todos: todosData,
//methods
saveTodo: ()=>{
localStorage.setItem('todos', JSON.stringify(todolist.todos))
},
addTodo: ()=>{
if(todolist.newTodo){
todolist.todos.push({ done: false, content: todolist.newTodo})
todolist.newTodo = ''
todolist.saveTodo()
}
},
doneTodo: (todo)=>{
todo.done = !todo.done
todolist.saveTodo()
},
removeTodo: (index)=>{
todolist.todos.splice(index, 1)
todolist.saveTodo()
},
})
```
>寫完reactive object以後,==**我們要用toRefs「解構」它**==,解構是什麼意思呢,大家可以先想成把一個大包裹裡面包的幾個小箱子,拿出來一個一個放在桌上,方便之後一個一個交貨給我們的元件[color=#907bf7]
>toRefs的寫法為: ==**return { ...toRefs(reative obj變數名稱) }**== [color=#907bf7]
```javascript
// todo.js的檔案結構如下:
// 一個function裡面包著以下三個變數 並return解構後的reacitve object
export default function useTodos(){
const defaultData = ...
const todosData = ...
const todolist = reactive({...})
return { ...toRefs(todolist) }
}
```
### 第二步:在元件中引入
>寫好composition funciton以後,我們就可以在任意一個component引入使用惹![color=#907bf7]
>請大家幫我點進 ==**src/components/todolist.vue**==[color=#907bf7]
>可以看到script這個區塊的東西,其實我們剛剛在composition function裡面都寫過了,所以這邊我們先註解起來,然後放入剛剛寫好的composition function![color=#907bf7]
```javascript
<script>
import useTodos from '../use/todo'
export default {
name: 'todolist',
}
</script>
```
>import function以後,我們要把剛剛解構出來的小箱子放進setup裡面![color=#907bf7]
```javascript
<script>
import useTodos from '../use/todo'
export default {
name: 'todolist',
setup(){
// 把小箱子一個個放在桌上
const { newTodo, todos, saveTodo, addTodo, removeTodo, doneTodo } = useTodos();
// 把小箱子們交給元件
return {
newTodo,
todos,
saveTodo,
addTodo,
removeTodo,
doneTodo
}
},
}
</script>
```
## 就是這樣! 下課!
::: success
登愣!!!!這樣就大功告成惹!!!!
:::
>點進去vue開發者工具,可以看到顯示的不再是data,而是setup![color=#907bf7]


>小總結:[color=#907bf7]
>vue2 -> vue3
(1)源碼上的改善讓效能大幅提昇,同時宣佈不支援IE
(2)除了vue2的options設計模式,vue3多提供了composition設計模式,讓開發者自行選擇
[color=#907bf7]
>vue3 composition api best practice
(1)建立use資料夾,依照功能分出不同的js檔案,裡面各自撰寫該功能需要的data/function等,功能命名慣例為use<功能名稱>
(2)用reactive包入所有data/function等,並用toRefs解構後,輸出至要用的元件
(3)在元件內的setup返回解構後的物件,就完成惹!
[color=#907bf7]
::: info
那今天的課程就到這邊結束了,謝謝大家的參與 :100:
:::
- [資料來源一 Evan You談rewrite vue的原因](https://increment.com/frontend/making-vue-3/)
- [資料來源二 vue3 vs react hook](https://zhuanlan.zhihu.com/p/133819602)
- [資料來源三 vue3 composition api官方文件](https://v3.vuejs.org/guide/composition-api-introduction.html)