React 實踐(一)

參考:《React 入門實例教程--阮一峰》、《React 學習教程--眾成翻譯》璃俗。React 框架入門學習摘錄姻报。

搭建開發(fā)環(huán)境

React 的學習曲線相當陡峭站欺,其實是入門階段里面也是各種坑蜻懦。
本篇開頭從搭建學習用的開發(fā)環(huán)境入手甜癞。React 很多時候使用 JSX 編寫。JSX是一門混合編寫 JS 和 HTML 的 JS 語法糖宛乃。所以悠咱,首先我們必須編譯 JSX 為瀏覽器能夠識別的 JS。在學習之初烤惊,我是在頁面中直接引用 React 的關鍵庫和編譯用的 babel 庫文件乔煞。

<script src="./libs/react.min.js"></script>
<script src="./libs/react-dom.min.js"></script>
<script src="./libs/browser.min.js"></script>

前面兩個文件可以在官網中下載吁朦,也可以使用 npm 下載柒室。建議使用 npm 下載,以方便之后結合 webpack 逗宜、gulp 工具來處理 JSX雄右。

cnpm install --save-dev react react-dom

注意空骚,最后的一個文件是 babel 提供的在瀏覽器環(huán)境使用的 babel 工具。babel 既能將 ES6 轉換為 ES5,同時也支持編譯 JSX擂仍。這個文件我是從 browser.min.js下載再拷貝到本地(實踐證明囤屹,從npm下載的 babel-core 里不包含這個文件)。
最后貼出這里的 HTML 模板:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>React Tutorial</title>
    <script src="./libs/react.min.js"></script>
    <script src="./libs/react-dom.min.js"></script>
    <script src="./libs/browser.min.js"></script>
</head>

<body>
    <div id="content"></div>
    <script type="text/babel">
        // JSX
    </script>
</body>

</html>

體驗 JSX 語法

HTMl 語言直接寫在 JavaScript 中逢渔,不加任何引號肋坚,就是 JSX 語法。JSX 的另一個基本語法規(guī)則:遇到 HTML 標簽的(以 <></> 標簽對包裹)肃廓,就用 HTML 規(guī)則解析智厌;遇到代碼塊(以 {} 包裹),就用 JavaScript 解析盲赊。如以下代碼(如無特殊說明铣鹏,之后的代碼塊都是寫在<scirpt type="text/bable"></script>中的):

var names = ['Alice', 'Emily', 'Kate'];

ReactDOM.render(
  <div>
  {
    names.map(function (name) {
      return <div>Hello, {name}!</div>
    })
  }
  </div>,
  document.getElementById('example')
))

簡單搭建學習用的 gulp 工具箱

現(xiàn)在只使用 gulp 工具和 browser-sync 來實現(xiàn)文件的熱加載。

cnpm install --save-dev gulp browser-sync

安裝完成后哀蘑,在項目目錄下面添加 gulpfile.js 文件诚卸。里面的代碼如下:

var gulp = require('gulp');
var browserSync = require('browser-sync').create();

gulp.task('server', function(){
    browserSync.init({
        server: {
            baseDir: './src'
        }
    });
    gulp.watch('./src/*.html', function(file){
        console.log(file.path + 'is changed __');
        browserSync.reload(); // 一旦監(jiān)視文件修改,就刷新瀏覽器
    })
});

組件

React 的組件是網頁或 spa 里完成最小功能的一部分代碼绘迁,這部分代碼會包含該組件的基本功能合溺,視覺樣式和交互能力。使用 React 開發(fā)缀台,首先就需要將產品切割成一個又一個組件辫愉,然后,將它們拼裝成產品将硝。下面的代碼定義了一個 HelloMessage 組件恭朗。

var HelloMessage = React.creatClass({
    render: function(){
        return <h1>Hello,{this.props.name}</h1>
    }
});

接下來看怎么使用這個組件:

ReactDOM.render(
    <HelloMessage name="John" />, document.getElementById("content")
);

