React框架的基本執行原理與元件定義方式

2023-10-21 15:01:11
React框架的基本執行原理
React的本質是內部維護了一套虛擬DOM樹,這個虛擬DOM樹就是一棵js物件樹,它和真實DOM樹是一致的,一一對應的。
當某一個元件的state發生修改時,就會生成一個新的虛擬DOM,讓它和舊的虛擬DOM通過Diff演演算法進行對比,生成一組差異物件。
然後變數差異物件,將修改更新到真實的DOM樹上。

React的三大特性:JSX語法糖,虛擬DOM, Diff演演算法
它們之間的關係可以簡單理解為:
1.JSX語法糖轉換:在webpack進行編譯時,JSX語法糖 被轉換成 React.createElement的React的API呼叫,React.createElement的React的API呼叫會得到一個js物件。
2.生成抽象語法樹:通過將這棵JSX樹中的每個元素 轉換成相應的 React.createElement 的API呼叫,最終得到一棵js物件樹, 這棵js物件樹就是虛擬DOM樹。
3.使用Diff演演算法對這棵樹進行比較運算,得出要更新的虛擬DOM差異物件,然後遍歷這些差異物件,將修改更新到真實的DOM中去。
Diff演演算法對這棵js物件樹的diff分三層:
tree diff :樹層級的對比
component diff :元件層級的對比
element diff: 元素層級的對比
key: key屬性,把頁面上的DOM節點與虛擬DOM節點做一層關聯關係。

 

DOM與虛擬DOM的概念
原來的DOM是瀏覽器中的JS提供的功能,所以我們只能使用瀏覽器提供的API,如:getElementById進行操作DOM物件。
虛擬DOM是程式設計師手動模擬的,類似與瀏覽器中的DOM。

虛擬DOM出現的原因
是為了提升瀏覽器的渲染效能,避免因為某一部分DOM元素的更新,重新整理整棵DOM樹。所以使用js建立一個js物件來模擬一個DOM物件。
一個js物件中包含多個子物件,就構成了一棵虛擬的DOM樹。
通過前後比較2棵虛擬DOM樹的差異,將得到的差異物件進行遍歷批次更新,從而真實的DOM得到了頁面更新。
React內部已經實現了虛擬DOM, 所以通過React框架開發的web頁面,預設就有了這個能力。

JSX語法糖
在React中,不能像Vue中那樣直接寫html元素,要通過react的API建立元素React.createElelement()
React.createElement有三個引數,並返回一個dom物件,也就是js物件
引數一:標籤名字串
引數二:屬性物件
引數三及其更多:子元素

JSX的原理是什麼?
JSX是符合XML規範的JS語法
JSX只是一個語法糖,它內部執行的時候是把類似於HTML這樣的標籤程式碼轉換為React.createElement的形式。
 
需要安裝:npm i babel-preset-react -D
webpack沒法編譯jsx程式碼,它會找到babel進行編譯這個程式碼,babel發現它是react內的語法,就會呼叫babel-preset-react外掛進行解析

babel碰到<>按照html的語意, 使用React.createElement進行解析
碰到{}會按照js進行解析, {}中只能存放一個帶返回值的js語句
/*
React: 建立,修改React元件,管理元件的生命週期
ReactDOM: 操作真實Dom, 將虛擬Dom渲染到真實的Dom之上。
*/
import React from "react";
import ReactDOM from "react-dom";

/*
在React中,不能像Vue中那樣直接寫html元素,要通過react的API建立元素React.createElelement()
React.createElement有三個引數,並返回一個dom物件,也就是js物件
引數一:標籤名字串
引數二:屬性物件
引數三及其更多:子元素
*/

/*
    JSX的原理是什麼?
    JSX是符合XML規範的JS語法
    JSX只是一個語法糖,它內部執行的時候是把類似於HTML這樣的標籤程式碼轉換為React.createElement的形式。

    需要安裝:npm i babel-preset-react -D
    webpack沒法編譯jsx程式碼,它會找到babel進行編譯這個程式碼,babel發現它是react內的語法,就會呼叫babel-preset-react外掛進行解析
*/


/*
    babel碰到<>按照html的語意, 使用React.createElement進行解析
    碰到{}會按照js進行解析, {}中只能存放一個帶返回值的js語句
*/

var list = []
for (let i = 0; i < 10; i++) {
    var p = <p key={i} >這是for迴圈生成的p標籤</p>
    list.push(p)
}

var myTitle = "這是標題的title"
var h2D = <div>
    這是一個jsx的h2標題
    <h1 title={ myTitle }>JSX真好用</h1>
    { list }
    {/* 這是jsx中的註釋 */}
</div>



var divD = React.createElement("div", {title:"這是一個div", id:"rootSub"},"這是一個React建立的div", h2D)

/*
將react元素渲染到頁面對應的位置上。
*/
ReactDOM.render(divD, document.getElementById("root"))

React元件定義方式 

JS中定義物件的方式有2種:
1.使用建構函式
2.使用類

建構函式元件
js檔案內部的建構函式元件
/*
    React中,建構函式就是一個最基本的元件, 使用時把建構函式的名稱當做html標籤名使用。
    React自定義的元件必須是大寫字母開頭,小寫字母編譯器預設是瀏覽器提供的元件。會從瀏覽器中去查詢。
*/
function Hello() {
    return <div>
        這是使用Hello建構函式 建立的基本元件Hello
    </div>
}
jsx檔案內的建構函式元件
import React from "react"

