Jsx, 合成事件與Refs(二)

React系列

React簡單模擬語法(一)
Jsx, 合成事件與Refs(二)
virtualdom diff算法實現分析(三)
從Mixin到HOC再到HOOKS(四)
createElement, ReactElement與Component部分源碼解析(五)

JSX的誕生

他是 JavaScrip 的一種擴展語法。愛掏網 - it200.com React 官方推薦使用這種語法來描述 UI 信息。愛掏網 - it200.comJSX 可能會讓你想起某種模板語言,但是它具有 JavaScrip 的全部能力

  • JSX 執行更快,因為它在編譯為 JavaScript 代碼后進行了優化。愛掏網 - it200.com
  • 它是類型安全的,在編譯過程中就能發現錯誤。愛掏網 - it200.com
  • 使用 JSX 編寫模板更加簡單快速。愛掏網 - it200.com

編譯

本質上來講,JSX 只是為 React.createElement(component, props, ...children)方法提供的語法糖

  123456
"use strict";

React.createElement("div", {
  className: "num",
  index: 1
}, React.createElement("span", null, "123456"));

具體效果可以在此體驗

這就是為什么盡管你看不到里面使用過React,但是如果你不引入模塊的話JSX會報錯.

JSX原理

從上面的編譯代碼來看,JSX最終包含的信息其實分別是: 元素標簽, 元素屬性, 子元素.如果用Javascript對象來表示的話:

{
  tag: 'div',
  attrs: { className: 'num', index: 1},
  children: [
    {
      tag: 'span',
      arrts: null,
      children: null
    }
  ]
}

所以整個過程大概如下

至于為什么會有中間編譯成JS對象那一步而不直接編譯成Dom元素.

  1. 除了普通頁面還可能渲染到canvas或者原生App(React Native了解一下)
  2. 后面的diff比較需要用到

事件處理

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中,事件的觸發實質上是要經過三個階段:事件捕獲、目標對象本身的事件處理和事件冒泡.

  • stopPropagation(): 停止事件冒泡
  • preventDefault(): 阻止默認行為
  • return false: 實際上使用這個的時候會做三件事
    • event.preventDefault();
    • event.stopPropagation();
    • 停止回調函數執行并立即返回。愛掏網 - it200.com

React是怎么管理事件系統的?

出于性能原因.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方法.

解決方案

  1. event.persist()

    事件回調中調用event.persist()方法,這樣會在池中刪除合成事件,并且允許用戶代碼保留對事件的引用。愛掏網 - it200.com

  2. 緩存屬性

    我們可以將事件屬性存儲在事件函數并且傳遞給異步回調函數而不是直接在異步回調里訪問它們.

    
    
  3. 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   |    .    |              ||              |                    +------------+
 * |     |      |    .    +--------------+
 * +-----|------+    .                ^        +-----------+
 *       |           .                |        |Enter/Leave|
 *       +           .                +-------+|Plugin     |
 * +-------------+   .                         +-----------+
 * | application |   .
 * |-------------|   .
 * |             |   .
 * |             |   .
 * +-------------+   .
 *                   .
 *    React Core     .  General Purpose Event Plugin System
 */

DOM將事件傳給ReactEventListener注冊到document,然后分發到具體節點.EventPluginHub負責事件的存儲,合成事件以及池方式的實現創建和銷毀,后面是各種類型的合成事件模擬,交互通過ReactEventEmitter將原生的DOM事件轉化成合成的事件,觸發將對應操作推入隊列批量執行.因為瀏覽器會為每個事件的每個listener創建一個事件對象,上面提到的池方式復用就是為了解決高額內存分配的問題.

合成事件

  • Clipboard Events
  • Composition Events
  • Keyboard Events
  • Focus Events
  • Form Events
  • Mouse Events
  • Pointer Events
  • Selection Events
  • Touch Events
  • UI Events
  • Wheel Events
  • Media Events
  • Image Events
  • Animation Events
  • Transition Events
  • Other Events

