React系列 --- 簡單模擬語法(一)
React系列 --- Jsx, 合成事件與Refs(二)
React系列 --- virtualdom diff算法實現分析(三)
React系列 --- 從Mixin到HOC再到HOOKS(四)
React系列 --- createElement, ReactElement與Component部分源碼解析(五)
React系列 --- 從使用React瞭解Css的各類使用方案(六)
React系列 --- 從零構建狀態管理及Redux源碼解析(七)
React系列 --- 擴展狀態管理功能及Redux源碼解析(八)html
他是 JavaScrip 的一種擴展語法。 React 官方推薦使用這種語法來描述 UI 信息。JSX 可能會讓你想起某種模板語言,可是它具備 JavaScrip 的所有能力node
本質上來說,JSX 只是爲 React.createElement(component, props, ...children)
方法提供的語法糖react
<div className="num" index={1}> <span>123456</span> </div>
"use strict"; React.createElement("div", { className: "num", index: 1 }, React.createElement("span", null, "123456"));
具體效果能夠在此體驗算法
這就是爲何儘管你看不到裏面使用過React
,可是若是你不引入模塊的話JSX會報錯.canvas
從上面的編譯代碼來看,JSX最終包含的信息其實分別是: 元素標籤, 元素屬性, 子元素.若是用Javascript對象來表示的話:segmentfault
{ tag: 'div', attrs: { className: 'num', index: 1}, children: [ { tag: 'span', arrts: null, children: null } ] }
因此整個過程大概以下瀏覽器
至於爲何會有中間編譯成JS對象那一步而不直接編譯成Dom元素.緩存
React的事件是基於SyntheticEvent的實例實現模擬跨瀏覽器原生事件同樣的接口,包括stopPropagation()
和preventDefault()
,指望事件的行爲跨瀏覽器是相同的.甚至兼容直達IE8.每一個SyntheicEvent
對象都有以下屬性:安全
boolean bubbles boolean cancelable DOMEventTarget currentTarget boolean defaultPrevented number eventPhase boolean isTrusted DOMEvent nativeEvent void preventDefault() boolean isDefaultPrevented() void stopPropagation() boolean isPropagationStopped() DOMEventTarget target number timeStamp string type
在JavaScript中,事件的觸發實質上是要通過三個階段:事件捕獲、目標對象自己的事件處理和事件冒泡.babel
return false: 實際上使用這個的時候會作三件事
出於性能緣由.React會經過池方式複用SyntheicEvent
對象,這意味着事件調用完成以後event.target
上全部的屬性都會失效.意思就是當咱們嘗試異步方式調用React事件,由於複用的緣由,在事件回調執行完以後SyntheicEvent
對象將再也不存在,因此咱們沒法訪問其屬性.
function onClick(event) { console.log(event); // => nullified object. console.log(event.type); // => "click" const eventType = event.type; // => "click" setTimeout(function() { console.log(event.type); // => null console.log(eventType); // => "click" }, 0); // Won't work. this.state.clickEvent will only contain null values. this.setState({clickEvent: event}); // You can still export event properties. this.setState({eventType: event.type}); }
比較常見的例子就是setState
方法.
event.persist()
事件回調中調用event.persist()
方法,這樣會在池中刪除合成事件,而且容許用戶代碼保留對事件的引用。
緩存屬性
咱們能夠將事件屬性存儲在事件函數而且傳遞給異步回調函數而不是直接在異步回調裏訪問它們.
<button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
Debouncing a synthetic event handler(不知道怎麼翻譯)
// Correct this.setState((prevState, props) => ({ counter: prevState.counter + props.increment }));
源碼註釋
/** * Summary of `ReactBrowserEventEmitter` event handling: * * - Top-level delegation is used to trap most native browser events. This * may only occur in the main thread and is the responsibility of * ReactDOMEventListener, which is injected and can therefore support * pluggable event sources. This is the only work that occurs in the main * thread. * * - We normalize and de-duplicate events to account for browser quirks. This * may be done in the worker thread. * * - Forward these native events (with the associated top-level type used to * trap it) to `EventPluginHub`, which in turn will ask plugins if they want * to extract any synthetic events. * * - The `EventPluginHub` will then process each event by annotating them with * "dispatches", a sequence of listeners and IDs that care about that event. * * - The `EventPluginHub` then dispatches the events. * * Overview of React and the event system: * * +------------+ . * | DOM | . * +------------+ . * | . * v . * +------------+ . * | ReactEvent | . * | Listener | . * +------------+ . +-----------+ * | . +--------+|SimpleEvent| * | . | |Plugin | * +-----|------+ . v +-----------+ * | | | . +--------------+ +------------+ * | +-----------.--->|EventPluginHub| | Event | * | | . | | +-----------+ | Propagators| * | ReactEvent | . | | |TapEvent | |------------| * | Emitter | . | |<---+|Plugin | |other plugin| * | | . | | +-----------+ | utilities | * | +-----------.--->| | +------------+ * | | | . +--------------+ * +-----|------+ . ^ +-----------+ * | . | |Enter/Leave| * + . +-------+|Plugin | * +-------------+ . +-----------+ * | application | . * |-------------| . * | | . * | | . * +-------------+ . * . * React Core . General Purpose Event Plugin System */
DOM將事件傳給ReactEventListener註冊到document,而後分發到具體節點.EventPluginHub負責事件的存儲,合成事件以及池方式的實現建立和銷燬,後面是各類類型的合成事件模擬,交互經過ReactEventEmitter將原生的DOM事件轉化成合成的事件,觸發將對應操做推入隊列批量執行.由於瀏覽器會爲每一個事件的每一個listener建立一個事件對象,上面提到的池方式複用就是爲了解決高額內存分配的問題.
其中事件都會被自動傳入一個event
對象,是由React將瀏覽器原生的event
對象封裝一下對外提供統一的API和屬性.
由於React裏調用傳入方法的時候並非經過對象方法方式,而是直接經過函數調用,因此裏面指向的this是null或者undefined.
通常傳入的時候須要手動用bind
或者箭頭函數
顯性綁定this指向
這是一種用於訪問render方法中建立的DOM節點或React元素的方式.通常用於
class MyComponent extends React.Component { constructor(props) { super(props) this.myRef = React.createRef() } render() { return <div ref={this.myRef} /> } }
const node = this.myRef.current;
React.createRef()
將接收底層 DOM 元素做爲它的 current
屬性以建立 ref
。ref
屬性被用於一個自定義類組件時,ref
對象將接收該組件已掛載的實例做爲它的 current
。不一樣於傳遞 createRef()
建立的 ref
屬性,你會傳遞一個函數。這個函數接受 React 組件的實例或 HTML DOM 元素做爲參數,以存儲它們並使它們能被其餘地方訪問。
class CustomTextInput extends React.Component { constructor(props) { super(props); this.textInput = null; this.setTextInputRef = element => { this.textInput = element; }; this.focusTextInput = () => { // 直接使用原生 API 使 text 輸入框得到焦點 if (this.textInput) this.textInput.focus(); }; } componentDidMount() { // 渲染後文本框自動得到焦點 this.focusTextInput(); } render() { // 使用 `ref` 的回調將 text 輸入框的 DOM 節點存儲到 React // 實例上(好比 this.textInput) return ( <div> <input type="text" ref={this.setTextInputRef} /> <input type="button" value="Focus the text input" onClick={this.focusTextInput} /> </div> ); } }
若是是組件間傳遞迴調形式的 refs以下:
function CustomTextInput(props) { return ( <div> <input ref={props.inputRef} /> </div> ); } class Parent extends React.Component { render() { return ( <CustomTextInput inputRef={el => this.inputElement = el} /> ); } }
由於無狀態組件是不會被實例化的,可是咱們能夠用過一個變量訪問其中的組件或者dom元素組件的實例引用
function CustomTextInput(props) { let inputRef; return ( <div> <input ref={(node) => inputRef = node} /> </div> ); }