然后在 power shell(window 10 ) 運行 gulp server。在打開的網頁中應該顯示 “Hello,John”依疼。
注意痰腮,組件類的命名使用雙駝峰方式,并且每一個組件類只能有一個頂層的標簽律罢。

React 創(chuàng)建組件的三種方法

stateless function VS creatClass VS ES6 Class
出于不同的原因膀值,React 先后出現(xiàn)了三種定義 react 組件的方式。分別是:

  1. 函數(shù)式定義的無狀態(tài)組件(stateless function)
  2. es5 原生的 React.creatClass 定義的組件
  3. es6 形式的 extends React.Componet 定義的組件

1. 無狀態(tài)函數(shù)式組件

無狀態(tài)函數(shù)式組件式為了創(chuàng)建純展示組件误辑,這類組件只負責根據(jù)傳入的 props 來展示沧踏,不涉及到要 state 的操作。無狀態(tài)函數(shù)式組件表現(xiàn)為一個只帶有 render 方法的組件類巾钉,通過函數(shù)形式或 ES6 arrow function 的形式創(chuàng)建翘狱。代碼如下:

function HelloComponent(props, context){
    return <div>Hello, {props.name}</div>
}

ReactDOM.render(<HelloComponent name="John") />, 
document.getElementById('content'));

細節(jié)待續(xù)……

2.React.creatClass 定義的組件

React.createClass 是 react 剛開始推薦的創(chuàng)建組件的方式,也是 es5 原生的 javascript 實現(xiàn) React 組件砰苍,代碼如下:

var InputControl = React.createClass({
    propTypes: {  // 定義傳入 props 中的屬性各種類型
        initialValue: React.PropTypes.string
    },
    defaultPorps: {  // 組件默認的 props 對象
        initialValue: ''
    },
    // 設置 initial state
    getInitialState: function() { // 組件相關狀態(tài)對象
        return {
            text: this.props.initialValue || 'placeholder'
        };
    },
    handleChange: function () {
        this.setState({
            text: event.target.value
        });
    },
    render: function() {
        return (
            <div>
                Type something:
                <input onChange={this.handleChange} value= {this.state.text} />
                <p>{this.state.text}</p>
            </div>
        );
    }
});
ReactDOM.render(<InputControl />, document.getElementById('content'));

該方式創(chuàng)建的組件會被實例化潦匈,可以訪問組件的生命周期方法阱高。

3. ES6 形式的創(chuàng)建組件

代碼如下:

class InputControl extends React.Component {
    constructor(props) {
        super(props);
        // 設置 intitial state
        this.state = {
            text: props.initialValue || 'placeholder'
        };

        // ES6 類中函數(shù)必須手動綁定
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(event) {
        this.setState({
            text: event.target.value
        });
    }

    render() {
        return (
            <div>Type something:
                <input onChange={this.handleChange} value={this.state.text}/>
                <p>{this.state.text}</p>
            </div>
        );
    }
}
InputControl.propTypes = {
    initialValue: React.PropTypes.string
};
InputControl.defaultProps = {
    initialValue: ''
};

//這里使用組件的時候,不需要 new 實例化茬缩。
ReactDOM.render(<InputControl/>, document.getElementById('content'));

這種方式和之前 ES5 的差別比較大赤惊,也是目前 React 比較推薦的寫法。delay……

組件的生命周期

一個組件就是一個狀態(tài)機:對于特定的輸入凰锡,它總會返回一致的輸出未舟。React 為組件提供了生命周期鉤子函數(shù)去響應不同的時期——創(chuàng)建、存在期及銷毀掂为。

