西安網(wǎng)站建設(shè)價格明細(xì)seo入門到精通
React(三)
- 一、腳手架安裝和創(chuàng)建
- 1.安裝腳手架
- 2.創(chuàng)建腳手架
- 3.看看腳手架目錄
- 4.運(yùn)行腳手架
- 二、腳手架下從0開始寫代碼
- 三、組件化
- 1.類組件
- 2.函數(shù)組件
- 四、React的生命周期
- 1.認(rèn)識生命周期
- 2.圖解生命周期
- (1)Constructor
- (2)componentDidMount
- (3)componentDidUpdate
- (4)componentWillUnmount
- 3.演示生命周期
- 4.不常用的生命周期
- 五、父子組件通信
- 1.父傳子props接收
- 2.props接收數(shù)據(jù)類型限制和默認(rèn)值
- 3.子傳父用函數(shù)
- 4.案例練習(xí)
- 六、React插槽效果
- 1.通過props.children傳遞
- 2.通過props直接傳遞
- 3.作用域插槽
- 七、祖孫及更深層次的通信
- 1.{...props}解構(gòu)
- 2.Context的使用(類組件)
- 3.Context的使用(函數(shù)組件)
一、腳手架安裝和創(chuàng)建
首先安裝Node:保姆級別教程
1.安裝腳手架
在git bash中輸入: npm install create-react-app -g
,然后輸入create-react-app --version
,如果能正常顯示版本號,那么安裝就成功了。
2.創(chuàng)建腳手架
目錄下右鍵 => git bash => create-react-app 項(xiàng)目名
=> 回車等幾分鐘就歐了。
注意項(xiàng)目名不能包含大寫字母。
3.看看腳手架目錄
4.運(yùn)行腳手架
腳手架目錄下 => npm run start
,然后就可以看到非常帥氣的大花。
二、腳手架下從0開始寫代碼
沒啥用的先刪了,我們自己搭建src中的文件:
好,那么接下來我們重新寫一下src里面的文件:
index.js
//重寫react代碼,并且通過react渲染出來對應(yīng)的內(nèi)容
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.querySelector('#root'));
root.render(<App/>);
App.jsx
import React from 'react';
import HelloReact from './Components/HelloReact';class App extends React.Component {constructor() {super();this.state = {name:'zzy'}}render() {return (<div><h2>奧里給</h2><button>按鈕</button><HelloReact/></div>)}
}export default App;
HelloReact.jsx
import React from 'react';class HelloReact extends React.Component {constructor() {super();this.state = {name: 'react'}}render() {return (<div><h2>Hello React!</h2></div>)}
}export default HelloReact
三、組件化
1.類組件
類組件的定義有如下要求:
- 組件的名稱是大寫字符開頭(無論類組件還是函數(shù)組件)
- 類組件需要繼承自
React.Component
- 類組件必須實(shí)現(xiàn)
render
函數(shù)
使用class定義一個組件:
- constructor是可選的,我們通常在constructor中初始化一些數(shù)據(jù);
this.state
中維護(hù)的就是我們組件內(nèi)部的數(shù)據(jù);render()
方法是 class 組件中唯一必須實(shí)現(xiàn)的方法;
class App extends React.Component {constructor() {super();this.state = {name:'zzy'}}render() {return [<div><h2>奧里給</h2><button>按鈕</button><HelloReact/></div>,<div></div>]}
}
render函數(shù)的返回值可以是什么?
- React 元素:
通常通過 JSX 創(chuàng)建。
例如,<div />
會被 React 渲染為 DOM 節(jié)點(diǎn),<MyComponent />
會被 React 渲染為自定義組件;
無論是<div />
還是<MyComponent />
均為 React 元素(通過creatElement創(chuàng)建出來的東西)。 - 數(shù)組或 fragments:使得 render 方法可以返回多個元素。
- Portals:可以渲染子節(jié)點(diǎn)到不同的 DOM 子樹中。
- 字符串或數(shù)值類型:它們在 DOM 中會被渲染為文本節(jié)點(diǎn)
- 布爾類型或 null:什么都不渲染
2.函數(shù)組件
函數(shù)組件是使用function來進(jìn)行定義的函數(shù),只是這個函數(shù)會返回和類組件中render函數(shù)返回一樣的內(nèi)容。
函數(shù)組件有自己的特點(diǎn)(當(dāng)然,后面我們會講hooks,就不一樣了):
- 沒有生命周期,也會被更新并掛載,但是沒有生命周期函數(shù);
- 沒有
this
(組件實(shí)例); - 沒有內(nèi)部狀態(tài)(
state
);
我們來定義一個函數(shù)組件:
//函數(shù)式組件
function App() {//返回的東西和render返回的是一樣的。return <h1>我是一個函數(shù)組件</h1>
}export default App;
四、React的生命周期
1.認(rèn)識生命周期
生命周期的概念和vue中是一樣的,只不過在React中鉤子更少一些。
React內(nèi)部為了告訴我們當(dāng)前處于哪些階段,會對我們組件內(nèi)部實(shí)現(xiàn)的某些函數(shù)進(jìn)行回調(diào),這些函數(shù)就是生命周期函數(shù):
- 比如實(shí)現(xiàn)componentDidMount函數(shù):組件已經(jīng)掛載到DOM上時,就會回調(diào);
- 比如實(shí)現(xiàn)componentDidUpdate函數(shù):組件已經(jīng)發(fā)生了更新時,就會回調(diào);
- 比如實(shí)現(xiàn)componentWillUnmount函數(shù):組件即將被移除時,就會回調(diào);
我們可以在這些回調(diào)函數(shù)中編寫自己的邏輯代碼,來完成自己的需求功能;
我們談React生命周期時,主要談的類的生命周期,因?yàn)楹瘮?shù)式組件是沒有生命周期函數(shù)的;(后面我們可以通過hooks來模擬一些生命周期的回調(diào))
2.圖解生命周期
這張圖畫的還是非常不錯的,在這里插入代碼片
(1)Constructor
如果不初始化 state 或不進(jìn)行方法綁定,則不需要為 React 組件實(shí)現(xiàn)構(gòu)造函數(shù)。
constructor中通常只做兩件事情:
1、通過給 this.state
賦值對象來初始化內(nèi)部的state;
2、為事件綁定實(shí)例(this);
(2)componentDidMount
componentDidMount()
會在組件掛載后(插入 DOM 樹中)立即調(diào)用。
componentDidMount中通常進(jìn)行哪里操作呢?
依賴于DOM的操作可以在這里進(jìn)行;
在此處發(fā)送網(wǎng)絡(luò)請求就最好的地方;(官方建議)
可以在此處添加一些訂閱(會在componentWillUnmount取消訂閱);
(3)componentDidUpdate
componentDidUpdate()
會在更新后會被立即調(diào)用,首次渲染不會執(zhí)行此方法。
當(dāng)組件更新后,可以在此處對 DOM 進(jìn)行操作;
如果你對更新前后的 props 進(jìn)行了比較,也可以選擇在此處進(jìn)行網(wǎng)絡(luò)請求;(例如,當(dāng) props 未發(fā)生變化時,則不會執(zhí)行網(wǎng)絡(luò)請求)。
(4)componentWillUnmount
componentWillUnmount()
會在組件卸載及銷毀之前直接調(diào)用。
在此方法中執(zhí)行必要的清理操作;
例如,清除 timer,取消網(wǎng)絡(luò)請求或清除在 componentDidMount()
中創(chuàng)建的訂閱等;
3.演示生命周期
我們創(chuàng)建累組件App,并將HelloReact組件作為它的子組件:
App組件:
class App extends React.Component {constructor() {console.log('APP-constructor')super();this.state = {name: 'zzy'}}changeData() {this.setState({name: 'ht'})}render() {console.log('App-render');const { name } = this.state;return (<div><h2>{name}</h2><button onClick={() => this.changeData()}>點(diǎn)擊修改數(shù)據(jù)</button>{name == 'zzy' && <HelloReact />}</div>)}componentDidMount() {console.log('App-componentDidMount')}componentDidUpdate() {console.log('App-componentDidUpdate')}
}
HelloReact組件
class HelloReact extends React.Component {constructor() {console.log('HR-constructor')super();this.state = {name: 'react'}}render() {console.log('HR-render')return (<div><h2>Hello React!</h2></div>)}componentDidMount() {console.log('HR-componentDidMount')}componentDidUpdate() {console.log('HR-componentDidUpdate')}componentWillUnmount() {console.log('HR-componentWillUnmount')}
}
讓我們看一下控制臺的輸出:
不難看出生命周期的一個順序:
對于掛載來說:父組件constuctor
=> 父組件render
=> 子組件constructor
=> 子組件render
=> 子組件掛載完畢 => 父組件掛載完畢
對于更新來說,如果要讓子組件從頁面上消失,那么點(diǎn)擊跟新執(zhí)行父組件render函數(shù)后子組件會走銷毀的鉤子,然后走子組件更新完畢的鉤子,和圖是一樣滴。
4.不常用的生命周期
請參考:React官方文檔生命周期
五、父子組件通信
在了解React中的組件通信前,我們先搭建一個組件嵌套結(jié)構(gòu):
class App extends React.Component {render() {const { name } = this.state;return (<div><Header/><Main/><Footer/></div>)}
}
Footer和Header長得一樣
class Header extends React.Component {render() {return (<div><h2>Header</h2></div>)}
}
class Main extends Component {render() {return (<div><h2>Main</h2><Banner/><ProductList/></div>)}
}
class Banner extends Component {render() {return (<div>Banner</div>)}
}
class ProductList extends Component {render() {return (<div>ProductList</div>)}
}
1.父傳子props接收
這里我們主要研究Main
給Banner或ProductList
組件傳數(shù)據(jù),我們可以準(zhǔn)備一些靜態(tài)數(shù)據(jù)。
和vue一樣,在子組件標(biāo)簽處寫屬性名,傳變量用{}
,傳其他用字符串。
class Main extends Component {constructor() {super();this.state = {banner: ['動作射擊', '角色扮演', '策略運(yùn)營'],productList: ['古墓麗影','鏡之邊緣','神秘海域','奧里給']}}render() {const { banner, productList } = this.state;return (<div><h2>Main</h2><Banner banner={banner} /><ProductList title="商品列表" productList={productList}/></div>)}
}
然后子組件在constructor
中使用super(props)
繼承父類的props
屬性,并把props
這個屬性設(shè)置為傳進(jìn)來的值。
當(dāng)然啊,如果不寫constructor,也能自動接收到props,這是一個小細(xì)節(jié)。
class ProductList extends Component {constructor(props) {console.log(props);super(props);}render() {console.log(this.props)const { title, productList } = this.props;return (<div><div>{title}</div><ul>{productList.map(item => {return <li key={item}>{item}</li>})}</ul></div>)}
}
接下來我們就可以隊(duì)商品列表和標(biāo)題進(jìn)行展示:
2.props接收數(shù)據(jù)類型限制和默認(rèn)值
我們以Main
組件和ProductList
組件為例,將Main中的數(shù)據(jù)傳給ProductList
class Main extends Component {constructor() {super();this.state = {productList: ['古墓麗影','鏡之邊緣','神秘海域','奧里給']}}render() {const { banner, productList } = this.state;return (<div><h2>Main</h2><ProductList age={18} title="商品列表" productList={productList}/><ProductList age={10}/></div>)}
}
接下來我們就可以在ProductList
組件中對接收的數(shù)據(jù)進(jìn)行類型和默認(rèn)值的設(shè)置:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class ProductList extends Component {constructor(props) {console.log(props);super(props);}render() {console.log(this.props);const { age,title, productList } = this.props;return (<div><h3>ProductList</h3><div>{age}</div><div>{title}</div><ul>{productList.map(item => {return <li key={item}>{item}</li>})}</ul></div>)}
}
//props接收數(shù)據(jù)類型的限定
ProductList.propTypes = {title: PropTypes.string, age: PropTypes.number.isRequired, //必須是數(shù)字類型,必須有productList: PropTypes.array,
}//props接收數(shù)據(jù)的默認(rèn)值
ProductList.defaultProps = {title: '我的網(wǎng)頁的干活',productList: [],
}
如上,首先引入PropTypes
import PropTypes from 'prop-types';
然后在類組件名字后面設(shè)置屬性propTypes
。
默認(rèn)值則設(shè)置屬性defaultProps
。
其他細(xì)節(jié)可以翻看官方文檔:props默認(rèn)值大全
有些新東西比如我們現(xiàn)在可以通過static關(guān)鍵字直接在組件中寫:
class Greeting extends React.Component {static defaultProps = {name: 'zzy'}render() {return (<div>Hello, {this.props.name}</div>)}
}
3.子傳父用函數(shù)
這里的方法和vue中類似(vue還可以用組件自定義事件)
1、在父組件中通過屬性給子組件傳遞一個回調(diào)
class App extends React.Component {getData(data) {console.log(data);}render() {return (<div><Son getSonData={(data) => this.getData(data)}/></div>)}
}
2、子組件可以調(diào)用父組件傳的函數(shù),并把組件數(shù)據(jù)作為參數(shù)傳遞過去,然后父組件就能拿到子組件的數(shù)據(jù)并進(jìn)行后續(xù)操作。
class Son extends Component {constructor(props) {super(props);this.state = {sonData: '我是子組件數(shù)據(jù)'}}sendData() {//調(diào)用父組件傳的函數(shù)并把子組件數(shù)據(jù)作為參數(shù)傳過去this.props.getSonData(this.state.sonData);}render() {return (<div><h2>子組件</h2><button onClick={this.sendData.bind(this)}>點(diǎn)擊把子組件數(shù)據(jù)傳給父組件</button></div>)}
}
4.案例練習(xí)
給我實(shí)現(xiàn)下面這個效果:
這里我們把上面的導(dǎo)航欄封裝成組件Navigate
,整體的思路如下:
1、父組件存放數(shù)據(jù),先傳給子組件一份
2、子組件接收數(shù)據(jù)并遍歷展示
3、子組件添加按鈕,動態(tài)顯示類名active(原理就是通過點(diǎn)擊事件修改currentIndex)
4、父組件給子組件一個回調(diào),子組件動態(tài)顯示類名后,把當(dāng)前index傳給父組件
5、父組件接收index并存起來,然后在下面展示對應(yīng)的數(shù)據(jù)。
class App extends React.Component {constructor() {super();this.state = {navList: ['新款', '精選', '流行'],contentIndex: 0,}}getContent(index) {this.setState({contentIndex: index})}render() {let { navList, index } = this.state;return (<div><Navigation navList={navList} getContent={(index) => this.getContent(index)} /><h2>{navList[contentIndex]}</h2></div>)}
}
export class Navigate extends Component {constructor(props) {super(props);this.state = {currentIndex: 0,}}changeIndex(index) {this.setState({currentIndex: index})this.props.getContent(index);}render() {let { currentIndex } = this.state;let { navList } = this.propsreturn (<div className='nav'>{navList.map((nav, index) => {return (<divkey={nav}className={`title ${currentIndex === index ? 'active' : ''}`}onClick={() => this.changeIndex(index)}>{nav}</div>)})}</div>)}
}
備注:react中使用scss:npm add node-sass@npm:dart-sass
// 安裝scss:npm add node-sass@npm:dart-sass
.nav {border: 2px solid black;display: flex;justify-content: space-around;.title {padding: 10px;&.active {color: red;border-bottom: 3px solid red;}}
}
六、React插槽效果
插槽也是父子通信的一種方式
1.通過props.children傳遞
我們在父組件中的子組件標(biāo)簽內(nèi)部寫幾個div:
父組件App
render() {return (<div><Navigation><div className="left">左邊</div><div className="middle">中間</div><div className="right">右邊</div></Navigation></div>)}
那么子組件中就可以通過this.props.children
讀取到我們寫的這些div,如果寫多個,那么children是一個數(shù)組
,如果寫一個,那么children就是一個react元素
(當(dāng)然啊,我們可以通過propType限制children的類型是數(shù)組還是react元素)。
子組件
export class Navigation extends Component {render() {//props中的children可以接收到子組件插槽中的react元素let {children} = this.props;return (<div className='box'>{children}</div>)}
}
這樣的話,我們就可以拿著這些東西去子組件展示
2.通過props直接傳遞
上面這種用children接收的方式有個問題,就是接到父組件的react元素默認(rèn)是按照子組件書寫順序傳入children數(shù)組的,這樣通過索引去寫可能會有展示的順序問題。,而且比較麻煩
render() {//props中的children可以接收到子組件插槽中的react元素let {children} = this.props;console.log(children)return (<div className='box'><div>{children[0]}</div><div>{children[1]}</div><div>{children[2]}</div></div>)}
比第一種更好的方式,就是我們在父組件中的子組件標(biāo)簽上直接添加屬性,傳入相應(yīng)的react元素,子組件就可以通過props直接讀取,直接用,非常奈斯
父組件
render() {const left = <div className="left">左邊</div>;const middle = <div className="middle">中間</div>;const right = <div className="right">右邊</div>;return (<div>{/* 2.第二種方式:直接通過props傳react元素 */}<Navigation left={left} middle={middle} right={right}/></div>)}
子組件可以根據(jù)屬性名隨意切換順序,不用去通過索引找元素
子組件
render() {//props中的children可以接收到子組件插槽中的react元素let {children,left,middle,right} = this.props;console.log(children)return (<div className='box'>{left}{right}{middle}</div>)}
3.作用域插槽
本質(zhì)上還是父給子傳個函數(shù),然后子去調(diào)用并把當(dāng)前的數(shù)據(jù)傳給父組件,父組件根據(jù)數(shù)據(jù)的類型,返回不同的節(jié)點(diǎn),這里就不寫了。
七、祖孫及更深層次的通信
1.{…props}解構(gòu)
使用{...props}
這種react官方提供的解構(gòu)語法,可以直接把數(shù)據(jù)傳下去,舉個例子:
父組件export class App extends Component {constructor() {super();this.state = {person: { name: 'zzy', age: 18 }}}render() {let { person } = this.state;return (<div>{/* 1.第一種傳遞方式,繁瑣 */}<Son name={person.name} age={person.age}/>{/* 2.第二種傳遞方式:直接解構(gòu) */}<Son {...person}/></div>)}
}
兒子
export class Son extends Component {render() {return (<div>{/* 2.傳過來的props也是一個對象,直接結(jié)構(gòu)繼續(xù)往下傳 */}<GrandSon {...this.props} /></div>)}
}
孫子
export class GrandSon extends Component {render() {return (<div><GGrandSon {...this.props}/></div>)}
}
曾孫可以直接拿到數(shù)據(jù)直接用
export class GGrandSon extends Component {render() {let {name, age} = this.props;return (<div><h2>我拿到了數(shù)據(jù)</h2><div>{name}-{age}</div></div>)}
}
2.Context的使用(類組件)
使用Context可以直接把數(shù)據(jù)給任意一層組件:
使用步驟:
1、使用React.createContext()
定義一個context
2、父組件引入一下
import myContext from './context'
在子組件外邊包一個標(biāo)簽(也可以在孫子組件包,可以理解為給誰傳就給誰包,如果兒子組件,那么孫子組件也可以用步驟3的方法獲取數(shù)據(jù),曾孫也可以),名字是剛才定義的名字.Provider
,然后加上value
屬性,屬性值就是要傳的值。
{/* 3.第三種傳遞方式:context */}
<myContext.Provider value={{name:'ht', age:'10'}}><Son/>
</myContext.Provider>
3、子組件或?qū)O子組件通過添加contextType
屬性為可以把數(shù)據(jù)添加到this.context
上(說的官方點(diǎn),就是訂閱我們這個myContext):
import React, { Component } from 'react';
import GrandSon from './GrandSon';
import myContext from '../context'
export class Son extends Component {render() {console.log('Son',this.context);return (<div>{this.context.name}<GrandSon /></div>)}
}Son.contextType = myContext; //3.添加contextType
3.Context的使用(函數(shù)組件)
對于函數(shù)組件,我們需要這樣來做: