🚀🚀🚀怎么像Spring Boot一样写React组件?——inversify-react实践

文章同步在公众号:萌萌哒草头将军,欢迎关注!也可以戳这个沸点

这篇文章,我已经计划了很久,上篇文章中简单的使用了依赖注入模式,但是这仅仅是牛刀小试。

💎 控制反转和依赖注入

因为和依赖注入(DI:dependency injection)密切还有控制反转(Ioc:inversion of control)。这方面最优秀的实践,莫过于spring boot了。

不用担心,下面我们不会深入聊后端,这里仅仅是引出我们的概念。

spring boot中,内置了容器存放注入的组件(spring里称为Bean),并且管理这些组件,在需要使用组件的地方向组件申请使用。

上面这句话,需要你仔细审读。

注入组件的过程就是我们熟悉的依赖注入。

容器管理组件(生命周期)就是控制反转,因为组件的创建和控制权从程序员本身转换到容器了。

如果你还是不理解,我们在举个简单的例子:

很久以前你还是个普通人,柴米油盐,都是你自己操持,但是某一天,你时来运转,发了大财,平地一声雷,陡然而富。你雇佣了两个管家,七八个佣人,从此你只管风花雪月,不用操持柴米油盐了。

你雇佣佣人和管家的过程就是依赖注入,管家和佣人就是容器。而这个结果就是控制反转。

下面这张图也是同样的道理。

好了,我们可以下个定义了,

依赖注入:是一种设计模式,主要用于管理软件组件之间的依赖关系。

控制反转:是一种设计原则,主要将控制权从组件自己转移到外部的容器或框架。

控制反转最大的特点是,我们再也不用使用new关键字创建对象了,因为这部分工作交给容器了

这时候,你肯定会问:react是不是也内置了容器,比如Context?,看起来是,但事实并非如此,因为这个"容器"没有管理组件的能力。

React的Context是React提供的一种在组件之间共享数据的机制。Context使得数据可以在组件树中的多个层级中传递,避免了通过props一层层传递的麻烦。

💎 注解的原理

spring boot还有个令人羡慕的功能,那就是注解了(可能很少有人跟我一样有这种"癖好"吧)。注解功能是依赖注入的一部分,

而注解的本质就是个符合装饰器模式的函数。

例如:我们有个简单的函数,弹出警告信息,

js 复制代码
function alertMsg(message: string) {
  return alert(message);
}

接下来的需求是每次弹出信息的时候记录在日志里。下面是装饰器模式的实现

js 复制代码
function logAlert(fn: Function) {
  return function (...args: any[]) {
    console.log(`Calling alertMsg with message: ${args[0]}`);
    const result = fn.apply(this, args);
    return result;
  };
}

如果使用注解的方式,那么我们需要修下参数。

