react、JSX編譯原理、生命週期、屬性、狀態改變、建立元件、複合元件間的資訊傳遞、受控和非受控元件、react腳手架

小山芋發表於2018-06-29

React是Facebook公司研發的一款JS框架(MVC:Model View Controller) 通過資料的改變來影響檢視

1、React腳手架

React是一款框架:具備自己開發的獨立思想 - 劃分元件開發

前端工程化開發:

  • 基於框架的元件/模組化開發
  • 基於webpack的自動部署

webpack來完成以上內容(自動化):

  • 基於路由的spa單頁面開發
  • 區分開發環境和生產環境
  • 安裝babel完成ES6編寫程式碼(上線時,ES6編譯成ES5)
  • 安裝css、style、file等處理器,處理合並壓縮的
  • 編譯less,sass等預編譯語言
  • 安裝webpack-dev-server ,可以在開發環境下,編譯後自動建立服務,開啟瀏覽器,當程式碼修改後,自動儲存編譯,頁面自動重新整理
  • ....

腳手架 配置webpack相對複雜,我們需要安裝很多的包,還需要寫很多相對複雜的配置,這時候腳手架應運而生,用來提升我們開發的效率

vue:vue-cli react:create-react-app(應用)

二、安裝

1、npm install create-react-app -g // 安裝到全域性能用命令 (npm root -g 檢視全域性npm安裝目錄) 2、create-react-app demo1 // 專案名稱demo1,名稱只能(/^[a-z0-9_-]$/)

腳手架生成目錄中的一些內容:

1、node_module 當前專案中依賴的包 .bin 本地專案中可執行的命令,在package.json的scripts中配置的對應的指令碼即可(還有一個是react-scripts命令) 2、public 存放當前專案的頁面(單頁面應用放index.html即可,多頁面根據自己需求放置需要的頁面)

在react中,所有的邏輯都是在JS中完成的(包括頁面機構的建立),如果想給當前的頁面匯入一些css樣式後者img圖片等內容,我們有兩種方式:

