React & Redux Tutorial Series (1): Contruct Scaffold and ES6 Basic

本系列将介绍如果完成一个react + redux的应用,本篇将介绍如何构建一个react,redux项目,以及其他必备的基础知识

React Overview

facebook 发布了革命性的react框架,作为对前端MV* 开发模式的反思,react为前端提供了组件式的开发方式,基于Virtual DOM的架构,相比于angulardirty watch大大增加的性能,利用优化的diff算法,可以接近O(n)的效率

Features:

  • JSX
  • Virtual DOM
  • Data Flow

对于典型的前端开发者,刚上手react的时候是略微痛苦的,react接管了所有的DOM操作,使得我们无法利用jQuery来操作DOM,这对于我们来说是一种思维模式的改变

我们将在使用redux的时候再介绍它

Environment Setup

安装react,我们推荐使用nvm来安装nodejs

$ curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.0/install.sh | bash
# 安装最新版本
$ nvm ls-remote
# 安装node 5.9.0
$ nvm install v5.9.0
# 设置node为默认值
$ nvm alias default node
$ npm -v 
3.7.x
$ node -v
5.9.x

我们需要一些准备工作,开发者应该对一下工具有一定的了解

  • webpack: 用于编译,打包等
  • yeoman: 用于生成项目脚手架

并且对ES6语法有一定了解,不了解的可以参考

Install

我们选择react-webpack-redux这个生成器来生成代码

$ npm install -g webpack
$ npm install -g yo
$ npm install -g generator-react-webpack-redux

# 新建一个目录,并进入目录
mkdir my-new-project && cd my-new-project

# 启动生成器
yo react-webpack-redux
# 你需要选择你选用的技术栈

安装完成之后,主要目录结构如下,省略一些次要的

- my-new-project
  - src: 
    index.html 首页
    index.js 程序入口
    - components: react组件
    - reducers: reducer处理器
    - actions: actions动作
    - stores: stores存储容器
    - containers: redux组件容器
  - cfg: webpack配置
  server.js
  webpack.config.js

之后运行npm start,就能看到一个开始页,我们的准备工作也就完成了

Appendix

在这里有必要提前介绍一些有关的JS基础,以后的篇幅中如果没有特殊情况,将不再做语法上的介绍

模块化

模块的导入,有以下几种导入方式:

// 如果模块使用 module.exports = xxx 的形式,可以用第一种
import React from 'react'
// 如果模块导出的是命名的方式,可以用第二种
import { MAKE_CHOICE,CLEAR_ANS } from '../constants/ActionTypes'
// 如果模块有默认导出,可以使用 { default as xxx }的形式
import {default as Api,ERR_ANS,ERR_TK} from '../api/question'

模块的导出

// 直接导出命名函数,也可以是class,variable等
export function clearAns() {
	return {
		type: types.CLEAR_ANS
	}
}
// 默认模块导出
export default {
	getRandomQuestions(callback,timeout) {
		// code here
	}
}

面向对象

react里面组件都是通过继承Component来实现

// 以前的写法
var HelloMessage = React.createClass({
  render: function() {
    return <div>Hello {this.props.name}</div>;
  }
});
// 在ES6中,现在这么写
import { Component } from 'react'
class HelloMessage extends Component {
	render() {
		return <div>Hello {this.props.name}</div>;
	}
}

Arrow Functions and Assignment Destructuring 箭头函数和解构赋值

箭头函数=>CoffeeScript中也有,用于将外部作用域的this导入闭包

this.name = "name";
// 以前的写法
callSomeFunction("arg1",(function(_this){
	return function(arg) {
		// 通过注入的变量访问外部的this
		return _this.name + "arg";
	};
})(this));
// ES6
callSomeFunction("arg1",(arg) =>
		_this.name + "arg"; // implicit return when no brace around
);

react中往往这样用:

// 快速将boundActionCreators对象的每个key,value对赋值给<QuestionItem>组件的属性值
{questions.map(question => {
	let answer = answers[question.id] != undefined ? answers[question.id] : NO_ANS
	return <QuesionItem key={question.id} question={question} answer={answer} {...boundActionCreators} />
})}

常用函数

Object.prototype.assign: 常用与合并多个对象

// 先忽略Map..会把第三个参数覆盖到第二个参数上,然后合并到第一个参数
Object.assign({},state,Map([[action.question_id,action.answer_id]]).toObject())
// 记得assign是浅合并,即第二层以后的元素不会判断合并,之后取最新的值
// 我们期望得到{a:{b:1,c:3}},然而结果是{a: {c: 3}
// 我们可以用 KyleAMathews/deepmerge 这个库来做
Object.assign({a: {b: 0}}, {a: {b: 1, c: 2}}, {a: {c: 3}});

Function.prototype.bind: 绑定函数内部this以及指定参数

react中常用于指定回调函数的参数

// 技巧:在render的时候指定回调函数的参数,在使用map的时候特别有用
<input onChange={this.props.makeChoice.bind(this,this.props.qid,this.props.aid)} />

其他

let关键字:创建块作用域的变量

if (true) {
	let tmp = "a";
	console.log(tmp); // "a"
}
console.log(tmp); // undefined

const关键字:定义常量