欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React運行機制超詳細(xì)講解

 更新時間:2022年11月04日 16:12:13   作者:goClient1992  
React 作為當(dāng)下最為流行的前端開發(fā)框架之一,使用它可以快速構(gòu)建大型 Web 應(yīng)用,加上其出色的性能表現(xiàn),使得眾多互聯(lián)網(wǎng)公司對它格外地青睞,這篇文章主要介紹了React運行機制

適合人群

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

講講廢話:

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

寫源碼之前的必備知識點

JSX

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

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

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

簡單舉個栗子:

return (
  <div>
    Hello  Word  </div>
)
實際上是:
return React.createElement(
  "div",
  null,
  "Hello"
)

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

虛擬Dom

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

我們描述一下react的最簡版的vnode:

function createElement(type, props, ...children) {
  props.children = children;
  return {
    type,
    props,
    children,
  };
}

這里的vnode也很好理解,

type表示類型,如div,span,

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

children表示子元素

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

原理簡介

我們寫一個react的最簡單的源碼:

import React from 'react'
import ReactDOM from 'react-dom'
function App(props){
     return <div>你好</div>
 </div>
}
ReactDOM.render(<App/>,  document.getElementById('root'))

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

首先,我們可以看到每一個js文件中,都一定會引入import React from ‘react’。但是我們的代碼里邊,根本沒有用到React。但是你不引入他就報錯了。

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

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

ReactDom渲染實際DOM,VDOM -> DOM

理解好這一步,我們再看ReactDOM。React將jsx轉(zhuǎn)換為“虛擬dom”對象。我們再利用ReactDom的虛擬dom通過render函數(shù),轉(zhuǎn)換成dom。再通過插入到我們的真是頁面中。

這就是整個mini react的一個簡述過程。相關(guān)參考視頻講解:進入學(xué)習(xí)

手寫react過程

基本架子的搭建

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

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

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

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

  {
    "name": "zwz_react_origin",
    "scripts": {
      "start": "react-scripts start"
    },
    "version": "0.1.0",
    "private": true,
    "dependencies": {
      "react-scripts": "3.4.1"
    },
  }

然后新建public下邊的index.html

  <!DOCTYPE html>
  <html lang="en">
    <head>
    </head>
    <body>
      <div id="root"></div>
    </body>
  </html>

再新建src下邊的index.js

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

  import React from "react";
  import ReactDOM from "react-dom";
  let jsx = (
    <div>
      <div className="">react啟動成功</div>
    </div>
  );
  ReactDOM.render(jsx, document.getElementById("root"));

這樣,一個可以寫react源碼的輪子就出來了。

React的源碼

let obj = (
  <div>
    <div className="class_0">你好</div>
  </div>
);
console.log(`obj=${ JSON.stringify( obj) }`);

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

‘React’ must be in scope when using JSX react/react-in-jsx-scope

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

我們import React,看看打印出來什么?

+ import React from "react";
let obj = (
  <div>
    <div className="class_0">你好</div>
  </div>
);
console.log(`obj:${ JSON.stringify( obj) }`);
結(jié)果:
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會識別到我們的jsx,通過createElement并將其dom(html語法)轉(zhuǎn)換為虛擬dom。從上述的過程,我們可以看到虛擬dom的組成,由type,key,ref,props組成。我們來模擬react的源碼。

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

function createElement() {
  console.log("createElement", arguments);
}
export default {
  createElement,
};

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

我們可以看出對象傳遞的時候,dom的格式,先傳入type, 然后props屬性,我們根據(jù)原本react模擬一下這個對象轉(zhuǎn)換的打印:

function createElement(type, props, ...children) {
  props.children = children;
  return {
    type,
    props,
  };
}

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

ReactDom.render

import React from "react";
+ import ReactDOM from "react-dom";
let jsx = (
  <div>
    <div className="class_0">你好</div>
  </div>
);
// console.log(`jsx=${ JSON.stringify( jsx) }`);
+ ReactDOM.render(jsx, document.getElementById("root"));

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

我們新建一個reactDom.js引入。

function render(vnode, container) {
  mount(vnode, container);
}
function mount(vnode, container){
    const { type, props } = vnode;
    const node = document.createElement(type);//創(chuàng)建一個真實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);
}
//主頁:
- import React from "react";
- import ReactDOM from "react-dom";
+ import React from "./myReact/index.js";
+ import ReactDOM from "./myReact/reactDom.js";
let jsx = (
  <div>
    <div className="class_0">你好</div>
  </div>
);
ReactDOM.render(jsx, document.getElementById("root"));

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

