react遍历对象的值_React 原理之实现 createElement 和 render 方法
前言
在 React 中,我們都知道可以寫 jsx 代碼會(huì)被編譯成真正的 DOM 插入到要顯示的頁(yè)面上。這具體是怎么實(shí)現(xiàn)的,今天我們就自己動(dòng)手做一下。
實(shí)現(xiàn) createElement 方法
這個(gè)方法平時(shí)開(kāi)發(fā)我們并不會(huì)用到,因?yàn)樗墙?jīng) babel 編譯后的代碼,我們新建一個(gè) React 項(xiàng)目,index.js 最簡(jiǎn)單的代碼結(jié)構(gòu)如下:
import?React?from?'react'import?ReactDOM?from?'react-dom'
ReactDOM.render(<h1?className='title'>Hello?Reacth1>,?document.getElementById('root'))
這里就 jsx 會(huì)變編譯成真正的 DOM ,把 html 代碼拿到 babel 官網(wǎng)編譯
于是我們就看到了 React.createElement() 方法,但這只是調(diào)用這個(gè)方法,它具體做了什么返回什么我們還不知道,我們可以打印這個(gè)函數(shù)運(yùn)行的結(jié)果:
console.log(??React.createElement(
????'h1',
????{
??????className:?'title',
????},
????'Hello?React'
??)
)
返回的這個(gè)對(duì)象就是虛擬 DOM 了。
我們來(lái)分析它返回的對(duì)象參數(shù),首先第一個(gè)是
- $$typeof: REACT_ELEMENT_TYPE
這個(gè)是 React 元素對(duì)象的標(biāo)識(shí)屬性
REACT_ELEMENT_TYPE 的值是一個(gè) Symbol 類型,代表了一個(gè)獨(dú)一無(wú)二的值。如果瀏覽器不支持 Symbol 類型,值就是一個(gè)二進(jìn)制值。
?為什么是 Symbol?主要防止 XSS 攻擊偽造一個(gè)假的 React 組件。因?yàn)?JSON 中是不會(huì)存在 Symbol 類型的。
?- key:這個(gè)比如循環(huán)中會(huì)用到這個(gè) key 值
- props:傳入的屬性值,比如 id, className, style, children 等
- ref:DOM 的引用
- 剩下的是私有屬性(本篇不展開(kāi)討論)
在本篇我們會(huì)用自己簡(jiǎn)單的方式實(shí)現(xiàn)這兩個(gè)方法,而不是根據(jù)源碼,所以實(shí)現(xiàn)上的方法只要能實(shí)現(xiàn)它的基本功能即可;有個(gè)基本概念在,以后再循序漸進(jìn)學(xué)習(xí)源碼。
而 createElement 中有三個(gè)參數(shù),更確切說(shuō)是 n 個(gè)參數(shù):
- type:表示要渲染的元素類型。這里可以傳入一個(gè)元素 Tag 名稱,也可以傳入一個(gè)組件(如 div span 等,也可以是是函數(shù)組件和類組件)
- props:創(chuàng)建 React 元素所需要的 props。
- childrens(可選參數(shù)):要渲染元素的子元素,這里可以向后傳入 n 個(gè)參數(shù)。可以為文本字符串,也可以為數(shù)組
初步 createElement 方法:
//?創(chuàng)建?JSX?對(duì)象function?createElement(type,?props,?...childrens)?{
????return?{
????????type,
????????props:?{
??????????...props,
??????????children:?childrens.length?<=?1???childrens[0]?||?''?:?childrens,
????????},
}
參數(shù)中 props 和 childrens 是并列關(guān)系,然后返回的 props 對(duì)象,里面包含了 children,所以我們需要再 props 里面添加 children 參數(shù),然后根據(jù) children 參數(shù)為一個(gè)或多個(gè)的可能在進(jìn)行取值處理。
調(diào)用該方法:
console.log(??createElement(
????'h1',
????{
??????className:?'title',
????},
????'Hello?React'
??)
)
除去其它本篇我們不討論的屬性,目前算是實(shí)現(xiàn)了一半;我們觀察原來(lái) React 自身方法輸出的結(jié)果有 key, ref, 同輸出的 props 也是并列關(guān)系,于是我們進(jìn)一步作出處理
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,
??}
}
同樣的方式調(diào)用結(jié)果如下:
如果添加多一些屬性,我們來(lái)看看結(jié)果
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')
??)
)
用了這種比較粗魯?shù)姆绞教砑?#xff0c;設(shè)置為 undefined 在實(shí)現(xiàn) render 方法的時(shí)候我們會(huì)根據(jù)這個(gè)忽略 props 內(nèi)部的 key 和 props 屬性,這里就實(shí)現(xiàn)了最基本的 createElement 方法了。
實(shí)現(xiàn) render 方法
render 方法的第一個(gè)參數(shù)接收的是 createElement 返回的對(duì)象,也就是虛擬 DOM;第二個(gè)參數(shù)則是掛載的目標(biāo) DOM。同樣的做法,我們用 babel 編譯來(lái)看:
執(zhí)行后,就被掛在到頁(yè)面了
實(shí)現(xiàn)代碼如下:
/*?*?功能:把創(chuàng)建的對(duì)象生成對(duì)應(yīng)的DOM元素,最后插入到頁(yè)面中
?* objJSX:createElement 返回的 JSX 對(duì)象
?* 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?//?不是私有的直接結(jié)束遍歷
????let?value?=?props[attr]?//?>如果當(dāng)前屬性沒(méi)有值,直接不處理即可
????if?(value?==?undefined)?continue?//?NULL?OR?UNDEFINED
????//?對(duì)幾個(gè)特殊屬性單獨(dú)設(shè)置
????switch?(attr.toUpperCase())?{
??????case?'ID':
????????newElement.setAttribute('id',?value)
????????break
??????case?'CLASSNAME':
????????newElement.setAttribute('class',?value)
????????break
??????case?'STYLE':?//?傳入的行內(nèi)樣式?style?是個(gè)對(duì)象,故需遍歷賦值
????????for?(let?styleAttr?in?value)?{
??????????if?(value.hasOwnProperty(styleAttr))?{
????????????newElement['style'][styleAttr]?=?value[styleAttr]
??????????}
????????}
????????break
??????case?'CHILDREN':
????????/*
?????????*?可能是一個(gè)值:可能是字符串也可能是一個(gè)JSX對(duì)象
?????????*?可能是一個(gè)數(shù)組:數(shù)組中的每一項(xiàng)可能是字符串也可能是JSX對(duì)象
?????????*/
????????//?首先把一個(gè)值也變?yōu)閿?shù)組,這樣后期統(tǒng)一操作數(shù)組即可
????????!(value?instanceof?Array)???(value?=?[value])?:?null
????????value.forEach((item,?index)?=>?{
??????????//?驗(yàn)證ITEM是什么類型的:如果是字符串就是創(chuàng)建文本節(jié)點(diǎn),如果是對(duì)象,我們需要再次執(zhí)行RENDER方法,把創(chuàng)建的元素放到最開(kāi)始創(chuàng)建的大盒子中
??????????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)
}
?
歡迎關(guān)注我掘金賬號(hào)和Github技術(shù)博客:
- 掘金:https://juejin.im/user/1257497033714477
- Github:https://github.com/Jacky-Summer
- 覺(jué)得對(duì)你有幫助或有啟發(fā)的話歡迎 star,你的鼓勵(lì)是我持續(xù)創(chuàng)作的動(dòng)力~
- 如需在微信公眾號(hào)平臺(tái)轉(zhuǎn)載請(qǐng)聯(lián)系作者授權(quán)同意,其它途徑轉(zhuǎn)載請(qǐng)?jiān)谖恼麻_(kāi)頭注明作者和文章出處。
總結(jié)
以上是生活随笔為你收集整理的react遍历对象的值_React 原理之实现 createElement 和 render 方法的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 数字图像与数字图像处理
- 下一篇: 玩转oracle 11g(21):修改为