前言
H5 項(xiàng)目基于 Web 技術(shù),可以在智能手機(jī)、平板電腦等移動(dòng)設(shè)備上的瀏覽器中運(yùn)行,無需下載和安裝任何應(yīng)用程序,且H5 項(xiàng)目的代碼和資源可以集中在服務(wù)器端進(jìn)行管理,只需更新服務(wù)器上的代碼,即可讓所有顧客訪問到最新的系統(tǒng)版本。
本系列將以肯德基自助點(diǎn)餐頁面為模板,搭建一款自助點(diǎn)餐系統(tǒng),第一次開發(fā)移動(dòng)端h5項(xiàng)目,免不了有所差錯(cuò)和不足,歡迎各位大佬指正。
項(xiàng)目代碼正在gitee同步更新中,項(xiàng)目地址:https://gitee.com/airheaven/kfg-vue,學(xué)習(xí)前請大家給個(gè)star哦??
技術(shù)棧
Vue3.2 + Vite + TS + Vant + Pinia + Node.js
一、起始準(zhǔn)備
1.1、安裝nvm
nvm 全英文也叫 node.js version management,是一個(gè) nodejs 的版本管理工具,用于管理nodejs。
首先進(jìn)入github鏈接:https://github.com/coreybutler/nvm-windows/releases 下載 nvm-setup.zip,隨后安裝。
安裝完成后輸入nvm version
顯示版本號就是安裝成功了,我這里用的是1.1.10版本。
PS E:\MyVueWorkspace> nvm version
1.1.10
1.2、利用nvm安裝node和npm
nvm install 版本號 安裝指定的版本的 nodejs,我這里輸入:nvm install 16.16.0
安裝16.16.0版本
nvm會(huì)自動(dòng)幫你安裝好node和npm,顯示如下信息就是成功了:
Downloading node.js version 16.16.0 (64-bit)...
Extracting node and npm...
Complete
npm v8.11.0 installed successfully.
Installation complete. If you want to use this version, type
nvm use 16.16.0
如果之前安裝過,可以使用nvm ls查看已經(jīng)安裝過的node版本。
安裝好node和npm后,需要使用nvm use 16.16.0
啟用該版本。
1.3、利用npm安裝Vite
既然是新項(xiàng)目,且用的是Vue3.2,那么我們必須用上現(xiàn)在嘎嘎香 嘎嘎快的Vite,Vite是新一代的前端開發(fā)與構(gòu)建工具,具有開箱即用、高度的可擴(kuò)展性和完整的類型支持。
使用npm install -g vite
安裝最新版的Vite,然后使用vite -v
查看版本(我的是4.1.4)
added 14 packages, and audited 16 packages in 13s
found 0 vulnerabilities
npm notice
npm notice New major version of npm available! 8.11.0 -> 9.5.1
npm notice Changelog: https://github.com/npm/cli/releases/tag/v9.5.1
npm notice Run npm install -g npm@9.5.1 to update!
npm notice
PS E:\MyVueWorkspace> vite -v
vite/4.1.4 win32-x64 node-v16.16.0
1.4、VsCode插件安裝
在VsCode中找到擴(kuò)展,需要安裝的擴(kuò)展插件有:
ESlint:開源的JavaScript驗(yàn)證工具,可以讓代碼更加規(guī)范:
Prettier:前端代碼格式工具,可以讓代碼保持風(fēng)格一致:
Vue Language Features(Volar):針對Vue3的vscode插件:
二、項(xiàng)目初始化
輸入npm create vite@latest
使用Vite初始化項(xiàng)目,填寫項(xiàng)目名稱(KFG-vue),選擇Vue作為框架,語言選擇TypeScript,如下所示:
Need to install the following packages:
create-vite@latest
Ok to proceed? (y) y
√ Project name: ... KFG-vue
√ Package name: ... kfg-vue
√ Select a framework: ? Vue
√ Select a variant: ? TypeScript
然后cd進(jìn)入KFG-vue項(xiàng)目目錄,輸入:npm install
安裝項(xiàng)目所需的基本依賴:
added 46 packages, and audited 47 packages in 37s
5 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
輸入npm run dev
運(yùn)行項(xiàng)目,控制臺(tái)會(huì)輸出網(wǎng)址,如http://127.0.0.1:5173/即可以查看初始化好的項(xiàng)目:
> kfg-vue@0.0.0 dev
> vite
VITE v4.1.4 ready in 261 ms
? Local: http://127.0.0.1:5173/
? Network: use --host to expose
然后我們需要?jiǎng)h除一些初始化項(xiàng)目中不需要的東西,刪除public里的vite.svg,刪除assets里面的vue.svg,刪除components里面的HelloWorld.vue,清空style.css,清空App.vue里的內(nèi)容(僅僅保留最基本的vue3模板):
由于vue中ts無法識(shí)別引入的vue文件,引入模塊后會(huì)提示打不到module,但是編譯可能成功,運(yùn)行也不報(bào)錯(cuò),為了我的強(qiáng)迫癥我選擇在src文件夾下新增一個(gè)env.d.ts
文件解決這個(gè)問題:
// src/env.d.ts
declare module "*.vue" {
import type { DefineComponent } from "vue";
// eslint-disable-next-line @typescript-eslint/ban-types
const vueComponent: DefineComponent<{}, {}, any>;
export default vueComponent;
}
三、代碼規(guī)范配置(可選)
插件拿來不一定能夠完全適用,需要提供了一些額外的適用于 ts 和vue語法的規(guī)則,配置以下項(xiàng):
ESlint配置:輸入npx eslint --init
,選擇如下:
√ How would you like to use ESLint? · problems(第二個(gè))
√ What type of modules does your project use? · esm(第一個(gè))
√ Which framework does your project use? · vue
√ Does your project use TypeScript? · No / Yes
√ Where does your code run? · browser, node(兩個(gè)都勾選)
√ What format do you want your config file to be in? · JavaScript
Prettier配置:輸入npm i prettier eslint-config-prettier eslint-plugin-prettier -D
:
創(chuàng)建prettier.cjs
文件,文件中輸入:
// prettier.cjs
module.exports = {
printWidth: 100,
tabWidth: 2,
useTabs: false, // 是否使用tab進(jìn)行縮進(jìn),默認(rèn)為false
singleQuote: true, // 是否使用單引號代替雙引號,默認(rèn)為false
semi: true, // 行尾是否使用分號,默認(rèn)為true
arrowParens: 'always',
endOfLine: 'auto',
vueIndentScriptAndStyle: true,
htmlWhitespaceSensitivity: 'strict',
};
配置eslintrc文件,將文件.eslintrc.cjs改為:
// eslintrc.cjs
module.exports = {
root: true, // 停止向上查找父級目錄中的配置文件
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:vue/vue3-essential',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
'prettier', // eslint-config-prettier 的縮寫
],
parser: 'vue-eslint-parser', // 指定要使用的解析器
// 給解析器傳入一些其他的配置參數(shù)
parserOptions: {
ecmaVersion: 'latest', // 支持的es版本
parser: '@typescript-eslint/parser',
sourceType: 'module', // 模塊類型,默認(rèn)為script,我們設(shè)置為module
},
plugins: ['vue', '@typescript-eslint', 'prettier'], // eslint-plugin- 可以省略
rules: {
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-var-requires': 'off',
},
};
配置保存文件自動(dòng)格式化:在項(xiàng)目的.vscode中新建一個(gè)setting.json文件,
文件中配置如下,使程序能夠保存時(shí)自動(dòng)使用eslint格式化(懶人福音),setting.json配置 如下所示:
// .vscode/setting.json
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
// #每次保存的時(shí)候自動(dòng)格式化
"editor.formatOnSave": true,
"editor.formatOnType": true,
// #每次保存的時(shí)候?qū)⒋a按eslint格式進(jìn)行修復(fù)
"eslint.autoFixOnSave": true,
"eslint.format.enable": true,
}
添加lint命令(可選):package.json中進(jìn)行配置,可以運(yùn)行npm run lint
檢查代碼:
// package.json
// 可以運(yùn)行`npm run lint`檢查代碼
"lint": "eslint --ext .js,.vue,.ts src --fix"
四、項(xiàng)目搭建
4.1、清除默認(rèn)樣式
在網(wǎng)上找一個(gè)reset.css文件,放入到src/styles文件夾(可能需要新建)中,從而清除默認(rèn)樣式:
/* src/styles */
/* 清除內(nèi)外邊距 */
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote,
dl, dt, dd, ul, ol, li,
pre,
fieldset, lengend, button, input, textarea,
th, td {
margin: 0;
padding: 0;
}
/* 設(shè)置默認(rèn)字體 */
body,
button, input, select, textarea { /* for ie */
/*font: 12px/1 Tahoma, Helvetica, Arial, "宋體", sans-serif;*/
font: 12px/1.3 "Microsoft YaHei",Tahoma, Helvetica, Arial, "\5b8b\4f53", sans-serif; /* 用 ascii 字符表示,使得在任何編碼下都無問題 */
color: #333;
}
h1 { font-size: 18px; /* 18px / 12px = 1.5 */ }
h2 { font-size: 16px; }
h3 { font-size: 14px; }
h4, h5, h6 { font-size: 100%; }
address, cite, dfn, em, var, i{ font-style: normal; } /* 將斜體扶正 */
b, strong{ font-weight: normal; } /* 將粗體扶細(xì) */
code, kbd, pre, samp, tt { font-family: "Courier New", Courier, monospace; } /* 統(tǒng)一等寬字體 */
small { font-size: 12px; } /* 小于 12px 的中文很難閱讀,讓 small 正常化 */
/* 重置列表元素 */
ul, ol { list-style: none; }
/* 重置文本格式元素 */
a { text-decoration: none; color: #666;}
/* 重置表單元素 */
legend { color: #000; } /* for ie6 */
fieldset, img { border: none; }
button, input, select, textarea {
font-size: 100%; /* 使得表單元素在 ie 下能繼承字體大小 */
}
/* 重置表格元素 */
table {
border-collapse: collapse;
border-spacing: 0;
}
/* 重置 hr */
hr {
border: none;
height: 1px;
}
.clearFix::after{
content:"";
display: block;
clear:both;
}
/* 讓非ie瀏覽器默認(rèn)也顯示垂直滾動(dòng)條,防止因滾動(dòng)條引起的閃爍 */
html { overflow-y: scroll; }
a:link:hover{
color : rgb(79, 76, 212) !important;
text-decoration: underline;
}
/* 清除浮動(dòng) */
.clearfix::after {
display: block;
height: 0;
content: "";
clear: both;
visibility: hidden;
}
在index.html中引入:
<link rel="stylesheet" href="./styles/reset.css">
4.2、路徑別名配置
路徑別名配置:在vite.config.ts, 加入以下內(nèi)容(resolve的alias),設(shè)置別名@:
// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// 設(shè)置路徑
import { resolve } from "path";
// Vant聲明和按需引入
import Component from "unplugin-vue-components/vite";
import { VantResolver } from "unplugin-vue-components/resolvers";
// vw方案,將px轉(zhuǎn)為vw
import postcsspxtoviewport from "postcss-px-to-viewport";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
Component({
resolvers: [VantResolver()],
}),
],
css: {
postcss: {
plugins: [
postcsspxtoviewport({
unitToConvert: "px", // 要轉(zhuǎn)化的單位
viewportWidth: 750, // UI設(shè)計(jì)稿的寬度,一般寫 320
// 下面的不常用,上面的常用
unitPrecision: 6, // 轉(zhuǎn)換后的精度,即小數(shù)點(diǎn)位數(shù)
propList: ["*"], // 指定轉(zhuǎn)換的css屬性的單位,*代表全部css屬性的單位都進(jìn)行轉(zhuǎn)換
viewportUnit: "vw", // 指定需要轉(zhuǎn)換成的視窗單位,默認(rèn)vw
fontViewportUnit: "vw", // 指定字體需要轉(zhuǎn)換成的視窗單位,默認(rèn)vw
selectorBlackList: ["ignore-"], // 指定不轉(zhuǎn)換為視窗單位的類名,
minPixelValue: 1, // 默認(rèn)值1,小于或等于1px則不進(jìn)行轉(zhuǎn)換
mediaQuery: true, // 是否在媒體查詢的css代碼中也進(jìn)行轉(zhuǎn)換,默認(rèn)false
replace: true, // 是否轉(zhuǎn)換后直接更換屬性值
landscape: false, // 是否處理橫屏情況
}),
],
},
},
resolve: {
alias: {
"@": resolve(__dirname, "./src"),
"*": resolve(""),
},
},
});
然后在tsconfig.json,加入以下內(nèi)容(baseUrl和paths):
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": ["ESNext", "DOM"],
"skipLibCheck": true,
"noEmit": true,
"baseUrl": ".",
// 用于設(shè)置解析非相對模塊名稱的基本目錄,相對模塊不會(huì)受到baseUrl的影響
"paths": {
"@/*": ["src/*"]
}
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}
4.3、項(xiàng)目搭建
1?? Less: CSS預(yù)處理器
我選用了less作為CSS預(yù)處理器,需要安裝相應(yīng)的預(yù)處理器依賴:npm i less -D
如果需要規(guī)定全局樣式的話,可以配置一下在src/asserts/less文件夾下新建一個(gè)global.less,然后在vite.config.ts的css中寫入:
// vite.config.ts
css: {
preprocessorOptions: {
less: {
modifyVars: {
hack: `true; @import (reference) "${resolve(
"src/assets/less/global.less"
)}";`,
},
javascriptEnabled: true,
},
},
//....
}
2?? Vant: 輕量可定制的移動(dòng)端組件庫
選用移動(dòng)端最常用的Vant作為組件庫,輸入npm i vant
安裝Vant。
然后安裝按需引入插件,輸入npm i unplugin-vue-components -D
安裝插件,它可以自動(dòng)引入組件,并按需引入組件的樣式。相比于常規(guī)用法,這種方式可以按需引入組件的 CSS 樣式,從而減少一部分代碼體積,但使用起來會(huì)變得繁瑣一些。
配置Vant的按需引入,在 vite.config.js 文件中配置插件:
// vite.config.ts
import vue from '@vitejs/plugin-vue';
import Components from 'unplugin-vue-components/vite';
import { VantResolver } from 'unplugin-vue-components/resolvers';
export default {
plugins: [
vue(),
Components({
resolvers: [VantResolver()],
}),
],
};
完成以上兩步,就可以直接在模板中使用 Vant 組件了,unplugin-vue-components 會(huì)解析模板并自動(dòng)注冊對應(yīng)的組件。
可以試試在App.vue中寫入:
<script setup lang="ts"></script>
<template>
<van-button type="primary">Hello World</van-button>
</template>
<style scoped></style>
顯示如下則成功引入Vant并自動(dòng)注冊了組件:
3?? vw/vh方案:移動(dòng)端適配
移動(dòng)端適配通常使用的有rem方案和vw/vh方案,我選用的是vw方案,首先輸入npm install postcss-px-to-viewport -D
安裝插件,將 px 轉(zhuǎn)化成 vw,安裝完成后,修改 vite.config.ts
聲明插件(由于 vite 中已經(jīng)內(nèi)聯(lián)了 postcss,所以無需創(chuàng)建 postcss.config.js文件來聲明插件):
// vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
// 設(shè)置路徑
import { resolve } from "path";
// Vant聲明和按需引入
import Component from "unplugin-vue-components/vite";
import { VantResolver } from "unplugin-vue-components/resolvers";
// vw方案,將px轉(zhuǎn)為vw
import postcsspxtoviewport from "postcss-px-to-viewport";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
Component({
resolvers: [VantResolver()],
}),
],
css: {
postcss: {
plugins: [
postcsspxtoviewport({
unitToConvert: "px", // 要轉(zhuǎn)化的單位
viewportWidth: 750, // UI設(shè)計(jì)稿的寬度,一般寫 320
// 下面的不常用,上面的常用
unitPrecision: 6, // 轉(zhuǎn)換后的精度,即小數(shù)點(diǎn)位數(shù)
propList: ["*"], // 指定轉(zhuǎn)換的css屬性的單位,*代表全部css屬性的單位都進(jìn)行轉(zhuǎn)換
viewportUnit: "vw", // 指定需要轉(zhuǎn)換成的視窗單位,默認(rèn)vw
fontViewportUnit: "vw", // 指定字體需要轉(zhuǎn)換成的視窗單位,默認(rèn)vw
selectorBlackList: ["ignore-"], // 指定不轉(zhuǎn)換為視窗單位的類名,
minPixelValue: 1, // 默認(rèn)值1,小于或等于1px則不進(jìn)行轉(zhuǎn)換
mediaQuery: true, // 是否在媒體查詢的css代碼中也進(jìn)行轉(zhuǎn)換,默認(rèn)false
replace: true, // 是否轉(zhuǎn)換后直接更換屬性值
landscape: false, // 是否處理橫屏情況
}),
],
},
},
resolve: {
alias: {
"@": resolve(__dirname, "./src"),
"*": resolve(""),
},
},
});
然后運(yùn)行項(xiàng)目,即可發(fā)現(xiàn)支持了移動(dòng)端適配,且自適應(yīng)的調(diào)整高度,成功配置
4?? Vue Router:路由配置
部分參考自:https://blog.csdn.net/jason_renyu/article/details/123261823
Vue Router是Vue.js 的官方路由,非常方便好用,在控制臺(tái)輸入:npm i vue-router@4
安裝Vue Router,新建router文件夾,新建router/index.ts
,配置如下:
/**
* createRouter 這個(gè)為創(chuàng)建路由的方法
* createWebHashHistory 這個(gè)就是vue2中路由的模式,
* 這里的是hash模式,這個(gè)還可以是createWebHistory等
* RouteRecordRaw 這個(gè)為要添加的路由記錄,也可以說是routes的ts類型
*/
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
// 路由記錄,這個(gè)跟vue2中用法一致,就不做過多解釋了
const routes: Array<RouteRecordRaw> = [
{
path: "/",
name: "todolist",
component: () => import("@/components/TodoList.vue"),
alias: "/todolist",
meta: {
title: "todolist頁面",
},
},
{
path: "/father",
name: "father",
component: () => import("@/components/Father.vue"),
meta: {
title: "father頁面",
},
},
];
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router;
測試一下,在compnents文件下下新建Father.vue和TodoList.vue,內(nèi)容如下:
Father.vue:
<!-- compnents/Father.vue -->
<template>
<div>
<h2>這是Father組件</h2>
<h3>路由傳入的參數(shù)為:{{ route.query.msg }}</h3>
</div>
</template>
<script setup lang="ts">
import { ref } from "vue";
// 路由接收參數(shù)
import { useRoute } from "vue-router";
const route = useRoute();
// 接收路由傳入的參數(shù)
let routeMsg = ref("");
if (route.query.msg) {
routeMsg.value = route.query.msg as string;
}
</script>
<style scoped></style>
TodoList.vue:
<!-- compnents/TodoList.vue -->
<template>
<div>
<h2>這是TodoList組件</h2>
</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>
在App.vue中測試調(diào)用:
<!-- App.vue -->
<script setup lang="ts">
// useRouter的使用
import { useRouter } from "vue-router";
const router = useRouter();
const jumpFather = () => {
// 編程式跳轉(zhuǎn)和傳參
router.push({
path: "/father",
query: {
msg: "hello Vue-Router",
},
});
};
</script>
<template>
<div>
<router-link to="/">todolist</router-link>
|
<router-link to="/father">father</router-link>
</div>
<div>
<van-button type="primary" @click="jumpFather">跳轉(zhuǎn)到father</van-button>
</div>
<router-view></router-view>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
然后輸入npm run dev
測試看看,顯示如下,todolist和father點(diǎn)擊后有所變化,配置成功啦!
5?? Pinia:狀態(tài)管理器
部分參考自:https://blog.csdn.net/qq1195566313/category_11672479.html
Pinia 是 Vue 的存儲(chǔ)庫,允許跨組件/頁面共享狀態(tài)。同樣我們輸入npm i pinia
安裝Pinia,在項(xiàng)目src下創(chuàng)建store文件夾,以后項(xiàng)目中所有的狀態(tài)管理部分文件都將放到store文件夾下。然后新建index.ts
,創(chuàng)建store:
// src/store/index.ts
import { createPinia } from 'pinia';
const pinia = createPinia();
export default pinia;
掛載store,打開main.ts
,以跟router差不多的方式,掛載進(jìn)去:
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
// 掛載router
import router from "./router/index";
// 掛載store
import store from "@/store";
const app = createApp(App);
app.use(router);
app.use(store);
app.mount("#app");
同樣測試一下,在store文件夾下新建types文件夾,types文件夾為狀態(tài)模塊類型管理文件夾,創(chuàng)建模塊類型文件home.ts
,寫入代碼如下:
export type storeHome = {
count: number,
status: boolean
}
在store文件夾下新建modules文件夾,modules文件夾為狀態(tài)模塊管理文件夾,創(chuàng)建模塊文件home.ts
,寫入代碼如下:
import { defineStore } from "pinia";
import { storeHome } from "../types/home";
export const useHomeStore = defineStore("index", {
state: (): storeHome => {
return {
count: 0,
status: false,
};
},
getters: {
curCount(): number {
return this.count;
},
curStatus(): boolean {
return this.status;
},
},
actions: {
updatecount(val: number) {
this.count = val;
},
changeStatus(val: boolean) {
this.status = val;
},
},
});
然后在TodoList.vue文件中測試store是否成功:
<template>
<div>
<h2>這是TodoList組件</h2>
</div>
<div class="main-box">
<h1>狀態(tài)管理測試界面</h1>
<h1>狀態(tài)count:{{ homeStore.count }}</h1>
<!-- <h1>狀態(tài)curCount:{{ calculateCount }}</h1>
<van-button @click="initCount">歸零</van-button> -->
<van-button type="success" @click="changeCount">計(jì)數(shù)</van-button>
<van-button type="danger" @click="randomCount">隨機(jī)</van-button>
</div>
</template>
<script setup lang="ts">
import { useHomeStore } from "@/store/modules/home";
const homeStore = useHomeStore();
const changeCount = () => {
homeStore.count++;
};
const randomCount = () => {
const num: number = Math.random() * 100;
homeStore.updatecount(Math.floor(num));
};
</script>
<style scoped></style>
輸入npm run dev 進(jìn)入測試,點(diǎn)擊todolist,然后點(diǎn)擊隨機(jī)或者計(jì)數(shù),可以看到狀態(tài)count值的變化。
狀態(tài)的持久化配置等內(nèi)容暫時(shí)不展開,后續(xù)需要用到會(huì)說明。
至此項(xiàng)目的整體結(jié)構(gòu)為:
6?? Axios:網(wǎng)絡(luò)請求封裝
Axios 是一個(gè)基于 promise 的網(wǎng)絡(luò)請求庫,其使用簡單,包尺寸小且提供了易于擴(kuò)展的接口,首先輸入npm i axios
安裝Axios。
新建 src/utils/http 文件夾,新建 axios.ts
和types.ts
axios.ts
封裝axios請求方法:
// src/utils/http/axios.ts
import axios, {
AxiosInstance,
AxiosError,
AxiosRequestConfig,
AxiosResponse,
} from "axios";
import { showToast } from "vant";
import "vant/es/toast/style";
// response interface { code, msg, success }
// 不含 data
interface Result {
code: number;
success: boolean;
msg: string;
}
// request interface,包含 data
interface ResultData<T = any> extends Result {
data?: T;
}
enum RequestEnums {
TIMEOUT = 10000, // 請求超時(shí) request timeout
FAIL = 500, // 服務(wù)器異常 server error
LOGINTIMEOUT = 401, // 登錄超時(shí) login timeout
SUCCESS = 200, // 請求成功 request successfully
}
// axios 基礎(chǔ)配置
const config = {
// 默認(rèn)地址,可以使用 process Node內(nèi)置的,
// 這里后續(xù)要配置到env.development里
baseURL: "/api",
timeout: RequestEnums.TIMEOUT as number, // 請求超時(shí)時(shí)間
withCredentials: true, // 跨越的時(shí)候允許攜帶憑證
};
class Request {
request(config: AxiosRequestConfig<any>) {
throw new Error("Method not implemented.");
}
service: AxiosInstance;
constructor(config: AxiosRequestConfig) {
// 實(shí)例化 serice
this.service = axios.create(config);
/**
* 請求攔截器
* request -> { 請求攔截器 } -> server
*/
this.service.interceptors.request.use(
(config: AxiosRequestConfig): any => {
const token = localStorage.getItem("token") ?? "";
return {
...config,
headers: {
customToken: "customBearer " + token,
},
};
},
(error: AxiosError) => {
// 請求報(bào)錯(cuò)
Promise.reject(error);
}
);
/**
* 響應(yīng)攔截器
* response -> { 響應(yīng)攔截器 } -> client
*/
this.service.interceptors.response.use(
(response: AxiosResponse) => {
const { data, config } = response;
if (data.code === RequestEnums.LOGINTIMEOUT) {
// 登錄過期,需要重定向至登錄頁面
localStorage.setItem("token", "");
location.href = "/";
}
if (data.code && data.code !== RequestEnums.SUCCESS) {
showToast({ message: data });
return Promise.reject(data);
}
return data;
},
(error: AxiosError) => {
const { response } = error;
if (response) {
this.handleCode(response.status);
}
if (!window.navigator.onLine) {
showToast({
message: "網(wǎng)絡(luò)連接失敗,請檢查網(wǎng)絡(luò)",
});
// 可以重定向至404頁面
}
}
);
}
public handleCode = (code: number): void => {
switch (code) {
case 401:
showToast({
message: "登陸失敗,請重新登錄",
});
break;
case 500:
showToast({
message: "請求異常,請聯(lián)系管理員",
});
break;
case 404:
showToast({
message: "404錯(cuò)誤,請聯(lián)系管理員",
});
break;
default:
showToast({
message: "請求失敗,請聯(lián)系管理員",
});
break;
}
};
// 通用方法封裝
get<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.get(url, { params });
}
post<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.post(url, params);
}
put<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.put(url, params);
}
delete<T>(url: string, params?: object): Promise<ResultData<T>> {
return this.service.delete(url, { params });
}
}
export default new Request(config);
types.ts
和后端約定好接口返回的數(shù)據(jù)結(jié)構(gòu),如:
// src/utils/http/types.ts
// 和后端約定好接口返回的數(shù)據(jù)結(jié)構(gòu)
export interface Response<T = any> {
code: number | string;
message: string;
result: T;
}
封裝axios的方式多種多樣,網(wǎng)上有很多種選擇,本項(xiàng)目Axios封裝主要參考自:https://blog.csdn.net/weixin_56650035/article/details/127467646
測試使用,src目錄下新增 api/user/index.ts,內(nèi)容如下:
// src/api/user/index.ts
import request from "@/utils/http/axios";
import { Response } from "@/utils/http/types";
export interface LoginParams {
username: string;
password: string;
}
export interface UserInfo {
id: number;
username: string;
mobile: number;
email: string;
}
export default {
async login(params: LoginParams) {
return await request.post<Response<UserInfo>>("/user/login", params);
},
};
最后,在某一個(gè)vue文件中調(diào)用api試試:
// 測試axios的封裝
import Api, { LoginParams } from "@/api/user";
const loginInfo: LoginParams = {
username: "name",
password: "string",
};
const login = async () => {
const result = await Api.login(loginInfo);
// do something
console.log(result);
};
五、git commit規(guī)范輔助和git版本管理
5.1、設(shè)置commit規(guī)范輔助
這里暫不使用git Husky和eslint,需要的同學(xué)可以配置
使用到Commitizen幫助編寫規(guī)范的commit message ,首先需要輸入npm install commitizen -D
安裝Commitizen,然后輸入npm i -D cz-customizable
安裝cz-customizable自定義的 Commitizen 插件,
配置 根目錄創(chuàng)建 .cz-config.js
,內(nèi)容如下:
module.exports = {
types: [
{
value: ':sparkles: feat',
name: '? feat: 新功能'
},
{
value: ':bug: fix',
name: '?? fix: 修復(fù)bug'
},
{
value: ':tada: init',
name: '?? init: 初始化'
},
{
value: ':pencil2: docs',
name: '?? docs: 文檔變更'
},
{
value: ':lipstick: style',
name: '?? style: 代碼的樣式美化'
},
{
value: ':recycle: refactor',
name: '?? refactor: 重構(gòu)'
},
{
value: ':zap: perf',
name: '?? perf: 性能優(yōu)化'
},
{
value: ':white_check_mark: test',
name: '? test: 測試'
},
{
value: ':rewind: revert',
name: '?? revert: 回退'
},
{
value: ':package: build',
name: '??? build: 打包'
},
{
value: ':rocket: chore',
name: '?? chore: 構(gòu)建/工程依賴/工具'
},
{
value: ':construction_worker: ci',
name: '?? ci: CI related changes'
}
],
messages: {
type: '請選擇提交類型(必填)',
customScope: '請輸入文件修改范圍(可選)',
subject: '請簡要描述提交(必填)',
body: '請輸入詳細(xì)描述(可選)',
breaking: '列出任何BREAKING CHANGES(可選)',
footer: '請輸入要關(guān)閉的issue(可選)',
confirmCommit: '確定提交此說明嗎?'
},
allowCustomScopes: true,
allowBreakingChanges: [':sparkles: feat', ':bug: fix'],
subjectLimit: 72
}
最后要把package.json的腳本中的"type": "module"
改為"type": "commonjs"
在scrpits中添加一行:"commit": "git add . && cz-customizable"
,如下
"scripts" : {
...
"commit": "git add . && cz-customizable"
}
以后就使用 npm run commit
代替 git commit
5.2、git版本管理
首先在自己的github或者gitee上新建一個(gè)空白項(xiàng)目,我命名為KFG-vue,然后復(fù)制剛剛創(chuàng)建好的項(xiàng)目地址如:https://gitee.com/airheaven/kfg-vue.git,然后:
git init # 把項(xiàng)目初始化,相當(dāng)于在項(xiàng)目的跟目錄生成一個(gè) .git 目錄
git add . # 把項(xiàng)目的所有文件加入暫存區(qū)
隨后使用npm run commit
進(jìn)行commit,可以看到規(guī)范配置成功,選擇輔助信息:
鏈接遠(yuǎn)程倉庫:git remote add origin https://gitee.com/airheaven/kfg-vue.git
(這里輸入你剛剛創(chuàng)建好的倉庫),然后git push origin master
將項(xiàng)目推送到master分支
5.3、git日常使用
npm run commit:對代碼進(jìn)行commit
git push origin master:將項(xiàng)目推送到master分支
?? 資源下載與學(xué)習(xí)
本部分的代碼已上傳至CSDN:https://download.csdn.net/download/air__Heaven/87530349
項(xiàng)目代碼正在gitee同步更新中,請大家給個(gè)star??,項(xiàng)目地址:https://gitee.com/airheaven/kfg-vue文章來源:http://www.zghlxwxcb.cn/news/detail-548639.html
?? 支持我:點(diǎn)贊??+收藏??+留言??文章來源地址http://www.zghlxwxcb.cn/news/detail-548639.html
到了這里,關(guān)于【Vue H5項(xiàng)目實(shí)戰(zhàn)】從0到1的自助點(diǎn)餐系統(tǒng)—— 搭建腳手架(Vue3.2 + Vite + TS + Vant + Pinia + Node.js)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!