
Security News
Software Engineering Daily Podcast: Feross on AI, Open Source, and Supply Chain Risk
Socket CEO Feross Aboukhadijeh joins Software Engineering Daily to discuss modern software supply chain attacks and rising AI-driven security risks.
MTOR是一个基于React的响应式数据流状态管理库,它基于原生React Hooks进行了二次封装,提供了更加简洁、高效的状态管理方案。
MTOR具有以下四大特点:
| 问题 | React Hooks | MTOR |
|---|---|---|
| 属性保存与修改 | 一个useState只能管理一个属性 | 使用setData方法可以同时对多个属性进行修改 |
| 可读性 | 随着组件规模增大,展示与业务逻辑混在一起,可读性降低 | UI展示与业务逻辑分离,结构更清晰,可读性更好 |
| 复用性 | 组件里面的业务逻辑不可复用 | 整个模块都是可复用的 |
| 数据共享 | 必须使用useContext或属性传值,增加大量非业务代码 | 使用依赖注入,多模块轻松实现共享/内部通信 |
| 开发体验 | 热更新时所有state丢失,useMemo、useCallback等依赖项容易出错 | 热更新数据保留,不用关心依赖项更新问题 |
# 使用npm安装
npm install --save mtor
# 或使用yarn安装
yarn add mtor
以下是一个简单的示例,实现从后端获取随机数并在页面中展示,点击按钮可以给随机数加1:
// HomeModel.js
import { service, Model } from 'mtor';
function ajax() { // 模拟ajax请求
return new Promise((resolve) => {
setTimeout(() => {
resolve(parseInt(Math.random() * 10, 10));
}, 16.7);
});
}
@service('home') // 或使用 @service(module.id) 或 @define(module)
class HomeModel extends Model {
num = 0;
async init() {
this.num = await ajax();
}
add() { // 普通方法
this.num++;
}
}
export default HomeModel;
// HomePage.jsx
import React, { useEffect } from 'react';
import { useModel } from 'mtor';
import HomeModel from './HomeModel';
export default () => {
const model = useModel(HomeModel);
const { num } = model;
useEffect(() => {
model.init();
}, []);
return (
<div>
<div onClick={model.add}>+1</div>
<div>{num}</div>
</div>
);
};
// HomePage.jsx
import React from 'react';
import { useInitModel } from 'mtor';
import HomeModel from './HomeModel';
export default () => {
const model = useInitModel(HomeModel, (m) => m.init(), true);
const { num } = model;
return (
<div>
<div onClick={model.add}>+1</div>
<div>{num}</div>
</div>
);
};
模块是MTOR的核心概念,通过@service装饰器定义:
@service('moduleName') // 或使用 @service(module.id) 或 @define(module)
class MyModel extends Model {
// 属性定义
count = 0;
// 方法定义
increment() {
this.count++;
}
async fetchData() {
// 异步操作
}
}
MTOR支持模块间的依赖注入,使用@inject装饰器:
import UserModel from './UserModel';
@service(module.id)
class HomeModel extends Model {
// 注入UserModel实例
@inject(UserModel)
user;
async init() {
// 可以直接使用注入的实例
console.log(this.user.name);
}
}
也可以使用@resource按名称注入:
@service(module.id)
class HomeModel extends Model {
// 按名称注入
@resource('usermodel')
user;
}
在模块方法中可以直接修改属性:
@service(module.id)
class CounterModel extends Model {
count = 0;
increment() {
this.count++; // 直接修改属性
}
}
可以使用setData方法同时修改多个属性:
@service(module.id)
class UserModel extends Model {
name = '';
age = 0;
updateUser(name, age) {
this.setData({
name,
age
});
}
}
在组件中也可以直接调用setData:
const model = useModel(UserModel);
// 在事件处理函数中
const handleClick = () => {
model.setData({ name: 'John', age: 30 });
};
MTOR对异步操作做了特别优化,可以在异步方法中直接修改属性:
@service(module.id)
class DataModel extends Model {
data = null;
loading = false;
error = null;
async fetchData() {
try {
this.loading = true; // 修改会立即反映到UI
this.data = await api.getData();
} catch (err) {
this.error = err.message;
} finally {
this.loading = false;
}
}
}
使用reset方法可以将模块状态重置为初始值:
useEffect(() => {
model.init();
return model.reset; // 组件卸载时重置模块
}, []);
@service(name: string):定义一个模块@inject(ModelClass):按类型注入依赖@resource(name: string):按名称注入依赖基础模型类,提供以下方法:
setData(data: Object):批量设置属性reset():重置模块状态onBeforeReset(callback: Function):注册重置前回调useModel(ModelClass):获取模块实例useInitModel(ModelClass, initFn?, clean?):获取模块实例并处理初始化和清理define(module):基于webpack模块定义模块getModels():获取所有模型实例evtBus:事件总线实例推荐的目录结构:
src/
├── models/
│ ├── HomeModel.js
│ └── UserModel.js
├── pages/
│ ├── Home/
│ │ ├── index.js
│ │ └── style.less
│ └── User/
│ ├── components/
│ │ └── UserCard/
│ │ ├── index.js
│ │ └── style.less
│ ├── index.js
│ └── style.less
└── index.js
@service(module.id)确保模块名称全局唯一Model基类module.hot?.accept()useInitModel代替useModel+useEffect组合修改对象类型属性时,需要更新引用:
// 错误方式
updateObj() {
this.obj.a = 2; // 不会触发更新
}
// 正确方式
updateObj() {
this.obj = { ...this.obj, a: 2 };
}
在模型文件末尾添加:
// webpack
module.hot?.accept();
// vite
import.meta.hot?.accept();
对于可控组件,推荐使用setData方法:
<Input
value={name}
onChange={({target: {value}}) => model.setData({name: value})}
/>
可能的原因:
避免模块间形成循环依赖,可以使用事件总线或共享服务模式解决。
主要变化:
async/await替代generator方法this修改数据迁移步骤:
generator方法改为async/await// UserModel.ts
import { service, Model } from 'mtor';
@service('usermodel')
class UserModel extends Model {
name = 'Guest';
isLoggedIn = false;
login(username: string) {
this.name = username;
this.isLoggedIn = true;
}
logout() {
this.name = 'Guest';
this.isLoggedIn = false;
}
}
export default UserModel;
// HomeModel.ts
import { service, Model, inject } from 'mtor';
import UserModel from './UserModel';
@service(module.id)
class HomeModel extends Model {
count = 0;
messages = [];
@inject(UserModel)
user;
async init() {
this.messages = await this.fetchMessages();
}
increment() {
this.count++;
}
async fetchMessages() {
// 模拟API调用
return new Promise(resolve => {
setTimeout(() => {
resolve(['Message 1', 'Message 2', 'Message 3']);
}, 1000);
});
}
}
export default HomeModel;
// HomePage.tsx
import React from 'react';
import { useInitModel } from 'mtor';
import HomeModel from '../models/HomeModel';
const HomePage: React.FC = () => {
const model = useInitModel(HomeModel, m => m.init(), true);
const { count, messages, user } = model;
return (
<div>
<h1>Welcome, {user.name}</h1>
<div>
<h2>Counter: {count}</h2>
<button onClick={() => model.increment()}>Increment</button>
</div>
<div>
<h2>Messages</h2>
<ul>
{messages.map((msg, index) => (
<li key={index}>{msg}</li>
))}
</ul>
</div>
{user.isLoggedIn ? (
<button onClick={() => user.logout()}>Logout</button>
) : (
<button onClick={() => user.login('John')}>Login</button>
)}
</div>
);
};
export default HomePage;
更多信息和示例,请访问GitHub仓库。
FAQs
react 模块化 依赖注入 数据流处理
We found that mtor demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Socket CEO Feross Aboukhadijeh joins Software Engineering Daily to discuss modern software supply chain attacks and rising AI-driven security risks.

Security News
GitHub has revoked npm classic tokens for publishing; maintainers must migrate, but OpenJS warns OIDC trusted publishing still has risky gaps for critical projects.

Security News
Rust’s crates.io team is advancing an RFC to add a Security tab that surfaces RustSec vulnerability and unsoundness advisories directly on crate pages.