chapter2.二、react技術

React

簡介

React是Facebook開發並開源的前端框架。javascript

當時他們的團隊在市面上沒有找到合適的MVC框架,就本身寫了一個Js框架,用來架設Instagram(圖片分享社交網絡)。2013年React開源。html

React解決的是前端MVC框架中的View視圖層的問題。前端

Virtual DOM

DOM(文檔對象模型Document Object Model)java

文檔react

<html>
    <body>
        <h1>Title</h1>
     <p>A <em>word</em></p> </body> </html>

將網頁內全部內容映射到一棵樹型結構的層級對象模型上,瀏覽器提供對DOM的支持,用戶能夠是用腳本調用DOM API來動態的修改DOM結點,從而達到修改網頁的目的,這種修改是瀏覽器中完成,瀏覽器會根據DOM的改變重繪改變的DOM結點部分。算法

修改DOM從新渲染代價過高,前端框架爲了提升效率,儘可能減小DOM的重繪,提出了Virtual DOM,全部的修改都是如今Virtual DOM上完成的,經過比較算法,找出瀏覽器DOM之間的差別,使用這個差別操做DOM,瀏覽器只須要渲染這部分變化就好了。瀏覽器

React實現了DOM Diff算法能夠高效比對Virtual DOM和DOM的差別。前端框架

 支持JSX語法

 JSX是一種JavaScript和XML混寫的語法,是JavaScript的擴展。網絡

React.render(
    <div>
        <div>
            <div>content</div>
        </div>
    </div>,
document.getElementById('example')
);        

測試程序

 替換 /src/index.js 爲下面的代碼框架

import React from "react";
import ReactDom from "react-dom";

class Root extends React.Component{
    render() {
        return <div>Hello World</div>;
    }
}

ReactDom.render(<Root/>,document.getElementById("root"));

保存後就會自動編譯,並從新裝載刷新瀏覽器端頁面

程序解釋

import React from 'react'; 導入react模塊

import ReactDOM from 'react-dom'; 導入react的DOM模塊

class Root extends React.Component 組件類定義,從React.Component類上繼承。這個類生成JSXElement對象即React元素。

render() 渲染函數。返回組件中渲染的內容。注意,只能返回惟一 一個頂級元素回去。

ReactDom.render(<Root/>, document.getElementById('root')); 第一個參數是JSXElement對象,第二個是DOM的Element元素。將React元素添加到DOM的Element元素中並渲染。

還可使用React.createElement建立react元素,第一參數是React組件或者一個HTML的標籤名稱(例如div、span)。

return React.createElement('div', null, 'why are you so cool.');
ReactDom.render(React.createElement(Root), document.getElementById('root'));

改寫的代碼爲

import React from "react";
import ReactDom from "react-dom";

class Root extends React.Component{
    render() {
        // return <div>Hello World</div>;
        return React.createElement('div', null, 'why are you so cool.');
    }
}

// ReactDom.render(<Root/>,document.getElementById("root"));
ReactDom.render(React.createElement(Root),document.getElementById("root"));

使用JSX更簡潔易懂,推薦使用JSX語法。

增長一個子元素

import React from "react";
import ReactDom from "react-dom";

class Sub extends React.Component{
    render() {
        return <div>Sub content</div>
    }
}

class Root extends React.Component{
    render() {
        return (<div>
            <h2>hello, I'm Riper</h2>
            <br />
            <Sub />
        </div>);
    }
}

ReactDom.render(<Root/>,document.getElementById("root"));

注意:

一、React組件的render函數return,只能是一個頂級元素

二、JSX語法是XML,要求全部元素必須閉合,注意 <br /> 不能寫成 <br>

 JSX規範

  • 約定標籤中首字母小寫就是html標記,首字母大寫就是組件
  • 要求嚴格的HTML標記,要求全部標籤都必須閉合。br也應該寫成 <br /> ,/前留一個空格。
  • 單行省略小括號,多行請使用小括號
  • 元素有嵌套,建議多行,注意縮進
  • JSX表達式:表達式使用{}括起來,若是大括號內使用了引號,會當作字符串處理,例如 <div>{'2>1?true:false'}</div> 裏面的表達式成了字符串了

 組件狀態state **

