提到状态管理,你是不是一脑门子汗,React vue 纯js, angular preact……
不同的 UI库 有自己的状态管理生态,彼此之间还不能打通。切换 UI库的技术栈意味着你需要把背后的应用研发生态都切换一遍,于是前端就有了 React 工程师, Vue 工程师这样奇怪的细分。
我在之前的文章中吐槽过 React 糟糕的状态管理生态,本以为 Vue 只有个 Vuex,最近又看到了新伙伴,量大 UI库背后的生态圈对于重复制造状态管理的轮子可谓乐此不疲。
这导致很多企业内部都形成了技术栈孤岛,招人还得看看研发背景,不仅浪费时间,更浪费生命。我们公司恰恰就是受害者之一,历史悠久遗留下来的 backbone vue react,每次切换都是大动干戈。
为了不再受到这种不同技术栈之间的生态壁垒的胁迫,我们一直在寻求一个能够跨越 UI库,不仅能够支持,同时能让不同的 UI库 和谐并存的方案。
这个方案要满足
-
一套 api 支持不同的 UI库,包括 backbone vue react angular ……
-
在应用中能够集成不同 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
先不展开,再来看看用 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…
欢迎加入我们共同推动应用研发在业务层面的统一标准 😁