个人框架-GUIYUAN

序言

框架名称:GUIYUAN(归元)

在正式开工之前,给项目起个名字是一件既浪漫又讲究仪式感的事。最开始脑子一热,想叫“盘古”,毕竟盘古开天辟地,听起来就很猛——但仔细一想,这名字太有压力了,好像我不是在写代码,而是在创造宇宙。
于是转念一想,不如低调点、有点禅意、还能传达出我们平台的初心和愿景,于是——GUIYUAN(归元),登场了。
“归元”是佛家术语,意为回归本源、返璞归真。做这个平台的初衷也正是如此:
工作上重复造轮子的场景太多了,CRUD做得我们都能梦里写增删改查。多客户交付、多系统分支、每个需求都像从头来过,写着写着就开始怀疑人生。
GUIYUAN 的使命,就是把 80% 高重复性的工作沉淀为平台能力,统一、可配置,灵活复用。 只把真正有价值、因地制宜的 20% 差异化,留给开发者自由发挥。
一切冗余,皆可归元。
我不想再在代码的轮回中苦修,只想搭个平台,佛系点高效点,把“重复的归平台,灵活的归定制”这才是程序员真正的解脱。

本文还是用到我解决问题的思路 💡 遇到什么需求、问题 + 业界解决方案 + 我的解决方案 + 落地效果
技术栈:MySQL + log4js日志 + node.js18 + koa2 + vue3 + element-plus + webpack5

行业现状与需求推倒

痛点与目标:

1.工作中有很多重复性高的工作,避免相似功能重复开发
2.工作中我们经常做crud工作,所以要变成是我们在维护某一个框架
3.同一个系统可能出来一个标准产品卖给不同的客户、甲方,就可能遇到同一套系统多方交付的场景,有点客户有定制化的需求,就要把现在的产品拷贝一份上面改造定制化的需求,会导致大量的重复工作,当多客户交付的时候项目越来越混乱,项目可能出现十几个分支,所以我们要支持多套系统建设,无需重新开发相同功能

需求:

1.沉淀80%的重复性工作,支持配置化,能只针对20%定制化工作进行开发
2.方案需落地为系统平台,平台支持大量的crud工作。且可持续集成。我们只需要维护这个平台,平台可配置
3.平台需支持多系统建设,且系统间功能复用

技术选型

业界方案调研

方案1.
大而全的触达系统比如神策、易观
详细了解了一下,这类系统就像一台“十八般武艺”全开的瑞士军刀——功能模块多到眼花缭乱,看起来高端大气上档次,实则灵活性感人。它们更像是让客户去适应系统,而不是系统去满足客户(听起来是不是有点霸道总裁的感觉?)
缺点一箩筐:
  • 定制化能力薄得像纸,想加点个性就得推翻重来
  • 不适合多客户交付场景,一个系统交给十个客户,能拧出十个分支
  • 模块太多,真正用上的没几个,反而拖慢了交付
  • 迭代一多,熵增爆表,越改越乱
方案2.
过于灵活的多个子系统类似微前端。
这类思路相当自由派:微前端、配置系统、建站平台、UI组件库,组合拳打得漂亮。一看架构图,顿时觉得人生好像可以不写代码了——全靠拖拉拽。
但现实往往是:
  • 配置自由度太高,非常复杂,学起来头大,用起来更大
  • 若处理不好容易代码断代,此时对提效没有实质的提高,反而成了负担
  • 通用配置场景搞得飞起,但碰上强业务领域(比如财务管理),就露馅了,感觉“工具箱里全是锤子,但现在我手里是一颗螺丝钉”。

我的方案

取长补短,搞点靠谱的。我们不走极端,既不要宇宙飞船般的重系统,也不搞无限可配的配置迷宫。我们主打一个——折中 + 有脑子
我们的方案融合两者优点,回归平台化的本质,从代码出发,用模块说话,让重复的归平台,灵活的归开发。核心思路如下:
  • 🧱粒子:算子服务-把系统拆成一个个原子级的算子服务(粒度够小,组合够灵)
  • 🌀AOP领域模型-切面注入、职责解耦,模块化构建你的业务宇宙
  • 🧩面向对象建站-按类建模块,按需装功能,像搭乐高一样做系统
一句话总结:我们不追求一招吃天下,而是希望这套平台能八方通吃、见招拆招、灵活应对各种客户交付场景。毕竟系统不是修仙,能干活才是王道。