每個React組件都有一個狀態屬性state,它是一個JavaScript對象,能夠爲它定義屬性來保存值。
若是狀態變化了,會觸發UI的從新渲染。使用setState()方法能夠修改state值。
注意:state是每一個組件本身內部使用的,是組件本身的屬性。

依然修改/src/index.js

 

import React from "react"
import ReactDom from "react-dom"

class Root extends React.Component{
    state = {
        p1 : "hello ",
        p2 : "world."
    };
    render () {
        this.state.p1 = "where is "; //不推薦在render中修改state
        // this.setState({p1: "where is"}) // 遞歸了,不能夠對還在更新中的state使用setState
        console.log("!!!!!!!!!!!!!!!!") 
        // setTimeout(() => this.setState({p1:"where is "}),5000) 
        //控制檯會每隔五秒打印一次!!!!!!!!!!!,由於setState會致使render重繪,這裏仍是會循環
        return (
            <div>
                <div>this say {this.state.p1}{this.state.p2}</div>   
            </div>);
    }
}

ReactDom.render(<Root />,document.getElementById("root"));

 

若是將 this.state.p1 = 'where is ' 改成 this.setState({p1:'where is '}); 就會出警告。

可使用延時函數 setTimeout(() => this.setState({ p1: 'where is ' }), 5000); 便可。若是網頁須要每一段時間更新一次,也可使用該方法。

import React from "react"
import ReactDom from "react-dom"

class Root extends React.Component{
    state = {
        count : 0
    };

    handleClick(event) {
        console.log(event)
        console.log(event.target);
        this.setState({'count': ++this.state.count})
    };
    render () {
        return (
            <div onClick={this.handleClick.bind(this)}> // 綁定,不然會有傳遞的this的坑
                {this.state.count}
            </div>);
    }
}

ReactDom.render(<Root />,document.getElementById("root"));

 

 複雜狀態

將腳本放在網頁中,傳統網頁實現

<html>

<head>
    <script type="text/javascript">
        function getEventTrigger(event) {
            x = event.target; // 從事件中獲取元素
            alert("觸發的元素的id是:" + x.id);
        }
    </script>
</head>

<body>
    <div id="t1" onmousedown="getEventTrigger(event)">
        點擊這句話,會觸發一個事件,並彈出一個警示框
    </div>
</body>

</html>

div的id是t1,鼠標按下事件捆綁了一個函數,只要鼠標按下就會觸發調用getEventTrigger函數,瀏覽器會送給它一個參數event。event是事件對象,當事件觸發時,event包含觸發這個事件的對象。

HTML DOM的JavaScript事件

屬性     此事件發生在什麼時候
onabort     圖像的加載被中斷
onblur     元素失去焦點
onchange     域的內容被改變
onclick     當用戶點擊某個對象時調用的事件句柄
ondblclick     當用戶雙擊某個對象時調用的事件句柄
onerror     在加載文檔或圖像時發生錯誤
onfocus     元素得到焦點
onkeydown     某個鍵盤按鍵被按下
onkeypress     某個鍵盤按鍵被按下並鬆開
onkeyup     某個鍵盤按鍵被鬆開
onload     一張頁面或一幅圖像完成加載
onmousedown     鼠標按鈕被按下
onmousemove     鼠標被移動
onmouseout     鼠標從某元素移開
onmouseover     鼠標移到某元素之上
onmouseup     鼠標按鍵被鬆開
onreset     重置按鈕被點擊
onresize     窗口或框架被從新調整大小
onselect     文本被選中
onsubmit     確認按鈕被點擊
onunload     用戶退出頁面

使用React實現上面的傳統的HTML

