做过React
这一块开发的前端同学们可能会有类似的感受,为了能够启动一个React
项目,我们可能不仅仅只需要React
这一组件,相应的我们需要去了解Webpack
的配置,Babel
的预编译,Redux
的数据管理,React-Router
的路由设计,这一套走下来可能就会非常麻烦了,学习成本也大大上升。
为此,本着简即是美的想法,受到react-redux-starter-kit这一项目的启发,于是我在它的基础上再做了一些事情,使得运行React
栈项目的难度大大降低。
如果你是一名初入React
的新同学,你可以用此脚手架快速的体验React
的美,熟悉ES6
的语法,而不用担心配置的细节。
如果你是一名大牛,因为我本身技术水平有限,你可以在阅读组织结构后,针对具体细节给予我反馈和改进意见。:blush:
你可以看到这个GIF
图,想在项目里增加一个新的页面(其实就是一个新的路由),简单地执行一句redux g container [ContainerName]
,一切便都关联好了,其实在代码层面,得益于Webpack
的热部署,使得新增加的代码会实时生效。
git clone https://github.com/luckyjing/react-redux-scaffold.git <your_project_name>
cd <your_project_name>
npm install # 当然,用cnpm更好
npm run dev
默认启动在3005
端口,与此同时,还在8005
端口上启动了mock
服务,方便大家进行开发,有关mock
的内容,可以查看此文章端口可以在package.json
里面进行配置。
现在,你可以访问http://localhost:3005
了
创建新页面我利用了redux-cli这个工具,你可以点进去查看它的设计理念,目前,我们只用到了它里面的一点点知识。
需要有一点注意的是:页面名称一定要大写字母开头,比如Test。
😧 如果提示找不到redux
,那么先执行一下npm install redux-cli -g
redux g container Test
😳背后发生了什么?
它其实就是将blueprints
下的container
渲染后复制到src
文件里,你可以在src
里看到新增的文件,位于scripts/containers
和scripts/redux
里。
.
├── README.md
├── blueprints # 这里便是我们执行redux命令时复制文件的源头
│ ├── container
│ └── subContainer
├── build
├── doc
├── mock2easy
├── node_modules
├── package.json
├── src
│ ├── scripts
│ ├── containers # 【主要操作区域】存放每个页面,页面相关的代码全部放置在页面文件夹内
│ ├── global # 公共的redux部分,比如loading action,为多个container所共用
│ ├── layouts # 页面布局根组件
│ ├── redux # 【主要操作区域】路由配置信息,各个页面的redux逻辑应存放于此
│ ├── routes # 路由入口文件,已经配置好
│ ├── store # store配置文件,无需配置
│ └── index.html # 主入口HTML
└── webpack.config.js
创建好页面后,进入到container
里的Test
,你会看到已经生成好了以下代码。
import React, {Component, PropTypes} from 'react';
export default class TestContainer extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
const {actions,state} = this.props;
actions.demo("Hello,这儿发起了一个actions,你可以直接发起你在redux定义的actions");
actions.getInfo();
}
render() {
return (
<div>
// ...
{this.props.children}
</div>
);
}
}
快速理解
render
方法里放置HTML
,必须至少return
一个<div></div>
,且所有内容的最外层必须为单组件,不允许下述情况。// 不允许 render(){ return ( <div>1</div> <div>2</div> ) } // 允许 render(){ return ( <div> <div>1</div> <div>2</div> </div> ) }
- 页面仅仅是数据的呈现者,所有的数据从
this.props.state
中拿到,这个state
存储了这个页面的全部信息,你可以console.log
仔细查看 - 页面会触发交互,触发事件,这些事件产生的内容,全部交给
redux
,叫做action
,比如action.renderPage
,action.loadingData
let { a,b } = {a:'a',b:'b'}
,这叫做解构,等价于let a = obj.a
和let b = obj.b
,写在一起比较简单。- 导入:页面尽可能拆成一个个组件,要什么
import
什么就可以了。 - 导出
// 形式一 export default A && import A from '../module' // 形式二 export function A(){} export function B(){} import {A,B} from '../module'
❓ 可能你会发问,redux中的数据怎么传递到页面里?放心,我已经做好了绑定了,在
redux
目录下里有对应的Test
,进去阅读代码,我已经将此state
绑定到页面上了,每个页面的this.props.state
就能拿到redux
对应的state
,action
同理。
- 在
redux/Test/redux
下新建一套constant
,reducer
,action
,然后试着在TestContainer
里面通过this.props.action
直接获取吧。
执行构建命令,build
文件夹会存放构建好的css
和js
文件。
npm run build
恭喜你,你现在可以愉快的开发了~ 🤔
在React中,每个组件都拥有自己的生命周期,理解起来也非常直观,每个组件都会经历即将挂载,挂载完成,卸载等等条件。我们想实现在页面加载完成后请求数据,只需要在挂载完成
这个状态,注入自己的逻辑即可,类似于在jQuery
中经常写的$(document).ready()
方法,我们在声明一个React Class
时,完
善componentDidMount
方法即可,可以在这里调用包含Ajax请求的action
,去获取数据。
componentDidMount() {
const {actions} = this.props;
actions.renderTable();
}
我们首先需要准备好一个一般的action
,它接收接口返回的数据,然后传递给Reducer
,如下所示:
export function renderPage(data){
return {
type:TYPE.RENDER,
data
}
}
异步调用Ajax
的思路一般是使用一个可以返回Promise
对象的action
,随后在这个Promise
完成时调用(dispatch
方法)已经准备好的一般action
,如下所示:
export function renderTable(params) {
return (dispatch, getState) => {
let state = getState().Table;
return $.ajax({
url: "/test/table.json",
type: 'get',
data: Object.assign({
type: state.tab
}, params,),
dataType: 'json'
}).done(res=> {
// done方法里写一个函数,这个函数将在异步请求完成时被触发,随后调用一般的action即可
dispatch(renderPage(res));
});
}
}
- dispatch : Function , 接受action对象,并发送给reducer
- getState : Function , 执行后返回全部的state,你可以
console.log
一下查看细节,简单来说,你需要用state.*
去获得你当前Container
的state
,getState().containerName
,containerName就是你自己起的页面名,如TableContainer那么就是Table,不包括后面的Container。
如果两个页面都需要用loading
这个action type
的,在本脚手架里是不会有问题的,我在最终引入action type
的地方,都加了namespace
,也就是说,最终呈现出来的action type
为A/loading
和B/loading
,所以是不会产生冲突的。