網站首頁 編程語言 正文
摘要
在上一篇說過,React創建元素有兩種方式:
第一種是通過JSX方式創建,第二種是通過React.createElement方法創建。但是通過JSX創建React元素的方式,最終也會經過babel進行轉換,再用React.createElement進行元素創建。
而這一篇文章,主要是講一下React.createElement是如何創建React元素的。
1.創建方法
為了了解React.createElement這個方法,我們自己手動去實現一個簡易版的。
OK,首先我們通過React.createElement方法創建一個React元素,并且打印出來:
const b = React.createElement('div',{data: '1234'},'oneTwo')
console.log(b);
打印出來的結果為:
所以,如果我們要實現出來React.createElement這個方法,定義的時候我們可以這么寫:
function createElement(type, config, children){
let props = {}
let myType = ''
let key = ''
let ref = ''
return {
$$typeOf: 'react.element',
type: myType,
props,
key,
ref
}
}
這里我們不管**$$typeOf**這個變量,只考慮其他的變量,最終返回的對象應該是這個數據結構。
2.處理type
好了,方法的定義已經寫完了。針對于傳進來的這三個參數,我們一個一個看。
第一個參數type,它代表的是創建React元素的類型。
這里面可以是傳統的HTML元素,例如div,h1,span等標簽。
也可以是一個React組件(注意這里哦)。
而React創建組件又有兩種方式,所以type的值,有三種數據類型:
(1)字符串:例如"div",“h1”,“span”,"p"等標簽
(2)函數:通過函數的方式創建React組件
(3)類:通過class的方式創建React組件
而這個時候就有一個問題!
class Demo {
}
typeof Demo
上面的值應該為什么呢?答案是function,所以在這里我們只需要考慮type為string和function兩種類型即可。
所以我們可以寫一個判斷類型的方法:
function isValidElementType(type){
if(typeof type === 'string' || typeof type === 'function'){
return true;
}
return false;
}
在我們的主方法里面引用:
function createElement(type, config, children){
let props = {}
let myType = ''
let key = ''
let ref = ''
if(isValidElementType(type)){
myType = type;
}
return {
$$typeOf: 'react.element',
type: myType,
props,
key,
ref
}
}
3.處理config
對于React.createElement方法的第二個參數,接收看一個對象。而對象下所有的屬性,都會被放在props里面。
這句話對不對呢?其實也不是很對,是有特例的,如果在這個對象下,有key或者ref這兩個屬性。它是不會被放在props里面的,而是直接賦值給key和ref
Ok,有了上面的話,我們就可以對config進行處理了:
if(config != null){
if(config.ref){
ref = config.ref
}
if(config.key){
key = config.key
}
for(let propName in config){
if(!(['ref','key'].includes(propName))){
props[propName] = config[propName]
}
}
}
我們只需要把config的key和ref分別給到我們返回對象里面的key和ref。再便利一下config,拿出來的屬性和值把key和ref排除。最終我們的config屬性就處理好了。
4.處理children
最后一步就是處理children屬性了。而React.createElement方法的第三個參數,也可以是第四個參數(就是后面的所有參數)。都可以為字符串,或者是React元素。
這里的React元素我們不管它是通過JSX創建的,還是通過React.createElement方法創建的都可以
而參數的情況分兩種:
第一種是只有三個參數,也就是children為一個值。這個時候props里面的children就是該字符串。
第二種是參數大于三個,這個時候,props里面的children是一個數組,數組里的元素就是后面的所有參數。
OK,有了上面的基礎,就可以對children進行處理了:
let childrenLength = arguments.length - 2
if(childrenLength === 1){
props.children = children
}else if(childrenLength > 1){
let children = new Array(childrenLength)
for(let i = 0;i<childrenLength;i++){
children[i] = arguments[i+2]
}
props.children = children
}
這里通過arguments來判斷參數的個數,進而確定分支條件。
然后再根據情況,確定props里的children。
最后再貼上完整的createElement方法(簡易版):
function createElement(type, config, children){
let props = {}
let myType = ''
let key = ''
let ref = ''
if(isValidElementType(type)){
myType = type;
}
if(config != null){
if(config.ref){
ref = config.ref
}
if(config.key){
key = config.key
}
for(let propName in config){
if(!(['ref','key'].includes(propName))){
props[propName] = config[propName]
}
}
}
let childrenLength = arguments.length - 2
if(childrenLength === 1){
props.children = children
}else if(childrenLength > 1){
let children = new Array(childrenLength)
for(let i = 0;i<childrenLength;i++){
children[i] = arguments[i+2]
}
props.children = children
}
return {
$$typeOf: 'react.element',
type: myType,
props,
key,
ref
}
}
5.對比真正的React.createElement源碼
OK,上面只是實現了一個比較簡單的React.createElement方法,但是懂了其中的過程,我們就可以看一下真正的React.createElement源碼:
isValidElementType方法
function isValidElementType(type) {
if (typeof type === 'string' || typeof type === 'function') {
return true;
} // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill).
if (type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || enableDebugTracing || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || enableLegacyHidden || type === REACT_OFFSCREEN_TYPE || enableScopeAPI || enableCacheElement || enableTransitionTracing ) {
return true;
}
if (typeof type === 'object' && type !== null) {
if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object
// types supported by any Flight configuration anywhere since
// we don't know which Flight build this will end up being used
// with.
type.$$typeof === REACT_MODULE_REFERENCE || type.getModuleId !== undefined) {
return true;
}
}
return false;
}
這里面也主要就是判斷type的類型,不過判斷情況多了幾種React自帶的元素類型。
createElement方法
function createElement(type, config, children) {
var propName; // Reserved names are extracted
var props = {};
var key = null;
var ref = null;
var self = null;
var source = null;
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
{
warnIfStringRefCannotBeAutoConverted(config);
}
}
if (hasValidKey(config)) {
{
checkKeyStringCoercion(config.key);
}
key = '' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source; // Remaining properties are added to a new props object
for (propName in config) {
if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
props[propName] = config[propName];
}
}
} // Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
var childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
{
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
} // Resolve default props
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
{
if (key || ref) {
var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
};
這個方法主要是對config和children進行處理。
其余的部分就不粘過來了,對源碼感興趣的可以自己打斷點嘗試一哈!
原文鏈接:https://blog.csdn.net/weixin_46726346/article/details/126471596
相關推薦
- 2023-02-15 Nginx如何根據前綴路徑轉發到不同的Flask服務_nginx
- 2022-10-26 使用Go?http重試請求的示例_Golang
- 2023-11-22 Linux/Ubuntu下如何刪除文件或者目錄
- 2022-02-02 Maven命令安裝本地jar包到本地倉庫
- 2023-05-29 docker部署xxl-job-admin出現數據庫拒絕問題及解決方法_docker
- 2022-08-23 C++?primer超詳細講解順序容器_C 語言
- 2022-11-07 python中if的基礎用法(if?else和if?not)_python
- 2022-11-02 使用ggsignif優雅添加顯著性標記詳解_R語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支