11、React之分页组件(含勾选、过滤、表头纵横合并、子组件向父组件传值)、redux、redux-thunk、react-router、react-redux、React基础|版本|帧执行栈宏任务队列|虚拟DOM、setState、生命周期、Hook钩子、性能优化、组件写法、插槽、Model多层弹窗组件、react.15.6源码、ant-design-pro构成、前端路由(2700行)

一、react之表格和分页组件
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8" />
      <title>react之表格和分页组件之React16.4.0版</title>
      <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
      <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
      <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
      <style>
        table{
          border-collapse: collapse;
          border: 1px solid #cbcbcb;
          width:1800px;
          background:#ffffff;
          text-align:center;
        }
        table td,table th {
          padding: 5px;
          border: 1px solid #cbcbcb;
          font-weight:100
        }
        div button{
          color:gray;
          margin-right:5px
        }
        .div{
          color:red;
          width:1800px;
          padding:10px 0;
        }
      </style>
    </head>
    <body>
      <div class="div">
        <div>表格组件的功能:</div>
        <div>(1)带过滤条件、</div>
        <div>(2)表头和表体自动关联和合并、</div>
        <div>(3)排序(暂不实现)、</div>
        <div>(4)表体可以嵌套表格(暂不实现)、</div>
        <div>(5)3种勾选(选择一项、选择一页、选择所有页)、</div>
        <div>(6)翻页记忆、</div>
        <div>(7)分页。</div>
      </div>
      <div id="container"></div>
    </body>
    <script type="text/babel">
      const container = document.getElementById('container');
      function TwoImg(props) {
        var checkImg = {
          yes: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAA+UlEQVQoFZWSMU4DMRBF/584G7QSRcIxuAZKEykNEiUVHVTQRaKh4AIcgAvQpkukVDlBOAYNSGSlXXuwpViyYYFdS9aMZ/6bsezh5HZ3T2KhqkfosEhWqnjkyd1u3xWKdQMsfaEAB0Zilf8swfdU0w0klmpGpz1BvpbHcklbPf8Okts0CfJtWBTz/Yc++Jc8S3PZVQfKGwiuvMD6XYsMzm1dT/1jXKdQ8E0asHRrAzOzbC6UGINWHPQp1UQ/6wjF2LpmJSKfhti4Bi8+lhWP4I+gAqV1uqSi8j9WRuF3m3eMWVUJBeKxzUoYn7bEX7HDyPmB7QEHbRjyL+/+VnuXDUFOAAAAAElFTkSuQmCC',
          no: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAADqADAAQAAAABAAAADgAAAAC98Dn6AAAAbklEQVQoFWM8c+ZMLQMDQxUQcwAxMeAHUFEbC5CoYmNj02ZmZn5FjK6/f/+K/fr16ypIIwdIk7a29hdiNF69ehWkjIOJGMXY1IxqxBYqULEhFDiglPMDlIygKQKPryBSILUgPSCNbaC0B6RJSuQAbowizhJuOsAAAAAASUVORK5CYII=',
        };
        return (<img src={props.isTrue?checkImg.yes:checkImg.no} onClick={props.clickImg.bind(this)}/>)
      }
      //一、以下TablePage是表格组件,约280行
      class TablePage extends React.Component {
        constructor(props) {
          super(props);
          this.state = {};
          this.initThead = [];
          this.dataIndexs = [];//表头索引,表头和表体关联的依据
          this.maxRowspan = 1;//表头最大跨行数
          if(props.giveParentInstance){
            props.giveParentInstance(this)//子组件向父组件传值-之三-给函数传参并执行
          } 
        }
        //表头跨行跨栏的总思路
        //(1)处理跨行的时候,上面决定下面,要看上面有几个单行
        //(2)处理跨栏的时候,下面决定上面,要看下面有几个单栏
        //一、以下2个函数解决“表头跨行”问题
        //1、获取表头“最大跨行数”
        getMaxRowspanAndDataIndexs(columns,initRowspan){
          var that=this;
          columns.forEach(function(item,index) {
            if(item.children){
              initRowspan+=1;
              if(that.maxRowspan<initRowspan) that.maxRowspan = initRowspan;
              that.getMaxRowspanAndDataIndexs(item.children,initRowspan)
            }else{
              that.dataIndexs.push(item.dataIndex)//生成表头“数据索引”
            }
          });
        }
        //2、添加表头“每栏跨行数”
        addEachColumnRowspan(columns,maxRowspan){
          var that=this;
          columns.forEach(function(item,index) {
            if(item.children){
              item.thisRowspan=1;
              that.addEachColumnRowspan(item.children,maxRowspan-1)
            }else{
              item.thisRowspan=maxRowspan;
            }
          });
        }
        //二、以下2个函数解决“表头跨栏”问题
        //3、添加表头“每栏跨栏数”
        addEachColumnColspan(columns){
          var that=this;
          columns.forEach(function(item) {
            if(item.thisColspan)return;
            if(item.children){
              that.addThisColumnColspan(item,item.children)
            }else{
              item.thisColspan=1;
            }
          });
        }
        //4、当某栏有多个子栏时,添加“该栏及其子栏的跨栏数”
        addThisColumnColspan(item,children){
          var that=this;
          children.forEach(function(child) {
            var thisChildren= child.children;
            if(thisChildren){//item的子级还有子级
              //下面添加item栏的跨栏数
              that.addThisColumnColspan(item,thisChildren);
              //下面添加item栏子栏的跨栏数
              that.addEachColumnColspan(children);
            }else{
              if(item.thisColspan){
                item.thisColspan+=1;
              }else{
                item.thisColspan=1;
              }
              child.thisColspan=1;
            }
          });
        }
        getInitThead(){
          for(var i=0;i<this.maxRowspan;i++){
            this.initThead.push([]);
          }
        } 
        getCenterThead(columns,initThead,index){
          var that=this;
          columns.forEach(function(item,indexIn){
            var itemTitle;
            if(item.title){
              itemTitle=item.title;
            }else{
              itemTitle=<TwoImg isTrue={that.props.checkSource.isSelectNowPage} clickImg={that.clickThisPage.bind(that,that.props.dataSource)}/>      
            }
            initThead[index].push(<th key={indexIn+Math.random()} rowSpan={item.thisRowspan} colSpan={item.thisColspan} dataindex={item.dataIndex||''}>{itemTitle}</th>)
            var children=item.children;
            if(children){
              that.getCenterThead(children,initThead,index+1)
            }
          })
        }  
        getLastThead(thead,initThead){
          var that=this;
          initThead.forEach(function(item,index){
            thead.push(<tr key={index}>{item}</tr>)
          })
        }
        getTbody(dataSource,trBody){
          var that=this;
          dataSource.forEach(function(tr,index){
            var trSingle=[];
            for(var i=0;i<that.dataIndexs.length;i++){
              var indexIn=that.dataIndexs[i];
              var td;
              if(indexIn === 'checkQC'){
                td = <TwoImg isTrue={tr.state} clickImg={that.clickSingleItem.bind(that,tr,dataSource)}/> 
              }else{
                td = tr[indexIn];
              }
              trSingle.push(<td key={indexIn}>{td}</td>) 
            }
            trBody.push(<tr key={index}>{trSingle}</tr>); 
          });
        }
        componentWillUpdate(nextProps) {
          this.signCheckbox(nextProps) 
        }
        setAllState(){
          this.props.checkboxClick({
            allIncludedIds: this.props.checkSource.allIncludedIds,
            allExcludedIds: this.props.checkSource.allExcludedIds,
            isSelectNowPage: this.props.checkSource.isSelectNowPage,
            isSelectAllPages: this.props.checkSource.isSelectAllPages,
            textAllPages: this.props.checkSource.textAllPages,
          })
        }
        clickChildAllPages(itemArray) {//所有页所有条目复选框被点击时执行的函数
          if(this.props.checkSource.isSelectAllPages){
            if(this.props.checkSource.allExcludedIds.length>0){
              this.props.checkSource.isSelectAllPages = true;
              this.props.checkSource.isSelectNowPage = true;
              this.props.checkSource.textAllPages= '已启用,无排除项!';
              itemArray.forEach(function (item) {
                item.state = true;
              });
            }else if(this.props.checkSource.allExcludedIds.length==0){
              this.props.checkSource.isSelectAllPages = false;
              this.props.checkSource.isSelectNowPage = false;
              this.props.checkSource.textAllPages= '未启用,无选择项!';
              itemArray.forEach(function (item) {
                item.state = false;
              });
            }
          }else{
            this.props.checkSource.isSelectAllPages = true;
            this.props.checkSource.isSelectNowPage = true;
            this.props.checkSource.textAllPages= '已启用,无排除项!';
            itemArray.forEach(function (item) {
              item.state = true;
            });
          }
          this.props.checkSource.allExcludedIds = [];
          this.props.checkSource.allIncludedIds = [];
          this.setAllState()
        }
        clickThisPage(itemArray) {//当前页所有条目复选框被点击时执行的函数
        //onClick={this.clickThisPage.bind(this,params.tableDatas)
          var that = this;
          this.props.checkSource.isSelectNowPage = !this.props.checkSource.isSelectNowPage;
          itemArray.forEach(function (item) {
            item.state = that.props.checkSource.isSelectNowPage;
            if (item.state) {
              that.delID(item[that.props.idKey], that.props.checkSource.allExcludedIds);
              that.addID(item[that.props.idKey], that.props.checkSource.allIncludedIds);
            } else {
              that.delID(item[that.props.idKey], that.props.checkSource.allIncludedIds);
              that.addID(item[that.props.idKey], that.props.checkSource.allExcludedIds);
            }
          });
          if(this.props.checkSource.isSelectAllPages){
            if(this.props.checkSource.isSelectNowPage && this.props.checkSource.allExcludedIds.length === 0){
              this.props.checkSource.textAllPages = '已启用,无排除项!';
            }else{
              this.props.checkSource.textAllPages = '已启用,已排除'+ this.props.checkSource.allExcludedIds.length + '项!排除项的ID为:' + this.props.checkSource.allExcludedIds;
            }
          }else{
            if(!this.props.checkSource.isSelectNowPage && this.props.checkSource.allIncludedIds.length === 0){
              this.props.checkSource.textAllPages='未启用,无选择项!';
            }else{
              this.props.checkSource.textAllPages = '未启用,已选择' + this.props.checkSource.allIncludedIds.length + '项!选择项的ID为:' + this.props.checkSource.allIncludedIds;
            }
          }
          this.setAllState()
        }
        clickSingleItem(item, itemArray) {//当前页单个条目复选框被点击时执行的函数
          var that = this;
          item.state = !item.state;
          if (item.state) {
            this.props.checkSource.isSelectNowPage = true;
            this.addID(item[this.props.idKey], this.props.checkSource.allIncludedIds);
            this.delID(item[this.props.idKey], this.props.checkSource.allExcludedIds);
            itemArray.forEach(function (item) {
              if (!item.state) {
                that.props.checkSource.isSelectNowPage = false;
              }
            });
          } else {
            this.props.checkSource.isSelectNowPage = false;
            this.addID(item[this.props.idKey], this.props.checkSource.allExcludedIds);
            this.delID(item[this.props.idKey], this.props.checkSource.allIncludedIds);
          }
          if(this.props.checkSource.isSelectAllPages){
            if(this.props.checkSource.isSelectNowPage && this.props.checkSource.allExcludedIds.length === 0){
              this.props.checkSource.textAllPages = '已启用,无排除项!';
            }else{
              this.props.checkSource.textAllPages = '已启用,已排除'+ this.props.checkSource.allExcludedIds.length + '项!排除项的ID为:' + this.props.checkSource.allExcludedIds;
            }
          }else{
            if(!this.props.checkSource.isSelectNowPage && this.props.checkSource.allIncludedIds.length === 0){
              this.props.checkSource.textAllPages='未启用,无选择项!';
            }else{
              this.props.checkSource.textAllPages = '未启用,已选择' + this.props.checkSource.allIncludedIds.length + '项!选择项的ID为:' + this.props.checkSource.allIncludedIds;
            }
          }
          this.setAllState()
        }
        signCheckbox(nextProps) {//标注当前页被选中的条目,在翻页成功后执行。
          var that = this;
          if(nextProps.checkSource.isSelectAllPages){
            nextProps.checkSource.isSelectNowPage = true;
            nextProps.dataSource.forEach(function (item) {
              var thisID = item[nextProps.idKey];
              var index = nextProps.checkSource.allExcludedIds.indexOf(thisID);
              if (index > -1) {
                item.state = false;
                nextProps.checkSource.isSelectNowPage = false;
              } else {
                item.state = true;
              }
            });
          }else{
            nextProps.checkSource.isSelectNowPage = true;
            nextProps.dataSource.forEach(function (item) {
              var thisID = item[nextProps.idKey];
              var index = nextProps.checkSource.allIncludedIds.indexOf(thisID);
              if (index === -1) {
                item.state = false;
                nextProps.checkSource.isSelectNowPage = false;
              } else {
                item.state = true;
              }
            });
          }
          this.state.isSelectNowPage=nextProps.checkSource.isSelectNowPage;
        }
        addID(id, idArray) {
          var index = idArray.indexOf(id);
          if (index === -1) {
            idArray.push(id);//如果当前页的单项既有勾选又有非勾选,这时勾选当前页,需要这个判断,以免重复添加
          }
        }
        delID(id, idArray) {
          var index = idArray.indexOf(id);
          if (index > -1) {
            idArray.splice(index, 1)
          }
        }
        render() {
          var that=this;
          var thead=[];
          var tbody=[];
          var trBody=[];
          var columns=this.props.columns;
          var dataSource=this.props.dataSource;
          this.initThead = [];
          this.dataIndexs = [];
          this.getMaxRowspanAndDataIndexs(columns,1);
          this.addEachColumnRowspan(columns,this.maxRowspan);
          this.addEachColumnColspan(columns);
          this.getInitThead();
          this.getCenterThead(columns,this.initThead,0);
          this.getLastThead(thead,this.initThead);
          this.getTbody(dataSource,trBody);
          return (
            <div>
              <table>
                <thead>
                {thead} 
                </thead>
                <tbody>
                {trBody} 
                </tbody>
              </table> 
            </div>
          )
        }
      }
      //二、以下DevidePage是分页组件,约150行
      class DevidePage extends React.Component {
        constructor(props) {
          super(props);
          this.state = { };
        }
        componentDidMount(){
          document.getElementById("inputQC").addEventListener("keydown", this.onKeyDown.bind(this))
        }
        componentDidUpdate(){
          document.getElementById("inputQC").addEventListener("keydown", this.onKeyDown.bind(this))
        }
        componentWillUnmount(){
          document.getElementById("inputQC").removeEventListener("keydown", this.onKeyDown.bind(this))
        }
        inputChange() {
          var value = parseInt(this.refs.input.value);
          var allPagesNum = this.props.divideSource.allPagesNum;
          if(value < allPagesNum && value > 1) {
            this.props.divideSource.inputValue = value
          }else if(value >= allPagesNum) {
            this.props.divideSource.inputValue = allPagesNum
          }else{//包含 value <= 1和value=其它非数字字符
            this.props.divideSource.inputValue = 1
          }
        }
        clickButton(value){
          var nowPageNum = null;
          if(value === 'front'){
            this.props.divideSource.nowPageNum--;
            nowPageNum = this.props.divideSource.nowPageNum
          }else if(value === 'back'){
            this.props.divideSource.nowPageNum++;
            nowPageNum = this.props.divideSource.nowPageNum
          }else if(value === 'leap'){
            this.inputChange();
            nowPageNum = this.props.divideSource.inputValue
          }else{
            nowPageNum = value
          }
          this.refs.input.value = nowPageNum;
          this.props.divideClick(nowPageNum,this.props.divideSource.eachPageItemsNum);
        }
        onKeyDown(event){
          if(event.key === 'Enter'){
            this.inputChange();
            this.refs.input.value = this.props.divideSource.inputValue;
            this.props.divideClick(this.props.divideSource.inputValue,this.props.divideSource.eachPageItemsNum);
          }
        }
        pageNumLeap(){
          var eachPageItemsNum = this.refs.select.value;
          this.props.divideSource.eachPageItemsNum = eachPageItemsNum;
          this.props.divideClick(1,eachPageItemsNum);
        }
        render() {
          var numButton=[];
          var divideSource = this.props.divideSource;
          //1、以下处理与分页相关的数字
          var nowPageNum = divideSource.nowPageNum;
          var allPagesNum = divideSource.allPagesNum;
          var inputValue = divideSource.inputValue;
          var eachPageItemsNum = divideSource.eachPageItemsNum;
          var allItemsNum = divideSource.allItemsNum;
          //2、以下是分页组件本身
          if (allPagesNum >= 1 && allPagesNum <= 10) {
            for (var i = 1; i <= allPagesNum; i++) {
              numButton.push(<button key={i} style={i==nowPageNum?{color:'red'}:{color:'gray'}} onClick={this.clickButton.bind(this,i)}>{i}</button>)
            }
          } else if (allPagesNum >= 11) {
            if (nowPageNum > 8) {
              numButton.push(<button key={1} onClick={this.clickButton.bind(this,1)}>{1}</button>);
              numButton.push(<button key={2} onClick={this.clickButton.bind(this,2)}>{2}</button>);
              numButton.push(<button key={3} onClick={this.clickButton.bind(this,3)}>{3}</button>);
              numButton.push(<button key={'front'} disabled>{'...'}</button>);
              numButton.push(<button key={nowPageNum-2} onClick={this.clickButton.bind(this,nowPageNum-2)}>{nowPageNum-2}</button>);
              numButton.push(<button key={nowPageNum-1} onClick={this.clickButton.bind(this,nowPageNum-1)}>{nowPageNum-1}</button>);
              numButton.push(<button key={nowPageNum} style={{color:'red'}} onClick={this.clickButton.bind(this,nowPageNum)}>{nowPageNum}</button>);
            } else {
              for (i = 1; i <= nowPageNum; i++) {
                numButton.push(<button key={i} style={i==nowPageNum?{color:'red'}:{color:'gray'}} onClick={this.clickButton.bind(this,i)}>{i}</button>)
              }
            }
            // 以上当前页的左边,以下当前页的右边
            if (allPagesNum - nowPageNum >= 7) {
              numButton.push(<button key={nowPageNum+1} onClick={this.clickButton.bind(this,nowPageNum+1)}>{nowPageNum+1}</button>);
              numButton.push(<button key={nowPageNum+2} onClick={this.clickButton.bind(this,nowPageNum+2)}>{nowPageNum+2}</button>);
              numButton.push(<button key={'back'} disabled>{'...'}</button>);
              numButton.push(<button key={allPagesNum-2} onClick={this.clickButton.bind(this,allPagesNum-2)}>{allPagesNum-2}</button>);
              numButton.push(<button key={allPagesNum-1} onClick={this.clickButton.bind(this,allPagesNum-1)}>{allPagesNum-1}</button>);
              numButton.push(<button key={allPagesNum} onClick={this.clickButton.bind(this,allPagesNum)}>{allPagesNum}</button>);
            } else {
              for (var i = nowPageNum + 1; i <= allPagesNum; i++) {
                numButton.push(<button key={i} onClick={this.clickButton.bind(this,i)}>{i}</button>)
              }
            }
          }
          //3、以下处理每页显示条数
          var selectOption=[];
          var numOptions=this.props.numOptions;
          if(!numOptions){numOptions=[10,20,30,40,50]}; 
          for(var i=0;i<numOptions.length;i++){
            selectOption.push(<option value={numOptions[i]} key={i} >{numOptions[i]}</option>)
          }
          //4、以下处理最右侧说明文字
          var textTemp=this.props.text||{};
          var text = {
            unit: textTemp.unit || "条,",
            frontMoreText: textTemp.frontMoreText || "",
            totalText: textTemp.totalText || "共",
            totalUnit: textTemp.totalUnit || "项",
            backMoreText: textTemp.backMoreText || "",
          };
          //5、以下渲染分页组件 
          return (
            <div style={{display:'block',display:"flex",width:"1800px",marginTop:"20px"}}>
              <div style={{display:"flex"}}>
                <button style={{marginRight:"5px"}} disabled={nowPageNum===1} onClick={this.clickButton.bind(this,'front')}>上一页</button>
                <div>{ numButton }</div>
                <button disabled={nowPageNum===allPagesNum} onClick={this.clickButton.bind(this,'back')}>下一页</button>
              </div>
              <div style={{display:"flex", flex:1, justifyContent:"flex-end"}}>
                <div style={{marginRight:"15px"}}>
                  <span>转到第</span>
                  <input id='inputQC' key={nowPageNum==1?Math.random():'key'} type="text" style={{width:"30px",margin:"0 5px"}} ref="input" onChange={this.inputChange.bind(this)} onKeyDown={this.onKeyDown.bind(this,event)} defaultValue={inputValue}/>
                  <span>页</span>
                  <button style={{margin:"0 5px"}} onClick={this.clickButton.bind(this,'leap')}>Go</button>
                </div>
                <div>
                  <span>每页显示</span>
                  <select style={{margin:"0 5px"}} ref="select" defaultValue={eachPageItemsNum||10} onChange={this.pageNumLeap.bind(this)}>
                    { selectOption }
                  </select>
                  <span>{text.unit}</span>
                </div>
                <div>
                  <span>{text.frontMoreText}</span>
                  <span>{text.totalText}</span>
                  <span>{allItemsNum||0}</span>
                  <span>{text.totalUnit}</span>
                  <span>{text.backMoreText}</span>
                </div>
              </div>   
            </div>
          )
        }
      }
      //三、以下WholePage是页面组件,根据自己的独特需求写
      class WholePage extends React.Component {
        constructor(props) {
          super(props);
          this.state = {
            filter: {
              input:'',
              select:1
            },
            dataSource: [],
            divideSource:{
              nowPageNum: 1,
              allPagesNum: 1,
              allItemsNum: 1,
              eachPageItemsNum: 10,
              inputValue:1,
            },
            checkSource:{
              allIncludedIds: [],
              allExcludedIds: [],
              isSelectAllPages: false,
              isSelectNowPage: false,
              textAllPages: '未启用,无选择项!', 
            },
          };
          this.columns = [
            {
              title: '',
              dataIndex: 'checkQC',
            },
            {
              title: '序号',
              dataIndex: 'order',
            },
            {
              title: '个人信息',
              children: [
                {
                  title: '姓名',
                  dataIndex: 'name'
                },
                {
                  title: '年龄',
                  dataIndex: 'age'
                },
              ],
            },
            {
              title: '学校',
              children: [
                {
                  title: '年级',
                  children: [
                    {
                      title: '班级',
                      children: [
                        {
                          title: '科目',
                          dataIndex: 'cross1'
                        },
                        {
                          title: '科目',
                          dataIndex: 'cross2'
                        },
                      ],
                    },
                    {
                      title: '班级',
                      children: [
                        {
                          title: '科目',
                          dataIndex: 'cross3'
                        },
                        {
                          title: '科目',
                          dataIndex: 'cross4'
                        },
                      ],
                    },
                  ],
                },
                {
                  title: '年级',
                  children: [
                    {
                      title: '班级',
                      children: [
                        {
                          title: '科目',
                          dataIndex: 'cross5'
                        },
                        {
                          title: '科目',
                          dataIndex: 'cross6'
                        },
                      ],
                    },
                    {
                      title: '班级',
                      children: [
                        {
                          title: '科目',
                          dataIndex: 'cross7'
                        },
                        {
                          title: '科目',
                          dataIndex: 'cross8'
                        },
                      ],
                    },
                  ],
                },
              ],
            },
            {
              title: '省级',
                children: [
                  {
                    title: '市级',
                    children: [
                      {
                        title: '县级',
                        children: [
                          {
                            title: '乡级',
                            children: [
                              {
                                title: '村级',
                                dataIndex: 'village1',
                              },
                              {
                                title: '村级',
                                dataIndex: 'village2',
                              },
                            ],
                          },
                          {
                            title: '乡级',
                            children: [
                              {
                                title: '村级',
                                dataIndex: 'village3',
                              },
                              {
                                title: '村级',
                                dataIndex: 'village4',
                              },
                            ],
                          },
                        ],
                      },
                      {
                        title: '县级',
                        children: [
                          {
                            title: '乡级',
                            children: [
                              {
                                title: '村级',
                                dataIndex: 'village5',
                              },
                              {
                                title: '村级',
                                dataIndex: 'village6',
                              },
                            ],
                          },
                          {
                            title: '乡级',
                            children: [
                              {
                                title: '村级',
                                dataIndex: 'village7',
                              },
                              {
                                title: '村级',
                                dataIndex: 'village8',
                              },
                            ],
                          },
                        ],
                      },
                    ],
                  },
                  {
                    title: '市级',
                    children: [
                      {
                        title: '县级',
                        children: [
                          {
                            title: '乡级',
                            children: [
                              {
                                title: '村级',
                                dataIndex: 'village9',
                              },
                              {
                                title: '村级',
                                dataIndex: 'village10',
                              },
                            ],
                          },
                          {
                            title: '乡级',
                            children: [
                              {
                                title: '村级',
                                dataIndex: 'village11',
                              },
                              {
                                title: '村级',
                                dataIndex: 'village12',
                              },
                            ],
                          },
                        ],
                      },
                      {
                        title: '县级',
                        children: [
                          {
                            title: '乡级',
                            children: [
                              {
                                title: '村级',
                                dataIndex: 'village13',
                              },
                              {
                                title: '村级',
                                dataIndex: 'village14',
                              },
                            ],
                          },
                          {
                            title: '乡级',
                            children: [
                              {
                                title: '村级',
                                children: [
                                  {
                                    title: '组级',
                                    dataIndex: 'team1'
                                  },
                                  {
                                    title: '组级',
                                    dataIndex: 'team2'
                                  },
                                ],
                              },
                              {
                                title: '村级',
                                children: [
                                  {
                                    title: '组级',
                                    dataIndex: 'team3'
                                  },
                                  {
                                    title: '组级',
                                    dataIndex: 'team4'
                                  },
                                ],
                              },
                            ],
                          },
                        ],
                      },
                    ],
                  },
                ],
            }
          ];  
        };
        componentWillMount() { 
          this.divideClick(1,this.state.divideSource.eachPageItemsNum)
        }
        divideClick(nowPageNum,eachPageItemsNum) { 
          var data=[]; 
          var allItemsNum = 193;
          var nowPageNum = nowPageNum||1; 
          var eachPageItemsNum = eachPageItemsNum||10; 
          var allPagesNum = Math.ceil(allItemsNum/eachPageItemsNum);
          for(var i=0;i<allItemsNum;i++){
            var obj={
              id: 'id'+(i+1),
              order: i+1,
              name: '姓名'+(i+1),
              age: '19岁',
            };
            for(var j=0;j<=40;j++){
              var crossKey = 'cross' + j;
              var villageKey = 'village' + j;
              var teamKey = 'team' + j;
              obj[crossKey] = j%2 == 0 ? '科1':'科2';
              obj[villageKey] = j%2 == 0 ? '村1' : '村2';
              obj[teamKey] = j%2 == 0 ? '组1' : '组2';
            }
            data.push(obj)
          };
          console.log( data );
          var dataSource = data.slice((nowPageNum-1)*eachPageItemsNum,nowPageNum*eachPageItemsNum);
          this.setState({
            dataSource: dataSource,
            divideSource: {
              nowPageNum: nowPageNum,
              allPagesNum: allPagesNum,
              allItemsNum: allItemsNum,
              eachPageItemsNum: eachPageItemsNum,
              inputValue: nowPageNum,
            },
          })
        }
        checkboxClick(object) { 
          this.setState({
            allIncludedIds: object.allIncludedIds,
            allExcludedIds: object.allExcludedIds,
            isSelectAllPages: object.isSelectAllPages,
            isSelectNowPage: object.isSelectNowPage,
            textAllPages: object.textAllPages,
          })
        }
        //附:兄弟组件传值(与本项目无关),在父组件的实例里定义一个对象,分别传给两个子组件,在子组件里,给该对象的一个属性赋值
        getChildInstance(that){//子组件向父组件传值-之一-在父组件定义函数
          this.childRef=that;//把子组件的实例赋值给父组件的childRef属性。非常重要!!!
        }
        clickParentAllPages(dataSource){
          this.childRef.clickChildAllPages(dataSource)//在父组件里调用子组件的方法。非常重要!!!
        }
        changeValue(key,event){
          this.setState({
            filter:{...this.state.filter,[key]:event.target.value}
          })
        }
        render() {
          var {dataSource,divideSource,checkSource,filter}={...this.state}; 
          return (
            <div> 
              <div>以下是过滤示例</div>
              <div style={{'border':'1px solid #cbcbcb','width':'1780px','padding':'10px','margin':'6px 0'}}>
                <div style={{'display':'flex'}}>
                  <div style={{'width':'212px'}}> 
                    <label style={{'paddingRight':'4px'}}>输入框示例</label>
                    <input type='text' placeholder='请输入' onChange={this.changeValue.bind(this,'input')}  style={{'border':'1px solid #cbcbcb','width':'100px'}}/>
                  </div>
                  <div style={{'width':'174px'}}> 
                    <label style={{'paddingRight':'4px'}}>下拉框示例</label>
                    <select onChange={this.changeValue.bind(this,'select')}>
                      <option value={1}>1分钟</option>
                      <option value={5}>5分钟</option>
                      <option value={10}>10分钟</option>
                      <option value={30}>30分钟</option>
                      <option value={60}>60分钟</option>
                    </select>
                  </div>
                  <div style={{'width':'500px'}}> 
                    <span>过滤条件为:{JSON.stringify(this.state.filter)}</span> 
                    <span style={{'padding':'0 10px'}}></span>  
                    <button onClick={this.divideClick.bind(this,3,10)}>过滤</button>
                    <button onClick={this.divideClick.bind(this,1,10)}>刷新</button>
                  </div>
                </div>
              </div>
              <div style={{'paddingBottom':'4px'}}>
                <span style={{'paddingRight':'10px'}}><TwoImg isTrue={checkSource.isSelectAllPages && checkSource.allExcludedIds.length===0} clickImg={this.clickParentAllPages.bind(this,dataSource)}/></span>     
                <span>{checkSource.textAllPages}</span>
              </div>
              <TablePage 
                idKey='id' 
                columns={this.columns}
                dataSource={dataSource}
                checkSource={checkSource}
                checkboxClick={this.checkboxClick.bind(this)}
                giveParentInstance={this.getChildInstance.bind(this)}//子组件向父组件传值-之二-将函数传给子组件
                />
              <DevidePage 
                divideSource={divideSource} 
                divideClick={this.divideClick.bind(this)}
                />  
            </div>
          )
        }
      }
      ReactDOM.render(<WholePage/>, container);
    </script>
  </html>
 