首先,這個過程中, className="class_0"消失了。我們想辦法渲染上頁面。此時,虛擬dom的對象,沒有辦法,區(qū)分,哪些元素分別帶有什么屬性,我們在轉(zhuǎn)義的時候優(yōu)化一下mount。

 function mount(vnode, container){
    const { type, props } = vnode;
    const node = document.createElement(type);//創(chuàng)建一個真實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

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

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

首先,我們先看看demo.

import React, { Component } from "react";
import ReactDOM from "react-dom";
 class MyClassCmp extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
    <div className="class_2" >MyClassCmp表示:{this.props.name}</div>
    );
  }
}
function MyFuncCmp(props) {
  return <div className="class_1" >MyFuncCmp表示:{props.name}</div>;
}
let jsx = (
  <div>
    <h1>你好</h1>
    <div className="class_0">前端小伙子</div>
    <MyFuncCmp />
    <MyClassCmp  />
  </div>
);
ReactDOM.render(jsx, document.getElementById("root"));

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

我們寫一個方法:

mountFunc(vnode, container);
function mountFunc(vnode, container) {
  const { type, props } = vnode;
  const node = new type(props);
  mount(node, container);
}

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

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

mountFunc(vnode, container);
function mountClass(vnode, container) {
  const { type, props } = vnode;
  const node = new type(props);
  mount(node.render(), container);
}

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

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

class Component {
  static isReactComponent = true;
  constructor(props) {
    this.props = props;
    this.state = {};
  }
  setState = () => {};
}

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

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

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

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

function createElement(type, props, ...children) {
  props.children = children;
  let vtype;
  if (typeof type === "string") {
    vtype = 1;
  }
  if (typeof type === "function") {
    vtype = type.isReactComponent ? 2 : 3;
  }
  return {
    vtype,
    type,
    props,
};

那么,我們處理時:

function mount(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)完成一個簡單可組件化的react源碼。不過,此時有個bug,就是文本元素的時候異常,因為文本元素不帶標(biāo)簽。我們優(yōu)化一下。

function mount(vnode, container) {
  const { vtype } = vnode;
  if (!vtype) {
    mountTextNode(vnode, container); //處理文本節(jié)點
  }
  //vtype === 1
  //vtype === 2
  // ....
}
//處理文本節(jié)點
function mountTextNode(vnode, container) {
  const node = document.createTextNode(vnode);
  container.appendChild(node);
}

簡單源碼

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-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": {
    "production": [
      ">0.2%",      "not dead",      "not op_mini all"    ],    "development": [      "last 1 chrome version",      "last 1 firefox version",      "last 1 safari version"    ]  }}

index.js

import React from "./wzReact/";
import ReactDOM from "./wzReact/ReactDOM";
class MyClassCmp extends React.Component {
  constructor(props) {
    super(props);
  }
render() {
    return (
    <div className="class_2" >MyClassCmp表示:{this.props.name}</div>
    );
  }
}
function MyFuncCmp(props) {
  return <div className="class_1" >MyFuncCmp表示:{props.name}</div>;
}
let jsx = (
  <div>
    <h1>你好</h1>
    <div className="class_0">前端小伙子</div>
    <MyFuncCmp name="真帥" />
    <MyClassCmp name="還有錢" />
  </div>
);
ReactDOM.render(jsx, document.getElementById("root"));

/wzReact/index.js

function createElement(type, props, ...children) {
  console.log("createElement", arguments);
  props.children = children;
  let vtype;
  if (typeof type === "string") {
    vtype = 1;
  }
  if (typeof type === "function") {
    vtype = type.isReactComponent ? 2 : 3;
  }
  return {
    vtype,
    type,
    props,
  };
}
class Component {
  static isReactComponent = true;
  constructor(props) {
    this.props = props;
    this.state = {};
  }
  setState = () => {};
}
export default {
  Component,
  createElement,
};

/wzReact/ReactDOM.js

