智能组件 VS 木偶组件javascript
在 React + Redux 结合做为前端框架的时候,提出了一个将组件分为“智能”和“木偶”两种css
所以,数据在page层获取、数据的操做和处理在page层定义,组件就是傻傻的知道执行和显示就好了,各操各的心。这种设计也整好符合目前不管是Vue、augular仍是React提倡的基于数据驱动的设计理念——程序定义好Model和View的关系,剩下的业余处理只须要关心数据变化,View的变化由框架自动执行,无需像jquery时代再去手动操做DOM。html
数据管理前端
下面看一下数据在系统中是如何传递的。这里的数据分为两种:java
针对这两种不一样的数据,固然要分开处理。针对第一种“系统数据”,系统一初始化就当即获取,而后交给Redux
作管理,这也符合Redux的特色。而针对第二种“业务数据”,那就何时用,就何时获取。react
代码架构(物理)jquery
使用React + Redux
设计代码结构,目前方式都比较统一,基本都是按照如下方式来设计,这些在github等社区都能找到类似的结构。git
app // 业务代码目录,或者叫 src --actions // 定义 Redux 的各个 action --components // 定义项目中的各个组件,里面可能有不少个子文件夹 --config // 项目配置,无具体规定,自由发挥 --constants // 定义 Redux 中用到的各个常量 --container // 定义项目中的全部的页面 --fetch // 定义项目中全部数据获取、提交的方法 --reducers // 定义 Redux 的 reducer 规则 --router // 定义项目中的 router 规则 --store // 定义 Redux 的全局 store 对象 --util // 工具函数,例如时间格式的处理等 `--index.jsx // 入口,被 ../index.html 引用 resource // 静态资源目录,或者叫 static mocha // 测试用例,通常叫 test ,可是使用 fis3 时候 test 文件夹有其余用处,不得不换个名字 .eslintignore .eslintrc.json .gitignore fis-conf.js index.html package.json README.md
以上目录中,app/components
和app/container
是开发中修改最多的目录。app/container
里面定义的都是页面,即智能组件,只关心数据,功能比较单一,所以结构也比较简单github
app/container list index.jsx Home.jsx Baijia.jsx detail detail.jsx imgDetail.jsx
可是针对组件app/components
来讲,要显示样式,固然就须要css和图片,文件类型比较多。所以,要按照以下方式定义组件web
app/components LoadMore img icon.png index.jsx style.less BannerAd img close.png index.jsx style.less
即,用一个文件夹来表示单个组件,里面的index.jsx
是业务和模板代码,style.less
是样式,img/
文件夹放图片文件。这样的话,能够把每一个组件都做为一个总体来管理,并且引用的时候也比较方便,例如import LoadMore from '../../app/components/LoadMore'
就能够了——目录名即组件的名字。
使用React重构百度新闻webapp前端 https://www.imooc.com/article/12433
————————————————————————————————————————————————————————————————————————
Redux 的 React 绑定库包含了 容器组件和展现组件相分离 的开发思想。明智的作法是只在最顶层组件(如路由操做)里使用 Redux。其他内部组件仅仅是展现性的,全部数据都经过 props 传入。
那么为何须要容器组件和展现组件相分离呢?
这里有个基本原则:容器组件仅仅作数据提取,而后渲染对应的子组件,记住这个点,Trust me!
看下面这个展现列表的例子,不区分容器和展现组件的状况
// CommentList.js class CommentList extends React.Component { constructor() { super(); this.state = { comments: [] } } componentDidMount() { $.ajax({ url: "/my-comments.json", dataType: 'json', success: function(comments) { this.setState({comments: comments}); }.bind(this) }); } render() { return <ul> {this.state.comments.map(renderComment)} </ul>; } renderComment({body, author}) { return <li>{body}—{author}</li>; } }
可用性:CommentList不能够复用
数据结构:组件应该对所须要的数据有所预期,但这里其实没有,PropTypes能够很好的作到这一点
那么来看下分离的状况:
// CommentListContainer.js class CommentListContainer extends React.Component { constructor() { super(); this.state = { comments: [] } } componentDidMount() { $.ajax({ url: "/my-comments.json", dataType: 'json', success: function(comments) { this.setState({comments: comments}); }.bind(this) }); } render() { return <CommentList comments={this.state.comments} />; } } // CommentList.js class CommentList extends React.Component { constructor(props) { super(props); } render() { return <ul> {this.props.comments.map(renderComment)} </ul>; } renderComment({body, author}) { return <li>{body}—{author}</li>; } }
这样就作到了数据提取和渲染分离,CommentList能够复用,CommentList能够设置PropTypes判断数据的可用性
来看下容器组件和展现组件的区别:
展现组件 | 容器组件 |
---|---|
关注事物的展现 | 关注事物如何工做 |
可能包含展现和容器组件,而且通常会有DOM标签和css样式 | 可能包含展现和容器组件,而且不会有DOM标签和css样式 |
经常容许经过this.props.children传递 | 提供数据和行为给容器组件或者展现组件 |
对第三方没有任何依赖,好比store 或者 flux action | 调用flux action 而且提供他们的回调给展现组件 |
不要指定数据如何加载和变化 | 做为数据源,一般采用较高阶的组件,而不是本身写,好比React Redux的connect(), Relay的createContainer(), Flux Utils的Container.create() |
仅经过属性获取数据和回调 | |
不多有本身的状态,即便有,也是本身的UI状态 | |
除非他们须要的本身的状态,生命周期,或性能优化才会被写为功能组件 |
优点:
展现和容器更好的分离,更好的理解应用程序和UI
重用性高,展现组件能够用于多个不一样的state数据源
展现组件就是你的调色板,能够把他们放到单独的页面,在不影响应用程序的状况下,让设计师调整UI
迫使你分离标签,达到更高的可用性
React 之容器组件和展现组件相分离解密 https://segmentfault.com/a/1190000006845396
————————————————————————————————————————————————————————————————————————
智能组件和木偶组件(也有叫容器组件和UI组件的)。见名知意,智能组件就要作一些比较智能比较‘高端’的工做,智能组件负责逻辑处理、数据获取等比较‘智能’的工做;
而木偶组件就比较‘呆板’,它原则上只负责展现功能,像一只木偶,别人(智能组件)用线牵着它来控制它的展现内容。
来看一个例子:
class ListUI extends React.Component{ render(){ let data = this.props.data; return ( <ul> {data.map(item => <li key={item.id} >{item.text}</li>)} </ul> ) } } class List extends React.Component{ constructor(){ super(); this.getData = this.getData.bind(this); } render(){ let data = this.getData(); return <ListUI data={data} /> } getData(){ return [{id : 1,text : 'hello'},{id : 2,text : 'world'}]; } }
本例中组件List(智能组件)负责获取数据(getData),实际开发中会比这复杂的多,涉及到调用接口获取数据,数据的处理等等,本例侧重说明概念,具体的就再也不赘述。
而组件ListUI(木偶组件)则只负责数据的展现,傻瓜式的别人给他什么他就展现什么。
这是react开发中很重要的一个概念,这种解构分工明确,解构清晰,方便维护和扩展。实属react开发必备之技能。
本例中还有一点值得注意:
构造函数中的绑定this 实际开发中会在一个组件中定义不少方法,这些方法中可能会有回调函数,这就会致使this指向的问题(固然ES6中的箭头函数能规避这个问题,也推荐只用ES6的写法)
可是也不排除有ES5的写法存在,因此须要咱们绑定this,这里强烈推荐在构造函数中统一的绑定this,这样哪些方法绑定了this就一目了然也便于维护。
基于这个概念作一些扩展。
事件处理: 有了以上概念的基础,现给组件添加一个点击事件,目的是点击li时弹出当前li的文本内容。根据以上的概念,要把逻辑处理的方法写在智能组件中,而后把方法做为一个prop传递给木偶组件,最后在木偶组件中添加点击事件来调用传过来的方法。
代码以下:
class ListUI extends React.Component{ constructor(){ super(); this.clickHandler = this.clickHandler.bind(this); } render(){ let data = this.props.data; return ( <ul> {data.map(item => <li key={item.id} onClick={this.clickHandler}>{item.text}</li>)} </ul> ) } clickHandler(e){ this.props.clickHandler(e); } } class List extends React.Component{ constructor(){ super(); this.getData = this.getData.bind(this); this.clickHandler = this.clickHandler.bind(this); } render(){ let data = this.getData(); return <ListUI data={data} click={this.clickHandler}/> } getData(){ return [{id : 1,text : 'hello'},{id : 2,text : 'world'}]; } clickHandler(e){ let ele = e.currentTarget || e.srcElement; alert(ele.innerHTML); } }
最后想说的是:智能组件和木偶组件这种编程思想很是重要,开发大型项目时体现的尤其明显,当项目很大时更须要把木偶组件细分,否则代码会杂乱无章,很是的不便于阅读,及其的不利于维护和扩展,最后的结果是项目没法进行。
摘自: https://blog.csdn.net/hkwBest/article/details/78688205?locationNum=6&fps=1