React運(yùn)行機(jī)制超詳細(xì)講解_第1頁(yè)
React運(yùn)行機(jī)制超詳細(xì)講解_第2頁(yè)
React運(yùn)行機(jī)制超詳細(xì)講解_第3頁(yè)
React運(yùn)行機(jī)制超詳細(xì)講解_第4頁(yè)
React運(yùn)行機(jī)制超詳細(xì)講解_第5頁(yè)
已閱讀5頁(yè),還剩14頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第React運(yùn)行機(jī)制超詳細(xì)講解目錄適合人群寫源碼之前的必備知識(shí)點(diǎn)JSX虛擬Dom原理簡(jiǎn)介手寫react過(guò)程基本架子的搭建React的源碼ReactDom.renderReactDom.Component簡(jiǎn)單源碼

適合人群

本文適合0.5~3年的react開發(fā)人員的進(jìn)階。

講講廢話:

react的源碼,的確是比vue的難度要深一些,本文也是針對(duì)初中級(jí),本意了解整個(gè)react的執(zhí)行過(guò)程。

寫源碼之前的必備知識(shí)點(diǎn)

JSX

首先我們需要了解什么是JSX。

網(wǎng)絡(luò)大神的解釋:React使用JSX來(lái)替代常規(guī)的JavaScript。JSX是一個(gè)看起來(lái)很像XML的JavaScript語(yǔ)法擴(kuò)展。

是的,JSX是一種js的語(yǔ)法擴(kuò)展,表面上像HTML,本質(zhì)上還是通過(guò)babel轉(zhuǎn)換為js執(zhí)行。再通俗的一點(diǎn)的說(shuō),jsx就是一段js,只是寫成了html的樣子,而我們讀取他的時(shí)候,jsx會(huì)自動(dòng)轉(zhuǎn)換成vnode對(duì)象給我們,這里都由react-script的內(nèi)置的babel幫助我們完成。

簡(jiǎn)單舉個(gè)栗子:

return(

div

HelloWord/div

實(shí)際上是:

returnReact.createElement(

"div",

null,

"Hello"

)

JSX本質(zhì)上就是轉(zhuǎn)換為React.createElement在React內(nèi)部構(gòu)建虛擬Dom,最終渲染出頁(yè)面。

虛擬Dom

這里說(shuō)明一下react的虛擬dom。react的虛擬dom跟vue的大為不同。vue的虛擬dom是為了是提高渲染效率,而react的虛擬dom是一定需要。很好理解,vue的template本身就是html,可以直接顯示。而jsx是js,需要轉(zhuǎn)換成html,所以用到虛擬dom。

我們描述一下react的最簡(jiǎn)版的vnode:

functioncreateElement(type,props,...children){

props.children=children;

return{

type,

props,

children,

這里的vnode也很好理解,

type表示類型,如div,span,

props表示屬性,如{id:1,style:{color:red}},

children表示子元素

下邊會(huì)在createElement繼續(xù)講解。

原理簡(jiǎn)介

我們寫一個(gè)react的最簡(jiǎn)單的源碼:

importReactfrom'react'

importReactDOMfrom'react-dom'

functionApp(props){

returndiv你好/div

/div

ReactDOM.render(App/,document.getElementById('root'))

React負(fù)責(zé)邏輯控制,數(shù)據(jù)-VDOM

首先,我們可以看到每一個(gè)js文件中,都一定會(huì)引入importReactfromreact。但是我們的代碼里邊,根本沒(méi)有用到React。但是你不引入他就報(bào)錯(cuò)了。

為什么呢?可以這樣理解,在我們上述的js文件中,我們使用了jsx。但是jsx并不能給編譯,所以,報(bào)錯(cuò)了。這時(shí)候,需要引入react,而react的作用,就是把jsx轉(zhuǎn)換為虛擬dom對(duì)象。

JSX本質(zhì)上就是轉(zhuǎn)換為React.createElement在React內(nèi)部構(gòu)建虛擬Dom,最終渲染出頁(yè)面。而引入React,就是為了時(shí)限這個(gè)過(guò)程。

ReactDom渲染實(shí)際DOM,VDOM-DOM

理解好這一步,我們?cè)倏碦eactDOM。React將jsx轉(zhuǎn)換為虛擬dom對(duì)象。我們?cè)倮肦eactDom的虛擬dom通過(guò)render函數(shù),轉(zhuǎn)換成dom。再通過(guò)插入到我們的真是頁(yè)面中。

這就是整個(gè)minireact的一個(gè)簡(jiǎn)述過(guò)程。相關(guān)參考視頻講解:進(jìn)入學(xué)習(xí)

手寫react過(guò)程

基本架子的搭建

react的功能化問(wèn)題,暫時(shí)不考慮。例如,啟動(dòng)react,怎么去識(shí)別JSX,實(shí)現(xiàn)熱更新服務(wù)等等,我們的重點(diǎn)在于react自身。我們借用一下一下react-scripts插件。

有幾種種方式創(chuàng)建我們的基本架子:

利用create-react-appzwz_react_origin快速搭建,然后刪除原本的react,react-dom等文件。(zwz_react_origin是我的項(xiàng)目名稱)

第二種,復(fù)制下邊代碼。新建package.json

{

"name":"zwz_react_origin",

"scripts":{

"start":"react-scriptsstart"

"version":"0.1.0",

"private":true,

"dependencies":{

"react-scripts":"3.4.1"

然后新建public下邊的index.html

!DOCTYPEhtml

htmllang="en"

head

/head

body

divid="root"/div

/body

/html

再新建src下邊的index.js

這時(shí)候react-scripts會(huì)快速的幫我們定為到index.html以及引入index.js

importReactfrom"react";

importReactDOMfrom"react-dom";

letjsx=(

div

divclassName=""react啟動(dòng)成功/div

/div

ReactDOM.render(jsx,document.getElementById("root"));

這樣,一個(gè)可以寫react源碼的輪子就出來(lái)了。

React的源碼

letobj=(

div

divclassName="class_0"你好/div

/div

console.log(`obj=${JSON.stringify(obj)}`);

首先,我們上述代碼,如果我們不importReact處理的話,我們可以打印出:

ReactmustbeinscopewhenusingJSXreact/react-in-jsx-scope

是的,編譯不下去,因?yàn)閖s文件再react-script,他已經(jīng)識(shí)別到obj是jsx。該jsx卻不能解析成虛擬dom,此時(shí)我們的頁(yè)面就會(huì)報(bào)錯(cuò)。通過(guò)資料的查閱,或者是源碼的跟蹤,我們可以知道,實(shí)際上,識(shí)別到j(luò)sx之后,會(huì)調(diào)用頁(yè)面中的createElement轉(zhuǎn)換為虛擬dom。

我們importReact,看看打印出來(lái)什么?

+importReactfrom"react";

letobj=(

div

divclassName="class_0"你好/div

/div

console.log(`obj:${JSON.stringify(obj)}`);

jsx={"type":"div","key":null,"ref":null,"props":{"children":{"type":"div","key":null,"ref":null,"props":{"className":"class_0","children":"你好"},"_owner":null,"_store":{}}},"_owner":null,"_store":{}}

由上邊結(jié)論可以知道,babel會(huì)識(shí)別到我們的jsx,通過(guò)createElement并將其dom(html語(yǔ)法)轉(zhuǎn)換為虛擬dom。從上述的過(guò)程,我們可以看到虛擬dom的組成,由type,key,ref,props組成。我們來(lái)模擬react的源碼。

此時(shí)我們已經(jīng)知道react中的createElement的作用是什么,我們可以嘗試著自己來(lái)寫一個(gè)createElement(新建react.js引入并手寫下邊代碼):

functioncreateElement(){

console.log("createElement",arguments);

exportdefault{

createElement,

};

此時(shí)的打印結(jié)果:

我們可以看出對(duì)象傳遞的時(shí)候,dom的格式,先傳入type,然后props屬性,我們根據(jù)原本react模擬一下這個(gè)對(duì)象轉(zhuǎn)換的打?。?/p>

functioncreateElement(type,props,...children){

props.children=children;

return{

type,

props,

}

這樣,我們已經(jīng)把最簡(jiǎn)版的一個(gè)react實(shí)現(xiàn),我們下邊繼續(xù)看看如何render到頁(yè)面

ReactDom.render

importReactfrom"react";

+importReactDOMfrom"react-dom";

letjsx=(

div

divclassName="class_0"你好/div

/div

//console.log(`jsx=${JSON.stringify(jsx)}`);

+ReactDOM.render(jsx,document.getElementById("root"));

如果此時(shí),我們引入ReactDom,通過(guò)render到對(duì)應(yīng)的元素,整個(gè)簡(jiǎn)版react的就已經(jīng)完成,頁(yè)面就會(huì)完成渲染。首先,jsx我們已經(jīng)知道是一個(gè)vnode,而第二個(gè)元素即是渲染上頁(yè)面的元素,假設(shè)我們的元素是一個(gè)html原生標(biāo)簽div。

我們新建一個(gè)reactDom.js引入。

functionrender(vnode,container){

mount(vnode,container);

functionmount(vnode,container){

const{type,props}=vnode;

constnode=document.createElement(type);//創(chuàng)建一個(gè)真實(shí)dom

const{children,...rest}=props;

children.map(item={//子元素遞歸

if(Array.isArray(item)){

item.map(c={

mount(c,node);

}else{

mount(item,node);

container.appendChild(node);

//主頁(yè):

-importReactfrom"react";

-importReactDOMfrom"react-dom";

+importReactfrom"./myReact/index.js";

+importReactDOMfrom"./myReact/reactDom.js";

letjsx=(

div

divclassName="class_0"你好/div

/div

ReactDOM.render(jsx,document.getElementById("root"));

此時(shí),我們可以看到頁(yè)面,我們自己寫的一個(gè)react渲染已經(jīng)完成。我們優(yōu)化一下。

首先,這個(gè)過(guò)程中,className=class_0消失了。我們想辦法渲染上頁(yè)面。此時(shí),虛擬dom的對(duì)象,沒(méi)有辦法,區(qū)分,哪些元素分別帶有什么屬性,我們?cè)谵D(zhuǎn)義的時(shí)候優(yōu)化一下mount。

functionmount(vnode,container){

const{type,props}=vnode;

constnode=document.createElement(type);//創(chuàng)建一個(gè)真實(shí)dom

const{children,...rest}=props;

children.map(item={//子元素遞歸

if(Array.isArray(item)){

item.map(c={

mount(c,node);

}else{

mount(item,node);

//+開始

Object.keys(rest).map(item={

if(item==="className"){

node.setAttribute("class",rest[item]);

if(item.slice(0,2)==="on"){

node.addEventListener("click",rest[item]);

//+結(jié)束

container.appendChild(node);

}

ReactDom.Component

看到這里,整個(gè)字符串render到頁(yè)面渲染的過(guò)程已完成。此時(shí)入口文件已經(jīng)解決了。對(duì)于原始標(biāo)簽div,h1已經(jīng)兼容。但是對(duì)于自定義標(biāo)簽?zāi)??或者怎么完成組件化呢。

我們先看react16+的兩種組件化模式,一種是function組件化,一種是class組件化。

首先,我們先看看demo.

importReact,{Component}from"react";

importReactDOMfrom"react-dom";

classMyClassCmpextendsReact.Component{

constructor(props){

super(props);

render(){

return(

divclassName="class_2"MyClassCmp表示:{}/div

functionMyFuncCmp(props){

returndivclassName="class_1"MyFuncCmp表示:{}/div

letjsx=(

div

h1你好/h1

divclassName="class_0"前端小伙子/div

MyFuncCmp/

MyClassCmp/

/div

ReactDOM.render(jsx,document.getElementById("root"));

先看簡(jiǎn)單點(diǎn)一些的Function組件。暫不考慮傳遞值等問(wèn)題,Function其實(shí)跟原本組件不一樣的地方,在于他是個(gè)函數(shù),而原本的jsx,是一個(gè)字符串。我們可以根據(jù)這個(gè)特點(diǎn),將函數(shù)轉(zhuǎn)換為字符串,那么Function組件即跟普通標(biāo)簽同一性質(zhì)。

我們寫一個(gè)方法:

mountFunc(vnode,container);

functionmountFunc(vnode,container){

const{type,props}=vnode;

constnode=newtype(props);

mount(node,container);

}

此時(shí)type即是函數(shù)體內(nèi)容,我們只需要實(shí)例化一下,即可跟拿到對(duì)應(yīng)的字符串,即是普通的vnode。再利用我們?cè)瓉?lái)的vnode轉(zhuǎn)換方法,即可實(shí)現(xiàn)。

按照這個(gè)思路,如果我們不考慮生命周期等相對(duì)復(fù)雜的東西。我們也相對(duì)簡(jiǎn)單,只需拿到類中的render函數(shù)即可。

mountFunc(vnode,container);

functionmountClass(vnode,container){

const{type,props}=vnode;

constnode=newtype(props);

mount(node.render(),container);

這里可能需注意,class組件,需要繼承React.Component。截圖一下react自帶的Component

可以看到,Component統(tǒng)一封裝了,setState,forceUpdate方法,記錄了props,state,refs等。我們模擬一份簡(jiǎn)版為栗子:

classComponent{

staticisReactComponent=true;

constructor(props){

ps=props;

this.state={};

setState=()=

再添加一個(gè)標(biāo)識(shí),isReactComponent表示是函數(shù)數(shù)組件化。這樣的話,我們就可以區(qū)分出:普通標(biāo)簽,函數(shù)組件標(biāo)簽,類組件標(biāo)簽。

我們可以重構(gòu)一下createElement方法,多定義一個(gè)vtype屬性,分別表示

普通標(biāo)簽函數(shù)組件標(biāo)簽類組件標(biāo)簽

根據(jù)上述標(biāo)記,我們可改造為:

functioncreateElement(type,props,...children){

props.children=children;

letvtype;

if(typeoftype==="string"){

vtype=1;

if(typeoftype==="function"){

vtype=type.isReactComponent2:3;

return{

vtype,

type,

props,

那么,我們處理時(shí):

functionmount(vnode,container){

const{vtype}=vnode;

if(vtype===1){

mountHtml(vnode,container);//處理原生標(biāo)簽

if(vtype===2){

//處理class組件

mountClass(vnode,container);

if(vtype===3){

//處理函數(shù)組件

mountFunc(vnode,container);

}

至此,我們已經(jīng)完成一個(gè)簡(jiǎn)單可組件化的react源碼。不過(guò),此時(shí)有個(gè)bug,就是文本元素的時(shí)候異常,因?yàn)槲谋驹夭粠?biāo)簽。我們優(yōu)化一下。

functionmount(vnode,container){

const{vtype}=vnode;

if(!vtype){

mountTextNode(vnode,container);//處理文本節(jié)點(diǎn)

//vtype===1

//vtype===2

//....

//處理文本節(jié)點(diǎn)

functionmountTextNode(vnode,container){

constnode=document.createTextNode(vnode);

container.appendChild(node);

}

簡(jiǎn)單源碼

package.json:

{

"name":"zwz_react_origin",

"version":"0.1.0",

"private":true,

"dependencies":{

"react":"^16.10.2",

"react-dom":"^16.10.2",

"react-scripts":"3.2.0"

"scripts":{

"start":"react-scriptsstart",

"build":"react-scriptsbuild",

"test":"react-scriptstest",

"eject":"react-scriptseject"

"eslintConfig":{

"extends":"react-app"

"browserslist":{

"production":[

"0.2%","notdead","notop_miniall"],"development":["last1chromeversion","last1firefoxversion","last1safariversion"]}}

index.js

importReactfrom"./wzReact/";

importReactDOMfrom"./wzReact/ReactDOM";

classMyClassCmpextendsReact.Component{

constructor(props){

super(props);

render(){

return(

divclassName="class_2"MyClassCmp表示:{}/div

functionMyFuncCmp(props){

returndivclassName="class_1"MyFuncCmp表示:{}/div

letjsx=(

div

h1你好/h1

divclassName="class_0"前端小伙子/div

MyFuncCmpname="真帥"/

MyClassCmpname="還有錢"/

/div

ReactDOM.render(jsx,document.getElementById("root"));

/wzReact/index.js

functioncreateElement(type,props,...children){

console.log("createElement",arguments);

props.children=children;

letvtype;

if(typeoftype==="string"){

vtype=1;

if(typeoftype==="function"){

vtype=type.isReactComponent2:3;

return{

vtype,

type,

props,

classComponent{

staticisReactComponent=true;

constructor(props){

ps=props;

this.state={};

setState=()=

exportdefault{

Component,

createElement,

};

/wzReact/ReactDOM.js

functionrender(vnode,container){

console.log("render",vnode);

//vnode-node

mount(vnode,container);

//container.appendChild(node)

//vnode-node

functionmount(vnode,container){

const{vtype}=vnode;

if(!vtype){

mountTextNode(vnode,container);//處理文本節(jié)點(diǎn)

if(vtype===1){

m

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論