外观
前端开发规范
2026-04-01
基于 nebula-web 实际用法整理,适用于所有 Nebula IceStark 微前端子应用项目。
读完本文,你将掌握 API 函数写法、TypeScript 类型定义、路由配置、IceStark 生命周期和 Token 工具的标准用法。
目录
- 一、技术栈
- 二、共享包
- 三、项目结构
- 四、API 请求实例
- 五、API 函数写法规范
- 六、TypeScript 类型定义规范
- 七、路由规范
- 八、IceStark 子应用生命周期
- 九、Token / Auth 工具
- 十、环境变量约定
- 十一、代码规范要点
一、技术栈
| 技术 | 版本 | 说明 |
|---|---|---|
| Vue | 3.5 | 组合式 API(<script setup>) |
| Vite | 5 | 构建工具 |
| TypeScript | 5 | 全量类型覆盖 |
| Element Plus | 2.8 | UI 组件库 |
| IceStark | 2.8.4 | 微前端子应用 SDK(@ice/stark-app) |
| pnpm | 9+ | 包管理器 |
| Node.js | 22+ | 运行环境 |
二、共享包
所有子应用必须使用以下共享包,不要自行重复封装:
| 包名 | 来源 | 说明 |
|---|---|---|
@nebula-web/types | GitLab Package Registry | 共享 TypeScript 类型定义(R<T>、PageResult<T> 等) |
@nebula-web/utils | GitLab Package Registry | 共享工具函数(createRequest、Token 工具等) |
@nebula-web/components | GitLab Package Registry | 共享业务组件库 |
@ice/stark-app | npm | IceStark 子应用 SDK |
类型定义参考(来自 @nebula-web/types):
// 统一 API 响应类型,对应后端 R<T>
interface R<T = unknown> {
code: number // 0 或 200 表示成功
message?: string // nebula 新版后端使用 message
msg?: string // 兼容旧版
data: T
}
// 分页响应类型,对应后端 PageResult<T>
// 注意:后端字段为 pageNo/pageSize(非 current/size)
interface PageResult<T> {
records: T[]
total: number
pageNo: number
pageSize: number
pages: number
}三、项目结构
src/
├── api/
│ ├── index.ts # 创建 Axios 请求实例(只此一处,统一导出)
│ └── {域名}.ts # 业务 API 函数(如 system.ts)
├── pages/
│ └── {模块名}/
│ └── index.vue # 页面组件
├── router/
│ └── routes.ts # 路由配置(路径不含 activePath 前缀)
├── App.vue
└── main.ts # IceStark 生命周期入口四、API 请求实例
只在 src/api/index.ts 创建请求实例,业务文件导入使用:
import { createRequest } from '@nebula-web/utils'
// 回退路径 /nebula-system 由 Vite proxy 在开发环境转发到后端服务
export const systemRequest = createRequest(import.meta.env.VITE_SYSTEM_API_BASE || '/nebula-system')createRequest 内置能力(无需业务层重复处理):
- 请求拦截:自动注入
Authorization: Bearer {token} - 响应拦截:
code === 0 || code === 200时自动解包,业务代码直接拿到T(不是R<T>) - 401 处理:自动静默刷新 Token,失败后触发
event.emit('401ToLogin')跳转登录 - 文件流响应:检测到 Excel 等文件流时直接返回
response.data,不解包
五、API 函数写法规范
import { systemRequest } from './index'
import type { PageResult } from '@nebula-web/types'
import type { UserPageParam, UserRespVO, UserDetailVO, UserSaveParam } from './types'
// 分页查询:POST + body
export function getUserPage(data: UserPageParam): Promise<PageResult<UserRespVO>> {
return systemRequest.post('/system/user/page', data)
}
// 查询详情:GET + pathVariable
export function getUserById(id: number): Promise<UserDetailVO> {
return systemRequest.get(`/system/user/${id}`)
}
// 新增:POST + body,返回新 id
export function createUser(data: UserSaveParam): Promise<number> {
return systemRequest.post('/system/user', data)
}
// 修改:PUT + body
export function updateUser(data: UserSaveParam): Promise<void> {
return systemRequest.put('/system/user', data)
}
// 批量删除:DELETE + body(id 列表)
export function deleteUser(ids: number[]): Promise<void> {
return systemRequest.delete('/system/user', { data: ids })
}
// 启用:PATCH /{id}/v
export function enableUser(id: number): Promise<void> {
return systemRequest.patch(`/system/user/${id}/v`)
}
// 禁用:PATCH /{id}/x
export function disableUser(id: number): Promise<void> {
return systemRequest.patch(`/system/user/${id}/x`)
}规范要点:
- 具名导出函数(不使用默认导出对象)
- 返回类型精确标注(
Promise<PageResult<T>>/Promise<T>/Promise<void>) - 业务代码中不需要再
.data解包(框架已处理) - 不需要在业务层处理 401(框架已静默刷新)
六、TypeScript 类型定义规范
在 src/api/ 目录下或专用 src/types/ 目录中定义业务类型:
// 分页查询入参(对应后端 XxxPageParamVO)
export interface UserPageParam {
pageNo?: number // 页码,默认 1
pageSize?: number // 每页条数,默认 10
username?: string // 可选过滤条件
status?: number
}
// 列表响应(对应后端 XxxRespVO)
export interface UserRespVO {
id: number
username: string
nickname: string
status: number
creator: string
createTime: string // 后端 LocalDateTime 序列化为字符串
modifyTime: string
}
// 详情响应(对应后端 XxxDetailVO)
export interface UserDetailVO extends UserRespVO {
remark?: string
customFields?: string
createUserId?: number
modifyUserId?: number
updater?: string
}
// 新增/修改入参(对应后端 XxxSaveParamVO)
export interface UserSaveParam {
id?: number // 新增时不传,修改时必传
username: string
nickname: string
status?: number
remark?: string
}七、路由规范
路径不含 /systemapp 等 activePath 前缀:
import type { RouteRecordRaw } from 'vue-router'
/**
* 路径不含 activePath 前缀。
* IceStark 通过 getBasename() 返回 activePath 作为 createWebHistory 的 base,
* Vue Router 自动将 base 从 URL 中剥除后再匹配路由。
*
* 浏览器 URL: /systemapp/user → Vue Router 匹配: /user
*/
const routes: RouteRecordRaw[] = [
{ path: '/', redirect: '/user' },
{
path: '/user',
name: 'User',
component: () => import('@/pages/user/index.vue'),
meta: { title: '用户管理' },
},
]
export default routes新增业务页面步骤:
- 在
src/pages/{模块名}/下新建页面组件(.vue) - 在
src/router/routes.ts注册路由(路径不含 activePath 前缀) - 在后端
nebula-system迁移脚本中添加菜单 INSERT(path为/{activePath}/{路由path})
八、IceStark 子应用生命周期
import { isInIcestark, setLibraryName, getBasename } from '@ice/stark-app'
import { createApp } from 'vue'
import { createRouter, createWebHistory } from 'vue-router'
import App from './App.vue'
import routes from './router/routes'
// 全局唯一库名称,必须与主应用 microAppConfig 中配置一致
setLibraryName('nebulaSystemApp')
let app: ReturnType<typeof createApp>
const mount = (props?: any) => {
const router = createRouter({
// 在 IceStark 环境中以 activePath 为 base,独立运行时为 /
history: createWebHistory(isInIcestark() ? getBasename() : '/'),
routes,
})
app = createApp(App)
app.use(router)
app.mount(props?.container?.querySelector('#app') || '#app')
}
// IceStark 环境:导出生命周期
if (isInIcestark()) {
export const bootstrap = () => Promise.resolve()
export const mount = mount
export const unmount = () => { app?.unmount() }
} else {
// 独立开发模式
mount()
}九、Token / Auth 工具
统一使用共享包提供的工具,不直接操作 localStorage:
import {
getToken, // 获取 AccessToken
setToken, // 存储 AccessToken
getRefreshToken, // 获取 RefreshToken
setRefreshToken, // 存储 RefreshToken
getTenantId, // 获取当前租户 ID
setTenantId,
removeToken, // 清除所有 Token(登出时调用)
isLoggedIn, // 判断是否已登录
} from '@nebula-web/utils'LocalStorage Key 命名(仅供了解,不要直接读写):
nebula_access_tokennebula_refresh_tokennebula_user_infonebula_tenant_id
十、环境变量约定
# .env.development — 联调本地内网
VITE_SYSTEM_API_BASE=http://192.168.x.x:8080
VITE_SYSTEM_BACKEND=http://192.168.x.x:8080 # 供 vite.config.ts proxy 使用
# .env.remote — 联调线上
VITE_SYSTEM_API_BASE=https://xxx.domain.cn
VITE_SYSTEM_BACKEND=https://xxx.domain.cn
# .env.production — 生产
VITE_SYSTEM_API_BASE= # 生产使用 Nginx 反向代理,留空走相对路径切换后端地址只修改 .env 文件,不改代码。
十一、代码规范要点
- 组件使用
<script setup lang="ts">语法 - 响应式数据优先使用
ref/reactive - Element Plus 组件按需引入(由 Vite 插件自动处理)
- 页面内不直接写请求逻辑,抽取到
src/api/中 - 错误提示统一使用
ElMessage.error(err.message),不console.error直接抛给用户 - TypeScript 严格模式:避免
any,必要时用unknown并做类型守卫
