react学习

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.

<Route name="app" path="/" handler={App}>
    <DefaultRoute name="home" handler={Home}></DefaultRoute>
    <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.这样就实现了不是嵌套关系的组件之间的通信。

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

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

转载于:https://www.cnblogs.com/alsy/p/5372770.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值