# VUE3 ## 路由守衛 - 全域 - beforeEach - beforeResolve - afterEach - beforeEnter - 元件區域 - beforeRouteEnter - beforeRouteUpdate - beforeRouteLeave ### 範例 全域 ```javascript const router = createRouter({ ... }) router.beforeEach((to, from) => { ... }) ``` ### 順序 | | 說明 | | :-: | :- | | beforeRouteLeave | 離開目前路由 (元件) | | beforeEach | 開始進入新路由之前 (全域) | | beforeEnter | 開始進入新路由之前 (路由) | | beforeRouteEnter | 路由尚未進入該元件時 (元件) | | beforeResolve | 路由與所搭配的元件已被解析 (全域) | | afterEach | 當路由跳轉結束後 (全域) | | beforeCreate | 元件實體建立前 (Vue Hook) | | setup | 元件實體已建立 (Vue Hook) | | beforeMount | 元件實體掛載前 (Vue Hook) | | onMounted | 元件實體掛載完成 (Vue Hook) | | beforeRouteEnter | next() 回呼函式 | | beforeRouteUpdate | 當路由更新時 (僅限同屬一個元件的情況,也可能完全不會發生) | ## Vuex ### 什麼是Vuex ? Vuex 是專為 Vue.js 應用程式開發的**狀態管理模式(State Management Pattern)**。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。 #### 什麼是狀態管理模式(State Management Pattern) ```typescript const Counter = { // state data () { return { count: 0 } }, // view template: ` <div>{{ count }}</div> `, // actions methods: { increment () { this.count++ } } } createApp(Counter).mount('#app') ``` 這個狀態管理應用包含以下幾個部分: * state:驅動應用的數據源; * view:以聲明方式將狀態映射到視圖; * actions:響應在視圖上的使用者輸入導致的狀態變化。 以下是一個表示"**單向數據流**"理念的簡單示意: ![image](https://hackmd.io/_uploads/HJNNau2VT.png) 然而,當我們的應用程式面臨多個組件共享狀態的情況時,單向數據流的簡潔性很容易被破壞: * 多個view依賴於同一state。 * 來自不同view的行為需要更改同一state。 對於問題一,傳遞參數的方法對於多層嵌套的組件將會非常繁瑣,對於兄弟組件之間的狀態傳遞無能為力。對於問題二,我們經常會採用父子組件直接引用或者通過事件來更改和同步狀態的多份拷貝。以上的這些模式非常脆弱,通常會導致難以維護的程式碼。 因此,為什麼不將組件的共享狀態抽取出來,以一個全局單例模式管理呢?在這種模式下,我們的組件樹構成了一個巨大的“視圖”,不論在樹的哪個位置,任何組件都能獲取狀態或者觸發行為! 通過定義和隔離狀態管理中的各種概念並通過強制規則維持視圖和狀態間的獨立性,我們的程式碼將會變得更結構化且易於維護。 ![image](https://hackmd.io/_uploads/rJNdxF3ET.png) 這就是 Vuex 背後的基本思想,借鑒了 Flux、Redux 和 The Elm Architecture。與其他模式不同的是,Vuex 是專門為 Vue.js 設計的狀態管理庫,以利用 Vue.js 的細粒度數據響應機制來進行高效的狀態更新。 Flux:FLUX是一個由Facebook提出來的開發架構(FLUX 是一個Pattern 而不是一個正式的框架),目的是在解決所謂的MVC在大型商業網站所存在的問題,把沒有條理跟亂七八糟的架構做一個流程規範的定義。 Redux:Redux一個用於應用程式狀態管理的開源JavaScript庫。Redux經常與React搭配運用,但其也可以獨立使用。 官方說明文件:https://vuex.vuejs.org/zh/ ### Provide/Inject ## pinia ## 元件 元件系統 (components system) 是 Vue.js 一個重要的概念與核心功能。 * 元件 (Component) 是 Vue 最主要也是最強大的特性之一,它提供了 HTML DOM 元素的擴充性, 也可將部分模板、程式碼封裝起來以便開發者維護以及重複使用。 * 傳統網頁的結構,從早期的「義大利麵式程式碼」(Spaghetti code) 把所有的東西通通往 HTML 頁面塞, 到後來將 CSS、Javascript 從 HTML 結構抽離,這是表現層級上的關注點分離。 * 但是當專案的架構越來越大,人們開始把「關注點」從表現層移到了架構層面, 思考如何將功能、邏輯抽象化,將封裝好的 UI 模組、功能重複使用,就如同樂高積木一般。 ![component1](https://hackmd.io/_uploads/SksmpO3Na.png) * 每一個被封裝後的元件單元,都含有自己的模板、樣式,與行為邏輯,並且可以被重複使用。 而在元件之中又可以含有元件,這樣由一個個元件單元組合而成的「元件樹」,就是 Vue.js 元件系統的概念。 ![component2](https://hackmd.io/_uploads/H1ybRu3Np.png) ### vue 常見的四種元件類型 #### 展示型元件 (Presentation) * 以負責呈現 UI 為主的類型,我們很單純地把資料傳遞進去,然後 DOM 就根據我們丟進去的資料生成出來。 這種元件的好處是可以提升 UI 的重複使用性 #### 容器型元件 (Container) * 這類型的元件主要負責與資料層的 service 溝通,包含了與 server 端、資料來源做溝通的邏輯, 然後再將資料傳遞給前面所說的展示型元件。 #### 互動型元件 (Interactive) * 像是大家所熟知的 elementUI、bootstrap 的 UI library 都屬於此種類型。 這種類型的元件通常會包含許多的互動邏輯在裡面,但也與展示型元件同樣強調重複使用。 像是表單等各種互動元素都算在這類型。 #### 功能型元件 (Functions) * 這類型的元件本身不渲染任何内容,主要負責將元件內容作為某種應用的延伸,或是某種機制的封裝。 像是 <transition> 、 <router-view> 等都屬於此類型。 ## VueJS VS React @StanShih ### 語法 實作一個數字加減 Vue 使用了 Template,使開發者能以直觀的方式來編寫 UI 結構。而 React 選擇使用 JSX,這是一種 JavaScript 的語法擴展。 :::success <p style="font-size: 24px " >React </p> ```javascript import React, { useState } from 'react'; function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}> Increase </button> <button onClick={() => setCount(count - 1)}> Decrease </button> </div> ); } ``` <p style="font-size: 24px " >VUE </p> ```javascript <template> <div> <p>Count: {{ count }}</p> <button @click="increase"> Increase </button> <button @click="decrease"> Decrease </button> </div> </template> <script> import { ref } from 'vue'; export default { setup() { const count = ref(0); const increase = () => { count.value++; }; const decrease = () => { count.value--; }; return { count, increase, decrease }; } } </script> ``` ::: ### 資料綁定 React 採用單向數據流,而 Vue 使用的是雙向綁定 React 遵循單向數據流,需要開發者明確地定義數據如何改變。而 Vue 的 v-model 指令使雙向數據綁定變得更簡單,但可能會使數據流動的方向變得不那麼明確。 :::info <p style="font-size: 24px " >React </p> ```javascript import { useState } from "react"; function InputComponent() { const [value, setValue] = useState(""); const handleChange = (event) => { setValue(event.target.value); }; return ( <> <input type="text" placeholder="請輸入文字" value={value} onChange={handleChange} /> <p>你輸入的文字是 {value}</p> </> ); } export default InputComponent; ``` <p style="font-size: 24px " >VUE </p> ```javascript <template> <input type="text" v-model="value" placeholder="請輸入文字" /> <p>你輸入的文字是 {{ value }}</p> </template> <script> import { ref } from "vue"; export default { name: "InputDemo", setup() { const value = ref(""); return { value, }; }, }; </script> ``` ::: :::spoiler 參考網站 - https://5xcampus.com/posts/react-vs-vue ::: ## Vite Vite(法文意為"快速的",發音/vit/,發音同"veet")是一種新型前端建構工具,能夠顯著提升前端開發體驗。 是近幾年前端業界熱門的建構工具 (build tool),它大幅地簡化了前端建構的流程與時間。 它主要由兩個部分組成: * 一個開發伺服器,它基於原生ES 模組提供了豐富的內建功能,例如速度快到驚人的模組熱更新(HMR)。 :::info HMR (hot module replacement) 當有任何的改動後,Vite 的熱模塊更新會以非常快的速度重新渲染本地的頁面,同時會保留當下的任何狀態 (state) ::: * 一套建置指令,它使用Rollup打包你的程式碼,而且它是預先配置的,可輸出用於生產環境的高度最佳化過的靜態資源。高度優化部署到生產環境 (production) 的打包結果。 ### Vite vs Webpack 假如有用過 Webpack 在大型專案的開發者,可能會有類似的經驗,就是在改動某部分程式碼後,要等數十秒才能看到畫面渲染新的版本,特別是專案中有用 TypeScript 時,每次的等待時間都讓開發體驗非常的差。 而 Vite 的出現就是要解決這類問題。Vite 不管在本地專案的冷啟動,或是專案改動時的熱模組更新,速度都非常的快。每次修改後不用等個數十秒才能看到新的畫面。這讓開發者的體驗更好,試想假如做某個專案,每次改動完都要等半分鐘,很可能讓開發者失去耐心。除此之外,這也讓回饋循環 (feedback loop speed) 更加快速,對於專案開發也是助益良多。 **延伸閱讀**:https://hackmd.io/@Jui-Cheng/SJCDBNZYj ## MVVM (Model — View — ViewModel) MVVM 中的 VM 指的是 ViewModel,一樣負責接收從 View 傳來的使用者操作事件,並使用 Model 提供的方法來處理資料。 最主要的差異在於由數據來驅動 View 的更新,當資料改變時,UI 自動更新(一般用觀察者模式實現)。 資料改變時 UI 自動更新的現象,舉一個生活上的例子大家會很有感覺: ![image](https://hackmd.io/_uploads/Hkzn2u3N6.png) MVVM 架構有幾個主要的優點: 1. 資料驅動: 事件都透過資料的變化來觸發,資料成為最關鍵的因素。 2. 下層元件不需要知道上層元件: 在 MVC / MVP 中,都需要有 View 的引用來更新 UI。但在 MVVM 中,由View 主動觀察資料,在資料變化後收到通知,而自動更新。 ViewModel 不需要知道 View 是誰。 3. 職責分離: ViewModel 中可以減少大量通知 View 的程式碼,專心的管理流程。 Activity 和 Fragment 不用儲存資料狀態,可以專心管理介面的操作和顯示,並妥善控制生命週期。 Vue 是以資料狀態操作畫面, 他扮演 MVVM 架構當中的 ViewModel, 可以有效的將負責視圖的 HTML 與負責資料狀態的 JavaScript 做一個切割。 能讓開發者專注於解決版面顯示的問題, 或是資料邏輯的 JavaScript 的問題。 例如: 使用者在畫面上(View)看到了 皮卡丘 與一個按鈕, 當使用者按下按鈕之後, 會觸發 Vue 的 changeName 方法(ViewModal), 將 pokemon 的值修改為 噴火龍(Modal), 因爲 ViewModal 與 View 是 Binding 的關係, 讓 View 的值顯示為 噴火龍。 ```javascript <body> <div id="app"> <div>{{ pokemon }}</div> <button @click="changeName">就決定是你了</button> </div> <script> var app = new Vue({ data: { pokemon: '皮卡丘', }, methods: { changeName() { this.pokemon = '噴火龍'; }, }, }); </script> </body> ``` :::spoiler 參考網站 - https://medium.com/ken-do-everything/mvvm-%E6%9E%B6%E6%A7%8B%E7%AF%87-%E6%9B%B8%E8%AE%80%E5%BE%97%E5%A4%9A-%E4%BA%BA%E8%87%AA%E7%84%B6%E5%B0%B1%E5%A5%BD%E7%9C%8B%E8%B5%B7%E4%BE%86-4fd595581e7f - https://medium.com/neptune-coding/vue-js-mvvm-%E7%9A%84%E6%A6%82%E5%BF%B5-983bdc5da207 ::: ## Vue2 vs. Vue3 ### 1. 生命週期變化 ![image](https://hackmd.io/_uploads/HyK0TO3VT.png) ### 2. ![image](https://hackmd.io/_uploads/H109COhNT.png) ### 3. 使用proxy代替defineProperty Object.defineProperty()語法 ```javascript Object.defineProperty( Obj, 'name', { enumerable: true, //可列舉 configurable: true, //可配置 // writable:true, //跟可配置不能同時存在 // value:'name', //可寫死直 get: function () { return def }, set: function ( val ) { def = val } } ) ``` Proxy的語法 ```javascript //兩個引數,物件,13個配置項 const handler = { get: function(obj, prop) { return prop in obj ? obj[prop] : 37; }, set:function(){ }, ...13個配置項 }; const p = new Proxy({}, handler); p.a = 1; p.b = undefined; console.log(p.a, p.b); // 1, undefined console.log('c' in p, p.c); // false, 37 ``` defineProperty只能繫結首次渲染時候的屬性,Proxy需要的是整體,不需要關心裡面有什麼屬性,而且Proxy的配置項有13種,可以做更細緻的事情,這是之前的defineProperty無法達到的 4. Vue composition API * 程式能依功能分類使用,增加可讀性; * 封裝功能,可跨元件使用,增加複用性; * 提供更好的 TypeScript 支持。 ## Props(屬性) & Emit(事件) **Props**是一種用於將數據從父組件傳遞到子組件的機制。通常,父組件可以將數據作為屬性傳遞給子組件,子組件可以通過props接收這些屬性。 * 父組件 ```javascript <template> <ChildComponent :message="parentMessage" /> </template> <script> import ChildComponent from './ChildComponent.vue'; export default { components: { ChildComponent, }, data() { return { parentMessage: 'Hello from parent!', }; }, }; </script> ``` * 子組件 ```javascript <template> <div> <p>{{ message }}</p> </div> </template> <script> import { defineComponent, PropType } from 'vue'; export default defineComponent({ props: { message: { type: String as PropType<string>, required: true, }, }, }); </script> ``` **Emit**是一種用於在子組件中觸發事件,並由父組件捕獲的機制。通常,當子組件中的某些操作需要通知父組件時,可以通過$emit方法觸發一個自定義事件。 * 子組件 ```javascript <template> <button @click="sendMessage">Click me!</button> </template> <script> import { defineComponent, ref } from 'vue'; export default defineComponent({ setup() { const sendMessage = () => { emit('message-clicked', 'Hello from child!'); }; return { sendMessage, }; }, }); </script> ``` * 父組件 ```javascript <template> <ChildComponent @message-clicked="handleMessageClick" /> </template> <script> import ChildComponent from './ChildComponent.vue'; import { defineComponent } from 'vue'; export default defineComponent({ components: { ChildComponent, }, methods: { handleMessageClick(message) { console.log(message); // 輸出: Hello from child! }, }, }); </script> ``` ## Directives 指令是 Vue 的一個基本面,充當範本語法中的特殊標記,指示框架對 DOM(文件物件模型)元素執行某些操作。它們以 v- 為前綴,表示它們是 Vue 提供的特殊屬性。指令提供了一種使用自訂行為擴充 HTML 的強大方法,對於使用 Vue 建立動態、互動式 Web 應用程式至關重要。 ## Vue特點 - 虛擬DOM #### 使用虛擬DOM + 優秀的Diff算法,盡量重複使用DOM節點 Vue有重複使用DOM節點的特點,在新增程式後,Vue會利用虛擬DOM比較上次DOM和這次DOM的差別,只渲染增加的部分,不會整個畫面重新渲染 ![image](https://hackmd.io/_uploads/ryvOSK3VT.png) **更詳細了解虛擬DOM**:https://ithelp.ithome.com.tw/articles/10233588 ## 議題討論: 如果在系統上實作需要克服什麼問題? ![2023-11-23-1556](https://hackmd.io/_uploads/rkAgqY34T.png) https://excalidraw.com/#json=ZiHLTcM9Mzpr2VBo8kY88,EeL25P8qw_7QLmUrO47z8g