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

react源碼層深入刨析babel解析jsx實(shí)現(xiàn)

 更新時(shí)間:2022年10月24日 10:46:20   作者:flyzz177  
同作為MVVM框架,React相比于Vue來講,上手更需要JavaScript功底深厚一些,本系列將閱讀React相關(guān)源碼,從jsx -> VDom -> RDOM等一些列的過程,將會(huì)在本系列中一一講解

經(jīng)過多年的發(fā)展,React已經(jīng)更新了大版本16、17、18,本系列主要講的是 version:17.0.2,在講這個(gè)版本之前,我們先看一看在babel的編譯下,每個(gè)大版本之下會(huì)有什么樣的變化。

jsx

<div className='box'>
  <h1 className='title' style={{'color':'red'}}>React源碼解析</h1>
  <ul>
    <li>第一章</li>
    <li>第二章</li>
    <li>第三章</li>
    <li>第四章</li>
  </ul>
</div>

v16.x及以前版本

v17及之后版本

所以各位看到了,在v16及以前我們babel進(jìn)行jsx解析編譯的是根據(jù)@babel/babel-preset-react-app解析成React.createElement進(jìn)行包裹的,而v17以及之后的版本,官網(wǎng)早就說明,對jsx的轉(zhuǎn)換用react/jsx-runtime,而不再依賴React.createElement了,看到這里我想各位對不同版本的babel解析jsx已經(jīng)有了眉目了,早已經(jīng)迫不及待想去看看jsx-runtime和createElement到底是如何玩的,那么進(jìn)入源碼

在babel解析后的v17產(chǎn)物中我們可以看得到 var _jsxRuntime = require("react/jsx-runtime");那么我們追本溯源可以找到在packages/react/src/jsx/ReactJSX.js里面的jsxs是怎么來的

// packages/react/src/jsx/ReactJSX.js
import {REACT_FRAGMENT_TYPE} from 'shared/ReactSymbols';
import {
  jsxWithValidationStatic,
  jsxWithValidationDynamic,
  jsxWithValidation,
} from './ReactJSXElementValidator';
import {jsx as jsxProd} from './ReactJSXElement';
const jsx = __DEV__ ? jsxWithValidationDynamic : jsxProd;
const jsxs = __DEV__ ? jsxWithValidationStatic : jsxProd;
const jsxDEV = __DEV__ ? jsxWithValidation : undefined;
export {REACT_FRAGMENT_TYPE as Fragment, jsx, jsxs, jsxDEV};

在非dev環(huán)境下我們繼續(xù)去找jsProd

export function jsx(type, config, maybeKey) {
  let propName;
  //標(biāo)簽上的屬性集合
  const props = {};
  //單獨(dú)處理key ref
  let key = null;
  let ref = null;
  if (maybeKey !== undefined) {
    key = '' + maybeKey;
  }
  if (hasValidKey(config)) {
    // 處理合法的key
    key = '' + config.key;
  }
  if (hasValidRef(config)) {
    // 處理合法的ref
    ref = config.ref;
  }
  // 把屬性加到props中
  for (propName in config) {
    if (
      hasOwnProperty.call(config, propName) &&
      !RESERVED_PROPS.hasOwnProperty(propName)
    ) {
      props[propName] = config[propName];
    }
  }
  // 處理默認(rèn)props
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  return ReactElement(
      type,
      key,
      ref,
      undefined,
      undefined,
      ReactCurrentOwner.current,
      props
  )
}

ReactElement

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // 表示是否為ReactElement
    $$typeof: REACT_ELEMENT_TYPE,
    // 元素自身屬性
    type: type,
    key: key,
    ref: ref,
    props: props,
    // Record the component responsible for creating this element.
    _owner: owner,
  };
  if (__DEV__) {
    element._store = {};
    // 開發(fā)環(huán)境下將_store、_self、_source屬性變?yōu)椴豢擅杜e
    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é)props、element防止被手動(dòng)修改
    if (Object.freeze) {
      Object.freeze(element.props);
      Object.freeze(element);
    }
  }
  return element;
};

這上面便是v17及之后版本的jsx-runtime所做的事情。那么這里再去看一下v16中的createElement所做的事情吧。

相關(guān)參考視頻講解:進(jìn)入學(xué)習(xí)

React.createElement