架构设计

项目架构图
项目架构图

数据层

数据库选择

特性/对比项 MySQL MongoDB
数据库类型 关系型数据库(Relational) 非关系型数据库(MySQL,文档型)
数据结构 表(Tables)、行(Rows)、列(Columns) 文档(Documents,JSON格式)
模式(Schema) 严格,表结构固定 灵活,可存储不同结构的数据
查询语言 SQL MongoDB Query Language(类JSON语法)
扩展性 垂直扩展为主(scale-up) 水平扩展友好(scale-out)
数据一致性 强一致性(默认) 最终一致性(可配置)
适合场景 结构化数据、事务性强的应用 非结构化数据、灵活字段、快速迭代开发
索引机制 支持多种索引 支持单字段、复合字段、全文索引等
性能特点 数据量大时性能受限 大数据量下读写性能优越
代表应用 金融系统、电商后台 内容管理系统、日志分析、实时大数据
备份与恢复 使用 mysqldump 或物理备份工具 使用 mongodumpmongorestore

虽然我主力是 Node.js,MongoDB 和它天生是一对,“一个写得快,一个存得爽”,配合得堪称默契。但技术选型这事吧,不能只看“感情”,还得考虑“家世背景”——毕竟放眼市面上,大多数公司后端主力语言还是 Java、Go,这些语言对MySQL 的支持更成熟,生态更健全。
所以,权衡下来,MySQL 更适合作为团队未来技术迁移与栈转型的基石。它是数据库界的“老干部”,稳定、可靠、文档多、社区大,迁移起来省心,招人也更容易。

日志系统选择

日志这事,向来是“事后诸葛”的关键,出问题的时候靠它定位,没问题的时候靠它安神。对于 Node.js 项目,我选的是老牌选手 —— Log4js
它虽然名字听着像 Java 家的远房亲戚,但其实在 Node 生态里也算是“德高望重”。配置灵活、输出丰富,支持分等级、按时间或文件大小分割,还能愉快地和各种日志中台打通。
一句话总结:Log4js 就像个靠谱的管家,默默记录项目的喜怒哀乐,关键时刻还能“翻旧账”,帮你把锅找对人😝。

BFF层

node.js + koa.js 本人对koa.js->egg.js熟悉程度更高,所以我会选择**koa.js**

展示层

展示层不过多描述 选择老朋友vue3 + element-plus + webpack5

方案设计

DSL与总览图

DSL是一种专门为某个特定领域或问题域设计的编程语言。它的主要目的是提供一种简洁、易于使用和高效的方式来描述和解决特定领域的问题。本框架通过dsl ->解析器->模版页面
大致框架如下:其中绿色为自定义组件:可自行变动保证灵活性,蓝色部分为已设计部分:固定部分减少重复操作

服务端-BFF层方案设计

运行前(磁盘文件)-->解析器-->运行时(内存)
通过约定的规则在对应的文件目录放置对应的模块 通过elpis-core 对文件进行解析挂载到app对象中 在进行编码时候通过app就可以获取到我们想要使用的能力

展示层-前端架构设计

结合面向对象的在设计思路:封装、继承、多态,也就是有个基类可以实例化各种子类,各种子类又可以派生出各种实例。
所以我们可以基于一个封装好的各种领域模型的基类在针对出不同项目继承出不同的项目配置,在这个过程中我们新增或者重载领域模型的配置,再通过解析器把这份派生出来的子配置实例化成各个具体化的系统,实现高度可复用、可扩展的前端架构设计。

预热结束啦!前面看得一脸问号也没关系,接下来才是正片—— GUIYUAN 代码时间到!小板凳🪑准备好,咱们开搞~


基于node.js实现服务器内核引擎

宏观角度

1. 接入层

  • 接口路由处理,区分API请求,页面请求
  • 路由规则校验:请求是否满足最基本的校验
  • 路由中间件:是否携带所需的参数,认证是否通过等

2. 业务层

  • controller 处理器
  • env 环境区分
  • config 全局变量提取
  • extend 服务拓展插件
  • schedule 定时任务
    3. 服务层
  • service处理器,是跟数据层的交互

解析器

GUIYUAN 的主要功能之一就是实现下面的解析器

  • router-loader 解析router请求文件
    • 页面请求,
    • API请求
  • router-schema-loader:解析API参数类型校验文件
  • middleware-loader: 解析自定义中间件文件
  • controller-loader: 解析逻辑处理文件
  • service-loader: 解析服务文件(日志,mysql,调用外部服务等)
  • extend-loadr: 解析扩展文件
  • config-loader: 解析配置文件

