react遍歷對象的值_React 原理之實現 createElement 和 render 方法

前言

在 React 中,我們都知道可以寫 jsx 代碼會被編譯成真正的 DOM 插入到要顯示的頁面上。這具體是怎么實現的,今天我們就自己動手做一下。

實現 createElement 方法

這個方法平時開發我們并不會用到,因為它是經 babel 編譯后的代碼,我們新建一個 React 項目,index.js 最簡單的代碼結構如下:

import?React?from?'react'
import?ReactDOM?from?'react-dom'
ReactDOM.render(<h1?className='title'>Hello?Reacth1>,?document.getElementById('root'))

這里就 jsx 會變編譯成真正的 DOM ,把 html 代碼拿到 babel 官網編譯

f39255d8ab1496a79eae2ec9d491348a.png

于是我們就看到了 React.createElement() 方法,但這只是調用這個方法,它具體做了什么返回什么我們還不知道,我們可以打印這個函數運行的結果:

console.log(
??React.createElement(
????'h1',
????{
??????className:?'title',
????},
????'Hello?React'
??)
)

8df72f74a1510f86715fcfc06c75e81e.png返回的這個對象就是虛擬 DOM 了。

我們來分析它返回的對象參數,首先第一個是

  • $$typeof: REACT_ELEMENT_TYPE

這個是 React 元素對象的標識屬性

REACT_ELEMENT_TYPE 的值是一個 Symbol 類型,代表了一個獨一無二的值。如果瀏覽器不支持 Symbol 類型,值就是一個二進制值。

?

為什么是 Symbol?主要防止 XSS 攻擊偽造一個假的 React 組件。因為 JSON 中是不會存在 Symbol 類型的。

?
  • key:這個比如循環中會用到這個 key 值
  • props:傳入的屬性值,比如 id, className, style, children 等
  • ref:DOM 的引用
  • 剩下的是私有屬性(本篇不展開討論)

在本篇我們會用自己簡單的方式實現這兩個方法,而不是根據源碼,所以實現上的方法只要能實現它的基本功能即可;有個基本概念在,以后再循序漸進學習源碼。

而 createElement 中有三個參數,更確切說是 n 個參數:

  • type:表示要渲染的元素類型。這里可以傳入一個元素 Tag 名稱,也可以傳入一個組件(如 div span 等,也可以是是函數組件和類組件)
  • props:創建 React 元素所需要的 props。
  • childrens(可選參數):要渲染元素的子元素,這里可以向后傳入 n 個參數。可以為文本字符串,也可以為數組

初步 createElement 方法:

//?創建?JSX?對象
function?createElement(type,?props,?...childrens)?{
????return?{
????????type,
????????props:?{
??????????...props,
??????????children:?childrens.length?<=?1???childrens[0]?||?''?:?childrens,
????????},
}

參數中 props 和 childrens 是并列關系,然后返回的 props 對象,里面包含了 children,所以我們需要再 props 里面添加 children 參數,然后根據 children 參數為一個或多個的可能在進行取值處理。

調用該方法:

console.log(
??createElement(
????'h1',
????{
??????className:?'title',
????},
????'Hello?React'
??)
)

06eca7d97120ccd68478abd4dcc06193.png除去其它本篇我們不討論的屬性,目前算是實現了一半;我們觀察原來 React 自身方法輸出的結果有 key, ref, 同輸出的 props 也是并列關系,于是我們進一步作出處理

function?createElement(type,?props,?...childrens)?{
??let?ref,?key
??if?('ref'?in?props)?{
????ref?=?props['ref']
????props['ref']?=?undefined
??}
??if?('key'?in?props)?{
????key?=?props['key']
????props['key']?=?undefined
??}
??return?{
????type,
????props:?{
??????...props,
??????children:?childrens.length?<=?1???childrens[0]?||?''?:?childrens,
????},
????ref,
????key,
??}
}

同樣的方式調用結果如下:

2f44952f7c1e5dcd3fa804bc9997fa0f.png如果添加多一些屬性,我們來看看結果

console.log(
??createElement(
????'div',
????{?id:?'box',?className:?'box',?style:?{?color:?'red'?},?key:?'20'?},
????'this?is?text',
????createElement('h2',?{?className:?'title'?},?'hello'),
????createElement('div',?{?className:?'content'?},?'Hi')
??)
)

9285e46d4651a2ab3979281d553175f4.png用了這種比較粗魯的方式添加,設置為 undefined 在實現 render 方法的時候我們會根據這個忽略 props 內部的 key 和 props 屬性,這里就實現了最基本的 createElement 方法了。

實現 render 方法

render 方法的第一個參數接收的是 createElement 返回的對象,也就是虛擬 DOM;第二個參數則是掛載的目標 DOM。同樣的做法,我們用 babel 編譯來看:

73f47b963694230f498e3b449e0ffc57.png執行后,就被掛在到頁面了

8ae996674fef5494b935f6579116d393.png

實現代碼如下:

/*
?*?功能:把創建的對象生成對應的DOM元素,最后插入到頁面中
?* objJSX:createElement 返回的 JSX 對象
?* container:掛載的容器,如 document.getElementById('root')
?*/
function?render(objJSX,?container)?{
??let?{?type,?props?}?=?objJSX
??let?newElement?=?document.createElement(type)
??for?(let?attr?in?props)?{
????//?遍歷傳入的?props?屬性
????if?(!props.hasOwnProperty(attr))?break?//?不是私有的直接結束遍歷
????let?value?=?props[attr]?//?>如果當前屬性沒有值,直接不處理即可
????if?(value?==?undefined)?continue?//?NULL?OR?UNDEFINED

????//?對幾個特殊屬性單獨設置
????switch?(attr.toUpperCase())?{
??????case?'ID':
????????newElement.setAttribute('id',?value)
????????break
??????case?'CLASSNAME':
????????newElement.setAttribute('class',?value)
????????break
??????case?'STYLE':?//?傳入的行內樣式?style?是個對象,故需遍歷賦值
????????for?(let?styleAttr?in?value)?{
??????????if?(value.hasOwnProperty(styleAttr))?{
????????????newElement['style'][styleAttr]?=?value[styleAttr]
??????????}
????????}
????????break
??????case?'CHILDREN':
????????/*
?????????*?可能是一個值:可能是字符串也可能是一個JSX對象
?????????*?可能是一個數組:數組中的每一項可能是字符串也可能是JSX對象
?????????*/
????????//?首先把一個值也變為數組,這樣后期統一操作數組即可
????????!(value?instanceof?Array)???(value?=?[value])?:?null
????????value.forEach((item,?index)?=>?{
??????????//?驗證ITEM是什么類型的:如果是字符串就是創建文本節點,如果是對象,我們需要再次執行RENDER方法,把創建的元素放到最開始創建的大盒子中
??????????if?(typeof?item?===?'string')?{
????????????let?text?=?document.createTextNode(item)
????????????newElement.appendChild(text)
??????????}?else?{
????????????render(item,?newElement)
??????????}
????????})
????????break
??????default:
????????newElement.setAttribute(attr,?value)
????}
??}
??container.appendChild(newElement)
}
?

歡迎關注我掘金賬號和Github技術博客:

  • 掘金:https://juejin.im/user/1257497033714477
  • Github:https://github.com/Jacky-Summer
  • 覺得對你有幫助或有啟發的話歡迎 star,你的鼓勵是我持續創作的動力~
  • 如需在微信公眾號平臺轉載請聯系作者授權同意,其它途徑轉載請在文章開頭注明作者和文章出處。
?

2f4273792bb2fb84e9658e56a92da263.png

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/272874.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/272874.shtml
英文地址,請注明出處:http://en.pswp.cn/news/272874.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

成熟就是深諳世故卻不世故

對于一個打小在農村長大的孩子而言&#xff0c;被夸作“早熟”是一種榮耀。它意味著你足夠懂事&#xff0c;可以分擔父母的一些憂愁。但這也是一個怪圈&#xff0c;因為你常要背負這種光環去做一些超乎自己年齡的事&#xff0c;強迫自己變得堅忍、不可戰勝。那年&#xff0c;我…

2015/10/9 Python核編初級部分學習總結

終于在十一長假之后的兩天看完了《Python核心編程》的初級部分。雖然到后來兩章&#xff0c;類和環境看得越來越慢&#xff0c;越來越難以理解。很多東西只能靠強記&#xff0c;也沒辦法真正掌握了&#xff0c;我想了想&#xff0c;還是不強迫自己去背下這些知識&#xff0c;累…

SQl常用語句總結(持續更新……)

創建示例數據庫USE master;GOIF DB_ID (Nmytest) IS NOT NULLDROP DATABASE mytest;GOCREATE DATABASE mytest;GOUSE mytest;GOIF OBJECT_ID(Ndbo.Orders) IS NOT NULLDROP TABLE dbo.Orders;GOCREATE TABLE dbo.Orders(ProductID INT NOT NULL,MadeFrom CHAR(20),Sales MONEY …

大話設計模式筆記 享元模式

享元模式&#xff08;Flyweigh&#xff09;&#xff0c;運用共享技術有效地支持大量細粒度的對象。 package flyweight;//可以接受并作用于外部狀態 public abstract class Flyweight {public abstract void operation(int extrinsicState);} package flyweight;//為內部狀態增…

python12歲_12歲。Python操作Excel,12Python,excel

excel在Python中的應用存測試數據有的時候大批量的數據&#xff0c;存到txt文件里顯然不是最佳的方式&#xff0c;我們可以存到excel文件里面&#xff0c;第一方便我們存儲數據和做數據&#xff0c;一方面方便我們讀取數據&#xff0c;比較明朗。測試的時候就從數據庫中讀取出來…

什么叫工作到位?很深刻!

1、匯報工作說結果不要告訴老板工作過程多艱辛&#xff0c;你多么不容易&#xff01;老板不傻&#xff0c;否則做不到今天。舉重若輕的人老板最喜歡&#xff0c;一定要把結果給老板&#xff0c;結果思維是第一思維。2、請示工作說方案不要讓老板做問答題&#xff0c;而是要讓老…

react 給一個引用的組件添加新屬性_高階組件在React中的應用

高階組件的定義接受React組件作為輸入&#xff0c;輸出一個新的React組件。概念源自于高階函數&#xff0c;將函數作為參數&#xff0c;或者輸出一個函數&#xff0c;如map&#xff0c;reduce&#xff0c;sort。 用haskell的函數簽名來表示&#xff1a; hocFactory:: W: React.…

雜想 · 警醒

今天無意間看到CSDN上一位大牛師姐的博客&#xff0c;真的是好驚訝啊&#xff01;很多時候總是太過自以為是&#xff0c;以為自己做不到的事情別人也很難做到。恰恰相反&#xff0c;成功總是伴隨那些謙遜、努力、認真的人的&#xff01;曾幾何時&#xff0c;自己或許也是一個認…

好好的活,簡簡單單過!

生命&#xff0c;每個人只有一次&#xff0c;或長或短&#xff1b;生活&#xff0c;每個人都在繼續&#xff0c;或悲或歡&#xff1b;人生&#xff0c;每個人都在旅途&#xff0c;或起或伏。人無完人&#xff0c;事無完美&#xff0c;有些小人&#xff0c;你不須計較&#xff0…

SQL Server 中創建數據庫、更改主文件組示例

以下示例在 SQL Server 實例上創建了一個數據庫。該數據庫包括一個主數據文件、一個用戶定義文件組和一個日志文件。主數據文件在主文件組中&#xff0c;而用戶定義文件組包含兩個次要數據文件。ALTER DATABASE 語句將用戶定義文件組指定為默認文件組。然后通過指定用戶定義文件…

lunixs 退出mysql_MySQL的基本操作

1、數據庫登錄格式&#xff1a;mysql -h主機地址 -u用戶名 -p用戶密碼 -P端口 -D數據庫 -e “SQL內容”[rootwulaoer ~]# mysql -uroot -p2、修改密碼格式&#xff1a;mysqladmin -u用戶名 -p舊密碼 password 新密碼[rootwulaoer ~]# mysqladmin -uroot password 123456注&…

交際中你所不知道的說話的12個技巧!

1.“有一說一”和“自以為是”不同&#xff0c;別把粗魯當成真性情與 他人相處&#xff0c;要遵循一個基本原則&#xff1a;己所不欲&#xff0c;勿施于人。你可以真摯地描述自己的感受&#xff0c;前提是不要帶有攻擊性&#xff0c;至于對他人做出評價和判斷&#xff0c;則需要…

寬客的人amp;amp;事件映射

看完《寬客》這本書&#xff0c;敘事介紹20世紀華爾街對沖基金、股票、投資者依賴股市從直覺交易數學家的早期演化、物理學家用數學模型開發過程中的交易&#xff0c;這些進入金融數學家、物理學家依靠大數據分析、稍縱即逝的交易機會來買入賣出&#xff1b;同一時候找出交易模…

社交中的黃金法則,你要細細體會品味

1&#xff0c;不要急著用你的嘴&#xff0c;來為你的眼睛辯護什么。因為天知道你的嘴說出什么來。2&#xff0c;假如有人朝你扔石頭&#xff0c;就收起來。因為那會是你日后建高樓的基石。3&#xff0c;能忍則忍&#xff0c;忍不了就改變&#xff0c;改變不了就認了哇。4&#…

Jsp筆記(1)

1. jsp頁面中出現中文亂碼怎么解決&#xff1f; 1 <% page contentType"text/html; charsetGB2312"%> 2 <% page language"java" import"java.util.*" pageEncoding"UTF-8"%> 用上面這兩行代碼去替換自動生成的代碼&#…

這些略帶幽默句子,笑的同時多感悟一下吧(收好)

1、窮&#xff0c;并不可怕&#xff0c;我怕窮著窮著就習慣了。2、世界上唯一不用努力&#xff0c;就能得到的只有年齡。3、我是你轉身就忘的路人甲&#xff0c;憑什么陪你蹉跎年華到天涯&#xff1f;4、世界上最快樂的事情就是吃&#xff0c;第二快樂的是待會再吃。5、琴棋書畫…

java 異常機制_深入理解Java異常處理機制

一、引子try…catch…finally恐怕是大家再熟悉不過的語句了&#xff0c;而且感覺用起來也是很簡單&#xff0c;邏輯上似乎也是很容易理解。不過&#xff0c;我親自體驗的“教訓”告訴我&#xff0c;這個東西可不是想象中的那么簡單、聽話。不信&#xff1f;那你看看下面的代碼&…

ThreadLocal http://blog.jobbole.com/20400/

d轉載于:https://www.cnblogs.com/hansongjiang/p/4875332.html

做人:失信是最大的破產!

一個人最大的破產是信用的破產&#xff01;哪怕你一無所有&#xff0c;但只要信用還在&#xff0c;就還有翻身的本金。保護好信用&#xff0c;珍惜別人給你的每一次信任&#xff01;因為有時候我們只有一次機會&#xff01;朋友有時候就像鈔票&#xff0c;有真也有假。我們需要…