react实战
GitHub上给出react的三个关键点:
1.Just the UI (仅仅是view层)
2.Virtual Dom (虚拟Dom)
3.Data flow (数据流是沿着组件树从上到下单向流动的)
理解react可以参考 这里、这里,还有深入浅出React一、二、三、四。
以练手的博客为例。(学习新技术最快的途径就是实践,解决问题不断提高)【完整代码】
react强调组件的开发方式,类似于搭积木。将一个网页拆分成一个个的组件,组件可以复用,组件之间可以嵌套使用,嵌套组件之间通过属性(props)通信。
比如首页被拆分成:、
render() { let nav = this.state.category; return (); } {/**/}
上面的
<DefaultRoute name="home" handler={Home}>DefaultRoute>
这样首页就拆分成了一个个组件,图中可以看到
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(); } });文章推荐details={details}/>
组件中通过this.props.details取得
let Article = React.createClass({ render() { let details = this.props.details; details = details.map( (item, index) => { return (); }); return( <Link to="details" params={{articleId: item.id}} title={item.title}> {item.title}{item.content}
{item.create_time}{item.category}{item.hits}details" params={{articleId: item.id}} className="btn btn-success btn-sm pull-right btn-read-all" title={item.title}>阅读全文>>); } });{details}
其他组件类似的这样搭建,这样首页就出来了。在点击有形成的链接,
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(); }, 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); } }); } } });this .state.threeArticle}/>
就可以得到文章详情页的效果了:
这时出现了一个问题:在我点击下一篇时我想让对应的导航条在相应的分类下加上我的active样式,这时设及到我要在组件中绑定事件,获取DOM的属性,向组件传递数据。注意此时的组件和组件并没有什么嵌套关系。
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 = ( ); } else if(item == 'pre'){ if(!value.id){ pre = (上一篇:无
); } else { pre = (onClick={this.changeNavClassPre} ref="articlePre"> <Link to="details" params={{articleId: value.id}} title={value.title}> 上一篇:{value.title}
); } } else { if(!value.id){ next = (下一篇:无
); } else { next = (this.changeNavClassNext} ref="articleNext"> <Link to="details" params={{articleId: value.id}} title={value.title}> 下一篇:{value.title}
); } } }); return ({cur}); } });{pre} {next}
这里需要注意:
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操作。
那么就剩下如何向组件传递消息了,这里我采取的是订阅发布模式(观察者模式)来进行通信的。引用PubSubJS
首先在中,我们订阅了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 ); }
在组件中去发布这个事件,并且传递了{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}); }
说到这里,对于数据的请求、数据的变化等场景,可以使用 Flux、Relay、GraphQL 来处理。
注:入门react,个人观点有误请指正,不足请提出。