11、React之分页组件(含勾选、过滤、表头纵横合并、子组件向父组件传值)、redux、redux-thunk、react-router、react-redux、React基础|版本与优化、Hook钩子、组件写法、插槽、Model多层弹窗组件、react.15.6源码、ant-design-pro构成、Vue与React的异同(2800行)

一、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([]);//1行表头对应1个空数组
          }
        } 
        getCenterThead(columns,initThead,index){
          var that=this;
          //被纵、横合并的单元格(th、td)及其右侧的单元格都会向右平移1格
          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)}/>      
            }
            //第1次执行时,遍历出columns的最外层内容,放进initThead的第1个数组
            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){
              //如果columns的某项有children,那就开始第2次执行,遍历的结果放进initThead的第2个数组
              that.getCenterThead(children,initThead,index+1)
            }
          })
        }  
        getLastThead(thead,initThead){
          var that=this;
          initThead.forEach(function(item,index){
            thead.push(<tr key={index}>{item}</tr>)//将每行表头放进每个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组件和容器组件,即StartList和LastList
(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自动生成,接收数据和派发行为

六、基础
1、React的缺点和优点  
  (1)缺点:
    A、React本身只是一个V,不是一个完整的框架,不是一套完整的框架
    B、需要加上React-Router和React-Redux才能成为一套完整的框架
  (2)优点:
    A、单向数据流动
    B、虚拟DOM取代物理DOM作为操作对象
    C、用JSX语法取代HTML模板,在JavaScript里声明式地描述UI 
2、React组件3个部分
  (1)3个部分,属性(props)、状态(state)以及生命周期方法
  (2)组件一旦接收到的参数(即props)或自身状态有所改变,就会执行相应的生命周期方法,最后渲染
  (3)整个过程完全符合传统组件所定义的组件职责(“属性更新”与“状态改变”)
  (4)以上内容来自《深入React技术栈》第18和30页
3、React生命周期
  (1)实例化
    A、React旧写法:getDefaultProps(){return{}};//获取默认属性
      //React16.3写法:getDefaultProps(){return{}}; 
    B、React旧写法:getInitialState(){return{}};
      //React16.3写法:constructor
    C、React旧写法:componentWillMount//执行setState会合并到初始化状态中;获取从属性生成的状态;此后生命状态会被重置为null;
      //React16.3写法:getDerivedStateFromProps,让组件在props变化时更新state,
      //该方法返回一个对象用于更新state,如果返回null则不更新任何内容
    D、render//执行setState会发起updateComponent导致-死循环
    E、componentDidMount//执行setState,执行“更新完成”钩子,而不是“加载完成”钩子,所以不会导致-死循环;
      //这是发起异步请求去API获取数据的绝佳时期
  (2)存在期
    A、React旧写法:componentWillReceiveProps//执行setState会合并到状态中;此后生命状态会被重置为null
      //React16.3写法:getDerivedStateFromProps,让组件在props变化时更新state,
      //该方法返回一个对象用于更新state,如果返回null则不更新任何内容
    B、shouldComponentUpdate//执行setState会发起updateComponent导致-死循环
    C、React旧写法:componentWillUpdate//执行setState会发起updateComponent导致-死循环
      //React16.3写法:此处没有componentWillUpdate
    D、render//执行setState会发起updateComponent导致-死循环
      //React16.3写法:此处新增getSnapshotBeforeUpdate,可以访问更新前的props和state,执行setState会发起updateComponent导致-死循环
    E、componentDidUpdate//可以有条件地执行setState
  (3)销毁期
    A、componentWillUnmount//等待页面卸载,改变state没意义
4、重要概念
  (1)纯函数,运行时不影响其他变量的函数,参数相同,返回值相同
  (2)副作用函数,运行时影响其他变量的函数
  (3)真实DOM,在其他的情况下,用户每次操作DOM(文档对象模型),都会改变逻辑树,都会引起页面的重新渲染
  (4)虚拟DOM,在react情况下,用户每次操作DOM(文档对象模型),都会改变虚拟DOM
  (5)虚拟DOM,Virtual DOM,堆内存的一个JS对象,包含很多虚拟节点VNode,Virtual Node
  (6)key属性,标识节点,让Diff算法更高效地识别节点、更新虚拟DOM;index不能做key,
    A、用index做key时,新增或删除节点的操作,会使一个节点使用另一节点的index,
    B、进而使用它的key,进而使用它的data,进而产生错误
  (7)状态提声(父组件的函数作为属性传给子组件)
    A、在父组件的constructor中定义状态
    B、在父组件的方法中执行this.setState({})
    C、把父组件的方法作为属性fromParent传给子组件
    D、在子组件的方法中加上this.props.fromParent(e.target.value);
    E、触发子组件的事件,执行子组件的方法,改变父组件的状态
  (8)ref(在标签中设置)
    A、示例1,值为字符串,直接通过ref获取DOM中的值
      <input type="text" ref="thisRef"/>
      getInputValue(){
        cnosole.log(this.refs.thisRef.value)
      }
    B、示例2,值为内置构造函数的实例,通过该实例获取DOM实例(官方推荐)
      this.thisRef = createRef();
      <input type="text" ref={this.thisRef} /> 
      getInputValue(){
        cnosole.log(this.refs.thisRef.value)
      }
    C、示例3,值为函数,通过自定义函数的参数获取DOM实例,函数执行的时机为:
      A、组件被挂载后,回调函数被立即执行,回调函数的参数为该组件的具体实例。
      B、组件被卸载或者原有的ref属性本身发生变化时,回调也会被立即执行,此时回调函数参数为null,以确保内存泄露。
        <input type="text" ref={getRef}/>
        this.thisRef = null;
        getRef(ref){
          this.thisRef = ref
        }
        getInputValue(){
          cnosole.log(this.refs.thisRef.value) 
        }
  (9)非约束性组件(非受控组件)
    <input type="text" defaultValue="a" />//用户输入A -> input 中显示A 
  (10)约束性组件(受控组件)
    <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
5、setState
  (1)this.setState接收两种参数
    A、对象+函数(可选):传入的对象浅层合并到新的state中
    B、函数+函数(可选):
      a、第1个参数函数接受2个参数,第1个参数是当前state,第2个参数是当前props,该函数返回1个对象,是要修改的state;
      b、第2个参数函数在state改变后触发,参数为更新后的state
  (2)this.setState调用后,发生了什么?
    A、React将参数对象与组件状态合并 
    B、构建新的虚拟DOM树
    C、自动计算出新老虚拟DOM树的节点差异
    D、最后根据差异对界面进行最小化渲染
  (3)this.setState何时渲染,
    A、_compositeLifeCycleState是否为null,来决定是否重新渲染
    B、因此在有的生命周期里,会产生死循环
  (4)this.setState何时同步何时异步?
    A、异步,
      a、由react控制的事件,如onChange、onClick、onTouchMove等处理程序,
      b、React出于性能考虑,并不会立马执行修改state,而是先把当前更新state对象以及后续的state对象合并到一起,多次调用会合并,
      c、最后一次渲染,以此来进行性能优化
        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)
        }
    B、同步,
      a、react控制之外的事件中调用setState是同步更新的
      b、比如原生js绑定的事件、setTimeout/setInterval、await等
        state = {
          count: 1,
        }
        setTimeout(function(){
          this.setState({
            count: this.state.count + 1,
          })
          console.log(this.state.count)//2
        })
6、跨多级组件传参原理,以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
    }
7、表单
  附、数据
    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)多选框示例
    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>
        )
      })
    }
  (4)下拉框示例
    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>
    }