二、redux实际运用(redux.4.0.0版源码,去掉注释和空行,共413行),redux,react-redux,redux-thunk
1、参数的定义
  function functionA(createStore3) {//7、接收functionB的返回值createStore3
    return function createStore4(reducer0, preloadedState0, enhancer0) {//8、返回值为createStore4
      //9、实际执行createStore4(reducer, preloadedState),此处加工参数reducer, preloadedState,传给下面的createStore3
      var store3=createStore3(reducer1, preloadedState1, enhancer1);
      //16、此处对createStore3的返回值store3进行加工,下面return的是createStore4的返回值,也是最终的返回值
      return {
        dispatch: dispatch3,
        subscribe: subscribe3,
        getState: getState3,
        replaceReducer: replaceReducer3
      }
    }
  };
  function functionB(createStore2) {//5、接收functionC的返回值createStore2
    return function createStore3(reducer1, preloadedState1, enhancer1) {//6、返回值为createStore3
      //10、此处加工参数,传给下面的createStore2
      var store2=createStore2(reducer2, preloadedState2, enhancer2);
      //15、此处对createStore2的返回值store2进行加工,下面return的是createStore3的返回值
      return {
        dispatch: dispatch2,
        subscribe: subscribe2,
        getState: getState2,
        replaceReducer: replaceReducer2
      }
    }
  };
  function functionC(createStore1) {//3、接收functionD的返回值createStore1
    return function createStore2(reducer2, preloadedState2, enhancer2) {//4、返回值为createStore2
      //11、此处加工参数,传给下面的createStore1
      var store1=createStore1(reducer3, preloadedState3, enhancer3);
      //14、此处对createStore1的返回值store1进行加工,下面return的是createStore2的返回值
      return {
        dispatch: dispatch1,
        subscribe: subscribe1,
        getState: getState1,
        replaceReducer: replaceReducer1
      }
    }
  };
  function functionD(createStore0) {//1、实际执行functionD(createStore)
    return function createStore1(reducer3, preloadedState3, enhancer3) {//2、返回值为createStore1
    //12、此处加工参数,传给下面的createStore0
      var store0=createStore0(reducer4, preloadedState4, enhancer4);
      //13、此处对createStore0的返回值store0进行加工,下面return的是createStore1的返回值
      return {
        dispatch: dispatch0,
        subscribe: subscribe0,
        getState: getState0,
        replaceReducer: replaceReducer0
      }
    }
  };
