自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

90行JS代碼構(gòu)建屬于你的React

開發(fā) 前端
當(dāng)我發(fā)現(xiàn) React 所做的一切非常簡(jiǎn)單,甚至如果我們不是下一家大型初創(chuàng)公司增加籌碼,僅需要很少的JS代碼就可以構(gòu)建它。這也是促使我寫這篇文章的動(dòng)力,希望你讀完這篇文章也有相同的感覺。

當(dāng)我學(xué)習(xí) React 的時(shí)候,我覺得它所做的一切都是魔術(shù),然后我就開始思考這種魔術(shù)究竟是什么。我感到非常驚訝,當(dāng)我發(fā)現(xiàn) React 所做的一切非常簡(jiǎn)單,甚至如果我們不是下一家大型初創(chuàng)公司增加籌碼,僅需要很少的JS代碼就可以構(gòu)建它。這也是促使我寫這篇文章的動(dòng)力,希望你讀完這篇文章也有相同的感覺。

我們將構(gòu)建什么功能?

  •  JSX
  •  函數(shù)組件
  •  類組件
  •  生命周期鉤子函數(shù)

我們不會(huì)構(gòu)建什么?

虛擬DOM

再次為了簡(jiǎn)單起見,我們不會(huì)在本文中實(shí)現(xiàn)我們自己的虛擬DOM,我們將使用 snabbdom ,有趣的是,Vue.js 虛擬DOM借鑒了它,你可以在這里讀更多關(guān)于 snabbdom 的內(nèi)容: https://github.com/snabbdom/s...

React Hooks

有些人可能對(duì)此感動(dòng)失望,但是,一口吃不成一個(gè)胖子,我們需要一步一步來(lái),因此讓我們首先構(gòu)建基本的東西,然后再在此基礎(chǔ)上加以補(bǔ)充。我計(jì)劃后續(xù)文章中在我們此次構(gòu)建的內(nèi)容之上,編寫我們自己的 React Hooks 以及虛擬DOM,

可調(diào)試性

這是增加任何庫(kù)或框架的復(fù)雜度的關(guān)鍵部分之一,由于我們只是出于娛樂(lè)目的而做,因此我們可以放心地忽略 React 提供的可調(diào)試性功能,例如 dev tools 和分析器。

性能和兼容性

我們不會(huì)過(guò)于關(guān)注我們的庫(kù)的性能,我們只想構(gòu)建能正常運(yùn)行的庫(kù)。讓我們也不要費(fèi)力地確保它可以在市場(chǎng)上的所有瀏覽器上使用,只有能夠在某些現(xiàn)代瀏覽器上可以使用,那就已經(jīng)很好了。

讓我們開始動(dòng)手

在開始之前,我們需要一個(gè)支持ES6,自動(dòng)熱更新的腳手架。我已經(jīng)創(chuàng)建了一個(gè)非常基礎(chǔ)的 webpack 腳手架,你可以進(jìn)行克隆和設(shè)置: https://github.com/ameertheha...

[[282515]]

JSX

JSX 是一個(gè)開放標(biāo)準(zhǔn),不僅限于 React,我們可以在沒(méi)有 React 的情況下使用它,它比你想象得還有容易。想要了解如何讓我們的庫(kù)支持 JSX ,我們首先需要看看在我們使用 JSX 時(shí)背后究竟發(fā)生了什么。 

  1. const App = (  
  2.     <div>  
  3.         <h1 className="primary">QndReact is Quick and dirty react</h1>  
  4.         <p>It is about building your own React in 90 lines of JavsScript</p>  
  5.     </div>  
  6. );  
  7. // 上面的 jsx 被轉(zhuǎn)換成下面這樣:  
  8. /**  
  9.  * React.createElement(type, attributes, children)  
  10.  */  
  11. var App = React.createElement(  
  12.     "div",  
  13.     null,  
  14.     React.createElement(  
  15.         "h1",  
  16.         {  
  17.             className: "primary"  
  18.         },  
  19.         "QndReact is Quick and dirty react"  
  20.     ),  
  21.     React.createElement(  
  22.         "p",  
  23.         null,  
  24.         "It is about building your own React in 90 lines of JavsScript"  
  25.     )  
  26. ); 

正如你看到的,每個(gè) JSX 元素都通過(guò) @babel/plugin-transform-react-jsx 插件轉(zhuǎn)換為了 React.createElement(...) 函數(shù)調(diào)用的形式,你可以在這里使用 JSX 進(jìn)行更多的轉(zhuǎn)換

為了使上述轉(zhuǎn)換運(yùn)行正常,在編寫 JSX 時(shí),你需要引入 React,你就是為什么當(dāng)你不引入 React 時(shí),編寫 JSX 會(huì)出現(xiàn)錯(cuò)誤的原因。 @babel/plugin-transform-react-jsx 插件已經(jīng)添加在了我們的項(xiàng)目依賴中,下面我們先安裝一下依賴 

  1. npm install 

把項(xiàng)目的配置增加到 .babelrc 文件中: 

  1.  
  2.     "plugins": [  
  3.         [  
  4.             "@babel/plugin-transform-react-jsx",  
  5.             {  
  6.                 "pragma": "QndReact.createElement", // default pragma is React.createElement  
  7.                 "throwIfNamespace": false // defaults to true  
  8.             }  
  9.         ]  
  10.     ]  

此后,只要 Babel 看到 JSX ,它就會(huì)調(diào)用 QntReact.createElement(...),但是我們還未定義此函數(shù),現(xiàn)在我們將其寫到 src/qnd-react.js 中。 

  1. const createElement = (type, props = {}, ...children) => {  
  2.     console.log(type, props, children);  
  3. };  
  4. // 像 React.createElement 一樣導(dǎo)出  
  5. const QndReact = {  
  6.     createElement  
  7. };  
  8. export default QndReact; 

我們?cè)诳刂婆_(tái)打印出了傳遞給我們的 type 、 props、 children。為了測(cè)試我們的轉(zhuǎn)換是否正常,我們可以在 src/index.js 中編寫一些 JSX 。 

  1. // QndReact 需要被引入  
  2. import QndReact from "./qnd-react";  
  3. const App = (  
  4.     <div>  
  5.         <h1 className="primary">  
  6.             QndReact is Quick and dirty react  
  7.         </h1>  
  8.         <p>It is about building your own React in 90 lines of JavsScript</p>  
  9.     </div>  
  10. ); 

啟動(dòng)項(xiàng)目: npm start,在瀏覽器輸入localhost:3000,現(xiàn)在你的控制臺(tái)看起來(lái)應(yīng)該與下圖類似:

根據(jù)以上信息,我們可以使用 snabbdom 創(chuàng)建我們內(nèi)部的 虛擬DOM節(jié)點(diǎn) ,然后我們才能將其用于我們的協(xié)調(diào)(reconciliation) 過(guò)程,可以使用如下的命令安裝 snabbdom: 

  1. npm install snabbdom 

當(dāng) QndReact.createElement(...) 被調(diào)用時(shí)嗎,創(chuàng)建和返回 虛擬DOM節(jié)點(diǎn)。 

  1. //src/qnd-react.js  
  2. import { h } from 'snabbdom';  
  3. const createElement = (type, props = {}, ...children) => {  
  4.     return h(type, { props }, children);  
  5. };  
  6. const QndReact = {  
  7.     createElement  
  8. };  
  9. export default QndReact; 

