concent
一个可预测、0入侵、渐进式、高性能的增强型状态管理方案,基于依赖标记、引用收集和状态分发原理,power you react!
✨ 特性
- 极简的核心api,
run
载入模块配置启动concent,register
注册组件,无需包一层Provider
在根组件。 - 0入侵成本接入,不改造代码的情况下直接接入;hello-concent
- 贴心的模块配置,除了
state
,还提供reducer
、computed
、watch
和init
四项可选定义。 - 灵活的数据消费粒度,支持跨多个模块场景,以及模块内stateKey级别的细粒度控制。
- 渐进式构建react应用,除了
setState
,支持dispatch
、invoke
调用来让ui视图与业务逻辑彻底解耦。从class到function - 组件能力增强,支持实例级别
computed
、watch
定义,支持emit&on
,以及支持setup
特性,让函数组件拥有定义静态api的能力。 - 高度一致的编程体验,
hoc
、render props
和hook
3种方式定义的组件均享有一致的api调用体验,相互切换代价为0。多种方式定义组件 - 渲染性能出众,内置
renderKey
、lazyDispatch
、delayBroadcast
等特性,保证极速的渲染效率。长列表精准渲染、批处理状态提交、高频输入场景状态延迟分发 - 干净的dom层级,对于class组件,默认采用反向继承策略,让react dom树的层级结构保持简洁与干净。
- 扩展中间件与插件,允许用户定义中间件拦截所有的数据变更提交记录,做额外处理,也可以自定义插件,接收运行时的发出的各种信号,按需增强concent自身的能力。
- 去中心化配置模块,除了
run
接口一次性配置模块,还提供configure
接口在任意地方动态配置模块。 - 模块克隆,支持对已定义模块进行克隆,满足你高维度抽象的需要。
搭配react-router使用
请移步阅读和了解react-router-concent,暴露history
对象,可以全局任意地方使用,享受编程式的导航跳转。
搭配redux-dev-tool使用
请移步阅读和了解concent-plugin-redux-devtool,全流程追溯你的状态变更过程。
搭配loading插件使用
请移步阅读和了解concent-plugin-loading,轻松控制concent应用里所有reducer函数的loading状态。
📦 快速开始
确保你本地机器上安装有nodejs。
创建一个app
在你的电脑上,选择一个合适的目录并进入,使用create-react-app 创建一个app
$ npm i -g create-react-app
$ create-react-app cc-app
安装cc
创建好app后,进入你的app根目录,使用npm安装concent
$ cd cc-app
$ npm i --save concent
或者使用yarn安装
$ yarn add concent
新手counter示例
将以下代码复制粘贴到cc-app
目录下的src/App.js
文件里(注:是完全覆盖掉原来的内容)。
- 运行concent,载入模块配置
import React, { Component, Fragment } from 'react';
import { register, run } from 'concent';
run({
counter: {//定义counter模块
state: {//定义state
count: 0,
},
reducer: {
inc(payload, moduleState) {
return { count: moduleState.count + 1 };
},
dec(payload, moduleState) {
return { count: moduleState.count - 1 };
}
}
}
})
- 基于react class注册成为cc类组件
class Counter extends Component {
//setState 能够将数据将同步到store,广播到其他实例
inc = () => {
this.setState({ count: this.state.count + 1 });
}
dec = () => {
this.setState({ count: this.state.count - 1 });
}
//调用dispatch, 同样的能够将数据将同步到store,广播到其他属于counter模块或者连接到counter模块的实例
incD = () => {
this.ctx.dispatch('inc');
}
decD = () => {
this.ctx.dispatch('dec');
}
render() {
//concent注入counter模块的数据到state
const { count } = this.state;
return (
<div style={{ padding: '12px', margin: '6px' }}>
<div>count: {count}</div>
<button onClick={this.inc}>inc by setState</button>
<button onClick={this.dec}>dec by setState</button>
<br />
<button onClick={this.incD}>inc by dispatch</button>
<button onClick={this.decD}>dec by dispatch</button>
</div>
);
}
}
//将Counter类注册为CcClazzCounter,属于counter模块
const CcClazzCounter = register('counter')(Counter);
- 基于renderProps注册为cc类组件
import { registerDumb } from 'concent';
const UI = ({count, inc, dec, incD, decD})=>{
return (
<div style={{ padding: '12px', margin: '6px' }}>
<div>count: {count}</div>
<button onClick={inc}>inc by setState</button>
<button onClick={dec}>dec by setState</button>
<br />
<button onClick={incD}>inc by dispatch</button>
<button onClick={decD}>dec by dispatch</button>
</div>
);
}
//定义setup,该函数只会在ui初次渲染前执行一次,通常用于定义一些方法,结果会收集到ctx.settings里
const setup = ctx=>{
const inc = () => {
ctx.setState({ count: ctx.state.count + 1 });
};
const dec = () => {
ctx.setState({ count: ctx.state.count - 1 });
};
const incD = () => {
ctx.dispatch('inc');
};
const decD = () => {
ctx.dispatch('dec');
};
return {inc, dec, incD, decD};
}
//定义mapProps,该函数在ui每次渲染前被执行,结果将映射到组件的props上
const mapProps = ctx=>{
return {count:ctx.state.count, ...ctx.settings};
}
//将Counter类注册为CcFnCounter,属于counter模块
const CcFnCounter = registerDumb({module:'counter', setup, mapProps})(UI);
- 基于hook注册为组件
import { useConcent } from 'concent';
function HookCounter(){
const {state, setState, dispatch} = useConcent('counter');
const inc = () => {
setState({ count: ctx.state.count + 1 });
};
const dec = () => {
setState({ count: ctx.state.count - 1 });
};
const incD = () => {
dispatch('inc');
};
const decD = () => {
dispatch('dec');
};
return (
<div style={{ padding: '12px', margin: '6px' }}>
<div>count: {count}</div>
<button onClick={inc}>inc by setState</button>
<button onClick={dec}>dec by setState</button>
<br />
<button onClick={incD}>inc by dispatch</button>
<button onClick={decD}>dec by dispatch</button>
</div>
);
}
- 更优的hook写法,将函数提升为静态api
import { useConcent } from 'concent';
//同样的,该函数只在ui首次渲染前被执行一次!!!
const setup = ctx =>{
const {state, setState, dispatch} = ctx;
const inc = () => {
setState({ count: ctx.state.count + 1 });
};
const dec = () => {
setState({ count: ctx.state.count - 1 });
};
const incD = () => {
dispatch('inc');
};
const decD = () => {
dispatch('dec');
};
return {inc, dec, incD, decD};
}
function HookCounter(){
const {settings, state} = useConcent({module:'counter', setup});
const {inc, dec, incD, decD} = settings;
return (
<div style={{ padding: '12px', margin: '6px' }}>
<div>count: {state.count}</div>
<button onClick={inc}>inc by setState</button>
<button onClick={dec}>dec by setState</button>
<br />
<button onClick={incD}>inc by dispatch</button>
<button onClick={decD}>dec by dispatch</button>
</div>
);
}