【第三部分】create-react-app 技术栈:react+react-router+redux+axios+es6+webpack

###配合TypeScript

第一种方式:创建项目的时候直接配置好TypeScript.

npx create-react-app my-app --typescript
#or
yarn create react-app my-app --typescript

第二种方式:为现有的React项目添加TypeScript

npm install --save typescript @types/node @types/react @types/react-dom @types/jest
# or
yarn add typescript @types/node @types/react @types/react-dom @types/jest

安装完成后,项目根目录下新建 tsconfig.json文件

{
  "compilerOptions": { // 编译选项
    "target": "es2016",  // 配置编译目标代码的版本标准
    "module": "commonjs",  // 配置编译目标使用的模块化标准
    "lib": ["es2016"]  
  }
}

ts和js分开

开发过程中我们肯定希望源代码和编译后的代码分开,加入以下两个配置选项
include : 需要编译的文件目录
outDir: 编译后的文件目录

{
  "compilerOptions": { // 编译选项
    "target": "es2016",  // 配置编译目标代码的版本标准
    "module": "commonjs",  // 配置编译目标使用的模块化标准
    "lib": ["es2016","dom"], // 配置环境
    "outDir": "./dist"
  },
  "include": ["./src"]
}

###Visual Studio Code配置React开发环境

## Visual Studio Code配置React开发环境

### React集成VSCode测试

第一步:
首先安装:[`Debugger for Chrome`](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome)插件。
第二步: 项目根目录下创建 `.vscode`文件夹。
第三步:创建`launch.json`文件
文件内容:

```json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Chrome",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:3000",
      "webRoot": "${workspaceFolder}/src",
      "sourceMapPathOverrides": {
        "webpack:///src/*": "${webRoot}/*"
      }
    }
  ]
}

##第11步:配置路由,登录

//安装 react-loadable 用来实现按需加载显示页面
npm i react-loadable -S

npm i react-router-dom -S

新建路由文件夹routes

src/routes/index.js

src/routes/initDataRoute.js

// src/routes/index.js
import React from 'react';
import { HashRouter, Switch, Route } from 'react-router-dom';
import { initRoutes, initRouteConfig } from './initDataRoute';
import { Home, ErrorView, } from '../views/index'
import App from '../App';

// 根据权限渲染的菜单数据
let childRoutes = initRoutes;
// 根据权限渲染的路由数据
let routeConfig = initRouteConfig;

class Routes extends React.PureComponent{
    constructor(props){
        super(props);
        this.state = {
            
        }
    }
    componentDidMount() {
        
    }
    render(){
        return( <HashRouter>
            <>
                <App>
                    <Switch>
                        <Route path="/" component={Home} exact key="firstPage"/>
                        <Route path='/404' component={ErrorView} exact key='errorPage'/>
                        {
                            routeConfig.length > 0 && routeConfig.map((item, index) => {
                                return (
                                    <Route path={item.path} component={item.component} key={`route${index}`}/>)
                            })
                        }
                        { 
                            routeConfig.length > 0 && <Route component={ErrorView} key="error"/>
                        }
                    </Switch>
                </App>
            </>
        </HashRouter> )
    }
}
export default Routes
export {childRoutes, routeConfig};

// src/routes/initDataRoute.js
/**
* 配置懒加载路由 
*/
import { Home, Menu1, Menu2, Menu3, ErrorView, Login, Register } from '../views'

//通过组件配置一级菜单
const initRoutes = [{
  path: '/home',
  component: Home,
  title: '首页'
}, {
  path: '/menu1',
  component: Menu1,
  title: '菜单一'
}, {
  path: '/menu2',
  component: Menu2,
  title: '菜单二'
}, {
  path: '/menu3/detail/:id',
  component: Menu3,
  title: '菜单三'
}, 
];

// 所有的路由配置
const initRouteConfig = [
...initRoutes,
{
  path: '/register',//测试页使用,实际是登录里的子组件
  component: Register,
  title: '注册'
}, {
  path: '/error',
  component: ErrorView,
  title: '错误页'
}, {
  path: '/login',
  component: Login,
  title: '登录'
},
];

export { initRoutes, initRouteConfig }

在src下新建文件

  • src

    • components

      • Header
        • index.js
        • index.scss
    • routes

      • index.js
      • initDataRoute.js
    • views

      • ErrorView

        • index.js
        • index.scss
      • Home

        • index.js
        • index.scss
      • Login

        • Register
          • index.js
          • index.scss
        • index.js
        • index.scss
      • Menu1

        • index.js
        • index.scss
      • Menu2

        • index.js
        • index.scss
      • Menu3

        • index.js
        • index.scss
      • index.js------------views/index.js 导出懒加载路由

// views/index.js
/**
* 配置懒加载路由 
*/
import Loadable from 'react-loadable';
const Loading = () => null;  //加载时不显示loading

const Home = Loadable({ 
  loader: () => import('./Home'), //按需加载 点击时只加载一个页面
  loading: Loading,
});
const Menu1 = Loadable({ 
  loader: () => import('./Menu1'),
  loading: Loading,
});
const Menu2 = Loadable({ 
  loader: () => import('./Menu2'),
  loading: Loading,
});
const Menu3 = Loadable({ 
  loader: () => import('./Menu3'),
  loading: Loading,
});
const ErrorView = Loadable({ 
  loader: () => import('./ErrorView'),
  loading: Loading,
});
const Login = Loadable({ 
  loader: () => import('./Login'),
  loading: Loading,
});
const Register = Loadable({ 
  loader: () => import('./Login/Register'),
  loading: Loading,
});

export {
  Home, 
  Menu1, 
  Menu2, 
  Menu3, 
  ErrorView, 
  Login, 
  Register
}//将页面导出

// src/index.js
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Routes from './routes/index';
import * as serviceWorker from './serviceWorker';

ReactDOM.render( <Routes /> , document.getElementById('root'));

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();
// src/App.js
import React, { PureComponent } from 'react';
import './App.scss';
import './assets/css/common.scss'
import Header from './components/Header'
import { ConfigProvider } from 'antd'
import { withRouter } from 'react-router-dom';
import zhCN from 'antd/es/locale/zh_CN';
import moment from 'moment';
import 'moment/locale/zh-cn';

moment.locale('zh-cn');

class App extends PureComponent {
  render() {
      const {location, children} = this.props;
    return (
        <ConfigProvider locale={zhCN}>
            <div className="app-wrap" key={this.props.location.key}>
                {
                    location.pathname && location.pathname !== '/404' ?
                  <Header {...this.props}/>
                  : null
                }
                <div className="app-main">
                    {children}
                </div>
          </div>
        </ConfigProvider>
    );
  }
}

export default withRouter(App);

// src/components/Header/index.js
/**
 * 头部组件
 */
import React, { Component } from 'react';
import { Menu, Icon } from 'antd';
import { childRoutes } from "../../routes";
// const { SubMenu } = Menu;

class Header extends Component {
    constructor(props) {
      super(props);
      this.state = {
        current: '',
      }
    }
    componentDidMount() {
    
    }

    handleMenuClick = (e) => {
      this.setState({
        crurent: e.key
      },() => {
        this.props.history.push(e.key)
      })
    }

    render() {
        const menuList =  childRoutes.length > 0 && childRoutes.map((item,index) => {
            return (
              <Menu.Item 
                key={item.path}>
                  <Icon type="mail" />
                   {item.title}
              </Menu.Item>
            )
        })
        return (
            <>
              <Menu 
                onClick={this.handleMenuClick} 
                selectedKeys={[this.state.current]} 
                mode="horizontal">
                  {menuList}
              </Menu>
          </>
        );
    }
}

export default Header;

目前的项目运行效果如下:
在这里插入图片描述

##第12步:配置redux

npm install redux react-redux --save
or
yarn add redux react-redux

// 这个版本先不安装
npm install redux-saga --save

新建需要的文件夹

src 目录下新建 actionsreducersconstants 文件夹,

actions 存放分发的 action函数;

reducer 存放公用的 reducer

constants 存放分发 actiontype 常量。

  • src
    • store
      • index.js
      • actions
        • index.js
      • constants
        • index.js
      • reducer
        • index.js

store 中创建 index.js,用来组合单个的 reducer,输出根 state

import { combineReducers } from 'redux'

export default combineReducers({})
  • 修改 webpack 文件来设置别名
alias: {
  styles: paths.appStyles,
  routes: paths.appRoutes,
  components: paths.appComponents,
  actions: paths.appActions,
  constants: paths.appConstants,
  reducers: paths.appReducers,
...
// 添加 redux-thunk 异步中间件
npm install redux-thunk --save

修改 routes 文件夹下的 index.js

...
import { Provider } from 'react-redux'
import { createStore, applyMiddleware, compose } from 'redux'
import thunkMiddleware from 'redux-thunk'
import rootReducer from 'reducers'
...
const store = createStore(
  rootReducer,
  compose(applyMiddleware(thunkMiddleware)),
)

const App = () => (
  <Provider store={store}>
    <Routes />
  </Provider>
)

现在你可以编写你自己的 actionreducer 了。

在项目中安装 redux-devtools-extension 插件

npm i redux-devtools-extension -D

配合浏览器安装辅助工具 Redux DevTools
Chrome浏览器安装 Redux DevTools 扩展程序,修改 routes 中的 index.js

let composeEnhancers = compose
if (process.env.NODE_ENV === 'development') {
  composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;  // eslint-disable-line
}

const store = createStore(
  rootReducer,
  composeEnhancers(applyMiddleware(thunkMiddleware)),
)

在浏览器界面打开 Redux DevTools 就能看见以下效果

编写middleware

如果需要自定义的 middleware,很简单,这个 middleware 只接收一个 action,执行后也需要返回一个 action;如果需要执行下一步,调用 next(action) 即可。

  • 日志的中间件
const logger = store => next => (action) => {
  console.log('dispatching', action);
  const result = next(action);
  console.log('next state', store.getState());
  return result;
};

修改 routes文件夹下的 index.js,将该日志中间件加上

const store = createStore(
  rootReducer,
  composeEnhancers(applyMiddleware(thunkMiddleware, logger)),
)

##第13步:

根据是否登录,设置路由

// 

##中途遇到的问题

  • 问题一:安装插件时如果使用npm就一直是npm,如果使用yarn就继续使用,不能一会使用npm,一会使用yarn,要不就得把node_modules删除,重新安装

未完,待续…

[百度][http://www.baidu.com]

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章