備戰秋招,複習基礎。若有錯誤,歡迎批評指正,共同進步!javascript
聲明式:建立用戶交互界面,高效渲染頁面。聲明式編寫UI方便調試。html
組件化:使用組件傳遞數據,將應用狀態和DOM拆分開。當內部狀態(this.state
)改變時,組件會調用方法(render()
)從新渲染。java
箭頭函數:var func = (x,y) => {return x+y;};
react
class → className
for → htmlFor
算法
樣式 style須要{{}}
express
JSX:很像XML的js語法擴展。可用{js表達式}
→ 沒有if else,只有三元表達式redux
Key:每次構建動態列表的時候,指定一個合適的key。在同一級元素之間保證惟一!segmentfault
組件API:promise
參考資料:react基本原理及性能優化性能優化
react只負責解決view層的渲染
參考資料:淺談React的最大亮點——虛擬DOM
虛擬DOM是在DOM的基礎上創建了一個抽象層,是一個js對象。對數據和狀態所作的任何改動,都會被自動且高效的同步到虛擬DOM,最後再批量同步到DOM中。
虛擬DOM能夠確保只對界面上真正變化的部分進行實際的DOM操做。
真實的DOM tree結構:
<div id="container">
<p>Real DOM</p>
<ul>
<li class="item">Item 1</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
</ul>
</div>
複製代碼
虛擬DOM結構:
let virtualDomTree = CreateElement('div', { id: 'container' }, [
CreateElement('p', {}, ['Virtual DOM']),
CreateElement('ul', {}, [
CreateElement('li', { class: 'item' }, ['Item 1']),
CreateElement('li', { class: 'item' }, ['Item 2']),
CreateElement('li', { class: 'item' }, ['Item 3']),
]),
]);
let root = virtualDomTree.render(); //轉換爲一個真正的dom結構或者dom fragment
document.getElementById('virtualDom').appendChild(root);
function CreateElement(tagName, props, children) {
if (!(this instanceof CreateElement)) {
return new CreateElement(tagName, props, children);
}
this.tagName = tagName;
this.props = props || {};
this.children = children || [];
this.key = props ? props.key : undefined;
}
CreateElement.prototype.render = function() {
let el = document.createElement(this.tagName);
let props = this.props;
for (let propName in props) {
setAttr(el, propName, props[propName]);
}
this.children.forEach((child) => {
let childEl = (child instanceof Element) ? child.render() : document.createTextNode(child);
el.appendChild(childEl);
});
return el;
};
複製代碼
參考資料:React之diff算法
計算出Virtual DOM中真正變化的部分,並只針對該部分進行原生DOM操做,而非從新渲染整個頁面。
diff對樹進行分層比較,只對比兩棵樹同級別的節點。跨層級移動節點,將會致使節點刪除,從新插入,沒法複用。
diff對組件進行類比較,類相同的遞歸diff子節點,不一樣的直接銷燬重建。diff對同一層級的子節點進行處理時,會根據key進行簡要的複用。兩棵樹中存在相同key的節點時,只會移動節點。
在對比同一層級的子節點時,diff算法會以新樹的第一個子節點做爲起點遍歷新樹,尋找舊樹中與之相同的節點。若是節點存在,則移動位置。若是不存在,則新建一個節點。 在這過程當中,維護了一個字段lastIndex,這個字段表示已遍歷的全部新樹子節點在舊樹中最大的index。在移動操做時,只有舊index小於lastIndex的纔會移動。 這個順序優化方案其實是基於一個假設,大部分的列表操做應該是保證列表基本有序的。 能夠推倒倒序的狀況下,子節點列表diff的算法複雜度爲O(n2)
getDefaultProps()
設置默認的propsgetInitialState()
定義初始this.statecomponentWillMount()
修改state的最後一次機會render()
建立一個虛擬DOM,表示組件的輸出(惟一必須的方法)componentDidMount()
訪問真實DOM,進行數據監聽、數據請求componentWillReceiveProps()
可更新state,觸發rendershouldComponentUpdate()
決定是否須要從新渲染,優化渲染性能componentWillUpdate()
從新渲染前調用componentDidUpdate()
訪問並修改DOM,監聽props或state變化。這裏放setState必須加條件,不然會致使死循環。componentWillUnmount()
將組件從DOM中卸載並銷燬`理想狀況:
setState是「異步」的,調用setState只會提交一次state修改到隊列中,不會直接修改this.state。 等到知足必定條件時,react會合並隊列中的全部修改,觸發一次update流程,更新this.state。
所以setState機制減小了update流程的觸發次數,從而提升了性能。
因爲setState會觸發update過程,所以在update過程當中必經的生命週期中調用setState會存在循環調用的風險。
另外用於監聽state更新完成,可使用setState方法的第二個參數,回調函數。在這個回調中讀取this.state就是已經批量更新後的結果。
特殊狀況:在實際開發中,setState的表現有時會不一樣於理想狀況。主要是如下兩種。
在mount流程中調用setState:不會進入update流程,隊列在mount時合併修改並render。
在setTimeout/Promise回調中調用setState:將不會進行隊列的批更新,而是直接觸發一次update流程。 這是因爲setState的兩種更新機制致使的,只有在批量更新模式中,纔會是「異步」的。
setState會引起一次組件的更新過程,從而引起頁面的從新繪製。主要會涉及如下幾個生命週期函數:
資料參考:詳解在React.js中使用PureComponent的重要性和使用方式
資料參考:React 的 PureComponent Vs Component
資料參考:什麼時候使用Component仍是PureComponent?
PureComponent改變了生命週期方法 shouldComponentupdate
,而且會自動檢查組件是否須要從新渲染。這時,只有PureComponent檢測到 state 或者 props 發生變化(引用類型的引用變化,或基本類型string number值變化)時,PureComponent纔會調用 render 方法。
淺比較源碼:
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps) || ! shallowEqual(inst.state, nextState);
}
複製代碼
使用pureComponent至關於在component中優化檢測:
-----這是component中的優化 在purecomponent中默認包含-----
shouldComponentUpdate(nextProps, nextState) {
return (nextState.person !== this.state.person);
}
複製代碼
class MyComponent extends PureComponent {...}
複製代碼
若是一個純組件(PureComponent)的 state 或 props 引用了一個新對象,那麼這個組件就會被從新渲染(re-render)。
handleClick() {
this.setState(prevState => ({
words: prevState.items.concat(['new-item']) ← 引用新對象 淺比較不一樣
}));
}
複製代碼
使用PureComponent的最佳狀況就是展現組件,它既沒有子組件,也沒有依賴應用的全局狀態。
若是prop和state每次都會變,那麼PureComponent的效率還不如Component!(由於還要淺比較)
不要在PureComponent中使用shouldComponentUpdate,由於根本沒有必要~
<button onClick = { (e) => this.deleteRow(id,e) } Delete </button>
複製代碼
等價於
<button onClick = { this.deleteRow.bind(this,id) } Delete </button>
複製代碼
具體方法 調用中 e/this → e
deleteRow (id, e){...}
複製代碼
true && expression
複製代碼
爲真才執行,取代if條件渲染
參考資料:react-router的實現原理
react-router就是控制不一樣的url渲染不一樣的組件。react-router在history庫的基礎上,實現了URL與UI的同步。
原理:DOM渲染完成以後,給window添加onhashchange事件監聽頁面hash的變化,而且在state屬性中添加了route屬性,表明當前頁面的路由。
具體步驟:
1 當點擊連接,頁面hash改變時,觸發綁定在 window 上的 onhashchange 事件;
2 在 onhashchange 事件中改變組件的 state中的route屬性,react組件的state屬性改變時,自動從新渲染頁面;
3 頁面隨着 state 中的route屬性改變,自動根據不一樣的hash給Child變量賦值不一樣的組件,進行渲染。
數據管理中心:單一數據源(全部state維護在一個根級store);狀態只讀(沒法直接修改,嚴控修改執行);純函數(只能用reducer描述修改)
Store:全局單例。
方法:
getState:獲取state;
dispatch:觸發action,更新state;
subcribe:訂閱數據變動,註冊監聽器。
const store = createStroe(Reducer, initStore);
action:行爲載體,映射響應Reducer。可成爲數據載體,是store惟一數據源。
Reducer:修改行爲的實質,用於描述如何修改數據的純函數。
純函數:對於相同的輸入,永遠會獲得相同的輸出,並且沒有任何可觀察的反作用,也不依賴外部環境的狀態。
緣由:Redux只經過比較新舊兩個對象的存儲位置來比較新舊兩個對象是否相同(淺比較)。若是你在reducer內部直接修改舊的state對象的屬性值,那麼新的state和舊的state將都指向同一個對象。所以Redux認爲沒有任何改變,返回的state將爲舊的state。兩個state相同的話,頁面就不會從新渲染了。由於比較兩個Javascript對象全部的屬性是否相同的的惟一方法是對它們進行深比較。可是深比較在真實的應用當中代價昂貴,由於一般js的對象都很大,同時須要比較的次數不少。所以一個有效的解決方法是做出一個規定:不管什麼時候發生變化時,開發者都要建立一個新的對象,而後將新對象傳遞出去。同時,當沒有任何變化發生時,開發者發送回舊的對象。也就是說,新的對象表明新的state。
複製代碼
處理異步:引入Redux-thunk或者redux-promise這種中間件,延遲事件的派發。
!!!!!!!!!!!!!寫一個!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
在函數定義組件中使用React特性
layout.ftl 最初渲染頁面
<html ...>
<body>
<div id = "app-container"></div>
<script type = "text/javascript" ... ></script
</body>
</html>
複製代碼
client.js 配置redux的頁面
在此引入 store router
...
ReactDom.render(
...引入其餘設置及組件
document.getElementById('app-container');
);
複製代碼