[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] ![vue3_Composition_api](https://i.imgur.com/89zcQaE.png) >看到這張圖大家可能會有個疑惑[color=#907bf7] ::: danger 修但幾勒!啊把一大坨程式碼塞進去setup裡面??這個新設計模式也太可怕了ㄅ ??!! ::: ```! 我只能說,這根據情況及工程師喜好有所不同,不過有個絕對的好處是,當程式碼拖到像下圖這~麼~長~的時候,光是想到在code review和開發時,明明都是同一個功能,卻要上上下下跳來跳去的講/寫code,setup就有足夠的使用價值惹(拭淚) ``` ![option_api](https://i.imgur.com/Rv0QFWB.png) ) >上圖中,大家可以看到剛剛提到的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表格(再度拭淚) ``` ![ORMS_remark](https://i.imgur.com/2jKTTIH.png) ```! 大家可以看到它其實就是一個三列的表格,只以表格的渲染來看,它的確該被放在一個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] ![todolist_preview](https://i.imgur.com/gL4T3SW.png) :::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 ![path](https://i.imgur.com/BQKQDvt.png) >點兩下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] ![vue_devtool_3](https://i.imgur.com/UvGaDoL.png) ![vue_devtool_2](https://i.imgur.com/Ppv0yNg.png) >小總結:[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)