2、createStore函数的定义与执行
(1)定义
  function createStore(reducer, preloadedState, enhancer) {
    return enhancer(createStore)(reducer, preloadedState);
  }
(2)执行
  createStore(
    rootReducer,
    preloadedState,
    compose(arrayFunction)
  )
3、compose的定义与执行
(1)定义
  var arrayFunction = [functionA, functionB, functionC, functionD];
  function compose(arrayFunction) {
    return arrayFunction.reduce(function (total, next) {//reduce用作高阶函数,compose其它函数
      // reduce第1次执行时,total是functionA,next是functionB,执行结果为functionOne
      // function functionOne() {
      //   return functionA(functionB.apply(undefined, arguments));
      // }
      // reduce第2次执行时,total是functionOne,next是functionC,执行结果为functionTwo
      // function functionTwo() {
      //   return functionOne(functionC.apply(undefined, arguments));
      // }
      // reduce第3次执行时,total是functionTwo,next是functionD,执行结果为functionThree
      // function functionThree() {
      //   return functionTwo(functionD.apply(undefined, arguments));
      // }
      // reduce将最后一次执行结果functionThree暴露出去
      return function () {
        return total(next.apply(undefined, arguments));
      };
    })
  }
(2)compose(arrayFunction)执行,返回functionThree
4、enhancer(createStore)(reducer, preloadedState)执行,就是functionThree(createStore)(reducer, preloadedState)执行
(1)enhancer(createStore)执行,就是functionThree(createStore)执行,最终返回createStore4
  //第1次执行时,functionThree(createStore0),functionD(createStore0),返回createStore1
  //第2次执行时,functionTwo(createStore1),functionC(createStore1),返回createStore2
  //第3次执行时,functionOne(createStore2),functionB(createStore2),返回createStore3
  //第4次执行时,functionA(createStore3),返回createStore4
