Web開發(fā) | React 介紹 & 使用總結(jié) (八)

React簡介

React 起源于Facebook的內(nèi)部項目,因為該公司對市場上所有JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設(shè) Instagram 的網(wǎng)站。做出來以后,發(fā)現(xiàn)這套東西很好用,就在2013年5月開源了。

  • 由于 React 的設(shè)計思想極其獨特,屬于革命性創(chuàng)新,性能出眾,代碼邏輯卻非常簡單。所以,越來越多的人開始關(guān)注和使用,認為它可能是將來 Web 開發(fā)的主流工具。

React 中文官網(wǎng)

React 核心的概念

虛擬DOM(Virtual Document Object Model)

  • DOM的本質(zhì):就是用JS表示的UI元素
  • 虛擬DOM:并不是由瀏覽器提供的,而是我們程序員手動模擬實現(xiàn)的,類似于瀏覽器中的DOM,有著本質(zhì)的區(qū)別
// 導(dǎo)入模塊
import React from 'react'
import ReactDOM from 'react-dom'

// 創(chuàng)建虛擬Dom元素
var myDiv = React.createElement('div', {
    title: 'this is a title',
    id: 'mydiv'
}, '我是一個 div')
ReactDOM.render(myDiv, document.getElementById('app'))

Diff算法

  • tree diff:新舊DOM樹,逐層對比的方式,就叫做 tree diff,每當我們從前到后,把所有層的節(jié)點對比完后,必然能夠找到那些 需要被更新的元素;
  • component diff:在對比每一層的時候,組件之間的對比,叫做 component diff;當對比組件的時候,如果兩個組件的類型相同,則暫時認為這個組件不需要被更新,如果組件的類型不同,則立即將舊組件移除,新建一個組件,替換到被移除的位置;
  • element diff:在組件中,每個元素之間也要進行對比,那么,元素級別的對比,叫做 element diff;
  • key:key這個屬性,可以把 頁面上的 DOM節(jié)點 和 虛擬DOM中的對象,做一層關(guān)聯(lián)關(guān)系;
image.png

JSX語法

  1. 如要要使用 JSX 語法,必須先運行 npm i babel-preset-react -D,然后再 .babelrc 中添加 語法配置;
  2. JSX語法的本質(zhì):還是以 React.createElement 的形式來實現(xiàn)的,并沒有直接把 用戶寫的 HTML代碼,渲染到頁面上;
  3. 如果要在 JSX 語法內(nèi)部,書寫 JS 代碼了,那么,所有的JS代碼,必須寫到 {} 內(nèi)部;
  4. 當 編譯引擎,在編譯JSX代碼的時候,如果遇到了<那么就把它當作 HTML代碼去編譯,如果遇到了 {} 就把 花括號內(nèi)部的代碼當作 普通JS代碼去編譯;
  5. 在{}內(nèi)部,可以寫任何符合JS規(guī)范的代碼;
  6. 在JSX中,如果要為元素添加class屬性了,那么,必須寫成className,因為 class在ES6中是一個關(guān)鍵字;和class類似,label標簽的 for 屬性需要替換為 htmlFor.
  7. 在JSX創(chuàng)建DOM的時候,所有的節(jié)點,必須有唯一的根元素進行包裹;
  8. 如果要寫注釋了,注釋必須放到 {} 內(nèi)部
// JSX語法創(chuàng)建
var mytitle = 'this is a title'
var name = 'Alex'
var myDiv = <div>
    <h1>我是一個標題,title 屬性: {mytitle} </h1>
    <p>我是 P 標簽</p>
    <p>我的名字是 {name}</p>
</div>

ReactDOM.render(myDiv, document.getElementById('app'))

React 項目搭建

  1. 運行 npm i react react-dom -S 安裝包

  2. 在項目中導(dǎo)入兩個相關(guān)的包

    • 在 React 學(xué)習(xí)中,需要安裝 兩個包 react react-dom
    • react 這個包,是專門用來創(chuàng)建React組件、組件生命周期等這些東西的;
    • react-dom 里面主要封裝了和 DOM 操作相關(guān)的包,比如,要把 組件渲染到頁面上
    import React from 'react'
    import ReactDOM from 'react-dom' 
    
  3. 使用JS的創(chuàng)建虛擬DOM節(jié)點

