react

40、React

什么是React?

  • React 是一個(gè)用于構(gòu)建用戶界面的框架(采用的是MVC模式):集中處理VIEW這一層
  • 核心專注于視圖,目的實(shí)現(xiàn)組件化開發(fā)

什么是組件化開發(fā)?
我們可以很直觀的將一個(gè)復(fù)雜的頁(yè)面分割成若干個(gè)獨(dú)立組件,每個(gè)組件包含自己的邏輯和樣式 再將這些獨(dú)立組件組合完成一個(gè)復(fù)雜的頁(yè)面。 這樣既減少了邏輯復(fù)雜度,又實(shí)現(xiàn)了代碼的重用

  • 可組合
  • 可重用
  • 可維護(hù)

安裝react腳手架(跑環(huán)境)

在全局下安裝create-react-app
打開命令行工具輸入以下代碼
npm install create-react-app -g
默認(rèn)會(huì)自動(dòng)安裝React,react-dom兩部分

使用react腳手架創(chuàng)建項(xiàng)目

需要在哪個(gè)文件夾下創(chuàng)建react項(xiàng)目就在那個(gè)項(xiàng)目下打開命令行工具輸入以下代碼
create-react-app xxx(xxx為項(xiàng)目名稱:不能出現(xiàn)大寫字母)
cd xxx(轉(zhuǎn)到項(xiàng)目目錄下)
yarn start或npm start啟動(dòng)項(xiàng)目即可

react和react-dom模塊

在js文件的頂部導(dǎo)入react和react-dom
react:核心模塊(生命周期等鉤子函數(shù)都是在這里定義的)
react-dom:提供與DOM相關(guān)的功能,會(huì)在window下增加ReactDOM屬性,內(nèi)部比較重要的方法是render,將react元素對(duì)象或者react組件插入到頁(yè)面中

import React from 'react';//核心基礎(chǔ) 模塊(生命周期)
//用來(lái)把react元素(jsx元素)渲染成真實(shí)的DOM結(jié)構(gòu)并添加到頁(yè)面當(dāng)中
import ReactDOM,{render} from 'react-dom';
//ReactDOM中就一個(gè)方法比較常用叫render
//可以把render從react-dom中解構(gòu)出來(lái)

ES6中的模塊導(dǎo)入和導(dǎo)出

//=>A模塊
import Temp,{lib} from "./B";//=>把導(dǎo)入的Temp中的部分屬性方法進(jìn)行解構(gòu)賦值
new Temp().init(); //new Temp生成一個(gè)實(shí)例,并且執(zhí)行它原型上的init方法
lib();//=>Temp.lib()

//=>B模塊
export default class Temp {
    init() {
        console.log(`hello world`);
    }
    static lib(){//=>Temp.lib
        
    }
}

react中的jsx語(yǔ)法與render渲染方法

JSX語(yǔ)法

react構(gòu)建頁(yè)面有自己的語(yǔ)法:JSX語(yǔ)法;
jsx是一種JS和html混合的語(yǔ)法,HTML 語(yǔ)言直接寫在 JavaScript 語(yǔ)言之中,不加任何引號(hào),這就是 JSX 的語(yǔ)法,它允許 HTML 與 JavaScript 的混寫;JSX最后會(huì)通過babel進(jìn)行轉(zhuǎn)義
jsx中的html部分和原生html基本一樣(也有不一樣的比如class要用className,label的for要用htmlFor),并且屬性名要使用駝峰命名法;
用jsx語(yǔ)法寫的內(nèi)容叫做jsx元素(虛擬DOM元素);
jsx元素具有不變性 :只在頁(yè)面初次加載的時(shí)候渲染一次,以后只能通過更改狀態(tài)和屬性來(lái)重新渲染;

jsx表達(dá)式的用法

  • 1、可以放JS的執(zhí)行結(jié)果:在react中想將js當(dāng)作變量引入到j(luò)sx中需要使用{}
  • 2.在jsx中,相鄰的兩個(gè)jsx元素 渲染時(shí)需要外面包裹一層元素(只能有一個(gè)根元素)