(2)createStore4(reducer, preloadedState)执行
  //1、给createStore4传参并执行,进而给createStore3、createStore2、createStore1、createStore0传参并执行
  //2、给createStore0的返回值加工,进而给createStore1、createStore2、createStore3、createStore4的返回值加工,生成最终store
5、createStore实际运用(真实案例)
  import { createStore, applyMiddleware, compose } from 'redux';
  import reduxThunk from 'redux-thunk';//从UI组件直接dispatch action。它的主要思想是扩展action,使得action从只能是一个对象变成还可以是一个函数。
  import rootReducer from 'reducers/index';
  import DevTools from 'containers/DevTools';
  export default function configureStore(preloadedState) {
    const store = createStore(
      rootReducer,
      preloadedState,
      compose(
        applyMiddleware(reduxThunk),
        DevTools.instrument()
      )
    )
    return store
  }
6、相关源码解析
(1)createStore
  function createStore(reducer, preloadedState, enhancer) {
    var _ref2;
    if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
      enhancer = preloadedState;
      preloadedState = undefined;
    }
    if (typeof enhancer !== 'undefined') {
      if (typeof enhancer !== 'function') {
        throw new Error('Expected the enhancer to be a function.');
      }
      return enhancer(createStore)(reducer, preloadedState);  }
    if (typeof reducer !== 'function') {
      throw new Error('Expected the reducer to be a function.');
    }
    var currentReducer = reducer;
    var currentState = preloadedState;
    var currentListeners = [];
    var nextListeners = currentListeners;
    var isDispatching = false;
    function ensureCanMutateNextListeners() {
      if (nextListeners === currentListeners) {
        nextListeners = currentListeners.slice();
      }
    }
    function getState() {
      return currentState;
    }
    function subscribe(listener) {
      if (typeof listener !== 'function') {
        throw new Error('Expected listener to be a function.');
      }
      var isSubscribed = true;
      ensureCanMutateNextListeners();
      nextListeners.push(listener);
      return function unsubscribe() {
        if (!isSubscribed) {
          return;
        }
        isSubscribed = false;
        ensureCanMutateNextListeners();
        var index = nextListeners.indexOf(listener);
        nextListeners.splice(index, 1);
      };
    }
    function dispatch(action) {
      if (!isPlainObject(action)) {
        throw new Error('Actions must be plain objects. ' + 'Use custom middleware for async actions.');
      }
      if (typeof action.type === 'undefined') {
        throw new Error('Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?');
      }
      if (isDispatching) {
        throw new Error('Reducers may not dispatch actions.');
      }
      try {
        isDispatching = true;
        currentState = currentReducer(currentState, action);
      } finally {
        isDispatching = false;
      }
      var listeners = currentListeners = nextListeners;
      for (var i = 0; i < listeners.length; i++) {
        var listener = listeners[i];
        listener();
      }
      return action;
    }
    function replaceReducer(nextReducer) {
      if (typeof nextReducer !== 'function') {
        throw new Error('Expected the nextReducer to be a function.');
      }
      currentReducer = nextReducer;
      dispatch({ type: ActionTypes.INIT });
    }
    function observable() {
      var _ref;
      var outerSubscribe = subscribe;
      return _ref = {
        subscribe: function subscribe(observer) {
          if (typeof observer !== 'object') {
            throw new TypeError('Expected the observer to be an object.');
          }
          function observeState() {
            if (observer.next) {
              observer.next(getState());
            }
          }
          observeState();
          var unsubscribe = outerSubscribe(observeState);
          return { unsubscribe: unsubscribe };
        }
      }, _ref[result] = function () {
        return this;
      }, _ref;
    }
    dispatch({ type: ActionTypes.INIT });
    return _ref2 = {
      dispatch: dispatch,
      subscribe: subscribe,
      getState: getState,
      replaceReducer: replaceReducer
    }, _ref2[result] = observable, _ref2;
  }
(2)compose
  function compose() {
    for (var _len = arguments.length, funcs = Array(_len), _key = 0; _key < _len; _key++) {
      funcs[_key] = arguments[_key];
    }
    if (funcs.length === 0) {
      return function (arg) {
        return arg;
      };
    }
    if (funcs.length === 1) {
      return funcs[0];
    }
    return funcs.reduce(function (a, b) {//reduce将最后一次计算结果暴露出去
      return function () {
        return a(b.apply(undefined, arguments));
      };
    });
  } 
(3)applyMiddleware
  function applyMiddleware() {//这是一个加工dispatch的中间件
    for (var _len = arguments.length, middlewares = Array(_len), _key = 0; _key < _len; _key++) {
      middlewares[_key] = arguments[_key];
    }
    return function (createStore) {
      return function (reducer, preloadedState, enhancer) {
        var store = createStore(reducer, preloadedState, enhancer);
        var _dispatch = store.dispatch;
        var chain = [];
        var middlewareAPI = {
          getState: store.getState,
          dispatch: function dispatch(action) {//此处为什么不是dispatch:store.dispatch
            return _dispatch(action);
          }
        };
        chain = middlewares.map(function (middleware) {
          return middleware(middlewareAPI);//return reduxThunk(_ref)
        });
        _dispatch = compose.apply(undefined, chain)(store.dispatch);//_dispatch = (function (next){})(store.dispatch) ,这是dispatch的新定义。
        return _extends({}, store, {//store里的dispatch,被这里的dispatch覆盖
          dispatch: _dispatch
        });
      };
    };
  }
(4)combineReducers
  function combineReducers(reducers) {
    //下面是关于reducers的定义,它的key如fn1、fn2、fn3、fn4后来也成了state的key。此论依据非常重要!”前面的代码
    // finalReducers = reducers = {
    //   fn1 : function(){ return { key1 : "value1" } },
    //   fn2 : function(){ return { key2 : "value2" } },
    //   fn3 : function(){ return { key3 : "value3" } },
    //   fn4 : function(){ return { key4 : "value4" } },
    // }
    var reducerKeys = Object.keys(reducers);
    var finalReducers = {};
    for (var i = 0; i < reducerKeys.length; i++) {
      var key = reducerKeys[i];
      {
        if (typeof reducers[key] === 'undefined') {
          warning('No reducer provided for key "' + key + '"');
        }
      }
      if (typeof reducers[key] === 'function') {
        finalReducers[key] = reducers[key];
      }
    }
    var finalReducerKeys = Object.keys(finalReducers);
    var unexpectedKeyCache = void 0;
    {
      unexpectedKeyCache = {};
    }
    var shapeAssertionError = void 0;
    try {
      assertReducerShape(finalReducers);
    } catch (e) {
      shapeAssertionError = e;
    }
    return function combination() {//这个返回值就是createStore(reducer, preloadedState, enhancer)中的reducer
      // 下面是关于state的定义,它的key如fn1、fn2、fn3、fn4来自于reducers的key。此论依据“非常重要!”前面的代码
      // nextState = state = { } = {
      //   fn1 : { key1 : "value1" },
      //   fn2 : { key2 : "value2" },
      //   fn3 : { key3 : "value3" },
      //   fn4 : { key4 : "value4" }
      // }
      var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      var action = arguments[1];
      if (shapeAssertionError) {
        throw shapeAssertionError;
      }
      {
        var warningMessage = getUnexpectedStateShapeWarningMessage(state, finalReducers, action, unexpectedKeyCache);
        if (warningMessage) {
          warning(warningMessage);
        }
      }
      var hasChanged = false;
      var nextState = {};
      for (var _i = 0; _i < finalReducerKeys.length; _i++) {
        var _key = finalReducerKeys[_i];
        var reducer = finalReducers[_key];
        var previousStateForKey = state[_key];//非常重要!previousStateForKey可能为undefined
        var nextStateForKey = reducer(previousStateForKey, action);//非常重要!可能执行reducer(undefined,action),即执行reducer(defaultState,action);此时previousStateForKey !== nextStateForKey 为false
        if (typeof nextStateForKey === 'undefined') {
          var errorMessage = getUndefinedStateErrorMessage(_key, action);
          throw new Error(errorMessage);
        }
        nextState[_key] = nextStateForKey;
        hasChanged = hasChanged || previousStateForKey !== nextStateForKey;           
        // const defaultState = {
        //   loading: true,
        // };
        // export default function accountReducer(state = defaultState, action) {
        //   switch (action.type) {
        //     //下面给state重新赋值,state的引用就改变了;此时previousStateForKey !== nextStateForKey,hasChanged为true 。 
        //     //vuex.js不需要这样,因为它采用了递归监听
        //     case "change_second_text":
        //       return {...state, current_item: action.key}
        //     case "change_three_text":
        //       return {...state, current_item_three: action.key}
        //     case "isRegister":
        //       return {...state, loading: action.loading||false }//action匹配成功,state的引用就改变了
        //     //下面action匹配不成功,state的引用就不改变了
        //     default:
        //       return state;
        //   }
        // }
      }
      //遍历结束,一次性返回结果。
      return hasChanged ? nextState : state;
    };
  }
(5)其它
  function componentWillMount() {
    const current = [
      { location: "222", url: "/" },
      { location: "333", url: "/carDetail" },
    ];
    this.props.dispatch(menuChange(current))
  }
  export const menuChange = function (key) {
    return function (dispatch) {
      dispatch({
        type: "menu_change",
        key
      })
    }
  }

三、redux-thunk实际运用,redux,react-redux,redux-thunk
1、reduxThunk核心代码
  //从UI组件直接dispatch action。它的主要思想是扩展action,使得action从只能是一个对象变成还可以是一个函数。
  function reduxThunk(_ref) {//middleware(middlewareAPI)
    var dispatch = _ref.dispatch;
    var getState = _ref.getState;
    return function (next) {//即return function(store.dispatch);next是dispatch的旧定义,即applyMiddleware函数的chain数组的下一项的执行结果。 
      return function (action) {//返回的函数是dispatch的新定义;其中action是参数,原本只能是对象,经改造后还可以是函数。
        if (typeof action === 'function') {
          return action(dispatch, getState);//把旧的dispatch、getState传进自定义函数参数里
        }
        return next(action);
      };
    };
  }
