concent

concent

  • Docs
  • API
  • ChangeLog
  • GitHub

›核心概念与功能

序言

  • concent是什么
  • 对比redux和mobx

新手指南

  • 快速开始
  • 渐进式的开发
  • 标准化的开发

核心概念与功能

  • 模块
  • 组件
  • reducer
  • 调试

生态与周边

  • react-router-concent
  • concent-plugin-loading

精彩示例

  • concent-antd-pro

模块的定义


模块是一个很重要的概念,一个应用程序随着不停的迭代一定会变得越来越复杂, 为了同时满足容易维护、方便扩展、适合多人协同开发这些特点,我们通常都会按照领域对象或者业务模型将我们庞大的应用拆分为各个模块,concent经过整合现有各个UI框架的最佳实践,为模块提炼出了5个核心的概念

  • state
  • reducer
  • init
  • computed
  • watch 模块 我们使用run函数启动concent时,载入的就是各个模块定义,然后通过提供register、connect、connectDumb函数让用户可以很方便的将ui与模块关联起来

模块是独立于组件存在的,用户根据自己的实际情况按需连接组件与模块

state

状态是react里非常重要的概念,它的变化驱动着视图的变化,同样的,状态也是concent模块里必不可少的配置,我们定义一个模块时,可以缺省reducer、computed、watch、init,唯独不能缺省state的定义

状态定义

在线示例

定义好foo模块的状态后,使用run载入foo模块,使用register将react类注册成为concent类并指定其属于foo模块

import React from 'react';
import ReactDOM from 'react-dom';
import { run, register } from 'concent';


run({
  foo: {//定义foo模块
    state: {//为foo模块定义state
      label:'hello concent'
    },
  },
});

class App extends React.Component{
  render(){
    const label = this.state.label;
    return (
      <div>
        {label}<br/>
        <input value={label} onChange={this.$$sync('label')} />
      </div>
    )
  }
}
const App_ = register('App', {module:'foo', watchedKeys:'*'})(App);

状态合成

上述示例中,我们并没有在constructor里声明过state,render函数里却直接从state里获取到label的值,是因为我们将App注册到foo模块了,concent在componentWillMount阶段自动将模块的state注入到实例上,实际上,你也可以显示的在constructor声明其他的状态,这并不妨碍的concent的正常运行,concent在实例首次render将指定模块的状态和用户自定义的状态合成到实例的state里。

在线示例

class App extends React.Component{
 constructor(props, context){
   super(props, context);
   this.state = { myLabel:'my private label' };
 }
 render(){
   const {label, myLabel} = this.state;
   return (
     <div>
       label:{label}<br/>
       myLabel:{myLabel}<br/>
       <input value={label} onChange={this.$$sync('label')} />
     </div>
   )
 }
}
const App_ = register('App', {module:'foo', watchedKeys:'*'})(App);
// or
// const App_ = register('App', 'foo')(App);

render里,我们取到了label、myLabel两个值,label来自于foo模块,myLabel来自于我们在狗朝气里的自定义值!
如果在我们在构造器里定义label值为'see what happen',render里依然取到的是'hello concent',concent总是会用模块里的状态覆盖掉用户的自定义状态,如果出现同名的key的话。

状态连接

上述示例中,我们定义App专属于foo模块,如果我们新增两个模块bar、baz,让App也能够读取到新增模块的值的话,定义connect属性,表示App还要连接其他的模块

在线示例

run({
  foo: {//定义foo模块
    state: {//为foo模块定义state
      label:'hello concent'
    },
  },
  bar:{//定义bar模块
    state:{
      barProp1: 'barProp1',
    }
  },
  baz:{//定义baz模块
    state:{
      bazProp1: 'bazProp1',
    }
  }
});

class App extends React.Component{
  render(){
    const label = this.state.label;
    const {bar, baz} = this.$$connectedState;
    return (
      <div>
        {label}<br/>
        {bar.barProp1}<br/>
        {baz.bazProp1}<br/>
        <input value={label} onChange={this.$$sync('label')} />
        <input value={bar.barProp1} onChange={this.$$sync('bar/barProp1')} />
      </div>
    )
  }
}
const App_ = register('App', {
  module:'foo', 
  watchedKeys:'*', 
  connect:{bar:'*', baz:'*'}, //连接bar模块和baz模块
})(App);

当然我们也可以注册的时候不指定concent类属于任何任何模块,直接使用connect定义来帮助用户获取store各个模块的数据

