🚀🚀🚀怎么像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框架吧。

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

相关推荐
wakangda20 分钟前
React Native 集成原生Android功能
javascript·react native·react.js
吃杠碰小鸡23 分钟前
lodash常用函数
前端·javascript
emoji11111133 分钟前
前端对页面数据进行缓存
开发语言·前端·javascript
一个处女座的程序猿O(∩_∩)O1 小时前
vue3 如何使用 mounted
前端·javascript·vue.js
迷糊的『迷』1 小时前
vue-axios+springboot实现文件流下载
vue.js·spring boot
User_undefined1 小时前
uniapp Native.js原生arr插件服务发送广播到uniapp页面中
android·javascript·uni-app
web135085886351 小时前
uniapp小程序使用webview 嵌套 vue 项目
vue.js·小程序·uni-app
麦兜*1 小时前
轮播图带详情插件、uniApp插件
前端·javascript·uni-app·vue
陈大爷(有低保)1 小时前
uniapp小案例---趣味打字坤
前端·javascript·vue.js
博客zhu虎康1 小时前
ElementUI 的 form 表单校验
前端·javascript·elementui