2、reduxThunk全部代码
  (function webpackUniversalModuleDefinition(root, factory) {
      if (typeof exports === 'object' && typeof module === 'object')
          module.exports = factory();//common.js模块下执行,factory()的执行结果为null
      else if (typeof define === 'function' && define.amd)
          define([], factory);//require.js异步模块下执行
      else if (typeof exports === 'object')
          exports["ReduxThunk"] = factory();
      else
          root["ReduxThunk"] = factory();
  })(
    this, 
    function () {
      return (function (modules) {
        var installedModules = {};
        function __webpack_require__(moduleId) {
          if (installedModules[moduleId])
            return installedModules[moduleId].exports;
          var module = installedModules[moduleId] = {
            exports: {},
            id: moduleId,
            loaded: false        
          };
          modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
          module.loaded = true;
          return module.exports;   
        }
        __webpack_require__.m = modules;
        __webpack_require__.c = installedModules;
        __webpack_require__.p = "";
        return __webpack_require__(0); 
      })([function (module, exports, __webpack_require__) {
          module.exports = __webpack_require__(1);
        },function (module, exports) {
              'use strict';
              exports.__esModule = true;
              exports['default'] = reduxThunk;//这是最终的注入
              function reduxThunk(_ref) {
                var dispatch = _ref.dispatch;
                var getState = _ref.getState;
                return function (next) {// return function (store.dispatch) 
                  return function (action) {
                    if (typeof action === 'function') {
                      return action(dispatch, getState);
                    }
                    return next(action);
                  };
                };
              }
          }
        ])
    }
  );
 
四、React-Router3、4版本的区别
来源:https://zhuanlan.zhihu.com/p/28585911
1、V3版用法
  const PrimaryLayout = props =>
    <div className="primary-layout">
      <header>Our React Router 3 App</header>
      <ul>
        <li>
          <Link to="/home">Home</Link>
        </li>
        <li>
          <Link to="/user">User</Link>
        </li>
      </ul>
      <main>
        {props.children}
      </main>
    </div>;
  const App = () =>
    <Router history={browserHistory}>
      <Route component={PrimaryLayout}>
        <Route path="/home" component={HomePage} />
        <Route path="/user" component={UsersPage} />
      </Route>
    </Router>;
  render(<App/>, document.getElementById("root"));
2、v4版用法
  const PrimaryLayout = () =>
    <div className="primary-layout">
      <header>Our React Router 4 App</header>
      <ul>
        <li>
          <Link to="/home">Home</Link>
        </li>
        <li>
          <Link to="/User">User</Link>
        </li>
      </ul>
      <main>
        <Route path="/home" exact component={HomePage} />
        <Route path="/user" component={UsersPage} />
      </main>
    </div>;
  const App = () =>
    <BrowserRouter>
      <PrimaryLayout />
    </BrowserRouter>;
  render(<App/>, document.getElementById("root"));

五、react-redux的实际运用,redux,react-redux,redux-thunk
1、react-redux把redux状态和React-Router路由连起来
  ReactDOM.render(
    <Provider store={store}>
      <Router history={browserHistory}>
        <Route component={PrimaryLayout}>
          <IndexRoute component={HomePage} />//在IndexRoute的所有兄弟路由都没有激活时,该组件才显示。
          <Route path="/user" component={UsersPage} />
        </Route>
      </Router>
    </Provider>,
    document.getElementById('app')
  );
2、React-Redux将所有组件分成两大类:UI组件和容器组件
(1)组件定义
  import React, {Component} from 'react';
  class StartList extends Component {  //var Button = React.createClass({
    state = {
      visible: false
    }
    showModal(){
      //如果mapDispatchToProps缺失,那么this.props.dispatch将出现
      this.props.dispatch(action);
      this.setState({
        visible: true,
      });
    }
    componentWillMount(){}
    componentDidUpdate(){}
    componentWillUnmount(){}
    render() {
      const { detail, todos, active, closeDialogue } = this.props;
      return (
        <div>
          <div>
            <div>this.props之-容器标签-携带的内容</div>
            <div>{detail}</div>
          </div>
          <div>
            <div>this.props之-mapStateToProps-携带的内容</div>
            <div>{todos}</div>
            <div>{active}</div>
          </div>
          <div>
            <div>this.props之-mapDispatchToProps-携带的内容</div>
            <button onClick={ closeDialogue }>解构-使用</button>
            <button onClick={ this.props.openDialogue }>不解构-使用</button>
            <button onClick={ this.showModal.bind(this) }>组件内部函数的调用</button>
          </div>
        </div>
      )
    }
  }
  //mapStateToProps,负责输入逻辑,即将state映射到UI组件的props里
  function mapStateToProps (state, ownProps) {//ownProps(容器组件的属性对象)
    return {
      todos: state.todos//todos: getVisibleTodos(state.todos, state.visibilityFilter)
      active: ownProps.filter === state.visibilityFilter
    }
  }
  //mapDispatchToProps,负责输出逻辑,即将用户对UI组件的操作映射成Action
  //mapDispatchToProps,为对象时,有key和value,key为组件的属性,value为function,即action creator,返回值为action
  const mapDispatchToProps = {
    closeDialogue: function(ownProps){
      return {
        type: 'dialogue',
        filter: ownProps.filter
      }
    }
  }
  //mapDispatchToProps,为函数时,返回值为对象,有key和value,key为组件的属性,value为function,执行过程中会dispatch action
  function mapDispatchToProps (dispatch, ownProps) {//ownProps(容器组件的属性对象)
    return {
      openDialogue: function(){
        //1、同步请求时,此处只有下面这些代码
        //2、异步请求时,此处将 ownProps 里的部分数据作为参数,向后台发送请求,获取返回值 result,在成功的回调里执行下面这些代码
        dispatch({
          type: 'detail',
          data: ownProps.result
        });
      },
    };
  } 
  const LastList =connect(mapStateToProps,mapDispatchToProps)(StartList);
  export default LastList
(2)组件使用
  <LastList detail="详情"/>
(3)组件说明
  A、StartList,UI组件,负责UI的呈现,即UI本身、UI接收数据和UI派发行为,由用户提供
  B、LastList,容器组件,负责管理数据和业务逻辑,由React-Redux自动生成,接收数据和派发行为

六、react基础
来源,https://github.com/facebook/react/blob/main/CHANGELOG.md#1702-march-22-2021
来源:https://blog.csdn.net/liuqiao0327/article/details/107297106
来源,https://blog.csdn.net/Sheng_zhenzhen/article/details/105336328
1、React主要版本发布时间
(1)React,0.3.0版,2013年05月29日
(2)React,0.14.8版,2016年03月29日
(3)React,15.0.0版,2016年04月07日
(4)React,16.0.0版,2017年09月26日,新增componentDidCatch(处理错误),
  新增纤维fiber架构,弃用旧虚拟DOM,解决了递归调用无法中断和卡顿掉帧的问题
(5)React,16.3.0版,2018年03月29日,沿用方案和新增方案只能二选一,
  沿用旧生命周期componentWillMount,componentWillReceiveProps,componentWillUpdate,
  新增新生命周期getDerivedStateFromProps,getSnapshotBeforeUpdate,
(6)React,16.8.0版,2019年02月06日,
  新增钩子函数Hooks,避免组件继承React实例,实现状态管理,弃用生命周期
(7)React,17.0.0版,2020年10月20日
(8)React,18.0.0版,2022年03月29日,
  新增了并发concurrency之时间切片(time slicing),包含悬挂Suspense、过渡useTransition、延迟值useDeferredValue
2、React18并发
  来源,https://blog.csdn.net/Android_boom/article/details/124711014
  来源,https://www.cnblogs.com/lucky1010/p/15263066.html
(1)与并发相关的概念
  并发,一台处理器上,两个或多个程序在同一时间间隔运行,一个时刻只有一个程序运行  
  并行,多台处理器上,两个或多个程序在同一时间间隔运行,一个时刻也有两个或多个程序运行  
  异步,不用等所有操作等做完,就可以做其他的处理
  同步,必须等所有的操作都做完,才可以做其他的处理 
  串行,就是程序代码从上到下按顺序执行,如果遇到io阻塞就等待
  并发、并行是硬件CPU的处理机制,异步、同步是软件的处理机制--自悟
(2)帧
  定义,浏览器1s刷新60次,即16.6ms刷新1次,渲染1帧画面
  内容,浏览器1帧里回调的执行顺序为,用户事件、js代码、scroll|resize回调、requestAnimationFrame回调、渲染界面、(有剩余时间时执行)requestIdleCallback回调
  内容,浏览器1帧里回调的执行顺序为,JS操作、requestAnimationFrame回调、渲染界面、(有剩余时间时执行)requestIdleCallback回调
(3)从猜想到可行方案
  猜想,React把更新(如200ms)拆分成多个小更新(如40个5ms的更新),放到浏览器每一帧的requestIdleCallback中执行,实现回调与更新的并发
  缺陷,requestIdleCallback的兼容性差,放弃这个并发方案
  解决,React团队的并发方案--时间切片(time slicing)
(4)并发方案之时间切片的实现原理
  更新开始,react18在当前帧记录开始时间,5ms后,开启一个“5ms更新”的宏任务,浏览器会将宏任务分配到当前帧(无JS操作)或下一帧(有JS操作)执行
(5)执行栈
  (1)同步任务,进入主线程
  (2)异步任务,放入Event Table,注册回调函数,放入Event Queue,主线程的任务执行完毕,去Event Queue读取对应的函数,进入主线程执行
(6)异步任务
  微任务,先执行,比如MutationObserver、Promise、async/await
  宏任务,后执行,比如渲染事件、交互事件、定时器事件、网络请求完成事件、文件读写完成事件
3、真实DOM、虚拟DOM
(1)真实DOM,在其他的情况下,用户每次操作DOM(文档对象模型),都会改变逻辑树,都会引起页面的重新渲染
(2)虚拟DOM,在react情况下,用户每次操作DOM(文档对象模型),都会改变虚拟DOM(Virtual DOM,堆内存的一个JS对象,包含很多虚拟节点VNode,Virtual Node)
4、React的缺点和优点  
(1)缺点:
  React本身只是一个V,不是一个完整的框架,不是一套完整的框架
  需要加上React-Router和React-Redux才能成为一套完整的框架
(2)优点:
  单向数据流动
  虚拟DOM取代物理DOM作为操作对象
  用JSX语法取代HTML模板,在JavaScript里声明式地描述UI 
5、React组件3个部分
(1)React组件基本上由3个部分组成--属性(props)、状态(state)以及生命周期方法
(2)React组件一旦接收到的参数(即props)或自身状态有所改变,React组件就会执行相应的生命周期方法,最后渲染
(3)整个过程完全符合传统组件所定义的组件职责(“属性更新”与“状态改变”)
(4)以上内容来自《深入React技术栈》第18和30页。
6、react组件的key属性('key'、"key"、‘key’、“key”)
(1)key属性的作用,标识节点,让Diff算法更高效地识别节点、更新虚拟DOM
(2)index不能做key,用index做key时,新增或删除节点的操作,会使一个节点使用另一节点的index,进而使用它的key,进而使用它的data,进而产生错误
7、纯函数与副作用函数
(1)纯函数,参数相同,返回值相同,不修改全局变量
(2)副作用函数,参数相同,返回值不同,如数组的方法:push、unshift,返回值为新数组的长度,另外可能修改全局变量
8、ref(在标签中设置)
(1)示例1,值为字符串,直接通过ref获取DOM中的值
  <input type="text" ref="thisRef"/>
  getInputValue(){
    cnosole.log(this.refs.thisRef.value)
  }
(2)示例3,值为内置构造函数createRef的实例,通过该实例获取DOM实例(官方推荐)
  this.thisRef = createRef();
  <input type="text" ref={this.thisRef} /> 
  getInputValue(){
    cnosole.log(this.refs.thisRef.value)
  }