function build(str) {
    return <h1>{str.name}</h1><h1>{str.name}</h1>;//在jsx語(yǔ)法中,這種寫法是錯(cuò)誤的
}
- 3、如果多個(gè)元素需要在return后面換行,我們需要加一個(gè)'()'當(dāng)作一個(gè)整體返回
- 4、可以把JSX元素當(dāng)作函數(shù)的返回值
- 5、用`<`和`{}`來(lái)區(qū)分是html還是js表達(dá)式:遇到 HTML 標(biāo)簽(以` <` 開頭),就用 HTML 規(guī)則解析;遇到代碼塊(以` { `開頭),就用 JavaScript 規(guī)則解析
- 6、循環(huán)綁定需要給元素設(shè)置唯一的KEY屬性(屬性值是唯一的)

**`jsx屬性的用法`**
在jsx中分為普通屬性和特殊屬性
- 1、普通屬性:和html中的一樣
- 2、特殊的屬性如class,要使用className;for(label中的for)要使用htmlFor
- 3、style要采用對(duì)象的方式
```javascript

import React from 'react';
import ReactDOM,{render} from 'react-dom';
let str='<h1>純標(biāo)簽</h1>';
let style={background:'red'};
render(<div>
<li className="aa"></li>
<li htmlFor="aa" style={style}></li>
<li dangerouslySetInnerHTML={{__html:str}}></li>
</div>,window.root);

- 4、dangerouslyInnerHTML插入html(基本上用不到)

React.createElement

  • React.createElement用來(lái)創(chuàng)建一個(gè)react對(duì)象,具有一個(gè)type屬性代表當(dāng)前的節(jié)點(diǎn)類型,還有節(jié)點(diǎn)的屬性props;
React.createElement("div",null,"姜,",React.createElement("span",null,"帥哥"))
//結(jié)果如下
{type:'div',props:{className:"red",children:['姜',{type:'span',props:{id:'handsome',children:'帥哥'}}]}}

render():此方法是由react-dom模塊中提供的方法

第一個(gè)參數(shù)為要渲染的jsx元素
第二個(gè)參數(shù)為要把渲染的DOM結(jié)構(gòu)插入到哪個(gè)容器當(dāng)中
第三個(gè)參數(shù)為渲染完成后需要執(zhí)行的回調(diào)函數(shù);

  • 用來(lái)將虛擬DOM元素(jsx元素)渲染成真實(shí)的DOM;
ReactDOM.render(<div className='app'>姜<span id='handsome'>帥哥</span></div>,window.root);

react中的虛擬DOM

我們?cè)趓eact中創(chuàng)建的組件和用jsx語(yǔ)法寫出的標(biāo)簽等,都是虛擬的DOM元素:
組件并不是真實(shí)的 DOM 節(jié)點(diǎn),而是存在于內(nèi)存之中的一種數(shù)據(jù)結(jié)構(gòu),叫做虛擬 DOM (virtual DOM)。只有當(dāng)它插入文檔以后,才會(huì)變成真實(shí)的 DOM 。根據(jù) React 的設(shè)計(jì),所有的 DOM 變動(dòng),都先在虛擬 DOM 上發(fā)生,然后再將實(shí)際發(fā)生變動(dòng)的部分,反映在真實(shí) DOM上,這種算法叫做 DOM diff ,它可以極大提高網(wǎng)頁(yè)的性能表現(xiàn)。

虛擬DOM渲染過程(虛擬DOM—>真實(shí)DOM)

1、構(gòu)建虛擬DOM元素(jsx元素):

  • 在js中用jsx語(yǔ)法寫的jsx元素就是虛擬DOM元素;(render中的元素)
    render(<h1 className='red'>心有猛虎,細(xì)嗅薔薇</h1>,window.root)

2、調(diào)用render方法把虛擬DOM元素渲染成真實(shí)的DOM結(jié)構(gòu)

  • 1)render方法會(huì)先用babel把jsx元素轉(zhuǎn)化為ES5語(yǔ)法
    'h1', {className: 'red'}, '心有猛虎,細(xì)嗅薔薇'
  • 2)再用React.createElement方法根據(jù)上面的結(jié)果創(chuàng)建出一個(gè)react對(duì)象
    React.createElement('h1', {className: 'red'}, '心有猛虎,細(xì)嗅薔薇')結(jié)果如下
    {type: "h1", props: {className: "red", children: "心有猛虎,細(xì)嗅薔薇"}}
  • 3)此時(shí)render方法將react對(duì)象轉(zhuǎn)化為真正的DOM結(jié)構(gòu),渲染到頁(yè)面上
    render(<h1 className='red'>心有猛虎,細(xì)嗅薔薇</h1>,window.root)

自己模擬React.createElement方法與render方法:(render不完善)

function createElement(type,props,...children) {
if(children.length === 1) children = children[0];
return {type,props:{...props,children:children}}
}
let myRender = (obj, container,callback) => {
let {type, props} = obj;
let ele = document.createElement(type);//創(chuàng)建第一層
for (let key in props) {
if (key === 'children') {
//children可能是數(shù)組也可能是一個(gè)字符串(純文本)
if ({}.toString.call(props[key]).slice(8,-1)==='Array') {
props[key].forEach(item => {
if (typeof item === 'object') {//如果子元素是對(duì)象,那就繼續(xù)調(diào)用render
myRender(item, ele);
} else {
ele.appendChild(document.createTextNode(item));
}
})
} else {//字符串的話直接創(chuàng)建文本節(jié)點(diǎn)并插入到元素中
ele.appendChild(document.createTextNode(props[key]));
}
} else if (key === 'className') {//className要做單獨(dú)處理
ele.setAttribute('class', props[key]);
} else {
ele.setAttribute(key, props[key]);//其他的直接設(shè)置(style等屬性未做處理)
}
}
container.appendChild(ele);//把DOM元素插入到html中
callback && callback();//=>添加成功后觸發(fā)回調(diào)函數(shù)執(zhí)行
};

react中的組件

react元素是是組件組成的基本單位
組件有兩種聲明方式

1、第一種方式是函數(shù)聲明

每一個(gè)創(chuàng)建的組件都是一個(gè)自定義的標(biāo)簽,在標(biāo)簽上設(shè)置的屬性都會(huì)轉(zhuǎn)換為react對(duì)象的props屬性,當(dāng)react渲染DOM的時(shí)候會(huì)把props傳遞給下面例子中的Build函數(shù)

import React from 'react';
import ReactDOM,{render} from 'react-dom';
let school1={name:'zfpx',age:10};
let school2={name:'珠峰',age:8};
let Build=function (props) {// "函數(shù)"(組件)的參數(shù)是屬性
return <p>{props&&props.name}{props&&props.age}</p>;
};
render(<div>
<Build name={school1.name} age={school1.age}/>
<Build {...school2}/>//利用解構(gòu)賦值將對(duì)象中的內(nèi)容解構(gòu)出來(lái)傳遞給Build組件
<h1></h1>
</div>,window.root);

###2、第二種是以類的形式聲明
import React,{Component} from 'react';
import ReactDOM from 'react-dom';
let school1 = {name:'珠峰',age:8};
let school2 = {name:'珠峰',age:0};
//1.類聲明的組件有this this上有props屬性
class Build extends Component{//Component這個(gè)基類上有this.setState
    render(){ // 這個(gè)組件在調(diào)用時(shí)默認(rèn)會(huì)調(diào)用render方法(此render相當(dāng)于注冊(cè)組件)
        let {name,age} = this.props;
        return <p>{name} {age}</p>
        // return  <p>{this.props.name} {this.props.age}</p>
    }
}
ReactDOM.render(
此render的渲染步驟
1)先渲染外層div,將jsx元素轉(zhuǎn)化為ES5的語(yǔ)法,然后調(diào)用React.createElement方法創(chuàng)建react對(duì)象,當(dāng)遇到div下的子元素的時(shí)候,會(huì)進(jìn)行以下操作
//1、發(fā)現(xiàn)Build 是一個(gè)繼承Component的類,它會(huì)創(chuàng)建這個(gè)類的一個(gè)實(shí)例,并且執(zhí)行原型上的render方法
//2、執(zhí)行原型上的render返回JSX元素,然后繼續(xù)執(zhí)行外層的render
2)繼續(xù)將上面返回的jsx元素轉(zhuǎn)化為es5語(yǔ)法,調(diào)用React.createElement方法創(chuàng)建虛擬DOM元素對(duì)象,Build 這個(gè)位置被返回的元素對(duì)象替換
3)外層ReactDOM.render將上面生成的react對(duì)象渲染成真實(shí)的DOM結(jié)構(gòu),并追加到頁(yè)面中;
<div>
    <Build name={school1.name} age={school1.age}/>
    <Build {...school2} />
</div>,window.root);

區(qū)別:類聲明有狀態(tài),this,和生命周期
聲明組件的規(guī)則:

  • 1、首字母必須大寫,目的是為了和JSX元素進(jìn)行區(qū)分
  • 2、組件定義后可以像JSX元素一樣進(jìn)行使用
  • 3、可以通過render方法將組件渲染成真實(shí)DOM(做了優(yōu)化 只會(huì)重新渲染改變的地方)

單閉合標(biāo)簽不能添加子節(jié)點(diǎn);雙閉合標(biāo)簽可以

組件中屬性(props)和狀態(tài)(state)的區(qū)別

屬性和狀態(tài)的變化都會(huì)影響視圖更新,但是兩者導(dǎo)致視圖刷新的機(jī)制是不一樣的;

  • 1、確切的說不是因?yàn)閜rops更新了導(dǎo)致的視圖刷新,而是傳遞屬性的組件(父組件)重新的渲染了,從而導(dǎo)致接受屬性的組件(子組件)重新渲染,這樣屬性就也隨著更新了;
  • 2、狀態(tài)導(dǎo)致的視圖刷新是只要通過this.setState改變狀態(tài),視圖就會(huì)刷新;

組件的數(shù)據(jù)來(lái)源有兩個(gè)地方:

1、通過props(屬性) 獲取數(shù)據(jù):

外界傳遞進(jìn)來(lái)的(存在默認(rèn)屬性和屬性校驗(yàn)),是只讀的,不能修改;
有屬性校驗(yàn)?zāi)K:prop-types(用來(lái)校驗(yàn)屬性的類型和是否必須設(shè)置某屬性)
用yarn add prop-types來(lái)安裝。用來(lái)校驗(yàn)屬性的

import React,{Component} from 'react';
import ReactDom,{render} from 'react-dom';
import PropTypes from 'prop-types';
// 1、默認(rèn)的屬性必須寫在defaultProps中(不能改名字)
// 2、校驗(yàn)器的名字必須是propTypes(不能改名字);
class School extends Component{//類上的屬性(把類當(dāng)作對(duì)象添加的屬性)就叫做靜態(tài)屬性
static defaultProps={//會(huì)先默認(rèn)調(diào)用defaultProps
name:'珠峰',
age:1
};
static propTypes={//校驗(yàn)屬性的值(number)類型和是否必須設(shè)置該屬性(isRequired)
age:PropTypes.number.isRequired
};
constructor(props){
//如果想在constructor中使用props,則只能傳遞參數(shù)props,在constructor外面可以直接用this.props
//不能在組件中更改屬性
super();
console.log(this.props)//undefined
}
render(){//渲染組件的,返回一個(gè)組件
//如果沒有默認(rèn)的defaultProps,也沒有傳遞props,則this.props是一個(gè)空對(duì)象
return <h1>{this.props.name} {this.props.age}</h1>
}
}
//name={'珠峰培訓(xùn)'}:這樣寫大括號(hào)中就可以是變量
render(<School name={'珠峰培訓(xùn)'} age={9}/>,window.root);

####2、this.state(狀態(tài)) 狀態(tài)是自己的:
是可讀寫的(寫了狀態(tài)就必須給默認(rèn)值);
我們可以通過this.setState({})方法來(lái)修改狀態(tài),由于修改狀態(tài)會(huì)引起視圖刷新(即更新DOM),而有關(guān)DOM的操作都是異步的,所以this.setState({})是異步的;
```javascript

