创建项目

1.创建 vite+vue3+uniapp 项目:
npx degit dcloudio/uni-preset-vue#vite my-vue3-project 2. cd my-vue3-project 3. 安装依赖:npm install 4. 安装 uview(ui 组件库):npm i uview-plus

  • 在项目根目录 App.vue 引入主样式文件
1
2
3
<style lang="scss">
/* 引入 uview-plus 样式 */ @import "uview-plus/index.scss";
</style>
  • 配置 main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// main.js
import { createSSRApp } from "vue";
import App from "./App.vue";
// 导入 uview-plus
import uviewPlus from "uview-plus";

export function createApp() {
const app = createSSRApp(App);
// 注册 uview-plus
app.use(uviewPlus);
return {
app,
};
}

配置 pages.json

1
2
3
4
5
6
7
8
9
10
11
12
// pages.json
{
"easycom": {
// 自动扫描并引入 uview-plus 组件
"^u-(.*)": "uview-plus/components/u-$1/u-$1.vue"
},
"pages": [
// 你的页面配置...
{
}
]
}

配置 vite.config.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// vite.config.js
import { defineConfig } from "vite";
import path from "path";

export default defineConfig({
resolve: {
// 设置别名,解决 uview-plus 内部路径引用
alias: {
"@": path.resolve(__dirname, "./src"),
"uview-plus": path.resolve(__dirname, "./node_modules/uview-plus"),
},
},
css: {
preprocessorOptions: {
scss: {
// 引入 uview-plus 的全局 SCSS 变量(用于主题定制)
additionalData: `@import "uview-plus/theme/index.scss";`,
},
},
},
});
  1. flyio(类似于 axios 的请求工具,代替 uni.request):npm i flyio
  • 配置示例:
    新建目录20250811194657
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import Fly from "flyio";

// 创建 fly 实例
const fly = new Fly();

// 1. 基础配置
fly.config.baseURL = "https://api.example.com"; // 接口基础地址
fly.config.timeout = 10000; // 超时时间(10秒)
fly.config.headers = {
"Content-Type": "application/json;charset=UTF-8",
};

