在java中,SPI机制是Java中提供的一种服务发现机制。同样,前端也很需要这种机制,这样可以做到组件可插拔,可替换,减少相互冗余。
快速使用
1.扩展点使用
通过使用Extension组件定义扩展点,通过name标记扩展点。
function App() {
return (<div>
<Extension name="header"/>
<Extension name="content"/>
<Extension name="footer"/>
</div>);
}
2.扩展点定义
在约定的init文件中,配置自定义的扩展点。
import {extensionManager} from "./extension";
extensionManager.register("header", () => <div>header</div>);
extensionManager.register("content", () => <div>content</div>);
extensionManager.register("footer", () => <div>footer</div>);
export default {}
3.使用效果
实现原理
ExtensionManager
这个类是全局的插件管理器,通过内置的map维护扩展点,在扩展点注册或改变后,通过钩子函数重新刷新扩展点组件。
class ExtensionManager {
constructor() {
this.extensions = {};
this.extensionRefreshs = {};
}
/**
* 注册扩展点
*/
register(key, component) {
this.extensions[key] = component;
// 执行刷新函数
let refresh = this.extensionRefreshs[key];
if (refresh) {
refresh();
}
}
/**
* 获取扩展点
*/
getExtension(key) {
return this.extensions[key];
}
/**
* 添加刷新者
*/
addExtensionRefresh(key, extensionRefresh) {
this.extensionRefreshs[key] = extensionRefresh;
}
init(){
import("./init")
}
}
本来想通过react context 以及react state来存储扩展点的,但是react useContext这些hook需要在组件渲染方法内才能调用,这样注册组件功能无法实现,所以直接用js 全局变量实现了。
/**
* 全局扩展点管理器
*/
const extensionManager = new ExtensionManager();
extensionManager.init();
extensionManager是全局变量;init方法会加载init文件,init文件约定注册扩展点组件。
Extension
扩展点组件会获取extensionManager对应name的组件,并返回。同时设置刷新回调函数(基于react state实现),在扩展点变更时进行重新渲染。
/**
* 扩展点组件
*/
const Extension = ({name}) => {
// 获取扩展点组件
let Component = extensionManager.getExtension(name);
// 设置刷新回调
const [version, setVersion] = useState(0);
extensionManager.addExtensionRefresh(name, () => {
setVersion(v => v + 1);
});
console.log("Extension:" + name + ",version:" + version);
// 返回扩展点组件
return Component ? <Component/> : null;
}