最近周末有时间,想把加入前端一来一年时间对于react以及redux的理解记录下来,没有什么比一个产品更有说服力,在这里搭建一个简单的框架,供刚加入前端准备学习react的小白作为入门学习。
项目代码,稍后会有升级。
首先使用create-react-app要初始化一个脚手架。

然后安装一个依赖包yarn add babel-plugin-transform-decorators-legacy -D,这种写法会将依赖包加载到package.jsondevDependencies中,而不是dependencies第一个是只用于开发环境,第二个要用于生产环境。所以生产环境中用不到的包就可以直接添加到第一个里面就好了。

下面是一共需要安装的包。我会尽量说一下每个的作用,尽量少的引入包,让系统更轻。
###依赖包

yarn add antd安装antd

yarn add babel-plugin-transform-decorators-legacy -D

yarn add babel-preset-env -D .babelrc中的配置需要

yarn add babel-plugin-transform-runtime -D

yarn add babel-plugin-import -D 配置后,可以引入模块,可以参考官网

yarn add babel-preset-stage-0 -D

yarn add babel-cli -D这个好像不是必须加的,我移除了之后代码也可以运行。
yarn add react-router-dom

安装了依赖包之后,在根目录下面新建一个.babelrc来覆盖一些eslint的默认操作。
###新建.babelrc文件

{
    "presets": [
        "env",   env参数囊括了es2015,2016,2017只写这一个就够了
        "react",   这个参数一定要加,解析react的
        "stage-0" 这是babel规范,没有这个,我知道的有import,export使用会发生异常。
    ],
    "plugins": [
        [
            "transform-runtime",  这个插件,是添加一个小垫片,拒绝把常用函数添加到每个引用文件,避免重复
            {
                "helpers": false,
                "polyfill": false,
                "regenerator": true
            }
        ],
        "transform-decorators-legacy",  可以使用装饰器
        [  
            "import",          这个是antd官网推荐的使用方式,不然antd无法正常模块按需引用
            {
                "libraryName": "antd",
                "libraryDirectory": "es",
                "style": "css"
            }
        ]
    ]
}

这样可以保证编译的时候不报错了,但是你会发现被装饰器装饰的方法或者类下面会有小红线,这样可以在根目录下面新建jsconfig.json ###新建jsconfig.json文件

{
    "compilerOptions": {
        "experimentalDecorators": true,
        "target": "es2017"
    },
    "exclude": [
        "node_modules",
        "dist"
    ],
    "include": [
        "src",
        "env",
        "static"
    ]
}

总结上面这套流程可是费了不少功夫的,基本上是报错一个加一个,有的还找了半天错误在哪里,我居然还天真的以为create-react-app默认引用的包有antd,一个莫名其妙的错误找到最后居然是么有引入antd…
然后项目中需要react-redux 以及相关资源包。

"react-redux": "^5.0.6",
    "react-router-redux": "^4.0.8",
    "redux": "^3.7.2",
    "redux-actions": "^2.2.1",
    "redux-devtools-extension": "^2.13.2",
    "redux-thunk": "^2.2.0",

以上就是所有的准备工作了,准备好之后就要开始干活了。提到redux,如果大家不是很清楚,可以参考这篇文章,这真是我读到redux入门最好的文章了。
然后,就要进入实战环节了,在src目录下新建Action,components,containers来存储网络请求/Action,组件以及页面容器。在Action目录下新建createFetchAction.js作为访问网络的工具类,感兴趣的可以参考我的代码,我做了很详细的注释。
然后看一下store里面的代码:

const composeEnhancers = composeWithDevTools({
    //为了redux dev tools 服务
	// options like actionSanitizer, stateSanitizer
});
const middleware = [thunk];   // 这里是加载的一个中间件,可以加入很多,也为实现功能提供了无限的扩展性和可能性。
const store = createStore(combineReducers({routering: routerReducer}),  // 这个是引入的默认路由
    {},
    composeEnhancers(
        applyMiddleware(...middleware)
    ));
    
store.appReducers = {}  //全局的reducer
export const updateReducer = (key,reducer) => {
    if (Object.hasOwnProperty.call(store.appReducers, key)) return;
    store.appReducers[key] = reducer; // 每调用方法,可以将当前reducer加入全局
    // 其中的routerReducer是引入的初始化状态,也可以自己本地定义一个,在项目启动的时候就加入进来。
    store.replaceReducer(combineReducers({routering: routerReducer ,...store.appReducers}));
}
export default store;

