zoukankan      html  css  js  c++  java
  • 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,个人观点有误请指正,不足请提出。

  • 相关阅读:
    【转】手动写一个Behavior Designer任务节点
    【转】BehaviorDesigner学习
    Behavior trees for AI: How they work
    【转】behave行为树学习使用第一天
    【转】写给笨人的法线贴图原理
    【转】如何把Json格式字符写进text文件中
    【转】系列化与反系列化
    【转】C#中将JSon数据转换成实体类,将实体类转换成Json
    【转】c#处理3种json数据的实例
    【转】Mecanim Animator使用详解
  • 原文地址:https://www.cnblogs.com/alsy/p/5372770.html
Copyright © 2011-2022 走看看