function render(vnode, container) {
  console.log("render", vnode);
  //vnode-> node
  mount(vnode, container);
  // container.appendChild(node)
}
// vnode-> node
function mount(vnode, container) {
  const { vtype } = vnode;
  if (!vtype) {
    mountTextNode(vnode, container); //處理文本節(jié)點
  }
  if (vtype === 1) {
    mountHtml(vnode, container); //處理原生標(biāo)簽
  }
  if (vtype === 3) {
    //處理函數(shù)組件
    mountFunc(vnode, container);
  }
  if (vtype === 2) {
    //處理class組件
    mountClass(vnode, container);
  }
}
//處理文本節(jié)點
function mountTextNode(vnode, container) {
  const node = document.createTextNode(vnode);
  container.appendChild(node);
}
//處理原生標(biāo)簽
function mountHtml(vnode, container) {
  const { type, props } = vnode;
  const node = document.createElement(type);
  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]);
    }
  });
  container.appendChild(node);
}
function mountFunc(vnode, container) {
  const { type, props } = vnode;
  const node = new type(props);
  mount(node, container);
}
function mountClass(vnode, container) {
  const { type, props } = vnode;
  const cmp = new type(props);
  const node = cmp.render();
  mount(node, container);
}
export default {
  render,
};

到此這篇關(guān)于React運行機制超詳細(xì)講解的文章就介紹到這了,更多相關(guān)React運行機制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React Native第三方平臺分享的實例(Android,IOS雙平臺)

    React Native第三方平臺分享的實例(Android,IOS雙平臺)

    本篇文章主要介紹了React Native第三方平臺分享的實例(Android,IOS雙平臺),具有一定的參考價值,有興趣的可以了解一下
    2017-08-08
  • react中的useEffect()的使用詳解

    react中的useEffect()的使用詳解

    useEffect()是react中的hook函數(shù),作用是用于創(chuàng)建由渲染本身引起的操作,介紹了依賴項數(shù)組不同的區(qū)別,對react useEffect()使用相關(guān)知識感興趣的朋友一起看看吧
    2024-05-05
  • react axios配置代理(proxy),如何解決本地開發(fā)時的跨域問題

    react axios配置代理(proxy),如何解決本地開發(fā)時的跨域問題

    這篇文章主要介紹了react axios配置代理(proxy),如何解決本地開發(fā)時的跨域問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • React實現(xiàn)下拉框的key,value的值同時傳送

    React實現(xiàn)下拉框的key,value的值同時傳送

    這篇文章主要介紹了React實現(xiàn)下拉框的key,value的值同時傳送方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • react自動化構(gòu)建路由的實現(xiàn)

    react自動化構(gòu)建路由的實現(xiàn)

    這篇文章主要介紹了react自動化構(gòu)建路由的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • React利用lazy+Suspense實現(xiàn)路由懶加載

    React利用lazy+Suspense實現(xiàn)路由懶加載

    這篇文章主要為大家詳細(xì)介紹了React如何利用lazy+Suspense實現(xiàn)路由懶加載,文中的示例代碼簡潔易懂,感興趣的小伙伴可以跟隨小編一起了解一下
    2023-06-06
  • React組件二次包裝的具體實現(xiàn)

    React組件二次包裝的具體實現(xiàn)

    本文主要介紹了React組件二次包裝的具體實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • useReducer使用詳解及其應(yīng)用場景

    useReducer使用詳解及其應(yīng)用場景

    這篇文章主要介紹了useReducer使用詳解及其應(yīng)用場景,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • React中useCallback useMemo使用方法快速精通

    React中useCallback useMemo使用方法快速精通

    在React函數(shù)組件中,當(dāng)組件中的props發(fā)生變化時,默認(rèn)情況下整個組件都會重新渲染。換句話說,如果組件中的任何值更新,整個組件將重新渲染,包括沒有更改values/props的函數(shù)/組件。在react中,我們可以通過memo,useMemo以及useCallback來防止子組件的rerender
    2023-02-02
  • 一看就懂的ReactJs基礎(chǔ)入門教程-精華版

    一看就懂的ReactJs基礎(chǔ)入門教程-精華版

    現(xiàn)在最熱門的前端框架有AngularJS、React、Bootstrap等。自從接觸了ReactJS,ReactJs的虛擬DOM(Virtual DOM)和組件化的開發(fā)深深的吸引了我,下面來跟我一起領(lǐng)略ReactJs的風(fēng)采吧~~ 文章有點長,耐心讀完,你會有很大收獲哦
    2021-04-04

最新評論