  • Mounting: 已插入真實的 DOM
  • Updating: 正在重新渲染
  • Unmounting: 移除真實的 DOM

Mounting 和 Updating 時期都有兩個方法:componentWillMount处面,componentDidMount 和 componentWillUpdate,componentDidUpdate菩掏。
同時魂角,Mounting 時期還分為兩種情況,一種是組件第一次被 Mounting智绸,一種是后續(xù)被 Mounting野揪。它們都有 getInitialState 方法,但是初次 mounting 還有一個 getDefaultProps 方法瞧栗。
Updating 時期 還有兩個特殊方法 componentWillReceiveProps斯稳,shouldComponentUpdate。 Unmounting 只有一個方法:componentWillUnmount迹恐。
同時可以參考以下圖片來理解組件的生命周期:

component 生命周期
component 生命周期

接下來在代碼中看看這些方法的調用挣惰。

var LogStatus = React.createClass({
    getDefaultProps: function() {
        console.log('組件開始實例化……設置默認 props');
    },
    getInitialState: function() {
        console.log('組件開始實例化……設置默認 state');
        return {
            num: 0
        };
    },
    componentWillMount: function(){
        console.log('組件即將實例化……');
    },
    componentWillReceiveProps: function() {
        console.log('組件即將初始化……并更新 state');
    },
    componentWillUpdate: function() {
        console.log('組件即將更新……');
    },
    onTest: function(ev) {
        this.setState(function(state, props){
            var i = state.num + 1;
            return {num: i};
        })
    },
    render: function(){

        var num = this.state.num;

        return (<h1 onClick={this.onTest}>this.props.text
            <span>{num}</span></h1>);
    },
    componentDidMount: function() {
        console.log('組件實例化完成……');
    },
    componentDidUpdate: function() {
        console.log('組件更新完成……');
    }
});
ReactDOM.render(
    <LogStatus text="組件第一次實例化"></LogStatus>, 
document.getElementById('content')
);
ReactDOM.render(
    <LogStatus text="組件第二次實例化"></LogStatus>,
    document.getElementById('content2')
)

代碼里基本演示了各個階段調用的方法。但這是 ES5 是的生命周期函數(shù)殴边,然而憎茂,現(xiàn)在,React 更推薦的 ES6 寫法锤岸,對生命周期函數(shù)也進行了調整竖幔。

修改如下圖:
ES6 組件的生命周期函數(shù)

參考代碼如下:

class LifeCycle extends React.Component {
    constructor (props) {
        super(props);
        this.state = {
            str : 's'
        };
        console.log("Initial render");
        console.log('constructor');
    }

    componentWillMount() {
        console.log("component will mount");
    }

    componentWillReceiveProps(nextProps){
        console.log("component will receive props");
    }

    shouldComponentUpdate() {
        console.log("should component update");
        return true;
    }

    componentDidUpdate() {
         console.log("component did updates");
    }

    componentWillUnmount () {
        console.log('component will unmount');
    }

    setTheState() {
        let s = "hello";
        if(this.state.str === 's'){
            s = "HELLO";
        }
        this.setState({
            str: s
        })
    }

    forceItUpdate() {
        this.forceUpdate();
    }

    render() {
        console.log("render");
        return (
            <div>
                <span>{"Props:"}<h2>{parseInt(this.props.num)}</h2>
                </span>
                <br></br>
                <span>{"state:"}<h2>{this.state.str}</h2>
                </span>
            </div>
        );
    }
}

class Container extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            num: Math.random() * 100
        };
    }

    propsChange() {
        this.setState({
            num: Math.random() * 100
        })
    }

    setLifeCycleState() {
        this.refs.rLifeCycle.setTheState();
    }

    forceLifeCycleUpdate() {
        this.refs.rLifeCycle.forceItUpdate();
    }

    parentForceUpdate() {
        this.forceUpdate();
    }

    render() {
        return (
            <div>
                <a href="javascript:;" className="weui_btn weui_btn_primary" 
                onClick={this.propsChange.bind(this)}>propsChange
                </a>
                <br/>
                <a href="javascript:;" className="weui_btn weui_btn_primary" 
                onClick={this.setLifeCycleState.bind(this)}>
                setState
                </a>
                <br/>
                <a href="javascript:;" className="weui_btn weui_btn_primary" 
                onClick={this.forceLifeCycleUpdate.bind(this)}>
                forceUpdate
                </a>
                <br/>
                <a href="javascript:;" className="weui_btn weui_btn_primary" 
                onClick={this.parentForceUpdate.bind(this)}>
                parentForceUpdateWithoutChange
                </a>
                <br/>
                <LifeCycle ref="rLifeCycle" num={this.state.num}></LifeCycle>
            </div>
        );
    }
}

