這幾天讀了鬍子大哈老師的《React.js 小書》,感受獲益良多,真心推薦你們能夠看一看!css
示例代碼請點這裏html
jsx 的本質是 React.createElement 的語法糖,在 React 文檔中,有這麼一段話:react
JSX 對使用React 不是必須的。當你不想在你的構建環境中設置編譯器,那麼不使用 JSX 的 React 是很是方便的。git
每個 JSX 元素都是調用 React.createElement(component, props, ...children) 的語法糖,所以,任何你使用 JSX 來作的事均可以經過純 JavaScript 實現。github
<div class="root">
<div class="child">hello man</div>
</div>
複製代碼
觀察以上代碼,咱們能夠知道,每一個 DOM 其實都只包含了三個信息:標籤名稱、屬性、子元素。所以,每一個 DOM 元素,咱們均可以用一個 js 對象來標示。上面這段代碼咱們能夠這樣表示:數組
{
tag: 'div',
attr: {
class: 'root'
},
child: {
tag: 'div',
attr: {
class: 'child'
},
child: 'hello man'
}
}
複製代碼
在 React 中,咱們使用 React.createElement 來將上面的這種 js 對象來轉化成爲真正的 DOM 元素,至於 React.createElement 的內部實現,這裏暫不作深究。瀏覽器
若是咱們直接在代碼中調用 React.createElement ,經過這種 js 對象的方式來生成 DOM 的話,代碼看起來會有些冗長繁瑣,不太符合咱們追求簡單的想法,因而,React 發明了 JSX 語法,經過 JSX ,咱們能夠直接在 js 代碼中書寫 html 結構,最後交給 babel 來編譯爲上面這種 js 描述 DOM 的對象,最後經過 React.createElement 來構建真正的 DOM。bash
在一些業務場景下,咱們或許須要處理大量的具備相同總體佈局但是包含子節點的佈局內容卻又徹底不一樣的 DOM,例以下圖所示: babel
咱們能夠看到,雖然左右兩個元素中的內部元素徹底不一樣,但是卻又有着相同的總體佈局!這個時候,雖然咱們能夠用相同的命名,引入相同的 css 來減小咱們的工做量,但是,當這些元素分別位於不一樣的組件時,咱們就要爲每一個組件都引入同一份 css ,還要給他們一樣的類名,顯然,這樣有些繁瑣。若是這時用一個能夠公用的容器組件,咱們只須要引入這個容器組件,而後往裏邊填充相應的內容便可,顯然可讓代碼顯得更加整潔,使用起來也更加方便。app
咱們用 create-react-app 新建一個 React 項目(示例代碼請點這裏),清除掉一些不相干的內容,讓咱們的項目看起來更加清晰,而後新建兩個組件:
Container.js
import React from 'react';
export default class Container extends React.Component {
constructor(){
super()
}
render(){
return (
<div>
Container
</div>
)
}
}
複製代碼
head.js
import React from 'react';
export default class Head extends React.Component {
constructor() {
super()
}
render() {
return (
<div>
head
</div>
)
}
}
複製代碼
咱們在 index.js 中引入這兩個組件,將 Head 組件插入 Container 組件中:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Container from './container';
import Head from './head';
ReactDOM.render(
<Container>
<div>hello man</div>
<Head></Head>
</Container>,
document.getElementById('root')
);
複製代碼
打開瀏覽器,咱們看到,在頁面上,並無出現 head ,也沒有出現 hello man,而是顯示出了 container :
咱們在控制檯將 container 打印出來:
能夠看到在 container 的 props.children
屬性中,儲存着咱們插入進 Container 組件的內容,咱們只須要將這些信息渲染出來就能夠了,咱們改寫一下 Container 組件的代碼:
import React from 'react';
export default class Container extends React.Component {
constructor(){
super()
}
render(){
return (
<div className="container">
<div className="head">{this.props.children[0]}</div>
<div className="body">{this.props.children[1]}</div>
</div>
)
}
}
複製代碼
打開瀏覽器,咱們看到,以前填充在 Container 組件中的內容已經正常顯示,而且總體佈局結構也和咱們預期同樣。咱們能夠根據具體的業務,來填充具體的內部元素。
這裏有個問題須要注意,當咱們註釋掉插入 Container 組件的 Head 組件時,咱們發現,插入的 hello man
也不能正常顯示:
ReactDOM.render(
<Container>
<h1>hello man</h1>
{/* <Head></Head> */}
</Container>,
document.getElementById('root')
);
複製代碼
咱們在控制檯從新打印出 Container 組件的信息:
咱們看到,此時的
props.children
已經再也不是一個數組,而是一個對象,因此咱們在 Container 組件內部經過下標來取值的作法,顯然是有問題的。咱們須要來對
props.children
進行判斷,來肯定取值方式,具體實現並不難,這裏就再也不寫示例代碼了。
首先,咱們來看一下這些定義:
高階組件是一個函數,可以接受一個組件並返回一個新的組件。
高階組件是一個純函數,不會改變原來的組件,沒有反作用。
簡單來講,知足下面條件的函數能夠稱之爲純函數:
返回結果徹底依賴傳入的參數。
執行過稱中沒有反作用。
一樣的輸入獲得一樣的輸出。const gen = Math.random() 就不是純函數。(感謝FateRiddle指正)
咱們來看下面這兩個函數,瞭解下什麼是返回結果徹底依賴傳入的參數:
let d = 1;
function add(a,b){
return a + b
}
function all(c){
return c + d
}
複製代碼
當咱們執行 add
和 all
時,add
的返回結果徹底依賴於傳入的參數 a
和 b
的數值,add(3,6)
必定返回 9,但是當 all
執行時, all(3)
的返回之就不必定是 4,當 d
的值變爲 2 的時候, all(3)
的返回值就變成了 5,因此,在這裏 all
就不能稱之爲是一個純函數,而 add
則是一個純函數。
但是當咱們從新聲明一個變量,改寫下add
:
let obj = {
x: 2
}
function add(obj,b){
obj.x = 1;
return obj.x + b
}
複製代碼
咱們再次調用 add(obj,6)
,這個時候,雖然 add
的返回結果依舊徹底依賴與傳入的參數,可是,傳入的 obj
對象的 x
屬性的值卻發生了變化,這就是產生的反作用,因此,add
就不是一個純函數。
而高階函數是一個純函數,不會改變傳入的組件。
說了這麼多,但是高階組件有哪些用處呢?
咱們來看下面這兩段代碼:
組件一:
import React from 'react';
export default class One extends React.Component {
constructor(props) {
super(props)
this.state = {
higher: 0
}
}
componentWillMount(){
let higher = this.props.higher * 2
this.setState({
higher: higher
})
}
render() {
return (
<div>{this.state.higher}</div>
)
}
}
組件二:
import React from 'react';
export default class Two extends React.Component {
constructor(props) {
super(props)
this.state = {
higher: 0
}
}
componentWillMount(){
let higher = this.props.higher * 2
this.setState({
higher: higher
})
}
render() {
return (
<h1>{this.state.higher}</h1>
)
}
}
複製代碼
咱們看到,除了組件返回的標籤元素外,其餘代碼徹底相同。兩個組件都在 componentWillMount
時對傳入的數據作了邏輯處理,這時,咱們就能夠利用高階組件,將公共的邏輯代碼抽離出來:
const higher = function(Component,data){
class Higher extends React.Component {
constructor(props) {
super(props)
this.state = {
higher: 0
}
}
componentWillMount() {
let higher = data * 2
this.setState({
higher: higher
})
}
render() {
return (
<Component higher={this.state.higher}></Component>
)
}
}
return Higher
}
複製代碼
咱們將原始組件和高階組件插入頁面:
let CopyOne = higher(One,30);
let CopyTwo = higher(Two, 40);
ReactDOM.render(
<div>
<CopyOne></CopyOne>
<CopyTwo></CopyTwo>
<One higher={10}></One>
<Two higher={20}></Two>
{/* <Container>
<h1>hello man</h1>
<Head></Head>
</Container> */}
</div>,
document.getElementById('root')
);
複製代碼
打開瀏覽器咱們看到:
經過高階組件函數生成的組件和原始組件都被渲染出來,能夠看到,高階組件函數根據傳入的原始組件,生成了不一樣的組件,而且沒有改變原始組件。這篇筆記簡要的梳理了一下本身對 React 的理解,如有描述不對之處,還望你們批評指正!