项目的入口就是App.js文件,在这个文件中要配置路由以及路由对应的要显示的组件。

async  componentDidMount() {
    	Object.keys(reducers).forEach(name=>{
	    	// 在项目运行开始引入全部的reducer,然后加载时全部添加到`appReducers`里面
			updateReducer(name,reducers[name]); 
		});
	}
	render() {
		return (
			<Provider store={store}> 通过provider全局引入 store
				<BrowserRouter> 监听路由变化,并且加载对应组件
					<div style={{ width: '100%'}}>
						<Switch> Switch中的组件只能同时加载一个,顺序从上向下依次判断
							<Route path='/login' component={APP.LOGIN} />
							<Route path='/page404' render={() => {
								return (<h1>404 not found</h1>); // 这个组件过于简单,不新建文件了
							}} />
							<Redirect from='/' to='/page404' /> 重定向url
						</Switch>
					</div>
				</BrowserRouter>
			</Provider>
		);
	}

上面看到了,引入了Login这个组件,所以,在containers中新建Login.js文件.这个文件中会有登录验证的代码,暂时先不用管,我自己在本地搭建了一套Node的服务器,这个过程稍后也会加入分项列表。
#####再讨论一下Action的创建:
导出的index.js我就不多说了,这里介绍一下login文件夹下面的两个文件:

index.js
[combineActions(...getValuesFromObj(actions))]:(state = {},action)=>{
    return {
        ...state,...loginReducer(state,action) 这个...loginReducer,包含了多个从login.js中导出的reducer,每次当从页面中调用同名的action,
        比如setLoginData就会对应到这边来,以调用setLoginData来说,就是返回当前的state和loginData这两个数据的合体。
        这也是redux的核心里面,每次只更新,不能修改。
    }
}
login.js
const setLoginData = createAction('登录action');
const setLoginData1 = createAction('测试action');
const getAllEmployeeOk =  createAction('得到所有用户');
//一下对本地的请求可以换成任何url,比如baidu等均可,调用成功之后会回调getAllEmployeeOK函数。
const getAllEmployee  = createAsyncAction('http://localhost:8080/tabledata','GET',getAllEmployeeOk);

export const actions = {
    setLoginData,
    setLoginData1,
    getAllEmployee,
    getAllEmployeeOk 
}
export default handleActions({
    [setLoginData]: (state, {payload}) => ({
        loginData: payload
    }),
    [setLoginData1]: (state, {payload}) => ({
        loginData1: payload
    }),
    [getAllEmployeeOk]:(state,{payload})=>{
        return{
            employee:payload
        }
    }
},{});

login.js是对函数进行定义并导出,index.js中的操作是对login.js导出的内容进一步拆分,比如login算一个,未创建的register也可以算一个。在这里利用handleActions导出了诸多actions,这里的操作相当于注册这些reducer,当在页面方法中调用该action的时候,就可以根据规则更新redux的值了【最终一个模块的是汇总到updateReducer中】。
那个getValuesFormObj本来官方提供的有ActionsMap提供使用,但是为了说明他的本质,我就自己写了个阉割版的。
这个组件就只是展示了用户名和密码的输入框,然后在componentDidMount和点击事件中对redux做了个小测试。

@connect(
    state => {  这个state是全局的state,也可以在这里进行一下过滤表示只想要一部分的store值。
        return state;  这里返回的state会被映射到Props,可以通过this.props访问到
    } 如果想访问一部分,可以 return state.login {如果有这个分支的话}
    ,
    dispatch => ({
        //bindActionCreators这个函数为通过createAction方式建立的action提供dispatch参数,要知道没有dispatch就无法调用action
        同时connect帮助我们,将所有的action[还有网络访问相关的]加入props中
        bindedactions: bindActionCreators({ ...actions }, dispatch),
    })
)

componentDidMount () {
    const { setLoginData1 } = this.props.bindedactions;
    setLoginData1('123'); 调用之后,redux的store中就多了一条记录,可以打印this.props确认。
}

antd 组件 描述description_前端


到这里,一个基本的架构就搭建起来了,后续我会把后台代码也一并总结上来。

如果有疑问或者认为我写的有缺陷的,希望能提出宝贵的建议,不胜感激!