目錄
一、環境安裝
二、創建項目
三、前端工程化配置
四、引入組件庫
五、選擇 API 風格
1、選項式 API (Options API)?
2、組合式 API (Composition API)?
六、頁面信息修改
七、通用布局選擇
1、基礎布局結構
2、全局底部欄?
3、動態替換內容
4、全局頂部欄?
八、引入 Axios 請求庫
1、請求工具庫
2、全局自定義請求
3、自動生成請求代碼
4、測試請求?
補充:
一、環境安裝
截止2025.2.6 ,官網發布的vue 3 穩定版本是 V 3.5.13
根據此時的官方文檔要求,node 版本需要大于等于 V 18.3
于是使用 nvm 安裝?v 20.18.0
二、創建項目
使用 Vue 官方推薦的腳手架 create-vue 快速創建 Vue3 的項目:
快速上手 | Vue.js
????????在終端中輸入命令:?npm create vue@latest? 但若后續文檔升級還想創建現在版本的框架的話,輸入?npm create vue@3.12.1
????????接下來按照如下選項創建項目,腳手架會自動幫我們安裝 Vue Router 路由、Pinia 全局狀態管理等實用類庫 :
????????然后用 Webstorm 打開項目,先在終端執行 npm install 安裝依賴,然后執行 npm run dev
能訪問網頁就成功了。
運行項目之后,會發現Vue 腳手架提供了一個調試工具 devtools(http://localhost:5173/devtools/),可以使用它來調試分析項目 :
三、前端工程化配置
????????腳手架已經幫我們整合了 Prettier 代碼美化、ESLint 自動校驗、TypeScript 類型校驗,無需再自行整合。但是需要在 webstorm 里開啟代碼美化插件 :?
在 vue 文件中執行格式化快捷鍵,不報錯,表示配置工程化成功。如果發現格式化效果不好,也沒關系,之后可以使用另外一種格式化快捷鍵
?修改 eslint.config.js、.prettierrc.json、tsconfig.json 文件可以改變校驗規則。如果不使用腳手架,就需要自己整合這些工具:
代碼規范:https://eslint.org/docs/latest/use/getting-started
代碼美化:https://prettier.io/docs/en/install.htm!
直接整合:https://github.com/prettier/eslint-plugin-prettier#recommended-configuration(包括https://github.com/prettier/eslint-config-prettier#installation)
對于前端新手來說,不需要深入了解這些,純當工具去使用即可,盡快上手項目。?
四、引入組件庫
????????引入 Ant Design Vue 組件庫,參考 官方文檔 快速上手 - Ant Design Vue?快速上手。
注意,本教程使用的是 v4.2.6 的組件庫版本,如果后續閱讀本教程中發現有組件或語法不一致,以官方文檔為主,或者在網站右上角切換對應版本的文檔即可:
執行命令:
npm i --save ant-design-vue@4.x
改變主入口文件 main.ts,全局注冊組件((為了方便)?
import App from './App.vue'
import router from './router'
import Antd from "ant-design-vue";
import "ant-design-vue/dist/reset.css";const app = createApp(App)
app.use(Antd);
app.use(createPinia())
app.use(router)app.mount('#app')
然后在?App.vue 中增加一個按鈕組件,測試 Ant Design Vue 組件是否安裝成功。
五、選擇 API 風格
1、選項式 API (Options API)?
????????使用選項式 API,可以用包含多個選項的對象來描述組件的邏輯,例如?data,
methods
?和?mounted
。選項所定義的屬性都會暴露在函數內部的?this
?上,它會指向當前的組件實例。
<script>
export default {// data() 返回的屬性將會成為響應式的狀態// 并且暴露在 `this` 上data() {return {count: 0}},// methods 是一些用來更改狀態與觸發更新的函數// 它們可以在模板中作為事件處理器綁定methods: {increment() {this.count++}},// 生命周期鉤子會在組件生命周期的各個不同階段被調用// 例如這個函數就會在組件掛載完成后被調用mounted() {console.log(`The initial count is ${this.count}.`)}
}
</script>
<template><button @click="increment">Count is: {{ count }}</button>
</template>
2、組合式 API (Composition API)?
????????通過組合式 API,可以使用導入的 API 函數來描述組件邏輯。在單文件組件中,組合式 API 通常會與?<script setup>?搭配使用。這個?setup
?attribute 是一個標識,告訴 Vue 需要在編譯時進行一些處理,讓我們可以更簡潔地使用組合式 API。比如,<script setup>
?中的導入和頂層變量/函數都能夠在模板中直接使用。
下面是使用了組合式 API 與?<script setup>
?改造后和上面的模板完全一樣的組件:
<script setup>
import { ref, onMounted } from 'vue'// 響應式狀態
const count = ref(0)// 用來修改狀態、觸發更新的函數
function increment() {count.value++
}// 生命周期鉤子
onMounted(() => {console.log(`The initial count is ${count.value}.`)
})
</script><template><button @click="increment">Count is: {{ count }}</button>
</template>
建議遵循 Vue3 的組合式 API(Composition API),而不是 選項式 AP!,開發更自由高效一些。
六、頁面信息修改
修改 html 文件中的 title 和 ico 部分,設置成適合自己項目的信息:
<!DOCTYPE html>
<html lang="">
<head><meta charset="UTF-8"><link rel="icon" href="/bitbug_favicon.ico"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>智能云圖庫平臺</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
還可以替換 public 目錄下默認的 ico 圖標為自己的,有很多 現成的網站(如:在線制作ico圖標 | 在線ico圖標轉換工具 方便制作favicon.ico - 比特蟲 - Bitbug.net) 可以制作 ico 圖標。效果如圖:
七、通用布局選擇
1、基礎布局結構
在 layouts 日錄下新建一個布局 BasicLayout.vue,在 App.vue 全局頁面入口文件中引入。App.vue 代碼如下 :?
<template><div id="app"><BasicLayout/></div>
</template><script lang="ts" setup>
import BasicLayout from "@/layouts/BasicLayout.vue";
</script>
可以移除頁面內的默認樣式、并且移除 main.ts 中默認引入的 main.css,防止樣式污染 :?
<style>
#app {
}
</style>
選用 Ant Design 組件庫的 Layout 組件 布局 Layout - Ant Design Vue,先把【上中下】布局編排好,然后再填充內容 :?
<template><div id="basicLayout"><a-layout style="min-height: 100vh"><a-layout-header>Header</a-layout-header><a-layout-content>Content</a-layout-content><a-layout-footer>Footer</a-layout-footer></a-layout></div>
</template><script setup lang="ts"></script><style scoped>
#basicLayout {
}
</style>
并且移除腳手架自帶的樣式 ,這些都用不上。
2、全局底部欄?
通常用于展示版權信息 :??
<a-layout-footer class="footer"><a href="https://www.baidu.com" target="_blank"> 智能云圖庫 by Asukabai</a>
</a-layout-footer>#basicLayout .footer {background: #efefef;padding: 16px;position: fixed;bottom: 0;left: 0;right: 0;text-align: center;
}
3、動態替換內容
????????項目使用了 Vue Router 路由庫 介紹 | Vue Router,可以在 router/index.ts 配置路由,能夠根據訪問的頁面地址找到不同的文件并加載渲染。(比如,此處當路由是 / 時 ,加載的組件是 HomeView , 而 HomeView 組件中又加載了 TheWelcome?于是?? <router-view /> 將會把?TheWelcome 的內容顯示出來 )
于是,修改 BasicLayout 內容部分的代碼如下 :?
<a-layout-content class="content"><router-view />
</a-layout-content>
修改樣式,要和底部欄保持一定的外邊距,否則內容會被遮住 :
<style scoped>
#basicLayout .content {background: linear-gradient(to right, #fefefe, #fff);margin-bottom: 28px;padding: 20px;
}
</style>
?修改之后,發現即使頁面拉倒底,也不會有內容被?footer 所擋住 :
4、全局頂部欄?
????????由于頂部欄的開發相對復雜,可以基于 Ant Design 的菜單組件 導航菜單 Menu - Ant Design Vue來創建 GlobalHeader 全局頂部欄組件,組件統一放在 components 日錄中 ?。先直接復制現成的組件示例代碼到 GlobalHeader 中即可 (此處選擇了“水平的頂部導航菜單”)
在基礎布局中引入頂部欄組件 :?
<a-layout-header class="header"><GlobalHeader />
</a-layout-header>
效果如下 :
樣式代碼如下:可以修改下全局 Header 的樣式,清除一些默認樣式(比如背景色等)
接下來要修改 GlobalHeader 組件,完善更多內容。
1) 給菜單外套一層元素,用于整體控制樣式 :
<div id="globalHeader"><a-menu v-model:selectedKeys="current" mode="horizontal" :items="items" />
</div>
2) 根據我們的需求修改菜單配置,key 為要跳轉的 URL 路徑, 并且在組件中選擇 icon 切換自己想要的圖標:
<script lang="ts" setup>
import { h, ref } from 'vue'
import { HomeOutlined } from '@ant-design/icons-vue'
import { MenuProps } from 'ant-design-vue'const current = ref<string[]>(['home'])
const items = ref<MenuProps['items']>([{key: '/',icon: () => h(HomeOutlined),label: '主頁',title: '主頁',},{key: '/about',label: '關于',title: '關于',},{key: 'others',label: h('a', { href: 'https://www.baidu.com', target: '_blank' }, '百度一下'),title: '百度一下',},
])
</script>
效果如圖: ( “主頁” 前多了一個圖標顯示)
3) 完善全局頂部欄,左側補充網站圖標和標題。
????????先把 logo.png 放到 src/assets 日錄下,替換掉原本的默認 Logo : 修改 GlobalHeader 代碼,補充 HTML :
<RouterLink to="/"><div class="title-bar"><img class="logo" src="../assets/logo.png" alt="logo" /><div class="title">魚皮云圖庫</div></div>
</RouterLink>
其中,RouterLink 組件的作用是支持超鏈接跳轉(不刷新頁面) ,補充 CSS 樣式?:
<style scoped>
.title-bar {display: flex;align-items: center;
}.title {color: black;font-size: 18px;margin-left: 16px;
}.logo {height: 48px;
}
</style>
得到的效果如圖所示 :?
4) 完善頂部導航欄,右側展示當前用戶的登錄狀態 (暫時用登錄按鈕代替)
<div class="user-login-status"><a-button type="primary" href="/user/login">登錄</a-button>
</div>
5) 優化導航欄的布局,采用 柵格組件 的自適應布局 (左中右結構,左側右側寬度固定,中間菜單欄自適應)
<a-row :wrap="false"><a-col flex="200px"><RouterLink to="/"><div class="title-bar"><img class="logo" src="../assets/logo.png" alt="logo" /><div class="title">智能云圖庫</div></div></RouterLink></a-col><a-col flex="auto"><a-menuv-model:selectedKeys="current"mode="horizontal":items="items"/></a-col><a-col flex="120px"><div class="user-login-status"><a-button type="primary" href="/user/login">登錄</a-button></div></a-col>
</a-row>
效果如圖,可以嘗試縮小瀏覽器窗口觀察導航條的變化:
6) 修改路由配置,?目標: 點擊菜單項后,可以跳轉到對應的頁面;并且刷新頁面后,對應的菜單自動高亮。按需修改 router/index.ts 文件的 routes 配置,定義我們需要的頁面路出,每個 path 對應一個component (要加載的組件)?
routes: [{path: '/',name: 'home',component: HomeView,},{path: '/about',name: 'about',// route level code-splitting// this generates a separate chunk (About.[hash].js) for this route// which is lazy-loaded when the route is visited.component: () => import('../views/AboutView.vue'),},
],
????????觀察上述代碼,會發現 component 支持直接傳入組件、或者使用 impor 按需懶加載組件,按需加載是一種優化首次打開站點性能的方式 。路由跳轉:? 給 GlobalHeader 的菜單組件綁定跳轉事件 :
import { useRouter } from "vue-router";
const router = useRouter();// 路由跳轉事件
const doMenuClick = ({ key }: { key: string }) => {router.push({path: key,});
};
修改 HTML 模板,綁定事件 :
<a-menuv-model:selectedKeys="current"mode="horizontal":items="items"@click="doMenuClick"
/>
刷新頁面后,你會發現當前菜單項并沒有高亮,所以需要同步路由的更新到菜單項高亮
同步高亮原理:
1.點擊菜單時,Ant Design 組件已經通過 v-model 綁定 current 變量實現了高亮。
2.刷新頁面時,需要獲取到當前 URL 路徑,然后修改 current 變量的值,從而實現同步。
使用 Vue Router 的 afterEach 路由鉤子實現,每次改變路由或刷新頁面時都會自動更新 current 的值,從而實現高亮:
const router = useRouter();
// 當前選中菜單
const current = ref<string[]>([]);
// 監聽路由變化,更新當前選中菜單
router.afterEach((to, from, next) => {current.value = [to.path];
});
????????但目前 路由和菜單配置中,有一些是重復的? 有沒有更好地方式來配置路由和菜單項,不用每次修改時都要改兩邊的代碼呢?? ( 答案就是將路由配置數組傳遞給菜單組件 )
八、引入 Axios 請求庫
????????一般情況下,前端只負責界面展示和動效交互,盡量避免寫復雜的邏輯;當需要獲取數據時,通常是向后端提供的接口發送請求,由后端執行操作(比如保存數據)并響應數據給前端。
????????前端如何向后端發送請求呢?最傳統的方式是使用 AJAX 技術。但其代碼有些復雜,我們可以使用第三方的封裝庫,來簡化發送請求的代碼,比如主流的請求工具庫 Axios。
1、請求工具庫
安裝請求工具類 Axios,參考官方文檔: https://axios-http.com/docs/intro?Getting Started |?Axios Docs
npm install axios
2、全局自定義請求
????????需要自定義全局請求地址等,參考 Axios 官方文檔,編寫請求配置文件 request.ts 。包括全局接口請求地址、超時時間、自定義請求響應攔截器等。
????????響應攔截器的應用場景:? 需要對接口的 通用響應 進行統一處理,比如從 response 中取出 data; 或者根據 code 去集中處理錯誤。這樣不用在每個接口請求中都去寫相同的邏輯。比如可以在全局響應攔截器中,讀取出結果中的 data,并校驗 code 是否合法,如果是未登錄狀態,則自動登錄。示例代碼如下,其中 withcredentials:true 一定要寫,否則無法在發請求時攜帶 Cookie,就無法完成登錄。代碼如下 :
import axios from 'axios'
import { message } from 'ant-design-vue'// 創建 Axios 實例
const myAxios = axios.create({baseURL: 'http://localhost:8123',timeout: 60000,withCredentials: true,
})// 全局請求攔截器
myAxios.interceptors.request.use(function (config) {// Do something before request is sentreturn config},function (error) {// Do something with request errorreturn Promise.reject(error)},
)// 全局響應攔截器
myAxios.interceptors.response.use(function (response) {const { data } = response// 未登錄if (data.code === 40100) {// 不是獲取用戶信息的請求,并且用戶目前不是已經在用戶登錄頁面,則跳轉到登錄頁面if (!response.request.responseURL.includes('user/get/login') &&!window.location.pathname.includes('/user/login')) {message.warning('請先登錄')window.location.href = `/user/login?redirect=${window.location.href}`}}return response},function (error) {// Any status codes that falls outside the range of 2xx cause this function to trigger// Do something with response errorreturn Promise.reject(error)},
)export default myAxios
3、自動生成請求代碼
如果采用傳統開發方式,針對每個請求都要單獨編寫代碼,很麻煩。推薦使用 OpenAP| 工具,直接自動生成即可:https://www.npmjs.com/package/@umijs/openapi按照官方文檔的步驟,先安裝:
npm i --save-dev @umijs/openapi
openapi.config.js ,根據自己的需要定制生成的代碼:
在 **項目根目錄 **新建
import { generateService } from '@umijs/openapi'generateService({requestLibPath: "import request from '@/request'",schemaPath: 'http://localhost:8123/api/v2/api-docs',serversPath: './src',
})
?注意,要將 schemaPath 改為自己后端服務提供的 Swagger 接口文檔的地址。
在package.json的script 中添加"openapi":"node openapi.config.js"
執行即可生成請求代碼,還包括 TypeScript 類型:
如果生成過程中出現,環境沒裝,根據提示安裝即可
如此處執行?npm install tslib --save-dev 即可,以后每次后端接口變更時,只需要重新生成一遍就好,非常方便?
4、測試請求?
可以嘗試在任意頁面代碼中調用 AP1 :
import { healthUsingGet } from '@/api/mainController'healthUsingGet().then((res) => {console.log(res)
})
按 F12 打開開發者工具查看請求,由于我們后端已經添加了全局跨域配置,正常情況下應該能看到如下響應 :
解決跨域 (可選)
如果發現請求錯誤,要查看錯誤信息具體分析。比如遇到跨域問題,這是由于前端網頁地址和后端請求接口地址不同導致的 :
這種情況下,可以通過修改后端代碼,增加全局跨域配置或者跨域注解來解決,如果后端代碼無法修改,還可以通過前端代理服務器來解決,如果項目使用 Vite,內置了代理服務器。可以修改 vite.config.ts 文件,增加代理配置 :
export default defineConfig({server: {proxy: {'/api': 'http://localhost:8123',}},
})
同時修改 request.ts,移除請求前綴 :
// 創建 Axios 實例
const myAxios = axios.create({baseURL: '',timeout: 60000,withCredentials: true,
})
????????這樣一來,前端發送的請求域名就等同于當前 URL 的域名,就不會出現跨域。但是訪問到 /api開頭的接口時,會被代理到請求 8123 端口的后端服務器,從而完成請求。當然,前端還有很多代理工具,比如 Whistle 。?
補充:
在后續開發中你會發現,Ant Design Vue 默認使用的是英文文案,如果需要替換為中文,可以參考 國際化文檔,只需給整個應用包裹一層組件即可完成。