react学习总结-梳理

react学习总结–梳理

说明

使用 React 开发项目,仅仅靠它自己是不够的,需要花费更多的时间去学习与之配套的技术,像是:Redux、React-Router 、Gulp(或者是Webpack)、Browserify、ES6 等,下面是我梳理的使用React的简易流程

技术要求

  • 开发工具 : Atom
  • 构建工具 : Gulp + Browserify
  • ES6 语法编写 JS,Sass 编写 CSS
  • React + React-Router + Redux 创建应用
  • jshint 语法检查

版本信息

  • “gulp” : “^3.9.1”
  • “browserify” : “^13.1.1”
  • “react” : “^15.4.1”
  • “react-router” : “^3.0.0”
  • “redux” : “^3.6.0”
  • “react-redux” : “^4.4.6”
  • “react-router-redux” : “^4.0.7”
  • “react-tap-event-plugin” : “^2.0.1”

项目结构

目录结构

目录设计的很烂,但是这里为了说明,还是粘出来了


    app
    |---_data
    |    |---a.json                         //存放json文件
    |
    |---css
    |    |---index.css                      //编译后的css文件
    |
    |---img
    |    |---a文件夹
    |    |---b文件夹 
    |           |---a.jpg                   //根据容器组件分成各个目录
    |
    |---js
    |    |---actions
    |    |      |---action.js               //action文件,在一个目录下
    |    | 
    |    |---components
    |    |      |---AppCom.js               //App容器组件下的唯一子组件,负责其他子组件的嵌套
    |    |      |---AppCom文件夹             //AppCom需要嵌套的子组件
    |    |      |---common文件夹             //共有的组件
    |    |
    |    |---containers
    |    |      |---App.js                  //根容器组件,router的最外层
    |    |      |---DevTools.js             //调试用组件
    |    |      |---Home.js                 //普通容器组件
    |    |
    |    |---reducers
    |    |      |---reducer.js              //根reducer,需要引入其他所有reducer合并
    |    |      |---appReducer.js           //普通的reducer分支
    |    |
    |    |---store
    |    |      |---configureStore.js       //store构造器
    |    |---constants.js                   //静态常量
    |    |---index.js                       //项目入口文件
    |    |---routes.js                      //路由配置文件
    |
    |---scss
    |    |---_common.scss                   //通用样式文件
    |    |---index.scss                     //主样式文件
    |    |---home.scss                      //一般容器组件的样式文件
    |    |---commoncom.scss                 //通用组件的样式文件
    |
    |---index.html                          //项目主页

注: 查资料的时候,看到有的文章上建议将一个组件的所有文件都放在一个目录,也可以试试看

    |---components             
    |      |---AppCom                       //文件夹             
    |      |        |---index.js            //组件
    |      |        |---app.scss            //样式
    |      |        |---AppCom1.js          //子组件
    |      |        |---AppCom2.js          //子组件
    |      |        |---AppCom3.js          //子组件
1.搭建开发环境

前端资源依赖于 npm ,所以请先确保已经安装了 node

所有资源都是通过 npm install 安装的,Mac 需要 sudo npm install

然后就是安装 Gulp 和 Browserify 了,详细的可以看 上一篇 文章
不要忘记编写.jshintrc,要不然命令行上就都是警告了

gulp 环境搭建好后,就需要下载 React 相关的文件了,我安装了这些:

  • es6-promise fetch-jsonp isomorphic-fetch :这三个用来代替ajax请求数据
  • react react-dom react-tap-event-plugin : React 基本使用
  • react-router react-router-redux : 解决 React 应用中的路由
  • redux react-redux :使用redux,管理应用状态
  • redux-thunk : 异步的中间件
  • redux-devtools redux-devtools-dock-monitor redux-devtools-log-monitor : redux 调试插件

我用 Atom 做编辑器,为了更好的开发也要安装相应的插件,安装了很多,这里列举一部分

  • emmet atom-ctags : 都是增强代码补全的
  • language-babel react redux-snippets :增强代码高亮等
  • linter linter-jshint jshint :js语法检查,安装jshint时,需要注意,可以设置支持jsx语法
  • autocomplete-paths autocomplete-modules :自动补全路径,还有模块
  • atom-ternjs : 代码支持增强
  • split-diff :检查文件差异
  • 还有很多插件…
