React框架的基本運行原理與組件定義方式

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"))

 

 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章