import React,{Component} from 'react';
import ReactDom,{render} from 'react-dom';
//1、state變化可以更新視圖,只能通過this.setState({})來(lái)更改狀態(tài),調(diào)用后會(huì)更新視圖
class Counter extends Component{
constructor(){
super();
console.log(this.state);//默認(rèn)為undefined
this.state={count:0}
}
add=(val)=>{ //val后面省略了一個(gè)參數(shù)e(事件對(duì)象),是onClick事件默認(rèn)傳進(jìn)來(lái)的,放在val的后面,因?yàn)閎ind的傳參機(jī)制
//setState方法會(huì)默認(rèn)按最后的設(shè)置進(jìn)行執(zhí)行
// this.setState({count:this.state.count+val});
this.setState({count:this.state.count+val});//會(huì)執(zhí)行這個(gè)
//setState有兩種寫法,一種是傳入對(duì)象的方式,一種是傳入函數(shù)的方式
//下一個(gè)狀態(tài)是依賴于上一個(gè)狀態(tài)時(shí),需要寫成函數(shù)的方式(回調(diào)函數(shù))
this.setState(prevState=>({count:prevState.count+val}));//如果返回的就是一個(gè)對(duì)象,則需要用'()'包裹.如果不用小括號(hào)包裹,則需要加return。因?yàn)楹瘮?shù)如果不加return或者return后什么也不寫,函數(shù)的返回值則為undefined
}); //等同于下面這種寫法
//this.setState({count:this.state.count+val},function () {
// this.setState({count:this.state.count+val});
//};
render(){
return <p>
{this.state.count}
/因?yàn)閍dd方法是在類里面聲明的,屬于ES7語(yǔ)法,所以 add方法中的this是指向當(dāng)前實(shí)例的,所以bind不需要改變this指向/
<button onClick={this.add.bind(null,2)}>+</button>
</p>
}
}
render(<Counter/>,window.root);

