摘要:寫在開頭從頭實(shí)現(xiàn)一個(gè)簡(jiǎn)易版一地址上一節(jié),我們?cè)敿?xì)介紹了實(shí)現(xiàn)一個(gè)簡(jiǎn)易的思路以及整體的結(jié)構(gòu),但是對(duì)于渲染和更新的原理,卻還沒有提及,因此,本節(jié)我們將重點(diǎn)放在的渲染上。
寫在開頭
從頭實(shí)現(xiàn)一個(gè)簡(jiǎn)易版React(一)地址:https://segmentfault.com/a/11...
上一節(jié),我們?cè)敿?xì)介紹了實(shí)現(xiàn)一個(gè)簡(jiǎn)易R(shí)eact的思路以及整體的結(jié)構(gòu),但是對(duì)于渲染和更新的原理,卻還沒有提及,因此,本節(jié)我們將重點(diǎn)放在vDom的渲染上。
我們把React元素分為text,basic,custom三種,并分別封裝了三種vDom的ReactComponent,用來(lái)處理各自的渲染和更新,在這里,我們將重心放在各自ReactComponet的mount方法上。
ReactTextComponentReactTextComponent用來(lái)處理文本節(jié)點(diǎn),為了標(biāo)識(shí)方便,在返回的內(nèi)容上加了span標(biāo)簽。
// 用來(lái)表示文本節(jié)點(diǎn)在渲染,更新,刪除時(shí)應(yīng)該做的事情
class ReactTextComponent extends ReactComponent {
// 渲染
mountComponent(rootId) {
this._rootNodeId = rootId
return `${this._vDom}`
}
}
//代碼地址:src/react/component/ReactTextComponent.js
ReactTextComponent的mount方法非常簡(jiǎn)單,打上標(biāo)識(shí)符,將內(nèi)容插入標(biāo)簽內(nèi),并把標(biāo)簽內(nèi)容返回就可以了。
ReactDomComponent這個(gè)類用來(lái)處理原生節(jié)點(diǎn)的vDom,在將vDom渲染為原生DOM時(shí),要考慮3點(diǎn):
元素類型
拼湊屬性,包含普通屬性及事件的處理
子節(jié)點(diǎn)的遞歸渲染
代碼如下:
// 用來(lái)表示原生節(jié)點(diǎn)在渲染,更新,刪除時(shí)應(yīng)該做的事情 class ReactDomComponent extends ReactComponent { constructor(vDom) { super(vDom) this._renderedChildComponents = null } // 渲染 mountComponent(rootId) { this._rootNodeId = rootId const { props, type, props: { children = [] } } = this._vDom, childComponents = [] // 設(shè)置tag,加上標(biāo)識(shí) let tagOpen = `${type} data-reactid=${this._rootNodeId}`, tagClose = `/${type}`, content = "" // 拼湊屬性 for (let propKey in props) { // 事件 if (/^on[A-Za-z]/.test(propKey)) { const eventType = propKey.replace("on", "") $(document).delegate(`[data-reactid="${this._rootNodeId}"]`, `${eventType}.${this._rootNodeId}`, props[propKey]) } // 普通屬性,排除children與事件 if (props[propKey] && propKey !== "children" && !/^on[A-Za-z]/.test(propKey)) { tagOpen += ` ${propKey}=${props[propKey]}` } } // 獲取子節(jié)點(diǎn)渲染出的內(nèi)容 children.forEach((item, index) => { // 再次使用工廠方法實(shí)例化子節(jié)點(diǎn)的component,拼接好返回 const childComponent = instantiateReactComponent(item) childComponent._mountIndex = index childComponents.push(childComponent) // 子節(jié)點(diǎn)的rootId是父節(jié)點(diǎn)的rootId加上索引拼接的值 const curRootId = `${this._rootNodeId}.${index}` // 得到子節(jié)點(diǎn)的渲染內(nèi)容 const childMarkup = childComponent.mountComponent(curRootId) // 拼接 content += childMarkup // 保存所有子節(jié)點(diǎn)的component this._renderedChildComponents = childComponents }) return `<${tagOpen}>${content}<${tagClose}>` } } //代碼地址:src/react/component/ReactDomComponent.js
在React的官方實(shí)現(xiàn)中,自己實(shí)現(xiàn)了一套事件系統(tǒng),這里用了jQuery的事件代替。
在樣式上,需要基于傳入的style對(duì)象創(chuàng)建樣式,這里也暫時(shí)忽略了。
在創(chuàng)建自定義組件時(shí),通常會(huì)這樣創(chuàng)建
import React from "react" class App extends React.Component { render() { return ( ) } }
所以,第一步,我們先實(shí)現(xiàn)Component這個(gè)父類
// 所有自定義組件的父類 class Component { constructor(props) { this.props = props } setState(newState) { this._reactInternalInstance.updateComponent(null, newState) } } //代碼地址:src/react/Component.js
Component類上我們主要實(shí)現(xiàn)了setState方法,至于有什么用,我們放在更新里說(shuō)。
在自定義組件的vDom中,type保存的是我們創(chuàng)建的Component的引用,所以在ReactCompositeComponent的mount方法中。我們首先根據(jù)vDom的type創(chuàng)建組件的實(shí)例,在以此調(diào)用它初始渲染的生命周期方法,render方法。
在render方法中,返回了組件渲染內(nèi)容的vDom,我們根據(jù)這個(gè)vDom創(chuàng)建它的ReactComponent并調(diào)用mount(),就得到了真實(shí)的渲染內(nèi)容。
貼代碼:
export default class extends ReactComponent { constructor(element) { super(element) // 存放對(duì)應(yīng)的組件實(shí)例 this._instance = null this._renderedComponent = null } // 渲染 mountComponent(rootId) { this._rootNodeId = rootId const { type: Component, props } = this._vDom // 獲取自定義組件的實(shí)例 const inst = new Component(props) this._instance = inst // 保留對(duì)當(dāng)前component的引用,下面更新時(shí)會(huì)用到 inst._reactInternalInstance = this inst.componentWillMount && inst.componentWillMount() // 調(diào)用自定義組件的render方法,返回一個(gè)Vdom const renderedVdom = inst.render() // 獲取renderedComponent的component const renderedComponent = instantiateReactComponent(renderedVdom) this._renderedComponent = renderedComponent // 得到渲染之后的內(nèi)容 const renderMarkup = renderedComponent.mountComponent(this._rootNodeId) // 在React.render方法最后觸發(fā)了mountReady事件,所在在這里監(jiān)聽,在渲染完成后觸發(fā) $(document).on("mountReady", () => { inst.componentDidMount && inst.componentDidMount() }) return renderMarkup } } // 代碼地址:src/react/component/ReactCompositeComponent.js
從這里可以看出,自定義組件的mount方法并不負(fù)責(zé)具體的渲染,這些都交給了它的render,它把重心放在了創(chuàng)建對(duì)象和調(diào)用生命周期上。
總結(jié)文章到這,我們的簡(jiǎn)易版react已經(jīng)初步實(shí)現(xiàn)了虛擬DOM的創(chuàng)建,生命周期的調(diào)用,虛擬DOM的遞歸渲染和事件處理。
總結(jié)一下,每一個(gè)vDom都有ReactComponent相對(duì)應(yīng),遞歸渲染的本質(zhì)無(wú)非就是獲取每個(gè)vDom的ReactComponent,并調(diào)用它的mount方法。
以上就是整個(gè)渲染的思路,下節(jié)我們將實(shí)現(xiàn)它的diff算法以及更新。
下一節(jié)地址:https://segmentfault.com/a/11...
參考資料,感謝幾位前輩的分享:
https://www.cnblogs.com/sven3...
https://github.com/purplebamb...
陳屹 《深入React技術(shù)?!?/p>
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/93110.html
摘要:寫在開頭從頭實(shí)現(xiàn)一個(gè)簡(jiǎn)易版二地址在上一節(jié),我們的已經(jīng)具備了渲染功能。參考資料,感謝幾位前輩的分享陳屹深入技術(shù)棧 寫在開頭 從頭實(shí)現(xiàn)一個(gè)簡(jiǎn)易版React(二)地址:https://segmentfault.com/a/11...在上一節(jié),我們的react已經(jīng)具備了渲染功能。在這一節(jié)我們將著重實(shí)現(xiàn)它的更新,說(shuō)到更新,大家可能都會(huì)想到React的diff算法,它可以說(shuō)是React性能高效的保...
摘要:既然看不懂,那就看看社區(qū)前輩們寫的一些源碼分析文章以及實(shí)現(xiàn)思路吧,又這么過(guò)了幾天,總算是摸清點(diǎn)思路,于是在參考了前輩們的基礎(chǔ)上,實(shí)現(xiàn)了一個(gè)簡(jiǎn)易版的??偨Y(jié)以上就是實(shí)現(xiàn)一個(gè)的總體思路,下節(jié)我們重點(diǎn)放在不同的上。 寫在開頭 工作中使用react也很長(zhǎng)一段時(shí)間了,雖然對(duì)它的用法,原理有了一定的了解,但是總感覺停留在表面。本著知其然知其所以然的態(tài)度,我試著去看了react源碼,幾天下來(lái),發(fā)現(xiàn)并不...
摘要:登錄視圖登陸失敗用戶名或密碼不能為空彈出提示框成功是點(diǎn)擊登錄按鈕后調(diào)用的函數(shù),這里的功能比較簡(jiǎn)單。通過(guò)把發(fā)出去密碼登錄聲明組件需要整個(gè)中的哪一部分?jǐn)?shù)據(jù)作為自己的將和組件聯(lián)系在一起編寫是負(fù)責(zé)生成的,所以在大項(xiàng)目中還會(huì)用到合并。 本豬說(shuō) 本豬豬剛學(xué)react,也剛看RN,就叫寫這個(gè),苦不堪言,搭環(huán)境就搭了好久。看網(wǎng)上教程也是改了好多小地方才寫完了。本著雷鋒精神手把手教你寫(假的)。 sho...
摘要:五六月份推薦集合查看最新的請(qǐng)點(diǎn)擊集前端最近很火的框架資源定時(shí)更新,歡迎一下。蘇幕遮燎沈香宋周邦彥燎沈香,消溽暑。鳥雀呼晴,侵曉窺檐語(yǔ)。葉上初陽(yáng)乾宿雨,水面清圓,一一風(fēng)荷舉。家住吳門,久作長(zhǎng)安旅。五月漁郎相憶否。小楫輕舟,夢(mèng)入芙蓉浦。 五、六月份推薦集合 查看github最新的Vue weekly;請(qǐng)::點(diǎn)擊::集web前端最近很火的vue2框架資源;定時(shí)更新,歡迎 Star 一下。 蘇...
閱讀 921·2023-04-25 18:51
閱讀 1875·2021-09-09 11:39
閱讀 3285·2019-08-30 15:53
閱讀 2104·2019-08-30 13:03
閱讀 1314·2019-08-29 16:17
閱讀 587·2019-08-29 11:33
閱讀 1888·2019-08-26 14:00
閱讀 2126·2019-08-26 13:41