(3)示例2,值为函数,通过自定义函数的参数获取DOM实例,函数执行的时机为:
  组件被挂载后,回调函数被立即执行,回调函数的参数为该组件的具体实例。
  组件被卸载或者原有的ref属性本身发生变化时,回调也会被立即执行,此时回调函数参数为null,以确保内存泄露。
  <input type="text" ref={getRef}/>
  this.thisRef = null;
  getRef(ref){
    this.thisRef = ref
  }
  getInputValue(){
    cnosole.log(this.refs.thisRef.value) 
  }
9、状态提声(父组件的函数作为属性传给子组件)
(1)在父组件的constructor中定义状态
(2)在父组件的方法中执行this.setState({})
(3)把父组件的方法作为属性fromParent传给子组件
(4)在子组件的方法中加上this.props.fromParent(e.target.value);
(5)触发子组件的事件,执行子组件的方法,改变父组件的状态
10、跨多级组件传参原理,以React-Redux源码定义组件Provider为例
来源,http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_three_react-redux.html
(1)定义组件Provider及getChildContext
  class Provider extends Component {
    getChildContext() {
      return {
        store: this.props.store
      };
    }
    render() {
      return this.props.children;
    }
  }
  Provider.childContextTypes = {
    store: React.PropTypes.object
  }
(2)在入口文件中调用组件Provider,并传入数据
  import { Provider } from 'react-redux'
  import { createStore } from 'redux'
  import allStates from './reducers'
  import App from './components/App'
  let store = createStore(allStates);
  render(
    <Provider store={store}>
      <App />
    </Provider>,
    document.getElementById('root')
  )
(3)后代组件获取数据
  class VisibleTodoList extends Component {
    componentDidMount() {
      const { store } = this.context;
      this.unsubscribe = store.subscribe(() =>
        this.forceUpdate()
      );
    }
    render() {
      const props = this.props;
      const { store } = this.context;
      const state = store.getState();
      // ...
    }
  }
  VisibleTodoList.contextTypes = {
    store: React.PropTypes.object
  }

七、组件和表单
1、组件
(1)约束性组件(受控组件)
  <input type="text" value={this.state.name} onChange={this.handleChange} />
  handleChange: function(e) {
    this.setState({name: e.target.value});
  }
  //用户输入内容A>触发onChange事件>handleChange中设置state.name="A"渲染input使他的value变成A
(2)非约束性组件(非受控组件)
  <input type="text" defaultValue="a" />//用户输入A -> input 中显示A 
2、表单
this.state = {
  name:'',
  sex:'',
  city:'',
  citys:[
    '北京','上海','深圳'
  ],
  hobby:[
    {
      'title':"睡觉",
      'checked':true
    },
    {
      'title':"吃饭",
      'checked':false
    },
    {
      'title':"敲代码",
      'checked':true
    }
  ],
};
(1)输入框示例
  handelName(event){
    this.setState({
      name:event.target.value
    })
  }
  <input type="text" value={this.state.name} onChange={this.handelName}/>
  (2)单选框示例
  changeSex(event){
    this.setState({
      sex:event.target.value
    })
  }
  <input type="radio" value="男" checked={this.state.sex=="男"} onChange={this.changeSex}/>
  <input type="radio" value="女" checked={this.state.sex=="女"} onChange={this.changeSex}/> 
(3)下拉框示例
  getCity(event){
    this.setState({
      city:event.target.value
    })
  }
  {
    <select value={this.state.city} onChange={this.getCity}>
      {
        this.state.citys.map(function (value,key) {
          return <option key={key}>{value}</option>
        })
      }
    </select>
  }
(4)多选框示例
  changeHobby(key){
    var hobby=this.state.hobby;
    hobby[key].checked=!hobby[key].checked;
    this.setState({
      hobby:hobby
    })
  }
  {
    this.state.hobby.map(function(value,key){
      return (
        <span key={key}>
          <input type="checkbox" checked={value.checked} onChange={this.changeHobby.bind(this,key)}/>{value.title}
        </span>
      )
    })
  }

八、setState
1、this.setState接收两种参数
(1)对象+函数(可选):传入的对象浅层合并到新的state中
(2)函数+函数(可选):第1个参数函数接受2个参数,第1个参数是当前state,第2个参数是当前props,该函数返回1个对象,是要修改的state;第2个参数函数在state改变后触发,参数为更新后的state
2、this.setState调用后,发生了什么? 
(1)React将参数对象与组件状态合并, 构建新的虚拟DOM树, 自动计算出新老虚拟DOM树的节点差异, 最后根据差异对界面进行最小化渲染
3、this.setState何时渲染,_compositeLifeCycleState是否为null,来决定是否重新渲染,因此在有的生命周期里,会产生死循环
4、this.setState何时同步何时异步?
(1)异步,由react控制的事件,如onChange、onClick、onTouchMove等处理程序,React出于性能考虑,并不会立马执行修改state,而是先把当前更新state对象以及后续的state对象合并到一起,多次调用会合并,最后一次渲染,以此来进行性能优化
  state = { count: 1 }
  this.setState({
    count: this.state.count + 1,
  })
  this.setState({
    count: this.state.count + 2,
  })
  console.log(this.state.count)//1,不能立马获取更新后的值
  this.setState({count: 20}, function(){//函数先放在队列里,等状态更改完毕后,依次传入本次的状态值并执行列队
    console.log(this.state.count);//20,文本已经被改变
  })
  componentDidUpdate() {
    console.log(this.state.count);
  }
  componentDidMount(){
    console.log(this.state.count)
  }
(2)同步,react控制之外的事件中调用setState是同步更新的。比如原生js绑定的事件、setTimeout/setInterval、await等
  state = {
    count: 1,
  }
  setTimeout(function(){
    this.setState({
      count: this.state.count + 1,
    })
    console.log(this.state.count)//2
  })

九、React生命周期
1、实例化
(1)React旧写法:getDefaultProps(){return{}};//获取默认属性
  //React16.3写法:getDefaultProps(){return{}}; 
(2)React旧写法:getInitialState(){return{}};
  //React16.3写法:constructor
(3)React旧写法:componentWillMount//执行setState会合并到初始化状态中;获取从属性生成的状态;此后生命状态会被重置为null;
  //React16.3写法:getDerivedStateFromProps,让组件在props变化时更新state,该方法返回一个对象用于更新state,如果返回null则不更新任何内容
(4)render//执行setState会发起updateComponent导致-死循环
(5)componentDidMount//执行setState,执行“更新完成”钩子,而不是“加载完成”钩子,所以不会导致-死循环;这是发起异步请求去API获取数据的绝佳时期
2、存在期
(1)React旧写法:componentWillReceiveProps//执行setState会合并到状态中;此后生命状态会被重置为null
  //React16.3写法:getDerivedStateFromProps,让组件在props变化时更新state,该方法返回一个对象用于更新state,如果返回null则不更新任何内容
(2)shouldComponentUpdate//执行setState会发起updateComponent导致-死循环
(3)React旧写法:componentWillUpdate//执行setState会发起updateComponent导致-死循环
  //React16.3写法:此处没有componentWillUpdate
(4)render//执行setState会发起updateComponent导致-死循环
  //React16.3写法:此处新增getSnapshotBeforeUpdate,可以访问更新前的props和state,执行setState会发起updateComponent导致-死循环
(5)componentDidUpdate//可以有条件地执行setState
3、销毁期
(1)componentWillUnmount//等待页面卸载,改变state没意义。

十、Hooks详解,redux,react-redux,redux-thunk
来源,https://blog.csdn.net/weixin_45654582/article/details/121058178
1、Hook产生的背景
(1)React的组件创建方式,一种是(有状态)类组件,一种是(无状态)纯函数组件,后者没有状态、生命周期、this
(2)类组件缺点,每个组件都要继承React实例
(3)纯函数组件缺点,没法进行状态管理
2、Hook特征
(1)组件都用函数声明,state都用预函数管理
(2)组件没有this和生命周期
(3)可以使用外部功能和副作用
(4)Hook函数主要有5个,useState、useReducer、useContext、useEffect、useRef
3、useState,状态钩子
(1)纯函数组件没有状态,useState用于为函数组件引入state状态,并进行状态数据的读写操作
(2)参数: 初始值
(3)返回值: 数组,包含2个元素,第1个为当前值,第2个为更新状态值的函数setState,
  import React,{ useState } from "react";
  const NewCount = ()=> {
    const [ count,setCount ] = useState(0)
    addCount = ()=> {
      let newCount = count;
      setCount(newCount +=1)
    }
    return (
      <div>
        <p> { count }</p>
        <button onClick={ addCount }>Count++</button>
      </div>
    )
  }
  export default NewCount;
(4)用户调用setState时,参数为值或函数,比如setState(newValue),setState(value => newValue) 
4、useReducer,状态管理钩子,1次管理1到多个状态
(1)参数:共2个,第1个为reducer函数,第2个为状态的初始值
(2)返回值:1个数组,第1项为当前的状态值,第2项为dispatch函数
  import  { useReducer } from "react";
  const HookReducer = () => {
    const reducer = (state, action) => {
      if (action.type === 'add') {
        return {
          ...state,
          count: state.count + 1
        }
      } else if (action.type === 'minus') {
        return {
          ...state,
          count: state.num - 1
        }
      } else {
        return state
      }
    }
    const [state, dispatch] = useReducer(reducer, { count: 0, num: 0 })
    const addCount = () => {
      dispatch({ type: 'add' })
    }
    const minusCount = () => {
      dispatch({ type: 'minus' })
    }
    return ( 
      <>
        <p>{state.count}{state.num}</p> 
        <button onClick = {addCount} > useReducer < /button> 
        <button onClick = {minusCount} > minusCount < /button> 
      </>
    )
  }
  export default HookReducer;
5、useContext,状态共享钩子,类似于getChildContext
(1)参数: 1个对象
(2)返回值: 1个对象,即上面的参数对象
(3)父组件定义
  const AppContext = React.createContext();
  const HookTest = ()=> {
    const [num, setNum] = useState(0);
    return (
      <AppContext.Provider value={{ num, setNum }}> // <AppContext.Provider value={{ name: '张三'}}>
        <A context={AppContext}/>
        {A({context: AppContext})}
      </AppContext.Provider>
    )
  }
  export default HookTest;
(4)子组件定义
  import React, { useContext } from "react";
  const A = (props)=> {
    const { num, setNum } = useContext(AppContext); // const { name, setName } = useContext(props.context)
    const onClick = () => {
      setNum(num => num + 1);
    };
    return (
      <p>
        <span>{ name }</span>
        <span>{ name }</span>
        <button onClick={onClick}>点击</button>
      </p>
    )
  }
6、useEffect,副作用钩子
(1)相当于componentDidMount、componentDidUpdate、componentWillUnmount这三个生命周期
(2)useEffect接受两个参数,其返回值是第一个参数的返回值
  第1个参数是异步函数,在挂载阶段执行,其返回值也是useEffect的返回值,在卸载阶段执行,清理副作用
  第2个参数是一个Effect依赖项数组
  Effect的执行时机
  第2个参数缺失时,组件每次渲染后(等同于render),Effect执行
  第2个参数为数组时,组件挂载后(等同于componentDidMount)或数组发生变化,Effect执行
(3)示例
  import { useEffect } from 'react'
  function Counter() {
    const [count, setCount] = useState(0);
    const [step, setStep] = useState(1);
    useEffect(//这不是函数的定义,是函数的调用,可以多次执行,每次执行对应一个监听数据
      function(){
        console.log('类似于componentDidMount,通常在此处调用api获取数据')
        console.log('类似于componentDidUpdate,当count发生改变时执行')
        const id = setInterval(function(){
          setCount(function(count){
            return count + step
          });
        },1000);
        return function(){ 
          clearInterval(id);
          console.log('类似于componentWillUnmount,通常用于清除副作用');
        }
      }, [step]
    ) 
    return (
      <div>
        <h1>{count}</h1>
        <input value={step} onChange={e => setStep(Number(e.target.value))} />
      </div>
    );
  }
