最近重写WAP项目,为了使前端和后端完全分离,需要完全摒弃原始的JSP页面,采用传统的 HTML+CSS+JS(不乏也会涉及部分HTML5,CSS3, 如 Storage, Location等)作为前端基础, 后端仅需提供数据交互API, 也就几乎类似APP + API的交互模式, 这样就能清晰地独立前端和后端项目,独立部署分发。与此同时,前端展现数据将显得力不从心, 没有JSP中丰富的JSTL标签,于是,我们需要一些纯前端的展现,如模板技术,框架技术, 听闻过如AngularJS,但其略显繁重, 在了解到React之后, 决定用其来作为前端主力。
通常我们会使用React作为MVC中的 V,React能够很轻松地融入项目中, 并不需要很复杂的技术。
在HTML中的DOM节点,对于节点的事件处理,UI更新,都需要我们手动监听事件,再在事件中去更新UI或其他操作, 为此React抽象出一层虚拟DOM, 使得节点事件处理和UI更新更佳轻松,而且性能更佳。
React实现了单向数据流,以减少模板的使用,因此比传统的数据绑定更简单。
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
var mountNode = document.getElementById('hello');
React.render(<HelloMessage name="John" />, mountNode);
该方法是必须的。当该方法被调用时, 会检查当前组件的this.props和 this.state, 意味你可以在该方法中开始访问 props和state。 你不应该在该方法中更新当前组件状态(State),这将导致组件重新被render, 因此,render方法应保持清晰干净,仅仅组装各种组件,设置组件属性即可。
该方法在组件被挂载前调用,仅调用一次,返回Object作为组件的初始状态, 可通过this.state.keyName访问这些状态变量, 并且可以通过this.state.setState({keyName: '...'})更新组件状态。
该方法用于返回默认属性,即当父组件没有传入属性值时,将使用这些默认值, 并且这些默认属性将被缓存,即所有该组件的实例共享该这些默认属性。
该属性用于多个组件公用一些方法,但并不推荐,毕竟React提倡的是组件开发, 使用可见这里。
顾名思义,statics用于定义组件的静态方法,如
var MyComponent = React.createClass({
statics: {
customMethod: function(foo) {
return foo === 'bar';
}
},
render: function() {
}
});
MyComponent.customMethod('bar'); // true
组件名称,可用于调试。
仅调用一次,在初始化render前被执行,因此,若在该方法中调用 setState,那么render将能看到更新的状态, 并且并不会render只会执行一次,尽管执行了setState。
仅调用一次,在初始化render后被执行,此时,组件已经有对应的真实DOM节点, 因此可以调用React.findDOMNode(this)获取组件的真实DOM节点, 通常会在该方法中去获取数据。
当组件被赋值新的属性时被调用,可以在该方法中根据新的属性,做一些处理,如
componentWillReceiveProps: function(nextProps) {
this.setState({
likesIncreasing: nextProps.likeCount > this.props.likeCount
});
}
该方法在render前调用,若返回false,render方法不将被执行,可以在该方法做一些判断,确认是否需要render组件。
shouldComponentUpdate: function(nextProps, nextState) {
return nextProps.id !== this.props.id;
}
该方法在当组件更新后且被刷新到DOM节点后被调用,不会在初始化render后调用, 可以在该方法中操作最新的DOM节点。
<LoginPage>
<Header>
<HeaderLink icon="icon icon-left-nav" />
<HeaderTitle title="快捷登录" />
</Header>
<Body>
<Input type="text" cls="mobile" placeholder="请输入手机号" />
<SendCodeBtn mobile={this.state.mobile}/>
<Input type="text" cls="valid-code" placeholder="请输入您的验证码" />
<Button type="button" cls="btn btn-block btn-warning" >登录</button>
</Body>
</LoginPage>
可见,即使是简单的input元素,也用Input组件来封装, 也是为了能够进行一些简单的定制化,如样式,事件等。
<Button type="button" cls="btn btn-block btn-warning" onTouch={this.onLogin}>登录</button>
var Button = React.createClass({
render: function() {
return <button className={this.props.cls} onTouchStart={this.props.onTouch}>{this.props.children}</button>;
}
});
当一个页面比较复杂时,会由很多组件组装起来,建议对于业务组件,将状态抽象到上层组件中进行统一管理, 也便于页面中的组件能够通信,因为一旦状态更新,就会render, 状态太混乱,造成不必要的UI渲染,或者错误的UI渲染。在业务组件编写中, 可以尽量用props代替不必要的state, 即让组件无状态,将状态更新交由外部组件去控制。
React本身并不支持组件依赖管理, 即不能像RequireJs等模块化管理工具一样,能维护模块之间的依赖关系, 比较简单的解决办法是将业务组件都concat到一个文件中,那么组件就能通过组合来调用其他组件。
React大多第三方组件是遵循npm规范, 即CommonJS规范, 于是我们可以考虑使用webpack来管理React组件的依赖关系,
{
entry:{
login: ['./app/components/login/index.jsx', './app/components/login/main.jsx']
},
output: {
filename: '[name].js'
},
module: {
loaders: [
{ test: /\.jsx$/, loader: 'babel-loader' }
]
},
...
}
var React = require('react');
// require other components
var Login = React.createClass({
...
});
module.exports = Login;