復(fù)合組件與數(shù)據(jù)傳遞

復(fù)合組件:將多個(gè)組件進(jìn)行組合嵌套;
復(fù)合組件之間的數(shù)據(jù)傳遞:

1、<font color=red>父?jìng)髯?lt;/font>:父組件傳遞給子組件數(shù)據(jù)通過設(shè)置屬性的方式

import React,{Component} from 'react';
import ReactDom,{render} from 'react-dom';
import PropTypes from 'prop-types';
import 'bootstrap/dist/css/bootstrap.css';
//1、什么是復(fù)合組件,將多個(gè)組件進(jìn)行組合
//2、結(jié)構(gòu)非常復(fù)雜時(shí)可以把組件分離開
//3、復(fù)合組件 有父子關(guān)系,父的數(shù)據(jù)傳遞給子(通過給子組件設(shè)置屬性)
// Panel組件 Heading Body
class Counter extends Component{
render(){
//由于在Counter中傳遞了屬性,所以這里可以用this.props獲取到;
let {header:header,body:body}=this.props;
return (
<div className='container'>
<div className='panel-default panel'>
//這里寫了之后就可以在下面的子組件中拿到對(duì)應(yīng)的props值
<Header header={header}></Header>
<Body body={body}></Body>
</div>
</div>
)
}
}
//react中需要把屬性一層一層向下傳遞:?jiǎn)蜗驍?shù)據(jù)流;
class Header extends Component{
render(){
return (
<div className='panel-heading'>
//上面?zhèn)鬟f了之后就可以直接在此處使用this.props屬性了;
{this.props.header};
</div>
)
}
}
class Body extends Component{
render(){
return (
<div className={'panel-body'}>
{this.props.body}
</div>
)
}
}
let data={header:'我很帥',body:'長(zhǎng)的英俊'};
render(<Counter {...data}/>,window.root);

