Rdeco 一个跨 UI 的状态管理方案,打破状态管理生态枷锁,解放前端生产力

·  阅读 3535

提到状态管理,你是不是一脑门子汗,React vue 纯js, angular preact……

不同的 UI库 有自己的状态管理生态,彼此之间还不能打通。切换 UI库的技术栈意味着你需要把背后的应用研发生态都切换一遍,于是前端就有了 React 工程师, Vue 工程师这样奇怪的细分。

我在之前的文章中吐槽过 React 糟糕的状态管理生态,本以为 Vue 只有个 Vuex,最近又看到了新伙伴,量大 UI库背后的生态圈对于重复制造状态管理的轮子可谓乐此不疲。

这导致很多企业内部都形成了技术栈孤岛,招人还得看看研发背景,不仅浪费时间,更浪费生命。我们公司恰恰就是受害者之一,历史悠久遗留下来的 backbone vue react,每次切换都是大动干戈。

为了不再受到这种不同技术栈之间的生态壁垒的胁迫,我们一直在寻求一个能够跨越 UI库,不仅能够支持,同时能让不同的 UI库 和谐并存的方案。

这个方案要满足

  1. 一套 api 支持不同的 UI库,包括 backbone vue react angular ……

  2. 在应用中能够集成不同 UI库 开发的页面和组件,并能够和谐并存

得益于我同事在编写服务中的灵感,我们率先想到了 rx.js, 这个在前端叫好不叫座的库。

不过没关系,经过一系列的实践和摸索,我们编写了基于 rx.js 的 rdeco,我相信这会是一个叫好又叫座的库。

接下来让我们通过代码来看看 rdeco 是如何打通和支持所有 UI库的。

由于我们的技术栈主要是 react 因此我们在开发 rdeco 的时候顺手编写了一个更容易集成 react 的包,在下面的示例中你可能会觉得 react 的例子会更加舒适,但这和 rdeco 并无关系。

用 rdeco 编写一个纯 js 组件

import { create, inject } from 'rdeco';

create({
  name: '@pure/js-com',
  state: {
    message: 'hello world',
  },
  service: {
    template() {
      return `<div>${this.state.message}</div>`;
    },
  },
  exports: {
    render([el]) {
      el.innerHTML = this.service.template();
    },
  },
});

inject('@pure/js-com').render(document.getElementById('pure'))
复制代码

这是个典型的基于纯 dom 操作的组件示例,代码运行后你会看到页面上渲染出一个 hello world

截屏2021-12-02 16.23.48.png

先不展开,再来看看用 rdeco 编写一个 react 组件有什么不同

用 rdeco 编写一个 React 组件

import React from 'react';
import ReactDOM from 'react-dom';
import { createComponent } from 'rdeco';

const Button = createComponent({
  name: '@react/button',
  state: {
    message: 'hello world',
  },
  view: {
    render() {
      return <div type="button">{message}</div>;
    },
  },
});

ReactDOM.render(Button, document.getElementById('react'));
复制代码

为了避免手动同步 React 组件的状态,我们编写了一个 createComponent 的 api 将 rdeco 声明的状态映射到了 react 中。

这个例子的结果和上面的例子是相同的。

然后是 vue,由于 vue 是双向绑定的,因此同步状态的事情就简单多了,我们没有编写额外的 api 来简化这件事,如果你对此感兴趣可以自己实现一个 rdeco-vue

用 rdeco 编写一个 Vue 组件

import { createApp } from 'vue';
import { create } from 'rdeco';

create({
  name: '@vue/counter',
  state: {
    counter: vm.counter,
  },
  ref: {
    vm: createApp({
      data() {
        return {
          counter: 0,
        };
      },
    }),
  },
  exports: {
    add() {
      vm.counter += 1;
      this.setter.counter(vm.counter);
    },
    render([el]) {
      createApp(this.ref.vm).mount(el);
    },
  },
});
复制代码

好了,以上的例子展现了 rdeco 作为状态管理的基本功能,状态的存储,下面展现的是 rdeco 如何实现状态管理的最核心能力,状态的传递

因为状态的传递如果能在不同的 UI 库之间实现, 那么在单个 UI库 中也是一样的。因此下面的示例展示的是,rdeco 如何为 纯js 组件, react 组件, vue 组件之间建立状态的传递和控制。

跨 UI 的状态传递

让我们编写一个简单需求来描述这个示例

我们通过 React 实现一个 Button,通过 Vue 实现一个 Counter,通过点击 ReactButton 来改变 VueCounter,最后把改变的结果传递给纯 js 组件来展示。

import React from 'react';
import { createComponent, create } from 'rdeco';
import { createApp } from 'vue';

createComponent({
  name: '@react/button',
  controller: {
    onClick() {
      inject('@vue/counter').add();
    },
  },
  view: {
    render() {
      return (
        <button type="button" onClick={this.controller.onClick}>
          Click add Counter
        </button>
      );
    },
  },
});

create({
  name: '@vue/counter',
  state: {
    counter: vm.counter,
  },
  ref: {
    vm: createApp({
      data() {
        return {
          counter: 0,
        };
      },
    }),
  },
  exports: {
    add() {
      vm.counter += 1;
      this.setter.counter(vm.counter);
    },
    render([el]) {
      createApp(this.ref.vm).mount(el);
    },
  },
});

create({
  name: '@puer/message',
  subscribe: {
    '@vue/counter': {
      state: {
        counter({ nextState }) {
          this.setter.message(nextState);
        },
      },
    },
  },
  state: {
    message: null,
  },
  service: {
    template() {
      return `<div>${this.state.message}</div>`;
    },
  },
  exports: {
    render([el]) {
      el.innerHTML = this.service.template();
    },
  },
});

复制代码

完整可运行的示例在这里: codesandbox.io/s/goofy-kap…

结尾惯例,如果你对我们的工作感兴趣,可以关注 rdeco: github.com/kinop112365…

欢迎加入我们共同推动应用研发在业务层面的统一标准 😁

分类:
前端
分类:
前端
收藏成功!
已添加到「」, 点击更改