koa洋葱模型

业务逻辑前遵从:先用先处理
业务逻辑后遵从:后用先处理

Loader加载器详细实现

前面我们已经设计好了解析器的思路,接下来看看如何具体实现这些 loader,让”约定大于配置”真正落地。

Controller & Service Loader

这两个 loader 的核心逻辑一致,都是通过 glob 扫描目录下的所有 JS 文件,然后按路径层级自动挂载到 app 对象上

核心特性:

  • 自动扫描:使用 glob.sync 扫描 app/controller/**/*.jsapp/service/**/*.js
  • 路径映射app/controller/user-info/login-controller.jsapp.controller.userInfo.loginController
  • 驼峰转换:自动将 -_ 转为驼峰命名
  • 类实例化:每个文件导出一个类,loader 自动 new 成实例
// app/controller/user/login.js
module.exports = (app) => {
  return class LoginController {
    async login(ctx) {
      // 使用 service
      const result = await app.service.user.login.doLogin(ctx.request.body)
      ctx.body = result
    }
  }
}

// 使用时:
router.post('/login', app.controller.user.login.login)

这样做的好处:

  1. 不需要手动 require 导入
  2. 目录结构即代码结构
  3. 新增功能只需新建文件,框架自动识别

Router Loader

Router Loader 职责是注册所有路由规则,并兜底处理 404 情况。

// app/router/user.js
module.exports = (app, router) => {
  router.post('/api/user/login', app.controller.user.login.login)
  router.get('/api/user/info', app.controller.user.info.getInfo)
}

核心机制:

  • 扫描 app/router/**/*.js 所有路由文件
  • 每个文件接收 (app, router) 参数,自行注册路由
  • 最后添加兜底路由 router.all('*') 重定向到首页,提升健壮性

Middleware & RouterSchema Loader

Middleware Loader:加载自定义中间件,支持在路由中按需使用

// app/middleware/auth.js
module.exports = (options) => {
  return async (ctx, next) => {
    // 鉴权逻辑
    if (!ctx.headers.token) {
      ctx.throw(401, 'Unauthorized')
    }
    await next()
  }
}

RouterSchema Loader:加载 API 参数校验规则,使用 ajv 进行 JSON Schema 校验

// app/router-schema/user.js
module.exports = {
  '/api/user/login': {
    type: 'object',
    required: ['username', 'password'],
    properties: {
      username: { type: 'string' },
      password: { type: 'string' }
    }
  }
}

Config & Extend Loader

Config Loader:加载不同环境的配置文件

// config/config.default.js  默认配置
// config/config.local.js    本地开发配置
// config/config.production.js  生产环境配置

Extend Loader:扩展 app 能力,比如数据库连接、日志工具等

// app/extend/logger.js
const log4js = require('log4js')
module.exports = (app) => {
  app.logger = log4js.getLogger()
}

GuiYuan-Core 启动流程

框架启动遵循固定的加载顺序,确保依赖关系正确:

// index.js 启动入口
const GuiYuanCore = require('./guiYuan-core')

GuiYuanCore.start({
  name: 'GuiYuan',
  homePage: '/'
})

启动时控制台输出:

-- [start] env: local
-- [start] load middleware done --
-- [start] load routerSchema done --
-- [start] load controller done --
-- [start] load service done --
-- [start] load config done --
-- [start] load extend done --
-- [start] global appMiddleware done --
-- [start] load router done --
Server is running on http://0.0.0.0:8080

前端工程化建设 - Webpack5 多入口架构

多页面应用架构设计

不同于单页应用(SPA),GUIYUAN 采用了多页面应用(MPA)架构,原因如下:

  1. 多系统建设需求:一个平台可能包含多个独立子系统(用户中心、数据看板、配置中心等)
  2. 按需加载:每个系统独立打包,避免首屏加载过大
  3. 隔离性强:各系统间互不干扰,方便独立部署和维护

约定式入口配置

框架通过 entry.*.js 命名约定自动识别入口文件:

app/pages/
├── page1/
│   ├── entry.page1.js   # 入口文件
│   ├── page1.vue
│   └── components/
├── page2/
│   ├── entry.page2.js
│   └── page2.vue
└── common/
    ├── curl.js
    └── utils.js