(1)、在JS中基於ES6 Module模組規範,使用import引入,這樣webpack在編譯合併JS的時候,會把匯入的資原始檔等插入到頁面的機構中(絕對不能在js管控的結構中通過相對目錄./ 或者../ 匯入資源,因為在webpack編譯的時候,地址就不在是之前的相對地址要用絕對地址

(2)、如果不想在js中匯入(JS中匯入的資源最後都是基於webpack編譯),我們也可以把資源手動的在html中代入,但是html最後也要基於webpack編譯,匯入的地址也不建議寫相對地址,而是使用%PUBLIC_URL%寫成絕對地址

<link rel="manifest" href="%PUBLIC_URL%/reset.min.css">
複製程式碼

3、src 專案結構中最主要的目錄,因為後期所有的JS、路由、元件等都是放到這裡面(包括需要編寫的css或者圖片等) index.js 是入口檔案 4、.gitignore git的忽略檔案 5、package.json 當前專案的配置清單

  "dependencies": {
    "react": "^16.4.1",
    "react-dom": "^16.4.1",
    "react-scripts": "1.1.4"
  },
複製程式碼

基於腳手架生成工程目錄,自動幫我們安裝了三個模組:

  • react 、
  • react-dom、
  • react-scripts(整合了webpack需要的內容babel,css處理,eslint, webpack....,注意:沒有less、sass)

package.json:

"scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  }
複製程式碼

可執行的指令碼

npm run start 或者 yarn start

yarn start

  • 建立一個預設埠號為3000 ,協議為http的web服務
  • 按照webpack.config.dev.js把專案編譯
  • 開啟瀏覽器預覽我們的專案
  • 專案修改時候,自動重新編譯,瀏覽器頁面自動重新整理,展示最新的效果

yarn build:

  • 基於webpack.config.prod.js 把專案進行編譯打包
  • 生成一個build資料夾,存放最後打包的檔案>- 生成一個build資料夾,存放最後打包的檔案
  • 部署上線的時候,只需要把build的內容釋出即可

三、React腳手架的深入剖析

create-react-app 腳手架為了讓結構目錄清晰,把安裝的webpack及配置檔案都整合在了react-scripts模組中,放到了node_modules中 但是真實專案中,我們需要在腳手架預設安裝的基礎上,額外安裝一些我們需要的模組,例如react-router-domaxios、還有lessless-loader

情況一:如果我們安裝其他的元件,但是安裝成功後不需要修改webpack的配置項,此時我們直接安裝,調取使用就行,比如react-router-dom 、axios等

情況二:我們安裝的外掛是基於webpack處理的,也就是需要把安裝的模組配置到webpack中(重新修改webpack配置項了) 1、首先需要把隱藏到node_module中的配置項暴露到專案中 yarn eject 首先會確定是否確認執行eject,這個操作不可逆轉,一旦暴露出來配置項,就無法再隱藏了 如果還有未提交到歷史區的內容,需要先提交,然後才能eject 2、再去修改對應的配置項 暴露後,專案目錄中多了兩個資料夾: config:存放的是webpack的配置檔案 (1)webpack.config.dev.js 開發環境下的配置項(yarn start) (2)webpack.config.prod.js 生產環境下的配置項(yarn build) scripts:存放的是可執行指令碼的JS檔案 (1)start.js: yarn start 執行的就是這個JS (2)build.js: yarn build 執行的就是這.個JS

我們預覽專案的時候,也是基於webpack編譯,把編譯後的內容放到瀏覽器中執行,所以如果專案中使用了less,我們需要修改webpack配置項,在配置項中加入less的編譯工作,這樣後期預覽專案,首先基於webpack把less編譯為css

set HTTPS=true && npm start 開啟HTTPS協議模式 set PORT=1111 && npm start 修改預設埠號

四、漸進式框架

一種漸進式框架設計思想,一般框架中都包含了很多內容,這樣導致框架的體積過於臃腫 拖慢載入的速度, 真實專案中,我們使用一個框架,不一定用到所有的功能,此時我們應該把框架的功能進行拆分,使用者想用什麼,讓其自己自由組合即可--"漸進式框架"

全家桶:漸進式框架N多部分的組合 vue全家桶:vue、vue-cli、vue-router、vuex、axios、vue-element 、vant、 react全家桶:react、react-react-app、react-dom、react-router、redux、react-redux、axios、ant、dva、saga、mobx

1、React

react的核心部分,提供了Component類可以進行元件開發,提供了鉤子函式(生命週期),所有的生命週期函式都是基於回撥函式完成的

2、React-dom:

raect獨有的,把JSX語法,渲染為真實DOM(能夠放到頁面中展示的機構都叫做真實的DOM)的元件

react框架都是在JS中進行的,然後通過webpack編譯,再放到瀏覽器中編譯

在index.js這個入口檔案:

import React from 'react';
import ReactDOM from 'react-dom';
// import ReactDOM,{render} from 'react-dom';
// 從react-dom中匯入一個reactDOM,逗號後面的內容是把ReactDOM這個物件進行結構

let data ='zufeng',
    root = document.querySelector("#root");

ReactDOM.render(<div id="box">hello world!{data}</div>,root,()=>{
    // 回撥,一般不用
    let oBox = document.querySelector("#box");
    console.log(oBox.innerHTML);
});
複製程式碼

2.1、JSX(也叫react元素):

JSX語法的渲染使用的是ReactDOM.render: ReactDOM.render([jsx],[container],[callback]);

jsx:react獨有的語法, JavaScript+xml(HTML),和我們之前拼接的字串類似,都是把html結構程式碼和js程式碼或者資料混合在一起了,但是他不是字串 container:容器,我們想把元素放到頁面中的哪個容器中 callback:當把容器放到頁面中呈現觸發的回撥函式

jsx語法特點:

1、不建議我們 直接把jsx直接放到body中,而是放在自己建立一個容器中,一般我們都放在一個id為root的div中

ReactDOM.render(<section><h2>內容</h2></section>,root)
複製程式碼

2、把資料嵌入到jsx中 在JSX中出現的{}是存放JS的,要求JS程式碼執行完成需要有返回結果(JS表示式),不能直接放一個物件資料型別的

let name = 'zhufeng'     // 基本資料型別的值
ReactDOM.render(<section><h2{name}</h2></section>,root)

let xx = {name:'xxxx'}
ReactDOM.render(<section><h2{xx}</h2></section>,root)  // 報錯
複製程式碼
  • 不能直接放一個物件資料型別的值(物件((除了給style賦值)),陣列(如果只有基本值或者jsx除外),函式都不行)
ReactDOM.render(<ul>
   {
     data.map((item,index)=>{
        return <li key={index}>{item.title}</li>
     })
   }
</ul>,root)
複製程式碼
  • 可以是基本型別的值(布林型別什麼都不顯示,null,undefined也是jsx元素,代表的是空)
  • 迴圈判斷語句都不行,但是支援三元運算子

3、迴圈陣列穿件jsx元素,需要給建立的元素設定唯一的key值(當前本次唯一即可) 4、只能出現一個根元素 5、給元素設定樣式類用的是className而不是class 6、style中不能直接寫樣式字串,需要基於一個樣式物件來遍歷賦值 7、可以給JSX元素設定屬性: => 屬性值對應大括號中,物件、函式都可以放(也可以放JS表示式) =>style屬性值必須是物件(不能是字串)<li style={{color:'#fff'}}></li> =>class 用className代替

ReactDOM.render(<h1 id={'box'} className="box" style={{color:'red'}}>我是標題</h1>);
複製程式碼

3、渲染原理(react的核心原理之一)

JSX虛擬DOM變為真實的DOM(react的核心原理之一)

let  styleObj = {color:'red'};
ReactDOM.render(<h1 id="titleBox" className='title' style={styleObj.color}></h1>)
複製程式碼

JSX渲染機制:JSX->真實DOM

1、基於babel babel-loader babel-present-react-app把JSX語法編譯為React.createElement([type],[props],[children])結構 =>React.createElement中至少有兩引數, type:第一引數的標籤(字串) props:屬性物件(沒有就是null) 其他的:都是子元素內容(只要子元素是html,就會變成新的createElement)

var styleObj = { color: 'red' };
ReactDOM.render(React.createElement('h1', { id: 'titleBox', className: 'title', style: styleObj.color }));
複製程式碼

2、執行React.createElement(....),建立一個物件(虛擬DOM)

react、JSX編譯原理、生命週期、屬性、狀態改變、建立元件、複合元件間的資訊傳遞、受控和非受控元件、react腳手架

=>key 和ref 都是createElement中的Prop提取出來的 =>props:{ chiledren:存放自己子元素的(沒有子元素就沒有這個屬性),如果有多個子元素,就以陣列的形式儲存資訊 } 3、ReactDOM.render(JSX語法最後生成的物件,container,callback),基於render方法把生成的物件動態建立為DOM元素,插入到指定的容器中

4、元件

不管vue還是react框架,設計之初都是期望我們按照“元件 / 模組管理” 的方式來構建程式的,也就是一個程式劃分一個個的元件來單獨管理 src -> component:這個資料夾下存放的就是開發用的元件 【優勢】 1、有助於多人協助開發 2、元件可以複用 ....

react 中建立元件有兩種方式:

1、函式式宣告元件

(1)函式返回結果是一個新的JSX(也就是當前元件的JSX結構) (2)props變數儲存的值是一個物件,包含了調取元件時候傳遞的屬性值,不傳遞的話是個空物件

知識點

createElement遇到一個元件,返回的物件中:type就不是字串標籤名了,而是一個函式(類),但是屬性還是存在props中

render渲染的時候,我們需要做處理:

首先判斷type的型別, 如果是字串,就建立一個元素標籤, 如果函式或者類,就把函式執行,把props中的每一項(包含children)傳遞給函式 在執行的時候,把函式中return的JSX轉換為新的物件(通過createElement),把這個物件返回:緊接著render按照以往的渲染方式,建立DOM元素,插入到指定的容器中即可

單閉合和雙閉合元件的區別,雙閉合元件中可以放子孫元素

函式式宣告特點:

1、會把基於createElement解析出來的物件中的props作為引數傳遞給元件(可以完成多次調取元件傳遞不同的資訊) 2、一旦元件調取成功,返回的jsx就會渲染到頁面中,但是後期不通過重新調取元件或者獲取DOM元素操作的方式,很難再把渲染好元件中的內容更改 -->"靜態元件"

//Dialog.js
import React from 'react';    // 每個元件必須引入,因為需要基於它的create-element把JSX進行解析渲染
export default function Dialog(props) {
    //props:調取元件的時候傳遞進來的屬性資訊(可能包含className、style、id、可能有children)
    let {con, lx = 0, children,style={}} = props,
        title = lx === 0 ? '系統提示' : '系統警告';
    //children 可能有,可能沒有,可能是個值,也可能是個陣列,每一項可能是字串也可能是個物件等,都代表雙閉合元件的子孫元素
    return <section style={style}>
        <h2>{title}</h2>
        <div>{con}</div>
        {/*1、把屬性中的子元素,放到元件中的指定位置*/}
        {/*{children}*/}
        {/*2、也可以使用react中專門遍歷children的方法*/}
            {children.map(item => item)}
            {/*// {React.Children.map(children,item=>item)}*/}
        </section>
}

// index.js

import React from 'react';
import ReactDOM from 'react-dom';
// 公共css放index.js中,這樣在其他元件中也可以使用(webpack會把所有的元件最後都編譯到一起,index是主入口)
// import 'static/css/reset.min.css'
import 'bootstrap/dist/css/bootstrap.css'  // 需要匯入未經過壓縮的檔案,否則報錯(真實專案中bootstrap已經是過去式了,後期用ant)

import Dialog from "./component/Dialog-bootstrap";

ReactDOM.render(<main>
    <Dialog content ='馬少帥很帥'/>
    <Dialog type ={2} content='系統錯誤了'/>
    <Dialog type = '請登入' content={
        '新的JSX語法'
    }>
        <div>
            <input type="text" className='form-control' placeholder='請輸入使用者名稱'/><br/>
            <input type="password" placeholder='請輸入密碼'/>
        </div>
        <br/>
        <button className='btn btn-success'>登入</button>
        <button className='btn btn-danger'>取消</button>
    </Dialog>
</main>,root);
複製程式碼

2、繼承component類來建立元件

基於component把JSX轉換為一個物件,當render渲染這個物件的時候,遇到type是一個函式或者類,不是直接建立元素,而是把方法執行,

  • 如果就是函式宣告的元件,就把它當做普通函式執行(方法中的this是undefined),把函式返回的JSX元素(也是解析後的物件)進行渲染
  • 如果是類宣告式的元件,會把當前類new執行,建立類的一個例項(當前本次呼叫的元件就是它的例項)執行constuctor之後,會執行this.render(),把rende返回的JSX拿過來渲染,所以“類宣告式的元件,必須有一個render的方法,方法中需要返回一個JSX元素” 但是不管哪一種方式,最後都會把解析出來的props屬性物件作為實參傳遞給對應的函式或者類

特點: 1、調取元件相當於建立類的例項(this),把一些私有的屬性掛載到例項上了,這樣元件內容所有的方法中都可以基於例項獲取這些值(包括:傳遞的屬性和自己管理的狀態) 2、有自己的狀態管理,當狀態改變的時候,react會重新渲染檢視(差異更新:基於DOM-DIFF只把有差異的部分渲染)

import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types'  // facebook是公司開發的一個外掛,基於這個外掛我們可以給元件傳的屬性設定規則,設定的規則不會影響頁面的渲染,可以不加
// 但是會在控制檯丟擲警告錯誤

class Dialog extends React.Component{
   // this.props只讀的,我們無法在方法中修改它的值,但是可以給其設定預設值或者設定一些規則(例如:設定是否是必須傳遞的以及傳遞的值的型別等)
   static  defaultProps ={
        lx:"系統提示"
   };
   static propTypes = {
        // 設定屬性規則,如果傳的不是這個規則的,不影響頁面的渲染,只是會在控制檯丟擲警告錯誤
        // con: PropTypes.string   傳遞的內容必須是字串
        con:PropTypes.string.isRequired  // 傳遞的內容是字串,而且還必須是傳遞
   };
   constructor(props){    // props context updater
       // props:當render渲染並且把當前類執行建立例項的時候,會把之前JSX解析出來的props物件中的資訊(可能有children)傳遞給引數props  =>  "調取元件傳遞的屬性"

       // props如果不傳,super也不傳,除了constructor中不能直接使用this.props,其他宣告周期函式中都可以使用(也就是執行完成constructor,
       // react已經幫我們把傳遞的屬性接收,並且掛載到例項上了)

       super(props);  // extends繼承,一旦使用了constructor,第一行位置必須設定super執行,相當於React.Component.call(this),也就是call繼承,把父類私有的屬性繼承過來
       // this.props:屬性集合
       // this.refs:ref集合(非受控元件中用到)
       // this.context:上下文

       // - 如果super(props); 在繼承父類私有的時候,就把props掛載到this例項上了,這個this只是constructor中的this,不影響原型上的this,寫不寫都行
       // - 如果只寫super() 雖然建立例項的時候把屬性傳遞過來了,但是並沒有傳遞父元件,也就是沒有把屬性掛載到例項上,使用this.props獲取的結果是undefined
       // -
       console.log(props);
       // AA = 12;  設定私有屬性,但是不符合ES6語法,需要babel-preset-react編譯
       // fn=()=>{}  設定一個箭頭函式,但是不符合ES6語法,需要babel-preset-react編譯
   }

   render(){
       // render必須寫還必須有返回的東西
       return <section>
           <h3>系統提示</h3>
           <div>
               zhufeng
           </div>
       </section>
   }
}

ReactDOM.render(<div>
    <Dialog>
        <span>d</span>
    </Dialog>
</div>,root);
複製程式碼

元件中的屬性(this.props)是調取元件的時候(建立類例項的時候)傳遞給元件的資訊,這部分資訊是隻讀的(只能獲取不能修改)-> "元件的屬性是隻讀的"

Object.defineProperty(this.props,'cont',{
   writable:true
});
複製程式碼

用這種方法也改不了


元件部分的總結

建立元件有兩種方式:一是“函式式”,一是“建立類式”

函式式

1、操作簡單 2、能實現的功能也很簡單,只是簡單的調取和返回

建立類式

1、操作相對複雜一點,但是也可以實現更為複雜的業務功能 2、能夠使用生命週期函式操作業務 3、函式式可以理解為靜態元件(元件中內容調取的時候,就已經固定了,很難再修改),而類這種方式,可以基於元件內部的狀態來動態更新渲染的內容:

所謂函式元件是靜態元件,和執行普通方法一樣,調取一次元件,就把元件中內容獲取到,如果不重新調取元件,顯示內容是不發生改變的


【需求】實現頁面上時間的走動

1、函式式的

import React from 'react';
import ReactDOM from 'react-dom';

function Clock() {
  return <div>
      <h3>當前北京時間為:</h3>
      <div style={{color:'red',fontWeight:'bold'}}>{new Date().toLocaleString()}</div>
  </div>
}
/*每隔一秒重新調取元件*/
setInterval(()=>{
    ReactDOM.render(<Clock/>,root);
},1000)
//適用於元件內容不會再次發生改變的情況下
複製程式碼

2、建立類式

class Clock extends React.Component{
   constructor(){
       super();
       // 初始化元件狀態(都是物件型別):要求我們在constructor中把後期需要使用的狀態資訊全部初始一下(約定俗稱的語法規範)
       this.state = {
           time:new Date().toLocaleString(),
       }
   }
   componentDidMount(){
       //react生命週期函式之一:第一次元件渲染完成後觸發(我們只需要間隔1000ms把state狀態中的time資料改變,這樣react會幫我們元件中的部門內容進行渲染)

       setInterval(()=>{
           // this.state.time = new Date().toLocaleString();  //雖然下面的定時器可以修改狀態,但是不會通知react重新渲染頁面,這樣不行
           //修改元件的狀態
            //  1、修改部分狀態:會用我們傳遞的物件和初始化的state進行匹配,只把我們傳遞的屬性進行修改,沒有傳遞的依然保留原始的狀態資訊(部分修改)
            //  2、修改狀態修改完成,會通知react把元件進行重新渲染
           
           this.setState({
               time:new Date().toLocaleString(),
           },()=>{
               /*當通知react把需要重新渲染的JSX元素重新渲染完成後,執行的回撥操作(類似於生命週期中的componentDidUpdate)專案中一般使用鉤子函式*/
               // 設定回撥的原因是:通知完就直接往下執行,render方法是個非同步操作
           });
       },1000)
   }
   render(){
       return <div>
           <h3>當前北京時間為:</h3>
           <div style={{color:'red',fontWeight:'bold'}}>{this.state.time}</div>
       </div>
   }
}
ReactDOM.render(<Clock/>,root);
複製程式碼

React中的元件有兩個非常重要的概念:

1、元件的屬性:[只讀]調取元件的時候傳遞進來的資訊 2、元件的狀態:[讀寫]自己在元件中設定和規劃的(只有類宣告式元件才有狀態的管控你,函式式元件宣告不存在狀態的管理)

元件狀態類似於VUE中的資料驅動:

我們資料繫結的時候是基於狀態值繫結,當修改元件狀態後,對應的JSX元素也會跟著重新渲染(差異渲染:只把資料改變的部分重新渲染,基於DOM-DIff演算法完成)

當代前端框架最重要的核心思想就是:“資料操作檢視(檢視影響資料)”,讓我們告別JQ手動操作DOM的時代,我們以後只需要改變資料,框架會幫我們重新渲染檢視,從而減少直接操作DOM(提高效能,也有助於開發效率)

儘量少操作DOM

五、React的用法

在react當中: 1、基於資料驅動(修改狀態資料,react幫助我們重新渲染檢視)完成的元件叫做“受控元件(受資料管控的元件)” 2、基於ref操作DOM實現檢視更新,叫做“非受控元件” 真實專案中,建議多使用“受控元件”

VUE:MVVM 資料更改,檢視跟著改變,檢視更改,資料也跟著改變(雙向資料繫結) React:MVC 資料更改檢視跟著改變(原本是單向的,但是我們可以手動設定為雙向的)

render(){
  let {text} = this.state;

  return <section className='panel panel-default'>
      <div className='panel-heading'>
          <input type="text" className='form-control' value={text} onChange={ev=>{
              // 在onChange中修改狀態資訊,實現的是檢視改變資料
              this.setState({
                  text:ev.target.value
              })
          }}/>
      </div>
      <div className='panel-body'>
          {text}
      </div>
  </section>
}
複製程式碼

六、生命週期

所謂生命週期函式(鉤子函式)描述一個函式或者元件從建立到銷燬的過程,我們可以在過程中間,基於鉤子函式完成自己的一些操作(例如:在第一次渲染完成做什麼,或者在第二次即將重新渲染之前做什麼等...)

1、基本流程

【基本流程】 constructor: 建立一個元件 componentWillMount: 第一次渲染之前 render:第一次渲染 componentDidMount: 第一次渲染之後 【修改流程】 當元件的狀態資料發生改變(setState)或者傳遞的屬性發生改變(重新呼叫元件,傳遞不同的屬性)都會引發render重新執行渲染(渲染也是差異渲染) shouldComponentUpdate 是否允許元件重新渲染 componentWillUpdate 重新渲染之前 render 第二次及以後重新渲染 componentDidUpdate 重新渲染之後

componentWillReceiveProps 父元件把傳遞給子元件的屬性發生改變後觸發的鉤子函式 屬性改變也會改變子元件重新渲染,觸發鉤子函式

【銷燬】 原有的渲染的不消失,以後不能基於資料改變檢視 componentWillUnmount 解除安裝元件之前(一般不用)

react、JSX編譯原理、生命週期、屬性、狀態改變、建立元件、複合元件間的資訊傳遞、受控和非受控元件、react腳手架

index.js:

import React from 'react';
import ReactDOM, {render} from 'react-dom';
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.css'

class A extends React.Component {
    static defaultProps = {}; // 第一個執行,屬性設定預設值
    constructor() {
        super();
        console.log('1=constructor');
        this.state = {
            n: 1
        }
    }

    componentWillMount() {
        console.log('3=componentWillMount 第一次渲染前', this.refs.HH);
        // 在這裡,如果直接setState修改資料(同步的),會把狀態資訊改變後,然後render和didMount,如果setState是放到一個非同步操作中完成(例如:定時器或者從伺服器獲取資料),也是先執行render和did
        // 然後再執行這個非同步操作修改狀態,緊接著走修改的流程(這樣和放到didMount中沒啥區別),所以我們一般吧資料請求放到DID中處理
        // 真實專案中的資料繫結,第一次元件渲染,我們都是繫結的預設屬性,第二次才是從伺服器獲取的資料,有些屬性,我們需要根據資料是否存在,判斷顯示隱藏
    }

    componentDidMount() {
        console.log('4=componentWillMount 第一次渲染後', this.refs.HH);
        //真實專案中,這個階段一般做如下處理:
        //  1、控制狀態資訊更改的操作
        //  2、從伺服器獲取資料,然後修改狀態資訊,完成資料繫結
        setInterval(() => {
            this.setState({
                n: this.state.n + 1
            })
        }, 5000)
    }

    shouldComponentUpdate(nextProps, nextState) {
        // this.state.n   更新之前的
        console.log('5=shouldComponentUpdate 函式返回true(允許),false(不允許)');
        // return true

        /*在這個鉤子函式中,我們獲取的state不是最新修改的,而是上一次的state的值
         例如:第一次載入完成後,5000ms後,我們基於setState把n修改為2,但是此處獲取的還是1呢
         但是這個有兩個引數:
          nextProps:最新修改的屬性
          nextState:最新修改的狀態
        */

        if (nextState.n > 3) {
            return true
        } else {
            return false
        }
    }

    componentWillUpdate(nextProps, nextState) {
        // this.state.n  也是更新之前的,也有兩個引數儲存最新的資訊
        console.log('6=componentWillUpdate');
    }

    componentDidUpdate() {
        // this.state.n   更新之後的
        // 先render
        console.log('8=componentWillUpdate');
    }

    render() {
        console.log('2=render');
        return <section ref='HH'>
            {this.state.n}
        </section>
    }
}

ReactDOM.render(<main>
    <A></A>
</main>, root);
複製程式碼

七、複合元件的傳值

父元件傳子元件:

基於屬性傳即可(而且傳遞是單方向的:只能把資訊給兒子,兒子不能直接把資訊作為屬性傳遞給父親) 後期子元件中的資訊需要修改: 可以讓父元件傳給子元件的資訊發生變化(也就是子元件接收的屬性發生變化,子元件會重新渲染 => componentWillReceiveProps鉤子函式)

子改父:

類似於這種子改父的操作,我們需要使用一下技巧: 1、把父元件中一個方法作為屬性傳遞給子元件 2、在子元件中,把基於屬性傳遞進來的方法,在合適的時候執行(相對於在執行父元件中的方法:而這個方法中完全可以操作父元件的資訊)

相關文章