// packages/react/src/ReactElement.js
export function createElement(type, config, children) {
  let propName;
  // 記錄標(biāo)簽上的屬性集合
  const props = {};
  //單獨(dú)處理key ref
  let key = null;
  let ref = null;
  let self = null;
  let source = null;
  // 當(dāng)config部位null的時(shí)候,表示標(biāo)簽上有屬性,加到props里面去
  if (config != null) {
    // 合法的ref才做處理
    if (hasValidRef(config)) {
      ref = config.ref;

      if (__DEV__) {
        warnIfStringRefCannotBeAutoConverted(config);
      }
    }
    if (hasValidKey(config)) {
      // 有合法的key才做處理
      key = '' + config.key;
    }
    // 記錄信息用于debug
    self = config.__self === undefined ? null : config.__self;
    source = config.__source === undefined ? null : config.__source;
    // 處理self,source,key,ref以外的屬性,加入props中
    for (propName in config) {
      if (
        hasOwnProperty.call(config, propName) &&
        !RESERVED_PROPS.hasOwnProperty(propName)
      ) {
        props[propName] = config[propName];
      }
    }
  }
  // 處理子節(jié)點(diǎn)
  const childrenLength = arguments.length - 2;
  // 單標(biāo)簽子節(jié)點(diǎn)
  if (childrenLength === 1) {
    props.children = children;
    //嵌套子節(jié)點(diǎn)
  } else if (childrenLength > 1) {
    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;
  }
  // 處理默認(rèn)props
  if (type && type.defaultProps) {
    const defaultProps = type.defaultProps;
    for (propName in defaultProps) {
      if (props[propName] === undefined) {
        props[propName] = defaultProps[propName];
      }
    }
  }
  if (__DEV__) {
    // dev環(huán)境下,key 與 ref不掛到props中去
    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)用返回
  return ReactElement(
    type,
    key,
    ref,
    self,
    source,
    ReactCurrentOwner.current,
    props,
  );
}

由React.createElement源碼得知,他做了如下事情

  • 解析config參數(shù)中是否有合法的 key、ref屬性,并處理,并將其他的屬性掛到props上。
  • 解析函數(shù)的第三參數(shù),并分情況將第三參數(shù)掛到props.children上。
  • 對默認(rèn)props進(jìn)行處理,如果存在該屬性則直接掛載到props上,不存在則要添加上。
  • 開發(fā)環(huán)境下將 _store、_self、_source 設(shè)置為不可枚舉狀態(tài),為后期的diff比較作優(yōu)化,提高比較性能。
  • type、key、ref、props等屬性通過調(diào)用ReactElement函數(shù)創(chuàng)建虛擬dom。

ReactElement

const ReactElement = function(type, key, ref, self, source, owner, props) {
  const element = {
    // This tag allows us to uniquely identify this as a React Element
    $$typeof: REACT_ELEMENT_TYPE,
    // Built-in properties that belong on the element
    type: type,
    key: key,
    ref: ref,
    props: props,
    // Record the component responsible for creating this element.
    _owner: owner,
  };
  if (__DEV__) {
    // The validation flag is currently mutative. We put it on
    // an external backing store so that we can freeze the whole object.
    // This can be replaced with a WeakMap once they are implemented in
    // commonly used development environments.
    element._store = {};
    // To make comparing ReactElements easier for testing purposes, we make
    // the validation flag non-enumerable (where possible, which should
    // include every environment we run tests in), so the test framework
    // ignores it.
    Object.defineProperty(element._store, 'validated', {
      configurable: false,
      enumerable: false,
      writable: true,
      value: false,
    });
    // self and source are DEV only properties.
    Object.defineProperty(element, '_self', {
      configurable: false,
      enumerable: false,
      writable: false,
      value: self,
    });
    // Two elements created in two different places should be considered
    // equal for testing purposes and therefore we hide it from enumeration.
    Object.defineProperty(element, '_source', {
      configurable: false,
      enumerable: false,
      writable: false,
      value: source,
    });
    if (Object.freeze) {
      Object.freeze(element.props);
      Object.freeze(element);
    }
  }
  return element;
};

仔細(xì)瞧一瞧,這個(gè)其實(shí)跟jsxs調(diào)用的ReactElement實(shí)現(xiàn)的差不多的功能,但是為什么要寫兩遍?仔細(xì)看來,在兩個(gè)版本的ReactElement中,傳入的參數(shù)不一致,在開發(fā)環(huán)境下,分別對其做劫持不可枚舉狀態(tài),僅此而已

React.Component

寫慣了hooks組件,但是Class組件也別忘了喲,因?yàn)樵?code>React17里面Class組件也是沒有被抹去的,所以既然是源碼解析,那么我們也要來看一看這個(gè)Component到底干了啥。

// packages/react/src/ReactBaseClasses.js
function Component(props, context, updater) {
  // 接受各種參數(shù),掛到this上
  this.props = props;
  this.context = context;
  this.refs = emptyObject;
  // updater ?? 
  this.updater = updater || ReactNoopUpdateQueue;
}
// 原型上掛載了isReactComponent用來區(qū)分函數(shù)組件與類組件
Component.prototype.isReactComponent = {};
//原型上掛載了setState方法用來觸發(fā)更新
Component.prototype.setState = function(partialState, callback) {
  invariant(
    typeof partialState === 'object' ||
      typeof partialState === 'function' ||
      partialState == null,
    'setState(...): takes an object of state variables to update or a ' +
      'function which returns an object of state variables.',
  );
  // 調(diào)用updater上的enqueueSetState方法???
  this.updater.enqueueSetState(this, partialState, callback, 'setState');
};
// 原型上掛載了強(qiáng)制更新的方法
Component.prototype.forceUpdate = function(callback) {
  this.updater.enqueueForceUpdate(this, callback, 'forceUpdate');
};

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

  • props, context, updater 掛載到this 上,props,context一目了然,后面的updater位觸發(fā)器,上面掛了很多方法,我們后面再談。
  • Component 原型鏈上添加 isReactComponent 對象,用于區(qū)分函數(shù)組件還是類組件。
  • Component 原型鏈上添加 setState 方法,觸發(fā)更新。
  • Component 原型鏈上添加 forceUpdate 方法,強(qiáng)制更新。