七、版本与优化
  附、Derived,/diˈraivd/,派生的
  来源,https://github.com/facebook/react/releases
  所有版本简介,https://github.com/facebook/react/blob/main/CHANGELOG.md#1702-march-22-2021
  reactdom版本,https://cdn.bootcdn.net/ajax/libs/react-dom/16.6.0/cjs/react-dom.development.js
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日
    A、15.1.0版,出现错误边界,error boundaries 
    B、15.2.3版,出现纯函数组件,PureComponent 
  (4)React,16.0.0版,2017年09月26日,
    A、新增componentDidCatch(--记录错误--)
    B、新增纤维fiber架构,
    C、弃用旧虚拟DOM,
    D、解决了递归调用无法中断和卡顿掉帧的问题
  (5)React,16.3.0版,2018年03月29日,
    A、沿用旧生命周期componentWillMount,componentWillReceiveProps,componentWillUpdate,
    B、新增新生命周期getDerivedStateFromProps,getSnapshotBeforeUpdate,
    C、沿用方案、新增方案只能二选一,
    D、组件自身state更新,shouldComponentUpdate()>render()>getSnapshotBeforeUpdate()>componentDidUpdate()
    E、传递过来的props更新,getDerivedStateFromProps()>shouldComponentUpdate()>render()>getSnapshotBeforeUpdate()>componentDidUpdate()
  (6)React,16.4.0版,2018年06月24日,
    A、新增Suspense(--组件--)
  (7)React,16.6.0版,2018年10月23日,
    A、新增getDerivedStateFromError(--处理错误--)
    B、新增React.memo()//只在props更改的时候才会重新渲染
    C、新增React.lazy()//与Suspense组合使用
  (8)React,16.8.0版,2019年02月06日,见本页-Hooks详解-
    A、新增钩子函数Hooks,可以
    B、避免组件继承React实例
    C、实现状态管理
    D、弃用生命周期
  (9)React,17.0.0版,2020年10月20日
    A、并没有添加任何面向开发人员的新特性
  (10)React,18.0.0版,2022年03月29日
    A、新增useDeferredValue(--使用延迟--)//与Suspense组合使用
    B、新增useTransition(--使用过渡--)
    C、18.3.1,2024年04月26日,最新版本