####2、**<font color=red>子傳父</font>:**通過父親傳遞給兒子一個(gè)函數(shù),兒子調(diào)用父親的函數(shù)將要改的值傳遞給父親,父親更新值,刷新視圖;
```javascript

import React, {Component} from 'react';
import ReactDom, {render} from 'react-dom';
import 'bootstrap/dist/css/bootstrap.css';
//1、子傳父 通過父親傳遞給兒子一個(gè)函數(shù),兒子調(diào)用父親的函數(shù)將要修改的值傳遞給父親,父親更新值,刷新視圖;
class Counter extends Component {
constructor() {
super();
this.state = {color: 'primary'};
}
changeColor = (color) => {
this.setState({color});
};
render() {
let {header} = this.props;
return (
<div className='container'>
<div className={'panel panel-' + this.state.color}>
<Header header={header} color={this.changeColor}></Header>
</div>
</div>
)
}
}
//react中需要把屬性一層一層向下傳遞:?jiǎn)蜗驍?shù)據(jù)流;
class Header extends Component {
clickColor = () => {
this.props.color('danger');//兒子調(diào)用父親的方法,把要改的值傳遞給父親,讓父親自己修改
};
render() {
return (
<div className='panel-heading'>
{this.props.header}
<button className={btn btn-danger} onClick={this.clickColor}>改顏色</button>
</div>
)
}
}
let data = {header: '我很帥'};
render(<Counter {...data}/>, window.root);

ref屬性

ref可以寫在組件上也可以寫在DOM元素上,獲取的時(shí)候得到兩者的結(jié)果是不同的;

  • 寫在組件上時(shí)(這里的組件是有狀態(tài)的組件),通過refs獲取的是組件的實(shí)例;
  • 寫在DOM元素上時(shí),通過refs獲取的是具體的DOM元素節(jié)點(diǎn).(獲取的是真實(shí)DOM)

ref的值可寫為兩種形式

1、直接寫為一個(gè)值

通過ref設(shè)置的屬性:可以通過this.refs.xxx獲取到對(duì)應(yīng)ref值為xxx的dom元素或者組件的實(shí)例

2、寫成一個(gè)函數(shù)的形式

寫成函數(shù)時(shí),這個(gè)函數(shù)將會(huì)在渲染成真實(shí)DOM結(jié)構(gòu)或組件被掛載后執(zhí)行,函數(shù)的參數(shù)為真實(shí)的DOM結(jié)構(gòu)或組件的實(shí)例

//如上面非受控組件中的例子
//1、直接將ref寫為一個(gè)值
<input type="text" ref='a'/>
//2、將ref寫為一個(gè)函數(shù)
<input type="text" ref={(x)=>this. c=x}/>
{/* x 代表真實(shí)的DOM(寫成函數(shù)的形式會(huì)默認(rèn)傳入當(dāng)前設(shè)置ref的DOM結(jié)構(gòu) ),上面這種寫法相當(dāng)于把DOM結(jié)構(gòu)掛載到了this的自定義屬性c上,以后可以直接用this.c拿到這個(gè)input的DOM結(jié)構(gòu)*/}
resultChange=()=>{//通過ref設(shè)置的屬性:可以通過this.refs.xxx獲取到對(duì)應(yīng)ref值為xxx的dom元素
this.c.value=parseFloat(this.refs.a.value)+parseFloat(this.refs.b.value);
};

受控組件與非受控組件(受狀態(tài)控制)

組件是否與this.state有關(guān)系區(qū)分為受控與非受控組件,受控組件可以賦予默認(rèn)的值

受控組件

需要與onInput/onChange/disabed/readOnly等控制value/checked的屬性或事件一起使用

import React, {Component} from 'react';
import ReactDom, {render} from 'react-dom';
class Input extends Component {
//1、受狀態(tài)控制的組件,必須要有onChange方法,否則不能使用;
//2、受控組件可以賦予默認(rèn)值(官方推薦使用受控組件)
constructor() {
super();
this.state = {val: '', a: 1, b: 2};//初始化狀態(tài):設(shè)置默認(rèn)值
}
inputChange = (val, e) => {//處理多個(gè)輸入框的值映射到this.state的方法
//val:要修改的state中的屬性的名稱
//e:事件源(e.target.value是要修改成的值)
this.setState({[val]: parseFloat(e.target.value)||0});//更改狀態(tài)
};
render() {
return (
<div>
//下面兩個(gè)input都受this.state影響(受控組件)
<input type="text" value={this.state.a} onChange={this.inputChange.bind(null, 'a')}/>
<input type="text" value={this.state.b} onChange={e =>{
//方法執(zhí)行的時(shí)候要用到事件對(duì)象e,所以必須要傳
this.inputChange('b', e)
}}/>
{this.state.a + this.state.b}
</div>
)
}
}
render(<Input/>, window.root);

####非受控組件(基于ref來(lái)管理)
```javascript

