zoukankan      html  css  js  c++  java
  • React(1702H)搭建架构,包裹redux、路由、api、拦截器、国际化

    一、使用到的库

    1.react-redux库,使用了两个api

    Provider组件:

    import { Provider } from 'react-redux';
    
    ReactDOM.render(
      <Provider store={Store}>
        <BrowserRouter>
          <Routers />
        </BrowserRouter>
      </Provider>
      , document.getElementById('root'));

    connect():

    const mapStateToProps = (state) => {
      return {
        count: state.getIn(['task', 'count']) 
      }
    }
    
    const mapDispatchToProps = (dispatch) => {
      return {
        TASK_CHANGAE_STATE: (key, value) => {
          dispatch({ type: 'TASK_CHANGAE_STATE', key, value });
        }    
      };
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(withRouter(Login))

    参考链接:https://www.jianshu.com/p/81e9e9eaf8fa

    2.redux-immutable库

    combineReducers():

    redux-immutable提供一个combineReducers()函数,将store中最外层的reducer中的state转化为immutable对象(这里涉及到reducer的拆分,拆分用到了与redux中同名的combineReducers()方法)。combineReducers() 将多个 reducer 合并成为一个。

    import { combineReducers } from 'redux-immutable';
    import { createStore, applyMiddleware, compose } from 'redux';
    import reducers from './reducers';
    
    const reducer = combineReducers({ ...reducers });
    const store = createStore(reducer);
    
    export default store;
    
    
    import { reducer as task } from './task';
    export default {
    	task
    };
    

    参考链接:https://blog.csdn.net/weixin_39786582/article/details/82623353

    3.redux库

    createStore()。创建store,第一个参数是reducer。

    createStore(reducer, [preloadedState], [enhancer])

    https://redux.js.org/api/createstore

    compose() 。字面意思:组成,构成(一个整体);  使用compose可以增强store。例如添加中间件。

    https://redux.js.org/api/compose

    Enhancers。字面意思:增强剂。

    applyMiddleware()。应用中间件

    https://redux.js.org/api/applymiddleware

    redux-devtools-extension无法使用的解决办法:

    https://github.com/zalmoxisus/redux-devtools-extension#usage

    import { combineReducers } from 'redux-immutable';
    import { createStore, applyMiddleware, compose } from 'redux';
    import thunk from 'redux-thunk';
    import reducers from './reducers';
    
    const composeEnhancers =
      typeof window === 'object' && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
        ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
        : compose;
    
    const enhancer = composeEnhancers(applyMiddleware(thunk));
    const reducer = combineReducers({ ...reducers });
    const store = createStore(reducer, enhancer);
    
    export default store;
    

    4.redux-thunk库

    redux的中间件,使用后dispatch可以接收一个函数作为参数。使用前只能接收一个对象作为参数

    https://www.npmjs.com/package/redux-thunk

    5.immutable库

    immutabble字面意思:不可改变的;永恒不变的

    mutabble字面意思:可变的;会变的

    官网:https://immutable-js.github.io/immutable-js/

    用的的api:

    formJS:把js对象转换成immutable对象

    import { fromJS } from 'immutable';
    
    const defaultState = fromJS({
      count: 0,
    })
    

     formJS() 、setIn() 、getIn()、toJS()

    // 原来的写法
    var foo = {a: {b: 1}};
    var bar = foo;
    bar.a.b = 2;
    console.log(foo.a.b);  // 打印 2
    console.log(foo === bar);  //  打印 true
     
    // 使用 immutable.js 后
    var foo = Immutable.fromJS({a: {b: 1}});
    var bar = foo.setIn(['a', 'b'], 2);   // 使用 setIn 赋值
    console.log(foo.getIn(['a', 'b']));  // 使用 getIn 取值,打印 1
    console.log(foo === bar);  //  打印 false     
    console.log(bar.getIn(['a', 'b']))  //2
    console.log(bar.toJS())  //转变成js对象
    

    reducer.js文件示例:

    import actions from './actionTypes';
    import { fromJS } from 'immutable';
    
    const defaultState = fromJS({
      count: 0,
    })
    
    export default (state = defaultState, action) => {
      switch (action.type) {
        case actions.TASK_CHANGAE_STATE: {
          if (typeof action.value !== 'undefined') {
            return state.setIn(action.key, action.value);
          } else {
            return state
          }
        }	    
        case actions.TASK_CHANGAE_STATE_FORMJS: {
          if (typeof action.value !== 'undefined') {
            return state.setIn(action.key, fromJS(action.value));
          } else {
            return state
          }
        }
        default:
          return state;
      }
    };
    

    actionTypes.js文件示例:

    export default {
    	TASK_CHANGAE_STATE: 'TASK_CHANGAE_STATE',
    	TASK_CHANGAE_STATE_FORMJS: 'TASK_CHANGAE_STATE_FORMJS'
    };
    

    我写的小demo:https://blog.csdn.net/xutongbao/article/details/81331179

    6.react-router-dom库

    常用api有:

    BrowserRouter:包裹根组件

    Switch: 有<Switch>标签,则其中的<Route>在路径相同的情况下,只匹配第一个,这个可以避免重复匹配

    Route:路由

    Link :链接

    withRouter:包裹组件,包裹后组件的props会增加三个属性: match, location, history

    1)通过js跳转路由:this.props.history.push('/tasklist')

    2)获取动态路由参数

        let { match } = this.props
        if (match.params.new === 'new') {
        }

    3)获取路径名:<div>{this.props.location.pathname}</div>

    官方文档:https://reacttraining.com/react-router/web/api/BrowserRouter

    index.js:

    import React from 'react';
    import ReactDOM from 'react-dom';
    import Routers from './router/index.js';
    import { BrowserRouter } from 'react-router-dom';  //路由
    import { Provider } from 'react-redux';
    import Store from './store/index.js';
    import 'antd/dist/antd.css';
    import './index.css';
    import * as serviceWorker from './serviceWorker';
    
    ReactDOM.render(
      <Provider store={Store}>
        <BrowserRouter>
          <Routers />
        </BrowserRouter>
      </Provider>
      , document.getElementById('root'));
    

    router/index.js:

    import React from 'react';
    import { Switch, Route } from 'react-router-dom';
    import config from "./config.js"
    
    //路由组件
    class Routers extends React.Component {
      render() {
        let router = this.renderSingleRoute()
        return (
          <Switch>
            {router}
          </Switch>
        );
      }
    }
    
    Object.assign(Routers.prototype, {
      renderSingleRoute() {
        return config.routerSingle.map((item) => {
          return <Route key={item.path} exact={item.exact}  path={item.path} component={item.component} />
        });
      }  
    })
    
    export default Routers

    config.js:

    import React, {lazy, Suspense } from 'react';
    import Login from '../pages/login/Login.js'
    const List = lazy(() => import('../pages/list/List.js'))
    
    const routerSingle = [
      {
        path: '/',
        exact: true,
        component: () => (
          <Suspense fallback={'loading...'}>
            <Login />
          </Suspense>
        )
      },
      {
        path: '/login',
        exact: true,
        component: () => (
          <Suspense fallback={'loading...'}>
            <Login />
          </Suspense>
        )
      },
      {
        path: '/list',
        exact: true,
        component: () => (
          <Suspense fallback={'loading...'}>
            <List />
          </Suspense>
        )
      }
    ];
    
    export default {
      routerSingle
    }

     首先加上了Switch,其次加入了exact。加入Switch表示,只显示一个组件。加exact表示精确匹配/

    嵌套路由,从广义上来说,分为两种情况:一种是每个路由到的组件都有共有的内容,这时把共有的内容抽离成一个组件,变化的内容也是一个组件,两种组件组合嵌套,形成一个新的组件。另一种是子路由,路由到的组件内部还有路由。

    对于共有的内容,典型的代表就是网页的侧边栏,假设侧边栏在左边,我们点击其中的按钮时,右侧的内容会变化,但不管右侧的内容怎么变化,左侧的侧边栏始终存在。

    嵌套路由的示例:

    import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
    import React, { Suspense, lazy } from 'react';
    
    const Home = lazy(() => import('./Home'));
    const Bar = lazy(() => import('./Bar'));
    const FileUpload = lazy(() => import('./FileUpload'));
    const Banner = lazy(() => import('./Banner'));
    
    const App = () => (
      <Router>
        <Suspense fallback={<div>loading</div>}>
          <div>
            <ul>
              <li><Link to="/">Home</Link></li>
              <li><Link to="/bar">Bar</Link></li>
              <li><Link to="/management/file_upload">后台管理</Link></li>
            </ul>
          </div>
          <Switch>
            <Route exact path="/" component={Home} />
            <Route path="/bar" component={Bar} />
            <Route>
              <div>
                <div>header</div>
                <ul>
                  <li><Link to="/management/file_upload">文件上传</Link></li>
                  <li><Link to="/management/banner">Banner</Link> </li>
                </ul>                   
                <Route exact path="/management/file_upload" component={FileUpload}></Route>
                <Route exact path="/management/banner" component={Banner}></Route>
              </div>
            </Route>
          </Switch>
        </Suspense>
      </Router>
    )
    
    export default App;
      renderManagement() {
        return (
          <Route>
            <div>
              <Header/>
              <div className="m-management">
                <div className="m-sidebar">
                  {
                    config.routerManagement.map((item) => {
                      return <NavLink key={item.path} to={item.path} className="m-management-link" activeClassName="active">{item.text}</NavLink>
                    })
                  }            
                </div>
                <div className="m-content-wrap">
                  {
                    config.routerManagement.map((item) => {
                      return <Route key={item.path} exact={item.exact} path={item.path} component={item.component}></Route>
                    })
                  }
                </div>
              </div>
            </div>
          </Route>
        )
      }

    7.react库

    代码分割用的两个api:lazy方法和Suspense组件

    参考链接:https://blog.csdn.net/xutongbao/article/details/84822315

    8.axios库

    拦截器:

    import axios from 'axios'
    
    axios.interceptors.request.use(
        (config) => {
            config.headers['token'] = localStorage.getItem('token')
            return config
        },
        (error) => {
            return Promise.reject(error.response);
        }
    )
    
    axios.interceptors.response.use(
        (response) => {
            if (response.data.code === 200) {
                return Promise.resolve(response)
            } else if (response.data.code === 400) {
                alert(response.data.message)
            } else if (response.data.code === 403) {
                console.log('登陆过期')
                window.location.href = '/login'
            }
            return Promise.reject(response);
        },
        (error) => {
            return Promise.reject(error.response);
        }    
    )

    api.js:

    import axios from 'axios';
    
    axios.defaults.baseURL = 'http://localhost:8888'
    
    export default async (config) => {
    	try {
    		const response = await axios(config);
    		if (response) {
    			const responseJSON = response.data;
    			return responseJSON;			
    		} else {
    			return {}
    		}
    	} catch (err) {
    		throw err;
    	}
    };

    index.js:

    import * as urls from './url';
    import common from './api';
    
    export default {
        login: (data) => common({url: urls.login, method: 'post', data}),
        captcha: () => common({url: urls.captcha, method: 'get'}),
        register: (data) => common({url: urls.register, method: 'post', data}),
        forgotPassword: (url) => common({url: urls.forgotPassword + url, method: 'get'}),
        resetPassword: (data) => common({url: urls.resetPassword, method: 'post', data}),
        getList: (url) => common({url: urls.getList + url, method: 'get'}),
        deleteItem: (data) => common({url: urls.deleteItem, method: 'post', data}),
        addItem:(data) => common({url: urls.addItem, method: 'post', data})
    }

    url.js:

    export const login = '/login';
    export const captcha = '/captcha'
    export const register = '/register'
    export const forgotPassword = '/forgot_password'
    export const resetPassword = '/reset_password'
    export const getList = '/getlist'
    export const deleteItem = '/deleteItem'
    export const addItem = '/addItem'
    
    

    keyCode.js:

    export const SUCCESS = 200;  // 成功
    export const ERROR = 400;  // 基础错误码
    export const PARAMS_ERR = 401;  // 请求参数错误
    export const PERMISSION_ERR = 402;  // 无权限
    export const AUTH_ERR = 403;  // TOKEN失效 无权限
    export const DATA_ERR = 404;  // 无数据
    export const NO_ACCESS = 427;  // 无数据
    export const FORMA_ERR = 500; // 格式错误

    9.antd库

    在入口index.js处引入样式:

    import 'antd/dist/antd.css';

    在页面里引入需要使用的组件:

    import { Button, Input } from 'antd';

    input受控组件示例:

    import React from 'react';
    import { connect } from 'react-redux'
    import { withRouter } from 'react-router-dom'
    import { Button, Input } from 'antd';
    import Api from '../../api/index.js'
    import * as keyCode from '../../api/keyCode.js'
    
    class Login extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          username: '',
          password: ''
        }
      }
      render() {
        let { count } = this.props
        let {
          username,
          password,
        } = this.state
        return (
          <div>
            登录{count}<button onClick={this.handleBtn.bind(this)}>按钮</button>
            <Input placeholder="请输入用户名" value={username} onChange={this.handleInput.bind(this, 'username')}></Input>
            <Input placeholder="请输入密码" value={password} onChange={this.handleInput.bind(this, 'password')}></Input>
            <Button onClick={this.handleLogin.bind(this)}>按钮</Button>
          </div>
        );
      }
    }
    
    Object.assign(Login.prototype, {
      handleBtn() {
        let {count} = this.props
        count = count + 1
        this.props.TASK_CHANGAE_STATE(['count'], count)
      },
      handleLogin() {
        let {username, password} = this.state
        console.log(username,password)
        let data = {
          username,
          password
        }
        this.props.history.push('/list')
        Api.login(data).then((res) => {
          
        })
      }
    })
    
    Object.assign(Login.prototype, {
      handleInput(field, e) {
        this.setState({
          [field]: e.target.value
        })
      }
    })
    
    const mapStateToProps = (state) => {
      return {
        count: state.getIn(['task', 'count']) 
      }
    }
    
    const mapDispatchToProps = (dispatch) => {
      return {
        TASK_CHANGAE_STATE: (key, value) => {
          dispatch({ type: 'TASK_CHANGAE_STATE', key, value });
        }    
      };
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(withRouter(Login))
    

    10.react-intl-universal

    多语言、国际化

    示例代码:

    1)初始化:

      languageInit() {
        let currentLocale = localStorage.getItem('language') || 'zh-CN'
        intl.init({
          currentLocale: currentLocale,    
          locales: {
            [currentLocale]: require(`../../i18n/${currentLocale}`).default
          }
        }).then(() => {
        }) 
        if (!localStorage.getItem('language')) {
          localStorage.setItem('language', 'zh-CN')
          this.setState({
            language: 'zh-CN'
          })
        } else {
          this.setState({
            language: currentLocale
          })      
        } 
      }

    2)语言文件zh-CN.js:

    export default ({
      language: 'zh',
      login: {
        loginTitle: '后台管理系统',
        login: '登录',
        usernamePlaceholder: '请输入用户名',
        passwordPlaceholder: '请输入密码',
        catpchaPlaceholder: '请输入验证码',
        userRegister: '用户注册',
        forgotPassword: '忘记密码'
      }
    })

    3)在dom中使用:

            <div className="m-login-title">
              {intl.get('login.loginTitle')}
            </div>

    4)切换语言:

      handleLanguage(language) {
        this.setState({
          language
        })
        localStorage.setItem('language', language);
        window.location.reload()     
      },

    参考链接:https://www.npmjs.com/package/react-intl-universal

    二、制作页面

    1.登录页

    1)动态渲染html:

              <span 
                className="m-captcha" 
                dangerouslySetInnerHTML={{__html: captchaSvg}}
                onClick={this.getCaptcha.bind(this)}
                >
              </span> 

    2)受控组件:

    <Input 
        placeholder={intl.get('login.passwordPlaceholder')} 
        type="password" 
        value={password} 
        onChange={this.handleInput.bind(this, 'password')}>
    </Input>
    
    
    
    //受控组件
    Object.assign(Login.prototype, {
      handleInput(field, e) {
        this.setState({
          [field]: e.target.value
        })
      }
    })

    3)input自动获取焦点:

             <Input 
                placeholder={intl.get('login.usernamePlaceholder')} 
                value={username} 
                ref={(input) => this.userNameInput = input}
                onChange={this.handleInput.bind(this, 'username')}></Input>
    
    
    
    this.userNameInput.focus()

    4)点击enter触发事件:

              <Input 
                className="m-login-input-catpcha" 
                placeholder={intl.get('login.catpchaPlaceholder')} 
                value={captcha}
                ref={(input) => this.captchaInput = input}
                onKeyDown={this.handleEnter.bind(this)} 
                onChange={this.handleInput.bind(this, 'captcha')}></Input> 
    
    
     handleEnter(e) {
    		if (e.keyCode === 13) {
    			this.handleLogin()
    			this.captchaInput.blur()
    		}    
      }

     5)

    import React from 'react';
    import { connect } from 'react-redux'
    import { withRouter, Link } from 'react-router-dom'
    import { Button, Input, Select } from 'antd';
    import { jsEncrypt } from '../../utils/index.js'
    import intl from 'react-intl-universal'
    import Api from '../../api/index.js'
    import * as keyCode from '../../api/keyCode.js'
    import './index.css'
    
    const { Option } = Select
    class Login extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          username: '',
          password: '',
          captcha: '',
          captchaSvg: '',
          language: 'zh-CN'
        }
      }
      render() {
        let { count } = this.props
        let {
          username,
          password,
          captcha,
          captchaSvg,
          language,
        } = this.state
        return (
          <div className="m-login">
            <div className="m-language">
              <Select value={language} onChange={this.handleLanguage.bind(this)}>
                <Option value="zh-CN">简体中文</Option>
                <Option value="en-US">Englist</Option>
                <Option value="zh-TW">繁體中文</Option>
              </Select>
            </div>
            <div className="m-login-title">
              {intl.get('login.loginTitle')}
            </div>
            <div className="m-login-row">
              <Input 
                placeholder={intl.get('login.usernamePlaceholder')} 
                value={username} 
                ref={(input) => this.userNameInput = input}
                onChange={this.handleInput.bind(this, 'username')}></Input>
            </div>
            <div className="m-login-row">
              <Input placeholder={intl.get('login.passwordPlaceholder')} type="password" value={password} onChange={this.handleInput.bind(this, 'password')}></Input>
            </div>
            <div className="m-login-row">
              <Input 
                className="m-login-input-catpcha" 
                placeholder={intl.get('login.catpchaPlaceholder')} 
                value={captcha}
                ref={(input) => this.captchaInput = input}
                onKeyDown={this.handleEnter.bind(this)} 
                onChange={this.handleInput.bind(this, 'captcha')}></Input>
              <span 
                className="m-captcha" 
                dangerouslySetInnerHTML={{__html: captchaSvg}}
                onClick={this.getCaptcha.bind(this)}
                >
              </span>          
            </div>
            <div className="m-login-row">
              <Button onClick={this.handleLogin.bind(this)}>{intl.get('login.login')}</Button>
            </div>
            <div className="m-login-row">
              <Link to="/register" className="m-link">{intl.get('login.userRegister')}</Link>
              <Link to="/forgot_password" className="m-link">{intl.get('login.forgotPassword')}</Link>
            </div>
            <div>
              {intl.get('login.login')}{count}<button onClick={this.handleBtn.bind(this)}>按钮</button>
            </div>        
    			</div>
        );
      }
    }
    
    //生命周期
    Object.assign(Login.prototype, {
      componentDidMount() {
        this.userNameInput.focus()
        this.getCaptcha()
        this.languageInit()
      },
      languageInit() {
        let currentLocale = localStorage.getItem('language') || 'zh-CN'
        intl.init({
          currentLocale: currentLocale,    
          locales: {
            [currentLocale]: require(`../../i18n/${currentLocale}`).default
          }
        }).then(() => {
        }) 
        if (!localStorage.getItem('language')) {
          localStorage.setItem('language', 'zh-CN')
          this.setState({
            language: 'zh-CN'
          })
        } else {
          this.setState({
            language: currentLocale
          })      
        } 
      }
    })
    
    //事件
    Object.assign(Login.prototype, {
      handleBtn() {
        let {count} = this.props
        count = count + 1
        this.props.TASK_CHANGAE_STATE(['count'], count)
      },
      handleLogin() {
        let {username, password, captcha} = this.state
        console.log(username,password)
        let data = {
          username,
          password: jsEncrypt(password),
          captcha
        }
        
        Api.login(data).then((res) => {
          if (res.code === keyCode.SUCCESS) {
            console.log(res)
            localStorage.setItem('token', res.data.token)
            localStorage.setItem('username', res.data.username)
            //this.props.history.push('/list')
            this.props.history.push('/management/file_upload')
          } else {
            this.getCaptcha()
          }
        }).catch((e) => {
          this.getCaptcha()
        })
      },
      getCaptcha() {
        Api.captcha().then((res) => {
          console.log(res)
          if (res.code === keyCode.SUCCESS) {
            localStorage.setItem('token', res.data.captchaId)
            this.setState({
              captchaSvg: res.data.captcha
            })
          }
        })
      },
      handleLanguage(language) {
        this.setState({
          language
        })
        localStorage.setItem('language', language);
        window.location.reload()     
      },
      handleEnter(e) {
    		if (e.keyCode === 13) {
    			this.handleLogin()
    			this.captchaInput.blur()
    		}    
      }
    })
    
    //受控组件
    Object.assign(Login.prototype, {
      handleInput(field, e) {
        this.setState({
          [field]: e.target.value
        })
      }
    })
    
    const mapStateToProps = (state) => {
      return {
        count: state.getIn(['task', 'count']) 
      }
    }
    
    const mapDispatchToProps = (dispatch) => {
      return {
        TASK_CHANGAE_STATE: (key, value) => {
          dispatch({ type: 'TASK_CHANGAE_STATE', key, value });
        }    
      };
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(withRouter(Login))
    

    注册页:

    import React from 'react';
    import { withRouter } from 'react-router-dom'
    import { Button, Input, message } from 'antd';
    import { jsEncrypt } from '../../utils/index.js'
    import Api from '../../api/index.js'
    import * as keyCode from '../../api/keyCode.js'
    import './index.css'
    
    class Register extends React.Component {
      constructor(props) {
        super(props)
        this.state = {
          username: '',
          password: '',
          email: '',
        }
      }
      render() {
        let { count } = this.props
        let {
          username,
          password,
          email,
        } = this.state
        return (
          <div className="m-login">
            <div>
              注册
            </div>
            <div className="m-login-row">
              <Input placeholder="请输入用户名" value={username} onChange={this.handleInput.bind(this, 'username')}></Input>
            </div>
            <div className="m-login-row">
              <Input placeholder="请输入密码" type="password" value={password} onChange={this.handleInput.bind(this, 'password')}></Input>
            </div>
            <div className="m-login-row">
              <Input placeholder="请输入邮箱" type="email" value={email} onChange={this.handleInput.bind(this, 'email')}></Input>
            </div>
            <div className="m-login-row">
              <Button onClick={this.handleRegister.bind(this)}>注册</Button>
            </div>
    			</div>
        );
      }
    }
    
    Object.assign(Register.prototype, {
      handleRegister() {
        let {username, password, email} = this.state
        console.log(username,password)
        let data = {
          username,
          password: jsEncrypt(password),
          email
        }
        
        Api.register(data).then((res) => {
          if (res.code === keyCode.SUCCESS) {
            console.log(res)
            message.info(res.message)
          }
        }).catch((e) => {
        })
      },
    })
    
    //受控组件
    Object.assign(Register.prototype, {
      handleInput(field, e) {
        this.setState({
          [field]: e.target.value
        })
      }
    })
    
    export default withRouter(Register)
    
  • 相关阅读:
    node.js简单的服务器
    简单的分页1
    定时跳转
    初始化多个vue实例对象
    js获取验证码的方法
    [z]Java代理(jdk静态代理、动态代理和cglib动态代理)
    .net操作word lib DocX
    git常用命令
    [z]查表空间使用情况
    [z]oracle job
  • 原文地址:https://www.cnblogs.com/xutongbao/p/11915679.html
Copyright © 2011-2022 走看看