前面的幾篇文章已經大致介紹了 Vue 3 的常用 API,現在綜合起來做一個實戰演練
配合完整代碼食用更香哦,專案地址:https://github.com/wisewrong/test-vue3-demo
一、初始化
首先通過 Vue-CLI 創建一個 Vue 3 專案,詳細流程可以參考《Vue3.x 從零開始(一)》
vue create test-vue3-demo



勾選 TypeScript、Router、Vuex,版本選用 Vue 3.x,其他的選項可以自行選擇,拿不準就直接回車選擇默認
初始化完成后的專案是這樣的:

store 目錄用來維護基于 Vuex 開發的狀態倉庫
router 目錄維護基于 vue-router 開發的路由配置
main.ts 是專案的入口檔案,在這里將 Router 和 Vuex 載入專案中:

二、頭部導航( Router )
首先需要創建頭部導航 <header> 組件,header 屬于公共組件,可以放到 components 目錄下
header 上有 Home 和 About 兩個頁面的導航入口,點擊導航可以跳轉到對應的頁面

這個功能可以通過 vue-router 提供的 <router-link> 來實作

<router-link> 是經過封裝的 <a> 標簽,它需要接收一個路由地址 to,類似于 <a> 標簽的 href
<router-link to="/home">Home</router-link>
如果目標路由地址配置了組件,就能在父組件的 <router-view> 中渲染對應組件
路由的組態檔是 src/router/index.ts ,可以配置路由資訊,包括路由地址和對應的組件

不過 <router-link> 只適合導航選單這種【只需要跳轉頁面,不需要做其他操作】的場景
更多的時候我們需要在函式中進行路由跳轉,這時候可以使用 this.$router.push()

三、登錄框彈窗( teleport + slot )

首先來完成彈窗組件 <modal>
從業務上來講,這個彈窗組件是在 <header> 上打開的,也就是說 <header> 會是 <modal> 的父組件
如果按照傳統開發組件的方式,<modal> 會渲染到父組件 <header> 的 DOM 節點下
但從互動的層面來說,彈窗是一個全域性的強互動,組件應該渲染到外部,比如 body 標簽
在 Vue 3 中提供了一個新的解決方案 teleport
在組件中用 <teleport> 組件包裹需要渲染到外部的模板,然后通過 to 指定渲染的 DOM 節點
<teleport to="body"> <div> <!-- 組件內容 --> </div> </teleport>
to 接收一個可以被 querySelector 識別的字串引數,用于查找目標 DOM 節點,該 DOM 節點必須在組件外部

這里將 modal 組件渲染到了body 標簽
上面的代碼還用到了插槽 <slot>
這個標簽允許父組件向子組件插入自定義的模板內容,在 <modal> 組件中可以讓父組件編輯彈窗的內容

如果組件中需要配置多個 <slot> 標簽,還可以用 name 來給 <slot> 命名
<div class="modal-header"> <slot name="header"> <!-- 這里是 slot-header 的默認模板 --> <span class="modal-title">{{title}}</span> <button class="modal-close"></button> </slot> </div>
然后在父組件中通過 <template v-slot:name> 向指定的 slot 插入內容
<template v-slot:header> <div> 這里是 slot-header 的內容 </div> </template>
四、完成彈窗表單( $refs + Vuex )
接下來開發登錄窗的表單組件 <sign-in-form>
組件的內容十分簡單,就是兩個輸入框 <input />,不多介紹,重點在于獲取表單資料
由于這個 <form> 組件是 <header> 的子組件,所以我們需要在 <header> 獲取 <form> 的資料并提交

