學習React系列1-React-tutorial全解析

React-tutorial例子全解析

Talk is cheap,Show me the codejavascript

近些時間一直在關注React,關於如何學習React能夠參照連接的文章自行制定計劃。千里之行,始於足下。本文是React官方的教程上的一個例子,經過詳細地學習,從中收穫很多,特此作了筆記,與你們共享交流進步。css

起步

  1. 下載例子,而後進行解壓html

  2. 因爲採用的node環境,所以下載解壓以後,只需在所在目錄運行java

    npm install
    node server.js
  3. 採用默認端口設置,只需打開瀏覽器,訪問http://localhost:3000/node

    圖片描述

目錄結構說明

react-tutorialreact

--node_modules
    --body-parser:express中間件,用於接收和解析json數據
    --express:express框架
--public
    --css
        --base.css:基本樣式文件
    --scripts
        -- example.js:React應用js文件
    index.html:基本的HTML結構
--.editorconfig:用於在不一樣的編輯器中統一編輯風格(文件編碼)的配置文件
--.gitignore:git相關配置文件
--app.json:web app的相關信息
--comments.json:上傳的評論數據
--LICENSE:項目代碼使用協議
--package.json:項目所依賴的包,npm install的安裝包的配置文件
--README.md:項目說明書,裏面有使用說明
--requirements.txt:不清楚
--server.js:服務器端的js代碼

App功能

此項目構建了一個簡單的應用,如圖所示git

圖片描述

服務器端

服務器端的功能仍是相對簡單的,經過代碼註釋的形式來分析github

  1. 導入了依賴的模塊web

    var fs = require('fs');  //讀寫文件
    var path = require('path'); //路徑
    var express = require('express'); //express框架
    var bodyParser = require('body-parser'); //中間件
  2. 生成app,而且進行配置ajax

    //獲取comments.json文件的路徑
    var COMMENTS_FILE = path.join(__dirname, 'comments.json');
    
    //設置端口
    app.set('port', (process.env.PORT || 3000));
    
    //設置靜態文件的文件目錄路徑
    app.use('/', express.static(path.join(__dirname, 'public')));
    //啓用bodyParser中間件接收請求,而且接收並解析json數據
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({extended: true}));
  3. 設置響應頭部信息

    app.use(function(req, res, next) {
        //容許跨域 CORS
        res.setHeader('Access-Control-Allow-Origin', '*');
        //緩存設置
        res.setHeader('Cache-Control', 'no-cache');
        next();
    });
  4. 設置get請求url對應的處理函數(獲取評論json數據)

    app.get('/api/comments', function(req, res) {
        //讀取comments.json文件,而且解析爲json數據
        fs.readFile(COMMENTS_FILE, function(err, data) {
            if (err) {
              console.error(err);
              process.exit(1);
            }
            //讀取成功後,返回
            res.json(JSON.parse(data));
        });
    });
  5. 設置post請求url對應的處理函數(提交評論數據)

    app.post('/api/comments', function(req, res) {
        //先讀取comments.json文件
        fs.readFile(COMMENTS_FILE, function(err, data) {
            if (err) {
              console.error(err);
              process.exit(1);
            }
            //將文件內容解析爲json數據
            var comments = JSON.parse(data);
            //獲取新評論
            var newComment = {
              id: Date.now(),
              author: req.body.author,
              text: req.body.text,
            };
            //添加json數組中
            comments.push(newComment);
            //將json數據寫回到comments.json文件中,而且返回所有的評論數據
             fs.writeFile(COMMENTS_FILE, JSON.stringify(comments, null, 4), function(err) {
                  if (err) {
                    console.error(err);
                    process.exit(1);
                  }
                  res.json(comments);
            });
        });
    });
  6. 啓動,監聽端口

    app.listen(app.get('port'), function() {
      console.log('Server started: http://localhost:' + app.get('port') + '/');
    });

web端

web端核心在於example.js文件,結合官網的資料,咱們對這個應用進行分析,學習如何構建一個簡單的react應用。

組件結構

React踐行了Web Components的理念,依照組件化的開發方式,咱們來分析這個應用的組件結構(如圖所示):

圖片描述

便是:

-- CommentBox
    -- CommentList
        -- Comment
    -- CommentForm

組件之間的關係圖爲:

圖片描述

組件Comment

如上述的結構圖,咱們從最底層開始編寫組件Comment,這個組件須要作兩件事情

  1. 接收上層組件CommentList傳遞的數據,動態渲染虛擬DOM節點,則從props中讀取數據

    //評論人
    {this.props.author}
    //評論的內容
    {this.props.children}
  2. 因爲評論是支持MarkDown語法的,所以須要使用第三放庫marked對用戶輸入的內容進行處理。

    var rawMarkup = marked(this.props.children.toString(), {sanitize: true});
  3. 此外,輸出的內容要解析爲HTML,而在默認狀況下,基於預防XSS攻擊的考慮,React對輸出的內容是不解析爲HTML的。此時,須要利用到特殊的屬性dangerouslySetInnerHTML,要將內容放到一個對象的_html屬性中,而後將這個對象賦值給dangerouslySetInnerHTML屬性

    var html = {_html:"輸出的html內容"};
    <span dangerouslySetInnerHTML={html} />
