要了解 JSX,首先先了解什麼三個主要問題,什麼事 VDOM,差別更新和 JSX 建模:html
VDOM,也叫虛擬 DOM,它是僅存於內存中的 DOM,由於還未展現到頁面中,因此稱爲 VDOMreact
var vdom = document.createElement("div");
上面這一句就是最簡單的虛擬 DOMgit
var vdom = document.createElement("div"); document.body.append(vdom);
上面這兩句就是把虛擬 DOM 轉化爲 真實 DOM,其實就是把節點 append 到頁面中es6
常見 DOM 操做,就三類:增、刪、改。對應的 DOM 操做以下:github
DOM 操做 | DOM 方法 |
---|---|
增長一個節點 | appendChild |
刪除一個節點 | removeChild |
更改一個節點 | replaceChild |
之前咱們寫代碼常常會拼接完模板,簡單粗暴的用$(el).html(template)
整塊節點替換算法
這樣作最大的問題在於性能,若是頁面比較小,問題不大,但若是頁面龐大,這樣會出現卡頓,用戶體驗會不好,因此解決辦法就是差量更新json
差量更新就是隻對局面的 HTML 片斷進行更新。好比你加了一個節點,那麼我就只更新這個節點,我無需整個模板替換。這樣作效率就會提升。但問題在於,不知道哪一個節點更新了,哪一個節點刪除了,哪一個節點替換了,因此咱們須要對 DOM 建模數組
DOM 建模,簡單點說就是用一個 JS 對象來表示 VDOM。
若是咱們能夠用一個 JS 對象來表示 VDOM,那麼這個對象上多一個屬性(增長節點),少一個屬性(刪除節點),或者屬性值變了(更改節點),就很清醒了性能優化
DOM 也叫 DOM 樹,是一個樹形結構,DOM 樹上有不少元素節點,要對 VDOM 進行建模,本質上就是對一個個元素節點進行建模,而後再把節點放回 DOM 樹的指定位置babel
JSX 建模,每一個節點都是由如下三部分組成:
{type:"div",props: null, children:[ {type:"img",props:{"src":"avatar.png", "className":"profile"},children:[], {type:"h3",props: null, children:[{[user.firstName, user.lastName].join(' ')}], ]}
上面 VDOM 建模是用下面的 HTML 結構轉出來的
var profile = ( <div> <img src="avatar.png" className="profile" /> <h3>{[user.firstName, user.lastName].join(" ")}</h3> </div> );
但這段代碼並非合法的 js 代碼,它是一種被稱爲 jsx 的語法擴展,經過它咱們就能夠很方便的在 js 代碼中書寫 html 片斷
本質上,jsx 是語法糖,上面這段代碼會被 babel 轉換成以下代碼
React.createElement( "div", null, React.createElement("img", { src: "avatar.png", className: "profile" }), React.createElement("h3", null, [user.firstName, user.lastName].join(" ")) );
而上面的這段被轉化的代碼是 將咱們的 VDOM 配合React.createElement
(通常應該是createElement
函數)轉化爲真實 DOM
注意,若是是自定義組件<App />
會轉化爲React.createElement(App, null)
,由於組件是class App extends React.Component {}
這樣定義的,因此 App 進入createElement
函數裏面就會變成是一個對象
這裏咱們能夠把這個函數放進createElement()
裏面生成一個 VDOM 對象,而後用生成的 VDOM 對象,配合render()
生成一個 DOM 插入頁面,從而轉變成真實 DOM 結構
React 元素,它是 React 中最小基本單位,咱們可使用上面提到的 JSX 語法輕鬆地建立一個 React 元素:
const element = <div>It is element</div>;
這個元素通過 babel 轉化以後會變成帶 React.createElement
的函數,而React.createElement()
構建 React 元素的時候。它接受三個參數,第一個參數能夠是一個標籤名。如 div、p,或者 React 組件。第二個參數爲傳入的屬性,如 class,style。第三個以及以後的參數,皆做爲組件的子組件。
React.createElement(type, [props], [...children]);
React.createElement
它執行後是一個普通的對象,因爲 React 元素不是真實的 DOM 元素,因此也沒辦法直接調用 DOM 原生的 API。上面的 JSX 轉譯後的對象大概是這樣的:
{ "_context": Object, "_owner": null, "key": null, "props": { "className": null, "children": "It is element" }, "ref": null, "type": "div" }
而 React 中有三種方法構建組件:
React.createClass()
因爲是舊版本的,咱們重點講兩種就夠了,一種是函數式(無狀態函數),一種是類式(ES6 類),就是用 ES6 class
咱們全部的組件都繼承自React.Component
函數式很簡單,就像咱們日常寫函數一個,接受一個參數做爲輸入,而後進行相應的輸出,只不過它輸出的 JSX 格式,注意組件只能有一個根元素:
function Wscats(props) { return <h1> {props.name}</h1>; } //ES6 const Wscats = ({ props }) => ( <div> <h1>{props.name}</h1> </div> );
類式組件以下,是一個純函數:
import React from 'react'; //推薦這種 class Wscats extends React.Component { render() { return <h1> {this.props.name}</h1> } } //or 這種方法將要廢棄 var Wscats = React.createClass({ render() { return <h1> {this.props.name}</h1> } }
React.createClass()
和ES6 class
構建的組件的數據結構本質都是類,而無狀態組件數據結構是純函數,但它們在 React 被能視爲組件,綜上所得組件是由元素構成的,元素是構造組件的重要部分,元素數據結構是普通對象,而組件數據結構是類或純函數。
類組件有生命週期和狀態,而函數組件則沒有。
React 給類組件定義了很是完善的生命週期函數,類組件渲染到頁面中叫掛載mounting
,因此渲染完成後,叫作componentDidMount
, 類組件的卸載叫Unmount
,因此類組件將要卸載 叫作componentWillUnmount
。咱們想要在何時使用狀態,就能夠直接調用生命週期函數,把想要作的事情寫到函數裏面,生命週期函數直接寫在類組件內部,類組件在初始化時會觸發 5 個鉤子函數:
id | 鉤子函數 | 用處 |
---|---|---|
1 | getDefaultProps() | 設置默認的 props,也能夠用 defaultProps 設置組件的默認屬性 |
2 | getInitialState() | 在使用 es6 的 class 語法時是沒有這個鉤子函數的,能夠直接在 constructor 中定義 this.state。此時能夠訪問 this.props |
3 | componentWillMount() | 組件初始化時只調用,之後組件更新不調用,整個生命週期只調用一次,此時能夠修改 state |
4 | render() | react 最重要的步驟,建立虛擬 dom,進行 diff 算法,更新 dom 樹都在此進行。此時就不能更改 state 了 |
5 | componentDidMount() | 組件渲染以後調用,能夠經過 this.getDOMNode()獲取和操做 dom 節點,只調用一次 |
類組件在更新時也會觸發 5 個鉤子函數:
id | 鉤子函數 | 用處 |
---|---|---|
6 | componentWillReceivePorps(nextProps) | 組件初始化時不調用,組件接受新的 props 時調用 |
7 | shouldComponentUpdate(nextProps, nextState) | react 性能優化很是重要的一環。組件接受新的 state 或者 props 時調用,咱們能夠設置在此對比先後兩個 props 和 state 是否相同,若是相同則返回 false 阻止更新,由於相同的屬性狀態必定會生成相同的 dom 樹,這樣就不須要創造新的 dom 樹和舊的 dom 樹進行 diff 算法對比,節省大量性能,尤爲是在 dom 結構複雜的時候。不過調用 this.forceUpdate 會跳過此步驟 |
8 | componentWillUpdate(nextProps, nextState) | 組件初始化時不調用,只有在組件將要更新時才調用,此時能夠修改 state |
9 | render() | 同上 |
10 | componentDidUpdate() | 組件初始化時不調用,組件更新完成後調用,此時能夠獲取 dom 節點。還有一個卸載鉤子函數 |
11 | componentWillUnmount() | 組件將要卸載時調用,一些事件監聽和定時器須要在此時清除 |
好比,頁面渲染完成後時間自動加一秒,這時還要涉及到類組件的狀態更改。React 不容許直接更改狀態, 或者說,咱們不能給狀態(如: date)進行賦值操做, 必須調用組件的setState()
方法去更改狀態。這裏寫一個函數changeTime
來更改狀態,詳情看 setState 更改狀態
changeTime
函數也能夠直接寫到組件裏面,根據 ES6 class
語法的規定,直接寫在類中的函數都會綁定在原型上,因此this.changeTime
能夠調用。但要保證 this 指向的是咱們這個組件,而不是其餘的東西, 這也是在 setInterval
中使用箭頭函數的緣由:
//類式組件 class Wscats extends React.Component { constructor(props) { super(props); this.state = { date: new Date() }; // 給組件添加狀態 } changeTime() { this.setState({ date: new Date() }); } // 生命週期函數 componentDidMount() { setInterval(() => { this.changeTime(); }, 1000); } render() { return ( <div> <h1>{this.props.name}</h1> <h2>如今時間是:{this.state.date.toLocaleTimeString()}</h2> </div> ); } } //組件的組合 function App() { return ( <div> <Wscats name="Oaoafly" /> <Wscats name="Eno" /> </div> ); }
PureCompoent
是更具性能的Component
的版本。它爲你提供了一個具備淺比較的 shouldComponentUpdate
方法,也就是上面咱們提到的那個類組件的生命週期,除此以外PureComponent
和 Component
基本上徹底相同。當狀態發生改變時,PureComponent
將對 props 和 state 進行淺比較。另外一方面,而Component
是不會比較的,當 shouldComponentUpdate
被調用時,組件默認的會從新渲染,因此能夠在Component
裏面本身手動調用shouldComponentUpdate
進行比較來獲取更優質的性能。
props(properties 的縮寫)和 state 都是普通的 JS 對象。它們都是用來保存信息的,這些信息能夠控制組件的渲染輸出。
而它們的一個重要的不一樣點就是:
class Test extends React.Component { constructor(props) { super(props); this.state = { message: "Hello World" }; } render() { return ( <div> <h1>{this.state.message} {this.props.message}</h1> </div> ); } } // 傳遞Props給組件,message=」Hi「會被 Test組件裏面的 props接受 React.render(<Test message="Hi">, document.getElementById('#demo'))
因此,狀態(State)與屬性(Props)很相似,但 state 是組件私有的控制的,除了自身外部任何組件都沒法訪問它,而 props 是組件從外部獲取的值,相似形參。
若是文章和筆記能帶您一絲幫助或者啓發,請不要吝嗇你的贊和收藏,你的確定是我前進的最大動力😁