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> ); }