很好,現(xiàn)在我們可以解析 JSX 并創(chuàng)建自己的虛擬DOM節(jié)點(diǎn),但是仍然無(wú)法在瀏覽器中呈現(xiàn)出來(lái)。為此,我們?cè)?src/qnd-react-dom.js 添加一個(gè) render 方法。 

  1. //src/qnd-react-dom.js  
  2. //React.render(<App />, document.getElementById('root'));  
  3. const render = (el, rootElement) => {  
  4.     //將el渲染到rootElement的邏輯  
  5.  
  6. const QndReactDom = {  
  7.     render  

與其我們自己去處理將元素放到 DOM 上的繁重工作,不如讓 snabbdom 去處理。為此我們可以引入模塊去初始化 snabbdom。snabbdom 中的模塊可以看做是插件,可以支持 snabbdom 做更多的事。 

  1. //src/qnd-react-dom.js  
  2. import * as snabbdom from 'snabbdom';  
  3. import propsModule from 'snabbdom/modules/props';  
  4. const reconcile = snabbdom.init([propsModule]);  
  5. const render = (el, rootDomElement) => {  
  6.     //將el渲染到rootElement  
  7.     reconcile(rootDomElement, el);  
  8.  
  9. const QndReactDom = {  
  10.     render  
  11.  
  12. export default QndReactDom; 

我們使用這個(gè)新的 render 函數(shù)去 src/index 中去做一些魔法。 

  1. //src/index.js  
  2. import QndReact from "./qnd-react";  
  3. import QndReactDom from './qnd-react-dom';  
  4. const App = (  
  5.     <div>  
  6.         <h1 className="primary">  
  7.             QndReact is Quick and dirty react  
  8.         </h1>  
  9.         <p>It is about building your own React in 90 lines of JavsScript</p>  
  10.     </div>  
  11. );  
  12. QndReactDom.render(App, document.getElementById('root')); 

瞧,我們的JSX已經(jīng)可以渲染到屏幕上了。

等下,這個(gè)有一個(gè)小問(wèn)題,當(dāng)我們兩次調(diào)用 render 時(shí),我們會(huì)在控制臺(tái)看到一些奇怪的錯(cuò)誤(譯者注: 可以在 index.js 中多次調(diào)用 render,查看控制臺(tái)錯(cuò)誤),背后的原因是我們只有在第一次渲染時(shí),可以在真實(shí)的DOM節(jié)點(diǎn)上調(diào)用 reconcile 方法,然后,我們應(yīng)該在之前返回的虛擬DOM節(jié)點(diǎn)上調(diào)用。 

  1. //src/qnd-react-dom.js  
  2. import * as snabbdom from 'snabbdom';  
  3. import propsModule from 'snabbdom/modules/props';  
  4. const reconcile = snabbdom.init([propsModule]);  
  5. let rootVNode;  
  6. //QndReactDom.render(App, document.getElementById('root'))  
  7. const render = (el, rootDomElement) => {  
  8.     if(rootVNode == null) {  
  9.         //第一次調(diào)用 render 時(shí)  
  10.         rootVNode = rootDomElement 
  11.     }  
  12.     rootVNode = reconcile(rootVNode, el);  
  13.  
  14. const QndReactDom = {  
  15.     render  
  16.  
  17. export default QndReactDom; 

很開心,我們的應(yīng)用程序中有一個(gè)能正常工作的 JSX 渲染,現(xiàn)在讓我們開始渲染一個(gè)函數(shù)組件,而不僅僅是一些普通的 HTML。

讓我們向 src/index.js 添加一個(gè) Greeting 函數(shù)組件,如下所示: 

  1. //src/index.js  
  2. import QndReact from "./qnd-react";  
  3. import QndReactDom from './qnd-react-dom';  
  4. const Greeting = ({ name }) => <p>Welcome {name}!</p> 
  5. const App = (  
  6.     <div>  
  7.         <h1 className="primary">  
  8.             QndReact is Quick and dirty react  
  9.         </h1>  
  10.         <p>It is about building your own React in 90 lines of JavsScript</p>  
  11.         <Greeting name={"Ameer Jhan"} />  
  12.     </div>  
  13. );  
  14. QndReactDom.render(App, document.getElementById('root')); 

此時(shí),在控制臺(tái)會(huì)出現(xiàn)以下錯(cuò)誤:

我們可以在 QndReact.createElement(...) 方法中打印出數(shù)據(jù)看一下原因。 

  1. //src/qnd-react.js  
  2. import { h } from 'snabbdom';  
  3. const createElement = (type, props = {}, ...children) => {  
  4.     console.log(type, props, children);  
  5.     return h(type, { props }, children);  
  6. };  
  7. ... 

 

如果可以看到,函數(shù)組件傳遞過(guò)來(lái)的 type 是一個(gè)JS函數(shù)。如果我們調(diào)用這個(gè)函數(shù),就能獲得組件希望渲染的 HTML 結(jié)果。

我們根據(jù) type 參數(shù)的類型,如果是函數(shù)類型,我們就調(diào)用這個(gè)函數(shù),并將 props 作為參數(shù)傳給它,如果不是函數(shù)類型,我們就當(dāng)作普通的 HTML 元素處理。 

  1. //src/qnd-react.js  
  2. import { h } from 'snabbdom';  
  3. const createElement = (type, props = {}, ...children) => {  
  4.     //如果是函數(shù)組件,那么調(diào)用它,并返回執(zhí)行結(jié)果  
  5.     if (typeof (type) == 'function') {  
  6.         return type(props);  
  7.     }  
  8.     return h(type, { props }, children);  
  9. };  
  10. const QndReact = {  
  11.     createElement  
  12. };  
  13. export default QndReact; 

歡呼!我們的函數(shù)組件已經(jīng)可以正常工作了。

我們已經(jīng)完成了很多,讓我們深吸一口氣,喝杯咖啡,因?yàn)槲覀円呀?jīng)差不多實(shí)現(xiàn)了 React,不過(guò)我們還需要攻克類組件。

我們首先在 src/qnd-react.js 中創(chuàng)建 Component 基類: 

  1. //src/qnd-react.js  
  2. import { h } from 'snabbdom';  
  3. const createElement = (type, props = {}, ...children) => {  
  4.     //如果是函數(shù)組件,那么調(diào)用它,并返回執(zhí)行結(jié)果  
  5.     if (typeof (type) == 'function') {  
  6.         return type(props);  
  7.     }  
  8.     return h(type, { props }, children);  
  9. };  
  10. class Component {  
  11.     constructor() { }  
  12.     componentDidMount() { }  
  13.     setState(partialState) { }  
  14.     render() { }  
  15.  
  16. const QndReact = {  
  17.     createElement,  
  18.     Component  
  19. };  
  20. export default QndReact; 

現(xiàn)在我們?cè)?src/counter.js 中編寫我們的第一個(gè) Counter 類組件: 

  1. //src/counter.js  
  2. import QndReact from './qnd-react';  
  3. export default class Counter extends QndReact.Component {  
  4.     constructor(props) {  
  5.         super(props);  
  6.         this.state = {  
  7.             count: 0  
  8.         }  
  9.     }  
  10.     componentDidMount() {  
  11.         console.log('Component mounted');  
  12.     }  
  13.     render() {  
  14.         return <p>Count: {this.state.count}</p>  
  15.     }  

是的,我知道我們尚未在計(jì)數(shù)器中實(shí)現(xiàn)任何邏輯,但是別擔(dān)心,一旦我們的狀態(tài)管理系統(tǒng)運(yùn)行正常,我們就會(huì)添加這些內(nèi)容。現(xiàn)在,讓我們嘗試在 src/index.js 中渲染它。 

  1. //src/index.js  
  2. import QndReact from "./qnd-react";  
  3. import QndReactDom from './qnd-react-dom';  
  4. import Counter from "./counter";  
  5. const Greeting = ({ name }) => <p>Welcome {name}!</p> 
  6. const App = (  
  7.     <div>  
  8.         <h1 className="primary">  
  9.             QndReact is Quick and dirty react  
  10.         </h1>  
  11.         <p>It is about building your own React in 90 lines of JavsScript</p>  
  12.         <Greeting name={"Ameer Jhan"} />  
  13.         <Counter />  
  14.     </div>  
  15. );  
  16. QndReactDom.render(App, document.getElementById('root')); 

和料想中的一樣,又又又報(bào)錯(cuò)了。

上面的錯(cuò)誤看起來(lái)是不是很熟悉,當(dāng)你嘗試使用類組件而不集成自 React.Component 時(shí),可能遇到過(guò)以上錯(cuò)誤。要知道為什么會(huì)這樣,我們可以在 React.createElement(...) 中添加一個(gè) console.log,如下所示: 

  1. //src/qnd-react.js  
  2. import { h } from 'snabbdom';  
  3. const createElement = (type, props = {}, ...children) => {  
  4.     console.log(typeof (type), type);  
  5.     //如果是函數(shù)組件,那么調(diào)用它,并返回執(zhí)行結(jié)果  
  6.     if (typeof (type) == 'function') {  
  7.         return type(props);  
  8.     }  
  9.     return h(type, { props }, children);  
  10. }; 

我們來(lái)看看控制臺(tái)打印了什么內(nèi)容。

你可以看出 Counter 的 type 類型也是函數(shù),這是因?yàn)?Babel 會(huì)將 ES6 類轉(zhuǎn)換為普通的 JS 函數(shù),那么我們?cè)撊绾晤惤M件的情況呢。其實(shí),我們可以在我們的 Component 基類中添加一個(gè)靜態(tài)屬性,這樣我們利用該屬性去檢查 type 參數(shù)是否是一個(gè)類。React 中也是相同的處理邏輯,你可以閱讀 Dan的博客 

  1. //src/qnt-react.js  
  2. import { h } from 'snabbdom';  
  3. const createElement = (type, props = {}, ...children) => {  
  4.     console.log(typeof (type), type);  
  5.     //如果是函數(shù)組件,那么調(diào)用它,并返回執(zhí)行結(jié)果  
  6.     if (typeof (type) == 'function') {  
  7.         return type(props);  
  8.     }  
  9.     return h(type, { props }, children);  
  10. }; 
  11. class Component {  
  12.     constructor() { }  
  13.     componentDidMount() { }  
  14.     setState(partialState) { }  
  15.     render() { }  
  16.  
  17. //給 Component 組件添加靜態(tài)屬性來(lái)區(qū)分是函數(shù)還是類   
  18. Component.prototype.isQndReactClassComponent = true 
  19. const QndReact = {  
  20.     createElement,  
  21.     Component  
  22. };  
  23. export default QndReact; 

現(xiàn)在,我們?cè)?QndReact.createElement(...) 中增加一些代碼來(lái)處理類組件。 

  1. //src/qnd-react.js  
  2. import { h } from 'snabbdom';  
  3. const createElement = (type, props = {}, ...children) => {  
  4.     console.log(type.prototype);  
  5.     /**  
  6.      * 如果是類組件  
  7.      * 1.創(chuàng)建一個(gè)實(shí)例  
  8.      * 2.調(diào)用實(shí)例的 render 方法  
  9.      */  
  10.     if (type.prototype && type.prototype.isQndReactClassComponent) {  
  11.         const componentInstance = new type(props);  
  12.         return componentInstance.render();  
  13.     }  
  14.     //如果是函數(shù)組件,那么調(diào)用它,并返回執(zhí)行結(jié)果  
  15.     if (typeof (type) == 'function') {  
  16.         return type(props);  
  17.     }  
  18.     return h(type, { props }, children);  
  19. };  
  20. class Component {  
  21.     constructor() { }  
  22.     componentDidMount() { }  
  23.     setState(partialState) { }  
  24.     render() { }  
  25.  
  26. //給 Component 組件添加靜態(tài)屬性來(lái)區(qū)分是函數(shù)還是類   
  27. Component.prototype.isQndReactClassComponent = true 
  28. const QndReact = {  
  29.     createElement,  
  30.     Component  
  31. };  
  32. export default QndReact; 

現(xiàn)在,我們的類組件已經(jīng)能夠渲染到瀏覽器上了:

我們向類組件中增加 state,在此之前,我們需要知道,每次調(diào)用 this.setState({}) 時(shí),如何更新 DOM 的責(zé)任是 react-dom 包,而不是 React 的責(zé)任。這是為了使 React 的核心部分,例如Component 類與平臺(tái)分離,從而提升代碼的可重用性。即在 ReactNative 中,你也可以使用同樣的 Component 類,react-native 負(fù)責(zé)如何更新UI。你可能會(huì)問(wèn)自己:當(dāng)調(diào)用 this.setState(...) 時(shí),React 如何知道該怎么做,答案就是 react-dom 通過(guò)在 React 上設(shè)置了一個(gè) __updater 屬性與 React 進(jìn)行通信。Dan 對(duì)此也有出色的文章,你可以點(diǎn)擊閱讀?,F(xiàn)在讓我們?cè)?QndReactDom 中為 QndReact 添加 __updater 屬性。

  1. //src/qnd-react-dom.js  
  2. import QndReact from './qnd-react';  
  3. import * as snabbdom from 'snabbdom';  
  4. import propsModule from 'snabbdom/modules/props';  
  5. ...  
  6. //QndReactDom 告訴 QndReact 如何更新 DOM  
  7. QndReact.__updater = () => {  
  8.     //當(dāng)調(diào)用 this.setState 的時(shí)候更新 DOM 邏輯  

無(wú)論何時(shí)我們調(diào)用 this.setState({...}),我們都需要比較組件的 oldVNode 和在組件上調(diào)用了 render 方法之后生成的 newVNode。為了進(jìn)行比較,我們?cè)陬惤M件上添加 __vNode 屬性,以維護(hù)該組件當(dāng)前的 VNode 實(shí)例。 

  1. //src/qnd-react.js  
  2. ...  
  3. const createElement = (type, props = {}, ...children) => {  
  4.     /**  
  5.      * 如果是類組件  
  6.      * 1.創(chuàng)建一個(gè)實(shí)例  
  7.      * 2.調(diào)用實(shí)例的 render 方法  
  8.      */  
  9.     if (type.prototype && type.prototype.isQndReactClassComponent) {  
  10.         const componentInstance = new type(props);  
  11.         componentInstancecomponentInstance.__vNode = componentInstance.render();  
  12.         return componentInstance.__vNode;  
  13.     }  
  14.     //如果是函數(shù)組件,那么調(diào)用它,并返回執(zhí)行結(jié)果  
  15.     if (typeof (type) == 'function') {  
  16.         return type(props);  
  17.     }  
  18.     return h(type, { props }, children);  
  19. };  
  20. ... 

現(xiàn)在我們來(lái)在 Component 的基類中實(shí)現(xiàn) setState 方法。 

  1. //src/qnd-react.js  
  2. ...  
  3. class Component {  
  4.     constructor() { }  
  5.     componentDidMount() { }  
  6.     setState(partialState) {   
  7.         this.state = {  
  8.             ...this.state,  
  9.             ...partialState  
  10.         }  
  11.         //調(diào)用 QndReactDom 提供的 __updater 方法  
  12.         QndReact.__updater(this);  
  13.     }  
  14.     render() { }  
  15.  
  16. ... 

處理 QndReactDom 中的 __updater 方法。 

  1. //src/qnd-react-dom.js  
  2. ...  
  3. QndReact.__updater = (componentInstance) => {  
  4.     //當(dāng)調(diào)用 this.setState 的時(shí)候更新 DOM 邏輯  
  5.     //獲取在 __vNode 上存儲(chǔ)的 oldVNode  
  6.     const oldVNode = componentInstance.__vNode;  
  7.     //獲取 newVNode  
  8.     const newVNode = componentInstance.render();  
  9.     //更新 __vNode  
  10.     componentInstance.__vNode = reconcile(oldVNode, newVNode);  
  11.  
  12. ...  
  13. export default QndReactDom; 

OK,我們?cè)?Counter 組件中增加 state 來(lái)檢驗(yàn)我們的 setState 實(shí)現(xiàn)是否生效。 

  1. //src/counter.js  
  2. import QndReact from './qnd-react';  
  3. export default class Counter extends QndReact.Component {  
  4.     constructor(props) {  
  5.         super(props);  
  6.         this.state = {  
  7.             count: 0  
  8.         }  
  9.         // update the count every second  
  10.         setInterval(() => {  
  11.             this.setState({  
  12.                 count: this.state.count + 1  
  13.             })  
  14.         }, 1000);  
  15.     }  
  16.     componentDidMount() {  
  17.         console.log('Component mounted');  
  18.     }  
  19.     render() {  
  20.         return <p>Count: {this.state.count}</p>  
  21.     }  

太棒啦,現(xiàn)在 Counter 組件運(yùn)行情況與我們預(yù)期完全一致。

我們繼續(xù)添加 componentDidMount 的生命周期鉤子函數(shù)。 Snabbdom 提供了一些鉤子函數(shù),通過(guò)他們,我們可以知道真實(shí)DOM上面是否有添加,刪除或是更新了虛擬DOM節(jié)點(diǎn),你可以在此處了解更多信息。 

  1. //src/qnd-react.js  
  2. import { h } from 'snabbdom';  
  3. const createElement = (type, props = {}, ...children) => {  
  4.     /**  
  5.      * 如果是類組件  
  6.      * 1.創(chuàng)建一個(gè)實(shí)例  
  7.      * 2.調(diào)用實(shí)例的 render 方法  
  8.      */  
  9.     if (type.prototype && type.prototype.isQndReactClassComponent) {  
  10.         const componentInstance = new type(props);  
  11.         componentInstancecomponentInstance.__vNode = componentInstance.render();  
  12.         return componentInstance.__vNode;  
  13.         //增加鉤子函數(shù)(當(dāng)虛擬DOM被添加到真實(shí)DOM節(jié)點(diǎn)上時(shí))  
  14.         componentInstance.__vNode.data.hook = {  
  15.             create: () => {  
  16.                 componentInstance.componentDidMount()  
  17.             }  
  18.         }  
  19.     }  
  20.     //如果是函數(shù)組件,那么調(diào)用它,并返回執(zhí)行結(jié)果  
  21.     if (typeof (type) == 'function') {  
  22.         return type(props);  
  23.     }  
  24.     return h(type, { props }, children);  
  25. };  
  26. ...  
  27. export default QndReact; 

至此,我們已經(jīng)在類組件上支持了 componentDidMount 生命周期鉤子函數(shù)。

結(jié)束之前,我們?cè)偬砑酉率录壎ǖ闹С?。為此,我們可以?Counter 組件中增加一個(gè)按鈕,點(diǎn)擊的時(shí)候,計(jì)數(shù)器的數(shù)字增加。請(qǐng)注意,我們遵循的是基于常規(guī)的JS事件命名約定,而非基于 React,即雙擊事件使用 onDblClick,而非 onDoubleClick。 

  1. import QndReact from './qnd-react';  
  2. export default class Counter extends QndReact.Component {  
  3.     constructor(props) {  
  4.         super(props);  
  5.         this.state = {  
  6.             count: 0  
  7.         }  
  8.     }  
  9.     componentDidMount() {  
  10.         console.log('Component mounted');  
  11.     }  
  12.     render() {  
  13.         return (  
  14.             <div>  
  15.                 <p>Count: {this.state.count}</p>  
  16.                 <button onClick={() => this.setState({  
  17.                     count: this.state.count + 1  
  18.                 })}>Increment</button>  
  19.             </div>  
  20.         )  
  21.     }  

上面的組件不會(huì)正常工作,因?yàn)槲覀儧](méi)有告訴我們的 VDom 如何去處理它。首先,我們給 Snabdom 增加事件監(jiān)聽模塊。 

  1. //src/qnd-react-dom.js  
  2. import QndReact from './qnd-react';  
  3. import * as snabbdom from 'snabbdom';  
  4. import propsModule from 'snabbdom/modules/props';  
  5. import eventlistenersModule from 'snabbdom/modules/eventlisteners';  
  6. const reconcile = snabbdom.init([propsModule, eventlistenersModule]);  
  7. ... 

Snabdom 希望將文本屬性和事件屬性作為兩個(gè)單獨(dú)的對(duì)象,我們我們需要這樣做: 

  1. //src/qnd-react.js  
  2. import { h } from 'snabbdom';  
  3. const createElement = (type, props = {}, ...children) => {  
  4.     ...  
  5.     let dataProps = {};  
  6.     let eventProps = {};  
  7.     for (let propKey in props) {  
  8.         // event 屬性總是以 `on` 開頭  
  9.         if (propKey.startsWith('on')) {  
  10.             const event = propKey.substring(2).toLowerCase();  
  11.             eventProps[event] = props[propKey];  
  12.         } else {  
  13.             dataProps[propKey] = props[propKey];  
  14.         }  
  15.     }  
  16.     return h(type, { props: dataProps, on: eventProps }, children);  
  17. };  
  18. ... 

現(xiàn)在當(dāng)我們點(diǎn)擊 Counter 組件的按鈕的時(shí)候,計(jì)數(shù)器加1。

太棒了,我們終于完成了一個(gè)React的簡(jiǎn)陋的實(shí)現(xiàn)。但是,我們還不能呈現(xiàn)列表,我想把它作為有趣的小任務(wù)交給您。我建議您嘗試在 src/index.js 中呈現(xiàn)一個(gè)列表,然后調(diào)試 QndReact.createElement(...) 方法找出問(wèn)題所在。

感謝您一直陪伴我,希望您喜歡構(gòu)建自己的 React ,并了解了 React 在此過(guò)程中是如何工作的。如果您在任何地方卡住了,請(qǐng)隨時(shí)參考我共享的代碼: https://github.com/ameertheha...

 

 

責(zé)任編輯:龐桂玉 來(lái)源: Win10系統(tǒng)之家
相關(guān)推薦

2017-03-28 21:03:35

代碼React.js

2023-10-08 18:05:57

2022-02-08 12:30:30

React事件系統(tǒng)React事件系統(tǒng)

2014-06-19 10:02:32

Haskell代碼

2021-09-09 05:57:57

JS模塊打包器前端

2012-06-18 15:18:32

JS

2021-12-16 06:21:16

React組件前端

2022-04-15 08:07:21

ReactDiff算法

2023-07-03 07:51:47

2016-10-09 11:03:41

Javascript模塊化Web

2021-01-24 16:00:22

Ansible系統(tǒng)運(yùn)維

2021-06-22 10:16:39

Java工具庫(kù)代碼

2015-04-28 09:21:28

JSJS俄羅斯方塊游戲帝國(guó)

2017-05-08 14:27:49

PHP框架函數(shù)框架

2025-03-25 08:15:00

JavaScript開發(fā)代碼

2017-12-25 05:08:53

智能客服深度學(xué)習(xí)人工智能

2021-12-26 12:10:21

React組件前端

2018-06-19 08:35:51

情感分析數(shù)據(jù)集代碼

2020-04-27 14:54:45

React庫(kù)開發(fā)

2021-06-23 10:32:24

前端ES6代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)