event

其中事件都會被自動傳入一個event對象,是由React將瀏覽器原生的event對象封裝一下對外提供統一的API和屬性.

this

因為React里調用傳入方法的時候并不是通過對象方法方式,而是直接通過函數調用,所以里面指向的this是null或者undefined.

一般傳入的時候需要手動用bind或者箭頭函數顯性綁定this指向

Refs & DOM

這是一種用于訪問render方法中創建的DOM節點或React元素的方式.一般用于

  • 處理表單,媒體控制
  • 觸發強制動畫
  • 集成第三方DOM庫

創建Refs

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.myRef = React.createRef()
  }
  render() {
    return 
  }
}

訪問 Refs

const node = this.myRef.current;
  • 如果用于一個普通HTMl元素時,React.createRef() 將接收底層 DOM 元素作為它的 current 屬性以創建 ref愛掏網 - it200.com
  • ref 屬性被用于一個自定義類組件時,ref 對象將接收該組件已掛載的實例作為它的 current愛掏網 - it200.com
  • 你不能在函數式組件上使用 ref 屬性,因為它們沒有實例。愛掏網 - it200.com

回調Refs

不同于傳遞 createRef() 創建的 ref 屬性,你會傳遞一個函數。愛掏網 - it200.com這個函數接受 React 組件的實例或 HTML DOM 元素作為參數,以存儲它們并使它們能被其他地方訪問。愛掏網 - it200.com

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 (
      
        

    );
  }
}

如果是組件間傳遞回調形式的 refs如下:

function CustomTextInput(props) {
  return (
    
      

  );
}

class Parent extends React.Component {
  render() {
    return (
       this.inputElement = el}
      />
    );
  }
}

無狀態組件中使用

因為無狀態組件是不會被實例化的,但是我們可以用過一個變量訪問其中的組件或者dom元素組件的實例引用

function CustomTextInput(props) {
  let inputRef;
  return (
    
       inputRef = node} />
    
  );
}
聲明:所有內容來自互聯網搜索結果,不保證100%準確性,僅供參考。如若本站內容侵犯了原著者的合法權益,可聯系我們進行處理。
發表評論
更多 網友評論0 條評論)
暫無評論

返回頂部

主站蜘蛛池模板: 久久国产一区二区三区| 同桌好舒服好粗好硬| 欧美猛少妇色xxxxx| Av鲁丝一区鲁丝二区鲁丝三区| 无码国产乱人伦偷精品视频 | 男人扒开女人下面狂躁动漫版| 国产三级在线观看播放| 樱桃视频影院在线观看| aaaa级毛片| 又爽又黄又无遮挡的视频| 97精品人妻一区二区三区香蕉| 亚洲sss综合天堂久久久| 国产小视频免费| 李丽珍蜜桃成熟时电影在线播放观看 | 国产美女在线一区二区三区| 男人添女人下部高潮全视频| 欧美一卡2卡3卡四卡海外精品| 国产精品十八禁在线观看| 久久精品国产99国产精品| 黄色a视频在线观看| 欧美va久久久噜噜噜久久| 男女一边摸一边做爽爽毛片| 果冻传媒国产电影免费看| 国产福利电影在线观看| 久久国产精品久久精品国产| 菠萝蜜亏亏带痛声的视频| 好男人在线神马影视在线观看www| 免费人成在线观看网站| a级国产乱理伦片在线观| 欧美性69式xxxx护士| 国产欧美日韩精品专区| 中文字幕在线观看91| 精品一区二区三区无码免费视频| 岛国在线播放v片免费| 亚洲欧美日韩精品高清| 香蕉视频网站在线观看| 最近中文字幕mv免费高清视频7| 国产嫩草影院在线观看| 亚洲欧美中日韩| 影音先锋男人看片资源| 日韩亚洲欧美视频|