@qinmu-/elpis
v1.0.3
Published
Elpis - 基于 Vue 3 + Koa 2 的全栈 Web 应用.
Readme
Elpis
基于 Vue 3 + Koa 2 的全栈 Web 应用框架
Elpis 是一个约定式全栈框架,提供自动加载、多层配置合并、API 参数校验、MPA 多页前端架构、Schema 驱动视图等能力,帮助开发者快速构建企业级 Web 应用。
目录
核心特性
| 特性 | 说明 |
|------|------|
| 约定式自动加载 | Controller、Service、Router、Middleware、Extend 按目录约定自动扫描注册 |
| 四层配置合并 | 框架默认 → 业务默认 → 框架环境 → 业务环境,链式覆盖 |
| 内置安全中间件 | API 签名校验(MD5 + 时间戳)、参数 Schema 校验(Ajv) |
| 统一响应格式 | Controller 基类提供 success() / fail() 标准化响应 |
| MPA 多页架构 | 前端按页面独立入口,Webpack 自动扫描打包,代码分割复用 |
| Schema 驱动视图 | 通过 JSON Schema 自动生成搜索面板、数据表格、表单弹窗 |
| Model 继承体系 | 项目(Project)可继承业务模型(Model)配置,支持 deep merge |
| HMR 热更新 | Express + webpack-dev-middleware + webpack-hot-middleware |
| 前后端分离开发 | 前端开发服务器代理 /api 到 Koa 后端,单端口访问 |
技术栈:
- 后端:Koa 2、koa-router、koa-bodyparser、superagent、ajv、knex
- 前端:Vue 3、Element Plus、Pinia、Vue Router、ECharts
- 构建:Webpack 5、Babel、Less
- 模板:Nunjucks
环境要求
- Node.js >= 12.x
- npm >= 6.x
快速开始
1. 创建项目
mkdir my-project
cd my-project
npm init -y2. 安装 Elpis
npm install @qinmu-/elpis3. 创建服务入口文件
在项目根目录创建 server.js:
const { serverStart } = require('@qinmu-/elpis')
const app = serverStart({
name: 'myApp',
homePage: '/view/project-list'
})4. 启动开发服务
启动 Koa 后端(端口 8080):
# macOS / Linux
_ENV='local' nodemon ./server.js
# Windows
set _ENV='local' && node ./server.js启动前端开发服务器(端口 8081,带 HMR 热更新):
# macOS / Linux
_ENV='local' node -e "require('@qinmu-/elpis').startDevServer({ pagesPath: require('path').resolve(__dirname, 'app') })"
# Windows
set _ENV='local' && node -e "require('@qinmu-/elpis').startDevServer({ pagesPath: require('path').resolve(__dirname, 'app') })"提示:开发时建议同时启动后端和前端开发服务器。前端开发服务器会将
/api/*请求代理到 Koa 后端(默认http://127.0.0.1:8080),实现单端口开发(访问http://localhost:8081)。
项目结构
一个典型的 Elpis 项目目录结构如下:
my-project/
├── server.js # 服务入口,调用 serverStart 启动
├── package.json # 项目配置
├── config/ # 配置文件(四层合并)
│ ├── config.default.js # 默认配置
│ ├── config.local.js # 本地环境配置
│ ├── config.beta.js # 测试环境配置
│ └── config.prod.js # 生产环境配置
├── app/ # 业务代码目录
│ ├── middleware.js # 全局中间件注册入口
│ ├── controller/ # 控制器
│ │ └── *.js
│ ├── service/ # 服务层
│ │ └── *.js
│ ├── middleware/ # 自定义中间件
│ │ └── *.js
│ ├── router/ # 路由注册
│ │ └── *.js
│ ├── router-schema/ # 参数校验 Schema
│ │ └── *.js
│ ├── extend/ # 框架扩展
│ │ └── *.js
│ ├── pages/ # 前端页面(MPA 多页入口)
│ │ └── {pageName}/
│ │ ├── entry.{name}.js # 页面入口文件
│ │ └── *.vue # 页面组件
│ └── public/ # 静态资源
│ └── dist/ # 构建产物
├── model/ # 模型配置(菜单/页面定义)
│ └── {bizName}/
│ ├── model.js # 通用模型
│ └── project/
│ └── {project}.js # 项目配置(继承 model)
└── logs/ # 日志目录各层职责
| 层级 | 目录 | 职责 |
|------|------|------|
| 配置 | config/ | 环境相关配置,四层链式合并 |
| 中间件 | app/middleware/ | 请求预处理(鉴权、日志、参数校验等) |
| 参数校验 | app/router-schema/ | 定义 API 的 headers/body/query 参数 Schema |
| 路由 | app/router/ | URL 到 Controller 方法的映射 |
| 控制器 | app/controller/ | 解析请求参数,调用 Service,返回响应 |
| 服务层 | app/service/ | 业务逻辑封装,数据库操作 |
| 扩展 | app/extend/ | 扩展 app 实例(如 logger) |
| 前端 | app/pages/ | Vue 组件,按页面独立打包 |
| 模型 | model/ | 菜单结构、页面 Schema 配置 |
配置指南
四层配置合并
Elpis 采用四层链式合并策略加载配置,优先级从低到高:
第1层: elpis/config/config.default.js (框架默认配置)
第2层: {项目}/config/config.default.js (业务默认配置)
第3层: elpis/config/config.{env}.js (框架环境配置)
第4层: {项目}/config/config.{env}.js (业务环境配置) ← 最高优先级环境映射关系:
| 环境 | _ENV 值 | 配置文件 |
|------|-----------|----------|
| 本地开发 | local | config.local.js |
| 测试环境 | beta | config.beta.js |
| 生产环境 | production | config.prod.js |
配置文件示例
config/config.default.js(默认配置):
module.exports = {
name: 'my-app',
apiBase: 'http://localhost:8080',
debug: true,
pageSize: 10,
}config/config.local.js(本地环境覆盖):
module.exports = {
apiBase: 'http://localhost:8080',
debug: true,
pageSize: 5,
}config/config.prod.js(生产环境覆盖):
module.exports = {
apiBase: 'https://api.example.com',
debug: false,
pageSize: 20,
}配置合并使用 Object.assign,后层同名字段会覆盖前层。
后端开发指南
Controller 控制器
控制器负责解析请求参数、调用 Service 处理业务逻辑、返回统一格式响应。
基类方法
所有 Controller 继承自 BaseController,可直接使用以下方法:
// 成功响应
this.success(ctx, data, metadata)
// 返回: { success: true, metadata: {...}, data: {...} }
// 失败响应
this.fail(ctx, message, code)
// 返回: { success: false, message: '...', code: 400 }编写 Controller
在 app/controller/ 目录下创建文件,导出工厂函数返回类:
// app/controller/product.js
module.exports = (app) => class ProductController {
constructor() {
this.app = app
this.config = app.config
// 通过 app.controller 访问其他控制器
// 通过 app.service 访问服务层
}
async getList(ctx) {
const { page = 1, pageSize = 10, keyword } = ctx.query
const data = await app.service.product.getList({ page, pageSize, keyword })
this.success(ctx, data)
}
async create(ctx) {
const params = ctx.request.body
const data = await app.service.product.create(params)
this.success(ctx, data)
}
async update(ctx) {
const { id } = ctx.query
const params = ctx.request.body
const data = await app.service.product.update(id, params)
this.success(ctx, data)
}
async delete(ctx) {
const { id } = ctx.query
const data = await app.service.product.delete(id)
this.success(ctx, data)
}
}自动挂载规则
Controller 按文件路径自动挂载到 app.controller:
| 文件路径 | 挂载路径 |
|----------|----------|
| app/controller/product.js | app.controller.product |
| app/controller/user/auth.js | app.controller.user.auth |
| app/controller/custom-module/order.js | app.controller.customModule.order |
连字符
-和下划线_会自动转换为驼峰命名。业务控制器会覆盖框架同路径控制器。
Service 服务层
Service 封装业务逻辑,基类注入 app、config、curl(superagent)。
基类属性
this.app // Koa 应用实例
this.config // 合并后的配置对象
this.curl // superagent HTTP 客户端编写 Service
// app/service/product.js
module.exports = (app) => class ProductService {
constructor() {
this.app = app
this.config = app.config
this.curl = require('superagent')
}
async getList({ page = 1, pageSize = 10, keyword = '' }) {
// 数据库查询、外部 API 调用等业务逻辑
const result = await this.curl
.get(`${this.config.apiBase}/external/products`)
.query({ page, pageSize, keyword })
return result.body
}
async create(params) {
// 创建逻辑
return { id: Date.now(), ...params }
}
async update(id, params) {
// 更新逻辑
return { id, ...params }
}
async delete(id) {
// 删除逻辑
return { id }
}
}Service 的自动挂载规则与 Controller 相同,挂载到 app.service。
Router 路由
在 app/router/ 目录下创建路由文件,导出函数接收 (app, router) 参数:
// app/router/product.js
module.exports = (app, router) => {
const { productController } = app.controller
router.get('/api/product', productController.getList.bind(productController))
router.post('/api/product', productController.create.bind(productController))
router.put('/api/product', productController.update.bind(productController))
router.delete('/api/product', productController.delete.bind(productController))
}// app/router/view.js
module.exports = (app, router) => {
const { viewController } = app.controller
// 页面渲染路由,支持 history 模式
router.get('/view/:page', viewController.renderPage.bind(viewController))
router.get('/view/:page/*', viewController.renderPage.bind(viewController))
}注意:Controller 方法通过
.bind(controllerInstance)绑定 this 上下文。路由在框架内置路由之后注册,业务路由可覆盖框架路由。
Router-Schema 参数校验
定义 API 接口的请求参数 Schema,由 api-params-verify 中间件自动校验。
// app/router-schema/product.js
module.exports = {
'/api/product': {
GET: {
headers: {
properties: {
projkey: { type: 'string', required: true, description: '项目标识' }
}
},
query: {
properties: {
page: { type: 'number', description: '页码' },
pageSize: { type: 'number', description: '每页条数' },
keyword: { type: 'string', description: '搜索关键词' }
}
}
},
POST: {
headers: {
properties: {
projkey: { type: 'string', required: true }
}
},
body: {
properties: {
name: { type: 'string', required: true, description: '商品名称' },
price: { type: 'number', required: true, description: '价格' },
category: { type: 'string', description: '分类' }
}
}
},
PUT: {
headers: {
properties: {
projkey: { type: 'string', required: true }
}
},
query: {
properties: {
id: { type: 'string', required: true, description: '商品ID' }
}
},
body: {
properties: {
name: { type: 'string', description: '商品名称' },
price: { type: 'number', description: '价格' }
}
}
},
DELETE: {
headers: {
properties: {
projkey: { type: 'string', required: true }
}
},
query: {
properties: {
id: { type: 'string', required: true, description: '商品ID' }
}
}
}
}
}Schema 使用 Ajv 进行校验,校验失败返回:
{
"success": false,
"code": 442,
"message": "request parameters fail: ..."
}Middleware 中间件
全局中间件注册
在 app/middleware.js 中按洋葱圈模型注册全局中间件:
// app/middleware.js
module.exports = (app) => {
const { errorHandler, apiSignVerify, apiParamsVerify, projectHandler } = app.middlewares
app.use(errorHandler)
app.use(apiSignVerify)
app.use(apiParamsVerify)
app.use(projectHandler)
// 注册自定义中间件
app.use(app.middlewares.customMiddleware)
}编写自定义中间件
// app/middleware/custom.js
module.exports = (app) => async (ctx, next) => {
const start = Date.now()
console.log(`[${ctx.method}] ${ctx.url} - 请求开始`)
await next()
const ms = Date.now() - start
console.log(`[${ctx.method}] ${ctx.url} - 响应完成 (${ms}ms)`)
}内置中间件说明
| 中间件 | 文件 | 说明 |
|--------|------|------|
| error-handler | app/middleware/error-handler.js | 全局异常捕获,统一错误响应格式 |
| api-sign-verify | app/middleware/api-sign-verify.js | API 签名校验(MD5 + 10分钟时间窗口),仅 /api 路径 |
| api-params-verify | app/middleware/api-params-verify.js | 基于 Router-Schema 的 Ajv 参数校验,仅 /api 路径 |
| project-handler | app/middleware/project-handler.js | 从请求头提取 projKey,注入 ctx.projKey,仅 /api 路径 |
API 签名规则:
- 前端请求需携带请求头
s_t(时间戳)和s_sign(签名) - 签名算法:
md5('ssadklkslflsdlfljasjksd' + '_' + s_t) - 时间差超过 10 分钟则签名失效
Extend 扩展
扩展用于向 app 实例直接挂载属性或方法:
// app/extend/logger.js
const log4js = require('log4js')
module.exports = (app) => {
log4js.configure({
appenders: { app: { type: 'file', filename: 'logs/app.log' } },
categories: { default: { appenders: ['app'], level: 'info' } }
})
const logger = log4js.getLogger('app')
return {
info: (msg) => logger.info(msg),
error: (msg) => logger.error(msg),
warn: (msg) => logger.warn(msg),
}
}挂载后通过 app.logger.info('message') 使用。扩展会直接挂载到 app 根属性,同名属性会触发警告提示。
前端开发指南
MPA 多页架构
Elpis 前端采用 MPA(Multi-Page Application)架构,每个页面独立入口、独立打包。
app/pages/
├── boot.js # 统一启动入口(跨页面复用,自动抽到 common.js)
├── common/ # 公共模块
│ ├── curl.js # axios 请求封装
│ ├── utils.js # 公共工具函数
│ └── stores/ # 公共 Store
├── store/ # 页面级 Store
│ ├── dashboard.js
│ └── user.js
├── dashboard/ # Dashboard 页面
│ ├── entry.dashboard.js # 入口文件(必需,命名规则:entry.{name}.js)
│ ├── dashboard.vue # 根组件
│ └── ... # 子组件/子页面
└── project-list/ # 项目列表页面
├── entry.project.js # 入口文件
└── project-list.vue # 根组件入口文件命名规则:entry.{name}.js,Webpack 会自动扫描 pages/*/entry.*.js 作为构建入口。
boot.js 统一启动
boot.js 提供统一的 Vue 应用启动能力,自动装配 Pinia、Element Plus、Vue Router:
// app/pages/boot.js
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import { createRouter, createWebHistory } from 'vue-router'
export function boot(rootComponent, options = {}) {
const app = createApp(rootComponent)
// 装配 Pinia
const pinia = createPinia()
app.use(pinia)
// 装配 Element Plus
app.use(ElementPlus)
// 可选装配 Vue Router
if (options.routes) {
const router = createRouter({
history: createWebHistory(`/view/${options.pageName}`),
routes: options.routes,
})
app.use(router)
}
app.mount('#app')
return app
}页面入口文件示例:
// app/pages/dashboard/entry.dashboard.js
import { boot } from '../boot'
import Dashboard from './dashboard.vue'
boot(Dashboard, {
pageName: 'dashboard',
routes: [
{ path: '/', redirect: '/todo' },
{ path: '/todo', component: () => import('./todo/todo.vue') },
{ path: '/schema', component: () => import('./complex-view/schema-view/schema-view.vue') },
{ path: '/sider/:chapters+', component: () => import('./complex-view/sider-view/sider-view.vue') },
]
})curl.js 请求层
curl.js 是对 axios 的二次封装,分为 HTTP 层和业务层:
HTTP 层(axios 拦截器)
自动注入请求头:
s_t:当前时间戳s_sign:MD5 签名(md5('ssadklkslflsdlfljasjksd' + '_' + s_t))Authorization:JWT TokenprojKey:当前选中项目标识(从 localStorage 读取)
业务层(curl 方法)
按后端统一格式 { success, metadata, data } 自动解包:
import curl from '@/common/curl'
// 成功时直接返回 data
const data = await curl.get('/api/product', { params: { page: 1 } })
// data = { list: [...], total: 100 }
// 失败时抛出包含 code/message/metadata 的 Error
try {
await curl.post('/api/product', { name: 'test' })
} catch (err) {
console.log(err.code) // 错误码
console.log(err.message) // 错误信息
}可用方法:curl.get()、curl.post()、curl.put()、curl.delete()
Store 状态管理
使用 Pinia 管理状态,在 app/pages/store/ 目录下创建:
// app/pages/store/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
username: '',
token: '',
}),
actions: {
login(username, token) {
this.username = username
this.token = token
},
logout() {
this.username = ''
this.token = ''
},
},
})在组件中使用:
import { useUserStore } from '@/store/user'
const userStore = useUserStore()
userStore.login('admin', 'xxx-token')Schema 驱动视图
Schema 驱动视图是 Elpis 的核心前端能力,通过一份 JSON Schema 配置即可自动生成搜索面板、数据表格和表单弹窗。
Schema 配置结构
const schemaConfig = {
// 搜索面板配置
searchFields: [
{ name: 'keyword', label: '关键词', type: 'STRING' },
{ name: 'category', label: '分类', type: 'SELECT', options: [
{ label: '电子产品', value: 'electronics' },
{ label: '服装', value: 'clothing' },
]},
{ name: 'createTime', label: '创建时间', type: 'DATE' },
],
// 表格列配置
tableColumns: [
{ name: 'id', label: 'ID', width: 80 },
{ name: 'name', label: '商品名称' },
{ name: 'price', label: '价格', sortable: true },
{ name: 'category', label: '分类' },
{ name: 'status', label: '状态' },
],
// 操作按钮
actions: {
top: [
{ name: 'create', label: '新增', type: 'primary' },
],
row: [
{ name: 'edit', label: '编辑' },
{ name: 'delete', label: '删除', type: 'danger' },
],
},
// 表单配置
formFields: [
{ name: 'name', label: '商品名称', type: 'STRING', required: true },
{ name: 'price', label: '价格', type: 'NUMBER', required: true },
{ name: 'category', label: '分类', type: 'SELECT', options: [
{ label: '电子产品', value: 'electronics' },
{ label: '服装', value: 'clothing' },
]},
],
// API 配置
api: {
list: '/api/product',
create: '/api/product',
update: '/api/product',
delete: '/api/product',
},
}支持的表单控件类型
| 类型 | 常量 | 渲染控件 |
|------|------|----------|
| STRING | 文本输入 | el-input |
| NUMBER | 数字输入 | el-input-number |
| SELECT | 下拉选择 | el-select |
| DATE | 日期选择 | el-date-picker |
公共组件
框架内置了两个布局容器组件:
| 组件 | 路径 | 说明 |
|------|------|------|
| header-container | pages/widgets/header-container/ | 顶部导航栏布局,含 el-menu 和项目选择器 |
| sider-container | pages/widgets/sider-container/ | 侧边栏布局,含可折叠菜单 |
在页面中使用:
<template>
<header-container>
<router-view />
</header-container>
</template>
<script setup>
import HeaderContainer from '@/widgets/header-container/header-container.vue'
</script>Model 模型层
Model 层用于定义业务模型和项目配置,支持项目继承模型的 deep merge 机制。
目录结构
model/
├── index.js # 模型解析器
├── buiness/ # 电商业务线
│ ├── model.js # 通用模型
│ └── project/
│ ├── jd.js # 京东项目
│ ├── pdd.js # 拼多多项目
│ └── taobao.js # 淘宝项目
└── course/ # 课程业务线
├── model.js # 通用模型
└── project/
├── bilibili.js # B站课堂
└── douyin.js # 抖音课堂模型配置
// model/buiness/model.js - 通用模型
module.exports = {
mode: 'dashboard',
menuList: [
{ name: 'product', label: '商品管理', type: 'custom' },
{
name: 'order', label: '订单管理',
type: 'schema',
schemaConfig: {
searchFields: [ /* ... */ ],
tableColumns: [ /* ... */ ],
api: { list: '/api/order' },
},
},
{ name: 'customer', label: '客户管理', type: 'custom' },
],
}// model/buiness/project/jd.js - 京东项目(继承通用模型)
module.exports = {
menuList: [
// 覆盖商品管理
{ name: 'product', label: '京东商品管理', type: 'custom' },
// 新增营销中心(分组菜单)
{
name: 'marketing', label: '营销中心', type: 'group',
children: [
{ name: 'coupon', label: '优惠券', type: 'custom' },
{ name: 'seckill', label: '秒杀', type: 'custom' },
],
},
// 新增数据分析(侧边栏模式)
{
name: 'analytics', label: '数据分析', type: 'sider',
children: [
{ name: 'sales-report', label: '销售报表', type: 'custom' },
{ name: 'user-portrait', label: '用户画像', type: 'custom' },
],
},
],
}菜单类型
| 类型 | 说明 |
|------|------|
| custom | 自定义页面(需自行开发 Vue 组件) |
| schema | Schema 驱动页面(自动生成搜索/表格/表单) |
| iframe | iframe 嵌入外部页面 |
| group | 分组菜单(顶部下拉子菜单) |
| sider | 侧边栏菜单(带子路由) |
继承机制
项目配置通过 deep merge 继承模型配置,同名 key 项目覆盖模型,模型中有而项目中没有的配置保留。
构建与部署
环境变量
通过 _ENV 环境变量控制运行环境:
| 命令 | 环境 |
|------|------|
| _ENV='local' | 本地开发 |
| _ENV='beta' | 测试环境 |
| _ENV='production' | 生产环境 |
开发命令
# 启动 Koa 后端
_ENV='local' node ./server.js
# 启动前端开发服务器(HMR 热更新,端口 8081)
_ENV='local' node -e "require('@qinmu-/elpis').startDevServer({ pagesPath: require('path').resolve(__dirname, 'app') })"生产构建
# 前端生产构建
_ENV='production' node -e "require('@qinmu-/elpis').buildApp({ pagesPath: require('path').resolve(__dirname, 'app') })"构建产物输出到 app/public/ 目录:
app/public/dist/— HTML 模板文件app/public/static/js/— JS bundle(带 contenthash)app/public/static/css/— CSS 文件
生产部署
# 启动生产服务
_ENV='production' node ./server.jsWindows 命令
Windows 环境下使用 set 命令:
# 开发
set _ENV='local' && node ./server.js
set _ENV='local' && node -e "require('@qinmu-/elpis').startDevServer({ pagesPath: require('path').resolve(__dirname, 'app') })"
# 构建
set _ENV='production' && node -e "require('@qinmu-/elpis').buildApp({ pagesPath: require('path').resolve(__dirname, 'app') })"
# 生产
set _ENV='production' && node ./server.jspackage.json 脚本配置
推荐在业务项目的 package.json 中配置便捷脚本:
{
"scripts": {
"dev": "_ENV='local' nodemon ./server.js",
"dev:win": "set _ENV='local' && node ./server.js",
"prod": "_ENV='production' node ./server.js",
"prod:win": "set _ENV='production' && node ./server.js",
"build": "_ENV='production' node -e \"require('@qinmu-/elpis').buildApp({ pagesPath: require('path').resolve(__dirname, 'app') })\"",
"build:win": "set _ENV='production' && node -e \"require('@qinmu-/elpis').buildApp({ pagesPath: require('path').resolve(__dirname, 'app') })\"",
"build:watch": "_ENV='local' node -e \"require('@qinmu-/elpis').buildApp({ pagesPath: require('path').resolve(__dirname, 'app'), watch: true })\"",
"dev:fe": "_ENV='local' node -e \"require('@qinmu-/elpis').startDevServer({ pagesPath: require('path').resolve(__dirname, 'app') })\""
}
}Webpack 构建特性
| 特性 | 开发模式 | 生产模式 |
|------|----------|----------|
| Mode | development | production |
| Source Map | eval-cheap-module-source-map | 关闭 |
| 文件名 | 无 hash | [contenthash:8] |
| CSS 提取 | vue-style-loader(内联) | MiniCssExtractPlugin(独立文件) |
| 代码压缩 | 不压缩 | TerserPlugin + CssMinimizerPlugin |
| 构建缓存 | 无 | filesystem 持久化缓存 |
| 构建清理 | 无 | CleanWebpackPlugin |
| console 移除 | 保留 | drop_console: true |
代码分割策略:
vendors:所有node_modules依赖打包为vendors.jscommon:被 2 个以上入口引用的公共模块(如boot.js)抽取为common.js
框架 API 参考
serverStart(options)
启动 Elpis 服务。
const { serverStart } = require('@qinmu-/elpis')
const app = serverStart(options)参数:
| 参数 | 类型 | 说明 |
|------|------|------|
| options.name | string | 应用名称 |
| options.homePage | string | 首页路径,如 '/view/project-list' |
返回值:Koa 应用实例。
内部流程:
- 创建 Koa 实例
- 设置
app.baseDir为process.cwd() - 设置
app.businessPath为{cwd}/app - 初始化环境变量(
app.env) - 依次加载配置、中间件、Router-Schema、Service、Controller、Extend
- 注册全局中间件(框架 + 业务)
- 注册路由
- 监听端口(默认
8080,可通过PORT和HOST环境变量配置)
BaseService
Service 基类,供业务 Service 继承。
const { BaseService } = require('@qinmu-/elpis')
module.exports = (app) => class MyService extends BaseService {
// 自动注入: this.app, this.config, this.curl
}基类属性:
| 属性 | 说明 |
|------|------|
| this.app | Koa 应用实例 |
| this.config | 合并后的配置对象 |
| this.curl | superagent HTTP 客户端 |
buildApp(options)
前端生产构建(Webpack 一次性构建)。
const { buildApp } = require('@qinmu-/elpis')
await buildApp({
pagesPath: '/path/to/business/app',
watch: false,
})参数:
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| pagesPath | string | 框架自身 app/ | 业务项目的 app/ 目录路径 |
| watch | boolean | false | 是否开启持续监听模式 |
startDevServer(options)
启动前端开发服务器(Express + webpack-dev-middleware + HMR)。
const { startDevServer } = require('@qinmu-/elpis')
startDevServer({
pagesPath: '/path/to/business/app',
port: 8081,
koaOrigin: 'http://127.0.0.1:8080',
})参数:
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| pagesPath | string | 框架自身 app/ | 业务项目的 app/ 目录路径 |
| port | number/string | 8081 | 开发服务器端口 |
| koaOrigin | string | http://127.0.0.1:8080 | Koa 后端地址(用于 API 代理) |
功能:
- 提供 HMR 热更新能力
/view/:page路由渲染对应页面模板/api/*请求代理到 Koa 后端/重定向到/view/page1
框架环境 API
通过 app.env 访问环境相关方法:
app.env.get() // 获取当前环境: 'local' | 'beta' | 'production'
app.env.isLocal() // 是否本地环境
app.env.isTest() // 是否测试环境
app.env.isProd() // 是否生产环境常见问题
Q: 如何添加新的 API 接口?
按照以下步骤添加:
- 在
app/service/创建 Service 文件,编写业务逻辑 - 在
app/controller/创建 Controller 文件,调用 Service 并返回统一响应 - 在
app/router/创建路由文件,绑定 URL 到 Controller 方法 - (可选)在
app/router-schema/创建 Schema 文件,定义参数校验规则
Q: 如何添加新的前端页面?
- 在
app/pages/下创建{pageName}/目录 - 创建
entry.{name}.js入口文件,使用boot()启动 - 创建 Vue 组件文件
- 在
app/router/中添加页面渲染路由 - 重新构建前端或重启开发服务器
Q: 配置合并后,如何确认最终生效的配置?
在 Controller 或 Service 中打印 this.config 或 app.config 即可查看合并后的最终配置对象。
Q: 如何跳过 API 签名校验?
API 签名校验仅在 /api 路径下生效。如需临时关闭,可在 app/middleware.js 中注释掉 apiSignVerify 中间件注册。
Q: 前端请求如何自动携带项目标识?
前端 curl.js 会自动从 localStorage.projKey 读取当前项目标识,并在每个 API 请求头中注入 projKey。切换项目时需更新 localStorage.projKey。
Q: Model 模型层的 project 如何继承 model 配置?
Model 解析器会自动按 model/ 和 project/ 子目录分类加载,通过 projectExtendModel 实现 deep merge:project 中的配置覆盖 model 同名配置,model 中独有的配置保留。
Q: Webpack 构建报错 "Module not found"?
检查业务项目的 node_modules 是否安装了前端依赖。Webpack 的 resolveLoader.modules 配置会优先查找框架的 node_modules,但部分依赖可能需要业务项目自行安装。如遇缺失,在业务项目中安装对应包即可。
Q: 如何自定义 Webpack 配置?
目前 Webpack 配置封装在框架内部,业务项目通过 buildApp() 和 startDevServer() 的 pagesPath 参数指定自己的 app/ 目录路径。如需深度自定义 Webpack 配置,可在框架的 app/webpack/config/ 目录下修改对应的 webpack.base.js、webpack.dev.js 或 webpack.prod.js。
Elpis - 让全栈开发更简单