js 复制代码
// target: 被装饰的类的原型对象
// key: 被装饰的方法名(在这里是 "alertMsg")
// descriptor: 被装饰的方法的属性描述符
function logAlert(
  target: any,
  key: string,
  descriptor: PropertyDescriptor
) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling alertMsg with message: ${args[0]}`);
    const result = originalMethod.apply(this, args);
    return result;
  };

  return descriptor;
}

class Utils {
  @logAlert
  alertMsg(message: string) {
    return alert(message);
  }
}

const instance = new MyClass();
instance.alertMsg('Hello, world!');

装饰器在 JavaScript/TypeScript 中仍处于实验阶段,并不是官方的标准。在某些情况下,装饰器的使用可能需要进行额外的配置或使用特定的工具链来支持。因此,在使用装饰器时,请确保您的开发环境和工具链支持装饰器的语法和功能。

💎 环境准备

好了,所有的铺垫都说完了,我们接下来怎么用控制反转的思想写React组件。

我们主要使用的库是:inversify、inversify-react。后者是对前者的react支持。

inversify是很有名的javascript控制反转库。它提供了容器去管理组件。

如果你十分感兴趣,可以看看这个官方的说明,你会明白管理组件的过程。

github.com/inversify/I...

好,我们首先下载依赖:

cmd 复制代码
npm install inversify reflect-metadata inversify-react --save

接着配置下tsconfig.json,开启experimentalDecorators和emitDecoratorMetadata选项。

json 复制代码
{
    "compilerOptions": {
        "target": "es5",
        "lib": ["es6"],
        "types": ["reflect-metadata"],
        "module": "commonjs",
        "moduleResolution": "node",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true
    }
}

此时,我们需要下载对应的babel插件

cmd 复制代码
npm install --save-dev @babel/plugin-proposal-decorators @babel/plugin-proposal-class-properties

如果你是vite项目,需要额外配置下vite,config.ts

js 复制代码
import { default as reactSupport } from "@vitejs/plugin-react";
import { defineConfig } from "vite";

// https://vitejs.dev/config/
export default defineConfig({
  optimizeDeps: {
    include: ["@babel/plugin-proposal-decorators"],
  },
  plugins: [
    reactSupport({
      babel: {
        parserOpts: {
          plugins: ["decorators-legacy", "classProperties"],
        },
      },
    })
  ],
});

💎 真正的实践

🚀 首先,声明一个组件

js 复制代码
import { injectable } from "inversify";

@injectable()
export class Utils {
  log() {
    console.log("data");
  }
}

🚀 接着,创建一个容器

js 复制代码
import { Container } from "inversify";
import { Utils } from "../utils";

// 创建 IoC 容器
const container = new Container();
container.bind<Utils>(Utils).toSelf();
export default container;

🚀 然后,提供容器

我们在入口文件一定要引入:import "reflect-metadata",这是inversify的主要依赖。

js 复制代码
import { Provider } from "inversify-react";
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import "reflect-metadata";
import App from "./App.tsx";
import container from "./Ioc/index.ts";

ReactDOM.render(
  <StrictMode>
    <Provider container={container}>
      <App />
    </Provider>
  </StrictMode>,
  document.getElementById("root")
);

🚀 最后,使用组件

使用组件你可以i根据不同类型选择不同的方式。如果使用hook组件。

js 复制代码
import { useInjection } from "inversify-react";
import { useEffect } from "react";
import { Utils } from "./utils";
function App() {
  const utils = useInjection(Utils);
  useEffect(() => {
    utils.log();
  }, []);
  return <>hello</>;
}
export default App;

如果,你使用类组件,那么可以更完美的体验注解的魅力。

js 复制代码
import { resolve } from "inversify-react";
import React from "react";
import { Utils } from "./utils";
class App extends React.Component {
  @resolve
  utils!: Utils;
  componentDidMount() {
    this.utils.log();
  }
  render(): React.ReactNode {
    return <div>hello</div>;
  }
}
export default App;

已经初具成效了,下篇文章我们一起搭建一个"逼真"的spring boot框架吧。

好了,今天的分享就这些了,希望可以帮到你。十分感谢您的阅读。

相关推荐
熊的猫1 分钟前
DOM 规范 — MutationObserver 接口
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
天农学子2 分钟前
Easyui ComboBox 数据加载完成之后过滤数据
前端·javascript·easyui
mez_Blog2 分钟前
Vue之插槽(slot)
前端·javascript·vue.js·前端框架·插槽
爱睡D小猪5 分钟前
vue文本高亮处理
前端·javascript·vue.js
paopaokaka_luck6 分钟前
基于Spring Boot+Vue的多媒体素材管理系统的设计与实现
java·数据库·vue.js·spring boot·后端·算法
开心工作室_kaic8 分钟前
ssm102“魅力”繁峙宣传网站的设计与实现+vue(论文+源码)_kaic
前端·javascript·vue.js
放逐者-保持本心,方可放逐9 分钟前
vue3 中那些常用 靠copy 的内置函数
前端·javascript·vue.js·前端框架
IT古董9 分钟前
【前端】vue 如何完全销毁一个组件
前端·javascript·vue.js
Henry_Wu00111 分钟前
从swagger直接转 vue的api
前端·javascript·vue.js
奋飞安全12 分钟前
初试js反混淆
开发语言·javascript·ecmascript