抖音“哲玄前端”《大前端全栈实践》里程碑一的回顾与思考
BFF 层架构设计
elpis-code 内核引擎设计
通过解析器 elpis-core 解析 app 文件夹下的文件,并挂载到 koa 实例上。利用 koa 洋葱圈模型实现中间件处理机制,启动运行时服务 - 简化版 egg.js
-
config loader 加载环境配置文件。提供默认配置,并提供对开发环境,测试环境,生产环境不同环境进行差异化配置的能力
-
controller loader 加载 controller 模块,提供核心业务处理能力
-
BaseController 基类收拢 controller 相关公共方法。比如
- API 处理成功时统一返回结构
- API 处理失败时统一返回结构
-
ProjectController - API 响应
-
ViewController - 页面渲染,服务端渲染
-
-
extend loader 加载 extend 模块。扩展能力,挂载在全局 app 实例上
- 日志 - log4js
-
middleware loader 加载中间件模块,挂载到 app 实例上
- api 参数校验
- api 签名合法性校验
- 运行时异常错误处理,兜底所有异常
- ...
-
global-middlerware 注册全局中间件,洋葱圈外层
-
router loader 加载路由模块。路由统一管理 - KoaRouter
-
router-schema loader 加载 json-schema 配置。通过 'json-schema & ajv' 对 API 规则进行约束, 配合 api-parms-verify 中间件使用
-
service loader 加载 service 模块,服务层处理
- 读写 mysql
- 日志
- ...
loader 加载顺序的考量
暂时无法在飞书文档外展示此内容
- 配置优先
- 扩展能力提前
- middleware 挂载到 app 实例上,等待注册使用
- global-middleware 注册全局中间件(洋葱圈外层)
- 进入服务层 - service(被 controller 依赖)
- router-schema 的 json 一般配置在 controller 和路由之前
- controller 做核心业务处理,连接服务层和展示层 - 洋葱圈内层
- router 依赖于 controller - 洋葱圈内层
依据
- 约定优于配置 - 存在一定沟通成本
- 执行顺序
- 依赖关系
问题
-
全局中间件无法获取路由参数 params
-
原因:全局中间件的执行顺序早于路由中间件,而路由参数 params 由 koa-router 在路由级中间件注入
-
解决方案
- API 参数校验中间件不注册为全局中间件使用,通过
router.get(path, middleware, handler)注入到具体路由 - 调整中间件执行顺序,破坏结构,不符合设计逻辑。如果路由不匹配,后续中间件不会执行
- 全局手动解析 url 路径获取参数,灵活性较差
- API 参数校验中间件不注册为全局中间件使用,通过
-
选择精准注入具体路由方式进行 API 校验,需要校验的路由注入,不需要校验的则不注入。灵活可控
-
-
服务端渲染 - ssr
koa-router调用ctx.render(),引擎将模板 + 数据合并为 HTMLkoa-static配置静态根目录koa-nunjucks-2模板渲染引擎。向 ctx 注入 render 方法,支持服务端拼接 HTMLkoa-bodyparser处理表单提交,结合模板重新渲染页面
-
302 临时重定向
-
为什么不使用 301
- 兜底路由用于处理无效路径,重定向到指定路径
- 301 会缓存重定向的结果,后续所有请求直接跳到旧路径
- 使用 302 重定向,浏览器不会将首页与无效路径关联
-
301 302 303 的区别
-
状态码 语义 浏览器行为 SEO影响 典型场景 301 永久重定向 缓存重定向结果,后续请求直接跳新URL 搜索引擎转移权重至新URL,旧URL被淘汰 域名永久更换、目录结构调整 302 临时重定向 不缓存重定向结果,每次请求均需验证跳转 搜索引擎保留旧URL索引,新URL不继承权重 A/B测试、临时维护页 303 见其他资源 强制使用GET方法重定向(防表单重复提交) 同302,不传递权重 POST请求后需跳转到结果页的场景
-
-
项目结构
elpis
├─ .eslintignore
├─ .eslintrc
├─ app
│ ├─ controller
│ │ ├─ base.js
│ │ ├─ middleware
│ │ ├─ project.js
│ │ └─ view.js
│ ├─ extend
│ │ └─ logger.js
│ ├─ middleware
│ │ ├─ api-params-verify.js
│ │ ├─ api-sign-verify.js
│ │ └─ error-handler.js
│ ├─ middleware.js
│ ├─ router
│ │ ├─ project.js
│ │ └─ view.js
│ ├─ router-schema
│ │ └─ project.js
│ └─ service
│ ├─ base.js
│ └─ project.js
├─ config
│ ├─ config.beta.js
│ ├─ config.default.js
│ └─ config.prod.js
├─ elpis-core
│ ├─ env.js
│ ├─ index.js
│ └─ loader
│ ├─ config.js
│ ├─ controller.js
│ ├─ extend.js
│ ├─ middleware.js
│ ├─ router-schema.js
│ ├─ router.js
│ └─ service.js
├─ index.js
├─ package.json
└─ README.md
总结
约定优于配置,分层明确,扩展能力强