import React,{Component} from 'react';
import ReactDom,{render} from 'react-dom';
class Input extends Component{
//輸入框value值不受狀態(tài)控制,不能初始化默認(rèn)值
resultChange=()=>{//通過ref設(shè)置的屬性:可以通過this.refs.xxx獲取到對(duì)應(yīng)ref值為xxx的dom元素
this.c.value=parseFloat(this.refs.a.value)+parseFloat(this.refs.b.value);
};
render(){
return (
<div onChange={this.resultChange}>
<input type="text" ref='a'/>
<input type="text" ref='b'/>
{/x代表真實(shí)的DOM(寫成函數(shù)的形式會(huì)默認(rèn)傳入當(dāng)前設(shè)置ref的DOM結(jié)構(gòu)),下面這種寫法相當(dāng)于把元素掛載到了this的自定義屬性c上/}
<input type="text" ref={(x)=>this.c=x}/>
</div>
)
}
}
render(<Input/>,document.getElementById('root'));

react元素中的事件綁定

給react元素(jsx元素)事件綁定對(duì)應(yīng)的方法時(shí),方法如果不做處理,那么執(zhí)行的時(shí)候方法中的this默認(rèn)是undefined
可通過如下方式解決:

  • 1、使用bind方式;(綁定的時(shí)候處理)

  • 2、使用ES6的箭頭函數(shù)(綁定的時(shí)候處理:在外層套一層箭頭函數(shù))

  • 3、使用ES7的箭頭函數(shù)(在類中定義的時(shí)候處理:直接寫為xxx=()=>{} )