Webpack 配置中通过 glob 自动扫描:

// app/webpack/config/webpack.base.js
const pageEntries = {};
const entryList = path.resolve(process.cwd(), './app/pages/**/entry.*.js')
glob.sync(entryList).forEach((file) => {
  const entryName = path.basename(file, '.js')
  pageEntries[entryName] = file // entry.page1: './app/pages/page1/entry.page1.js'
})

这样新增页面时:

  1. 创建 app/pages/new-page/entry.newPage.js
  2. 重启 webpack,自动识别并打包
  3. 无需手动修改 webpack 配置

Webpack 核心配置解析

1. 模块解析(Module Rules)

Loader 类型 处理文件 作用
vue-loader .vue 解析 Vue 单文件组件
babel-loader .js ES6+ 转 ES5,兼容旧浏览器
ts-loader .ts / .tsx TypeScript 支持
url-loader 图片 小于 300 字节转 base64
css-loader .css 处理 CSS 文件
less-loader .less 处理 Less 预处理器
sass-loader .scss 处理 Sass 预处理器
file-loader 字体 处理 woff/ttf 等字体文件

2. 别名配置(Alias)

简化导入路径,提升开发体验:

resolve: {
  alias: {
    $pages: path.resolve(process.cwd(), './app/pages'),
    $common: path.resolve(process.cwd(), './app/pages/common'),
    $store: path.resolve(process.cwd(), './app/pages/store'),
  }
}

// 使用示例:
import curl from '$common/curl.js'  // 代替 '../../../common/curl.js'

3. 代码分割优化(SplitChunks)

将代码拆分为三类,充分利用浏览器缓存:

splitChunks: {
  cacheGroups: {
    vendor: { // 第三方依赖(vue、element-plus等)
      test: /[\\/]node_modules[\\/]/,
      name: 'vendor',
      priority: 20,  // 优先级最高
    },
    common: { // 项目公共模块(utils、components)
      name: 'common',
      minChunks: 2,  // 被引用 2 次以上才提取
      priority: 10,
    }
  }
}

优势

  • vendor.js:第三方库基本不变,浏览器长期缓存 ✅
  • common.js:公共模块改动少,缓存命中率高 ✅
  • entry.page.js:业务代码频繁更新,单独打包 ✅

4. 多入口 HTML 生成

使用 html-webpack-plugin 为每个入口生成独立的 .tpl 模板文件:

HtmlWebpackPluginList.push(
  new HtmlWebpackPlugin({
    filename: path.resolve(process.cwd(), './app/public/dist/', `${entryName}.tpl`),
    template: path.resolve(process.cwd(), './app/view/entry.tpl'),
    chunks: [entryName],  // 只注入当前入口的 JS/CSS
  })
)

生成的 .tpl 文件会被 Koa 通过 koa-nunjucks-2 渲染:

// app/router/page.js
router.get('/page1', async (ctx) => {
  await ctx.render('public/dist/entry.page1')  // 渲染 entry.page1.tpl
})

开发环境热更新配置

使用 webpack-dev-middleware + webpack-hot-middleware 实现热更新:

// app/webpack/dev.js
const express = require('express')
const webpack = require('webpack')
const devMiddleware = require('webpack-dev-middleware')
const hotMiddleware = require('webpack-hot-middleware')

const app = express()
const compiler = webpack(webpackConfig)

// 监控文件改动,实时编译
app.use(devMiddleware(compiler, {
  writeToDisk: (filePath) => filePath.endsWith('.tpl'),  // .tpl 文件落地磁盘
  publicPath: webpackConfig.output.publicPath,
}))

// 实现热更新(HMR)
app.use(hotMiddleware(compiler, {
  path: '/hmr',
}))

app.listen(3000, () => {
  console.log('Webpack DevServer 运行在 http://127.0.0.1:3000')
})

开发流程

  1. 启动后端服务:npm run dev(启动 Koa,监听 8080 端口)
  2. 启动前端构建:npm run build:dev(启动 Webpack DevServer,监听 3000 端口)
  3. 修改 .vue.js 文件,浏览器自动热更新 🔥

环境配置管理

通过环境变量区分开发、测试、生产环境:

{
  "scripts": {
    "dev": "_ENV='local' nodemon ./index.js",
    "beta": "_ENV='beta' nodemon ./index.js",
    "prod": "_ENV='production' nodemon ./index.js",
    "build:dev": "node --max_old_space_size=4096 ./app/webpack/dev.js",
    "build:prod": "node ./app/webpack/prod.js"
  }
}

