完成 elpis-npm 包分离并发布

elpis-npm包拆分与发布实践

背景与目标

在早期的 elpis 项目中,前后端代码、业务逻辑、工程化构建都混杂在一起,项目启动、构建、路由、中间件等都紧耦合于具体业务。随着项目演进,我们希望将核心能力抽象为一个可复用的 SDK(npm 包),以便:

  • 在多个业务项目中复用 elpis 的启动、路由加载、构建流程等能力;

  • 保持业务项目的灵活性,让业务方能做扩展或覆盖;

  • 使得核心框架与业务代码分离,便于维护、升级和版本管理。

所以,本次工作的目标是把 elpis 拆分成一个 npm 包(比如 @your-org/elpis-core 或类似命名),然后发布到 npm 上供业务项目安装使用。

下面我按步骤讲解这个拆分、封装与发布的过程。


整体思路与拆分策略

在拆分过程中,我主要考虑以下几点:

  1. 清晰边界:哪些代码属于框架核心(启动流程、加载器、插件机制、路由/中间件加载等),哪些代码属于业务项目(具体 controller、service、视图页面等)要划清边界;

  2. 加载机制:核心包内部要能够“读取”业务项目目录下的 controller、service、middleware、路由定义等,并合并/注册它们;

  3. 配置合并:核心包应带默认配置,业务项目可以提供配置覆盖或扩展;

  4. 前端工程集成:核心包中集成前端构建能力(webpack 配置、页面入口扫描、模板注入等),业务项目可以按需注入自定义配置;

  5. 别名与路径映射:在 webpack、模块解析、alias 等处,需要让核心包和业务项目文件能互相寻址;

  6. 对外 API:在核心包的 index.js 或主入口处暴露出启动、构建、控制器、服务、扩展点等接口;

  7. 打包与发布:决定包中的文件结构、哪些文件要纳入 npm 包、发布流程、版本管理等。

总的来说,就是“核心包做框架骨架 + 约定式加载业务代码 + 业务项目可配置/覆盖”的模式。


步骤详解

下面是一个较为完整的拆分流程和技术细节:

1. 构建核心包的 package.json

  • 包名通常采用命名空间形式(@your-org/elpis@your-org/elpis-core 等),便于区分管理与版本升级。

  • 指定入口文件(main 字段),如 index.js,作为对外暴露能力的入口。

  • package.json 中配置 files 字段,指定哪些文件被打包到 npm(例如核心目录、loader、模板、公共资源等)。

  • 如果是公开包,设定发布权限:

    "publishConfig": {
      "registry": "https://registry.npmjs.org",
      "access": "public"
    }
    

  • 添加标准的 scripts(如 lint、test 等)和版本管理(利用 npm version)等。

2. 在核心包里设计加载器(Loader)机制

核心包需要能在启动时,扫描并加载业务项目中的 module(middleware、controller、service、router、schema 等)。具体流程大致如下(灵感源自上文文章):

  • 通过 process.cwd() 确定业务项目根目录(app.businessPath)。

  • 在核心包内定义一系列 loader(middlewareLoader、configLoader、controllerLoader、serviceLoader、routerLoader 等)。

  • 每个 loader:

    1. 先加载自身 core 包内部对应目录的资源;

    2. 再扫描业务项目目录下对应路径(如 app/controller/**app/service/**app/middleware/** 等);

    3. 合并、覆盖或挂载业务的模块到核心应用上下文。

  • 在加载某些文件时,用 glob 等工具遍历文件列表,并用统一的 handleFile(file) 逻辑处理(提取文件名、驼峰命名、require 模块、挂载到 app.controllerapp.services 等)。

  • 对于配置文件(如 config.default.jsconfig.local.js 等),进行合并:默认配置 + 业务覆盖配置。

  • 对于路由、schema、扩展点,也做类似的加载与合并。

3. 暴露对外 API:启动、构建、拓展等

在核心包的入口文件(index.js 或类似)中暴露外部使用者调用的接口:

module.exports = {
  serverStart(options = {}) {
    const app = ElpisCore.start(options);
    return app;
  },
  frontendBuild(env) {
    if (env === 'local') FEBuildDev();
    else if (env === 'production') FEBuildProd();
  },
  Controller: {
    Base: require('./app/controller/base.js'),
  },
  Service: {
    Base: require('./app/service/base.js'),
  },
  // 如有必要还可暴露扩展点、插件 API 等
};

这样业务项目就可以这样使用:

const { serverStart, frontendBuild, Controller } = require('@your-org/elpis-core');

serverStart({ /* 配置选项 */ });
frontendBuild('production');

4. 前端工程化集成

一个完整的 elpis 框架既包含后端服务,也包含前端构建流程。因此在核心包中还要处理前端部分:

  • 在核心包内部维护默认的前端页面目录、入口文件(如 core/pages/entry.*.js 等);

  • 业务项目可以在其 app/pages/**/entry.*.js 下定义自己的页面入口;

  • 在打包阶段,扫描核心包目录和业务目录的入口文件,将它们合并成 webpack 的 entry 配置;

  • 构建时引入 HtmlWebpackPlugin 等插件,将打包产物注入到模板中;

  • 允许业务通过在根目录下提供 app/webpack.config.js 来扩展 webpack 配置。核心包要用 merge.smart(或其他合并策略)将默认配置和业务配置合并。

  • 在 loader 里引入核心包内(或业务内)各种资源时,应优先使用 require.resolve(...) 等方法,确保加载的是核心包内的模块而不是业务项目的同名模块。

  • 在 webpack 的 alias 配置里,需要把核心包的页面路径(如 $elpisPages$elpisCommon 等)映射过去,同时给一些可扩展模块(如 $businessDashboardRouterConfig$businessComponentConfig 等)留一个空白模块或 fallback,以便业务项目覆盖。

  • 在构建流程里,协同业务项目自定义的 webpack 配置中的插件、loader、别名、路径等与核心包的默认配置融合。

  • 对于资源(图片、CSS、Less、字体等)的规则也要统一处理,确保核心包和业务项目的资源能一起被处理。