2.开始写应用
2.1 app / index.html (主页)

与一般页面的内容一样

    <!DOCTYPE html>
    <html lang="en">

    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="ie=edge,chrome=1">
        <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0"> 
        <title>标题</title>
        <!-- 引入编译后的css文件 -->
        <link rel="stylesheet" href="./css/app.css">
    </head>

    <body>
        <!-- 项目的容器 -->
        <div id="app"></div>
        <!-- 引入编译后的js文件 -->
        <script src="./js/bundle.js"></script>
    </body>

    </html>
2.2 app / js / index.js (入口文件)

在这里,确定容器(#app),将组件导入容器中,创建 store ,通过,使得所有子组件都可以拿到 store 中的 state。

    import React from 'react';
    import ReactDOM from 'react-dom';
    import {Provider} from 'react-redux';
    import {Router, browserHistory} from 'react-router';
    import {syncHistoryWithStore} from 'react-router-redux';    //结合store同步导航事件
    import configureStore from './store/configureStore';        //引入store生成器
    import reducer from './reducers/reducer';                   //引入合并后的reducer
    import routes from './routes.js';                           //引入路由配置文件
    // import DevTools from './containers/DevTools';            //引入redux调试插件
    import injectTapEventPlugin from 'react-tap-event-plugin';  //引入提供Touch事件的库

    injectTapEventPlugin();                                     //初始化Touch事件


    // 给增强后的store传入reducer
    const store = configureStore(reducer);
    // 创建一个增强版的history来结合store同步导航事件
    const browhistory = syncHistoryWithStore(browserHistory, store);

    ReactDOM.render((
        <Provider store={store}>
            <div>
                <Router history={browhistory} routes={routes} />
                {/* <DevTools /> */}
            </div>
        </Provider>
    ), document.getElementById('app'));
2.3 app / js / routes.js (路由配置文件)

引入 路由配置文件

    import React from 'react';
    import {Route, IndexRoute} from 'react-router';
    // 引入容器组件
    import App from './containers/App';
    import Home from './containers/Home';
    import Temp from './containers/Temp';
    export default (
        <Route path="/" component={App}>
            <IndexRoute component={Home} />
            <Route path="home" component={Home}/>
            <Route path="temp" component={Temp}/>
        </Route>
    );
2.4 app / js / constants.js (静态常量)

LOGOUT这种完全大写的常量是用来做 action.type的, 看了很多资料,都建议将这些字符串用常量的形式保存,避免更改出错

    export const LOGOUT = "LOGOUT";

    export const LOGIN = "LOGIN";
2.5 app / js / store / configureStore.js ( store 生成器)

在这里是把createStore,增强了一下,加入了中间件便于处理异步操作,

compose,是redux的方法,意思是:按照顺序组合多个函数,这是函数式编程的方法,用来把多个store增强器依次执行

    import { compose, createStore, applyMiddleware } from 'redux';
    import thunk from 'redux-thunk';                // 引入thunk 中间件,处理异步操作
    // import createLogger from 'redux-logger';     // 利用 redux-logger打印日志
    import DevTools from '../containers/DevTools';  // 引入DevTools调试组件

    // const loggerMiddleware = createLogger();     // 调用日志打印方法

    const middleware = [thunk];

    /*
       调用 applyMiddleware ,使用 middleware 来增强 createStore
    */
    const configureStore = compose(
       applyMiddleware(...middleware),
       DevTools.instrument()
    )(createStore);

    export default configureStore;
2.6 app / js / reducers / reducer.js (合并后的reducer)

所有分支的reducer,都会被引入到这里,合并后作为创建 store 的参数

    // 引入分支reducer
    import rootReducer from './rootReducer';   
    import appReducer from './appReducer';
    import {combineReducers} from 'redux';
    import {routerReducer} from 'react-router-redux'; // 导入routerReducer,将url信息合并到store中

    // 合并到主reducer
    const reducer = combineReducers({
       rootReducer,
       appReducer,
       routing: routerReducer,
    });

    export default reducer;
2.7 app / js / reducers / appReducer.js (分支reducer)

一般是每个容器组件,建立一个reducer分支,管理这个容器组件内的state

    const initialState = {          //初始化数据
        isLogin : true
        userId : 99990002
    };
    /* jshint -W138 */
    export default function rootReducer(state = initialState, action) {
        switch (action.type) {      //根据action的type属性,确定执行什么操作
             case "LOGOUT":
                // 退出登录
                return Object.assign({}, state, {
                   isLogin : false,
                   userId : null
                });
             case "LOGIN":
                // 登录
                let {isLogin,userId} = action.msg;
                return Object.assign({}, state, {
                   isLogin : isLogin,
                   userId : userId
                });
             case "ERROR_LOGIN":
                console.log(action.msg);
                return state;
            default:
                return state;
        }
    }
2.8 app / js / actions / appAction.js (app组件的Action)

同样更多容器组件定义相应的action,使用fetch代替ajax获取数据,也可以使用其他类库:axios,jQuery等。

    // 导入type类型常量
    import {LOGOUT,LOGIN,ERROR_LOGIN} from '../constants.js';
    require('es6-promise').polyfill();    //使支持fetch
    require('isomorphic-fetch');

    //退出登录action
    export function logout() {
       return type:LOGOUT};
    }

    // 登录action
    export function login(userId) {
       // 登录后或者注册后获取用户userId,根据userId获取余额信息
       console.log('root get userId = '+userId);
       return dispatch => {
          fetch('../../_data/a.json').then(resp => {
             if (resp.status === 200)
                 return resp.json();
             throw new Error('false of json');
          }).then(json => {
             dispatch({type:LOGIN,msg : {userId:userId,isLogin:true}});
          }).catch(error => {
             dispatch({type:ERROR_LOGIN,msg : error});
          });
       };
    }
