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