// 2. 请求拦截器(请求发送前处理)
fly.interceptors.request.use(
(config) => {
// 示例:添加 token 到请求头
const token = uni.getStorageSync("token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
// 显示加载中动画
uni.showLoading({ title: "加载中...", mask: true });
return config;
},
(err) => {
uni.hideLoading();
return Promise.reject(err);
}
);

// 3. 响应拦截器(请求返回后处理)
fly.interceptors.response.use(
(response) => {
uni.hideLoading(); // 隐藏加载中动画
const res = response.data;

// 假设接口返回格式:{ code: 200, data: ..., msg: '' }
if (res.code === 200) {
return res.data; // 成功时返回数据主体
} else {
// 业务错误(如参数错误、权限不足)
uni.showToast({
title: res.msg || "操作失败",
icon: "none",
duration: 2000,
});
return Promise.reject(res);
}
},
(err) => {
uni.hideLoading(); // 隐藏加载中动画
// 网络错误处理
let errorMsg = "网络异常,请稍后重试";
if (err.status) {
errorMsg = `请求错误: ${err.status}`; // 如 404、500 等
} else if (err.message.includes("timeout")) {
errorMsg = "请求超时,请重试";
}
uni.showToast({
title: errorMsg,
icon: "none",
duration: 2000,
});
return Promise.reject(err);
}
);

export default fly;
  • 使用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<template>
<view class="index-page">
<!-- uview-plus 组件示例 -->
<u-navbar title="uview+flyio 示例" :border-bottom="false"></u-navbar>

<view class="content">
<!-- 按钮组 -->
<u-button type="primary" @click="getList" class="btn">
发送 GET 请求
</u-button>

<u-button type="success" @click="submitData" class="btn">
发送 POST 请求
</u-button>

<!-- 列表展示接口数据 -->
<u-list v-if="list.length > 0" title="请求结果">
<u-list-item
v-for="(item, index) in list"
:key="index"
:title="item.name"
:desc="item.desc"
></u-list-item>
</u-list>

<!-- 空状态 -->
<u-empty v-if="list.length === 0 && !loading" text="暂无数据"></u-empty>
</view>
</view>
</template>

<script setup>
import { ref } from "vue";
import fly from "@/utils/request.js"; // 引入 flyio 实例

// 数据定义
const list = ref([]);
const loading = ref(false);

// 发送 GET 请求示例
const getList = async () => {
try {
loading.value = true;
// 调用接口(参数会自动拼接到 URL)
const data = await fly.get("/demo/list", {
page: 1,
limit: 5,
});
list.value = data; // 赋值接口返回的数据
} catch (err) {
console.error("GET 请求失败:", err);
} finally {
loading.value = false;
}
};

// 发送 POST 请求示例
const submitData = async () => {
try {
// 调用接口(参数在请求体中)
const result = await fly.post("/demo/submit", {
name: "测试数据",
time: new Date().toLocaleString(),
});
// 显示提交成功弹窗
uni.showModal({
title: "成功",
content: "POST 请求提交成功",
showCancel: false,
});
console.log("提交结果:", result);
} catch (err) {
console.error("POST 请求失败:", err);
}
};
</script>

<style scoped lang="scss"></style>
  1. dayjs(时间处理代替 moment.js):npm i dayjs
  2. 数据处理 lodash(函数库):npm i lodash
  3. 状态管理 pinia:npm i pinia
  • 在 main.js 注册 pinia
1
2
3
4
5
6
// 1. 导入 pinia
import { createPinia } from "pinia";
// 2. 创建 pinia 实例
const pinia = createPinia();
// 3. 注册 pinia 到 app
app.use(pinia);
  • 创建用户状态模块(src/store/index.js)index.js 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// src/store/user.js
import { defineStore } from "pinia";

// 定义并导出 store(参数1:唯一id,参数2:配置对象)
export const useUserStore = defineStore("index", {
// 1. 状态(类似 Vuex 的 state)
state: () => ({
// 用户信息
}),

// 2. 计算属性(类似 Vuex 的 getter)
getters: {
// 示例:获取用户名称(带默认值)
},

// 3. 方法(类似 Vuex 的 action,支持异步)
actions: {
// // 登录(异步示例,如调用接口)
// // async login(loginForm) {
// // 假设使用之前配置的 flyio 发送请求
// // const fly = await import('@/utils/request.js')
// // const res = await fly.default.post('/api/login', loginForm)
// // 更新状态(直接修改 state,无需 mutation)
// this.info = res.userInfo
// this.isLogin = true
// // 持久化到本地存储
// uni.setStorageSync('userInfo', res.userInfo)
},
});
  1. 路由管理 uni-simple-router:
  • 创建路由配置文件(src/router/index.js)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// 1. 导入依赖
import { createRouter, createWebHashHistory } from "uni-simple-router";

// 2. 定义路由表(与 pages.json 对应,支持嵌套路由)
const routes = [
{
path: "/",
redirect: "/pages/index/index", // 重定向到首页
},
{
path: "/pages/index/index",
name: "Home", // 路由名称(唯一)
component: () => import("@/pages/index/index.vue"), // 组件路径
meta: {
title: "首页", // 页面标题
requiresAuth: false, // 是否需要登录
},
},
{
path: "/pages/user/index",
name: "User",
component: () => import("@/pages/user/index.vue"),
meta: {
title: "用户中心",
requiresAuth: true, // 需要登录才能访问
},
},
{
path: "/pages/detail/:id", // 动态路由参数
name: "Detail",
component: () => import("@/pages/detail/index.vue"),
meta: {
title: "详情页",
},
},
// 404 页面
{
path: "/:pathMatch(.*)*",
name: "NotFound",
component: () => import("@/pages/404.vue"),
},
];

// 3. 创建路由实例
const router = createRouter({
history: createWebHashHistory(), // 路由模式(hash 模式,uni-app 推荐)
routes, // 路由表
});

// 4. 全局路由守卫(示例:登录验证)
router.beforeEach((to, from, next) => {
// 如果页面需要登录,且未登录,则跳转到登录页
if (to.meta.requiresAuth && !uni.getStorageSync("token")) {
return next({
path: "/pages/login/index",
query: { redirect: to.fullPath }, // 记录跳转前的路径,登录后可返回
});
}
// 设置页面标题
if (to.meta.title) {
uni.setNavigationBarTitle({ title: to.meta.title });
}
next(); // 必须调用 next() 继续导航
});

// 5. 导出路由实例
export default router;

在 main.js 中注册

1
2
import router from "./router"; // 导入路由实例
app.use(router); // 注册路由

uni-simple-router 需要与 pages.json 配合,但路由管理以插件配置为准,pages.json 只需保留基础配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
{
"pages": [
{ "path": "pages/index/index" },
{ "path": "pages/user/index" },
{ "path": "pages/detail/index" },
{ "path": "pages/login/index" },
{ "path": "pages/404" }
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app"
}
}
  1. 表单验证 vee-validate
  2. 自动引入 ui 组件:npm i unplugin-vue-components -D
  3. 自动引入npm i unplugin-auto-import -D
  4. 配置 easycom 组件模式
    在 pages.json 中配置
1
2
3
4
5
6
7
8
{
"easycom": {
"autoscan": true,
"custom": {
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue" // 自动识别uView组件
}
}
}