// 2. 在 react 中,如要要創(chuàng)建 DOM 元素了,只能使用 React 提供的 JS API 來創(chuàng)建,不能【直接】像 Vue 中那樣,手寫 HTML 元素
// React.createElement() 方法,用于創(chuàng)建 虛擬DOM 對象,它接收 3個及以上的參數(shù)
// 參數(shù)1: 是個字符串類型的參數(shù),表示要創(chuàng)建的元素類型
// 參數(shù)2: 是一個屬性對象,表示 創(chuàng)建的這個元素上,有哪些屬性
// 參數(shù)3: 從第三個參數(shù)的位置開始,后面可以放好多的虛擬DOM對象,這寫參數(shù),表示當前元素的子節(jié)點
var myH1 = React.createElement('h1', null, '這是一個大大的H1')
var myDiv = React.createElement('div', { title: 'this is a div', id: 'mydiv' }, '這是一個div', myH1)
  1. 使用 ReactDOM 把元素渲染到頁面指定的容器中:
// ReactDOM.render('要渲染的虛擬DOM元素', '要渲染到頁面上的哪個位置中')
// 注意: ReactDOM.render() 方法的第二個參數(shù),和vue不一樣,不接受 "#app" 這樣的字符串,而是需要傳遞一個 原生的 DOM 對象
ReactDOM.render(myDiv, document.getElementById('app'))


React-組件

創(chuàng)建方式一:

函數(shù)組件

// 組件創(chuàng)建一
var person = {name: 'alex',age: 20}

function Hello(props) {
    return <div>
        <h1>我是一個組件,我通過function 構(gòu)造器來創(chuàng)建</h1>
        <p>我的姓名是 {props.name},  我的年齡: {props.age}</p>
    </div>
}
// 擴散傳值
ReactDOM.render(<Hello {...person}></Hello>, document.getElementById('app'))

通過props 傳值

function Welcome (props) {
    return <div>Hello, {props.name}</div>
}
ReactDOM.render(<Welcome name = "alex"></Welcome>, document.getElementById('app'))

創(chuàng)建方式二:

class 組件

var person = { name: 'alex', age: 20 }
class Hello extends React.Component{
    render() {
        return <div>
            <h1>我是一個組件,我通過function 構(gòu)造器來創(chuàng)建</h1>
            <p>我的姓名是 {this.props.name},  我的年齡: {this.props.age}</p>
        </div>
    }
}
var myDiv = <div>
    <Hello {...person}></Hello>
</div>
ReactDOM.render(myDiv, document.getElementById('app'))

組合組件

// 組件組合
function Welcome(props) {
    return <div>Hello, {props.name}</div>
}

function App() {
    return (
        <div>
            <Welcome name='alex'></Welcome>
            <Welcome name='demo'></Welcome>
            <Welcome name='ground'></Welcome>

            <div className='box'></div>
        </div>
    )
}
ReactDOM.render(<App />, document.getElementById('app'))

兩種創(chuàng)建組件方式的對比

  1. 用構(gòu)造函數(shù)創(chuàng)建出來的組件:專業(yè)的名字叫做“無狀態(tài)組件”
  2. 用class關(guān)鍵字創(chuàng)建出來的組件:專業(yè)的名字叫做“有狀態(tài)組件”

用構(gòu)造函數(shù)創(chuàng)建出來的組件,和用class創(chuàng)建出來的組件,這兩種不同的組件之間的本質(zhì)區(qū)別就是:有無state屬性!??!
有狀態(tài)組件和無狀態(tài)組件之間的本質(zhì)區(qū)別就是:有無state屬性!

組件生命周期&State

在組件創(chuàng)建、到加載到頁面上運行、以及組件被銷毀的過程中,總是伴隨著各種各樣的事件,這些在組件特定時期,觸發(fā)的事件,統(tǒng)稱為 組件的生命周期;

組件生命周期分為三部分:

1.組件創(chuàng)建階段

