最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

React jsx轉(zhuǎn)換與createElement使用超詳細(xì)講解

 更新時(shí)間:2022年11月04日 11:41:57   作者:goClient1992  
這篇文章主要介紹了React jsx轉(zhuǎn)換與createElement使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧

jsx的轉(zhuǎn)換

我們從 react 應(yīng)用的入口開始對(duì)源碼進(jìn)行分析,創(chuàng)建一個(gè)簡(jiǎn)單的 hello, world 應(yīng)用:

import React, { Component } from 'react';
import ReactDOM from 'react-dom';
export default class App extends Component {
  render() {
    return <div>hello, world</div>;
  }
}
ReactDOM.render(<App />, document.getElementById('root'));

我們注意到,我們?cè)?App 組件中直接寫了 return <div>hello, world</div> 的 jsx 語(yǔ)句,那么 jsx 語(yǔ)法是如何被瀏覽器識(shí)別執(zhí)行的呢?

另外我在第一次學(xué)習(xí) react 的時(shí)候,就有一個(gè)疑惑: import React, { Component } from 'react' 這段代碼中,React 似乎在代碼中沒(méi)有任何地方被用到,為什么要引入呢?

16.x版本及之前

我們?cè)?react16.8 版本的代碼中,嘗試將 React 的引用去掉:

// import React, { Component } from 'react';
import { Component } from 'react'; // 去掉 React 的引用
import ReactDOM from 'react-dom';
export default class App extends Component {
  render() {
    return <div>hello, world</div>;
  }
}
ReactDOM.render(<App />, document.getElementById('root'));

運(yùn)行應(yīng)用程序,發(fā)現(xiàn)會(huì)提示 'React' must be in scope when using JSX 的 error:

這是因?yàn)樯鲜龅念惤M件 render 中返回了 <div>hello, world</div> 的 jsx 語(yǔ)法,在React16版本及之前,應(yīng)用程序通過(guò) @babel/preset-react 將 jsx 語(yǔ)法轉(zhuǎn)換為 React.createElement 的 js 代碼,因此需要顯式將 React 引入,才能正常調(diào)用 createElement。我們可以在 Babel REPL 中看到 jsx 被 @babel/preset-react 編譯后的結(jié)果

17.x版本及之后

React17版本之后,官方與 bbel 進(jìn)行了合作,直接通過(guò)將 react/jsx-runtime 對(duì) jsx 語(yǔ)法進(jìn)行了新的轉(zhuǎn)換而不依賴 React.createElement,轉(zhuǎn)換的結(jié)果便是可直接供 ReactDOM.render 使用的 ReactElement 對(duì)象。因此如果在React17版本后只是用 jsx 語(yǔ)法不使用其他的 react 提供的api,可以不引入 React,應(yīng)用程序依然能夠正常運(yùn)行。

更多有關(guān)于 React jsx 轉(zhuǎn)換的內(nèi)容可以去看官網(wǎng)了解:介紹全新的JSX轉(zhuǎn)換,在這里就不再過(guò)多展開了。

React.createElement源碼

雖然現(xiàn)在 react17 之后我們可以不再依賴 React.createElement 這個(gè) api 了,但是實(shí)際場(chǎng)景中以及很多開源包中可能會(huì)有很多通過(guò) React.createElement 手動(dòng)創(chuàng)建元素的場(chǎng)景,所以還是推薦學(xué)習(xí)一下React.createElement源碼。

React.createElement 其接收三個(gè)或以上參數(shù):

  • type:要?jiǎng)?chuàng)建的 React 元素類型,可以是標(biāo)簽名稱字符串,如 'div' 或者 'span' 等;也可以是 React組件 類型(class組件或者函數(shù)組件);或者是 React fragment 類型。
  • config:寫在標(biāo)簽上的屬性的集合,js 對(duì)象格式,若標(biāo)簽上未添加任何屬性則為 null。
  • children:從第三個(gè)參數(shù)開始后的參數(shù)為當(dāng)前創(chuàng)建的React元素的子節(jié)點(diǎn),每個(gè)參數(shù)的類型,若是當(dāng)前元素節(jié)點(diǎn)的 textContent 則為字符串類型;否則為新的 React.createElement 創(chuàng)建的元素。

