




版權(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ì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 產(chǎn)業(yè)園區(qū)租賃運(yùn)營(yíng)合作協(xié)議書及要點(diǎn)
- 金融投資合規(guī)培訓(xùn)
- 員工離職管理保密協(xié)議
- 環(huán)保技術(shù)轉(zhuǎn)讓與合作協(xié)議
- 車輛占用協(xié)議書范本
- 車間行車梁安裝合同協(xié)議
- 未交就業(yè)協(xié)議書
- 車房轉(zhuǎn)讓協(xié)議書合同
- 款項(xiàng)代收協(xié)議書
- 水井共用協(xié)議書
- (完整版)韓國(guó)商法
- 2024中國(guó)南水北調(diào)集團(tuán)東線有限公司招聘筆試參考題庫(kù)含答案解析
- 2024春期國(guó)開電大思政課《中國(guó)近現(xiàn)代史綱要》在線形考(專題檢測(cè)一至八)試題及答案
- 2024年上海市行政執(zhí)法類公務(wù)員招聘筆試參考題庫(kù)附帶答案詳解
- 2024年安徽皖豐長(zhǎng)能投資有限責(zé)任公司招聘筆試參考題庫(kù)附帶答案詳解
- 復(fù)方氨基酸注射液(17AA-II)-臨床用藥解讀
- 客房服務(wù)員:高級(jí)客房服務(wù)員考試題
- 跨行業(yè)合作與創(chuàng)新
- GJB9001C內(nèi)部審核檢查表
- IgG4相關(guān)性疾病的影像改變課件
- 讀后續(xù)寫微技能之動(dòng)作描寫課件-高三英語(yǔ)一輪復(fù)習(xí)
評(píng)論
0/150
提交評(píng)論