var Comment = React.createClass({
    rawMarkup : function() {
        var rawMarkup =  marked(this.props.children.toString(),{sanitize:true});
        return {_html : rawMarkup}; //React的規則,會讀取這個對象的_html內容,
    },
    render : function() {
        return (
            <div className="comment">
                <h2 className="commentAuthor">
                    {this.props.author}
                </h2>
                <span  dangerouslySetInnerHTML={this.rawMarkup()} />
            </div>
        );
    }
});

組件CommentList

組件CommentList須要作的就是接收上一層組件CommentBox傳遞過來的數據,而後根據數據生成多個子組件Comment

var CommentList = React.createClass({
    render : function() {
        var commentNodes = this.props.data.map(function(comment){
            return (
                <Comment author={comment.author} key={comment.id}>
                    {comment.text}
                </Comment>
            );
        });
        return (
            <div className="commentList">
                {commentNodes}
            </div>
        );
    }
})

在生成子組件Comment時,將每一個子組件的key屬性設置爲comment.id,這是由於key是一個可選的惟一標識符,經過它能夠給組件設置一個獨一無二的鍵,並確保它在一個渲染週期中保持一致,使得React可以更加智能地決定應該重用一個組件,仍是銷燬並從新建立一個組件,進而提高渲染性能。

組件CommentForm

組件CommentForm須要作的就是兩件事情

  1. 管理自身的狀態this.state(即表單中輸入的評論人和評論內容)

    1. 當表單輸入發生變化時

    2. 當表單提交時

  2. 當submit事件觸發時,調用上一層組件CommentBox的事件處理函數,改變組件CommentBox的狀態。

var CommentForm = React.createClass({
    getInitialState : function() {
        //設置初始狀態,
        return {author:'',text:''};
    },
    handleAuthorChange : function(e) {
        this.setState({
            author : e.target.value
        });
    },
    handleTextChange : function(e) {
        this.setState({
            text : e.target.value
        });
    },
    handleSubmit : function(e) {
        e.preventDefault();
        var author = this.state.author.trim();
        var text = this.state.text.trim();
        if(!text || !author){ //爲空驗證
            return;
        }
        //觸發評論提交事件,改變父組件的狀態
        this.props.onCommentSubmit({author:author,text:text});
        //改變自身的狀態
        this.setState({author:'',text:''});
    }
});

在這裏有一個值得注意的點,那就是抽象的自定義事件commentSubmit和真實的事件submit之間的聯繫,這是一個至關實用的技巧,在接下來的章節能夠看到是如何實現的。

組件CommentBox

做爲整個應用的頂層組件,CommentBox須要作的事情有:

  1. 從服務器端請求已有的評論數據

  2. 將新的評論數據上傳到服務器

  3. 管理自身的狀態,根據狀態對視圖進行渲染(狀態改變的示意圖以下)

圖片描述

var CommentBox = React.createClass({
    getInitialState : function(){
        return {data : []};
    },
    loadCommentsFromServer : function() {
        //使用了jQuery的Ajax
        $.ajax({
            url : this.props.url,
            dataType : 'json',
            cache : false,
            success : function(data) {
                this.setState({data:data});
            }.bind(this),
            error : function(xhr,status,err){
                console.err(this.props.url,status,err.toString());
            }.bind(this)
        });
    },
    componentDidMount : function() {
        /*
        這個方法屬於React組件生命週期方法,在render方法成功調用而且真實的DOM
        已經渲染以後,調用此方法,這個方法發送json數據請求,而且設置一個定時器
        ,每隔一段時間就向服務器請求數據
        */
        this.loadCommentsFromServer();
        setInterval(this.loadCommentsFromServer,this.props.pollInterval);
    },
    handleCommentSubmit : function(comment) {
        /*
        這個方法也是比較有意思:
        1. 自定義了一個commentSubmit事件,而且此方法做爲該事件的處理函數。
        2. 此方法是在子組件CommentForm的submit事件處理函數中調用
        */
        var comments = this.state.data;
        comment.id = Date.now();
        var newComments = comments.concat([comment]);
        //改變自身狀態
        this.setState({data:newComments});
        $.ajax({
            url : this.props.url,
            dataType: 'json',
            type : 'POST',
            data : comment,
            success : function(data) {
                this.setState({data:data});
            }.bind(this),
            error : function(xhr,status,err) {
                //還原數據
                this.setState({data:comments});
                console.err(this.props.url,status,err.toString());
            }.bind(this)
        });
    },
    render : function() {
        return (
            <div className="commentBox">
                <h1>Comments</h1>
                <CommentList data={this.state.data} />
                <CommentForm onCommentSubmit={this.handleCommentSubmit} />
            </div>
        );
    }
});

最後,只需將組件CommentBox掛載到真實的DOM節點上,就能夠看到效果了

ReactDOM.render(
    <CommentBox url="/api/comments" pollInterval={2000} />,
    document.getElementById('content')
);
相關文章
相關標籤/搜索