A flexible Node.js plugin mechanism system for dynamic loading, management and execution of modules. Supports hot reload, lifecycle management, and modern JavaScript features.
npm install mm_machine一个灵活的Node.js插件化机制系统,用于动态加载、管理和执行模块。
``bash`
npm install mm_machine --save
推荐的项目结构:
``
my_project/
├── index.js # 主应用入口
├── app/ # 默认模块目录
│ ├── module1/ # 模块1
│ │ ├── config_demo.json # 配置文件 (注意命名规则)
│ │ └── index.js # 模块实现
│ └── module2/ # 模块2
│ ├── config_demo.json # 配置文件
│ └── main.js # 模块实现 (可以使用index.js或main.js)
└── package.json
`javascript
const $ = require('mm_expand');
const Machine = require('mm_machine');
// 创建引擎实例
const engine = new Machine();
// 初始化并加载模块
async function init() {
// 更新配置(可选)
engine.update_config_all({
searchPath: './app/' // 默认路径,可自定义
});
// 加载所有模块
await engine.loads();
// 执行所有模块的main方法
const results = await engine.execute('main');
$.log.debug('执行结果:', results);
// 获取特定模块
const demo1 = engine.get('demo1');
if (demo1) {
$.log.debug('找到模块:', demo1.name);
}
$.log.debug(已加载 ${engine.list.length} 个模块);
}
// 启动应用
init().catch((err) => {
$.log.error('初始化失败:', err);
});
`
1. 创建配置文件
在模块目录中创建 config_demo.json 文件(注意命名规则):
`json`
{
"name": "demo1",
"title": "测试模块1",
"description": "这是第一个测试模块",
"sort": 10,
"state": 1,
"show": 1,
"config": {
"debug": true,
"timeout": 3000
}
}
2. 创建模块实现
在同一目录中创建 index.js 或 main.js 文件:
`javascript
const $ = require('mm_expand');
/**
* 主方法
*/
async function main() {
$.log.info([${this.name}] 测试模块执行);
// 访问模块配置
const debug = this.config.debug;
if (debug) {
$.log.debug([${this.name}] 调试模式已启用);
}
return {
success: true,
message: ${this.name} 执行成功
};
}
// 导出方法
module.exports = {
main
};
`
配置文件必须按照以下格式命名:
``
config_{type}.json
其中{type}必须与您在Engine类中设置的this.type属性值完全匹配。例如:
- 如果this.type = "demo",配置文件必须命名为config_demo.jsonthis.type = "plugin"
- 如果,配置文件必须命名为config_plugin.json
以下是一个完整的示例,展示如何创建和管理基础模块:
1. 项目结构
``
my_project/
├── index.js # 主应用入口
├── app/ # 模块目录
│ ├── demo1/ # 模块1
│ │ ├── config_demo.json # 模块配置文件
│ │ └── index.js # 模块实现
│ └── demo2/ # 模块2
│ ├── config_demo.json # 模块配置文件
│ └── main.js # 模块实现
└── package.json
2. 主应用入口 (index.js)
`javascript
const $ = require('mm_expand');
const Machine = require('mm_machine');
// 创建引擎实例
const engine = new Machine();
// 初始化引擎
async function init() {
// 加载模块
await engine.loads();
// 执行所有模块的main方法
await engine.execute('main');
// 获取特定模块
const demo1 = engine.get('demo1');
if (demo1) {
$.log.debug('模块1状态:', demo1.state ? '启用' : '禁用');
// 执行特定模块的自定义方法
const result = await engine.execute('my_custom_method', 'demo1');
$.log.debug('自定义方法结果:', result);
}
// 监听文件变化(热更新模式)
engine.mode = 2;
engine.watch();
$.log.debug(已加载 ${engine.list.length} 个模块);
}
// 启动应用
init().catch((err) => {
$.log.error('初始化失败:', err);
});
`
3. 模块配置文件示例 (app/demo1/config_demo.json)
`json`
{
"name": "demo1",
"title": "测试模块1",
"description": "这是第一个测试模块",
"sort": 10,
"state": 1,
"show": 1,
"config": {
"debug": true,
"timeout": 3000,
"api_key": "your_api_key_here"
}
}
4. 模块实现示例 (app/demo1/index.js)
`javascript
const $ = require('mm_expand');
// 模块状态变量
let counter = 0;
/**
* 初始化方法
*/
async function init() {
$.log.info([${this.name}] 模块初始化中...);
counter = 0;
return true;
}
/**
* 加载方法
*/
async function load() {
$.log.info([${this.name}] 模块加载中...);
// 可以在这里执行加载资源等操作
return true;
}
/**
* 主方法
* @param {Object} params - 传入参数
*/
async function main(params = {}) {
$.log.info([${this.name}] 主方法执行,当前计数: ${counter++});
// 访问模块配置
const debug = this.config.debug;
if (debug) {
$.log.debug([${this.name}] 调试模式已启用);
}
// 返回结果
return {
success: true,
message: ${this.name} 执行成功,
count: counter,
params: params
};
}
/**
* 主方法执行前钩子
*/
async function main_before() {
$.log.info([${this.name}] 主方法执行前处理);
return true;
}
/**
* 主方法执行后钩子
*/
async function main_after(result) {
$.log.info([${this.name}] 主方法执行后处理,结果:, result);
return result;
}
/**
* 自定义方法
*/
async function my_custom_method() {
return {
custom: '这是一个自定义方法',
module_name: this.name
};
}
// 导出方法
module.exports = {
init,
load,
main,
main_before,
main_after,
my_custom_method
};
`
如果需要使用非默认的模块目录,可以按以下方式配置:
`javascript
const engine = new Machine();
// 使用自定义目录
engine.update_config_all({
searchPath: './custom_modules/'
});
// 然后加载模块
await engine.loads();
`
以下示例展示如何实现模块的热更新和动态管理:
`javascript
const engine = new Machine();
// 设置为热更新模式
engine.mode = 2;
// 初始化
async function setup() {
// 加载模块
await engine.loads();
// 开始监听文件变化
engine.watch();
// 定时检查模块数量
setInterval(async () => {
$.log.debug(当前模块数量: ${engine.list.length});
}, 5000);
// 模拟动态管理模块
setTimeout(async () => {
// 重载特定模块
await engine.reload('demo1');
$.log.debug('已重载模块 demo1');
}, 10000);
setTimeout(async () => {
// 卸载模块
await engine.unload('demo2');
$.log.debug('已卸载模块 demo2');
}, 20000);
}
setup().catch($.log.error);
`
`javascript
const engine = new Machine();
async function run() {
try {
// 加载模块并捕获可能的错误
await engine.loads();
// 安全地执行模块方法
try {
const result = await engine.execute('main');
$.log.debug('执行结果:', result);
} catch (err) {
$.log.error('执行方法时出错:', err);
// 继续执行,不会中断整个应用
}
// 单独执行特定模块并捕获可能的错误
try {
const demo1Result = await engine.execute('main', 'demo1');
$.log.debug('demo1 执行结果:', demo1Result);
} catch (err) {
$.log.error('执行 demo1 时出错:', err);
}
} catch (err) {
$.log.error('初始化失败:', err);
}
}
run();
`
每个模块支持完整的生命周期方法:
- init: 模块初始化时调用load
- : 模块加载时调用main
- : 主执行方法main_before
- : 主方法执行前的钩子main_after
- : 主方法执行后的钩子
系统支持4种运行模式,可通过mode属性设置:
1. 生产模式 (mode = 1): 最高性能,文件改变不会触发重新加载mode = 2
2. 热更新模式 (): 文件改变时自动重新加载配置和代码mode = 3
3. 重载模式 (): 执行完后重新加载脚本,避免变量污染mode = 4
4. 热更新+重载模式 (): 结合热更新和重载的特性
`javascript
// 重载特定模块
await engine.reload('moduleName');
// 卸载模块(保留文件)
await engine.unload('moduleName');
// 卸载并删除模块文件
await engine.unload('moduleName', true);
// 保存模块配置
await engine.save();
// 获取特定模块
const module = engine.get('moduleName');
`
- 所有相对路径都会基于模块的目录进行解析
- 使用fullname()方法可以获取绝对路径,避免路径问题
检查项目:
- 确认配置文件命名格式正确 (config_{type}.json)type
- 确认属性与配置文件前缀匹配state
- 检查模块的配置是否为1(启用)
- 检查文件权限是否正确
检查项目:
- 确认mode设置为2或4(热更新模式)
- 确认文件确实被修改了(注意编辑器的保存设置)
- 检查文件路径是否正确
检查项目:
- 确认模块已正确加载 (engine.list.length > 0)state
- 确认方法名拼写正确
- 检查模块实现中是否正确导出了该方法
- 检查模块的是否为1(启用状态)
解决方案:
- 使用绝对路径或确保相对路径正确
- 利用__dirname和fullname()方法构建可靠的路径
mm_machine 模块系统基于以下核心设计理念:
1. 高内聚、低耦合:每个模块独立封装功能,通过明确的接口与其他模块交互
2. 可插拔架构:模块可以动态加载、卸载,不影响系统整体运行
3. 配置驱动:通过配置文件控制模块行为,无需修改代码
4. 生命周期管理:提供完整的模块生命周期钩子,便于资源管理
5. 热更新支持:无需重启应用即可更新模块功能
以下是模块系统的基本工作流程:
``
初始化引擎 → 扫描模块目录 → 解析配置文件 → 加载模块代码 → 初始化模块 → 执行方法 → 监听文件变化(热更新模式)
详细工作流程说明:
1. 初始化引擎:创建 Machine 实例,设置配置项
2. 扫描模块目录:根据 searchPath 扫描目录,查找 config_{type}.json 文件
3. 解析配置文件:读取并解析配置,验证模块状态
4. 加载模块代码:根据配置中的信息,动态加载模块的实现文件
5. 初始化模块:按排序顺序初始化模块,执行 init 和 load 方法
6. 执行方法:根据应用需求,执行模块的特定方法(如 main)
7. 监听文件变化:在热更新模式下,监听文件变化并自动重新加载模块
模块间的数据流转遵循以下规则:
1. 引擎将调用参数传递给模块方法
2. 模块方法处理后返回结果
3. 钩子方法可以拦截和修改输入/输出数据
4. 模块可以访问自己的配置,但不能直接访问其他模块的数据
1. 单一职责原则:每个模块应专注于单一功能领域
2. 接口一致性:遵循模块生命周期接口约定,确保兼容性
3. 错误处理:在模块内部实现完善的错误处理机制
4. 日志记录:使用统一的日志接口记录模块活动
5. 资源管理:在适当的生命周期钩子中释放资源
1. 配置分层:将公共配置和模块特有配置分开
2. 配置验证:在模块初始化时验证配置有效性
3. 默认值处理:为重要配置项提供合理的默认值
4. 敏感信息保护:避免在配置文件中直接存储敏感信息
1. 懒加载:对于资源密集型模块,考虑实现懒加载机制
2. 缓存策略:合理使用缓存减少重复计算
3. 异步操作:使用 async/await 处理异步操作,避免阻塞
4. 减少全局状态:尽量减少模块间的状态共享
1. 日志级别:利用不同级别的日志(debug、info、error)辅助调试
2. 热更新调试:使用热更新模式(mode=2)进行开发调试
3. 模块隔离:出现问题时,尝试单独测试特定模块
4. 配置检查:验证配置文件格式和内容是否正确
1. 生产环境模式:部署到生产环境时,设置 mode=1 以获得最佳性能
2. 配置备份:定期备份模块配置文件
3. 版本控制:对模块代码和配置进行版本控制
4. 依赖管理:明确声明和管理模块依赖
如果您有兴趣为 mm_machine 项目贡献代码或改进,请遵循以下步骤:
1. Fork 项目仓库
2. 创建您的功能分支 (git checkout -b feature/amazing-feature)git commit -m 'Add some amazing feature'
3. 提交您的更改 ()git push origin feature/amazing-feature`)
4. 推送到分支 (
5. 打开 Pull Request
- 基础模块管理功能
- 配置文件加载和解析
- 模块生命周期管理
- 热更新支持
- 基础错误处理
- 改进模块加载逻辑,支持自定义目录
- 增强错误处理和异常捕获
- 添加更多生命周期钩子
- 优化性能和内存使用
- mm_config: ^1.1.4
- mm_hot_reload: ^1.0.5
- mm_expand: 用于路径处理和文件操作
ISC
本模块由 mm_modules 团队开发和维护。如有问题或建议,请联系我们。