2、优化方案
  (1)Error Boundaries,错误边界,
    A、componentDidCatch,记录错误
    B、getDerivedStateFromError,处理错误
      import React from 'react';
      class ErrorBoundary extends React.Component {
        state = { hasError: false };
        static getDerivedStateFromError(error) { 
          return { hasError: true };
        }
        componentDidCatch(error, errorInfo) { 
          console.error('Uncaught error:', error, errorInfo);
        }
        render() {
          if (this.state.hasError) { 
            return <h1>Something went wrong.</h1>;
          }
          return this.props.children; 
        }
      }
  (2)Suspense,异步加载,//--加载指示器--
    A、lazy
      import React, {Suspense, lazy} from 'react'; //Suspend,挂起;Suspense /səˈspens/,担心
      const fetchData = () => new Promise((resolve) =>
        setTimeout(() => resolve({ name: 'John Doe' }), 1000)
      );
      const UserDetails = lazy(() => fetchData().then(data => { 
        return { default: UserDetailsComponent, data: data }; //组件UserDetails-异步加载-组件UserDetailsComponent
      }));
      const UserDetailsComponent = (props) => {
        return <h1>Hello, {props.data.name}!</h1>;
      };
      const App = () => (
        <Suspense fallback={<div>Loading...</div>}> //
          <UserDetails />
        </Suspense>
      );
    B、useDeferredValue,在组件更新期间, 跳过复杂组件、提高渲染效率,随后使用新值重新渲染
      import { useState, Suspense, useDeferredValue } from "react";
      export default function App() {
        const [query, setQuery] = useState('');
        const deferredQuery = useDeferredValue(query);
        return (
          <>
            <label>
              Search albums:
              <input value={query} onChange={e => setQuery(e.target.value)} />
            </label>
            <Suspense fallback={<div>Loading...</div>}> 
              <SearchResults query={deferredQuery}/>
            </Suspense>
          </>
        );
      } 
  (3)useTransition,过渡,//--加载指示器--
    注、isPending,是否有待处理状态;处理完毕,自行修改;/pend/;推迟决定
    A、在ajax里调用startTransition。//替代方案,请求前后分别给isPending赋值
      import React, { useState, useTransition } from 'react';
      const App = () => {
        const [userData, setUserData] = useState(null);
        const [isPending, startTransition] = useTransition();
        const fetchUserData = async () => {
          startTransition(async () => {
            const data = await fetchUserDataAPI();
            setUserData(data);
          });
        };
        return (
          <div>
            <button onClick={fetchUserData}>Load User Data</button>
            {isPending ? <div>Loading...</div> : <div>User Data: {userData}</div>}
          </div>
        );
      };
    B、直接调用startTransition。//替代方案,用定时器
      来源,https://blog.csdn.net/weixin_48633811/article/details/134877658
      import React, { useState, useTransition } from 'react';
      function Example() {
        const [show, setShow] = useState(false);
        const [isPending, startTransition] = useTransition({ timeoutMs: 1000 });
        const handleClick = () => {
          startTransition(() => {
            setShow(!show);
          });
        };
        return (
          <div>
            <button onClick={handleClick} disabled={isPending}>
              {show ? '隐藏' : '显示'}
            </button>
            {isPending ? '正在加载...' : null}
            {show ? <div>这是要显示的内容</div> : null}
          </div>
        );
      }