函數(shù)中會(huì)對(duì)參數(shù)進(jìn)行一系列的解析,源碼如下,對(duì)源碼相關(guān)的理解都用注釋進(jìn)行了標(biāo)記:

export function createElement(type, config, children) {
  let propName;
  // 記錄標(biāo)簽上的屬性集合
  const props = {};
  let key = null;
  let ref = null;
  let self = null;
  let source = null;
  // config 不為 null 時(shí),說(shuō)明標(biāo)簽上有屬性,將屬性添加到 props 中
  // 其中,key 和 ref 為 react 提供的特殊屬性,不加入到 props 中,而是用 key 和 ref 單獨(dú)記錄
  if (config != null) {
    if (hasValidRef(config)) {
      // 有合法的 ref 時(shí),則給 ref 賦值
      ref = config.ref;
      if (__DEV__) {
        warnIfStringRefCannotBeAutoConverted(config);
      }
    }
    if (hasValidKey(config)) {
      // 有合法的 key 時(shí),則給 key 賦值
      key = '' + config.key;
    }
    // self 和 source 是開發(fā)環(huán)境下對(duì)代碼在編譯器中位置等信息進(jìn)行記錄,用于開發(fā)環(huán)境下調(diào)試
    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // 將 config 中除 key、ref、__self、__source 之外的屬性添加到 props 中
    for (propName in config) {
      if (
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)
      ) {
        props[propName] = config[propName];
      }
    }
  }
  // 將子節(jié)點(diǎn)添加到 props 的 children 屬性上
  const childrenLength = arguments.length - 2;
  if (childrenLength === 1) {
    // 共 3 個(gè)參數(shù)時(shí)表示只有一個(gè)子節(jié)點(diǎn),直接將子節(jié)點(diǎn)賦值給 props 的 children 屬性
    props.children = children;
  } else if (childrenLength > 1) {
    // 3 個(gè)以上參數(shù)時(shí)表示有多個(gè)子節(jié)點(diǎn),將子節(jié)點(diǎn) push 到一個(gè)數(shù)組中然后將數(shù)組賦值給 props 的 children
    const childArray = Array(childrenLength);
    for (let i = 0; i < childrenLength; i++) {
      childArray[i] = arguments[i + 2];
    }
    // 開發(fā)環(huán)境下凍結(jié) childArray,防止被隨意修改
    if (__DEV__) {
      if (Object.freeze) {
        Object.freeze(childArray);
      }
    }
    props.children = childArray;
  }
  // 如果有 defaultProps,對(duì)其遍歷并且將用戶在標(biāo)簽上未對(duì)其手動(dòng)設(shè)置屬性添加進(jìn) props 中
  // 此處針對(duì) class 組件類型
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  // key 和 ref 不掛載到 props 上
  // 開發(fā)環(huán)境下若想通過(guò) props.key 或者 props.ref 獲取則 warning
  if (__DEV__) {
    if (key || ref) {
      const displayName =
        typeof type === 'function'
          ? type.displayName || type.name || 'Unknown'
          : type;
      if (key) {
        defineKeyPropWarningGetter(props, displayName);
      }
      if (ref) {
        defineRefPropWarningGetter(props, displayName);
      }
    }
  }
  // 調(diào)用 ReactElement 并返回
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

相關(guān)參考視頻:傳送門

由代碼可知,React.createElement 做的事情主要有:

  • 解析 config 參數(shù)中是否有合法的 key、ref、__source 和 __self 屬性,若存在分別賦值給 key、ref、source 和 self;將剩余的屬性解析掛載到 props 上
  • 除 type 和 config 外后面的參數(shù),掛載到 props.children
  • 針對(duì)類組件,如果 type.defaultProps 存在,遍歷 type.defaultProps 的屬性,如果 props 不存在該屬性,則添加到 props 上
  • 將 type、key、ref、self、props 等信息,調(diào)用 ReactElement 函數(shù)創(chuàng)建虛擬 dom,ReactElement 主要是在開發(fā)環(huán)境下通過(guò) Object.defineProperty 將 _store、_self、_source 設(shè)置為不可枚舉,提高 element 比較時(shí)的性能:
const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // 用于表示是否為 ReactElement
    $$typeof: REACT_ELEMENT_TYPE,
    // 用于創(chuàng)建真實(shí) dom 的相關(guān)信息
    type: type,
    key: key,
    ref: ref,
    props: props,
    _owner: owner,
  };
  if (__DEV__) {
    element._store = {};
    // 開發(fā)環(huán)境下將 _store、_self、_source 設(shè)置為不可枚舉,提高 element 的比較性能
    Object.defineProperty(element._store, 'validated', {
      configurable: false,
      enumerable: false,
      writable: true,
      value: false,
    });
    Object.defineProperty(element, '_self', {
      configurable: false,
      enumerable: false,
      writable: false,
      value: self,
    });
    Object.defineProperty(element, '_source', {
      configurable: false,
      enumerable: false,
      writable: false,
      value: source,
    });
    // 凍結(jié) element 和 props,防止被手動(dòng)修改
    if (Object.freeze) {
      Object.freeze(element.props);
      Object.freeze(element);
    }
  }
  return element;
};

所以通過(guò)流程圖總結(jié)一下 createElement 所做的事情如下:

React.Component 源碼

我們回到上述 hello,world 應(yīng)用程序代碼中,創(chuàng)建類組件時(shí),我們繼承了從 react 庫(kù)中引入的 Component,我們?cè)倏匆幌翿eact.Component源碼:

function Component(props, context, updater) {
  // 接收 props,context,updater 進(jìn)行初始化,掛載到 this 上
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  // updater 上掛載了 isMounted、enqueueForceUpdate、enqueueSetState 等觸發(fā)器方法
  this.updater = updater || ReactNoopUpdateQueue;
}
// 原型鏈上掛載 isReactComponent,在 ReactDOM.render 時(shí)用于和函數(shù)組件做區(qū)分
Component.prototype.isReactComponent = {};
// 給類組件添加 `this.setState` 方法
Component.prototype.setState = function(partialState, callback) {
  // 驗(yàn)證參數(shù)是否合法
  invariant(
    typeof partialState === 'object' ||
      typeof partialState === 'function' ||
      partialState == null
  );
  // 添加至 enqueueSetState 隊(duì)列
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
// 給類組件添加 `this.forceUpdate` 方法
Component.prototype.forceUpdate = function(callback) {
  // 添加至 enqueueForceUpdate 隊(duì)列
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

從源碼上可以得知,React.Component 主要做了以下幾件事情:

  • 將 props, context, updater 掛載到 this 上
  • 在 Component 原型鏈上添加 isReactComponent 對(duì)象,用于標(biāo)記類組件
  • 在 Component 原型鏈上添加 setState 方法
  • 在 Component 原型鏈上添加 forceUpdate 方法,這樣我們就理解了 react 類組件的 super() 作用,以及 this.setStatethis.forceUpdate 的由來(lái)

總結(jié)

本章講述了 jsx 在 react17 之前和之后的不同的轉(zhuǎn)換,實(shí)際上 react17 之后 babel 的對(duì) jsx 的轉(zhuǎn)換就是比之前多了一步 React.createElement 的動(dòng)作:

另外講述了 React.createElementReact.Component 的內(nèi)部實(shí)現(xiàn)是怎樣的。通過(guò) babel及 React.createElement,將 jsx 轉(zhuǎn)換為了瀏覽器能夠識(shí)別的原生 js 語(yǔ)法,為 react 后續(xù)對(duì)狀態(tài)改變、事件響應(yīng)以及頁(yè)面更新等奠定了基礎(chǔ)。

后面的章節(jié)中,將探究 react 是如何一步步將狀態(tài)等信息渲染為真實(shí)頁(yè)面的。

到此這篇關(guān)于React jsx轉(zhuǎn)換與createElement使用超詳細(xì)講解的文章就介紹到這了,更多相關(guān)React jsx轉(zhuǎn)換與createElement內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Jira 任務(wù)管理系統(tǒng)項(xiàng)目總結(jié)講解

    Jira 任務(wù)管理系統(tǒng)項(xiàng)目總結(jié)講解

    這篇文章主要為大家介紹了Jira 任務(wù)管理系統(tǒng)項(xiàng)目總結(jié)講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • react.js使用webpack搭配環(huán)境的入門教程

    react.js使用webpack搭配環(huán)境的入門教程

    本文主要介紹了react 使用webpack搭配環(huán)境的入門教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-08-08
  • React中常見的TypeScript定義實(shí)戰(zhàn)教程

    React中常見的TypeScript定義實(shí)戰(zhàn)教程

    這篇文章主要介紹了React中常見的TypeScript定義實(shí)戰(zhàn),本文介紹了Fiber結(jié)構(gòu),F(xiàn)iber的生成過(guò)程,調(diào)和過(guò)程,以及 render 和 commit 兩大階段,需要的朋友可以參考下
    2022-10-10
  • React模仿網(wǎng)易云音樂(lè)實(shí)現(xiàn)一個(gè)音樂(lè)項(xiàng)目詳解流程

    React模仿網(wǎng)易云音樂(lè)實(shí)現(xiàn)一個(gè)音樂(lè)項(xiàng)目詳解流程

    這篇文章主要介紹了React模仿網(wǎng)易云音樂(lè)實(shí)現(xiàn)一個(gè)音樂(lè)項(xiàng)目的詳細(xì)流程,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • 詳解React 父組件和子組件的數(shù)據(jù)傳輸

    詳解React 父組件和子組件的數(shù)據(jù)傳輸

    這篇文章主要介紹了React 父組件和子組件的數(shù)據(jù)傳輸?shù)南嚓P(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用React,感興趣的朋友可以了解下
    2021-04-04
  • React實(shí)現(xiàn)簡(jiǎn)單登錄的項(xiàng)目實(shí)踐

    React實(shí)現(xiàn)簡(jiǎn)單登錄的項(xiàng)目實(shí)踐

    登錄、注冊(cè)、找回密碼是前端項(xiàng)目經(jīng)常遇到的需求,本文主要介紹了React實(shí)現(xiàn)簡(jiǎn)單登錄的項(xiàng)目實(shí)踐,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-01-01
  • Electron打包React生成桌面應(yīng)用方法詳解

    Electron打包React生成桌面應(yīng)用方法詳解

    這篇文章主要介紹了React+Electron快速創(chuàng)建并打包成桌面應(yīng)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-12-12
  • react build 后打包發(fā)布總結(jié)

    react build 后打包發(fā)布總結(jié)

    這篇文章主要介紹了react build 后打包發(fā)布總結(jié),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-08-08
  • 詳解React中傳入組件的props改變時(shí)更新組件的幾種實(shí)現(xiàn)方法

    詳解React中傳入組件的props改變時(shí)更新組件的幾種實(shí)現(xiàn)方法

    這篇文章主要介紹了詳解React中傳入組件的props改變時(shí)更新組件的幾種實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09
  • React引入css的三種方式小結(jié)

    React引入css的三種方式小結(jié)

    這篇文章主要介紹了React引入css的三種方式小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12

最新評(píng)論

洛扎县| 宝应县| 固阳县| 湖南省| 绩溪县| 黔南| 横峰县| 三穗县| 专栏| 临潭县| 新河县| 津南区| 米易县| 福州市| 河北省| 海口市| 揭阳市| 郁南县| 集安市| 应用必备| 沙湾县| 信宜市| 湖北省| 京山县| 于都县| 大姚县| 丰城市| 南皮县| 盐亭县| 崇义县| 璧山县| 佛坪县| 静安区| 共和县| 磴口县| 鹤壁市| 新晃| 勃利县| 宁海县| 夏津县| 安化县|