Class中聲明的方法this默認(rèn)不綁定到實(shí)例上,需要手動(dòng)綁定.ES6語(yǔ)法規(guī)范;
在Class中利用箭頭函數(shù)聲明的方法,被規(guī)定為ES7語(yǔ)法,方法中的this都是當(dāng)前實(shí)例。

React中的生命周期

在react操作過程中,設(shè)定了很多的執(zhí)行階段,每個(gè)階段提供了不同方法,
[圖片上傳失敗...(image-a06d22-1541658838762)]

[初期渲染]

defaultProps:

  • 默認(rèn)屬性設(shè)置與校驗(yàn)

constructor:

  • 獲取屬性和設(shè)置默認(rèn)初始狀態(tài)

componentWillMount:(只執(zhí)行一次)

  • constructor執(zhí)行完成后,DOM結(jié)構(gòu)開始加載之前,執(zhí)行此方法,

render(開始渲染)

componentDidMount(只執(zhí)行一次)

  • DOM結(jié)構(gòu)加載完成后會(huì)自動(dòng)觸發(fā)此函數(shù)

[狀態(tài)發(fā)生改變時(shí)觸發(fā)]

shouldComponentUpdate

  • 狀態(tài)更新時(shí)會(huì)觸發(fā)此方法,返回布爾值

componentWillUpdate

  • 如果shouldComponentUpdate返回true則執(zhí)行此函數(shù),否則不執(zhí)行

render(開始渲染)

componentDidUpdate

  • 組件完成更新后觸發(fā)此方法

[卸載組件時(shí)觸發(fā)]