Vue 中可以通過 ref 屬性獲取自定義組件的實體
比如上面的代碼就在 <sign-in-form> 組件上指定了 ref="signInForm"
然后就能在 header 組件中通過 this.$refs.signinForm 獲取到表單組件的實體,并直接使用它的 methods 或者 data
// 沒有找到適合 $ref 的型別斷言,只好用 any const data = https://www.cnblogs.com/wisewrong/p/(this.$refs.signInForm as any).getValue(); // getValue 是 signInForm 組件中的 methods
現在獲取到了登錄資訊,正常來說需要用登錄資訊請求登錄介面,如果用戶名和密碼正確,介面會回傳用戶資訊
這里我們就跳過請求介面的程序,直接把登錄資訊當做用戶資訊
用戶資訊對于整個專案來說是一個共用資訊,我們可以選擇暫存在 localStorage 或 sessionStorage 中,也可以使用 Vuex 來管理
如果在使用 Vue-CLI 創建專案時勾選了 Vuex,就能在 src/store/index.ts 中維護公共變數和方法
然后在組件中通過 this.$store 來使用 Vuex 提供的 API
Vuex 中有 State、Getter、Mutation、Action、Module 五個核心屬性
其中 State 就像是 Vue 組件中的相應資料 data,Getter 類似于計算屬性 computed
然后 Mutation 和 Action 都可以看做 methods,區別在于:
Mutation 是同步函式,用來更新 state(在嚴格模式下只能通過 mutation 來更新 state)
// Vuex const state = { user: 'wise wrong' }; const mutations = { // 所有 mutation 的第一個引數都是 state,后面的引數在呼叫時傳入 updateUser(state, payload) { state.user = payload; } }; // 組件中通過 commit 來呼叫 mutations export default { // ... methods: { handler() { this.$store.commit('updateUser', 'new user'); } }, };
而 Action 可以看做 Mutation 的父級,用來提交 Mutation,而且可以包含異步函式
// Vuex const actions = { // action 的第一個引數是 context,其中包含 commit,用來呼叫 mutation fetchUser(context, payload) { fetch('/api', payload) .then((res) => { // 呼叫 mutation context.commit('updateUser', res.data); }) .catch() } } // 組件中通過 dispatch 來呼叫 action export default { // ... methods: { handler() { this.$store.dispatch('fetchUser', {id: 123}); } }, };
回到我們的專案上來,由于我們用的是 TypeScript,所以需要提前定義 state 的型別
// 用戶資訊 export interface UserState { user: string; password: string; } // state 的根型別 export interface RootState { userInfo: UserState; }
然后創建 state.ts 和 mutations.ts


然后在組件中通過 commit 呼叫 mutation 以更新用戶資訊:

由于在 Vuex 4 中洗掉了對全域屬性 $store 的型別支持,所以上面的截圖中 $store 被標紅,代碼也無法運行

為解決該問題,可以在 src 目錄下創建一個 shims-vuex.d.ts 檔案,手動宣告 $store
import { Store } from 'vuex';
declare module "@vue/runtime-core" {
interface ComponentCustomProperties {
$store: Store;
}
}
但我更推薦使用 Vue 提供的輔助函式 map
對于 state,我們可以在 computed 中使用 mapState 將需要的 state 映射到當前組件
import { mapState } from 'vuex';
export default {
// ...
computed: {
// 組件本身的計算屬性
localComputed () { /* ... */ },
// 使用物件展開運算子將 state 混入當前組件
...mapState([
'userInfo', // 以陣列的形式傳入 state 的鍵名
])
},
methods: {
test() {
// 以計算屬性的形式使用 state
console.log(this.user);
}
}
}
同樣的,對于 mutations 可以通過 mapMutations 混入 methods 中
import { mapMutations } from 'vuex';
export default {
// ...
methods: {
// 組件本身的方法
test() {
// 以 methods 的形式呼叫 mutation
this.updateUserInfo();
},
...mapMutations([
// 混入名為 updateUserInfo 的 mutation
'updateUserInfo',
])
}
}
上面 this.$store.commit 的呼叫方式就可以替換成:

到這里為止已經具備了一個簡單的 Vue 專案的雛形
接下來會打造一個綜合性的 Todo List,并真正用上 Composition API
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/202449.html
標籤:JavaScript
下一篇:Oss 物件服務存盤前端方法封裝