八、Hooks详解,redux,react-redux,redux-thunk
 来源,https://blog.csdn.net/weixin_45654582/article/details/121058178
 附、此前React组件的缺点
  (1)(有状态)类组件的缺点,每个组件都要继承React实例
  (2)(无状态)纯函数组件的缺点,没法进行状态管理;没有状态、生命周期、this
 附、Hook特征
  (1)组件都用函数声明
  (2)state都用预函数管理
  (3)组件没有this和生命周期
  (4)可以使用外部功能和副作用
  (5)Hook函数主要有6个,useState、useReducer、useContext、useRef、useEffect、useMemo;前3个与状态相关
1、useState,状态读写钩子
   附、纯函数组件没有状态,useState用于为函数组件引入state状态,并进行状态数据的读写操作
  (1)参数: 初始值
  (2)返回值: 数组,包含2个元素,第1个为当前值,第2个为更新状态值的函数setState,
  (3)示例
    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) 
2、useReducer,状态管理钩子,
   附、1次管理1到多个状态;Reducer /riˈdjuːsə/ 减速器
  (1)参数:共2个,第1个为reducer函数,第2个为状态的初始值
  (2)返回值:1个数组,第1项为当前的状态值,第2项为dispatch函数
  (3)示例
    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;
3、useContext,状态共享钩子,
   附、类似于getChildContext
  (1)参数: 1个对象
  (2)返回值: 1个对象,即上面的参数对象
  (3)示例
    A、父组件定义
      const AppContext = React.createContext();
      const HookTest = ()=> {
        const [num, setNum] = useState(0);
        return (
          <AppContext.Provider value={{ num, setNum }}>
            <A context={AppContext}/>  //{A({context: AppContext})}
          </AppContext.Provider>
        )
      }
      export default HookTest;
    B、子组件定义
      import React, { useContext } from "react";
      const A = (props) = > {
        const { num, setNum } = useContext( props.context );
        const onClick = () => {
          setNum(num => num + 1);
        };
        return (
          <p>
            <span>{ name }</span>
            <span>{ name }</span>
            <button onClick={onClick}>点击</button>
          </p>
        )
      }
4、useRef,引用钩子
  (1)参数,初始值
  (2)返回值
    A、只有current属性的可变对象{current: initialValue},更新current值,不会重渲染
    B、在整个生命周期内一直存在
  (3)示例
    import{ useRef,useEffect} from "react";
    const RefComponent = () => {
      let inputRef = useRef(initialValue);
      useEffect(() => {
        inputRef.current.focus();
      })
      return (
        <input type="text" ref={inputRef}/>
      ) 
    }
5、useEffect,副作用钩子 
   附、副作用函数,运行时影响其他变量的函数
   附、相当于componentDidMount、componentDidUpdate、componentWillUnmount这三个生命周期
  (1)参数: 2个
    A、第1个参数是异步函数
      a、执行时机
        第2个参数缺失时,组件每次渲染后(等同于render)
        第2个参数为数组时,组件挂载后(等同于componentDidMount)或数组发生变化后
      b、返回值,也是useEffect的返回值,是函数
        执行时机,组件卸载时执行
        作用,清理副作用 
    B、第2个参数是数组
  (2)返回值: 是第一个参数的返回值
  (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>
      );
    }
6、useMemo,记忆钩子
    附、如果父组件状态的改变不影响子组件,那么子组件不再重新渲染
  (1)父组件
    import {useMemo,memo} from 'react';
    const Parent = () => {
      const [parentState,setParentState] = useState(0); 
      const toChildComputed = useMemo(() => {
        console.log("需要传入子组件的计算属性");
        return 1000;
      },[])
      return (<div>
        <Button onClick={() => setParentState(val => val+1)}>
          点击我改变父组件中与Child组件无关的state
        </Button>
          //将父组件的计算属性传入子组件
        <Child computedParams={toChildComputed}></Child>
      <div>)
    }
  (2)子组件,被memo保护
    const Child = memo(() => {
      consolo.log("我被打印了就说明子组件重新构建了")
      return <div><div>
    })

九、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是进一步简化后用于配置的唯一值

十四、Vue与React的异同
  1、相同点:
  (1)用于创建UI的js库
  (2)单向数据流
  (3)用虚拟DOM
  (4)基于组件
  2、不同点:
  (1)vue使用html模板;react使用jsx模板
  (2)vue有双向绑定语法;react没有
  (3)vue有computed和watch;react没有
  

  

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