• React之智能组件和木偶组件


    智能组件 VS 木偶组件

    在 React + Redux 结合作为前端框架的时候,提出了一个将组件分为“智能”和“木偶”两种

    • 智能组件:它是数据的所有者,它拥有数据、且拥有操作数据的action,但是它不实现任何具体功能。它会将数据和操作action传递给子组件,让子组件来完成UI或者功能。这就是智能组件,也就是项目中的各个页面。
    • 木偶组件:它就是一个工具,不拥有任何数据、及操作数据的action,给它什么数据它就显示什么数据,给它什么方法,它就调用什么方法,比较傻。这就是木偶组件,即项目中的各个组件。

    因此,数据在page层获取、数据的操作和处理在page层定义,组件就是傻傻的知道执行和显示就行了,各操各的心。这种设计也整好符合目前无论是Vue、augular还是React提倡的基于数据驱动的设计理念——程序定义好Model和View的关系,剩下的业余处理只需要关心数据变化,View的变化由框架自动执行,无需像jquery时代再去手动操作DOM。

    数据管理

    下面看一下数据在系统中是如何传递的。这里的数据分为两种:

    • 第一种可以称之为“系统数据”,和业务无关的,一般任何系统中都会有。例如用户id、用户头像、配置信息等,这个的特点就是符合单例模式,全局共用一套
    • 第二种可以称之为“业务数据”,每个页面都可能不一样。如在娱乐频道需要的是娱乐新闻列表,而到了军事频道就需要军事新闻的列表了,再到新闻详情页就需要新闻的内容了

    针对这两种不同的数据,当然要分开处理。针对第一种“系统数据”,系统一初始化就立即获取,然后交给Redux做管理,这也符合Redux的特点。而针对第二种“业务数据”,那就什么时候用,就什么时候获取。


    代码架构(物理)

    使用React + Redux设计代码结构,目前方式都比较统一,基本都是按照以下方式来设计,这些在github等社区都能找到相似的结构。

    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/componentsapp/container是开发中修改最多的目录。app/container里面定义的都是页面,即智能组件,只关心数据,功能比较单一,因此结构也比较简单

    app/container
        list
            index.jsx
            Home.jsx
            Baijia.jsx
        detail
            detail.jsx
            imgDetail.jsx

    但是针对组件app/components来说,要显示样式,当然就需要css和图片,文件类型比较多。因此,要按照如下方式定义组件

    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 

  • 相关阅读:
    我对NHibernate的感受(3):有些尴尬的集合支持
    NHibernate3.0剖析:Query篇之NHibernate.Linq标准查询
    NHibernate3.0剖析:Query篇之NHibernate.Linq增强查询
    30个优秀的后台管理界面设计案例分享
    NHibernate Session Flush的作用
    NHibernate 3 Extending the Linq Provider to fix some System.NotSupportedException
    Activity初步
    公司间交易学习笔记概述
    公司间交易学习笔记值映射
    BOM配置
  • 原文地址:https://www.cnblogs.com/rik28/p/8676151.html
Copyright © 2020-2023  润新知