上次總結的知識已經入門的差很少了,今天繼續總結第二階段的東西,若是發覺有錯請及時幫我糾正一下謝謝!
React組件中父組件能夠經過
<Children state={this.state} />
這種方式傳遞狀態,而後在子組件裏面能夠經過this.props
拿到這個狀態,可是兩個子組件若是須要傳遞狀態的話,子組件之間是沒法直接訪問獲得的。css
咱們能夠將這種組件之間共享的狀態交給組件最近的公共父節點保管,而後再經過
props
傳遞給另外一個子組件就好了,這樣就能夠在子組件之間共享數據了,把狀態交給公共父組件的行爲就叫狀態提高,當某個狀態被多個組件依賴或者影響的時候,就把該狀態提高到這些組件的最近公共父組件中去管理,用 props 傳遞數據或者函數來管理這種依賴或行爲。html
在父組件中傳遞一個回調函數而後子組件調用把東西傳過來,而後父組件再傳遞給其餘子組件,咱們就能夠實現這種效果了。前端
<!-- index -->
class Index extends Component {
constructor(props) {
super(props)
this.state = {
message: ''
}
}
<!-- 回調函數 -->
onSubString(options) {
<!-- 回調結束設置參數 -->
console.log(options)
this.setState({
message: options
})
}
render() {
return(
<div>
<!-- 向組件一傳入一個回調函數 -->
<Template1 onSubString={this.onSubString.bind(this)} />
<!-- 向組件二傳入組件一的參數 -->
<Template2 message={this.state.message} />
</div>
)
}
}
<!-- template1 -->
class Template1 extends Component {
static defaultProps = {
onSubString() {
console.log('默認值')
}
}
constructor(props) {
super(props)
this.state = {
message: 'is template1'
}
}
<!-- 組件掛載前調用回調函數傳入參數 -->
componentWillMount() {
this.props.onSubString(this.state.message)
}
render() {
return (
<div className="template1">
<h1>template1</h1>
</div>
)
}
}
<!-- template2 -->
class Template2 extends Component {
constructor(options) {
super(options)
}
render() {
return(
<div className="template2">
<!-- 接受組件一的參數 -->
<h1>這裏是template2 收到 {this.props.message}</h1>
</div>
)
}
}
<!-- 渲染 -->
ReactDOM.render(
<Index />,
document.getElementById('root')
)
複製代碼
上面是打開瀏覽器展現的效果,狀態提高項對於複雜的項目來講很很差維護和管理,對於子組件之間傳遞參數通常都是利用
Reudx
來操做,也就是把一組數據共享出來全部組件均可以拿到。api
對於各類具備架構能力的框架來講生命週期是少不了的,
React
的生命週期跟其餘框架的也差很少,首先得看看React
的運行過程。數組
ReactDOM.render(
<Index />,
document.getElementById('root')
)
編譯爲:
ReactDOM.render(
React.createElement(Index, null),
document.getElementById('root')
)
複製代碼
執行上面步驟時
React
也會執行幾個步驟:瀏覽器
// React.createElement 中實例化一個 Index
const Index = new Index(props, children)
// React.createElement 中調用 index.render 方法渲染組件的內容
const indexJsxObject = index.render()
// ReactDOM 用渲染後的 JavaScript 對象來來構建真正的 DOM 元素
const indexDOM = createDOMFromObject(indexJsxObject)
// ReactDOM 把 DOM 元素塞到頁面上
document.getElementById('root').appendChild(indexDOM)
複製代碼
咱們把 React.js 將組件渲染,而且構造 DOM 元素而後塞入頁面的過程稱爲 組件的掛載安全
-> constructor() // 初始化
-> componentWillMount() // 掛載前
-> render() // 渲染
// 而後構造 DOM 元素插入頁面
-> componentDidMount() // 掛載後
// ...
// 即將從頁面中刪除
-> componentWillUnmount() // 刪除
// 從頁面中刪除
複製代碼
除了上面掛載和刪除的生命週期還有更新的生命週期,更新的生命週期。咱們知道
setData
和組件的props
會給組件和另外一個組件帶來從新渲染,從而會觸發更新生命週期性能優化
1. componentWillUpdate() // 更新前
2. componentDidUpdate() // 更新後
3. componentWillReceiveProps() // 組件從父組件接收到新的 props 以前調用
複製代碼
除了上面三個之外還有一個極爲關鍵的
shouldComponentUpdate()
,這個方法能夠控制組件是否從新渲染。若是返回false
組件就不會從新渲染。這個生命週期在 React.js 性能優化上很是有用。bash
class Index extends Component {
constructor(props) {
super(props)
this.state = {
message: '組件更新'
}
}
updateMessage() {
this.setState({
message: this.state.message
})
}
componentWillUpdate() {
console.log('更新前')
}
componentDidUpdate() {
console.log('更新後')
}
render() {
return(
<div className="header">
<button onClick={this.updateMessage.bind(this)}>更新數據</button>
</div>
)
}
}
複製代碼
上面的代碼我點擊三次而且我只是調用了
setState
並無更新message
的數據,他也會每次都從新渲染,爲了節約性能,咱們可使用shouldComponentUpdate()
方法,shouldComponentUpdate()
是重渲染時render()
函數調用前被調用的函數,它接受兩個參數:nextProps
和nextState
,分別表示下一個props
和下一個state
的值。而且,當函數返回false
時候,阻止接下來的render()
函數的調用,阻止組件重渲染,而返回true
時,組件照常重渲染。架構
class Index extends Component {
constructor(props) {
super(props)
this.state = {
message: '組件更新'
}
}
updateMessage() {
this.setState({
message: this.state.message
})
}
<!-- 調用 setState 時控制是否須要從新渲染 -->
shouldComponentUpdate(nextProps, nextState) {
console.log(nextState.message) // 打印的是更新前的 message
console.log(nextProps) // 打印的是更新前的 props (這裏沒有 props 因此是空的對象)
<!-- 利用上一次的 message 跟更新後的對比 若是相同則返回 false -->
if (nextState.message == this.state.message) {
return false
}
}
componentWillUpdate() {
console.log('更新前')
}
componentDidUpdate() {
console.log('更新後')
}
render() {
return (
<div className="header">
<button onClick={this.updateMessage.bind(this)}>更新數據</button>
</div>
)
}
}
複製代碼
我這裏點擊了4次,可是沒有打印其餘兩個更新的生命週期,只打印了我在
shouldComponentUpdate()
方法裏打印的東西,這說明頁面並無從新渲染。
咱們再繼續看
props
的從新渲染。組件的state
沒有變化,而且從父組件接受的props
也沒有變化,也有可能從新渲染。
<!-- Son -->
class Son extends Component {
<!-- 點擊時候把父級傳過來的參數原封不動傳回去 -->
updateMessage() {
this.props.handleClick(this.props.message)
}
render() {
<!-- 看看是否從新渲染 -->
console.log(this.props.message, 'son')
return(
<div>
<button onClick={this.updateMessage.bind(this)}>{this.props.message}</button>
</div>
)
}
}
<!-- Index -->
class Index extends Component {
constructor(props) {
super(props)
this.state = {
message: '組件更新',
}
}
updateMessage() {
this.setState({
message: this.state.message
})
}
componentWillUpdate() {
console.log('更新前')
}
componentDidUpdate() {
console.log('更新後')
}
<!-- 把子級的參數從新設置 -->
handleClick(message) {
this.setState({
message: message
})
}
render() {
return (
<div className="header">
<Son handleClick={this.handleClick.bind(this)} message={this.state.message} />
</div>
)
}
}
複製代碼
我這裏點擊三次,不只父組件會從新渲染,連子組件都會從新渲染。咱們再來處理一下這個狀況。
<!-- Son -->
shouldComponentUpdate(nextProps, nextState) {
console.log(nextProps.message)
if (nextProps.message === this.props.message) {
return false
}
}
複製代碼
若是在子組件加上這個生命週期,監聽父級上次傳過來的
props
是否和此次穿過來的props
是否同樣,而後咱們再點擊時子組件就不會從新渲染了。
這裏進來時渲染打印一次,而後以後就沒有打印了,以後點的兩次只打印父級的更新生命週期還有子級
shouldComponentUpdate()
裏面打印的props
。
通常來講相似
Vue
和React
這種具備MVVM
思想的框架通常能作到像setData
同樣的從新渲染,還有父子組件傳參來更新狀態,這些都不須要經過手動操做DOM
來實現,雖然這些從新渲染機制幫助咱們免除大部分的DOM
操做,可是有部分仍是不能知足,最多見的就是獲取元素的寬度和高度來進行操做,或者是控制輸入框的狀態等等,這些都是須要依靠DOM
操做來實現的。
React
提供了ref
屬性來幫助咱們獲取 已經掛載 的元素的DOM
節點
class Index extends Component {
componentDidMount() {
console.log(this.indexBox)
}
render() {
return(
<div className="index" ref={(indexBox) => this.indexBox = indexBox} style={{width: '500px', height: '500px', backgroundColor: 'green'}}></div>
)
}
}
複製代碼
獲取到元素以後你能夠調用他原有的
api
,好比輸入框的禁止輸入以及自動聚焦等等操做。
能不用 ref 就不用。特別是要避免用 ref 來作 React.js 原本就能夠幫助你作到的頁面自動更新的操做和事件監聽。多餘的 DOM 操做實際上是代碼裏面的「噪音」,不利於咱們理解和維護。
除此以外咱們還能夠給組件加上
ref
class Header extends Component {
render(){
return(
<div>
<h1>組件組件組件</h1>
</div>
)
}
}
class Index extends Component {
componentDidMount() {
console.log(this.header)
}
render() {
return(
<div>
<Header ref={(header)=> this.header = header } />
</div>
)
}
}
複製代碼
這種用法並不經常使用,也不推薦這樣作。
組件自己是一個不帶任何內容的方形的容器,我能夠在用這個組件的時候給它傳入任意內容。
class Index extends Component {
render() {
console.log(this.props.children)
return(
<div className="index">
{this.props.children}
</div>
)
}
}
ReactDOM.render(
<Index>
<h1>我洗渣渣輝</h1>
<h2>我洗古天樂</h2>
</Index>,
document.getElementById('root')
)
複製代碼
這個屬性打印出來的是一個數組,把咱們嵌套的
jsx
元素一個個都放到數組當中,而後經過props.children
傳給Index
,咱們能夠把它們分別存在不一樣的地方。
class Index extends Component {
render() {
console.log(this.props.children)
return(
<div className="index">
<div className="header">
<!-- 第0條 -->
{this.props.children[0]}
</div>
<div className="main">
<!-- 第1條 -->
{this.props.children[1]}
</div>
</div>
)
}
}
複製代碼
使用自定義組件的時候,能夠在其中嵌套
JSX
結構。嵌套的結構在組件內部均可以經過props.children
獲取到,這種組件編寫方式在編寫容器類型的組件當中很是有用。
出於安全考慮的緣由(XSS 攻擊),在
React.js
當中全部的表達式插入的內容都會被自動轉義,就至關於jQuery
裏面的text()
函數同樣,任何的HTML
格式都會被轉義掉。
class Index extends Component {
constructor(props) {
super(props)
this.state = {
content: '<h1>富文本轉義</h1>'
}
}
render() {
return(
<div className="index">
{this.state.content}
</div>
)
}
}
複製代碼
若是要把
<h1>
轉爲標籤,可使用dangerouslySetHTML
屬性動態設置元素的innerHTML
。
class Index extends Component {
constructor(props) {
super(props)
this.state = {
content: '<h1>富文本轉義</h1>'
}
}
render() {
console.log(this.props.children)
return(
<div className="index" dangerouslySetInnerHTML={{__html: this.state.content}}></div>
)
}
}
複製代碼
在添加了
dangerouslySetHTML
屬性的元素裏面不能再嵌套任何東西( 包括文本 ),不然將會報錯。設置 innerHTML 可能會致使跨站腳本攻擊(XSS),React.js 團隊認爲把事情搞複雜能夠防止(警示)你們濫用這個屬性。這個屬性沒必要要的狀況就不要使用。
在
React
的style
裏面的css屬性須要轉化爲對象才能傳給元素,而且全部元素須要帶-
的都要轉爲駝峯命名,好比font-size
、background-color
,須要寫爲fontSize
、backgroundColor
纔有效果。
<div className="index">
<h1 style={{color: 'red', fontSize: 14}}>文本文本</h1>
</div>
複製代碼
React.js 的組件實際上是爲了構建大型應用程序而生。可是由於 JavaScript 這樣的特性,你在編寫了一個組件之後,根本不知作別人會怎麼使用你的組件,往裏傳什麼亂七八糟的參數,咱們能夠利用
propTypes
這個庫來指定傳遞過來參數的類型,而這個庫在後面的context
也要用到。
class Template1 extends Component {
render() {
return(
<h1>{ this.props.userData.name }</h1>
)
}
}
class Index extends Component {
constructor(props) {
super(props)
this.state = {
userData: {
name: 'jack',
age: 18
}
}
}
render() {
return(
<div className="index">
<Template1 userData={ this.state.userData } />
</div>
)
}
}
複製代碼
上面的組件不管父級傳什麼東西都是沒有限制了,這讓其餘人共同開發項目很複雜,有時會由於參數問題處處找
Bug
,這時你能夠給組件配置參數加上 類型驗證 。
import PropTypes from 'prop-types'
class Template1 extends Component {
static propTypes = {
userData: PropTypes.object // 接受對象參數
}
render() {
return(
<h1>{ this.props.userData.name }</h1>
)
}
}
class Index extends Component {
constructor(props) {
super(props)
this.state = {
userData: {
name: 'jack',
age: 18
}
}
}
render() {
return(
<div className="index">
<!-- 這裏傳遞的是數值 -->
<Template1 userData={ 123 } />
</div>
)
}
}
複製代碼
如今能夠驗證參數的數據類型了,若是傳的類型與設置的類型不一致,將會報出警告。
propTypes
提供了多種 數據驗證 ,還能夠限制單個元素傳遞,而且當參數沒有傳遞時還能夠設置默認值,若是在不傳遞參數下還不設置默認值,則默認爲undefined
,這時會出現一個很不友好的報錯信息。
這時咱們可使用
isRequired
來強調這個參數必須傳入。
static propTypes = {
userData: PropTypes.object.isRequired
}
複製代碼
這時報錯信息裏面會再添加一條提示,這樣的話就更容易找到問題所在。