在线示例


class App extends React.Component{
  render(){
    const {foo, bar, baz} = this.$$connectedState;
    return (
      <div>
        {foo.label}<br/>
        {bar.barProp1}<br/>
        {baz.bazProp1}<br/>
        <input value={foo.label} onChange={this.$$sync('foo/label')} />
        <input value={bar.barProp1} onChange={this.$$sync('bar/barProp1')} />
      </div>
    )
  }
}
const App_ = register('App', {
  connect:{foo:'*', bar:'*', baz:'*'}, //连接foo模块,bar模块和baz模块
})(App);

此时所有的模块状态都从this.$$connectedState上获取了,如果一个类不属于任何模块,同时也要连接多个模块,可以使用connect方法代替register方法

import { connect } from 'concent';
const App_ = connect('App', {foo:'*', bar:'*', baz:'*'} )(App);

状态修改

对比react类,concent类扩展了很多特性,如上述示例中,你可能留意到了this.$$sync的写法,但是如果你不需要用到它们,或者在你没有了解它们之前,你依然可以按照最古典的react写法类书写你的代码,唯一不同的只是,状态被提升到了store的某个模块下

在线示例

import React from 'react';
import { run, register } from 'concent';

run({
  foo: {//定义foo模块
    state: {//为foo模块定义state
      label:'hello concent'
    },
  },
});

class App extends React.Component{
  changeLabel = (e)=>{
    this.setState({label: e.currentTarget.value});
  }
  render(){
    const label = this.state.label;
    return (
      <div>
        {label}<br/>
        <input value={label} onChange={this.changeLabel} />
      </div>
    )
  }
}
const App_ = register('App', {module:'foo', watchedKeys:'*'})(App);

状态变更观察

上述演示的古典写法里,除了修改foo模块的label值,似乎并没有看出其他不同之处,我们仔细看register函数里,传入了一个watchedKeys,正如其名字描述的一样,表示当前concent类的实例将观察foo模块的所有key的值变化,只要任意一个实例修改了foo下的任意key的值,那么其他实例都将获得最新的修改值并被触发渲染。

在线示例

class Foo extends React.Component{
  changeLabel = (e)=>{
    this.setState({label: e.currentTarget.value});
  }
  render(){
    const label = this.state.label;
    return (
      <div>
        {label}<br/>
        <input value={label} onChange={this.changeLabel} />
      </div>
    )
  }
}
const Foo_ = register('Foo', 'foo')(Foo);
// or
// const Foo_ = register('Foo', {module:'foo', watchedKeys:'*'})(Foo);

const App = ()=>{
  return (
    <div>
      <Foo />
      <Foo />
      <Foo />
    </div>
  );
}

以上三个Foo实例,任意一个修改label值,都会同步到其他两个实例上

reducer

reducer函数用于书写用户的业务逻辑并返回新的片段状态,提高代码的复用性,并将视图渲染和业务逻辑彻底隔离,更多关于reducer的定义请跳转此处查看

watch

watch允许用户观察某些key的变化书写一些异步逻辑

典型的场景1,导航组件切换标签页,对应的路由组件需要刷新的逻辑,导航组件负责跟上tabId,其他组件负责观察tabId变化,并刷新对应tabId的数据

典型的场景2, 类组件里,一个单选按钮组切换,类里定义watch观察并更新,注:这里指的是实例级别的watch函数,不同上面的模块级别的watch函数,但它们本质都一样:某些场景里,需要让数据的变化和业务逻辑解耦,就是你需要使用watch的时候

class Foo extends Component{
  $$watch() {
    return {
      /** 观察到_duration发生变化,从后端获取最新数据 */
      _duration: () => {
        this.fetchStatData(this.state._account);
      }
    }
  }
  render(){
    return (
      <Group value={_duration} onChange={this.$$sync('_duration')}>
        <Button value="day">日</Button>
        <Button value="week">周</Button>
        <Button value="month">月</Button>
      </Group>
    );
  }
}

computed

对key定义computed后,concent调用这些key的computed函数结算出结果并将结果缓存起来,只有当key再次发生变化时,才会触发重计算


← 标准化的开发组件 →
  • state
    • 状态定义
    • 状态合成
    • 状态连接
    • 状态修改
    • 状态变更观察
  • reducer
  • watch
  • computed
Copyright © 2019 concentjs.org