保姆級Vue3+Vite項目實戰多佈局(上)
寫在前面
本文為 Vue3+Vite
項目實戰系列教程文章第二篇,系列文章建議從頭觀看效果更佳,大家可關注 Vue3 實戰系列 防走失!點個贊再看有助於全文完整閲讀!
此係列文章主要使用到的主要技術站棧為 Vue3+Vite
,那既然是 Vue3
,狀態庫我們使用的是 Pinia
而不是 Vuex
,在寫法上也肯定是以 CompositionAPI
為主而不是 OptionsAPI
,組件庫方面我們使用的是 ArcoDesign
(趕緊丟掉 ElementUI
吧!)。
上文 這是一份保姆級Vue3+Vite實戰教程 中我們主要介紹了 Vue3+Vite
項目的搭建以及項目上的一些配置,當然後續在開發過程中如果有需要也會陸陸續續的補充一些配置。
本文我們終於要開始寫代碼了,但其實依舊還是在為項目做準備,因為此文的核心是項目的多佈局實戰,先把頁面佈局搭建好,隨後再開始寫項目,這也是項目開發中必不可少的一環,當然重要的還是寫代碼過程中的細節以及小技巧,新同學可以跟着碼一遍,老同學可以快速閲讀一遍看有沒有什麼用得上的實戰小技巧。
👉🏻 項目 GitHub 地址
如果大家不想從頭來過可以直接下載截止到上文內容的代碼,👉🏻 toolsdog tag v0.0.1-dev !
代碼拉下來之後,npm install || pnpm install
下載依賴,然後 npm run serve || pnpm serve
啟動,如果一切沒問題的話,當前項目運行起來是這樣的:
PS: 在開始寫代碼之前,你需要簡單看下 Vue3
官方文檔,去了解一下基礎 API
,這很重要!!!
OK,接下來開始本文內容!
項目多佈局思路
你想要的多佈局是哪種?
我們平常所説的多佈局比較籠統,仔細分來其實有兩種需要多佈局的場景,大家可以自行匹配一下:
- 項目有很多頁面,有些頁面是一樣的佈局,但還有些頁面是另外一種佈局,所以我們需要多種佈局提供給不同的頁面。
- 項目有很多頁面,頁面都是統一的佈局,但是我們需要提供多種可以自由切換的佈局,讓用户在生產環境自己去選擇。
多頁面不同佈局
如果你只是需要在不同的頁面使用不同的佈局,那麼很簡單。
因為你只需要寫多個不同的佈局組件,然後使用二級路由通過指定父級路由的 component
就可以決定採用哪個佈局,如下:
假如我們有 2 個佈局:
```js // layout 1 Layout1.vue
// layout 2 Layout2.vue ```
頁面 page_a
想要使用 Layout1
佈局,頁面 page_b
想要使用 Layout2
佈局,那麼只需在配置路由時如下:
```js { routes: [ { path: '/layout1', name: 'Layout1', component: () => import('/Layout1.vue'), redirect: '/layout1/page_a', children: [ { path: 'page_a', name: 'PageA', component: () => import('/PageA.vue') },
// ...
]
},
{
path: '/layout2',
name: 'Layout2',
component: () => import('***/Layout2.vue'),
redirect: '/layout2/page_b',
children: [
{
path: 'page_b',
name: 'PageB',
component: () => import('***/PageB.vue')
},
// ...
]
}
] } ```
如上所示,我們只需要在根組件和佈局組件中寫上 <router-view />
就 OK 了!
可動態切換的佈局
再來看可以動態切換的佈局,其實也很簡單,一般來説,我們使用 Vue
的 component
組件,通過 is
屬性去動態的渲染布局組件就可以了,如下:
```html
```
然後,我們直接在父路由中引入此頁面,就可以通過改變狀態來動態切換所有的子路由佈局了,如下:
```js { routes: [ { path: '/', component: () => import('/SwitchLayout.vue'), redirect: '/page_a', children: [ { path: 'page_a', name: 'PageA', component: () => import('/PageA.vue') },
// ...
]
},
} ```
PS: Vue
內置的component
組件在 Vue2
中 is
也可以通過組件名稱切換, Vue3
中只能通過組件實例切換!
OK,到此本文就結束了,謝謝大家觀看!!!🤨
準備工作
結束是不可能結束的,多佈局本身其實很簡單,更重要的其實還是我們寫的過程中遇到的一些技術點和細節,那接下來我們開始安排!!!
咱們先寫一個可以動態切換的佈局,首先,在項目 src
目錄下創建一個佈局文件夾 layout
。
接下來我們在 src/layout
文件下創建一個可切換佈局的入口組件 SwitchIndex.vue
,內容和上面所寫的差不多,如下:
```html
```
component
組件我們暫且註釋,因為目前還沒有佈局組件。
接下來我們創建兩個佈局組件,由於我們要把這兩種佈局的選擇權交給用户,所以我們在 layout
文件夾下新建一個 switch
文件夾,把可以切換的這兩個佈局組件放到裏面統一管理下。
創建可切換的默認佈局文件:layout/switch/DefaultLayout.vue
```html
```
創建可切換的邊欄佈局文件:layout/switch/SidebarLayout.vue
```html
```
這兩個佈局的預期如下:
其實就是兩種很普通很常見的佈局,一種是有側邊欄的 SidebarLayout
( 下文叫它邊欄佈局)、一種無側邊欄的 DefaultLayout
(下文叫它默認佈局),大家先了解下要寫的樣子即可。
OK,接下來我們先完善兩種佈局然後再寫動態切換。
默認佈局組件 DefaultLayout
上面我們已經創建好了 DefaultLayout
組件,那先來把它用上。
修改一下 DefaultLayout
組件,如下:
```html
```
然後直接在 SwitchIndex
組件引入使用這個佈局,上文中我們雖然配置了組件自動引入,但是並沒有配置 layout
目錄,所以 layout
文件夾下的組件是不會被自動引入的,那我們還需要現在 vite.config.js
配置文件中把 layout
目錄加上,如下:
```js export default defineConfig(({ mode }) => { return { // ...
plugins: [
// ...
Components({
// 新增 'src/layout' 目錄配置
dirs: ['src/components/', 'src/view/', 'src/layout'],
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
resolvers: [
ArcoResolver({
sideEffect: true
}),
VueUseComponentsResolver(),
VueUseDirectiveResolver(),
IconsResolver({
prefix: 'icon',
customCollections: ['user', 'home']
})
]
}),
]
} }) ```
OK,然後我們就可以直接在 SwitchIndex
組件中使用 DefaultLayout
佈局組件了,我們寫的組件是匿名組件,默認組件名即文件名,如下:
```html
```
然後,我們需要修改下路由文件 router/index.js
,把 SwitchIndex
組件作為一級路由組件,那此路由下的所有子路由就都可以使用我們的佈局了:
js
routes: [
{
path: '/',
name: 'Layout',
component: () => import('@/layout/SwitchIndex.vue'),
redirect: '/',
children: [
{
path: '/',
name: 'HomePage',
meta: {
title: 'TOOLSDOG'
},
component: () => import('@/views/HomePage.vue')
}
]
}
]
保存看頁面:
如果你的運行效果也同上,那就已經用上了佈局組件,到此都是 OK 的,接下來就可以調整佈局 UI
樣式了!
碼一下頁面佈局
上面也説了,我們的默認佈局其實很簡單,就是很普通的上中下三分佈局。
上文我們已經裝好了 ArcoDesign
,同樣也配置了其組件自動引入,這裏我們直接使用 ArcoDesign
的 layout
佈局組件做一個常規的上中下三分佈局即可,需要注意的是,我們給 Navbar
導航部分加了一個固釘組件 a-affix
,用於固定在頁面頂部。
PS: ArcoDesign
組件均以子母 a
開頭。
修改 DefaultLayout
組件,如下:
```html
```
接着我們簡單調整一下樣式。
注意,CSS
這裏我們接上文的配置,使用的是原子化 CSS
引擎(叫框架也行哈) UnoCSS ,不太瞭解的可以看下文檔教程,如果你也跟着上文配置了它並下載了 UnoCSS for VSCode
插件,那就跟着我寫,配合插件提供懸浮提示原生樣式,跟着我這邊寫一寫其實就會了,很簡單,畢竟常用的 CSS
樣式也就那些,語法記住就 OK 了,當然,你如果不習慣或者沒有配置此項,也無所謂,因為都是些基礎 CSS
樣式,直接用 CSS
寫一下也可以,咱就是圖個省事兒 ~
還有一點,由於我們想保證風格統一,還有就是後面想搞一下黑白模式切換,對於一些顏色、字體、尺寸方面,我這邊直接全使用了 ArcoDesign
拋出的 CSS
變量,沒有自己去自定義一套基礎變量,沒錯,同樣也是圖省事兒 ~
其實目前正經的 UI
庫都會拋出其統一設計的顏色變量(您要是説 ElementUI
,OK,當我沒説),那對於我們這個項目來説,ArcoDesign
自身的這些變量已經夠了,如果你是真的寫正式項目,建議遵循自家 UI
的設計風格,搞一套自己的基礎 CSS
變量來用(對一些有主題、顏色切換需求的同學來説,沒有此需求寫死顏色即可),同時如果項目中使用了開源 UI
庫,你還需要定製一下 UI
庫樣式以匹配自家 UI
設計風格,目前正經的開源 UI
庫對定製主題這塊那都沒得説,清晰明瞭且 Easy (您要是還提 ElementUI
,再次當我沒説,苦 ElementUI
良久 😄)
那説了這麼多,我們先來看下 ArcoDesign
的顏色變量吧!👉🏻 ArcoDesign CSS變量傳送們
如上,我們直接使用對應的 CSS
變量即可,這樣後期我們處理黑白模式時,直接可以用 UI 庫自帶的黑暗模式。
OK,我們簡單的寫下佈局樣式
```html
```
如上,我們給 Navbar
一個下邊框以及 58px
高度,給 Footer
一個上邊框,同時,我們給 Navbar、Content、Footer
加了不同級別的背景顏色(AcroDesign
背景色 CSS
變量),最後我們為了讓 Footer
首頁不顯示出來,給 a-layout-content
組件加了一個最小高度,使用視口高度 100vh
減去 Navbar
的高度就是該組件的最小高度了!
再説一次:相信大家無論用沒用過 UnoCSS
都可以看懂寫的樣式是啥意思,@apply
是 UnoCSS
在 style
標籤中的寫法,那在 HTML
標籤中 class
屬性中可以直接去寫 UnoCSS
樣式,如上面我們在 a-layout-content
標籤中寫的那樣,如果裝了插件,懸浮上去會有原生樣式的提示,如下:
一切都沒問題的話,我們的項目保存運行就如下所示了(Footer
需要滾動查看)
導航組件 Navbar
簡單的佈局組件做好了,接下來我們慢慢填充佈局內容,先來做 Navbar
組件。
上文我們已經看了我畫的草圖,好吧,再看一遍
我們想要實現的兩種佈局都有導航欄,唯一的區別就是菜單的位置,所以我們這裏把導航欄中的各個元素單獨拆分作為獨立的組件,使用插槽的方式在 Navbar
組件去使用,Navbar
組件相當於導航欄的一個佈局組件。這樣導航欄組件在哪種佈局中都是可用的,避免重複代碼。
好,開始做了,在 src/layout
文件夾下新建 components
文件夾存放佈局相關的公共組件。
在 src/layout/components
文件夾下創建 Navbar.vue
文件,內容如下:
```html ```
如上,我們給 Navbar
組件做了三個具名插槽,採用左中右這種結構並使用 flex
佈局將中間的插槽撐滿,同時我們也將默認插槽放在了中間的插槽位置,這樣默認會往佈局中間填充內容。
注意,導航區域的高度在佈局組件中已經固定寫死 58px
了,導航組件這裏我沒有設置高度,讓它自己撐滿就行了。因為在任何佈局下,導航欄高度是相同的。
我們在 DefaultLayout
佈局組件中的 a-layout-header
標籤中使用一下導航條組件,同樣無需引入直接使用,如下:
```html
<!-- 默認插槽和center插槽,默認插槽可不加template直接寫內容,作用同center插槽 -->
<template #center></template>
<!-- right插槽 -->
<template #right></template>
```
由於插槽中沒有寫內容,所以頁面上沒有東西,導航條殼子搞好了,接下來我們開始填充內容。
左側插槽我們寫一個 Logo
組件,中間插槽就是導航菜單 Menu
組件了,右側插槽則是一些頁面小功能組件,暫定為 Github
組件(用來跳轉 Github
的)、做佈局切換的 SwitchLayout
組件、切換模式的 SwitchMode
組件,暫時就這些。
OK,接下來我們依次寫一下這些組件。
Logo 組件
在 src/layout/components
文件夾下新建 Logo.vue
文件,寫入如下內容:
```html
```
然後把 Logo
組件填充到我們 DefaultLayout
組件下 Navbar
組件的 左側插槽中即可:
html
<Navbar>
<template #left>
<Logo />
</template>
</Navbar>
運行如下:
OK,解釋下 Logo
組件,其實就是一個圖標加上一個路由標題。
樣式我就不解釋了,跟着一塊寫的同學有什麼不知道的樣式就用編譯器鼠標懸浮看一下原生 CSS
是什麼即可,老同學應該都能大致看懂啥樣式。
那關於 logo
我們直接在 iconify 圖標庫中找了一個圖標用,我們這裏用的是 ri:hammer-fill
圖標,關於 icon
的配置以及使用這塊上文已經説過了,不瞭解的請看上文,下文中再使用該庫圖標我就直接寫個圖標名不再解釋了哈。另外,點擊 logo
會跳轉首頁。
標題呢,我們直接用 Vue
的 watchEffect
方法監聽了當前路由 meta
對象中的 title
屬性並賦值給響應式變量 title
,這樣後面我們每次跳轉到某個功能頁面時, Logo
旁邊的文字信息以及瀏覽器 Tab
頁籤都會變成該頁面路由中配置的 title
信息。
useRoute
方法是 Vue3
組合式 API
,它返回一個當前頁面路由的響應式對象,同樣 Vue
的核心 API
我們都做了自動引入,所以這裏沒有引入。
watchEffect
也是 Vue3
的 API
,該方法會立即運行一個函數,同時響應式地追蹤其依賴,並在依賴更改時重新執行。簡單來説就是隻要該回調中有響應式數據,這些響應式數據的依賴發生改變時就會重新執行此回調,默認會立即執行一次。那在這個場景下就比 watch
好用多了。
那響應式變量 title
是怎麼來的呢?代碼中我們使用了 useTitle
方法,同樣沒有引入,它不是 Vue
的 API
,其實,它是 VueUse
庫中的一個方法,VueUse useTitle 傳送門,在上文我們已經給 VueUse
這個庫的方法做了自動引入,所以可以直接用,該方法會返回一個響應式變量,這個響應式變量在改變時會自動改變我們的網頁標題,注意這裏的標題指的是瀏覽器 Tab
標籤中的標題,如下:
如上圖,既然已經拿圖標當了 logo
,那一不做二不休,把 Tab
籤中的 ico
圖標也換了吧,就是上圖中默認的 Vue
圖標,再次去 iconify
圖標庫在線網站中找到我們使用的 logo
圖標,下載個 png
下來,然後找個免費在線的圖片 png
轉 ico
格式網站轉一下格式(百度、谷歌找),把轉換後的文件名改成 favicon.ico
,替換掉項目根目錄下的 public/favicon.ico
文件即可,然後我們瀏覽器中的 Tab ico
圖片就換好了,如下:
OK,到此 Logo
組件就搞好了。
Github 跳轉小組件
寫 Github
跳轉組件之前我們需要在 config/index.js
文件中配置一下 GitHub Url
地址,方便日後我們在項目中使用或統一修改,Config
配置文件具體內容看文章開頭的代碼或者看上文配置講解。
在 config/index.js
文件的 configSource
對象中新增一個 github
屬性,屬性值寫上我們的項目地址,如下:
```js const configSource = { // ...
github: 'https://github.com/isboyjc/toolsdog' } ```
Github
跳轉組件很簡單,就是字面意思,我們搞一個圖標放上去,然後能夠點擊打開一個新標籤跳轉到項目的 GitHub
地址就行了。在 src/layout/components
文件夾下新建 Github.vue
文件,寫入如下內容:
```html
```
GitHub
的圖標我們用的 iconify
圖標庫中 mdi:github
圖標,這個就沒啥需要説的了,什麼?你問我為啥不直接用 a
標籤或者 UI
庫的 Link
組件?答案是這個組件庫按鈕懸浮的交互很好看~
接着我們去使用一下,把 Github
組件填充到默認佈局 DefaultLayout
組件下 Navbar
組件的右側插槽中即可:
html
<Navbar>
<template #right>
<Github />
</template>
</Navbar>
運行如下:
SwitchLayout
組件、SwitchMode
組件我們得放到後面再説,接下來我們來寫導航菜單組件。
菜單組件 Menu
由於我們目前只有一個路由,就是首頁,還沒有其他正八經兒的功能頁面,所以,這裏我們要先寫一個路由頁面。
那佈局寫完之後我們第一個功能應該是要寫正則可視化校驗功能,這裏我們就提前給它把路由以及頁面定義好吧!
首先,在 src/views
文件夾下新建 RegularPage.vue
文件作為正則校驗頁面組件:
```html
```
接着我們要配置一下路由,注意,由於現在寫的頁面路由它同時還是個菜單,所以我們把這些可以作為菜單的路由單獨寫一個路由文件,這樣我們後期可以直接可以導出當作菜單項配置用。
在 src/router
文件夾下新建 menuRouter.js
文件,導出一個菜單路由數組,如下:
js
export const menuRouter = []
在 src/router/index.js
中使用一下:
```js import { createRouter, createWebHistory } from 'vue-router' // 導入菜單路由 import { menuRouter } from './menuRouter'
const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: '/', name: 'Layout', component: () => import('@/layout/index.vue'), redirect: '/', children: [ { path: '/', name: 'HomePage', meta: { title: 'TOOLSDOG' }, component: () => import('@/views/HomePage.vue') }, // 使用菜單路由 ...menuRouter ] } ] })
export default router ```
OK,接下來我們配置菜單路由數組,由於我們將來可能會寫到很多不同種類的功能,所以我們使用多級路由的方式給這些頁面做個分類,正則可視化校驗屬於開發工具類,所以我們給它一個 devtools
的父級路由,另外,在菜單路由中,每個父級菜單我們給他在 meta
對象中添加一個 icon
屬性,然後導入一個圖片組件作為對應 icon
的值,這樣做的目的是將來要在導航菜單中給每個分類的菜單都加個圖標。
OK,修改 menuRouter.js
文件如下:
```js import IconMaterialSymbolsCodeBlocksOutline from '~icons/material-symbols/code-blocks-outline'
export const menuRouter = [ { path: 'devtools', name: 'DevTools', meta: { title: '開發工具', icon: markRaw(IconMaterialSymbolsCodeBlocksOutline) }, redirect: { name: 'RegularPage' }, children: [ { path: 'regular', name: 'RegularPage', meta: { title: '正則在線校驗' }, component: () => import('@/views/RegularPage.vue') } ] } ] ```
如上,我們如果想要訪問此頁面,只需要訪問 /devtools/regular
路由即可,那可能有些人注意到該配置中的父級路由的重定向中我們使用的是 name
來做的重定向,這裏不用 path
是為了更安全,這個安全指的是由於我們單獨抽離出了這個菜單路由模塊,雖然目前是在把它引入並寫在了 /
路由下,但是將來萬一改變了一級路由,那整體的 path
都會改變,而使用 name
字段重定向就不存在這個問題,我們只需要注意下各個路由的 name
字段配置不重複即可。
注意,我們上面手動引入了 iconify
圖標庫中的圖標,可能有人會問不是做了 iconify
的自動引入嗎?為什麼還要手動去引入?其實,組件的自動引入是靠解析識別組件模板中引入的組件再做的匹配,而這裏我們沒有在組件模板中使用,而是在 JS
中直接使用的,包括我們做項目經常會做的菜單配置,都是隻存一個圖標名,它是靠我們在運行時通過圖標名去匹配組件,這是一個運行時動態的過程,開發時是做不了自動引入的,這類情況我們需要手動引入一下。
還有一個大家可能發現了,在寫入圖標組件時,我們使用了 Vue3
的 markRaw
方法,markRaw
方法會標記一個對象,使其不能成為一個響應式對象,因為後面我們會將整個菜單路由數據作為一個響應式對象傳入菜單組件渲染,那如果我們在這個數據中存在 Vue
組件,將會造成一些不必要的性能開銷,所以這裏我們直接使用 markRaw
對象給它標記下,使該對象不會被遞歸解析成響應式對象即可。
接下來在瀏覽器訪問下 /devtools/regular
路由,看看效果,同下即沒問題:
已經有菜單了數據了,我們去寫菜單 Menu
組件。先理一下思路,通常組件庫中會有 Menu
組件,當然 ArcoDesign
也不例外,我們可以直接拿過來封裝一層去使用。封裝什麼呢?雖然我們目前只有一個路由,但是我們在應該要考慮到多級的情況,那其實解決辦法就是做一個可以無限遞歸的菜單組件。
OK,在寫菜單組件之前,路由菜單數據還需要處理下,我們寫個遞歸方法拼接一下每個菜單的完整路由,並把每個路由菜單中的 meta
對象壓平到菜單裏,方便我們後面使用,還是在 src/router
文件夾下的 menuRouter.js
文件,新增一個 menuRouterFormat
方法處理菜單數據並將處理後的數據導出,如下:
```js export const menuRouter = [ // ... ]
/*
* @description 菜單路由數組 format
* @param { Array } router 路由數組
* @param { String } parentPath 父級路由 path
* @return { Array }
/
export const menuRouterFormat = (router, parentPath) => {
return router.map(item => {
// 拼接路由,例:'devtools' -> '/devtools' 'regular' -> '/devtools/regular'
item.path = parentPath ? ${parentPath}/${item.path}
: /${item.path}
// 存在 children 屬性,且 children 數組長度大於 0,開始遞歸
if (item.children && item.children.length > 0) {
item.children = menuRouterFormat(item.children, item.path)
}
return Object.assign({}, item, item.meta || {})
}) }
// 解析後 路由菜單列表 export const menuRouterFormatList = menuRouterFormat([...menuRouter]) ```
在 src/layout/components
文件夾下新建 Menu/index.vue
文件:
```html
```
簡單説一下上面代碼,我們先導入了之前 menuRouter.js
中的菜單解析後的數據 menuRouterFormatList
對菜單數據進行了一個初始化。
再來看模板,我們用到了 arcoDesign
組件庫的 a-menu
組件。
accordion
開啟手風琴效果。mode
屬性是設置菜單模式(水平或垂直),我們給它設置成水平即horizontal
。menuItemClick
子菜單點擊時觸發,該回調參數為key
。selected-keys
選中的菜單項key
數組。auto-open-selected
默認展開選中的菜單。
這塊還是建議看下組件庫文檔哈。
子菜單點擊方法中我們直接使用 router.push
傳入 key
跳轉路由即可。那對於 selectedKeys
,我們直接用計算屬性 computed
返回了當前路由對象 route
中 path
屬性值組成的數組,這樣每次路由改變該方法就會被觸發,selectedKeys
數組值就會響應式的改變。key
值即子菜單的唯一標識,下面我們寫子菜單組件時會將每個子菜單的 key
設置為菜單對應的路由 path
。
上面我們用到了一個還沒有創建的 MenuItem
組件,它其實就是我們的子菜單組件,接下來我們還是在 src/layout/components/Menu
文件夾下新建 MenuItem.vue
文件,內容如下:
```html
```
子菜單組件 MenuItem
如上,首先,它是一個完全受控組件,就是字面意思,這個組件完全依靠父組件傳入的數據,它只做渲染使用,所以叫完全受控組件。
我們在 Menu
組件中遍歷了菜單數據,並給每個子菜單組件傳入了一個 menu
屬性即對應的菜單信息對象。
在子菜單中,我們使用 defineProps
定義了一個 menu
屬性,Vue3
有個 toRefs
方法可以將響應式對象轉換成普通對象,但是這個普通對象中的屬性會變成一個個響應式屬性。正常情況下我們需要使用 props.menu
才能調用父組件傳入的屬性並且該屬性是單向傳遞,也就是我們不能在子組件中修改它,但是我們可以使用 Vue3
的 toRefs
方法將整個 props
對象解構,解構出的每個屬性值都是響應式的對象,並且在子組件中直接修改該值可以同步父組件。我們這裏使用 toRefs
只是為了方便,並沒有在子組件去修改父組件傳入的值,這裏簡單提一下可以這樣做,算是一個小技巧吧,不然我們想要在子組件修改父組件傳入的值,只能先定義一個響應式數據接收傳入的值,再在該響應值改變時,emit
出去一個事件,那在 Vue3
中使用 emit
還需要使用 defineEmits
去定義一下事件名,還是比較麻煩的,不理解沒關係,後面有案例會用到這種方法,到時候還會説!!!
OK,接着説,我們拿到了父組件中傳入的 menu
對象,接下來看組件模板,首先我們在模板中校驗了一下傳入的 menu
對象中是否存在 children
屬性。
如果不存在 children
屬性,那它就是一個菜單,我們直接使用 a-menu-item
組件寫入子菜單並把唯一標識 key
設置為 menu
對象的 path
屬性即可(菜單路由),該組件有兩個插槽,默認插槽我們寫入菜單標題 menu.title
,接着校驗了一下傳入的 menu
對象中有沒有 icon
屬性,有的話就在 icon
插槽中使用 component
直接渲染 icon
組件(這裏傳入的 icon
屬性值必須為組件對象,其實目前我們只是在帶有子級的菜單傳入了 icon
組件)。
如果存在 children
屬性,那它就是一個帶子級的菜單項,我們使用 a-sub-menu
組件去渲染它,和之前不一樣的是,帶子級的菜單組件 a-sub-menu
我們直接把菜單標題傳入該組件的 title
屬性就可以了,icon
還是一樣的操作,劃重點,那既然是帶子級的菜單,我們還需要把它的子級再渲染出來,那它的子級可能還有子級(無限套娃),這裏我們只需要遞歸的調用一下組件自身就可以無限的渲染直到沒有子級數據。那 Vue3
的遞歸組件(組件自身調用自身),寫法和 Vue2
其實也沒太大差別,我們這裏由於配置了自動引入,所以都不需要引入,直接像在外部調用該組件一樣,直接調用自己就行,這裏其實涉及到匿名組件和具名組件的問題,我們這個組件是個匿名組件,但是在匿名狀態下,我們可以直接將該組件文件名作為組件名調用即可,那如果組件文件名是 index.vue
,Vue
會自動將上層的文件夾名作為組件名調用,注意,僅僅是調用而已,其他的都操作不了,想要通過組件名操作一個組件,還是得具名,Vue3
組合式 API
組件的具名方法下文有案例會提到,這裏不多説。如上代碼,還是像父組件 Menu
中調用 MenuItem
組件一樣調用自己調自己就行了,到此我們的菜單組件就寫好了。
在 Menu
組件中還寫了一些樣式,其實就是簡單調整一下組件庫中菜單組件的樣式,具體樣式這裏就不多説了,因為會的能看懂,不會的自己跟着實踐一下就懂了(太佔篇幅)。
OK,寫完了使用一下看看效果,把 Menu
組件填充到默認佈局 DefaultLayout
組件下 Navbar
組件的中間插槽或者默認插槽中即可:
```html
<!-- 默認插槽和center插槽,默認插槽可不加template直接寫內容,作用同center插槽 -->
<template #center>
<Menu />
</template>
<!-- ... -->
```
看下頁面效果:
到此默認佈局的導航組件就寫的差不多了,下面來寫下頁尾組件!
頁尾組件 Footer
頁尾區域我們在佈局組件中沒有設置高度,因為頁尾的高度不固定,可能隨時會在頁尾加個內容啥的,所以就讓它隨組件內容高度自由撐開吧。。
由於頁尾需要展示一些個人信息,所以我們統一把這些數據都放在 config/index.js
中的基礎配置對象裏,Config
文件內容配置以及使用上文已經説過了,不再描述,這裏的數據沒什麼重要的,不需要脱敏,如需脱敏,可以配合 env.local
環境變量配置文件去做,env.local
環境變量配置文件默認會被 git
忽略,寫入該環境變量文件,並在 config
文件中引入環境變量即可,關於環境變量的配置也請看上文或者直接看文檔 👉🏻 Vite 中 env 配置文檔
```js // ...
const configSource = { // ...
// 個人配置 me: { name: 'isboyjc', // 公眾號 gzhName: '不正經的前端', gzhUrl: 'http://qiniuimages.isboyjc.com/picgo/202210030159449.jpeg', // github github: 'https://github.com/isboyjc' } } ```
我們在 src/components
文件夾下新建 Footer.vue
文件,Footer
組件比較簡單,暫時也沒寫太多內容,這裏我就不會多描述了,直接看代碼吧。
```html
```
在佈局文件中使用一下:
在 DefaultLayout
默認佈局組件中的 a-layout-footer
組件標籤中使用一下 Footer 組件,同樣無需引入直接使用,如下:
```html