7、useRef,引用钩子
(1)参数:初始值
(2)返回值:返回一个只有1个属性{current: initialValue}的可变对象,该对象在整个生命周期内一直存在,更新current值,不会重渲染,
(3)useRef在useEffect或event handler里更新
(4)useRef类似于类组件的this
  import{ useRef,useEffect} from "react";
  const RefComponent = () => {
    let inputRef = useRef(initialValue);
    useEffect(() => {
      inputRef.current.focus();
    })
    return (
      <input type="text" ref={inputRef}/>
    ) 
  }

十一、react性能优化
1、使用React.memo记忆组件,很像PureComponent,函数组件和类组件都行
来源:https://blog.csdn.net/qq_48637854/article/details/125135034
(1)父组件中state(状态)改变,子组件也会重新渲染
(2)memo函数包起来的组件,父组件只有改变它的props才会重新渲染它
(3)const MyComponent = React.memo(function MyComponent(props) {
    /* 只在props更改的时候才会重新渲染 */
  });
2、使用React.PureComponent纯组件,对状态和props数据进行浅层比较,相同则组件不会重新渲染
  来源,https://blog.csdn.net/qq_38629292/article/details/128571036
  import React from 'react';
  export default class Children extends React.PureComponent {
    render() {
      console.log('Children 被重新绘制');
      return <div>Children component</div>;
    }
  }
3、使用shouldComponentUpdate生命周期事件
4、不要使用内联函数定义,即不把函数定义在html标签里
5、懒加载组件

十二、React组件写法
1、React15.6.1版组件写法
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8" />
      <title>React实例</title>
      <script src="https://lib.baomitu.com/react/15.6.1/react.js"></script>
      <script src="https://lib.baomitu.com/react/15.6.1/react-dom.js"></script>
      <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    </head>
    <body>
      <div id="example"></div>
    </body>
  </html>
  <script type="text/babel">
    var Button = React.createClass({  // class StartList extends Component {
      setNewNumber(number,event) {
        this.setState({number: this.state.number + 1})
      },
      getDefaultProps() {
        return { name: "计数器" };
      },
      getInitialState() {
        return{number: 0};
      },
      render() {
        return (
          <div>
            <button onClick = {this.setNewNumber.bind(null,this.state.number,event)}>点击{this.props.name}</button>
            <Text myNumber = {this.state.number}></Text>
          </div>
        );
      }
    })
    var Text = React.createClass({
      //一、以下实例化时期
      getDefaultProps() {
        console.log("1.getDefaultProps 获取默认属性");
        return { };
      },
      getInitialState() {
        console.log("2.getInitialState 获取初始状态");
        return { };
      },
      componentWillMount() {
        console.log("3.componentWillMount 此组件将要被渲染到目标组件内部");
      },
      componentDidMount() {
        console.log("5.componentWillMount 此组件已经被渲染到目标组件内部");
      },
      //二、以下存在时期
      componentWillReceiveProps() {
        console.log("6.componentWillReceiveProps 此组件将要收到属性");
      },
      shouldComponentUpdate(newProps, newState) {
        console.log("7.shouldComponentUpdate 组件是否应该被更新");
        return true;
      },
      componentWillUpdate() {
        console.log("8.componentWillUpdate 组件将要被更新");
      },
      componentDidUpdate() {
        console.log("10.componentDidUpdate 组件已经更新完成");
      },
      //三、以下销毁时期
      componentWillUnmount() {
        console.log("11.componentWillUnmount 组件将要销毁");
      },
      render() {
        console.log("4和9.render 组件将要渲染");
        return (
          <div>
            <h3>{this.props.myNumber}</h3>
          </div>
        );
      }
    })
    ReactDOM.render(
      <div>
          <Button />
      </div>,
      document.getElementById('example')
    );
  </script>
2、React16.4.0版组件写法
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8" />
      <title>React实例</title>
      <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
      <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
      <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    </head>
    <body>
      <div id="example"></div>
    </body>
  </html>
  <script type="text/babel">
    class Button extends React.Component {
      //name="计算器";state = {number: 0};
      //上下写法,二选一
      constructor(props) {
        super(props);
        this.name="计算器";
        this.state = {number: 0};
      };
      setNewNumber(number,event) {
        this.setState({number: this.state.number + 1})
      };
      render() {
        return (
          <div>
            <button onClick = {this.setNewNumber.bind(this,this.state.number,event)}>点击{this.name}</button>
            <Text myNumber = {this.state.number}></Text>
          </div>
        );
      }
    }
    class Text extends React.Component {
      //一、以下实例化时期
      constructor(props) {
        super(props);
        console.log("2.constructor 获取初始状态");
      }
      componentWillMount() {
        console.log("3.componentWillMount 此组件将要被渲染到目标组件内部");
      }
      componentDidMount() {
        console.log("5.componentWillMount 此组件已经被渲染到目标组件内部");
      }
      //二、以下存在时期
      componentWillReceiveProps() {
        console.log("6.componentWillReceiveProps 此组件将要收到属性");
      }
      shouldComponentUpdate(newProps, newState) {
        console.log("7.shouldComponentUpdate 组件是否应该被更新");
        return true;
      }
      componentWillUpdate() {
        console.log("8.componentWillUpdate 组件将要被更新");
      }
      componentDidUpdate() {
        console.log("10.componentDidUpdate 组件已经更新完成");
      }
      //三、以下销毁时期
      componentWillUnmount() {
        console.log("11.componentWillUnmount 组件将要销毁");
      }
      render() {
        console.log("4和9.render 组件将要渲染");
        return (
          <div>
            <h3>{this.props.myNumber}</h3>
          </div>
        );
      }
    }
    ReactDOM.render(
      <div>
          <Button />
      </div>,
      document.getElementById('example')
    );
  </script>
  来源 https://www.runoob.com/try/try.php?filename=try_react_life_cycle2
 
十三、插槽
1、portal插槽
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8" />
      <title>React插槽实例</title>
      <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
      <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
      <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    </head>
    <body>
      <div><span>这是器内:</span><span id="container"></span></div>
      <div style="margin:20px;"></div>
      <div><span>这是器外:</span><span id="outer"></span></div>
    </body>
    <script type="text/babel">
      const container = document.getElementById('container');
      const outer = document.getElementById('outer');
      class Model extends React.Component {
        constructor(props) {
          super(props);
          this.span = document.createElement('span');
        }
        render() {
          return ReactDOM.createPortal(
            this.props.children,
            this.span
          );
        }
        componentDidMount() {
          outer.appendChild(this.span);
        }
        componentWillUnmount() {
          outer.removeChild(this.span);
        }
      }
      class Parent extends React.Component {
        constructor(props) {
          super(props);
          this.state = {clicks: 0};
          this.handleClick = this.handleClick.bind(this);
        }
        handleClick() {
          this.setState(state => ({
            clicks: state.clicks + 1
          }));
        }
        render() {
          return (
            <span onClick={this.handleClick}>
              <button>器内正常内容仍在器内</button>
              <span style={{paddingLeft:"8px"}}>{this.state.clicks}</span>
              <Model>
                <span>
                  <button>器内插槽内容置于器外</button>
                  <span style={{paddingLeft:"8px"}}>器内插槽内容置于器外</span>
                </span>
              </Model>
            </span>
          );
        }
      }
      ReactDOM.render(<Parent/>, container);
    </script>
  </html>