import React from 'react';
import ReactDom from 'react-dom';

class Toggle extends React.Component {
    state = {flag : true};
    handleClick(event) {
        console.log(event.targer === this, event.target.id);
        console.log(this, this.state);
        this.setState({flag: !this.state.flag})
    };
    render() {
        return <div id="t1" onClick={this.handleClick.bind(this)}>
            點擊按鈕會發生事件 {this.state.flag.toString()}
        </div>
    }
}

class Root extends React.Component {
    state = {p1: "Are you kidding me"}
    render () {
        setTimeout(() => this.setState({p1: "Yes!"}),5000)
        return (
            <div>
                <div>{this.state.p1}</div>
                <br />
                <Toggle />
            </div>
        )
    }
}

ReactDom.render(<Root />, document.getElementById('root'));

分析:

Toggle類,有本身的state屬性

當render完成後,網頁上有一個div標籤,div標籤對象捆邦了一個click事件,div標籤內有文本內容。

點擊左鍵後,觸發了click方法關聯的handleClick函數,改變了狀態值。

狀態的改變會致使render的重繪。

組件的state變化只會致使本身的render方法重繪。

注意:

{this.handleClick.bind(this)},不能外加引號

this.handleClick.bind(this) 必定要綁定this,不然當觸發捆綁的函數時,this是函數執行的上下文決定的,this已經不是觸發事件的對象了。

console.log(event.target.id),取回的產生事件的對象的id,可是這不是咱們封裝的組件對象。因此,console.log(event.target===this)是false。因此這裏必定要用this,而這個this是經過綁定來的。

React中的事件

使用小駝峯命名

使用JSX表達式,表達式中指定事件處理函數

不能使用return false,若是要阻止事件默認行爲,使用event.preventDefault()

屬性props **

props就是組件的屬性properties。

把React組件當作標籤使用,能夠爲其增長屬性,以下

<Toggle name="school" parent={this} />

爲上面的Toggle元素增長屬性:
一、 name = "school" ,這個屬性會做爲一個單一的對象傳遞給組件,加入到組件的props屬性中
二、 parent = {this} ,注意這個this是在Root元素中,指的是Root組件自己
三、在Root中爲使用JSX語法爲Toggle增長子元素,這些子元素也會被加入Toggle組件的props.children中

 

import React from 'react';
import ReactDom from 'react-dom';
class Toggle extends React.Component {
    state = { flag: true }; // 類中定義state
    handleClick(event) {
        // event.preventDefult()
        console.log(event.target.id);
        console.log(event.target === this);
        console.log(this);
        console.log(this.state);
        this.setState({ flag: !this.state.flag });
    }
    render() {/* 注意必定要綁定this onClick寫成小駝峯 */
        return <div id="t1" onClick={this.handleClick.bind(this)}>
        點擊這句話,會觸發一個事件。{this.state.flag.toString()}<br />
        {this.props.name} : {this.props.parent.state.p1 + this.props.parent.state.p2}<br />
        {this.props.children}
        </div>;
    }
}
class Root extends React.Component {
    // 定義一個對象
    state = { p1: '會發生', p2: '變化' }; // 構造函數中定義state
    // handleClick(event) {
    //     console.log(this.state)
    // };
    
    render() {
        // setTimeout(() => this.setState({ p1: '已經' }), 5000);
        setInterval(() => this.setState({ p1: '已經' }), 5000);
        return (
            <div>
                <div>這裏5秒 {this.state.p1}{this.state.p2}</div>
                <br />
                <Toggle name="teststr" parent={this}>
                <hr />
                <span>我是Toggle元素的子元素</span>
                </Toggle>
            </div>);
    }
}
ReactDom.render(<Root />, document.getElementById('root'));

 

有了props,能夠在子組件中控制父組件中的state。

嘗試修改props中的屬性值,會拋出 TypeError: Cannot assign to read only property 'name' of object '#<Object>' 異常。