總結(jié)

不管是類組件還是函數(shù)組件,最終我們寫的jsx都被babel轉(zhuǎn)化成了可識別的元素,其中我們也看了ReactElement,createElement,Component等內(nèi)部實(shí)現(xiàn),了解到了作為ReactElement他是怎么被創(chuàng)建的,但是遠(yuǎn)遠(yuǎn)沒有完,因?yàn)槲覀冎牢覀冊趯慠eact的時(shí)候,會(huì)在后面帶上一個(gè)ReactDOM.render(<Element/>, 'root'),沒錯(cuò)我們下一章節(jié)就要去探索一下ReactDOM.render方法了。

到此這篇關(guān)于react源碼層深入刨析babel解析jsx實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)react babel解析jsx內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用React組件編寫溫度顯示器

    使用React組件編寫溫度顯示器

    這篇文章主要為大家詳細(xì)介紹了使用React組件編寫溫度顯示器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • React中classnames庫使用示例

    React中classnames庫使用示例

    這篇文章主要為大家介紹了React中classnames庫使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-10-10
  • 編寫簡潔React組件的小技巧

    編寫簡潔React組件的小技巧

    這篇文章主要介紹了編寫簡潔React組件的小技巧,幫助大家更好的理解和學(xué)習(xí)使用React,感興趣的朋友可以了解下
    2021-04-04
  • 解決React初始化加載組件會(huì)渲染兩次的問題

    解決React初始化加載組件會(huì)渲染兩次的問題

    這篇文章主要介紹了解決React初始化加載組件會(huì)渲染兩次的問題,文中有出現(xiàn)這種現(xiàn)象的原因及解決方法,感興趣的同學(xué)跟著小編一起來看看吧
    2023-08-08
  • React?Native?中實(shí)現(xiàn)倒計(jì)時(shí)功能

    React?Native?中實(shí)現(xiàn)倒計(jì)時(shí)功能

    這篇文章主要介紹了React?Native中實(shí)現(xiàn)倒計(jì)時(shí)功能示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • React Router6.x路由表封裝的兩種寫法

    React Router6.x路由表封裝的兩種寫法

    本文主要介紹了React Router6.x路由表封裝的兩種寫法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • webpack 2的react開發(fā)配置實(shí)例代碼

    webpack 2的react開發(fā)配置實(shí)例代碼

    本篇文章主要介紹了webpack 2的react開發(fā)配置實(shí)例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-07-07
  • Reactjs?+?Nodejs?+?Mongodb?實(shí)現(xiàn)文件上傳功能實(shí)例詳解

    Reactjs?+?Nodejs?+?Mongodb?實(shí)現(xiàn)文件上傳功能實(shí)例詳解

    今天是使用?Reactjs?+?Nodejs?+?Mongodb?實(shí)現(xiàn)文件上傳功能,前端我們使用?Reactjs?+?Axios?來搭建前端上傳文件應(yīng)用,后端我們使用?Node.js?+?Express?+?Multer?+?Mongodb?來搭建后端上傳文件處理應(yīng)用,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2022-06-06
  • react實(shí)現(xiàn)頁面水印效果的全過程

    react實(shí)現(xiàn)頁面水印效果的全過程

    大家常常關(guān)注的是網(wǎng)站圖片增加水印,而很少關(guān)注頁面水印,其實(shí)這個(gè)需求也是比較常見的,比如公文系統(tǒng)、合同系統(tǒng)等,這篇文章主要給大家介紹了關(guān)于react實(shí)現(xiàn)頁面水印效果的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • axios請求響應(yīng)數(shù)據(jù)加解密封裝實(shí)現(xiàn)詳解

    axios請求響應(yīng)數(shù)據(jù)加解密封裝實(shí)現(xiàn)詳解

    這篇文章主要為大家介紹了axios請求響應(yīng)數(shù)據(jù)加解密封裝實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03

最新評論

昭平县| 镇赉县| 游戏| 营口市| 蓬溪县| 岳西县| 大邑县| 新邵县| 全州县| 崇明县| 麻城市| 和林格尔县| 富顺县| 庆城县| 马山县| 饶河县| 平昌县| 泾阳县| 普陀区| 甘孜县| 嘉兴市| 镇远县| 仙游县| 漾濞| 合江县| 甘肃省| 喀喇沁旗| 德格县| 宜宾市| 通江县| 嘉义市| 和政县| 桃园市| 昆山市| 绥中县| 逊克县| 扎兰屯市| 龙山县| 罗定市| 年辖:市辖区| 满洲里市|