function World(props) {
    return <div>
        接收到的引數:{props.name}
        <hr></hr>
    </div>
}
export default World
可以通過物件引數擴散的方式進行批次傳參
{/* 如果傳遞的引數是個物件,可以進行對物件進行屬性擴散進行批次傳參 */}
<World {...person}></World>
 
類元件
在類中定義的方法和利用建構函式建立物件時將方法放在建構函式protoType中,在記憶體中的位置是一樣的。
// 1.建構函式, 建立物件
function Person(name, age) {
    this.name = name;
    this.age = age;
}
//Person.prototype中定義的方法和屬性是是定義在當前物件的protoType中的(A區域)
Person.prototype.say = function () {
    console.log('hello')
}
Person.prototype.myId = 220

var p1 = new Person('jack', 12);
console.log(p1);



// 2.類,建立物件, 類的本質也是一個構造方法實現的
class Per {
    //構造器, 呼叫new方法建立物件時,會呼叫個constructor
    constructor(name, age){
        this.name = name
        this.age = age
    }
    //es6 定義的方法定義方式,類中的方法也是定義在當前物件的protoType中的(A區域)
    say() {
        console.log('這是Person中的hello方法')
    }

    //class中的static方法和變數,是放在了constructor方法內部的原型物件中(B區域)
    static myId = 230;
    static myIdGet = function () {
        console.log('myIdGet')
    }
}

var p2 = new Per('jack22', 122);
console.log(p2);
類的重要特性:封裝,繼承,多型, 



// 3.類的重要特性:封裝,繼承,多型, 
//繼承:實現功能的複用
class Chinese extends Per {
    constructor(address, sex, name, age){
        super(name, age)
        this.address = address
        this.sex = sex
    }
}

var c1 = new Chinese('北京', '男', 'jack', 12)
console.log(c1);
Chinese.myIdGet();

//多型:父類別中定義一個抽象空方法,不同的子類進行不同的重寫實現
class Animal {
    say() {

    }
}
class Dog extends Animal {
    say(){
        console.log("wang wang");
    }
}
class Cat extends Animal {
    say(){
        console.log("miao miao");
    }
}

 函陣列件與類元件的區別

函陣列件內部沒有state, 只有從外部傳入的props
類元件內部有state和props, 並且通過setState更新資料可以重新整理UI介面
結論:
1.函陣列件是無狀態元件,類元件是有狀態元件
2.類元件是一個元件模板,它有生命週期。函陣列件沒有生命週期

如何選擇使用?
1.如果元件需要儲存私有狀態,並根據不同的狀態執行不同的邏輯,那麼使用類元件。
2.如果元件只用於UI展示,那麼就用函陣列件。
import React from "react";
import ReactDOM from "react-dom";



var h1Dom = React.createElement("h2", {title:"hello, jsx的h1"}, '這是jsx的子元素')

//1.建構函式元件 的引數需要在引數中進行顯示宣告
function Hello(props) {
    return <div>
        Hello 建構函式 元件:{props.name} - {props.age}
    </div>
}

// 2.通過繼承React.Component類,就得到一個類元件模板
// 類元件的引數 內部會將引數自動包裝到props的內部
class Hello2 extends React.Component {
    constructor(props) {
        super(props)
        //在constructor中是不能直接通過this.props獲取引數的,如果想獲取引數,需要顯示的在constructor中新增引數
        console.log(this.props)
        console.log('---------')
        console.log(props)


        //props外部傳參,不能進行修改
        //state:私有狀態變數,呼叫setState修改可以更新UI
        this.state = {
            msg: 'des',
            state: 10
        }
    }

    render() {
        return <div>
            這是一個React.Component的元件:{this.props.name} - {this.props.age}
        </div>
    }
}

/*
函陣列件與類元件的區別
函陣列件內部沒有state, 只有從外部傳入的props
類元件內部有state和props, 並且通過setState更新資料可以重新整理UI介面
結論:
1.函陣列件是無狀態元件,類元件是有狀態元件
2.類元件是一個元件模板,它有生命週期。函陣列件沒有生命週期

如何選擇使用?
1.如果元件需要儲存私有狀態,並根據不同的狀態執行不同的邏輯,那麼使用類元件。
2.如果元件只用於UI展示,那麼就用函陣列件。

*/


ReactDOM.render(<div>
    {h1Dom}
    <Hello name="jack" age={20}></Hello>
    <Hello2 name="jack" age={20}></Hello2>
</div>, document.getElementById("root"))
函陣列件與類元件的使用
1.類元件基本使用
class CommentList extends React.Component {
    constructor(props) {
        super(props)

        this.state = {
            list : [
                {user:'zhang san', content: '呵呵呵'},
                {user:'zhang san2', content: '呵呵,55'},
                {user:'zhang san3', content: '呵呵呵kk'},
                {user:'zhang san4', content: '呵呵呵dfgfd'},
                {user:'zhang san5', content: '呵呵呵fff'},
                {user:'zhang san6', content: '呵呵呵dddd'},
                {user:'zhang san7', content: '呵呵呵3333'},
            ]
        }
    }

    render(){
        return <div>
            {
                this.state.list.map((item,index) => {
                    return <div key={index}>
                        <h3>姓名:{item.user}</h3>
                        <p>內容:{item.content}</p>
                    </div>
                })
            }
        </div>
    }
}


ReactDOM.render(<div>
    <CommentList></CommentList>
</div>, document.getElementById("root"))