react学习

来源:互联网 发布:sql中给表起别名 编辑:程序博客网 时间:2024/05/22 17:23

http://www.cnblogs.com/alsy/p/5372770.html

react实战

GitHub上给出react的三个关键点:

1.Just the UI (仅是view层)

2.Virtual Dom (虚拟Dom)

3.Data flow (数据流是沿着组件树从上到下单向流动的)

理解react可以参考 这里、这里,还有深入浅出React一、二、三、四。

以练手的博客为例。(学习新技术最快的途径就是实践,解决问题不断提高)【完整代码】

react强调组件的开发方式,类似于搭积木。将一个网页拆分成一个个的组件,组件可以复用,组件之间可以嵌套使用,嵌套组件之间通过属性(props)通信。

比如首页被拆分成:<Nav />、<ContainerLeft />、<Home />、<Article />、<Footer />

复制代码
render() {        let nav = this.state.category;        return (            <div>                <Nav nav={nav} />                <div className="container">                    <div className="row">                        <div className="col-md-3">                            <ContainerLeft/>                        </div>                        <div className="col-md-9">                            <div id="container-right">                                {/* <RouteHandler/> */}                                <RouteHandler/>                            </div>                        </div>                    </div>                </div>                <Footer/>            </div>        );    }
复制代码

上面的<Home />是由<Route />组件中的默认路由,了解react-router.

1
2
3
4
5
<Route name="app" path="/" handler={App}>
    <<span style="background-color: #888888;">DefaultRoute</span> name="home" handler={<span style="background-color: #888888;">Home</span>}></<span style="background-color: #888888;">DefaultRoute</span>>
    <Route name="category" path="/category/:categoryId" handler={Category}/>
    <Route name="details" path="/details/:articleId" handler={Details}/>
  </Route>

这样首页就拆分成了一个个组件,图中可以看到<Home />里面包含<Article />。

<Home />组件中在组件挂载之前初始化state值为{details:[]},组件挂载执行render(),组件挂载完成时请求文章的数据detail.json(模拟的前台数据)并设置state值。state改变触发render()执行。

复制代码
let Home = React.createClass({  getInitialState() {    return {      details: []    };  },  loadCommentsFromServer() {    let self = this;    $.ajax({      url: './mock/detail.json',      dataType: 'json',      success: function(r) {        if(200 == r.errCode){          AppF.articleCut(r.data, config.indexShowNum, (detailsCuted) => {            AppF.timeToStr(detailsCuted, (detailsStrTime) => {              AppF.isStrCut(detailsStrTime, 'title', config.indexTitleLength, (details) => {                AppF.isStrCut(details, 'content', config.indexContentLength, (details) => {                  self.setState({details: details || []});                });              });            });          });        }      },      error: function(xhr, status, err) {        console.error(xhr, status, err.toString());      }    });  },  componentDidMount() {    this.loadCommentsFromServer();  },  render() {    let details = this.state.details;    console.log("Home -- render()");    return(      <div>        <div className="headline">文章<span className="font-green">推荐</span></div>        <Articles details={details}/>      </div>    );  }});
复制代码

<Home />向<Article />中传值是通过属性(props)details,这是组件之间通信的一种方式。

<Article />组件中通过this.props.details取得<Home />传过来的属性值,使用map()去遍历数组,<Link />是react-router提供的一个组件,类似于<a>链接

复制代码
let Article = React.createClass({    render() {        let details = this.props.details;        details = details.map( (item, index) => {            return (                <article key={"article-" + index}>                    <div className="title" title={item.title}>                        <Link to="details" params={{articleId: item.id}} title={item.title}>                            <i className="glyphicon glyphicon-triangle-right icon-title"></i>                            &nbsp;{item.title}                        </Link>                    </div>                    <div className="row">                        <div className="col-md-3">                            <img className="img-thumbnail" src ={ "../vendor/images/" + item.img_file }/>                        </div>                        <div className="col-md-9">                            <div className="content">                                <p>{item.content}</p>                            </div>                        </div>                    </div>                    <div className="row sub-title">                        <div className="col-md-3"><i className="glyphicon glyphicon-time"></i>&nbsp;{item.create_time}</div>                        <div className="col-md-2"><i className="glyphicon glyphicon-folder-open"></i>&nbsp;&nbsp;{item.category}</div>                        <div className="col-md-2"><i className="glyphicon glyphicon-eye-open"></i> {item.hits}</div>                        <div className="col-md-5">                            <Link to="details" params={{articleId: item.id}}  className="btn btn-success btn-sm pull-right btn-read-all" title={item.title}>阅读全文>></Link>                        </div>                    </div>                </article>            );        });        return(            <div>                <div className="article-list">                    {details}                </div>            </div>        );    }});
复制代码

