mm_machine
v2.1.1
Published
这是超级美眉框架机制构建辅助模块,用于快速构建一个机制,支持动态加载、热更新、模块管理等功能,并具有增强的错误处理和现代JavaScript特性支持。
Maintainers
Readme
mm_machine
一个灵活的Node.js插件化机制系统,用于动态加载、管理和执行模块。
快速开始
安装
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使用示例
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 文件(注意命名规则):
{
"name": "demo1",
"title": "测试模块1",
"description": "这是第一个测试模块",
"sort": 10,
"state": 1,
"show": 1,
"config": {
"debug": true,
"timeout": 3000
}
}2. 创建模块实现
在同一目录中创建 index.js 或 main.js 文件:
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.json - 如果
this.type = "plugin",配置文件必须命名为config_plugin.json
完整使用示例
示例1:创建和管理基础模块
以下是一个完整的示例,展示如何创建和管理基础模块:
1. 项目结构
my_project/
├── index.js # 主应用入口
├── app/ # 模块目录
│ ├── demo1/ # 模块1
│ │ ├── config_demo.json # 模块配置文件
│ │ └── index.js # 模块实现
│ └── demo2/ # 模块2
│ ├── config_demo.json # 模块配置文件
│ └── main.js # 模块实现
└── package.json2. 主应用入口 (index.js)
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)
{
"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)
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
};示例2:使用自定义模块目录
如果需要使用非默认的模块目录,可以按以下方式配置:
const engine = new Machine();
// 使用自定义目录
engine.update_config_all({
searchPath: './custom_modules/'
});
// 然后加载模块
await engine.loads();示例3:实现热更新和动态管理
以下示例展示如何实现模块的热更新和动态管理:
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);示例4:错误处理和异常捕获
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属性设置:
- 生产模式 (
mode = 1): 最高性能,文件改变不会触发重新加载 - 热更新模式 (
mode = 2): 文件改变时自动重新加载配置和代码 - 重载模式 (
mode = 3): 执行完后重新加载脚本,避免变量污染 - 热更新+重载模式 (
mode = 4): 结合热更新和重载的特性
模块管理API
// 重载特定模块
await engine.reload("moduleName");
// 卸载模块(保留文件)
await engine.unload("moduleName");
// 卸载并删除模块文件
await engine.unload("moduleName", true);
// 保存模块配置
await engine.save();
// 获取特定模块
const module = engine.get("moduleName");路径处理
- 所有相对路径都会基于模块的目录进行解析
- 使用
fullname()方法可以获取绝对路径,避免路径问题
常见问题与故障排除
1. 模块无法加载
检查项目:
- 确认配置文件命名格式正确 (
config_{type}.json) - 确认
type属性与配置文件前缀匹配 - 检查模块的
state配置是否为1(启用) - 检查文件权限是否正确
2. 热更新不工作
检查项目:
- 确认
mode设置为2或4(热更新模式) - 确认文件确实被修改了(注意编辑器的保存设置)
- 检查文件路径是否正确
3. 执行方法返回null
检查项目:
- 确认模块已正确加载 (
engine.list.length > 0) - 确认方法名拼写正确
- 检查模块实现中是否正确导出了该方法
- 检查模块的
state是否为1(启用状态)
4. 路径错误
解决方案:
- 使用绝对路径或确保相对路径正确
- 利用
__dirname和fullname()方法构建可靠的路径
模块系统设计理念与工作流程
设计理念
mm_machine 模块系统基于以下核心设计理念:
- 高内聚、低耦合:每个模块独立封装功能,通过明确的接口与其他模块交互
- 可插拔架构:模块可以动态加载、卸载,不影响系统整体运行
- 配置驱动:通过配置文件控制模块行为,无需修改代码
- 生命周期管理:提供完整的模块生命周期钩子,便于资源管理
- 热更新支持:无需重启应用即可更新模块功能
工作流程
以下是模块系统的基本工作流程:
初始化引擎 → 扫描模块目录 → 解析配置文件 → 加载模块代码 → 初始化模块 → 执行方法 → 监听文件变化(热更新模式)详细工作流程说明:
- 初始化引擎:创建 Machine 实例,设置配置项
- 扫描模块目录:根据 searchPath 扫描目录,查找
config_{type}.json文件 - 解析配置文件:读取并解析配置,验证模块状态
- 加载模块代码:根据配置中的信息,动态加载模块的实现文件
- 初始化模块:按排序顺序初始化模块,执行 init 和 load 方法
- 执行方法:根据应用需求,执行模块的特定方法(如 main)
- 监听文件变化:在热更新模式下,监听文件变化并自动重新加载模块
数据流向
模块间的数据流转遵循以下规则:
- 引擎将调用参数传递给模块方法
- 模块方法处理后返回结果
- 钩子方法可以拦截和修改输入/输出数据
- 模块可以访问自己的配置,但不能直接访问其他模块的数据
最佳实践与开发建议
模块设计最佳实践
- 单一职责原则:每个模块应专注于单一功能领域
- 接口一致性:遵循模块生命周期接口约定,确保兼容性
- 错误处理:在模块内部实现完善的错误处理机制
- 日志记录:使用统一的日志接口记录模块活动
- 资源管理:在适当的生命周期钩子中释放资源
配置管理建议
- 配置分层:将公共配置和模块特有配置分开
- 配置验证:在模块初始化时验证配置有效性
- 默认值处理:为重要配置项提供合理的默认值
- 敏感信息保护:避免在配置文件中直接存储敏感信息
性能优化建议
- 懒加载:对于资源密集型模块,考虑实现懒加载机制
- 缓存策略:合理使用缓存减少重复计算
- 异步操作:使用 async/await 处理异步操作,避免阻塞
- 减少全局状态:尽量减少模块间的状态共享
调试技巧
- 日志级别:利用不同级别的日志(debug、info、error)辅助调试
- 热更新调试:使用热更新模式(mode=2)进行开发调试
- 模块隔离:出现问题时,尝试单独测试特定模块
- 配置检查:验证配置文件格式和内容是否正确
部署注意事项
- 生产环境模式:部署到生产环境时,设置 mode=1 以获得最佳性能
- 配置备份:定期备份模块配置文件
- 版本控制:对模块代码和配置进行版本控制
- 依赖管理:明确声明和管理模块依赖
贡献指南
如果您有兴趣为 mm_machine 项目贡献代码或改进,请遵循以下步骤:
- Fork 项目仓库
- 创建您的功能分支 (
git checkout -b feature/amazing-feature) - 提交您的更改 (
git commit -m 'Add some amazing feature') - 推送到分支 (
git push origin feature/amazing-feature) - 打开 Pull Request
更新日志
v1.0.0 (初始版本)
- 基础模块管理功能
- 配置文件加载和解析
- 模块生命周期管理
- 热更新支持
- 基础错误处理
v1.1.0
- 改进模块加载逻辑,支持自定义目录
- 增强错误处理和异常捕获
- 添加更多生命周期钩子
- 优化性能和内存使用
依赖
- mm_config: ^1.1.4
- mm_hot_reload: ^1.0.5
- mm_expand: 用于路径处理和文件操作
许可证
ISC
作者信息
本模块由 mm_modules 团队开发和维护。如有问题或建议,请联系我们。