5. 路由与扩展能力

业务项目通常需要在默认路由基础上做扩展或定制。为此:

  • 核心包内部维护自己的路由(headerRoutes、siderRoutes、schemaRoutes 等);

  • 业务项目可以提供如 app/pages/dashboard/business-router.js(或命名类似)来定义业务自己的 headerRoutes/siderRoutes;

  • 在启动阶段,核心包会读取业务的路由配置(若存在),然后把业务路由插入或合并到默认路由中;

  • 对于路由以外的扩展点(如业务自定义组件、表单项、搜索栏项、schema 组件等),可以通过 alias 映射 + 业务提供的模块(如 $businessComponentConfig)来覆盖或扩展。

6. 打包 & 发布流程

当核心包搭建好了、API 定义好了、loader 机制实现完毕之后,就可以做发布准备:

  1. 版本管理
    使用 npm version patch / npm version minor / npm version major 来更新版本号,自动修改 package.json 并打 git tag。
     

  2. 切换 npm registry / 登录
    如果之前曾使用镜像(如淘宝镜像),需要切回官方 registry,如:

    npm config set registry https://registry.npmjs.org/

    然后执行

    npm login
  3. 发布命令
    对于普通公开包:

    npm publish --access public

    如果有自定义 registry 或作用域包,可加 --registry 等参数。
     

  4. 验证 & 安装测试
    发布之后,在一个干净的业务项目里尝试安装:

    npm install @your-org/elpis-core

    然后编写最小示例代码验证 serverStartfrontendBuild、业务 controller/route 是否能正常加载运行。

  5. 弃用 / 删除策略
    如果到了某个版本想废弃旧包,可以使用 npm deprecate 给包打弃用提示;而对于某个版本删除,需要注意 npm 对 unpublish 的限制(通常只能在发布后 72 小时内删除)等。

7. 遇到的挑战与思考

在这个拆分 + 发布过程中,会碰到一些比较棘手的问题,下面列出几条常见挑战与思路:

挑战解决思路 / 注意点
如何保证业务代码的异步加载 / 热更新机制在开发环境下,可以让核心包启动服务时启用 webpack dev middleware + hot middleware,业务项目同样参与热更新;业务部分也要让其入口符合核心包识别机制
模块版本冲突 / 重复依赖在核心包 dependencies 中声明各类依赖,且在业务项目中尽量避免再重复引入同版本,以免版本冲突;通过 peerDependencies 或指导业务项目统一依赖版本
模板路径 / 静态资源路径解析问题在 alias 和路径映射上要精心设计,使得核心包和业务项目之间模块导入路径不会冲突;模板填充、静态文件拷贝等也要兼顾两者的目录结构
可扩展性与可配置性平衡核心包应内建合理默认值、插件能力和扩展点,但不要让扩展体系过于臃肿;业务项目要能按需渐进式覆盖默认能力
发布包体积 & 依赖控制files 字段、 .npmignore 等方式控制不要把不必要的测试、配置、文档、源码(如果不必暴露)都打包进去。

完整示例结构(目录示意)

下面是一个拆分完成后的示例目录结构示意:

elpis-core/
├── package.json
├── index.js           // 对外暴露启动 / 构建 / 扩展接口
├── app/                // 核心包内置的 controller / service / middleware 等
│   ├── controller/
│   ├── service/
│   ├── middleware/
│   └── ...
├── loader/             // 各种 loader(configLoader、controllerLoader、routerLoader…)
├── pages/              // 核心默认的前端页面入口等
├── view/               // html / tpl 模板文件
├── webpack/            // 默认 webpack 配置文件(base / dev / prod)
└── utils/              // 工具 / 公共方法等

业务项目则目录可能如下:

my-app/
├── package.json
├── app/
│   ├── controller/
│   ├── service/
│   ├── middleware/
│   ├── pages/
│   │   ├── dashboard/
│   │   └── ...  // 自定义页面入口等
│   ├── config/
│   │   └── config.default.js
│   └── webpack.config.js  // 业务可选的 webpack 扩展配置
└── scripts / src / public / …  // 其他业务项目目录

在业务项目入口里只需很少的启动逻辑:

const { serverStart, frontendBuild } = require('@your-org/elpis-core');

serverStart({
  // 业务自定义配置覆盖核心默认值
});

frontendBuild('production');

核心包启动时会自动扫描 my-app/app 下面的 controller/service/router 等,并挂载到主应用中,从而整体运行。


总结与感悟

通过这次 elpis 拆分和 npm 发布的实践,我有以下几点体会:

  1. 架构先行思考很重要:在拆分之前,先把框架核心能力、扩展点、业务需求都画一个蓝图,有助于之后分层拆解。

  2. 加载器 + 扫描机制是关键:让核心包能够“透明”地识别业务项目结构,并把它们合并到运行时环境中,是整个拆分方案的中枢。

  3. 配置合并与约定优于配置:给出合理的默认配置,同时提供覆盖扩展方式,是兼顾灵活性与简洁性的良方。

  4. 对外接口设计要稳妥:暴露给业务项目的 API 要简洁、清晰、向下兼容,避免后续演进带来的大破坏。

  5. 发布流程 & 包管理也要谨慎:版本控制、包体积、依赖管理、弃用策略都需要提前设计好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值