这是React和ECMAScript6结合使用系列文章的第二篇。
React | JS |
---|---|
在第一篇文章中,我们开始介绍如何使用ES6来创建静态的组建并且输出Hello from ES6
. Not so exciting :)
在这篇文章中,我们将创建一个名字叫做CartItem
的更复杂的组建。它将输出购物车中的一些产品信息,包括图片、标题和价格。
此外,用户可以和CartItem
组建交互,通过点击增加
或者减少
改变items的数量。并且我们的组建将对交互后的总价格做出动态改变。
最后的项目效果图:
创建index.html文件
让我们来创建一个简单的html模版文件。
<!DOCTYPE html> <html> <head lang="en"> <Meta charset="UTF-8"> <title>React and ES6 Part 2</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.2/css/foundation.min.css"> </head> <body> <div class="root"></div> <script src="dist/bundle.js"></script> </body> </html>
注意我们已经通过cdn
添加了Foundation CSS框架服务.它可以让我们微应用看起来很漂亮。同时,class为root的div将是我们应用加载的地方。
Gulpfile.js
创建gulpfile.js
文件,拷贝下面的代码到gulpfile.js
文件中。
var gulp = require('gulp'); var browserify = require('browserify'); var babelify = require('babelify'); var source = require('vinyl-source-stream'); gulp.task('build',function () { return browserify({entries: './app.jsx',extensions: ['.jsx'],debug: true}) .transform('babelify',{presets: ['es2015','react']}) .bundle() .on('error',function(err) { console.error(err); this.emit('end'); }) .pipe(source('bundle.js')) .pipe(gulp.dest('dist')); }); gulp.task('watch',['build'],function () { gulp.watch('*.jsx',['build']); }); gulp.task('default',['watch']);
package.json
运行
npm install --save react react-dom
,这将安装react
和react-dom
到你的node_modules
文件夹并且作为依赖库保存到package.json
文件中。运行
npm install --save-dev gulp browserify babelify vinyl-source-stream babel-preset-es2015 babel-preset-react
,这将安装更多的依赖到你的node_modules
文件夹。
Main application React Component
创建app.jsx
:
import React from 'react'; import ReactDOM from 'react-dom'; import CartItem from './cartItem'; const order = { title: 'Fresh fruits package',image: 'http://images.all-free-download.com/images/graphiclarge/citrus_fruit_184416.jpg',initialQty: 3,price: 8 }; ReactDOM.render( <CartItem title={order.title} image={order.image} initialQty={order.initialQty} price={order.price}/>,document.querySelector('.root') );
上面的代码做了什么:
Lines 1-2. 我们导入 React / ReactDOM 库。
Line 3. 导入我们马上要创建的
CartItem
组建。Lines 5-10. 给
CartItem
组建设置相关属性(属性包括 item title,image,initial quantity and price).Lines 12-18. 加载CartItem组建到一个CSS类为root的DOM元素上。
CartItem React Component 架构
import React from 'react'; export default class CartItem extends React.Component { constructor(props) { super(props); this.state = { qty: props.initialQty,total: 0 }; } componentWillMount() { this.recalculateTotal(); } increaseQty() { this.setState({qty: this.state.qty + 1},this.recalculateTotal); } decreaseQty() { let newQty = this.state.qty > 0 ? this.state.qty - 1 : 0; this.setState({qty: newQty},this.recalculateTotal); } recalculateTotal() { this.setState({total: this.state.qty * this.props.price}); } }
代码解释:
Lines 4-10. 这是ES6中React类的构造函数。super(props)这句代码是先处理父类的props,这句代码必不可少。下面的状态机变量的设置相当于ES5中
getInitialState()
方法状态机变量的初始化,我们通过this.state
来给状态机变量设置初始值。个人意见,我比较喜欢ES6中构造函数的写法。Lines 11-13.
componentWillMount()
是生命周期中的方法,在这个方法里面我们通过recalculateTotal()
方法对总价格做了计算。Lines 14-20. 给用户提供增加和减少的交互方法。当用户点击相应的按钮(如前面的效果图所示)时,这两个方法会被调用。
CartItem render method
export default class CartItem extends React.Component { // prevIoUs code we wrote here render() { return ( <article className="row large-4"> <figure className="text-center"> <p> <img src={this.props.image}/> </p> <figcaption> <h2>{this.props.title}</h2> </figcaption> </figure> <p className="large-4 column"><strong>Quantity: {this.state.qty}</strong></p> <p className="large-4 column"> <button onClick={this.increaseQty.bind(this)} className="button success">+</button> <button onClick={this.decreaseQty.bind(this)} className="button alert">-</button> </p> <p className="large-4 column"><strong>Price per item:</strong> ${this.props.price}</p> <h3 className="large-12 column text-center"> Total: ${this.state.total} </h3> </article> ) } }
这里我们只是使用JSX+组建,再加上一些基础的CSS输出漂亮的标签。
不要担心{this.increaseQty.bind(this)}
这句代码的使用,下一小结中我们将会详细讲解,现在你需要相信我,这句代码会调用CartItem
类中的increaseQty()
方法即可。
因此,到现在我们已经有了和用户交互的漂亮的应用了,它向我们展示了如何使用ECMAScript6
来编写React 组建。就我个人而言,我很喜欢ES6带来的新的语法。
到现在我们还没有完成。在完成这篇文章之前,我们将再看看ES6中其它的一些新的特性。
Default Props and Prop Types for ES6 React classes
想象我们想要为CartItem组建添加一些验证和默认值。
static get defaultProps() { return { title: 'Undefined Product',price: 100,initialQty: 0 } } static propTypes = { title: React.PropTypes.string.isrequired,price: React.PropTypes.number.isrequired,initialQty: React.PropTypes.number }
PS: 也可以将上面的代码删除,在cartItem
里面非CartItem
类的内部添加如下代码:
CartItem.defaultProps = { title: 'Undefined Product',initialQty: 0 } CartItem.propTypes = { title: React.PropTypes.string.isrequired,initialQty: React.PropTypes.number }
就我个人而言,比较喜欢第一种写法,它不会破坏类的封装性。
添加上面的代码后,如果你给title
属性添加numeric
类型的值,将在控制台出现警告,这就是React属性验证的功能。
Bringing ES7 into the project
你可能会问一个合理的问题,为什么ES6还没有定稿,在你的标题中为什么出现ES7呢?
我会告诉你,让我们展望未来。我们开始使用non-breaking、property initializers和decorators的新特性。
即使ES7还处于非常早期的阶段,在Babel中已经实现了部分的建议性的新特性。这些实验性的新特性是从ES5到ES7令人惊叹的新的特性的转变。
首先,通过npm install --save-dev babel-preset-stage-0
命令安装缺失的npm module
。
其次,为了在我们项目中能够使用新的语法,我们需要在gulpfile.js
文件的第8
行做一些改变,代码如下:
.transform('babelify',{presets: ['react','es2015','stage-0']})
你可以从GitHub repository直接拷贝gulpfile.js
完整的代码。
ES7 React 组建属性初始化/默认值/类型
Inside class add this right above :
在CartItem
中将下面的代码:
static get defaultProps() { return { title: 'Undefined Product',initialQty: React.PropTypes.number }
替换成下面的代码:
static propTypes = { title: React.PropTypes.string.isrequired,initialQty: React.PropTypes.number }; static defaultProps = { title: 'Undefined Product',initialQty: 0 };
ES7 Property Initialiazers for initial state of React component
export default class CartItem extends React.Component { // .. constructor starts here state = { qty: this.props.initialQty,total: 0 }; // .. some code here
最后你需要检查一下cartItem.jsx
文件里面完整的代码:
import React from 'react'; export default class CartItem extends React.Component { constructor(props) { super(props); } state = { qty: this.props.initialQty,total: 0 }; static propTypes = { title: React.PropTypes.string.isrequired,initialQty: React.PropTypes.number }; static defaultProps = { title: 'Undefined Product',initialQty: 0 }; componentWillMount() { this.recalculateTotal(); } increaseQty() { this.setState({qty: this.state.qty + 1},this.recalculateTotal); } recalculateTotal() { this.setState({total: this.state.qty * this.props.price}); } render() { return ( <article className="row large-4"> <figure className="text-center"> <p> <img src={this.props.image}/> </p> <figcaption> <h2>{this.props.title}</h2> </figcaption> </figure> <p className="large-4 column"><strong>Quantity: {this.state.qty}</strong></p> <p className="large-4 column"> <button onClick={this.increaseQty.bind(this)} className="button success">+</button> <button onClick={this.decreaseQty.bind(this)} className="button alert">-</button> </p> <p className="large-4 column"><strong>Price per item:</strong> ${this.props.price}</p> <h3 className="large-12 column text-center"> Total: ${this.state.total} </h3> </article> ) } }
结论
在这一节中,我们熟练掌握了ES6和ES7 React组建属性的初始化,类型绑定。
下一小结,我们继续研究React+ES6系列教程。
参考文档
扫码申请加入全栈部落 |
---|