應該說,state是私有private的屬於組件本身的屬性,組件外沒法直接訪問。能夠修改state,可是建議使用setState方法。

props是公有public屬性,組件外也能夠訪問,但只讀。

 在修改state屬性時,注意

react在處理事件時,會在傳遞給子組件後,再傳遞給父組件。好比子組件和父組件都綁了onclick事件。通常不會同時接受。

構造器constructor

使用ES6的構造器,要提供一個參數props,並把這個參數使用super傳遞給父類

import React from 'react';
import ReactDom from 'react-dom';
class Toggle extends React.Component {
    constructor(prpos){
        super(prpos)
    this.state = { flag: true }; // 類中定義state
    }
    handleClick(event) {
        this.setState({ flag: !this.state.flag });
        console.log(this.props.parent);
        let r = this.props.parent;
        r.setState({p1:"hello !!!"})
    }
    render() {/* 注意必定要綁定this onClick寫成小駝峯 */
        return <div style={{padding:"20px"}}>
        <span id="t1" onClick={this.handleClick.bind(this)} >點擊這句話,會觸發一個事件。{this.state.flag.toString()}</span>
        <br />
        {this.props.name} : {this.props.parent.state.p1 + this.props.parent.state.p2}<br />
        {this.props.children}
        </div>;
    }
}
class Root extends React.Component {
    constructor(prpos){
        super(prpos)
    this.state = { p1: '會發生', p2: '變化' }; // 構造函數中定義state
    }
    
    render() {
        // setTimeout(() => this.setState({ p1: '已經' }), 5000);
        setInterval(() => this.setState({ p1: '已經' }), 5000);
        return (
            <div>
                <div>這裏5秒 {this.state.p1}{this.state.p2}</div>
                <br />
                <Toggle name="teststr" parent={this}>
                <hr />
                <span style={{backgroundColor:"white"}}>我是Toggle元素的子元素</span>
                </Toggle>
            </div>);
    }
}
ReactDom.render(<Root />, document.getElementById('root'));

 

組件的生命週期 *

組件的生命週期可分紅三個狀態:

  • Mounting:已插入真實 DOM
  • Updating:正在被從新渲染
  • Unmounting:已移出真實 DOM

組件的生命週期的方法

裝載時觸發

componentWillMount: 渲染前調用,只在裝載前調用一次,在客戶端和服務端都執行

componentDidMount: 在第一次渲染後執行,只在客戶端執行,以後組件已經生成了對應的DOM結構,能夠經過this.getDOMNode()來進行訪問。 若是你想和其餘JavaScript框架一塊兒使用,能夠在這個方法中調用setTimeout, setInterval或者發送AJAX請求等操做(防止異部操做阻塞UI)。只在裝載完成後調用一次,在render以後

更新時觸發,不會再首次render時調用

componentWillReceiveProps(nextProps) 在組件接收到一個新的prop時被調用。這個方法在初始化render時不會被調用

shouldComponentUpdate(nextProps, nextState) 返回一個布爾值。在組件接收到新的props或者state時被調用。在初始化時或者使用forceUpdate時不被調用。

能夠在你確認不須要更新組件時使用。

若是設置爲false,就是不容許更新組件,那麼componentWillUpdate、componentDidUpdate不會執行。

componentWillUpdate(nextProps, nextState) 在組件接收到新的props或者state但尚未render時被調用。在初始化時不會被調用。

componentDidUpdate(prevProps, prevState) 在組件完成更新後當即調用。在初始化時不會被調用。

卸載組件觸發

componentWillUnmount在組件從 DOM 中移除的時候馬上被調用。

 

 

 

由圖可知
constructor構造器是最先執行的函數。

觸發 更新生命週期函數 ,須要更新state或props。

所以,從新編寫/src/index.js。

構造兩個組件,在子組件Sub中,加入全部生命週期函數。

下面的例子添加是裝載、卸載組件的生命週期函數

 