2、React.Children插槽
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8" />
      <title>React插槽实例</title>
      <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
      <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
      <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
    </head>
    <body>
      <div id="container"></div>
      <div id="outer"></div>
    </body>
    <script type="text/babel">
      class Parent extends React.Component {
        constructor(props) {
          super(props);
          this.state = {clicks: 0};
          this.handleClick = this.handleClick.bind(this);
        }
        handleClick(event) {
          console.log(event)
          this.setState(state => ({
            clicks: state.clicks + 1
          }));
        }
        render() {
          var that = this;
          return (
            <div>
              <div>{this.state.clicks}</div>
              <div><button onClick={this.handleClick}>clicks</button></div>
              <ul>
                {
                  React.Children.map(this.props.children,function(item,index){
                    if(index !=1){
                      return <li onClick={that.handleClick}>{item}</li>
                    }else{
                      return <li onClick={that.handleClick}>{item}---{index+1}</li>
                    }
                  })
                }
              </ul>
            </div>
          );
        }
      }
      ReactDOM.render(<Parent>
        <span style={{cursor:'pointer',userSelect: 'none'}}>插槽一</span>
        <span style={{cursor:'pointer',userSelect: 'none'}}>插槽二</span>
        <span style={{cursor:'pointer',userSelect: 'none'}}>插槽三</span>
      </Parent>, document.getElementById('container'));
    </script>
  </html>

  十四、Model多层弹窗
  1、不可拖拽(含插槽)
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8" />
      <title>React弹窗实例</title>
      <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
      <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
      <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
      <style>
        .simpleDialog {
          position: fixed;
          width: 100%;
          height: 100%;
          top: 0;
          left: 0;
          display: none;
          justify-content: center;
          align-items: center;
        }
        .simpleDialog .mask {
          position: fixed;
          width: 100%;
          height: 100%;
          top: 0;
          left: 0;
          background: black;
          opacity: 0.5;
        }
        .simpleDialog .content {
          position: fixed;
          background: white;
          opacity: 1;
          display: flex;
          flex-direction: column;
        }
        .simpleDialog .content .title {
          display: flex;
          background: blue;
          color: white;
          padding: 10px;
          cursor: pointer;
        }
        .simpleDialog .content .title {
          display: flex;
          background: blue;
          color: white;
          padding: 10px;
          cursor: pointer;
        }
        .simpleDialog .content .conform {
          display: flex;
          justify-content: center;
          padding: 10px;
          background: blue;
        }
      </style>
    </head>
    <body>
      <div id="container"></div>
    </body>
  </html>
  <script type="text/babel">
    const container = document.getElementById('container');
    class Model extends React.Component {
      constructor(props) {
        super(props);
      }
      componentDidUpdate(){
        var id = this.props.id||'simpleDialog';
        if(this.props.isShow){
          document.getElementById(id).style.display = 'flex';
        }else{
          document.getElementById(id).style.display = 'none';
        }
      }
      close() {
        if(this.props.close){
          this.props.close()
        }
      }
      open() {
        if(this.props.open){
          this.props.open()
        }
      }
      render() {
        return (
          <div>
            <div className="simpleDialog" id={this.props.id||'simpleDialog'}>
              <div className="mask"></div>
              <div className="content">
                <div className="title">
                  <span>系统消息</span>
                </div>
                <div style={{width:this.props.width||'800px',height:this.props.height||'600px'}}>
                  {
                    React.Children.map(this.props.children,function(item,index){
                      return item
                    })
                  }
                </div>
                <div className="conform">
                  <button onClick={this.close.bind(this)}>关闭</button>
                  <button onClick={this.open.bind(this)}>打开</button>
                </div>
              </div>
            </div>
          </div>
        );
      }
    }
    class Parent extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          outShow: false,
          midShow: false,
          inShow: false,
        };
      }
      outOpen() {
        this.setState({
          outShow: true
        });
      }
      outClose() {
        this.setState({
          outShow: false
        });
      }
      midOpen() {
        this.setState({
          midShow: true
        });
      }
      midClose() {
        this.setState({
          midShow: false
        });
      }
      inOpen() {
        this.setState({
          inShow: true,
        });
      }
      inClose() {
        this.setState({
          inShow: false,
        });
      }
      render() {
        return (
          <div>
            <div><button onClick={this.outOpen.bind(this)}>出现弹窗</button></div>
            <Model isShow={this.state.outShow} open={this.midOpen.bind(this)} close={this.outClose.bind(this)} id="simpleDialog1" width="900px" height="600px">
              这是插槽1
              <Model isShow={this.state.midShow} open={this.inOpen.bind(this)} close={this.midClose.bind(this)} id="simpleDialog2" width="600px" height="400px">
                这是插槽2
                <Model isShow={this.state.inShow} close={this.inClose.bind(this)} id="simpleDialog3" width="300px" height="200px">
                  这是插槽3
                </Model>
              </Model>
            </Model>
          </div>
        );
      }
    }
    ReactDOM.render(<Parent/>, container);
  </script>
  2、可拖拽(含插槽)
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8" />
      <title>React弹窗实例</title>
      <script src="https://cdn.staticfile.org/react/16.4.0/umd/react.development.js"></script>
      <script src="https://cdn.staticfile.org/react-dom/16.4.0/umd/react-dom.development.js"></script>
      <script src="https://cdn.staticfile.org/babel-standalone/6.26.0/babel.min.js"></script>
      <script>
        function drag(wholeTitleId, wholeContentId) {
          var wholeTitleId = wholeTitleId||'titleId';
          var wholeContentId = wholeContentId||'contentId';
          var oDiv = document.getElementById(wholeContentId);
          if(!oDiv) return;
          oDiv.onmousedown = down;
          function processThis(fn, nowThis) {
            return function (event) {
              fn.call(nowThis, event);
            };
          }
          function down(event) {
            event = event || window.event;
            if (event.target.id != wholeTitleId) return;
            this.initOffsetLeft = this.offsetLeft;
            this.initOffsetTop = this.offsetTop;
            this.initClientX = event.clientX;
            this.initClientY = event.clientY;
            this.maxOffsetWidth =
              (document.documentElement.clientWidth || document.body.clientWidth) -
              this.offsetWidth;
            this.maxOffsetHeight =
              (document.documentElement.clientHeight ||
                document.body.clientHeight) - this.offsetHeight;
            if (this.setCapture) {
              this.setCapture();
              this.onmousemove = processThis(move, this);
              this.onmouseup = processThis(up, this);
            } else {
              document.onmousemove = processThis(move, this);
              document.onmouseup = processThis(up, this);
            }
          }
          function move(event) {
            var nowLeft = this.initOffsetLeft + (event.clientX - this.initClientX);
            var nowTop = this.initOffsetTop + (event.clientY - this.initClientY);
            this.style.left = nowLeft + 'px';
            this.style.top = nowTop + 'px';
          }
          function up() {
            if (this.releaseCapture) {
              this.releaseCapture();
              this.onmousemove = null;
              this.onmouseup = null;
            } else {
              document.onmousemove = null;
              document.onmouseup = null;
            }
          }
        }; 
      </script>
      <style>
        .simpleDialog {
          position: fixed;
          width: 100%;
          height: 100%;
          top: 0;
          left: 0;
          display: none;
          justify-content: center;
          align-items: center;
        }
        .simpleDialog .mask {
          position: fixed;
          width: 100%;
          height: 100%;
          top: 0;
          left: 0;
          background: black;
          opacity: 0.5;
        }
        .simpleDialog .content {
          position: fixed;
          background: white;
          opacity: 1;
          display: flex;
          flex-direction: column;
        }
        .simpleDialog .content .title {
          display: flex;
          background: blue;
          color: white;
          padding: 10px;
          cursor: pointer;
        }
        .simpleDialog .content .title {
          display: flex;
          background: blue;
          color: white;
          padding: 10px;
          cursor: pointer;
        }
        .simpleDialog .content .conform {
          display: flex;
          justify-content: center;
          padding: 10px;
          background: blue;
        }
      </style>
    </head>
    <body>
      <div id="container"></div>
    </body>
  </html>
  <script type="text/babel">
    const container = document.getElementById('container');
    class Model extends React.Component {
      constructor(props) {
        super(props);
      }
      componentDidUpdate(){
        var id = this.props.id||'simpleDialog';
        var title = this.props.title||'title';
        var content = this.props.content||'content';
        if(this.props.isShow){
          document.getElementById(id).style.display = 'flex';
          drag(title,content)
        }else{
          document.getElementById(id).style.display = 'none';
        }
      }
      close() {
        if(this.props.close){
          this.props.close();
          var content = this.props.content;
          document.getElementById(content).style.cssText = "position:fixed;";
        }
      }
      open() {
        if(this.props.open){
          this.props.open()
        }
      }
      render() {
        return (
          <div>
            <div className="simpleDialog" id={this.props.id||'simpleDialog'}>
              <div className="mask"></div>
              <div className="content" id={this.props.content||'content'}>
                <div className="title" id={this.props.title||'title'}>
                  <span>系统消息</span>
                </div>
                <div style={{width:this.props.width||'800px',height:this.props.height||'400px'}}>
                  {
                    React.Children.map(this.props.children,function(item,index){
                      return item
                    })
                  }
                </div>
                <div className="conform">
                  <button onClick={this.close.bind(this)}>关闭</button>
                  <button onClick={this.open.bind(this)}>打开</button>
                </div>
              </div>
            </div>
          </div>
        );
      }
    }
    class Parent extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          outShow: false,
          midShow: false,
          inShow: false,
        };
      }
      outOpen() {
        this.setState({
          outShow: true
        });
      }
      outClose() {
        this.setState({
          outShow: false
        });
      }
      midOpen() {
        this.setState({
          midShow: true
        });
      }
      midClose() {
        this.setState({
          midShow: false
        });
      }
      inOpen() {
        this.setState({
          inShow: true,
        });
      }
      inClose() {
        this.setState({
          inShow: false,
        });
      }
      render() {
        return (
          <div>
            <div><button onClick={this.outOpen.bind(this)}>出现弹窗</button></div>
            <Model isShow={this.state.outShow} open={this.midOpen.bind(this)} close={this.outClose.bind(this)} id="simpleDialog1" title="title1" content="content1" width="900px" height="600px">
              这是插槽1
              <Model isShow={this.state.midShow} open={this.inOpen.bind(this)} close={this.midClose.bind(this)} id="simpleDialog2" title="title2" content="content2" width="600px" height="400px">
                这是插槽2
                <Model isShow={this.state.inShow} close={this.inClose.bind(this)} id="simpleDialog3" title="title3" content="content3" width="300px" height="200px">
                  这是插槽3
                </Model>
              </Model>
            </Model>
          </div>
        );
      }
    }
    ReactDOM.render(<Parent/>, container);
  </script>

十五、React.15.6.0源码外框
  /**
  * React v15.6.0
  */
  (function (allFn) {
    if (typeof exports === "object" && typeof module !== "undefined") {
      module.exports = allFn()
    } else if (typeof define === "function" && define.amd) {
      define([], allFn)
    } else {
      var tempGlobal;
      if (typeof window !== "undefined") {
        tempGlobal = window
      } else if (typeof global !== "undefined") {
        tempGlobal = global
      } else if (typeof self !== "undefined") {
        tempGlobal = self
      } else {
        tempGlobal = this
      }
      tempGlobal.React = allFn()
    }
  })(function () {
    var define, module, exports;
    return (function outerFn(first, second, third) {
      function recursion(number) {
        if (!second[number]) {
          if (!first[number]) {
            var error = new Error("Cannot find module '" + number + "'");
            throw error.code = "MODULE_NOT_FOUND", error
          }
          var module = second[number] = {
            exports: {}
          };
          first[number][0].call(module.exports, function (key) {
            var value = first[number][1][key];
            return recursion(value ? value : key)
          }, module, module.exports, outerFn, first, second, third)
        }
        return second[number].exports//在react实例化的过程中,这行代码不但因获取依赖而多次执行,而且还因获取react实例而最后执行。
      }
      for (var number = 0; number < third.length; number++) recursion(third[number]);//fn(16)第1次执行,执行结果没有变量接收
      return recursion //执行到这,整个逻辑就快结束了。前两行可以合并为一行:return recursion(third[0]),同时下面的"(48)"应当删掉。 
    })(
      { 2: [function (_dereq_, module, exports) { var thisVar = _dereq_(138) }, { "25": 25, "30": 30 }], },
      { }, 
      [16]
    )(16)// fn(16)第2次执行,因为n[num]为真,所以直接返回n[num].exports并被挂在g.React上 
  });
附、自执行
  (function (allFn) {
    allFn()
  })(function () {
    return (function outerFn(m) {
      function recursion(n) {
        console.log( n );
        return n
      }
      recursion(m)
      return recursion
    })(1)(16)
  });
 
十六、ant-design-pro脚手架的构成
Pro的底座是umi,umi是一个(基于)webpack之上的(自动化)整合工具。
Pro的核心是umi,umi的核心是webpack。
1、web 技术
2、Umi-前端应用框架(可整个或部分复用的软件)
(1)Node.js 前端开发基础环境
(2)Webpack 前端必学必会的打包工具
(3)React Router 路由库,被dva内置
(4)proxy 反向代理工具
(5)dva 轻量级的应用框架(可整个或部分复用的软件)
(6)fabric 严格但是不严苛的 lint 规则集
(7)TypeScript 带类型的 JavaScript
3、Ant Design 前端组件库
4、ProComponents 模板组件
5、useModel 简易数据流
6、编译时和运行时
(1)编译时,环境是node环境,可以使用fs,path等功能;没有使用webpack,不能使用jsx,不能引入图片
(2)运行时,编译完成,开始在浏览器环境运行,不能使用fs、path,有跨域的问题;这个环境被webpack编译过,可以写jsx,导入图片
7、Umi的插件
(1)plugin-access,权限管理
(2)plugin-analytics,统计管理
(3)plugin-antd,整合 antd UI 组件
(4)plugin-initial-state,初始化数据管理
(5)plugin-layout,配置启用 ant-design-pro 的布局
(6)plugin-locale,国际化能力
(7)plugin-model,基于 hooks 的简易数据流
(8)plugin-request,基于 umi-request 和 umi-hooks 的请求方案
8、Umi其它
(1)配置:开发配置和(浏览器)运行配置
(2)路由:配置路由和约定式路由
(3)插件:id和key,每个插件都会对应一个id和一个key,id是路径的简写,key是进一步简化后用于配置的唯一值。

十七、前端路由:url有变化,但不向后台发送请求,
1、它的作用是 
(1)记录当前页面的状态; 
(2)可以使用浏览器的前进后退功能; 
2、hash模式帮我们实现这两个功能,因为它能做到: 
(1)改变url且不让浏览器向服务器发出请求; 
(2)监测 url 的变化; 
(3)截获 url 地址,并解析出需要的信息来匹配路由规则。 
3、history 模式改变 url 的方式会导致浏览器向服务器发送请求。
4、react前端路由触发条件
(1)<Link to={item.url}>{item.location}</Link>                         
(2)browserHistory.push('/aaa')
  

  

posted @ 2020-10-26 23:42  WEB前端工程师_钱成  阅读(9888)  评论(0编辑  收藏  举报