配置文件加载逻辑:

// guiYuan-core/loader/config.js
const env = process.env._ENV || 'local'
const defaultConfig = require(`${app.baseDir}/config/config.default.js`)
const envConfig = require(`${app.baseDir}/config/config.${env}.js`)

app.config = Object.assign({}, defaultConfig, envConfig)

前端技术栈总结

技术 作用 为什么选它
Vue 3 前端框架 Composition API 更灵活,性能更强
Element Plus UI 组件库 开箱即用,企业级后台首选
Webpack 5 构建工具 成熟稳定,生态丰富,多入口场景支持好
Pinia 状态管理 Vue 3 官方推荐,比 Vuex 更轻量
Vue Router 路由管理 Vue 官方路由,支持嵌套路由和动态路由
Axios HTTP 请求 Promise 封装,拦截器机制方便统一处理
Less/Sass CSS 预处理器 嵌套、变量、混入,提升样式开发效率

实战效果展示

目录结构一览

GuiYuan/
├── guiYuan-core/          # 框架核心引擎
│   ├── loader/           # 各类加载器
│   │   ├── router.js
│   │   ├── controller.js
│   │   ├── service.js
│   │   ├── middleware.js
│   │   ├── router-schema.js
│   │   ├── config.js
│   │   └── extend.js
│   ├── index.js          # 框架启动入口
│   └── env.js            # 环境配置管理
├── app/                  # 业务代码目录
│   ├── controller/       # 控制器层
│   ├── service/          # 服务层
│   ├── router/           # 路由层
│   ├── middleware/       # 自定义中间件
│   ├── router-schema/    # API 校验规则
│   ├── extend/           # 扩展功能
│   ├── pages/            # 前端页面
│   │   ├── page1/
│   │   │   ├── entry.page1.js
│   │   │   └── page1.vue
│   │   ├── common/       # 公共模块
│   │   └── store/        # 状态管理
│   ├── view/             # 模板文件
│   ├── public/           # 静态资源
│   └── webpack/          # Webpack 配置
│       ├── config/
│       │   ├── webpack.base.js
│       │   ├── webpack.dev.js
│       │   └── webpack.prod.js
│       ├── dev.js
│       └── prod.js
├── config/               # 环境配置
│   ├── config.default.js
│   ├── config.local.js
│   └── config.production.js
├── index.js              # 项目启动文件
└── package.json

核心优势总结

约定式开发:新建文件即生效,无需手动注册
模块化设计:controller/service/router 职责分明
多入口架构:支持多系统并行开发,按需加载
热更新开发:修改代码实时预览,开发效率提升 300%
代码分割优化:充分利用浏览器缓存,加载速度提升 50%
环境隔离:local/beta/production 配置分离,部署无忧
TypeScript 支持:渐进式引入类型检查,代码更健壮
扩展性强:通过 extend 机制轻松集成日志、数据库、消息队列等


下一步规划

虽然 GUIYUAN 的基础建设已经完成,但要真正实现”归元”的愿景,还有很多路要走:

Phase 1 - 基础建设 ✅

  • ✅ GuiYuan-Core 核心引擎
  • ✅ Webpack 多入口工程化
  • ✅ 约定式目录结构

Phase 2 - 能力沉淀 🚧

  • 🔲 配置化页面生成系统
  • 🔲 拖拽式组件编排
  • 🔲 通用 CRUD 模板
  • 🔲 数据库 ORM 封装(基于 Knex)
  • 🔲 权限管理系统

Phase 3 - 平台化 🔮

  • 🔲 多租户系统支持
  • 🔲 插件市场机制
  • 🔲 低代码 DSL 设计
  • 🔲 可视化配置后台
  • 🔲 CI/CD 自动化部署

结语

GUIYUAN 的诞生,源于对重复劳动的厌倦,也源于对更高效工作方式的向往。

这不仅仅是一个技术框架,更是一种**”让80%的事情归于平台,让20%的创造归于开发者”**的哲学。

路漫漫其修远兮,框架的完善需要时间的打磨和实战的检验。但至少,我们已经迈出了第一步——从0到1,从想法到落地

接下来,就是在实际项目中不断迭代、优化、沉淀,让 GUIYUAN 真正成为那个能让开发者”佛系高效”的平台。

一切冗余,皆可归元。🌟

持续更新中…