import React from 'react';
import ReactDom from 'react-dom';
class Sub extends React.Component {
    constructor(props) {
        console.log('Sub constructor')
        super(props); // 調用父類構造器
        this.state = { count: 0 };
    }
    handleClick(event) {
        this.setState({ count: this.state.count + 1 });
    }
    render() {
        console.log('Sub render');
        return (<div style={{ height: 200 + 'px', color: 'red', backgroundColor: '#f0f0f0' }}>
            <a id="sub" onClick={this.handleClick.bind(this)}>
                Sub's count = {this.state.count}
            </a>
        </div>);
    }
    componentWillMount() {
        // constructor以後,第一次render以前
        console.log('Sub componentWillMount');
    }
    componentDidMount() {
        // 第一次render以後
        console.log('Sub componentDidMount');
    }
    componentWillUnmount() {
        // 清理工做
        console.log('Sub componentWillUnmount');
    }
    componentWillReceiveProps(nextProps) {
        // props變動時,接到新props了,交給shouldComponentUpdate。
        // props組件內只讀,只能從外部改變
        console.log(this.props);
        console.log(nextProps);
        console.log('Sub componentWillReceiveProps', this.state.count);
    }
    shouldComponentUpdate(nextProps, nextState) {
        // 是否組件更新,props或state方式改變時,返回布爾值,true纔會更新
        console.log('Sub shouldComponentUpdate', this.state.count, nextState);
        return true; // return false將攔截更新
    }
    componentWillUpdate(nextProps, nextState) {
        // 贊成更新後,真正更新前,以後調用render
        console.log('Sub componentWillUpdate', this.state.count, nextState);
    }
    componentDidUpdate(prevProps, prevState) {
        // 贊成更新後,真正更新後,在render以後調用
        console.log('Sub componentDidUpdate', this.state.count, prevState);
    }
}
class Root extends React.Component {
    constructor(props) {
        console.log('Root Constructor')
        super(props); // 調用父類構造器
        // 定義一個對象
        this.state = { flag: true, name: 'root' };
    }
    handleClick(event) {
        this.setState({
            flag: !this.state.flag,
            name: this.state.flag ? this.state.name.toLowerCase() : this.state.name.toUpperCase()
        });
    }
    render() {
        return (
            <div id="root" onClick={this.handleClick.bind(this)}>
                My Name is {this.state.name}
                <hr />
                <Sub /> {/*父組件的render,會引發下一級組件的更新流程,致使props從新發送,即便子組件props沒有
改變過*/}
            </div>);
    }
}
ReactDom.render(<Root />, document.getElementById('root'));

 

componentWillMount 第一次裝載,在首次render以前。例如控制state、props

componentDidMount 第一次裝載結束,在首次render以後。例如控制state、props

componentWillReceiveProps 在組件內部,props是隻讀不可變的,可是這個函數能夠接收到新的props,能夠對props作一些處理,this.props = {name:'roooooot'};這就是偷樑換柱。componentWillReceiveProps觸發,也會走shouldComponentUpdate。

shouldComponentUpdate 判斷是否須要組件更新,就是是否render,精確的控制渲染,提升性能。

componentWillUpdate 在除了首次render外,每次render前執行,componentDidUpdate在render以後調用。

不過,大多數時候,用不上這些函數,這些鉤子函數是爲了精確的控制。shouldComponentUpdate能夠在程序中攔截,控制屬性的值。

無狀態組件

import React from "react";
import ReactDom from "react-dom"

let Root = props => <div>{props.schoolName}</div>

ReactDom.render(<Root schoolName="magedu" />, document.getElementById('root'));

無狀態組件,也叫函數式組件

開發中,不少狀況下,組件其實很簡單,不須要state狀態,也不須要使用生命週期函數。無狀態組件很好的知足了須要。

無狀態組件函數應該提供一個參數props,返回一個React元素。

無狀態組件函數自己就是render函數。

相關文章
相關標籤/搜索