此文是我在react项目搭建后对整个流程的一些环节进行整理和梳理,如有错误还请指正。
一般目录结构搭建好后,我们会首先在src中开始工作,因为大多数代码都是在src中完成的。
这里要说一个React项目的构建思想:
1 准备工作
快速构建React项目,我采用的是脚手架构建方式。
首先我们需要复制一个脚手架。将里面public、src文件夹下的文件全部删除。接着将需要的html粘进public文件夹下,当然也包括对应的css。
接下来就需要在src文件夹下建立一个index.js文件,用来将js文件中的信息传递到html里面;同时建立一个components文件夹,用来存放react组件;
index.js文件里面引入react和react-dom库,并引入主组件APP.js。将其传递到id为app的div标签里面。
比较全面的目录结构如下:
public
src
–api (一些公用方法,如ajax请求函数的定义)
–assets (公用的图片和less文件)
–components (UI组件)
–containers (容器组件,用于包装UI组件来传递方法和redux状态)
–redux (actions,action-types,reducers,store)
src下的index.js,是整个项目的入口文件。核心是用来将各个容器组件包装的UI组件以路由组件的形式渲染到真实DOM app中。
ReactDOM.render((
{/*不写path,默认匹配所有路径*/}
), document.getElementById('app'));
2 建立主组件
其实就是在src内写UI组件,根据不同的功能,将整个html里面的标签需要设计拆分成组件。
首先我们新建app.js文件。其实后面每个组件的套路都是一样的;通用代码如下。
const React,{Component} from “react”;
Class App extends Component{
render()
return(
这里面放html代码;
)
}
export default app;
基本上就是这样套路。上来就这么写就ok,不用多想。
3拆分组件
这时需要分析怎么拆分。根据功能点拆分,这个是最基本的。拆分完要细化拆分,功能里面有独立功能的拆分,有多条同样数据的拆分。比如一个ul下多个li的直接拆分。然后根据上面的套路建立对应的js文件夹。
完成后在App.js文件夹里面引入每个对应的组件,起初可以使用标签的方式进行添加,保证组件可以正常加载。到了后来都用路由组件包装一下。可以参考上文的入口文件的书写方式。
4.动态化修改
这一步骤是非常关键的,直接决定做好的功能是否可用。首先将静态的数据提取出来,需要保存的在本页面的state中保存好,如果有多个页面需要使用的数据或者交互的数据,这时就要引用redux,集中管理公用的状态。
下面我们引入一个业务场景,加入注册页面需要向服务器发送ajax请求。下面是围绕着redux的一套比较固定的逻辑。
1.定义接口和方法
因为要发送ajax请求,首先在api中定义好axios方法和针对于不同业务的请求函数。
注意:此处prefix为空,因为在开发环境中解决跨域问题可以使用proxy方法。
//定义注册的请求
export const reqRegister = data => ajax(`${prefix}/register`, data, 'POST');
2.完成store
import {createStore, applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import {composeWithDevTools} from 'redux-devtools-extension';
//引入reducers函数
import reducers from './reducers';
export default createStore(reducers, composeWithDevTools(applyMiddleware(thunk)));
3.actions和action-type
actions内部是创建action对象的工厂函数,actions内会定义一些同步函数,为了方便存储和reducers复用会存储到actions-types中。当涉及到异步函数时,我们会首先定义一个同步函数,然后在异步函数内调用同步函数。由于发送ajax请求是异步函数,所以采用如下写法。
//定义同步action creator
export const authSuccess = data => ({type: AUTH_SUCCESS, data});
export const authError = data => ({type: AUTH_ERROR, data});
需要定义两个同步方法,因为注册存在成功和失败两种情况,返回值不相同。
//定义异步action creator
export const register = ({username, password, rePassword, type}) => {
//表单验证
if (!username) {
//变成同步action creator
return authError({errMsg: '请输入用户名'});
} else if (!password) {
return authError({errMsg: '请输入密码'});
} else if (password !== rePassword) {
return authError({errMsg: '两次密码输入不一致'});
}
return dispatch => {
//做异步任务,发送ajax请求
reqRegister({username, password, type})
.then(({data}) => {
//请求成功~
if (data.code === 0) {
//注册成功~
//更新状态, 分发成功的action对象
dispatch(authSuccess(data.data));
} else {
//注册失败,更新状态,分发失败的action对象
dispatch(authError({errMsg: data.msg}));
}
})
.catch(err => {
//请求失败~
dispatch(authError({errMsg: '网络不稳定,请刷新试试~'}));
})
}
}
4.reducers
这里是真正更新store中状态的地方。这里定义了公用状态的初始化值和更新状态的方法。定义 方法时需要传两个参数,一个是previousState,另一个是action,通过对action.type的判断,就知道ajax请求失败了还是成功了,从而通过return不同的值来控制store中状态的不同。
//初始化状态的值
const initUserState = {
username: '',
type: '',
_id: '',
errMsg: '',
redirectTo: '',
header: '',
post: '',
salary: '',
info: '',
company: ''
};
function user(previousState = initUserState, action) {
switch (action.type) {
case AUTH_SUCCESS :
return {...action.data, redirectTo: getRedirectPath(action.data.type, action.data.header)};
case AUTH_ERROR :
return {...initUserState, ...action.data};
case UPDATE_USER_INFO :
return {...action.data, redirectTo: getRedirectPath(action.data.type, action.data.header)};
case RESET_USER_INFO :
return {...initUserState, ...action.data}
default :
return previousState;
}
}
当有多个函数时,可以使用合并reducers函数的方式
//默认暴露合并后的reducers函数
// {xxx: function xxx() {}, yyy: function yyy() {}}
export default combineReducers({
user,
userList,
chatMessages
})
至此“redux无脑一顿操作’’ 完成了请求方法的定义。现在只需要在注册页面中调用就好了,可是现在问题来了,这个方法怎么从actions传过去。
此处就用到了容器组件
import {connect} from 'react-redux';
//引入UI组件
import Register from '../components/register';
//引入action creators
import {register} from '../redux/actions';
//暴露容器组件
export default connect(
state => ({user: state.user}),
{register}
)(Register);
然后在组件中以props的方式拿到传递过来的redux状态和方法,就可以调用ajax请求如下。
register = async () => {
//收集表单数据
const {laoban, password, rePassword, username} = this.state;
//发送ajax
console.log(laoban, password, rePassword, username);
//调用容器组件传递的更新状态的方法
this.props.register({type: laoban ? 'laoban' : 'dashen', password, rePassword, username});
}
至此,一个react项目中比较简单的业务逻辑基本完成。整个项目中会出现各种问题和更优的写法,所以此套逻辑适用于一般业务逻辑场景,具体问题需要接待对待。
(想要看整个项目逻辑的可以看一下我的仓库,关于在线招聘的react项目。
https://github.com/tunshiyu/zhipin-client)