保姆級Vue3+Vite專案實戰多佈局(上)

語言: CN / TW / HK

寫在前面

本文為 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 了!

可動態切換的佈局

再來看可以動態切換的佈局,其實也很簡單,一般來說,我們使用 Vuecomponent 元件,通過 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 元件在 Vue2is 也可以通過元件名稱切換, 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,同樣也配置了其元件自動引入,這裡我們直接使用 ArcoDesignlayout 佈局元件做一個常規的上中下三分佈局即可,需要注意的是,我們給 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 都可以看懂寫的樣式是啥意思,@applyUnoCSSstyle 標籤中的寫法,那在 HTML 標籤中 class 屬性中可以直接去寫 UnoCSS 樣式,如上面我們在 a-layout-content 標籤中寫的那樣,如果裝了外掛,懸浮上去會有原生樣式的提示,如下:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/19f1fca99b614d3ab5df5783043b32e0~tplv-k3u1fbpfcp-zoom-1.image

一切都沒問題的話,我們的專案儲存執行就如下所示了(Footer 需要滾動檢視)

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/85283dc8cfa6462daeecd263bd6a9fc9~tplv-k3u1fbpfcp-zoom-1.image

導航元件 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 會跳轉首頁。

標題呢,我們直接用 VuewatchEffect 方法監聽了當前路由 meta 物件中的 title 屬性並賦值給響應式變數 title ,這樣後面我們每次跳轉到某個功能頁面時, Logo 旁邊的文字資訊以及瀏覽器 Tab 頁籤都會變成該頁面路由中配置的 title 資訊。

useRoute 方法是 Vue3 組合式 API,它返回一個當前頁面路由的響應式物件,同樣 Vue 的核心 API 我們都做了自動引入,所以這裡沒有引入。

watchEffect 也是 Vue3API,該方法會立即執行一個函式,同時響應式地追蹤其依賴,並在依賴更改時重新執行。簡單來說就是隻要該回調中有響應式資料,這些響應式資料的依賴發生改變時就會重新執行此回撥,預設會立即執行一次。那在這個場景下就比 watch 好用多了。

那響應式變數 title 是怎麼來的呢?程式碼中我們使用了 useTitle 方法,同樣沒有引入,它不是 VueAPI,其實,它是 VueUse 庫中的一個方法,VueUse useTitle 傳送門,在上文我們已經給 VueUse 這個庫的方法做了自動引入,所以可以直接用,該方法會返回一個響應式變數,這個響應式變數在改變時會自動改變我們的網頁標題,注意這裡的標題指的是瀏覽器 Tab 標籤中的標題,如下:

如上圖,既然已經拿圖示當了 logo,那一不做二不休,把 Tab 籤中的 ico 圖示也換了吧,就是上圖中預設的 Vue 圖示,再次去 iconify 圖示庫線上網站中找到我們使用的 logo 圖示,下載個 png 下來,然後找個免費線上的圖片 pngico 格式網站轉一下格式(百度、谷歌找),把轉換後的檔名改成 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 中直接使用的,包括我們做專案經常會做的選單配置,都是隻存一個圖示名,它是靠我們在執行時通過圖示名去匹配元件,這是一個執行時動態的過程,開發時是做不了自動引入的,這類情況我們需要手動引入一下。

還有一個大家可能發現了,在寫入圖示元件時,我們使用了 Vue3markRaw 方法,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 返回了當前路由物件 routepath 屬性值組成的陣列,這樣每次路由改變該方法就會被觸發,selectedKeys 陣列值就會響應式的改變。key 值即子選單的唯一標識,下面我們寫子選單元件時會將每個子選單的 key 設定為選單對應的路由 path

上面我們用到了一個還沒有建立的 MenuItem 元件,它其實就是我們的子選單元件,接下來我們還是在 src/layout/components/Menu 資料夾下新建 MenuItem.vue 檔案,內容如下:

```html

```

子選單元件 MenuItem 如上,首先,它是一個完全受控元件,就是字面意思,這個元件完全依靠父元件傳入的資料,它只做渲染使用,所以叫完全受控元件。

我們在 Menu 元件中遍歷了選單資料,並給每個子選單元件傳入了一個 menu 屬性即對應的選單資訊物件。

在子選單中,我們使用 defineProps 定義了一個 menu 屬性,Vue3 有個 toRefs 方法可以將響應式物件轉換成普通物件,但是這個普通物件中的屬性會變成一個個響應式屬性。正常情況下我們需要使用 props.menu 才能呼叫父元件傳入的屬性並且該屬性是單向傳遞,也就是我們不能在子元件中修改它,但是我們可以使用 Vue3toRefs 方法將整個 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.vueVue 會自動將上層的資料夾名作為元件名呼叫,注意,僅僅是呼叫而已,其他的都操作不了,想要通過元件名操作一個元件,還是得具名,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

```

儲存後瀏覽器中看看效果吧:

預設佈局到此告一段落,目前首頁是我們上文留下的示例程式碼,有點醜,我們稍微修改一下,讓它有個網站的樣子。

首頁修改 HomePage

開啟 src/views/HomePage.vue 檔案,清空當前內容,寫入下面程式碼:

```html ```

不再細說了,因為沒啥東西,還是那個 logo 圖示放上去,加了點樣式,給它放到頁面中間就行了,暫時先這樣,儲存檢視效果如下:

太長了,看下文吧!

寫在最後

由於此文內容篇幅比較長,為了閱讀體驗,邊欄佈局以及動態切換這些內容我放在下文裡了,由於本文描述核心內容並未結束,所以沒有給程式碼打 Tag,大家有精力可以直接點選下文連結接著看,兩文應該會一塊釋出,專案分支程式碼隨時會變動,不建議直接看哈,下文結束程式碼會打一個 Tag 釋出供大家下載檢視當前版本程式碼,就這樣。

保姆級 Vue3+Vite 專案實戰多佈局(下)

謝閱,如有錯誤請評論糾正,有什麼疑問或者不理解的地方都可以私信諮詢我,由於不經常寫實戰文章,也為了不同程度同學都可以看下去,文章可能稍微有些囉嗦,見諒,再次歡迎關注專欄 Vue3實戰系列 ,點贊關注不迷路,回見!

本文為稀土掘金技術社群首發簽約文章,14天內禁止轉載,14天后未獲授權禁止轉載,侵權必究!