其他组件类似的这样搭建,这样首页就出来了。在点击有<Link to="details" params={{articleId: item.id}} />形成的链接,<Route />组件路由到<Details />组件,并传递了参数{articleId: item.id}。

<Details />组件中通过this.props.params.articleId取得传递过来的参数。

复制代码
let Details = React.createClass({    getInitialState() {        return {            threeArticle: []        };    },    loadCommentsFromServer() {        console.log("loadCommentsFromServer");        let self = this;        $.ajax({            url: './mock/detail.json',            dataType: 'json',            success: function(r) {                if(200 == r.errCode){                    var details = r.data;                    AppF.articleSort(details, 'id', 'ase', (details) => {                        AppF.timeToStr(details, (details) => {                            var details = details;                            self.getArticleKey(details, self.props.params.articleId, (key) => {                                self.getThreeArticle(details, key, (threeArticle) => {                                    //console.log(threeArticle);                                    self.setState({                                        threeArticle: threeArticle[0]                                    });                                });                            });                        });                    });                }            },            error: function(xhr, status, err) {                console.error(xhr, status, err.toString());            }        });    },    componentWillMount(){        console.log("Details -- componentWillMount");        this.loadCommentsFromServer();    },    //当组件在页面上渲染完成之后调用    componentDidMount() {        console.log("Details -- componentDidMount");    },    //在组件接收到新的 props 的时候调用。在初始化渲染的时候,该方法不会调用。    componentWillReceiveProps(nextProps) {        console.log("Details -- componentWillReceiveProps");        this.loadCommentsFromServer();    },    render() {        console.log(this.state.threeArticle);        return(            <div>                <Article article={this.state.threeArticle}/>            </div>        );    },    getThreeArticle(details, key, cb) {        var arr = [];        var length = details.length;        if( length == 0 || key === ''){            cb([]);        } else if(length == 1){            arr.push({pre: {}, cur: details[0], next: {} });        } else {            if(key == 0){                arr.push({pre: {}, cur: details[0], next: details[1] });            } else if (key == length - 1){                arr.push({pre: details[length - 2], cur: details[length - 1], next: {} });            } else {                arr.push({pre: details[key - 1], cur: details[key], next: details[key + 1] });            }        }        cb(arr);    },    getArticleKey(details, article_id, cb) {        if(0 == details.length){            cb('');        } else {            details.forEach(function(item, k){                if(item['id'] == article_id){                    cb(k);                }            });        }    }});
复制代码

<Details />组件里包含了<Article />组件,同样的通过属性article传递数据。同样的<Article />组件中通过this.props.article来取得<Deatils />传递过来的article属性。

就可以得到文章详情页的效果了:

这时出现了一个问题:在我点击下一篇时我想让对应的导航条在相应的分类下加上我的active样式,这时设及到我要在<Article />组件中绑定事件,获取DOM的属性,向<Nav />组件传递数据。注意此时的<Nav />组件和<Article />组件并没有什么嵌套关系。

复制代码
let Article = React.createClass({    changeNavClassPre: function(e){        console.log("Article -- changeNavClassPre");        let cid = React.findDOMNode(this.refs.articlePre).getAttribute("data-category-id");        PubSub.publish(EventName.navClass, {categoryId: cid});    },    changeNavClassNext: function(e){        console.log("Article -- changeNavClassPre");        let cid = React.findDOMNode(this.refs.articleNext).getAttribute("data-category-id");        PubSub.publish(EventName.navClass, {categoryId: cid});    },    render() {        let threeArticle = this.props.article;        let cur, pre, next;        //console.log(threeArticle);        $.each(threeArticle, (item, value) => {            if(item == 'cur'){                cur = (                    <div>                        <div className="col-md-12 article-title">{value.title}</div>                        <div className="col-md-12 article-icon">                            <ul>                                <li>                                    <i className="glyphicon glyphicon-folder-open"></i>&nbsp;{value.category}                                </li>                                <li>                                    <i className="glyphicon glyphicon-time"></i>&nbsp;{value.create_time}                                </li>                                <li>                                    <i className="glyphicon glyphicon-eye-open"></i>&nbsp;{value.hits}人阅读                                </li>                            </ul>                        </div>                        <div className="col-md-12">                            <p className="article-content">{value.content}</p>                        </div>                        <div className="col-md-12 article-icon">                            <ul>                                <li>                                    <a href="javascript:;"><i className="glyphicon glyphicon-thumbs-up"></i>&nbsp;赞</a>                                </li>                                <li>                                    <i className="glyphicon glyphicon-comment"></i>&nbsp;0人评论                                </li>                            </ul>                        </div>                    </div>                );            } else if(item == 'pre'){                if(!value.id){                    pre = (                        <p className="article-pre">                            <i className="glyphicon glyphicon-chevron-up"></i>&nbsp;上一篇:无                        </p>                    );                } else {                    pre = (                        <p className="article-pre" data-category-id={value.category_id} onClick={this.changeNavClassPre} ref="articlePre">                            <Link                                to="details"                                params={{articleId: value.id}}                                title={value.title}>                                <i className="glyphicon glyphicon-chevron-up"></i>                                &nbsp;上一篇:{value.title}                            </Link>                        </p>                    );                }            } else {                if(!value.id){                    next = (                        <p className="article-pre">                            <i className="glyphicon glyphicon-chevron-down"></i>&nbsp;下一篇:无                        </p>                    );                } else {                    next = (                        <p className="article-pre" data-category-id={value.category_id} onClick={this.changeNavClassNext} ref="articleNext">                            <Link                                to="details"                                params={{articleId: value.id}}                                title={value.title}>                                <i className="glyphicon glyphicon-chevron-down"></i>                                &nbsp;下一篇:{value.title}                            </Link>                        </p>                    );                }            }        });        return (            <div className="row">                {cur}                <div className="col-md-12" id="pre-next">                    {pre}                    {next}                </div>            </div>        );    }});
复制代码

这里需要注意:

1.React并不会真正的绑定事件到每一个具体的元素上,而是采用事件代理的模式:在根节点document上为每种事件添加唯一的Listener,然后通过事件的target找到真实的触发元素。这样从触发元素到顶层节点之间的所有节点如果有绑定这个事件,React都会触发对应的事件处理函数。这就是所谓的React模拟事件系统。

2.如果你要获取原生的Dom,然后进行相关Dom操作,你可以给你想要获得的标签内分配ref属性(比如ref="articleNext"),然后在组件内React.findDOMNode(this.refs.articleNext)获得对应的Dom,然后就可以愉快的操作Dom了。

3.React高效的Diff算法。这让我们无需担心性能问题,由虚拟DOM来确保只对界面上真正变化的部分进行实际的DOM操作。

那么就剩下如何向<Nav />组件传递消息了,这里我采取的是订阅发布模式(观察者模式)来进行通信的。引用PubSubJS

首先在<Nav />中,我们订阅了navClass这么一个事件

复制代码
    componentDidMount: function () {        console.log("Nav -- componentDidMount");        token = PubSub.subscribe(EventName.navClass, this.changeActive);    },    changeActive: function(msg, data){        //console.log(data);        let self = $('#navbar-nav li[data-active="' + data.categoryId +'"]');        $('#navbar-nav li').removeClass('active');        self.addClass('active');        self.parents("li.dropdown").addClass('active');    },    componentWillUnmount: function () {        console.log("componentWillUnmount");        PubSub.unsubscribe( token );    }
复制代码

 在<Article />组件中去发布这个事件,并且传递了{categoryId: cid}过去

复制代码
changeNavClassPre: function(e){        console.log("Article -- changeNavClassPre");        let cid = React.findDOMNode(this.refs.articlePre).getAttribute("data-category-id");        PubSub.publish(EventName.navClass, {categoryId: cid});    },    changeNavClassNext: function(e){        console.log("Article -- changeNavClassPre");        let cid = React.findDOMNode(this.refs.articleNext).getAttribute("data-category-id");        PubSub.publish(EventName.navClass, {categoryId: cid});    }
复制代码

 <Nav / >就可以去响应这个事件,相应的去操作Dom.这样就实现了不是嵌套关系的组件之间的通信。

说到这里,对于数据的请求、数据的变化等场景,可以使用 Flux、Relay、GraphQL 来处理。

注:入门react,个人观点有误请指正,不足请提出。

分类: web
0 0