mm_machine
Version:
这是超级美眉框架机制构建辅助模块,用于快速构建一个机制,支持动态加载、热更新、模块管理等功能,并具有增强的错误处理和现代JavaScript特性支持。
577 lines (436 loc) • 15.3 kB
Markdown
一个灵活的Node.js插件化机制系统,用于动态加载、管理和执行模块。
```bash
npm install mm_machine --save
```
推荐的项目结构:
```
my_project/
├── index.js
├── app/
│ ├── module1/
│ │ ├── config_demo.json
│ │ └── index.js
│ └── module2/
│ ├── config_demo.json
│ └── 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.load_list();
// 执行所有模块的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.json`
- 如果`this.type = "plugin"`,配置文件必须命名为`config_plugin.json`
以下是一个完整的示例,展示如何创建和管理基础模块:
**1. 项目结构**
```
my_project/
├── index.js
├── app/
│ ├── demo1/
│ │ ├── config_demo.json
│ │ └── index.js
│ └── demo2/
│ ├── 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.load_list();
// 执行所有模块的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.load_list();
```
以下示例展示如何实现模块的热更新和动态管理:
```javascript
const engine = new Machine();
// 设置为热更新模式
engine.mode = 2;
// 初始化
async function setup() {
// 加载模块
await engine.load_list();
// 开始监听文件变化
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.load_list();
// 安全地执行模块方法
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`): 最高性能,文件改变不会触发重新加载
2. **热更新模式** (`mode = 2`): 文件改变时自动重新加载配置和代码
3. **重载模式** (`mode = 3`): 执行完后重新加载脚本,避免变量污染
4. **热更新+重载模式** (`mode = 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`)
3. 提交您的更改 (`git commit -m 'Add some amazing feature'`)
4. 推送到分支 (`git push origin feature/amazing-feature`)
5. 打开 Pull Request
- 基础模块管理功能
- 配置文件加载和解析
- 模块生命周期管理
- 热更新支持
- 基础错误处理
- 改进模块加载逻辑,支持自定义目录
- 增强错误处理和异常捕获
- 添加更多生命周期钩子
- 优化性能和内存使用
- mm_config: ^1.1.4
- mm_hot_reload: ^1.0.5
- mm_expand: 用于路径处理和文件操作
ISC
本模块由 mm_modules 团队开发和维护。如有问题或建议,请联系我们。