組件創(chuàng)建階段的生命周期函數(shù),有一個顯著的特點:創(chuàng)建階段的生命周期函數(shù),在組件的一輩子中,只執(zhí)行一次;

  • componentWillMount: 組件將要被掛載,此時還沒有開始渲染虛擬DOM
  • render:第一次開始渲染真正的虛擬DOM,當render執(zhí)行完,內(nèi)存中就有了完整的虛擬DOM了
  • componentDidMount: 組件完成了掛載,此時,組件已經(jīng)顯示到了頁面上,當這個方法執(zhí)行完,組件就進入都了 運行中 的狀態(tài)

2.組件運行狀態(tài)

組件運行階段:也有一個顯著的特點,根據(jù)組件的state和props的改變,有選擇性的觸發(fā)0次或多次;

  • componentWillReceiveProps: 組件將要接收新屬性,此時,只要這個方法被觸發(fā),就證明父組件為當前子組件傳遞了新的屬性值;
  • shouldComponentUpdate: 組件是否需要被更新,此時,組件尚未被更新,但是,state 和 props 肯定是最新的
  • componentWillUpdate: 組件將要被更新,此時,尚未開始更新,內(nèi)存中的虛擬DOM樹還是舊的
  • render: 此時,又要重新根據(jù)最新的 state 和 props 重新渲染一棵內(nèi)存中的 虛擬DOM樹,當 render 調(diào)用完畢,內(nèi)存中的舊DOM樹,已經(jīng)被新DOM樹替換了!此時頁面還是舊的
  • componentDidUpdate: 此時,頁面又被重新渲染了,state 和 虛擬DOM 和 頁面已經(jīng)完全保持同步

3.組件銷毀階段

也有一個顯著的特點,一輩子只執(zhí)行一次;

  • componentWillUnmount: 組件將要被卸載,此時組件還可以正常使用;
image.png

defaultProps

在組件創(chuàng)建之前,會先初始化默認的props屬性,這是全局調(diào)用一次,嚴格地來說,這不是組件的生命周期的一部分。在組件被創(chuàng)建并加載候,首先調(diào)用 constructor 構(gòu)造器中的 this.state = {},來初始化組件的狀態(tài)。

React生命周期的回調(diào)函數(shù)總結(jié)成表格如下:


image.png

組件生命周期的執(zhí)行順序:

  • Mounting:
  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()
  • Updating:
  • componentWillReceiveProps(nextProps)
  • shouldComponentUpdate(nextProps, nextState)
  • componentWillUpdate(nextProps, nextState)
  • render()
  • componentDidUpdate(prevProps, prevState)
  • Unmounting:
  • componentWillUnmount()

綁定this并傳參的三種方式

1.在事件中綁定this并傳參:

<input type="button" value="--綁定傳參數(shù)--" onClick={this.handleMsg.bind(this, 10, 20)} />
handleMsg (num, num2) {
        console.log(num, num2);
        this.setState({
            msg: num
        })
 }

2.在構(gòu)造函數(shù)中綁定this并傳參:

// 修改構(gòu)造函數(shù)中的代碼:
this.handleMsg2 = this.handleMsg2.bind(this, '', '');
<input type="button" value="在構(gòu)造函數(shù)中綁定this并傳參" onClick={this.handleMsg2} />
// 在構(gòu)造函數(shù)中綁定this并傳參
handleMsg2(arg1, arg2) {
    this.setState({
        msg: '在構(gòu)造函數(shù)中綁定this并傳參:' + arg1 + arg2
    });
}

3.用箭頭函數(shù)綁定this并傳參

<input type="button" value="用箭頭函數(shù)綁定this并傳參" onClick={() => { this.handleMsg3('', '') }} />
// 用箭頭函數(shù)綁定this并傳參
    handleMsg3(arg1, arg2) {
        this.setState({
            msg: '用箭頭函數(shù)綁定this并傳參:' +    arg1 + arg2
        });
}

綁定文本框與state中的值

在React.js中,默認沒有提供雙向數(shù)據(jù)綁定這一功能,默認的,只能把state之上的數(shù)據(jù)同步到界面的控件上,但是不能默認實現(xiàn)把界面上數(shù)據(jù)的改變,同步到state之上,需要程序員手動調(diào)用相關(guān)的事件,來進行逆向的數(shù)據(jù)傳輸!

綁定文本框和state的值:

{/*只要將value屬性,和state上的狀態(tài)進行綁定,那么,這個表單元素就變成了受控表單元素,這時候,如果沒有調(diào)用相關(guān)的事件,是無法手動修改表單元素中的值的*/}
<input style={{ width: '100%' }} ref="txt" type="text" value={this.state.msg} onChange={this.handleTextChange} />
// 這是文本框內(nèi)容改變時候的處理函數(shù)
handleTextChange = () => {
    this.setState({
        msg: this.refs.txt.value
    });
}

注意setState的一個問題:

// 保存最新的state狀態(tài)值,在保存的時候,是異步地進行保存的,所以,如果想要獲取最新的,剛剛保存的那個狀態(tài),需要通過回掉函數(shù)的形式去獲取最新state
this.setState({
msg: this.refs.txt.value
// msg: e.target.value
}, function () {
// 獲取最新的state狀態(tài)值
console.log(this.state.msg);
});

context特性

context 可以將父組件的值傳遞給子組件來進行使用

// getChildContextTypes
// 1. 在 父組件中,定義一個 function,這個function 有個固定的名稱,叫做 getChildContext ,內(nèi)部,必須 返回一個 對象,這個對象,就是要共享給 所有子孫自建的  數(shù)據(jù)
getChildContext() {
    return {
        color: this.state.color
    }
}

// 2. 使用 屬性校驗,規(guī)定一下傳遞給子組件的 數(shù)據(jù)類型, 需要定義 一個 靜態(tài)的(static) childContextTypes(固定名稱,不要改)
static childContextTypes = {
    color: ReactTypes.string // 規(guī)定了 傳遞給子組件的 數(shù)據(jù)類型
}

// 3. 上來之后,先來個屬性校驗,去校驗一下父組件傳遞過來的 參數(shù)類型
static contextTypes = {
    color: ReactTypes.string // // 這里,如果子組件,想要使用 父組件通過 context 共享的數(shù)據(jù),那么在使用之前,一定要先 做一下數(shù)據(jù)類型校驗
}

記住一串單詞組合getChildContextTypes
前3個、后3個、后兩個
一個方法、兩個靜態(tài)屬性

Ant Design

antd 是基于 Ant Design 設(shè)計體系的 React UI 組件庫,主要用于研發(fā)企業(yè)級中后臺產(chǎn)品。

官方地址

路由

1.安裝: npm add react-router-dom
2.導(dǎo)入: import { HashRouter, Route, Link } from 'react-router-dom'

  • HashRouter 表示一個路由的根容器,將來,所有的路由相關(guān)的東西,都要包裹在 HashRouter 里面,而且,一個網(wǎng)站中,只需要使用一次 HashRouter 就好了;
  • Route 表示一個路由規(guī)則, 在 Route 上,有兩個比較重要的屬性, path component
  • Link 表示一個路由的鏈接 ,就好比 vue 中的 <router-link to=""></router-link>

3.設(shè)置HashRouter根路由
4.使用Linke創(chuàng)建路由連接
5.Route創(chuàng)建路由規(guī)則

// 路由學(xué)習(xí)
export default class App extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
        }
    }
    render() {
        // 當 使用 HashRouter 把 App 根組件的元素包裹起來之后,網(wǎng)站就已經(jīng)啟用路由了
        return <HashRouter>
            <div>
                <h1>這是網(wǎng)站的APP根組件</h1>
                <hr />

                <Link to="/home">首頁</Link> &nbsp;&nbsp;
                <Link to="/movie">電影</Link> &nbsp;&nbsp;
                <Link to="/about">關(guān)于</Link>

                <hr />

                {/* Route 創(chuàng)建的標簽,就是路由規(guī)則,其中 path 表示要匹配的路由,component 表示要展示的組件 */}
                {/* Route 具有兩種身份:1. 它是一個路由匹配規(guī)則; 2. 它是 一個占位符,表示將來匹配到的組件都放到這個位置, 如果想讓路由規(guī)則,進行精確匹配,可以為 Route,添加 exact 屬性,表示啟用精確匹配模式 */}
                <Route path="/home" component={Home}></Route>
                <Route path="/movie" component={Movie}></Route>
                <Route path="/about" component={About}></Route>

            </div>

        </HashRouter>
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容