ReactDOM.render(
    <Container></Container>, document.getElementById('content')
)

沒有找到銷毀一個 React component 的方法,所以是偷,componentWillUnmount() 沒有使用拳氢。

數(shù)據(jù)流

在 React,數(shù)據(jù)的流向是單向的,從父節(jié)點傳遞到子節(jié)點蛋铆。在之前的代碼已反復使用了數(shù)據(jù)流里相關的兩個參數(shù) props, state馋评。

1.Props

Props 就是 properties 的縮寫。它可以把任意類型的數(shù)據(jù)傳遞給組件刺啦。
可以在掛載組件的時候設置它的 props:

var MyTitle = React.createClass({
    render: function(){
        return (
            <h1>{this.props.title}</h1>
        );
    }
});
ReactDOM.render(
    <MyTitle title="hello留特,world"></MyTitle>,
    document.getElementById('content')
)

上面代碼中,在掛載時傳入 title="hello,world"的參數(shù),這里的參數(shù)都會添加到 props 中磕秤。
另外可以通過調用組件實例的 serProps 方法(很少需要這樣做)來設置其 props:

var listSurveys = React.render(
    <ListSurveys />, document.querySlector('body')
    );
listSurveys.serProps({surveys: surveys});

你只能在子組件或者在組件樹外調用 setProps。千萬別使用 this.setProps 或者直接修改 this.props捧韵。

- PropTypes

通過在組件中定義一個配置對象市咆,React提供了一中驗證 props 的方式:

var SurveyTableRow = React.createClass({
    propTypes: {
        survey: React.PropTypes.shape({
            id: React.PropTypes.number.isRequired
            }).isRequired,
        onClick: React.PropTypes.func
    },
    ……
    });

組件初始化的時候,如果傳遞的屬性和 propTypes 不匹配再来,則會打印一個 console.warn 日志蒙兰。
如果是可選的配置,則可以去掉 .isRequired芒篷。
注意搜变,在應用中使用的 propTypes 并不是強制的,但這提供了一種描述組件 API 的方式针炉。
但在 ES6 的寫法中挠他,是單獨將 propTypes 綁定在組件類中了,寫法如下:

class CustomButton extends React.Component {
  // ...
}
CustomButton.propTypes = {
  name: React.PropTypes.string
};

- getDefaultProps

如果要添加屬性的默認值篡帕,可以使用 getDefaultProps 函數(shù)殖侵。不過,這應該只針對那些非必要屬性镰烧。

var SurveyTable = React.createClass({
    getDefaultProps: function() {
        return {
            surveys: []
        };
    }
    });

注意拢军,getDefaultProps 并不是在組件實例化的時候被調用的,而是在 React.createClass 調用的時候調用的怔鳖,返回值會被緩存起來茉唉,所以,不能在 這個函數(shù)里調用任何特定的實例數(shù)據(jù)结执。下面再看看 ES6 的實現(xiàn):

class CustomButton extends React.Component {
  // ...
}

CustomButton.defaultProps = {
  color: 'blue'
};

2.State

每個 React 組件都有自己的 state,但是 state 只存在于組件的內部度陆。state 是用來確定和修改組件的狀態(tài)的。一個組件與用戶交互的過程中献幔,會根據(jù)用戶的輸入不斷更新狀態(tài)坚芜,實際就是依靠 state 來更新的。具體實現(xiàn)看下面的代碼:

var CountryDropdown = React.createClass({
    getInitialState : function(){
        return {
            showOptions: false
        };
    },

    render: function() {
        var options;
        if(this.state.showOptions){
            options = (
                <ul className='options'>
                    <li>United States of America</li>
                    <li>New Zealand</li>
                    <li>Denmark</li>
                </ul>
                );
        }
        return (
            <div className="dropdown" onClick={this.handleClick}>
            <label>Choose a country </lable>.{options}
            </div>
            );
    },
    handleClick: function()}{
        this.setState({
            showOptions: true
            });
    }
    });

注意斜姥,不能直接使用 this.state 來修改 state, 而是要使用 this.setState 來修改鸿竖。

事件處理

React 事件本質上和 JavaScript 事件一樣。所有事件在命名上也和原生 JavaScript 規(guī)范一致铸敏,并且會在相同的請將下被觸發(fā)缚忧。但是,React 綁定事件處理器的語法和 直接在 HTML 綁定事件的語法類似杈笔。代碼如下:

<button className="btn btn-save" 
onClick={this.handleSaveClicked}> Save </button>

用戶點擊按鈕時闪水,handleSaveClicked()會被調用。事實上蒙具,這種寫法雖然和 HTML 內聯(lián)事件寫法類似球榆,其實在底層實現(xiàn)上并沒有使用 HTML 的 onClick 屬性朽肥。React 只是用這個寫法來綁定事件處理器,其內部則按需高效地維護著事件處理器持钉。

通常當一個事件被觸發(fā)以后衡招,會有一個事件對象被傳遞給事件處理函數(shù)中。打印出事件對象信息如下:
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末每强,一起剝皮案震驚了整個濱河市始腾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌空执,老刑警劉巖浪箭,帶你破解...
    沈念sama閱讀 221,406評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異辨绊,居然都是意外死亡奶栖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,395評論 3 398
  • 文/潘曉璐 我一進店門门坷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驼抹,“玉大人,你說我怎么就攤上這事拜鹤】蚣剑” “怎么了?”我有些...
    開封第一講書人閱讀 167,815評論 0 360
  • 文/不壞的土叔 我叫張陵敏簿,是天一觀的道長明也。 經常有香客問我,道長惯裕,這世上最難降的妖魔是什么温数? 我笑而不...
    開封第一講書人閱讀 59,537評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮蜻势,結果婚禮上撑刺,老公的妹妹穿的比我還像新娘。我一直安慰自己握玛,他們只是感情好够傍,可當我...
    茶點故事閱讀 68,536評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挠铲,像睡著了一般冕屯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上拂苹,一...
    開封第一講書人閱讀 52,184評論 1 308
  • 那天安聘,我揣著相機與錄音,去河邊找鬼。 笑死浴韭,一個胖子當著我的面吹牛丘喻,可吹牛的內容都是我干的。 我是一名探鬼主播念颈,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼泉粉,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了舍肠?” 一聲冷哼從身側響起搀继,我...
    開封第一講書人閱讀 39,668評論 0 276
  • 序言:老撾萬榮一對情侶失蹤窘面,失蹤者是張志新(化名)和其女友劉穎翠语,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體财边,經...
    沈念sama閱讀 46,212評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡肌括,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,299評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了酣难。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片谍夭。...
    茶點故事閱讀 40,438評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖憨募,靈堂內的尸體忽然破棺而出紧索,到底是詐尸還是另有隱情,我是刑警寧澤菜谣,帶...
    沈念sama閱讀 36,128評論 5 349
  • 正文 年R本政府宣布珠漂,位于F島的核電站,受9級特大地震影響尾膊,放射性物質發(fā)生泄漏媳危。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,807評論 3 333
  • 文/蒙蒙 一冈敛、第九天 我趴在偏房一處隱蔽的房頂上張望待笑。 院中可真熱鬧,春花似錦抓谴、人聲如沸暮蹂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,279評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽椎侠。三九已至,卻和暖如春措拇,著一層夾襖步出監(jiān)牢的瞬間我纪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,395評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留浅悉,地道東北人趟据。 一個月前我還...
    沈念sama閱讀 48,827評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像术健,于是被迫代替她去往敵國和親汹碱。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,446評論 2 359

推薦閱讀更多精彩內容