视图与业务,好一对冤家业务型modelmodel是需要精心的设计和合理的划分的,这是我们之前开发大型的redux+react单页面应用,大家都认同的真理,同样的,在react-control-center+react的开发里也适用这条黄金规则,通常,我们在接到需求,定制开发计划的时候,会抽象出很多业务相关的关键词,这些关键词慢慢经过进一步整理,将成为我们划分功能或者模块的有效依据,这些模块最终在前端这里会沉淀为model,每一个model定义了自己的state、reducer,当然如果有需要,还可以为其定义computed、init,通过精心的目录组织和规范的约定,视图的渲染逻辑和我们书写的业务逻辑被有效的解耦到component里和reducer里,这样当我们需要重构UI组件,可以放心的对其重构或者新增一个组件,复用相同的state和reducer 参考cc-antd-pro的划分|________layouts| |________BasicLayout.js| |________BasicLayout.less| |________BlankLayout.js| |________PageHeaderLayout.js| |________PageHeaderLayout.less| |________UserLayout.js| |________UserLayout.less|________models| |________activities.js| |________chart.js| |________form.js| |________global.js| |________index.js| |________list.js| |________login.js| |________monitor.js| |________profile.js| |________project.js| |________register.js| |________rule.js| |________user.js视图型model有一些状态,我们开发的过程中,发现和视图紧密相关,不同的组件在不同的生命周期阶段,都需要使用他们或者感知到他们的变化,例如右上角用户勾选的主题色,影响左下角一个抽屉的弹出策略或效果,这些状态同样需要交个状态管理框架集中管理起来,所以我们也会这些需求设计相应的model,这一类和主要业务逻辑不想管,但是我们依然需要精心管理起来的model,我们称之为视图型model.视图代码膨胀之困惑通常,我们已开始精心设计好各种model后,开始信心满满的进入开发流程,随着功能迭代越来越块,需求变动越来频繁,我们的model会不停的调整或者扩展,按照class组件和function组件比例2:8开的原则,我们总是想抽出更多的function组件,class组件负责和和model打通,然后从model里拿到的数据层层派发它的所以孩子function组件里,但是function组件通常都不是只负责展示,还是有不少function组件需要修改model的state,所以我们在ant-design-pro里或者别的地方,依然会看到不少类似代码@connect(state => ({ register: state.register,}))class Foo extends Component { render(){ return ( <MyStatelessFoo {…this.props}/> ); }}const MyStatelessFoo = ({dispatch}){ return <div onClick={dispatch(‘foo/changeSomething’)}>whaterver</div>}如果有function组件Foo1、Foo2、Foo3,Foo1嵌套了Foo2,Foo2嵌套了Foo3,看起来要一层一层传递下去了。 同时视图组件调整的时间占比会远大于reducer函数的书写,我们有时候为了那个某个model的state,不停的传递下去或者慢慢的将某些比较重的function组件又提升为class组件react hooks解决了什么呢?这里复制一段facebook引出hooks要解决的问题所在之处难以重用和共享组件中的与状态相关的逻辑逻辑复杂的组件难以开发与维护,当我们的组件需要处理多个互不相关的 local state * 时,每个生命周期函数中可能会包含着各种互不相关的逻辑在里面。类组件中的this增加学习成本,类组件在基于现有工具的优化上存在些许问题。由于业务变动,函数组件不得不改为类组件等等。可是如果我们的function组件如果是需要共享或者修改model的state呢,有什么更优雅的办法解决吗?CcFragment为你带来全新的无状态组件书写体验一个典型的CcFragment使用方式如下import {CcFragment} from ‘react-control-center’;//在你的普通的react组件或者cc组件里,都可以写如下代码 render() { <div> <span>another jsx content</span> <hr/> <CcFragment ccKey=“toKnowWhichFragmentChangeStore” connect={{ ‘foo/’: ‘’, ‘bar/a’: ‘a’, ‘bar/b’: ‘alias_b’ }}> { ({ propState, setState, dispatch, emit, effect, xeffect, lazyEffect, lazyXeffect }) => ( <div onClick={() => setState(‘foo’, { name: ‘cool, I can change foo module name’ })}> {/ 以上方法,你可以像在cc类组件一样的使用它们,没有区别 */} {propState.foo.name} {propState.bar.a} {propState.bar.alias_b} </div> ) } </CcFragment> </div> }上面代码里,CcFragment标记一个ccKey,connectcc默认是会为所有CcFragment自动生成ccKey的,但是我们推荐你书写一个有意义的ccKey,因为CcFragment允许无状态组件直接使用setState, dispatch, emit, effect, xeffect, lazyEffect, lazyXeffect方法去修改状态或者发起通知,这些函数的使用体验是和cc class一摸一样,加上ccKey,你可以在你的中间件函数里看到某一次的状态变化是由哪一个ccKey触发的,这样未来你可以在还在计划开发中的cc-dev-tool里查看具体的状态变迁历史,当然目前你需要查看状态变化的话,可以写一个简答的中间件函数来logfunction myMiddleware(params, next) { //params 里你可以看到本次状态变化提交的状态是什么,由什么方法触发,由那个ccKey的引用触发等 console.log(‘myMiddleware’, params); next();}cc.startup({ //… middlewares: [myMiddleware]});connect和cc.register、cc.connect一样,表示该CcFragment关注那些模块,哪些值的变化,上述示例的效果会是1 只要bar模块的a或者b变化了,都会触发该CcFragment的渲染 2 只要foo模块的任意key变化了,都会触发该CcFragment的渲染 3 点击了div,会去修改foo模块的name值,关注foo模块name值变化的所有cc组件或者CcFragment组件都会触发渲染所以CcFragment解决了用户在无状态组件里共享了model数据的问题,你写的无状态组件很容易和cc store打通,而无需在考虑抽取为cc class组件,CcFragment本质上和hooks不存在冲突管理,也和现有cc class不冲突,只是作为cc世界里更重要的补充,让你可以无损的使用现有的function组件。 注意一点哦,CcFragment本身是不会因为父组件的更新而被更新的哦,仅仅受控制于connect参数观察的参数是否发生变化,所以它的渲染依然是高效的。那么可爱的各位看官,还不赶紧使用起来在线示例点我cc版本ant-design-pro基础入门项目runjs录像教程