react学习总结-梳理

来源:互联网 发布:one域名备案 编辑:程序博客网 时间:2024/05/21 22:35

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 0