React.js 小書學(xué)習(xí) 之 【使用 JSX 描述 UI 信息】
- 從 JSX 到頁面 過程圖解:
JSX 到頁面過程圖解 -
JSX <=> JavaScript 對象:
JSX把HTML模板轉(zhuǎn)化為JS對象,使之符合JS語法,所以使用 React 和 JSX 一定會(huì)有編譯過程 -
React小書總結(jié)(提示注意點(diǎn)):
1、JSX 是 JavaScript 語言的一種語法擴(kuò)展,JSX 元素就是 JavaScript 對象,長得像 HTML,但并不是 HTML。
2、React.js 可以用 JSX 來描述你的組件長什么樣的。
3、JSX 在編譯的時(shí)候會(huì)變成相應(yīng)的 JavaScript 對象描述。
4、react-dom 負(fù)責(zé)把這個(gè)用來描述 UI 信息的 JavaScript 對象變成 DOM 元素,并且渲染到頁面上。
React.js 小書學(xué)習(xí) 之 【組件的 render 方法】
- React.js 中一切皆組件,用 React.js 寫的其實(shí)就是 React.js 組件;
- 插值表達(dá)式{}:{} 內(nèi)可以放任何 JavaScript 的代碼,包括變量、表達(dá)式計(jì)算、函數(shù)執(zhí)行等等。
React.js 小書學(xué)習(xí) 之 【組件的組合、嵌套和組件樹】
- 自定義的組件都必須要用大寫字母開頭,普通的 HTML 標(biāo)簽都用小寫字母開頭;
-
組件樹 圖解:
組件樹圖解 - 數(shù)據(jù)流動(dòng):數(shù)據(jù)在組件樹內(nèi)自上往下流動(dòng);
React.js 小書學(xué)習(xí) 之 【事件監(jiān)聽】
-
事件綁定(
on*={···}
):on*
屬性名必須是駝峰命名法;
如:onClick={this.handleClick}
;
注:handleClick,事件監(jiān)聽函數(shù),它是當(dāng)前組件類自己的一個(gè)實(shí)例方法 - 通常情況下,
on*
的事件監(jiān)聽只能用在普通的 HTML 的標(biāo)簽上,而不能用在組件標(biāo)簽上,如果需要能用到組件標(biāo)簽上是需要經(jīng)過特殊處理的; -
event
對象:
1、和普通瀏覽器一樣,事件監(jiān)聽函數(shù)會(huì)被自動(dòng)傳入一個(gè)event
對象,這個(gè)對象和普通的瀏覽器event
對象所包含的方法和屬性都基本一致;
2、React.js 將瀏覽器原生的event
對象封裝了一下,對外提供統(tǒng)一的 API 和屬性,這樣你就不用考慮不同瀏覽器的兼容性問題; - 關(guān)于事件監(jiān)聽函數(shù)中的
this
:
1、一般來講,某個(gè)組件類的實(shí)例方法里面的this
指向 當(dāng)前組件類實(shí)例本身,
但是 事件監(jiān)聽函數(shù)handleClick
里面的this
并不是,直接輸出會(huì)是null
orundefined
原因是:React.js 對事件監(jiān)聽函數(shù)handleClick
的調(diào)用并不是通過對象方法的方式調(diào)用的,而是直接通過函數(shù)調(diào)用的,所以 事件監(jiān)聽函數(shù)handleClick
內(nèi)并不能通過this
獲取到實(shí)例;
2、基于(1),如果我們需要在 事件監(jiān)聽函數(shù)handleClick
里面使用當(dāng)前實(shí)例的話,我們需要手動(dòng)地將實(shí)例方法bind
到當(dāng)前實(shí)例上再傳入給 React.js;
如:onClick={this.handleClick.bind(this)}
這樣就能在 事件監(jiān)聽函數(shù)handleClick
內(nèi)通過this
獲取到當(dāng)前組件類實(shí)例了; -
React小書總結(jié)(提示注意點(diǎn)):
1、為 React 的組件添加事件監(jiān)聽是很簡單的事情,你只需要使用 React.js 提供了一系列的on*
方法即可;
2、React.js 會(huì)給每個(gè)事件監(jiān)聽傳入一個(gè)event
對象,這個(gè)對象提供的功能和瀏覽器提供的功能一致,而且它是兼容所有瀏覽器的;
3、React.js 的事件監(jiān)聽方法需要手動(dòng)bind
到當(dāng)前組件類實(shí)例,這種模式在 React.js 中非常常用;
React.js 小書學(xué)習(xí) 之 【組件的 state 和 setState】
- 一個(gè)組件的顯示形態(tài)是可以由它數(shù)據(jù)狀態(tài)
state
和配置參數(shù)props
共同決定的; -
setState
方法由父類Component
所提供。當(dāng)我們調(diào)用這個(gè)函數(shù)的時(shí)候,React.js 會(huì)更新組件的狀態(tài)state
,并且重新調(diào)用render
方法,然后再把render
方法所渲染的最新的內(nèi)容顯示到頁面上; - 當(dāng)我們要改變組件的狀態(tài)的時(shí)候,不能直接用
this.state = xxx
這種方式來修改,如果這樣做 React.js 就沒辦法知道你修改了組件的狀態(tài),它也就沒有辦法更新頁面。所以,一定要使用 React.js 提供的setState
方法,它接受一個(gè)對象或者函數(shù)作為參數(shù); -
setState 合并 :
1、當(dāng)你調(diào)用setState
的時(shí)候,React.js 并不會(huì)馬上修改 state。而是把這個(gè)對象放到一個(gè)更新隊(duì)列里面,稍后才會(huì)從隊(duì)列當(dāng)中把新的狀態(tài)提取出來合并到state
當(dāng)中,然后再觸發(fā)組件更新;
2、在 React.js 內(nèi)部會(huì)把 JavaScript 事件循環(huán)中的消息隊(duì)列的同一個(gè)消息中的setState
都進(jìn)行合并以后再重新渲染組件,所以在使用 React.js 的時(shí)候,并不需要擔(dān)心多次進(jìn)行setState
會(huì)帶來性能問題; - 基于(4),setState的合并:
1、setState
方法接受一個(gè)對象參數(shù) 適用于 僅更新組件狀態(tài)state
,后續(xù)操作不依賴前一個(gè)setState
的結(jié)果的情況;
2、setState
方法接受一個(gè)函數(shù)參數(shù) 適用于 后續(xù)操作依賴前一個(gè)setState
的結(jié)果的情況,因?yàn)?React.js 會(huì)把上一個(gè)setState
的結(jié)果傳入這個(gè)函數(shù),你就可以使用該結(jié)果進(jìn)行運(yùn)算、操作,然后返回一個(gè)對象作為更新state
的對象:handleClickOnLikeButton () { this.setState((prevState) => { // prevState:上一個(gè) setState 的返回值 return { count: 0 } }) this.setState((prevState) => { // 上一個(gè) setState 的返回是 count 為 0,當(dāng)前返回 1 return { count: prevState.count + 1 } }) this.setState((prevState) => { // 上一個(gè) setState 的返回是 count 為 1,當(dāng)前返回 3 return { count: prevState.count + 2 } }) // 最后的結(jié)果是 this.state.count 為 3 }
- 更新組件狀態(tài)
state
,只需要setState
對應(yīng)狀態(tài)字段的key
value
即可,無需傳入整個(gè)對象;constructor (props) { super(props) this.state = { name: 'Tomy', isLiked: false } } handleClickOnLikeButton () { this.setState({ isLiked: !this.state.isLiked }) }
React.js 小書學(xué)習(xí) 之 【配置組件的 props】
-
React小書總結(jié)(提示注意點(diǎn)):
1、為了使得組件的可定制性更強(qiáng),在使用組件(引入組件)的時(shí)候,可以在組件標(biāo)簽上加屬性來傳入配置參數(shù),我們可以把任何類型的數(shù)據(jù)作為組件的參數(shù),包括字符串、數(shù)字、對象、數(shù)組、甚至是函數(shù)等等,這樣一個(gè)組件的樣式和行為都可以用props
來控制,就可以達(dá)到很好的可配置性;
2、組件可以在內(nèi)部通過this.props
獲取到配置參數(shù),組件可以根據(jù)props
的不同來確定自己的樣式和行為,達(dá)到可配置的效果;
3、可以通過給組件添加類屬性defaultProps
來配置默認(rèn)參數(shù);
4、props
一旦傳入,你就不可以在組件內(nèi)部對它進(jìn)行修改,但是你可以通過父組件主動(dòng)重新渲染的方式來傳入新的props
,從而達(dá)到更新的效果;import React, { Component } from 'react' import ReactDOM from 'react-dom' import './index.css' class LikeButton extends Component { // 定義 默認(rèn)的props配置參數(shù) defaultProps static defaultProps = { wordings: { likedText: '取消', unlikedText: '點(diǎn)贊' } } constructor () { super() this.state = { isLiked: false } } handleClickOnLikeButton () { this.setState({ isLiked: !this.state.isLiked }) // 子組建LikeButton 使用 父組件Index 傳遞過來的 props配置參數(shù) 處理行為 if (this.props.onClick) { this.props.onClick() } } render () { return ( <button onClick={this.handleClickOnLikeButton.bind(this)}> {/* 子組建LikeButton 獲取 父組件Index 傳遞過來的 props配置參數(shù) 處理樣式,獲取后的 props 在子組建中是不可變的,所以就會(huì)有在 父組件Index 中的 handleClickOnChange 方法 */} {this.state.isLiked ? this.props.wordings.likedText : this.props.wordings.unlikedText} ?? </button> ) } } class Index extends Component { constructor () { super() // 父組件Index 中初始化參數(shù) wordings this.state = { wordings: { likedText: '已贊', unlikedText: '贊' } } } // 父組件Index 中處理參數(shù) wordings 的變化,作為處理好的 props 配置參數(shù)傳遞到 子組件LikeButton handleClickOnChange () { this.setState({ wordings: { likedText: '取消', unlikedText: '點(diǎn)贊' } }) } render () { return ( <div> <LikeButton // 使用子組件LikeButton 并傳遞 配置參數(shù)到 子組件LikeButton 的 props // 在這里 wordings 定義子組建樣式,傳入的是 對象; onClick 定義子組建行為,傳入的是 函數(shù) wordings={this.state.wordings} onClick={() => console.log('Click on like button!')}/> <div> <button style={{marginTop: 20 + 'px'}} onClick={this.handleClickOnChange.bind(this)}> 修改 wordings </button> </div> </div> ) } } ReactDOM.render( <Index />, document.getElementById('root') )
React.js 小書學(xué)習(xí) 之 【state vs props】
- state vs props:http://huziketang.mangojuice.top/books/react/lesson12
注:上面鏈接已經(jīng)把兩者的區(qū)別介紹得比較詳盡了,還特意說明了函數(shù)式組件的編寫方式
React.js 小書學(xué)習(xí) 之 【渲染列表數(shù)據(jù)】
如果你往 JSX 插值表達(dá)式
{}
中放一個(gè)數(shù)組,React.js 會(huì)幫你把數(shù)組里面一個(gè)個(gè)元素羅列并且渲染出來;-
使用 map 渲染列表數(shù)據(jù):
{ users.map( (user, i) => <User key={i} user={user}/> ) }
;
注:這里的key
僅僅只是作為每個(gè)元素的唯一標(biāo)識(shí),雖然寫法上同props
傳參一樣,但并不是props
的一員,不能作為props
在User
這個(gè)子組件中使用,若在User
子組件使用,會(huì)發(fā)生以下錯(cuò)誤,其key
值為undefined
:class Lesson extends Component { handleClick (props, e) { console.log(props.key); // undefined, 不能作為 props 使用 console.log(props.index + '-' + props.lesson.title); } render () { const { key, index, lesson } = this.props; return ( <div onClick={this.handleClick.bind(this, { key, index, lesson })}> <h1>{lesson.title}</h1> <p>{lesson.description}</p> </div> ); } }
-
對于用表達(dá)式套數(shù)組羅列到頁面上的元素,都要為每個(gè)元素加上
key
屬性,這個(gè)key
必須是每個(gè)元素唯一的標(biāo)識(shí)。標(biāo)準(zhǔn)做法(最好),key
的值可以直接用后臺(tái)數(shù)據(jù)返回的id
,因?yàn)楹笈_(tái)的id
都是唯一的;import React, { Component } from 'react' import ReactDOM from 'react-dom' import './index.css' const users = [ { username: 'Jerry', age: 21, gender: 'male' }, { username: 'Tomy', age: 22, gender: 'male' }, { username: 'Lily', age: 19, gender: 'female' }, { username: 'Lucy', age: 20, gender: 'female' } ] class User extends Component { render () { const { user } = this.props; return ( <div> <div>姓名:{user.username}</div> <div>年齡:{user.age}</div> <div>性別:{user.gender}</div> <hr/> </div> ) } } class Index extends Component { render () { return ( <div> { users.map( (user, i) => <User key={i} user={user}/> ) } </div> ) } } ReactDOM.render( <Index />, document.getElementById('root') )
React.js 小書學(xué)習(xí) 之 【實(shí)戰(zhàn)分析:評(píng)論功能(一)】
- React.js 中一切都是組件,用 React.js 構(gòu)建的功能其實(shí)也就是由各種組件組合而成;
- 組件的劃分沒有特別明確的標(biāo)準(zhǔn),劃分組件的目的性是為了代碼可復(fù)用性、可維護(hù)性;
- 我們遵循一個(gè)原則:如果一個(gè)文件導(dǎo)出的是一個(gè)類,那么這個(gè)文件名就用大寫開頭;
-
構(gòu)建組件樹:遵循“自頂而下,逐步求精”的原則,我們從組件的頂層開始,再一步步往下構(gòu)建組件樹;
組件劃分(1)
組件劃分(2)
React.js 小書學(xué)習(xí) 之 【實(shí)戰(zhàn)分析:評(píng)論功能(二)】
- React.js 認(rèn)為所有的狀態(tài)都應(yīng)該由 React.js 的
state
控制,只要類似于<input />
、<textarea />
、<select />
這樣的輸入控件被設(shè)置了value
值,那么它們的value
值永遠(yuǎn)以被設(shè)置的state
值為準(zhǔn)。state
值不變,value
就不會(huì)變化; - 類似于
<input />
、<textarea />
、<select />
這些元素的value
值被 React.js 所控制、渲染的組件,在 React.js 當(dāng)中被稱為受控組件(Controlled Component)。對于用戶可輸入的控件,一般都可以讓它們成為受控組件,這是 React.js 所推崇的做法; -
CommentApp
組件將CommentInput
和CommentList
組合起來,它是它們倆的父組件,可以充當(dāng)橋接兩個(gè)子組件的橋梁。所以當(dāng)用戶點(diǎn)擊發(fā)布按鈕的時(shí)候,我們就將CommentInput
的state
當(dāng)中最新的評(píng)論數(shù)據(jù)傳遞給父組件CommentApp
,然后讓父組件把這個(gè)數(shù)據(jù)傳遞給CommentList
進(jìn)行渲染; -
向父組件傳遞數(shù)據(jù):父組件
CommentApp
只需要通過props
給子組件CommentInput
傳入一個(gè)回調(diào)函數(shù)。當(dāng)用戶點(diǎn)擊發(fā)布按鈕的時(shí)候,CommentInput
調(diào)用props
中的回調(diào)函數(shù)并且將state
傳入該函數(shù)即可;
React.js 小書學(xué)習(xí) 之 【實(shí)戰(zhàn)分析:評(píng)論功能(三)】
- 實(shí)現(xiàn)功能之前先理解、分析需求,劃分組件。并且掌握劃分組件的基本原則——可復(fù)用性、可維護(hù)性;
-
受控組件的概念,React.js 中的
<input />
、<textarea />
、<select />
等元素的value
值如果是受到 React.js 的控制,那么就是受控組件; - 組件之間使用
props
通過父元素傳遞數(shù)據(jù)的技巧:
向父組件傳遞數(shù)據(jù):父組件CommentApp
只需要通過props
給子組件CommentInput
傳入一個(gè)回調(diào)函數(shù)。當(dāng)用戶點(diǎn)擊發(fā)布按鈕的時(shí)候,CommentInput
調(diào)用props
中的回調(diào)函數(shù)并且將state
傳入該函數(shù)即可;
React.js 小書學(xué)習(xí) 之 【前端應(yīng)用狀態(tài)管理 — 狀態(tài)提升】
-
組件間數(shù)據(jù)共享:我們將組件之間共享的狀態(tài)交給組件最近的公共父節(jié)點(diǎn)保管,然后最近的公共父節(jié)點(diǎn)通過
props
把狀態(tài)(數(shù)據(jù))傳遞給子組件,這樣就可以在組件之間共享數(shù)據(jù)了; - 當(dāng)某個(gè)狀態(tài)被多個(gè)組件依賴或者影響的時(shí)候,就把該狀態(tài)提升到這些組件的最近公共父組件中去管理,用
props
傳遞數(shù)據(jù)或者函數(shù)來管理這種依賴或著影響的行為; - React.js 并沒有提供好的解決方案來管理這種組件之間的共享狀態(tài)。在實(shí)際項(xiàng)目當(dāng)中狀態(tài)提升并不是一個(gè)好的解決方案,所以需要引入 Redux 這樣的狀態(tài)管理工具來幫助我們來管理這種共享狀態(tài);
React.js 小書學(xué)習(xí) 之 【掛載階段的組件生命周期(一)】
- 組件的掛載(初始化組件 -> 掛載到頁面上的過程):React.js 將組件渲染,并且構(gòu)造 DOM 元素然后塞入頁面的過程;
- React.js 控制組件在頁面上掛載和刪除過程的幾個(gè)方法:
1、componentWillMount
:組件掛載開始之前,也就是在組件調(diào)用render
方法之前調(diào)用;
2、componentDidMount
:組件掛載完成以后,也就是 DOM 元素已經(jīng)插入頁面后調(diào)用;
3、componentWillUnmount
:組件對應(yīng)的 DOM 元素從頁面中刪除之前調(diào)用;
組件掛載和刪除過程方法(掛載階段生命周期及調(diào)用順序)
React.js 小書學(xué)習(xí) 之 【掛載階段的組件生命周期(二)】
- 把組件的
state
的初始化工作放在constructor
里面去做; - 在
componentWillMount
進(jìn)行組件的啟動(dòng)工作,例如 Ajax 數(shù)據(jù)拉取、定時(shí)器的啟動(dòng); - 有些組件的啟動(dòng)工作是依賴 DOM 的,例如動(dòng)畫的啟動(dòng),而
componentWillMount
的時(shí)候組件還沒掛載完成,所以沒法進(jìn)行這些啟動(dòng)工作,這時(shí)候就可以把這些操作放在componentDidMount
當(dāng)中; - 組件從頁面上銷毀的時(shí)候,有時(shí)候需要一些數(shù)據(jù)的清理,例如定時(shí)器的清理,就會(huì)放在
componentWillUnmount
里面去做;
React.js 小書學(xué)習(xí) 之 【更新階段的組件生命周期】
- 組件的掛載指的是將組件渲染并且構(gòu)造 DOM 元素然后插入頁面的過程。這是一個(gè)從無到有的過程,React.js 提供一些生命周期函數(shù)可以給我們在這個(gè)過程中做一些操作;
-
組件更新階段:說白了就是
setState
導(dǎo)致 React.js 重新渲染組件并且把組件的變化應(yīng)用到 DOM 元素上的過程,這是一個(gè)組件變化的過程; -
更新階段的組件生命周期:
1、shouldComponentUpdate(nextProps, nextState)
:你可以通過這個(gè)方法控制組件是否重新渲染。如果返回 false 組件就不會(huì)重新渲染。這個(gè)生命周期在 React.js 性能優(yōu)化上非常有用;
2、componentWillReceiveProps(nextProps)
:組件從父組件接收到新的 props 之前調(diào)用;
3、componentWillUpdate()
:組件開始重新渲染之前調(diào)用;
4、componentDidUpdate()
:組件重新渲染并且把更改變更到真實(shí)的 DOM 以后調(diào)用;
React.js 小書學(xué)習(xí) 之 【ref 和 React.js 中的 DOM 操作】
在 React.js 當(dāng)中可以直接通過
setState
的方式重新渲染組件,渲染的時(shí)候可以把新的props
傳遞給子組件,從而達(dá)到頁面更新的效果;React.js 這種重新渲染的機(jī)制幫助我們免除了絕大部分的 DOM 更新操作,也讓類似于 jQuery 這種以封裝 DOM 操作為主的第三方的庫從我們的開發(fā)工具鏈中刪除;
-
JSX 元素加上
ref
屬性:獲取掛載完成后的當(dāng)前 DOM 節(jié)點(diǎn)對象,ref
屬性值是一個(gè)函數(shù),接受一個(gè)參數(shù)(當(dāng)前 DOM 節(jié)點(diǎn)對象),在函數(shù)中我們把這個(gè)參數(shù)(DOM 元素)設(shè)置為組件實(shí)例的一個(gè)屬性,這樣以后我們就可以通過this.input
屬性獲取到這個(gè) DOM 元素了,再配合componentDidMount
生命周期函數(shù)使用這個(gè) DOM 元素,并且調(diào)用類似this.input.focus()
的 DOM API,進(jìn)行相關(guān)的DOM操作;
注:原則上,能不用 ref 就不用import React, { Component } from 'react' import ReactDOM from 'react-dom' import './index.css' class AutoFocusInput extends Component { componentDidMount () { // DOM 節(jié)點(diǎn)加載完,才能調(diào)用 DOM API, // 所以放在 componentDidMount 生命周期里 this.input.focus() } render () { return ( <div> <input ref={(input) => this.input = input} /> {/* 組件標(biāo)簽也可以加上 ref 獲取到的是這個(gè) Clock 組件在 React.js 內(nèi)部初始化的實(shí)例 但這并不是什么常用的做法,而且也并不建議這么做 */} {/* <Clock ref={(clock) => this.clock = clock} /> */} </div> ) } } ReactDOM.render( <AutoFocusInput />, document.getElementById('root') )
React.js 小書學(xué)習(xí) 之 【props.children 和容器類組件】
- 如果組件標(biāo)簽也能像普通的 HTML 標(biāo)簽?zāi)菢泳帉憙?nèi)嵌的結(jié)構(gòu),那么就方便很多了。實(shí)際上,React.js 默認(rèn)就支持這種寫法,所有嵌套在組件中的 JSX 結(jié)構(gòu)都可以在組件內(nèi)部通過
props.children
(是一個(gè)數(shù)組)獲取到;
React.js 小書學(xué)習(xí) 之 【dangerouslySetHTML 和 style 屬性】
出于安全考慮的原因(XSS 攻擊),在 React.js 當(dāng)中所有的表達(dá)式插入的內(nèi)容都會(huì)被自動(dòng)轉(zhuǎn)義成文本形式,就相當(dāng)于 jQuery 里面的
text(…)
函數(shù)一樣,任何的 HTML 格式都會(huì)被轉(zhuǎn)義成其對應(yīng)的文本形式渲染,也就是說,表達(dá)式插入的內(nèi)容中的HTML結(jié)構(gòu)渲染是會(huì)被原樣輸出,不會(huì)采用HTML語法格式渲染;-
React.js 提供了一個(gè)屬性
dangerouslySetInnerHTML
,可以讓我們設(shè)置動(dòng)態(tài)設(shè)置元素的 innerHTML;import React, { Component } from 'react' import ReactDOM from 'react-dom' import './index.css' class Editor extends Component { constructor() { super() this.state = { content: '<h1>React.js 小書</h1>' } } render () { return ( <div> {/* 正確 動(dòng)態(tài)設(shè)置元素的 innerHTML */} <div className='editor-wrapper' dangerouslySetInnerHTML={{__html: this.state.content}} /> {/* 錯(cuò)誤 動(dòng)態(tài)設(shè)置元素的 innerHTML */} <div className='editor-wrapper'> {this.state.content} </div> </div> ) } } ReactDOM.render( <Editor />, document.getElementById('root') )
因?yàn)樵O(shè)置
innerHTML
可能會(huì)導(dǎo)致跨站腳本攻擊(XSS),所以 React.js 團(tuán)隊(duì)認(rèn)為把事情搞復(fù)雜可以防止(警示)大家濫用這個(gè)屬性,dangerouslySetInnerHTML
這個(gè)屬性不必要的情況就不要使用;-
style屬性:
style
接受一個(gè)對象,這個(gè)對象里面是這個(gè)元素的 CSS 屬性鍵值對,原來 CSS 屬性中帶-
的元素都必須要去掉-
換成駝峰命名,如font-size
換成fontSize
,text-align
換成textAlign
;<h1 style={{fontSize: '12px', color: this.state.color}}>React.js 小書</h1>
React.js 小書學(xué)習(xí) 之 【PropTypes 和組件參數(shù)驗(yàn)證】
React.js 就提供了一種機(jī)制,讓你可以給組件的配置參數(shù)加上類型驗(yàn)證,如果傳進(jìn)來的配置參數(shù)類型不符合要求,便會(huì)強(qiáng)制報(bào)錯(cuò);
組件參數(shù)驗(yàn)證 依賴于 React 提供的第三方庫
prop-types
,安裝命令如下:
npm:npm install --save prop-types
yarn:yarn add prop-types
-
使用如下:
import React, { Component } from 'react'; import PropTypes from 'prop-types'; // 引入?yún)?shù)校驗(yàn)依賴 class Comment extends Component { // 定義組件類 參數(shù)類型屬性 static propTypes = { // 定義對應(yīng)參數(shù)類型: object-對象; isRequired-必須傳值 // 必傳 對象類型 的參數(shù) comment: PropTypes.object.isRequired } render () { const { comment } = this.props return ( <div className='comment'> <div className='comment-user'> <span>{comment.username} </span>: </div> <p>{comment.content}</p> </div> ) } }
-
React.js 提供的
PropTypes
提供了一系列的數(shù)據(jù)類型可以用來配置組件的參數(shù):
PropTypes 的數(shù)據(jù)類型 通過
PropTypes
給組件的參數(shù)做類型限制,可以在幫助我們迅速定位錯(cuò)誤,這在構(gòu)建大型應(yīng)用程序的時(shí)候特別有用;另外,給組件加上propTypes
,也讓組件的開發(fā)、使用更加規(guī)范清晰;
React.js 小書學(xué)習(xí) 之 【實(shí)戰(zhàn)分析:評(píng)論功能(四)】
- 數(shù)據(jù)加載操作等不依賴 DOM 操作的組件啟動(dòng)的操作都可以放在
componentWillMount
中進(jìn)行; - React 小書 小貼士(1):組件的私有方法都用
_
開頭,所有事件監(jiān)聽的方法都用handle
開頭。把事件監(jiān)聽方法傳給組件的時(shí)候,屬性名用on
開頭; - React 小書 小貼士(2):組件的內(nèi)容編寫順序(推薦)
1、static
開頭的組件類屬性,如defaultProps
、propTypes
;
2、構(gòu)造函數(shù),constructor
;
3、getter/setter
方法;
4、組件生命周期;
5、_
開頭的私有方法;
6、事件監(jiān)聽方法,handle*
;
7、render*
開頭的方法,有時(shí)候render()
方法里面的內(nèi)容會(huì)分開到不同函數(shù)里面進(jìn)行,這些函數(shù)都以render*
開頭;
8、render()
方法;
React.js 小書學(xué)習(xí) 之 【實(shí)戰(zhàn)分析:評(píng)論功能(五)】
暫無
React.js 小書學(xué)習(xí) 之 【實(shí)戰(zhàn)分析:評(píng)論功能(六)】
-
dangerouslySetInnerHTML
屬性:動(dòng)態(tài)設(shè)置元素的innerHTML
,該屬性能不用就不用,易造成 XSS 跨站腳本攻擊漏洞,如果用的話,也應(yīng)該對相應(yīng)的 HTML 字符進(jìn)行轉(zhuǎn)義處理,如下:/* ``符號(hào) 包含的內(nèi)容轉(zhuǎn) <code></code> 處理 */ <p dangerouslySetInnerHTML={{ __html: this._getProcessedContent(this.props.comment.content) }} /> /** * ``符號(hào) 包含的內(nèi)容轉(zhuǎn) <code></code> 處理 */ _getProcessedContent (content) { return content .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'") .replace(/`([\S\s]+?)`/g, '<code>$1</code>'); }
React.js 小書學(xué)習(xí) 之 【高階組件(Higher-Order Components)】
-
高階組件:高階組件是一個(gè)函數(shù)(而不是組件),它接收一個(gè)子組件作為參數(shù),返回一個(gè)新組件。新組件會(huì)使用傳入的組件作為子組件;
const NewComponent = higherOrderComponent(OldComponent);
高階組件的作用其實(shí)不言而喻,其實(shí)就是為了組件之間的代碼復(fù)用。組件可能有著某些相同的邏輯,把這些邏輯抽離出來,放到高階組件中進(jìn)行復(fù)用。高階組件內(nèi)部的包裝組件和被包裝組件之間通過
props
傳遞數(shù)據(jù);-
用法示例:
// 第一個(gè)文件 定義高階組件: 高階組件就是一個(gè)函數(shù),接收一個(gè)子組件作為參數(shù),返回一個(gè)新組件 import React, { Component } from 'react' export default (WrappedComponent, name) => { class NewComponent extends Component { constructor () { super() this.state = { data: null } } componentWillMount () { let data = localStorage.getItem(name) this.setState({ data }) } render () { return <WrappedComponent data={this.state.data} /> } } return NewComponent } // 第二個(gè)文件 使用高階組件(調(diào)用高階組件函數(shù)): 創(chuàng)建高階組件處理后的新組件并導(dǎo)出 import wrapWithLoadData from './wrapWithLoadData' // 從 ./wrapWithLoadData文件 引入高階組件 class InputWithUserName extends Component { render () { return <input value={this.props.data} /> } } InputWithUserName = wrapWithLoadData(InputWithUserName, 'username') export default InputWithUserName // 第三個(gè)文件 使用被高階組件加工過的導(dǎo)出后的新組件 import InputWithUserName from './InputWithUserName' // 從 ./InputWithUserName文件 引入 InputWithUserName 組件 class Index extends Component { render () { return ( <div> 用戶名:<InputWithUserName /> </div> ) } }
-
多層高階組件數(shù)據(jù)流向,如圖:
要求:需要先從 LocalStorage 中加載數(shù)據(jù),再用這個(gè)數(shù)據(jù)去服務(wù)器取數(shù)據(jù)
多層高階組件數(shù)據(jù)流向
React.js 小書學(xué)習(xí) 之 【React.js 的 context】
- 除非你覺得自己的 React.js 水平已經(jīng)比較爐火純青了,否則你永遠(yuǎn)不要使用
context
,只需要用好這些第三方的應(yīng)用狀態(tài)管理庫就行了,如:Redux
。就像你學(xué) JavaScript 的時(shí)候,總是會(huì)被提醒不要用全局變量一樣,React.js 的context
其實(shí)就像是組件樹上某顆子樹的全局變量; - React.js 的
context
:某個(gè)組件只要往自己的context
里面放了某些狀態(tài),這個(gè)組件之下的所有子組件都直接訪問這個(gè)狀態(tài)而不需要通過中間組件的傳遞。一個(gè)組件的context
只有它的子組件能夠訪問,它的父組件是不能訪問到的,你可以理解每個(gè)組件的context
就是瀑布的源頭,只能往下流不能往上飛; -
React小書總結(jié)(提示注意點(diǎn)):
1、一個(gè)組件可以通過getChildContext
方法返回一個(gè)對象,這個(gè)對象就是子樹的 context,提供 context 的組件必須提供childContextTypes
作為 context 的聲明和驗(yàn)證。
2、如果一個(gè)組件設(shè)置了 context,那么它的子組件都可以直接訪問到里面的內(nèi)容,它就像這個(gè)組件為根的子樹的全局變量。任意深度的子組件都可以通過contextTypes
來聲明你想要的 context 里面的哪些狀態(tài),然后可以通過this.context
訪問到那些狀態(tài)。
3、context 打破了組件和組件之間通過props
傳遞數(shù)據(jù)的規(guī)范,極大地增強(qiáng)了組件之間的耦合性。而且,就如全局變量一樣,context 里面的數(shù)據(jù)能被隨意接觸就能被隨意修改,每個(gè)組件都能夠改 context 里面的內(nèi)容會(huì)導(dǎo)致程序的運(yùn)行不可預(yù)料。
React.js 小書學(xué)習(xí) 之 【動(dòng)手實(shí)現(xiàn) Redux(一):優(yōu)雅地修改共享狀態(tài)】
- Redux 和 React-redux 并不是同一個(gè)東西。Redux 是一種架構(gòu)模式(Flux 架構(gòu)的一種變種),它不關(guān)注你到底用什么庫,你可以把它應(yīng)用到 React 和 Vue,甚至跟 jQuery 結(jié)合都沒有問題。而 React-redux 就是把 Redux 這種架構(gòu)模式和 React.js 結(jié)合起來的一個(gè)庫,就是 Redux 架構(gòu)在 React.js 中的體現(xiàn);
- 定義
dispatch
中間處理函數(shù),它專門負(fù)責(zé)數(shù)據(jù)的修改,所有對數(shù)據(jù)的操作必須通過dispatch
函數(shù)。它接受一個(gè)參數(shù)action
,這個(gè)action
是一個(gè)普通的 JavaScript 對象,里面必須包含一個(gè)type
字段來聲明你到底想干什么。dispatch
在swtich
里面會(huì)識(shí)別這個(gè)type
字段,能夠識(shí)別出來的操作才會(huì)執(zhí)行對共享狀態(tài)appState
的修改;
React.js 小書學(xué)習(xí) 之 【動(dòng)手實(shí)現(xiàn) Redux(二):抽離 store 和監(jiān)控?cái)?shù)據(jù)變化】
針對每個(gè)不同的 App,我們可以給
createStore
傳入初始的共享狀態(tài)數(shù)據(jù)appState
和一個(gè)描述數(shù)據(jù)變化的函數(shù)stateChanger
,然后生成一個(gè)store
。需要修改數(shù)據(jù)的時(shí)候通過store.dispatch
,需要獲取數(shù)據(jù)的時(shí)候通過store.getState
;通用的
createStore
,它可以產(chǎn)生一種我們新定義的數(shù)據(jù)類型store
,通過store.getState
我們獲取共享狀態(tài),而且我們約定只能通過store.dispatch
修改共享狀態(tài)。store
也允許我們通過store.subscribe
監(jiān)聽數(shù)據(jù)數(shù)據(jù)狀態(tài)被修改了,并且進(jìn)行后續(xù)的例如重新渲染頁面的操作;-
本節(jié)完整代碼示例如下:
function createStore(state, stateChanger) { const listeners = [] const subscribe = (listener) => listeners.push(listener) const getState = () => state const dispatch = (action) => { stateChanger(state, action) listeners.forEach((listener) => listener()) } return { getState, dispatch, subscribe } } function renderApp(appState) { renderTitle(appState.title) renderContent(appState.content) } function renderTitle(title) { const titleDOM = document.getElementById('title') titleDOM.innerHTML = title.text titleDOM.style.color = title.color } function renderContent(content) { const contentDOM = document.getElementById('content') contentDOM.innerHTML = content.text contentDOM.style.color = content.color } let appState = { title: { text: 'React.js 小書', color: 'red', }, content: { text: 'React.js 小書內(nèi)容', color: 'blue' } } function stateChanger(state, action) { switch (action.type) { case 'UPDATE_TITLE_TEXT': state.title.text = action.text break case 'UPDATE_TITLE_COLOR': state.title.color = action.color break default: break } } const store = createStore(appState, stateChanger) store.subscribe(() => renderApp(store.getState())) // 監(jiān)聽數(shù)據(jù)變化 renderApp(store.getState()) // 首次渲染頁面 store.dispatch({ // 修改標(biāo)題文本 type: 'UPDATE_TITLE_TEXT', text: '《React.js 小書》' }) store.dispatch({ // 修改標(biāo)題顏色 type: 'UPDATE_TITLE_COLOR', color: 'blue' })
React.js 小書學(xué)習(xí) 之 【動(dòng)手實(shí)現(xiàn) Redux(三):純函數(shù)(Pure Function)簡介】
- 純函數(shù)(Pure Function): 一個(gè)函數(shù)的返回結(jié)果只依賴于它的參數(shù),并且在執(zhí)行過程里面沒有副作用,我們就把這個(gè)函數(shù)叫做純函數(shù);
- 一個(gè)函數(shù)執(zhí)行過程產(chǎn)生了外部可觀察的變化那么就說這個(gè)函數(shù)是有副作用的;
- 外部可觀察的變化:除了修改外部的變量,一個(gè)函數(shù)在執(zhí)行過程中還有很多方式產(chǎn)生外部可觀察的變化,比如說調(diào)用 DOM API 修改頁面,或者你發(fā)送了 Ajax 請求,還有調(diào)用 window.reload 刷新瀏覽器,甚至是 console.log 往控制臺(tái)打印數(shù)據(jù)也是副作用;
- 純函數(shù)很嚴(yán)格,也就是說你幾乎除了計(jì)算數(shù)據(jù)以外什么都不能干,計(jì)算的時(shí)候還不能依賴除了函數(shù)參數(shù)以外的數(shù)據(jù);