use reactjs

- 3 mins

最近在一个项目中使用了React作为主力开发框架,写篇总结,说说自己的感受和踩过的坑。

Angular的对比,网上可以搜到各种文章,着重推荐这篇From AngularJS to React: The Isomorphic Way,讲的比较详实,同时还提出了一种名为The Isomorphic Way的思路。

首先来说一下我在这个项目中使用React的方式,在React的官方网站和很多介绍文章里都表示 - React不是一个完整的MVCMVVM框架,事实也是如此React其实应该算做是一个增强型的view模板,当然,你说他是Web Component其实也是合理的。在这里就不讨论它的分类问题了,毕竟分类都是人为的,怎么在实际中更好的应用,才是广大使用者更关心的话题。

由于React除了部分的Event能力之外,并不具备完整的MVC能力,所以需要其他工具或者框架来辅助其完成整个前端逻辑的开发,在这次的项目中,我选择的是browserify,其在浏览器端,提供了nodejs中的一些模块调用,使开发过程类似于node的开发过程。

当然,也可以配合其他框架,比如jquery,zepto,requirejs,seajs什么的,使用browserify其实主要就是我比较懒…o(╯□╰)o

再说回React,其实React的核心就是View,也就是UI层面,在使用Backbone时,我们会将整个页面拆分成不同的组件,然后再组合成不同的collections,最后再组成一整个页面,使用React也类似,只不过名字在这里一变,变成了各种component,最终组成的Domtree是类似这样的:

<Card>
	<Cardheader>This is Header</Cardheader>
	<Cardbody>
		<Userlist data={this.props.list} />
	</Cardbody>
</Card>

这里面的类似于dom元素的<Card>等就可以理解为component,是不是有点像xml?其实在React的官方文档里,这种形式被称为jsx,可以理解成用js写的xml,所以React的文件,也都是以jsx作为后缀的。

在开发过程中,如果我们新建一个UI组件(或者叫模块也好,这个随便),这个时候我们总是需要传入一些数据才行,没有数据,就没办法渲染模板;在很多时候,我们用一些模板引擎的render方法,将数据作为参数传到render函数中,对模板进行渲染。

React中,如你所见,我们的UI组件是类似于html标签的,显然没有一个方法入口,可以将数据作为参数传入,但是可以通过类似于html参数的方式,将数据传入,看🌰:

var Userlist = React.renderCompoent(
	<Userlist data={window.user} />,
	document.body
);

通过在<Userlist>上标记data属性,我们就可以将所需要的数据传入到UI组件中;在Userlist的具体方法中,我也可以通过this.props这个对象,来获取其标签上写入的属性。

######props and state

说到props,这个对象基本可以理解为组件与外界通讯的唯一方式,而且还是单向的,也就是说它是外界对组件的输入入口,在组件本身内部,只能对其get,而不能进行set

在组件中,对于数据的操作,建议使用state来进行,可以跟props有个区分,隔离开组件内外的环境。但是这里其实有个坑,就是假如有一份数据,既要A组件内可更新,又要再其他组件内更新,这个时候,无论是用props还是state其实都是不大合适的。理想的方法是参照Flux规范,使数据的流动成为一个闭环,这个时候,可能我们就需要在更新数据的每个阶段添加一个事件,再在外部监听这个事件去更新数据。关于ReactFlux的种种,以后会再单开篇文章讨论,这里就不展开了。

在这里说一个比较trick但是很实用的方法,可以让子组件可以更新父级组件的状态,方法也很简单,还是看🌰吧:

var Parent = React.createClass({
	"getInitialState":function () {
		return {
			"nums": 0
		}
	},
	"render": function() {
		return (
			<div className="wrap">
				<p className="num">
					{this.state.nums}
				</p>
				<Son clickHandle={this.addNum} nums={this.state.nums}>
					Click Me!
				</Son>
			</div>
		)
	},
	"addNum": function(nums) {
		var num = nums;
		this.setState({
			"nums": num
		});
	}
});

var Son = React.createClass({
	"render": function() {
		return (
			<button onClick={this.clickHandle}>
				Click Me!
			</button>
		)
	},
	"clickHandle": function () {
		var num = this.props.nums + 1;
		if (typeof(this.props.clickHandle)) {
			this.props.clickHandle(num)
		};
	}
});

通过将父元素的方法绑定在子元素的props上,就可以实现在子元素中调用父级元素的方法啦。

######方法继承 - mixins

在以往的开发经验中,我们经常会用一些办法实现方法继承,无论是通过prototype的方式也好,还是通过extend的方式也好。在react中,同样有方式实现方法继承

react中,可以通过mixins来实现方法继承,废话少说,举个🌰:

var modal = {
	"hide": function() {
		console.log("hide")
	},
	"show": function() {
		console.log("show")
	}
};

var tips = React.createClass({
	"mixins": [modal],
	"render": function() {
		// code
	},
	"componentDidMount":function () {
		// 调用show方法
		this.show();
	}
});

######元素的clone - React.addon.cloneWithProps

在开发过程中,如果我们需要复制某个元素,可以通过React.addon.cloneWithProps方法来实现。例如,现在有个List列表,但是其中的Item模板类型不确定,出于通用性考虑,我们可以在List内部cloneItem元素,这样,无论Item元素怎么变,只要List的逻辑不变,理论上可以插入任何类型的Item,看🌰:

var List = React.createClass({
	"render": function() {
		var Items = this.props.data.map(function(itemData,index) {
			// 直接clone子元素
			var _item = React.addon.cloneWithProps(this.props.children);
			
			_item.props.data = itemData;
			_item.props.key = index;
		});
		
		return (
			<div className="list">
				{Items}
			</div>
		)
	}
});

var itemA = React.createClass({
	"render": function () {
		return (
			<div className="itemA">
				{this.props.data.name}
			</div>
		)
	}
});

var itemB = React.createClass({
	"render": function() {
		return (
			<div className="itemB">
				{this.props.name}
			</div>
		)
	}
});

// 下面是调用list和item的实例

var MainFoo = React.createClass({
	"render": function() {
		return (
			<List data={this.props.data}>
				<itemA />
			</List>
		)
	}
})

var MainBar = React.createClass({
	"render": function() {
		return (
			<List data={this.props.data}>
				<itemB />
			</List>
		)
	}
});

通过这个🌰,可以看出来,使用clone可以使List更具拓展性。

######根据状态改变样式

涉及到UI,就会涉及到变化。在react中,通过高效的render函数,可以允许我们根据状态的变化渲染元素的结构。通常情况下可以通过css3中新增的选择器来做,例如:

.item[data-status="default"] {
	/* code */
}
.item[data-status="focus"] {
	/* code */
}
.item[data-status="error"] {
	/* code */
}

如果不喜欢这种方式,也可以通过设置className的方式改变元素的样式,在react中,有个方便的函数,可以根据状态生成不同的className列表,举个🌰:

var foo = React.createClass({
	"getInitialState": function () {
		return {
			"status": this.props.status || "default"
		}
	},
	"render": function () {
		var cx = React.addon.classSet;
		var classes = cx(
			"btn":true,
			"default": this.state.status === "default",
			"focus": this.state.status === "focus",
			"error": this.state.status === "error"
		);
		return (
			<button className={classes}>
				Click!
			</button>
		)
	}
});

######last

以上仅仅列举了一些我在这一段使用react的时间内,觉得有点儿意思的点,更多的内容可以访问react的官方文档查看。

rss facebook twitter github youtube mail spotify lastfm instagram linkedin google google-plus pinterest medium vimeo stackoverflow reddit quora