项目搭建文档
项目概述
本项目是一个基于 Uni-app 框架开发的多端应用,支持 H5、微信小程序、支付宝小程序等多种平台。项目使用 Vue 3 作为前端框架,结合 Pinia 进行状态管理,同时集成了 Tailwind CSS 进行样式开发。
技术栈
主要框架和库
- Uni-app :版本
3.0.0-4060620250520001
,用于开发多端应用。 - Vue 3 :版本
^3.5.17
,用于构建用户界面。 - Pinia :版本
^2.3.1
,Vue 3 的状态管理库。 - Tailwind CSS :版本
^4.1.10
,用于快速构建样式。 - weapp-tailwindcss :版本
^4.1.9
,uniapp适配库。
开发工具
- Vite :版本
5.2.8
,快速的构建工具。 - TypeScript :版本
^4.9.5
,为 JavaScript 添加静态类型检查。 - ESLint :版本
^9.29.0
,用于代码规范检查。 - prettier :版本
^3.5.3
,用于代码规范检查。 - stylelint :版本
^16.20.0
,用于代码规范检查。
提交校验
- husky :版本
^15.5.2
,用于代码规范检查。 - lint-staged :版本
^8.0.3
,用于代码规范检查。
UI库
- uni-ui:官方UI库。
- wot-design-uni:wot-design-uni组件库基于vue3+Typescript构建(sass版本:1.78.0)。
项目基本结构
plaintext
f:\taokou-front-end/
├── .editorconfig
├── .env.development
├── .env.production
├── .env.test
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .stylelintignore
├── .stylelintrc.mjs
├── .vscode/
│ └── settings.json
├── README.md
├── commitlint.config.mjs
├── docs/
│ └── .vitepress/
│ ├── cache/
│ ├── config.ts
│ └── utils/
├── eslint.config.mjs
├── index.html
├── package.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── shims-uni.d.ts
├── src/
│ ├── App.vue
│ ├── api/
│ ├── components/
│ ├── components.d.ts
│ ├── core/
│ ├── env.d.ts
│ ├── hook/
│ ├── index.md
│ ├── main.ts
│ ├── manifest.json
│ ├── pages/
│ ├── pages.json
│ ├── shime-uni.d.ts
│ ├── static/
│ ├── store/
│ ├── uni-app.d.ts
│ ├── uni.scss
│ ├── uni_modules/
│ └── utils/
├── tailwind.config.js
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
项目搭建步骤
1. 初始化项目
首先,确保你已经安装了 Node.js 和 pnpm。然后使用 Uni-app 官方脚手架创建一个新的项目:
bash
pnpm init uni-app@latest my-uniapp-project --template vue-ts
cd my-uniapp-project
2. 集成依赖
集成 Vue 3 和 Pinia
bash
pnpm add vue@^3.5.17 pinia@^2.3.1 pinia-plugin-persistedstate@^4.3.0
在 src/store/index.ts
中配置 Pinia:
typescript
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
export default pinia
在 src/main.ts
中引入并使用 Pinia:
typescript
import { createApp } from 'vue'
import App from './App.vue'
import pinia from './store'
const app = createApp(App)
app.use(pinia)
app.mount('#app')
集成 Tailwind CSS
bash
pnpm add -D tailwindcss@^4.1.10 @tailwindcss/vite@^4.1.10 weapp-tailwindcss@^4.1.9
初始化 Tailwind CSS 配置文件:
bash
npx tailwindcss init
在 tailwind.config.mjs
中配置:
javascript
module.exports = {
content: ['./src/**/*.{vue,js,ts,jsx,tsx}'],
theme: {
extend: {}
},
plugins: []
}
在 vite.config.ts
中添加 Tailwind CSS 插件:
typescript
import tailwindcss from '@tailwindcss/vite'
import { UnifiedViteWeappTailwindcssPlugin } from 'weapp-tailwindcss/vite'
const isH5 = process.env.UNI_PLATFORM === 'h5'
const isApp = process.env.UNI_PLATFORM === 'app'
const WeappTailwindcssDisabled = isH5 || isApp
// 在 defineConfig 的 plugins 数组中添加
plugins: [
tailwindcss(),
UnifiedViteWeappTailwindcssPlugin({
rem2rpx: true,
disabled: WeappTailwindcssDisabled,
cssPreflight: {
replace: true,
rootValue: 32
}
})
]
3. 配置开发工具
配置 TypeScript
在 tsconfig.json
中添加以下配置:
json
{
"compilerOptions": {
"noImplicitAny": false,
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "node",
"lib": ["ESNext", "DOM", "DOM.Iterable"],
"skipLibCheck": false,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "preserve",
"jsxImportSource": "vue",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true,
"allowJs": true,
"baseUrl": ".",
"paths": { "@/*": ["src/*"], "#/*": ["types/*"] },
"types": ["@dcloudio/types"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
配置 ESLint 和 Prettier
安装相关依赖:
bash
pnpm add -D eslint@^9.28.0 eslint-config-prettier@^10.1.5 eslint-plugin-prettier@^5.4.1 prettier@^3.5.3
在项目根目录创建 eslint.config.mjs
和 .prettierrc
文件进行配置。
typescript
/**.eslint.config.mjs*/
// import pluginJs from '@eslint/js'
import simpleImportSort from 'eslint-plugin-simple-import-sort'
import pluginVue from 'eslint-plugin-vue'
import globals from 'globals'
import tseslint from 'typescript-eslint'
import vueParser from 'vue-eslint-parser'
/** @type {import('eslint').Linter.Config[]} */
export default [
{
// 设置 ECMAScript 版本和模块类型
languageOptions: {
parser: vueParser, // 使用vue解析器,这个可以识别vue文件
parserOptions: {
parser: tseslint.parser, // 在vue文件上使用ts解析器
sourceType: 'module', // 指定代码使用 ES 模块化(import 和 export)语法
ecmaFeatures: {
jsx: true
}
},
globals: {
...globals.browser,
...globals.node,
var1: 'writable', // 声明一个可写的全局变量
ResponseResult: 'readonly', // 声明一个只读的全局变量
ResponseList: 'readonly',
ResponseData: 'readonly'
}
}
},
/** js推荐配置 */
// pluginJs.configs.recommended,
...tseslint.configs.recommended,
...pluginVue.configs['flat/essential'],
/** vue推荐配置 */
...pluginVue.configs['flat/recommended'],
{
languageOptions: { parserOptions: { parser: tseslint.parser } },
files: ['**/*.{js,mjs,cjs,ts,vue}'],
plugins: {
'simple-import-sort': simpleImportSort
},
rules: {
'simple-import-sort/imports': 2,
'simple-import-sort/exports': 2,
'vue/no-v-html': 'off',
'no-undef': 'off',
'no-unused-vars': 1,
'@typescript-eslint/no-unused-vars': 1,
'prettier/prettier': 'off',
'arrow-body-style': 'off',
'prefer-arrow-callback': 'off',
'vue/attributes-order': [
'error',
{
order: [
'DEFINITION',
'LIST_RENDERING',
'CONDITIONALS',
'RENDER_MODIFIERS',
'GLOBAL',
'UNIQUE',
'TWO_WAY_BINDING',
'OTHER_DIRECTIVES',
'OTHER_ATTR',
'EVENTS',
'CONTENT'
]
}
],
'vue/one-component-per-file': 'off',
'vue/comment-directive': [
'error',
{
reportUnusedDisableDirectives: false
}
],
'vue/max-attributes-per-line': [
'error',
{
singleline: {
max: 10
},
multiline: {
max: 4
}
}
],
'vue/html-self-closing': [
0,
{
html: { normal: 'never', void: 'always' },
svg: 'always',
math: 'always'
}
],
'vue/singleline-html-element-content-newline': 'off',
'vue/block-order': [
'error',
{
order: [['template', 'script'], 'style']
}
],
'vue/multi-word-component-names': 'off', // 禁用vue文件强制多个单词命名
'@typescript-eslint/no-explicit-any': 'off', //允许使用any
'@typescript-eslint/no-this-alias': [
'error',
{
allowedNames: ['that'] // this可用的局部变量名称
}
],
'no-unused-expressions': 'off',
'@typescript-eslint/no-unused-expressions': 'off',
'@typescript-eslint/ban-ts-comment': 'off', //允许使用@ts-ignore
'@typescript-eslint/no-non-null-assertion': 'off', //允许使用非空断言
'no-console': [
//提交时不允许有console.log
'off',
{
allow: ['warn', 'error']
}
],
'no-debugger': 'warn' //提交时不允许有debugger
}
}
]
json
/**.prettierrc*/
{
"eslintIntegration": true,
"printWidth": 100,
"tabWidth": 2,
"useTabs": false,
"semi": false,
"vueIndentScriptAndStyle": true,
"singleQuote": true,
"quoteProps": "as-needed",
"bracketSpacing": true,
"trailingComma": "none",
"jsxBracketSameLine": false,
"jsxSingleQuote": false,
"arrowParens": "always",
"insertPragma": false,
"requirePragma": false,
"proseWrap": "never",
"htmlWhitespaceSensitivity": "strict",
"endOfLine": "lf",
"plugins": ["prettier-plugin-tailwindcss"]
}
配置 stylelint
安装相关依赖:
bash
pnpm add -D stylelint stylelint-config-recess-order stylelint-config-standard stylelint-config-standard-scss stylelint-config-standard-vue stylelint-order stylelint-scss
在项目根目录创建 .stylelintrc.mjs
文件进行配置。
typescript
/**.stylelintrc.mjs*/
export default {
extends: [
'stylelint-config-standard',
'stylelint-config-standard-scss',
'stylelint-config-standard-vue',
'stylelint-config-recess-order'
],
overrides: [
{
files: ['*.scss', '**/*.scss'],
customSyntax: 'postcss-scss'
},
{
files: ['*.vue', '**/*.vue'],
customSyntax: 'postcss-html'
}
],
ignoreFiles: ['**/uni_modules/**/*'],
rules: {
'property-no-unknown': [
true,
{
ignoreProperties: ['page']
}
],
'selector-type-no-unknown': [
true,
{
ignore: ['custom-elements'],
ignoreTypes: ['page']
}
],
'function-no-unknown': null,
'scss/no-global-function-names': null,
'no-empty-source': null,
'selector-class-pattern': null,
'no-duplicate-selectors': null,
'declaration-property-value-no-unknown': null,
'unit-no-unknown': [
true,
{
ignoreUnits: ['rpx']
}
],
'function-name-case': [
'lower',
{
ignoreFunctions: ['constant', 'env']
}
]
}
}
配置 lint-staged提交规范
安装相关依赖:
bash
pnpm add -D husky lint-staged
在 package.json
中添加以下配置:
json
{
"scripts": {
"prepare": "husky install && (grep -q 'commitlint' .husky/commit-msg 2>/dev/null || husky add .husky/commit-msg 'npx --no-install commitlint --edit $1')"
}
"lint-staged": {
"src/**/*.{vue,js,jsx,ts,tsx,json}": ["pnpm run lint", "pnpm run prettier"],
"*.{css, less}": ["stylelint --fix"]
}
}
文档搭建
bash
pnpm add vitepress
目录结构
lua
├─ docs
│ ├─ .vitepress
│ │ └─ config.js
│ │ └─ utils.js
│ │ │ └─ sidebar.ts
├─ src
│ └─ index.md
└─ package.json
typescript
/**
sidebar.ts
*/
import { readdirSync, statSync } from 'fs'
import { join } from 'path'
interface SidebarItem {
text: string
link?: string
items?: SidebarItem[]
}
function generateSidebarItems(dir: string, basePath: string = ''): SidebarItem[] {
const items: SidebarItem[] = []
const files = readdirSync(dir)
files.forEach((file) => {
const fullPath = join(dir, file)
const stat = statSync(fullPath)
if (stat.isDirectory()) {
// 如果是目录,递归处理
const subItems = generateSidebarItems(fullPath, join(basePath, file))
if (subItems.length > 0) {
items.push({
text: file,
items: subItems
})
}
} else if (file.endsWith('.md')) {
// 如果是 Markdown 文件
const fileName = file.replace('.md', '')
// 确保 link 以 / 开头,并移除多余的斜杠
const link = '/' + join(basePath, fileName).replace(/\\/g, '/').replace(/\/+/g, '/')
items.push({
text: fileName,
link: link
})
}
})
return items
}
export function generateSidebar(srcPath: string) {
const sidebar = generateSidebarItems(srcPath)
return {
'/': [
{
items: sidebar
}
]
}
}
typescript
/**
config.js
*/
import { resolve } from 'path'
import { defineConfig } from 'vitepress'
import { generateSidebar } from './utils/sidebar'
// https://vitepress.dev/reference/site-config
export default defineConfig({
lang: 'zh',
title: '软件文档',
cleanUrls: true,
lastUpdated: true,
srcDir: '../src',
description: 'A VitePress Site',
vue: {
// @vitejs/plugin-vue 选项
},
themeConfig: {
search: {
provider: 'local'
},
footer: {
message: 'Released under the MIT License.',
copyright: 'Copyright © 2019-present Evan You'
},
nav: [],
sidebar: generateSidebar(resolve(__dirname, '../../src')),
socialLinks: [{ icon: 'gitee', link: 'https://gitee.com/fxg1997/taokou-front-end.git' }]
}
})
开发环境运行
H5 平台
bash
pnpm run dev:h5
微信小程序平台
bash
pnpm run dev:mp-weixin
5. 生产环境构建
H5 平台
bash
pnpm run build:h5
微信小程序平台
bash
pnpm run build:mp-weixin
package.json
json
/** package.json*/
{
"name": "@apps/shoumaba",
"version": "0.0.0",
"type": "module",
"scripts": {
"ins": "rm -rf node_modules pnpm-lock.yaml && pnpm install",
"force": "pnpm store prune && pnpm install --force",
"dev:custom": "uni -p",
"dev:test": "uni --mode test",
"dev:h5": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-jd": "uni -p mp-jd",
"dev:mp-kuaishou": "uni -p mp-kuaishou",
"dev:mp-lark": "uni -p mp-lark",
"dev:mp-qq": "uni -p mp-qq",
"dev:mp-toutiao": "uni -p mp-toutiao",
"dev:mp-weixin": "uni -p mp-weixin",
"dev:mp-xhs": "uni -p mp-xhs",
"dev:quickapp-webview": "uni -p quickapp-webview",
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
"build:custom": "uni build -p",
"build:h5": "uni build",
"build:h5:test": "uni build --mode test",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp-baidu": "uni build -p mp-baidu",
"build:mp-jd": "uni build -p mp-jd",
"build:mp-kuaishou": "uni build -p mp-kuaishou",
"build:mp-lark": "uni build -p mp-lark",
"build:mp-qq": "uni build -p mp-qq",
"build:mp-toutiao": "uni build -p mp-toutiao",
"build:mp-weixin": "uni build -p mp-weixin",
"build:mp-xhs": "uni build -p mp-xhs",
"build:quickapp-webview": "uni build -p quickapp-webview",
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
"type-check": "vue-tsc --noEmit",
"docs:dev": "vitepress dev docs --port 8080",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs",
"lint": "eslint src --cache --fix --ext .ts,.tsx,.vue,.js,.jsx --max-warnings 0",
"prettier": "prettier --write .",
"lint:style": "stylelint --cache --fix \"src/**/*.{less,postcss,css,scss,vue}\" --cache --cache-location node_modules/.cache/stylelint/",
"prepare": "husky install && (grep -q 'commitlint' .husky/commit-msg 2>/dev/null || husky add .husky/commit-msg 'npx --no-install commitlint --edit $1')"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-4060620250520001",
"@dcloudio/uni-app-harmony": "3.0.0-4060620250520001",
"@dcloudio/uni-app-plus": "3.0.0-4060620250520001",
"@dcloudio/uni-components": "3.0.0-4060620250520001",
"@dcloudio/uni-h5": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-alipay": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-baidu": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-harmony": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-jd": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-lark": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-qq": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-toutiao": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-weixin": "3.0.0-4060620250520001",
"@dcloudio/uni-mp-xhs": "3.0.0-4060620250520001",
"@dcloudio/uni-quickapp-webview": "3.0.0-4060620250520001",
"@vue/runtime-dom": "^3.5.17",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"deep-pick-omit": "^1.2.1",
"destr": "^2.0.5",
"jsencrypt": "^3.3.2",
"lodash": "^4.17.21",
"pinia": "^2.3.1",
"pinia-plugin-persistedstate": "^4.3.0",
"postcss": "^8.5.6",
"vconsole": "^3.15.1",
"vue": "^3.5.17",
"vue-i18n": "^9.14.4"
},
"devDependencies": {
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.3",
"@dcloudio/types": "^3.4.15",
"@dcloudio/uni-automator": "3.0.0-4060620250520001",
"@dcloudio/uni-cli-shared": "3.0.0-4060620250520001",
"@dcloudio/uni-stacktracey": "3.0.0-4060620250520001",
"@dcloudio/vite-plugin-uni": "3.0.0-4060620250520001",
"@eslint/js": "^9.29.0",
"@tailwindcss/vite": "^4.1.10",
"@types/crypto-js": "^4.2.2",
"@types/lodash": "^4.17.18",
"@types/node": "^22.15.32",
"@typescript-eslint/eslint-plugin": "^8.34.1",
"@typescript-eslint/parser": "^8.34.1",
"@vitejs/plugin-basic-ssl": "^1.2.0",
"@vitejs/plugin-vue-jsx": "^4.2.0",
"@vue/runtime-core": "^3.5.17",
"@vue/tsconfig": "^0.1.3",
"eslint": "^9.29.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-prettier": "^5.5.0",
"eslint-plugin-simple-import-sort": "^12.1.1",
"eslint-plugin-vue": "^9.33.0",
"globals": "^13.24.0",
"husky": "^8.0.3",
"lint-staged": "^15.5.2",
"prettier": "^3.5.3",
"prettier-plugin-tailwindcss": "^0.6.12",
"rollup-plugin-visualizer": "^5.14.0",
"sass": "1.78.0",
"lightningcss": "^1.30.1",
"stylelint": "^16.20.0",
"stylelint-config-recess-order": "^6.1.0",
"stylelint-config-standard": "^37.0.0",
"stylelint-config-standard-scss": "^14.0.0",
"stylelint-config-standard-vue": "^1.0.0",
"stylelint-order": "^6.0.4",
"stylelint-scss": "^6.12.1",
"tailwindcss": "^4.1.10",
"typescript": "^4.9.5",
"typescript-eslint": "^8.34.1",
"unplugin-auto-import": "^19.3.0",
"unplugin-vue-components": "^28.7.0",
"vite": "5.2.8",
"vite-plugin-cdn-import": "^1.0.1",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-externals": "^0.6.2",
"vite-plugin-imagemin": "^0.6.1",
"vite-plugin-stylelint": "^6.0.0",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-devtools": "^7.7.7",
"vitepress": "^1.6.3",
"vue-eslint-parser": "^9.4.3",
"vue-tsc": "^1.8.27",
"weapp-tailwindcss": "^4.1.9"
},
"lint-staged": {
"src/**/*.{vue,js,jsx,ts,tsx,json}": ["pnpm run lint", "pnpm run prettier"],
"*.{css, less}": ["pnpm run lint:style"]
}
}
项目基础配置
env环境
- 在项目根目录添加
.env
文件,用于配置通用环境变量。在src
目录中使用环境变量时,变量名必须以VITE_
开头。例如,在main.js
中可以这样输出环境变量:
javascript
console.log('环境变量', import.meta.env)
- 在 vite 配置中使用环境变量没有限制,可以直接通过
import.meta.env.XXX
访问。
vite 环境模式
vite支持两种默认环境模式:
- 开发环境(development) :在执行
vite
命令时使用,对应的环境变量文件为.env.development
。 - 生产环境(production) :在执行
vite build
命令时使用,对应的环境变量文件为.env.production
。
自定义生产环境
若需要自定义生产环境(如 release
环境),可按以下步骤操作:
- 在
package.json
中添加脚本:
json
"scripts": {
"release": "vite build --mode release"
}
- 在根目录创建
.env.release
文件,并声明变量:
plaintext
VITE_BASE_URL = ''
VITE_BASE_DEFAULTIMG = ''
- 执行
yarn release
命令,加载.env.release环境
api封装
- 封装考虑重复请求,统一拦截,防止重复请求
- 重复用map实现,有相同的key,值会被覆盖
javascript
const pendingMap = new Map()
/**
* 生成每个请求唯一的键
*/
function getPendingKey(config): string {
const { url, method } = config
const { data = {}, params = {} } = config
return [url, method, JSON.stringify(data), JSON.stringify(params)].join('&')
}
/**
* 储存每个请求唯一值, 用于取消请求
*/
function addPending(config, requestTask): void {
const pendingKey = getPendingKey(config)
pendingMap.set(pendingKey, requestTask)
}
/**
* 删除重复的请求
*/
function removePending(config): void {
const pendingKey = getPendingKey(config)
if (pendingMap.has(pendingKey)) {
const requestTask = pendingMap.get(pendingKey)!
requestTask.abort() // 取消请求
pendingMap.delete(pendingKey)
}
}
/**
* 删除重复的请求
*/
function completePending(config): void {
const pendingKey = getPendingKey(config)
if (pendingMap.has(pendingKey)) {
pendingMap.delete(pendingKey)
}
}
- 封装请求
javascript
export function createRequest(defaultConfig {}) {
// 默认配置
const defaultOptions = {
baseURL: '',
timeout: 60000,
header: {},
dataType: 'json',
responseType: 'text',
repeatRequestCancel: true,
...defaultConfig
}
// 请求拦截器数组
const requestInterceptors: RequestInterceptor[] = []
// 响应拦截器数组
const responseInterceptors: ResponseInterceptor[] = []
/**
* 添加请求拦截器
*/
function interceptRequest(
onFulfilled: RequestInterceptor['onFulfilled'],
onRejected?: RequestInterceptor['onRejected']
) {
requestInterceptors.push({
onFulfilled,
onRejected
})
}
/**
* 添加响应拦截器
*/
function interceptResponse(
onFulfilled: ResponseInterceptor['onFulfilled'],
onRejected?: ResponseInterceptor['onRejected']
) {
responseInterceptors.push({
onFulfilled,
onRejected
})
}
/**
* 执行请求拦截器链
*/
async function processRequestInterceptors(config) {
let currentConfig = { ...config }
for (const interceptor of requestInterceptors) {
try {
const result = await interceptor.onFulfilled(currentConfig)
currentConfig = result || currentConfig
} catch (error) {
if (interceptor.onRejected) {
interceptor.onRejected(error)
}
throw error
}
}
return currentConfig
}
/**
* 执行响应拦截器链
*/
async function processResponseInterceptors(response){
let currentResponse = { ...response }
for (const interceptor of responseInterceptors) {
try {
const result = await interceptor.onFulfilled(currentResponse)
currentResponse = result || currentResponse
} catch (error) {
if (interceptor.onRejected) {
interceptor.onRejected(error)
}
throw error
}
}
return currentResponse
}
/**
* 发起请求的核心函数
*/
async function request<T = any>(config) {
// 合并默认配置
const mergedConfig = {
...defaultOptions,
...config,
header: { ...defaultOptions.header, ...config.header }
}
try {
// 执行请求拦截器链
const processedConfig = await processRequestInterceptors(mergedConfig)
// 处理重复请求
if (processedConfig.repeatRequestCancel) {
removePending(processedConfig)
}
// 发起请求
return await new Promise((resolve, reject) => {
const requestTask = uni.request({
url: processedConfig.baseURL
? processedConfig.baseURL + processedConfig.url
: processedConfig.url,
data: processedConfig.data,
method: processedConfig.method,
header: processedConfig.header,
timeout: processedConfig.timeout,
dataType: processedConfig.dataType,
responseType: processedConfig.responseType,
success: async (res) => {
// 请求完成后,移除pending中的请求
if (processedConfig.repeatRequestCancel) {
completePending(processedConfig)
}
// 构造响应对象
const response{
data: res.data,
statusCode: res.statusCode,
header: res.header,
cookies: res.cookies,
config: processedConfig
}
try {
// 执行响应拦截器链
const processedResponse = await processResponseInterceptors(response)
resolve(processedResponse.data)
} catch (error) {
reject(error)
}
},
fail: (err) => {
// 请求失败后,移除pending中的请求
if (processedConfig.repeatRequestCancel) {
completePending(processedConfig)
}
reject(err)
}
})
// 存储请求任务用于取消
if (processedConfig.repeatRequestCancel) {
addPending(processedConfig, requestTask)
}
})
} catch (error) {
console.log('【请求未知】', error)
return Promise.reject(error)
}
}
// 添加请求方法别名
const requestInstance = {
request,
interceptors: {
request: {
use: interceptRequest
},
response: {
use: interceptResponse
}
}
}
return requestInstance
}
- 使用
javascript
import { createRequest } from './request/index'
const apiRequest = createRequest({
baseURL: 'https://api.example.com',
timeout: 30000,
repeatRequestCancel: true
})
// 添加拦截器
apiRequest.interceptors.request.use(
(config: RequestConfig) => {
// 请求前处理
return config
},
(error: Error) => {
return Promise.reject(error)
}
)
// 添加默认响应拦截器
apiRequest.interceptors.response.use(
(response: UniResponse) =>{
// 请求前处理
return config
},
(error: Error) => {
return Promise.reject(error)
}
)
// 导出请求实例
export const request = apiRequest.request
export function request(data) {
return request({
url: '/index',
method: 'POST',
data
})
}
- 根据业务需求拓展 缓存 重试