0. React介紹
0.1 什么是React?
React(有時稱為React.js 或ReactJS)是一個為數據提供渲染HTML視圖的開源JavaScript庫。
它由FaceBook、Instagram和一個由個人開發者和企業組成的社群維護,現在國外比較流行的Facebook、Imgur、Airbnb、uber、Instagram,國內的美團、阿里、大搜車、去哪兒等都在使用React技術。
logo是個原子,中間是原子核,旁邊是3個電子的移動軌跡。
0.2 FB為什么推出React?
React 起源於 Facebook 的內部項目,因為該公司對市場上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設 Instagram 的網站。做出來以后,發現這套東西很好用,就在2013年5月開源了。
React是解決什么問題的,在官網可以找到這樣一句話:
We built React to solve one problem: building large applications with data that changes over time.
數據變化會帶來兩個問題,大量的DOM操作(自動操作DOM),邏輯及其復雜(狀態和內容對應起來 )
0.3 React歷史
React是由FaceBook的工程師Jordan Walke創建的,是受到php的HTML組件庫XHP影響,React在11年時,剛開始是部署在FaceBook的newsfeed;
隨后在12年時部署於Instagram,於13年5月在JSConf US宣布開源
14年成為facebook第一個在Github上達到1萬star的旗艦開源項目,
15年3月在JSConf,FaceBook發布了React Native,可以使用React來構建nativeApp,並提出自己的理念“learn once,write anywhere”。
16年4月,發布V15正式版本,但是依然不夠穩定,這畢竟是最新的技術。
0.4 React特點
聲明式設計:采用聲明范式,可以輕松描述應用
高效:通過對DOM的模擬,最大限度減少與DOM的交互
靈活:可以方便的搭配其它庫來使用
JSX:是js語法的擴展
組件:構建組件,方便復用
單向相應的數據流
0.5 如何學習React
中文社區:http://reactjs.cn/
gitbook:https://www.gitbook.com/book/hulufei/react-tutorial/details
菜鳥教程:http://www.runoob.com/react/react-tutorial.html
書籍:React引領未來的用戶界面開發框架(資料中有pdf電子書)
1.認識React
1.1 概述
1.1.1核心思想
React的核心思想是:封裝組件。
各個組件維護自己的狀態和UI,當狀態改變,自動重新繪制整個組件。
1.1.2 核心概念
React中的核心概念:
概念1:組件
概念2:JSX
JavaScriptXml,不是新語言,也沒有改變js的語法,只是對js的擴展,
使用React,建議使用JSX語法,原生js也可以,但是由於JSX在定義類似HTML這種樹形結構時,十分簡單明了,所欲推薦使用JSX語法。
概念3:Virtual DOM
如果真的這樣大面積的操作 DOM,性能會是一個很大的問題,所以 React 實現了一個虛擬 DOM,組件 DOM 結構就是映射到這個虛擬 DOM 上,React 在這個虛擬 DOM 上實現了一個 diff 算法,當要更新組件的時候,會通過 diff 尋找到要變更的 DOM 節點,再把這個修改更新到瀏覽器實際的 DOM 節點上,所以實際上不是真的渲染整個 DOM 樹。這個虛擬 DOM 是一個純粹的 JS 數據結構,所以性能會比原生 DOM 快很多。
前面提到 virtual DOM 和真實的 DOM 有着不用的語義, 但同時也有明顯不同的 API。
DOM 樹上的節點被稱為元素, 而 virtual DOM 是完全不同的抽象, 叫做 components。
component 的使用在 React 里極為重要, 因為 components 的存在讓計算 DOM diff 更高效
舉例:
比如說,現在你的list是這樣,
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
你想把它變成這樣
<ul>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
通常的操作是什么?
先把0, 1,2,3這些Element刪掉,然后加幾個新的Element 6,7,8,9,10進去,這里面就有4次Element刪除,5次Element添加。
而React會把這兩個做一下Diff,然后發現其實不用刪除0,1,2,3,而是可以直接改innerHTML,然后只需要添加一個Element(10)就行了,這樣就是4次innerHTML操作加1個Element添加,比9次Element操作快多了!
概念4:Data Flow
單向數據綁定。是指數據更新后會自動渲染到頁面,避免在業務中頻繁的DOM操作。
2. 搭建React開發環境
2.1 依賴
依賴三個庫: react.js 、react-dom.js 和 Browser.js ;
它們必須首先加載。其中,react.js 是 React 的核心庫,react-dom.js 是提供與 DOM 相關的功能,Browser.js 的作用是將 JSX 語法轉為 JavaScript 語法,
2.2 創建模板文件
上面代碼有兩個地方需要注意。首先,最后一個 <script> 標簽的 type 屬性為 text/babel 。這是因為 React 獨有的 JSX 語法,跟 JavaScript 不兼容。凡是使用 JSX 的地方,都要加上 type="text/babel"
3. Hello React
3.1. 開始
創建1.html
ReactDOM.render( <h1> hello React </h1>, document.getElementById('example') );
3.2. render方法介紹
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
ReactDOM.render 是 React 的最基本方法,用於將模板轉為 HTML 語言,並插入指定的 DOM 節點
4. JSX語法
4.1 基本語法規則
遇到HTML標簽(以<開頭),就用HTML來解析;遇到代碼塊(以{開頭)就用js來解析
JSX允許使用html語法來創建js對象,拿創建a標簽為例,如果使用原生js,是這樣的:
React.createElement('a', {href: 'https://facebook.github.io/react/'}, 'Hello!')
當使用jsx語法時,就變成了這樣:
<a href="https://facebook.github.io/react/">Hello!</a>
JSX的基本語法規則:
接下來,創建2.html,驗證上述語法。
var names = ['daxu','dongdong','wenhua']; ReactDOM.render( <div> { names.map(function (name) { return <div> hello {name}</div> }) } </div>, document.getElementById('example') )
細心的同學會發現,上邊的代碼運行時,會有個警告,是因為:
React為了方便DOM渲染,對於每個東西產生時都希望有一個單獨的key,我們上邊沒有設置,所以會有警告,這里先不管它。
4.2 JavaScript表達式
我們可以在JSX中使用JavaScript表達式,表達式寫在{}中,比如說:創建3.html
var arr = [ <h1>react is awesome</h1>, <h2>let's start to learn!</h2> ] ReactDOM.render( < div > {/* 注釋必須卸載花括號中*/} <h1>{2+3}</h1> <h2>{3 == 2?'true':'false'}</h2> {/*如果在花括號中執行數組,數組會自動展開所有成員*/} {arr} </div >,document.getElementById('example') )
5 組件
5.1 創建並使用組件
React 允許將代碼封裝成組件(component),然后像插入普通 HTML 標簽一樣,在網頁中插入這個組件。React.createClass 方法就用於生成一個組件類
創建4.html
上面代碼中,變量 HelloMsg 就是一個組件類。模板插入 < HelloMsg /> 時,會自動生成 HelloMsg 的一個實例(下文的"組件"都指組件類的實例)。所有組件類都必須有自己的 render 方法,用於輸出組件。
注意,組件類的第一個字母必須大寫,否則顯示會有問題,比如HelloMessage不能寫成helloMessage。另外,組件類只能包含一個頂層標簽,否則也會有問題。
在上述代碼上稍作修改:
組件的用法與原生的 HTML 標簽完全一致,可以任意加入屬性,比如 <HelloMsg name="daxu"> ,就是 HelloMsg 組件加入一個 name 屬性,值為 daxu。組件的屬性可以在組件類的 this.props 對象上獲取,比如 name 屬性就可以通過 this.props.name 讀取。上面代碼的運行結果如下。
var HelloMsg = React.createClass({ render: function () { return <div><h1> hello {this.props.firstname+this.props.name}</h1> <p>come on!</p></div>; } }) ReactDOM.render( <HelloMsg name='daxu' firstname='zhao'/>,document.getElementById('example') );
5.2 認識props
5.3 常用方法
this.props 對象的屬性與組件的屬性一一對應,但是有一個例外,就是 this.props.children 屬性。它表示組件的所有子節點
這里需要注意, this.props.children 的值有三種可能:如果當前組件沒有子節點,它就是 undefined ;如果有一個子節點,數據類型是 object ;如果有多個子節點,數據類型就是 array 。所以,處理 this.props.children 的時候要小心。
React 提供一個工具方法 React.Children 來處理 this.props.children 。我們可以用 React.Children.map 來遍歷子節點,而不用擔心 this.props.children 的數據類型是 undefined 還是 object
創建5.html
var MyList = React.createClass({ render: function () { return( <ol> { React.Children.map(this.props.children, function (child) { return <li>{child}</li> }) } </ol> ); } }); ReactDOM.render( <MyList> <span>lesson1</span> <span>lesson2</span> <span>lesson3</span> </MyList>, document.getElementById('example') );
5.3 復合組件
我們可以通過創建多個組件來合成一個組件,即把組件的不同功能點進行分離。
以下實例我們實現了輸出網站名字和網址的組件:
創建6.html
6、VDOM
組件並不是真實的 DOM 節點,而是存在於內存之中的一種數據結構,叫做虛擬 DOM (virtual DOM)。只有當它插入文檔以后,才會變成真實的 DOM 。根據 React 的設計,所有的 DOM 變動,都先在虛擬 DOM 上發生,然后再將實際發生變動的部分,反映在真實 DOM上,這種算法叫做 DOM diff ,它可以極大提高網頁的性能表現
但是,有時需要從組件獲取真實 DOM 的節點,這時就要用到 ref
屬性
上面代碼中,組件 MyComponent 的子節點有一個文本輸入框,用於獲取用戶的輸入。這時就必須獲取真實的 DOM 節點,虛擬 DOM 是拿不到用戶輸入的。為了做到這一點,文本輸入框必須有一個 ref 屬性,然后 this.refs.[refName] 就會返回這個真實的 DOM 節點。
需要注意的是,由於 this.refs.[refName] 屬性獲取的是真實 DOM ,所以必須等到虛擬 DOM 插入文檔以后,才能使用這個屬性,否則會報錯。上面代碼中,通過為組件指定 Click 事件的回調函數,確保了只有等到真實 DOM 發生 Click 事件之后,才會讀取 this.refs.[refName] 屬性。
React 組件支持很多事件,除了 Click 事件以外,還有 KeyDown 、Copy、Scroll 等
7、state
7.1 認識state
組件免不了要與用戶互動,React的一大創新,就是將組件看成是一個狀態機,一開始就有一個初始狀態,然后用戶互動,導致狀態變化,從而觸發重新渲染UI。
具體實現起來,React里有個state,只要更新組件的state,然后根據state重新渲染用戶界面(不要操作DOM),React來決定如何最高效的更新DOM。
7.2使用場合
針對text field的變化(用戶輸入)、服務器請求做出響應時,才需要用到state。
7.3 如何使用
方法介紹:
方法1:getInitialState
定義初始狀態,也就是一個對象
方法2:setState 可以獲取getInitialState中定義的對象,如果調用setState修改了狀態值,每次修改后,都將自動調用this.render方法,再次渲染組件
7.4 案例
var LikeButton = React.createClass({ getInitialState: function () { return {liked:false}; }, handlerClick: function (event) { this.setState({liked:!this.state.liked}); }, render: function () { var text = this.state.liked?'like':'do not like'; return ( <p onClick={this.handlerClick}> You {text} this.click to toggle! </p> ); } }) ReactDOM.render( <LikeButton />,document.getElementById('example') )
上面代碼是一個 LikeButton
組件,它的 getInitialState
方法用於定義初始狀態,也就是一個對象,這個對象可以通過 this.state
屬性讀取。當用戶點擊組件,導致狀態變化,this.setState
方法就修改狀態值,每次修改以后,自動調用 this.render
方法,再次渲染組件。
8、組件的生命周期
8.1 生命周期的狀態
Mounting 已經插入真是DOM
Updating 正在被重新渲染
Unmounting 已經移出DOM
8.2 狀態的處理函數
React 為每個狀態都提供了兩種處理函數:
1、 will 函數在進入狀態之前調用
2、 did 函數在進入狀態之后調用,
三種狀態共計五種處理函數:
- § componentWillMount() 准備插入
- § componentDidMount() 已經插入
- § componentWillUpdate(object nextProps, object nextState) 准備更新
- § componentDidUpdate(object prevProps, object prevState) 已經更新
- § componentWillUnmount() 准備從DOM中移除。
8.3 案例分析
var Hello = React.createClass({ getInitialState: function () { return { opacity: 1.0 }; }, componentWillMount: function () { console.log('准備加入DOM'); }, componentDidMount: function () { console.log('已經加入DOM'); this.timer = setInterval(function () { var op = this.state.opacity; op-=0.5; if(op < 0.1) { op = 1.0; }; this.setState({ opacity: op }); }.bind(this),1000); }, render: function () { return( <div style={{opacity:this.state.opacity}}> hello {this.props.name} </div> ) } } ) ReactDOM.render(<Hello name='daxu'/>,document.getElementById('example'));
9、使用ref和state來實現一個乘法計算器
效果圖如下:
var LianXi = React.createClass({ getInitialState:function(){ return {result:0}; }, handlerClick: function () { this.setState({result:this.refs.num1.value*this.refs.num2.value}); }, render: function () { return ( <div> <input type='number' ref='num1'/> <input type='number' ref='num2'/> <button onClick={this.handlerClick}>求乘數</button> <p >{this.state.result}</p> </div> ) } }); ReactDOM.render(<LianXi /> ,document.getElementById('example'));