ReactDOM.unmountComponentAtNode

  • 刪除某個(gè)組件

componentWillUnmount(組件將要卸載)

  • 組件移除時(shí)會(huì)調(diào)用此方法,一般在這個(gè)方法中我們要清除定時(shí)器

[屬性發(fā)生改變時(shí)觸發(fā)]

componentWillReceiveProps

import React, {Component, PureComponent} from 'react';
import ReactDom, {render} from 'react-dom';
class Counter extends Component {/PureComponent是純組件:比較的是狀態(tài)的地址,如果是同一個(gè)地址,則不會(huì)更新,所以狀態(tài)最好采用新的狀態(tài)替換掉老的/
static defaultProps = {
name: 'zfpx'
};
constructor(props) {
super();
this.state = {number: 0};
console.log('1、constructor');
}
componentWillMount() {//同步代碼放在此處最好:如獲取本地的數(shù)據(jù):在渲染之前獲取數(shù)據(jù),只渲染一次
console.log('2、父組件將要加載');
}
componentDidMount() {
this.setState({number: this.state.number + 1});
console.log('5、父組件已經(jīng)掛載完成');
}
click = (e) => {
this.setState({number: this.state.number + 1});
};
//react可以在shouldComponentUpdate方法中優(yōu)化 PureComponent 可以幫我們做這件事
shouldComponentUpdate(nextProps, nextState) {//分別代表下一次的屬性和下一次的狀態(tài)
//組件是否需要更新
console.log('父組件是否需要更新');
return nextState.number % 2;//如果此函數(shù)返回了false,就不會(huì)調(diào)用render方法了
}
//亂用this.setState({})會(huì)造成棧溢出
componentWillUpdate() {
console.log('父組件將要更新');
}
componentDidUpdate() {
console.log('父組件完成更新');
}
render() {
console.log(3、父組件render(渲染));
return (
<div>
<p>{this.state.number}</p>
<ChildCounter n={this.state.number}/>
<button onClick={this.click}>+</button>
</div>
)
}
}
class ChildCounter extends Component {
componentWillReceiveProps(newProps) {//第一次不會(huì)執(zhí)行,之后屬性更新時(shí)才會(huì)執(zhí)行
/父組件渲染完成后發(fā)現(xiàn)有狀態(tài)更新,執(zhí)行componentWillReceiveProps,接著執(zhí)行shouldComponentUpdate,然后子組件渲染/
/componentWillReceiveProps只寫在子組件中/
console.log('子有新的屬性');
}
render() {
console.log('4、child render');
return (
<div>
{this.props.n}
</div>
)
}
shouldComponentUpdate(nextProps, nextState) {
console.log('子組件是否需要更新');
return nextProps.n % 3;
}
}
render(<Counter name={'計(jì)數(shù)器'}/>, window.root);

###<font color=red>**注意**</font>:
**最好不要在任何一個(gè)狀態(tài)更新時(shí)觸發(fā)的方法中寫**`this.setState()`,如果不加條件控制的話,會(huì)再次觸發(fā)狀態(tài)更新而導(dǎo)致方法執(zhí)行,這樣就會(huì)造成棧溢出;
```javascript

/頁(yè)面初始化時(shí)觸發(fā)/
//defaultProps:
//constructor
//componentWillMount(將要掛載)頁(yè)面初次加載時(shí)執(zhí)行1次
//render
//componentDidMount(完成掛載)頁(yè)面初次加載完成時(shí)執(zhí)行1次
/狀態(tài)更新時(shí)會(huì)觸發(fā)/
//shouldComponentUpdate
//componentWillUpdate
//render
//componentDidUpdat
/屬性更新/
//componentWillReceiveProps
/卸載/
// componentWillUnmount

獲取組件的所有子節(jié)點(diǎn)

在函數(shù)式聲明的組件中我們用props.children來(lái)獲取組件的所有子節(jié)點(diǎn);
在以類聲明的組件中我們用this.props.children來(lái)獲取組件的所有子節(jié)點(diǎn);

render(<Dinner>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容