2.9 app / js / containers / App.js (容器组件)

容器组件只负责处理state,和action,并将其传入子组件

    import React, {Component} from 'react';
    import AppCom from '../components/AppCom';
    import {connect} from 'react-redux';
    import { bindActionCreators } from 'redux';
    import{openDatePicker,closeDatePicker,closeAndChangeDate} from '../actions/appAction'; //引入action

    class App extends Component {
        constructor(props) {
            super(props);
        }
        render() {
            return (
                <AppCom {...this.props}>{this.props.children}</AppCom>
            );
        }
    }

    // 绑定action,将其传入子组件中,子组件通过this.props.fn调用函数,就会触发action
    const mapDispatchToProps = dispatch => {
       return {
          openDatePicker : bindActionCreators(openDatePicker, dispatch),
          closeDatePicker : bindActionCreators(closeDatePicker, dispatch),
          closeAndChangeDate : bindActionCreators(closeAndChangeDate, dispatch)
       };
    };

    export default connect(
        // 转换state,将store中的state,按照需求,传入子组件,子组件通过this.props使用
       state => ({
          rootState:state.rootReducer,
          appState: state.appReducer,
          appRoute : state.routing}),
       mapDispatchToProps       //绑定action多的时候就分离出去,少的话直接写在里边
    )(App);
2.10 app / js / components / AppCom.js (UI组件)

展示组件,负责样式展示,数据都是从容器组件中得到

    import React,{ Component } from 'react';
    import BaiduMap from './HomeCom/BaiduMap';
    import PullupDetails from './HomeCom/PullupDetails';

    export default class HomeCom extends Component {
       constructor(props) {
          super(props);
          this.handleClick = this.handleClick.bind(this);
       }
       handleClick() {
        this.props.close();             //调用父组件传递过来的事件处理函数,触发action
       }
       render() {
          return (
             <div id="home-container">
                <div className="home-nav" onClick={this.handleClick}>
                    {this.props.appState.userId}    // 使用父组件传递过来的store中的state
                </div>
                {this.props.children}
             </div>
          );
       }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值