記錄一下react腳手架+redux+antd v4 實現登陸鑑權,多級路由的學習過程

前兩天學習了下React,特記錄下學習過程,react新手有哪裏能夠優化的往大佬留言指點,小弟萬分感謝!javascript

項目初始化css

一、react腳手架html

create-react-app react-shop-dome
複製代碼

刪除多於文件src留下App.js以及index.js,並把對應引入刪掉java

二、引入antd和antd-mobilereact

1.下載依賴包ios

npm install antd antd-mobile -S
複製代碼

2.實現按需加載git

npm install babel-plugin-import react-app-rewired customize-cra --dev
複製代碼

修改package.jsongithub

"scripts": {
-   "start": "react-scripts start",
+   "start": "react-app-rewired start",
-   "build": "react-scripts build",
+   "build": "react-app-rewired build",
-   "test": "react-scripts test",
+   "test": "react-app-rewired test",
}
複製代碼

在項目的根目錄下建立一個 config-overrides.js 用於修改默認配置npm

// 實現antd /ant-mobile的按需引入
const { override, fixBabelImports } = require('customize-cra');

module.exports = override(
  fixBabelImports('pc', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    style: 'css',
  }),
  fixBabelImports('mobile', {
    libraryName: 'antd-mobile',
    libraryDirectory: 'es',
    style: 'css',
  }),
);
複製代碼

三、自定義主題json

npm install less less-loader -D
複製代碼

而後修改 config-overrides.js 配置

const { override, fixBabelImports, addWebpackAlias, addLessLoader } = require('customize-cra');
const path = require("path");
module.exports = override(
  // 實現antd /ant-mobile的按需引入

  fixBabelImports('pc', {
    libraryName: 'antd',
    libraryDirectory: 'es',
    // style: 'css',
    style: true, //主題才能生效
  }),
  // fixBabelImports('mobile', {
  //   libraryName: 'antd-mobile',
  //   libraryDirectory: 'es',
  //   style: 'css',
  // }),

  addLessLoader({
    javascriptEnabled: true,
    modifyVars: { '@primary-color': '#1DA57A' },
  }),
  //別名配置
  addWebpackAlias({
    ["@"]: path.resolve(__dirname, "./src"),
    ["@v"]: path.resolve(__dirname, "./src/views"),
    ["@c"]: path.resolve(__dirname, "./src/components")
  })
);
複製代碼

四、配置路由

App.js

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Provider } from 'react-redux';
import Admin from '@v/Admin'
import Login from '@v/Login';
import './assets/style/base.less'
function App() {
  return (
    <Router>
      <Switch>
        <Route path='/' exact component={Admin}></Route>
        <Route path='/login' component={Login}></Route>
      </Switch>
    </Router>
  );
}

export default App;
複製代碼

五、登陸頁

import React, { Component } from 'react'
import { Form, Input, Button, Checkbox } from 'antd';
import { UserOutlined, LockOutlined } from '@ant-design/icons';
import './login.less'
import api from '../../api/index.js'
export default class Login extends Component {
  // 校驗經過才執行
  onFinish = values => {
    console.log('獲取form表單的數據: ', values);
    api.login({
      "username": "admin",
      "password": "123456"
    }).then(res => {
      console.log(res);
      // 跳轉到管理界面 不須要回退用replace
      this.props.history.replace('/')
    })
  };
  render() {
    return (
      <div className="login">
        <header className='login-header'><h2>React項目-後臺管理系統</h2></header>
        <section className='login-content'>
          <h3>用戶登陸</h3>
          <Form
            name="normal_login"
            className="login-form"
            // 表單默認值
            initialValues={{
              remember: true,
              username:'admin',
              password:'123456',
            }}
            onFinish={this.onFinish}
          >
            <Form.Item
              name="username"
              validateFirst='true'
              rules={[
                {
                  // 必填
                  required: true,
                  // 校驗錯誤,就不會繼續往下校驗
                  whitespace: true,
                  message: '請輸入用戶名',
                },
                {
                  min: 4,
                  message: '用戶名最少4位',
                },
                {
                  max: 12,
                  message: '用戶名最多12位',
                },
                {
                  pattern: /^[a-zA-Z0-9_]+$/,
                  message: '用戶名必須是英文、數字或下劃線組成',
                },
              ]}
            >
              <Input prefix={<UserOutlined className="site-form-item-icon" />} placeholder="用戶名" />
            </Form.Item>

            <Form.Item
              name="password"
              rules={[
                {
                  required: true,
                  whitespace: true,
                  message: '請輸入密碼',
                },
              ]}
            >
              <Input
                prefix={<LockOutlined className="site-form-item-icon" />}
                type="password"
                placeholder="密碼"
              />
            </Form.Item>

            <Form.Item>
              <Form.Item name="remember" valuePropName="checked" noStyle>
                <Checkbox>記住密碼</Checkbox>
              </Form.Item>
            </Form.Item>

            <Form.Item>
              <Button type="primary" htmlType="submit" className="login-form-button">
                登陸
              </Button>
            </Form.Item>
          </Form>
        </section>
      </div>
    )
  }
}
複製代碼

六、發送請求(axios)

1.src目錄下建立api文件夾

2.api 建立一個request.js 以及index.js
複製代碼

request.js

import axios from 'axios';
import { message } from 'antd';
const $axios = axios.create({
  // baseURL: process.env.xxxx,
  timeout: 6000,
  retry: 4,
  retryDelay: 1000
});

//請求攔截
$axios.interceptors.request.use(
  config => {
    // 在發送請求以前作些什麼
    // 經過reudx的store拿到拿到全局狀態樹的token ,添加到請求報文,後臺會根據該報文返回status
    // 此處應根據具體業務寫token
    // const token = store.getState().user.token || localStorage.getItem('token');
    const token = 'FA2019';
    config.headers['X-Token'] = token;
    console.log(config)
    return config;
  },
  error => {
    // 對請求錯誤作些什麼
    message.error(error);
    return Promise.reject(error);
  }
);

// 添加響應攔截器
$axios.interceptors.response.use(
  response => {
    return Promise.resolve(response)
  },
  error => {
    console.log(error.response) // for debug
    //  對請求失敗的HTTP狀態碼作處理
    message.error('請求失敗');
    return Promise.reject(error)
  }
);

// 輸出方法
export default function request(url, data = {}, method = 'POST') {
  return new Promise((resolve, reject) => {

    const options = method === 'POST' ? {
      url,
      method,
      data
    } : {
        url,
        method,
        params: data
      }
    $axios(options)
      .then(res => {
        const {data}= {...res}
        console.log('返回數據', res)
        if (data.code !== 0) {
          message.error(data.msg);
        } else {
          resolve(data.data)
        }

      })
      .catch(error => {
        reject()
        console.error(error)
      })
  })
}
複製代碼

index.js

import request from './request'

const api = {
  // 登陸
  login(data) {
    return request('/admin/login/login', data)
  }
}
export default api
複製代碼

七、redux使用

npm i redux -S
複製代碼

redux思路

大致思路:先建立store ---> 注入store到應用程序 --> 組件獲取store中state數據

1.建立store

1. 須要從redux中引入createStore方法,建立store容器對象。
2. 由於createStore須要傳遞參數:reducers和中間件(暫且不考慮),因此須要建立reducers函數
3. 建立reducers函數,它包含兩個參數:state 和 action。這個函數必須return一個全新的state
4. 將建立成功的reducers函數放進createStore方法中
----------------------------------------------------------
src/store/index.js

// applyMiddleware方法用於使用中間件
import { createStore, applyMiddleware } from 'redux'
import rootReducer from './reducers'
//  利用createStore方法建立store容器對象
let store = createStore(rootReducer)
export default store

------------------------------------------------------------------

src/store/reducers/index.js

// 引入combineReducers方法,用於合併多個reducer成爲一個rootReducer
import { combineReducers } from 'redux'

import { userReducer } from './userReducer'

let rootReducer = combineReducers({
  userModule: userReducer
})

export default rootReducer

-----------------------------------------------------------------------

src/store/reducers/userReducer.js

let initState = {
  loginState: false // 默認用戶登陸狀態爲false
}

export const userReducer = (state = initState, action) => {
  switch (action.type) {
    case 'CHANGE_LOGIN_STATE':
      return {...state, loginState: action.payload}
  
    default:
      return state
  }
}
複製代碼

React 和 Redux鏈接

index.js

import React from 'react';
import ReactDOM from 'react-dom';
// 1.3 引入Provider組件,在裏面注入store
import { Provider } from 'react-redux'
import store from './store'

import App from './App';

ReactDOM.render(<Provider store={store}>
  <App />
複製代碼

其餘組件如何調用redux中的state或者觸發reducer函數呢

import {connect} from "react-redux";

// 使用 connect() 前,須要先定義 mapStateToProps 這個函數來指定如何把當前 Redux store state 映射到展現組件的 props 中
const mapStateToProps = (state) => {
  return {
    user: state.userModule
  };
};

// 除了讀取 state,容器組件還能分發 action。相似的方式,能夠定義 mapDispatchToProps() 方法接收 dispatch() 方法並返回指望注入到展現組件的 props 中的回調方法
const mapDispatchToProps = (dispatch) => {
    return {
        setName: (name) => {
            dispatch({
                type: "SET_NAME",
                payload: name
            });
        }
    };
};

export default connect(mapStateToProps, mapDispatchToProps)(App);

// 接下來,能夠經過this.props.user.name獲取state中的name值
// 經過this.props.etName('xxx')觸發一個reducer
複製代碼

八、路由鑑權

component中建立AuthRouter高階組件

// 路由鑑權
import React from 'react'
import { withRouter } from 'react-router';
import { Route, Redirect } from 'react-router-dom';
import { connect } from 'react-redux'

const AuthRouter = ({ component: Component, ...rest }) => {
  // console.log(rest);
  const isLogged = rest.loginState;
  return <Route {...rest} render={props => (isLogged ? <Component {...props} /> : <Redirect to={'/login'} />)} />;
};

// 建立映射函數讀取redux中保存用戶登陸狀態
const mapStateToProps = (state) => {
  return {
    loginState: state.userModule.loginState
  }
}


export default connect(mapStateToProps)(withRouter(AuthRouter))
// export default connect(mapStateToProps)(AuthRouter)
複製代碼

src中建立routers 文件管理路由並引入AuthRouter

import React from 'react';
import { HashRouter, Route, Switch } from 'react-router-dom';
import Login from '@/views/Login';
import Layout from '@/views/Layout';
import AuthRouter from '@c/AuthRouter';
const Router = () => {
	return (
		<HashRouter>
			<Switch>
				<Route component={Login} exact path="/login" />
				<AuthRouter path="/" component={Layout} />
			</Switch>
		</HashRouter>
	);
};

export default Router;
複製代碼

修改App.js

import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import Admin from '@v/Admin'
import Login from '@v/Login'
import AuthRouter from '@c/AuthRouter';
import './assets/style/base.less'
function App() {
  return (
    <Router>
      <Switch>
        <Route path='/login' component={Login}></Route>
        <AuthRouter path='/' component={Admin}></AuthRouter>
      </Switch>
    </Router>
  );
}

export default App;
複製代碼

登陸頁中登陸成功經過派發action修改redux中用戶模塊userReducer中的登陸狀態

Login.js修改成

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Form, Input, Button, Checkbox,message } from 'antd';
import { UserOutlined, LockOutlined } from '@ant-design/icons';
import './login.less'
import api from '../../api/index.js'
class Login extends Component {
  // 校驗經過才執行
  onFinish = values => {
    console.log('獲取form表單的數據: ', values);
    api.login({
      "username": values.username,
      "password": values.password
    }).then(res => {
      console.log(res);
      message.success('登陸成功',2).then(() => {
        // 修改登陸狀態
        this.props.changeLoginState(true)
        // 跳轉到對應頁面 不須要回退用replace
        let { from } = this.props.location.state || { from: { pathname: "/" } }
        this.props.history.replace(from.pathname)
      });


    })
  };
  render() {
    return (
      <div className="login">
        <header className='login-header'><h2>React項目-後臺管理系統</h2></header>
        <section className='login-content'>
          <h3>用戶登陸</h3>
          <Form
            name="normal_login"
            className="login-form"
            // 表單默認值
            initialValues={{
              remember: true,
              username: 'admin',
              password: '123456',
            }}
            onFinish={this.onFinish}
          >
            <Form.Item
              name="username"
              validateFirst='true'
              rules={[
                {
                  // 必填
                  required: true,
                  // 校驗錯誤,就不會繼續往下校驗
                  whitespace: true,
                  message: '請輸入用戶名',
                },
                {
                  min: 4,
                  message: '用戶名最少4位',
                },
                {
                  max: 12,
                  message: '用戶名最多12位',
                },
                {
                  pattern: /^[a-zA-Z0-9_]+$/,
                  message: '用戶名必須是英文、數字或下劃線組成',
                },
              ]}
            >
              <Input prefix={<UserOutlined className="site-form-item-icon" />} placeholder="用戶名" />
            </Form.Item>

            <Form.Item
              name="password"
              rules={[
                {
                  required: true,
                  whitespace: true,
                  message: '請輸入密碼',
                },
              ]}
            >
              <Input
                prefix={<LockOutlined className="site-form-item-icon" />}
                type="password"
                placeholder="密碼"
              />
            </Form.Item>

            <Form.Item>
              <Form.Item name="remember" valuePropName="checked" noStyle>
                <Checkbox>記住密碼</Checkbox>
              </Form.Item>
            </Form.Item>

            <Form.Item>
              <Button type="primary" htmlType="submit" className="login-form-button">
                登陸
              </Button>
            </Form.Item>
          </Form>
        </section>
      </div>
    )
  }
}

// 建立映射函數,提交action
const mapActionToProps = (dispatch) => {
  return {
    changeLoginState: (newState) => {
      dispatch({ type: 'CHANGE_LOGIN_STATE', payload: newState })
    }
  }
}

export default connect(null, mapActionToProps)(Login)
複製代碼

九、redux持久化(sessionStorage實現)

App.js 添加刷新前的事件保存redux數據到本地

import React from 'react';
import { BrowserRouter as Router, Route, Switch, Redirect } from 'react-router-dom';
import { connect } from 'react-redux'
import Admin from '@v/Admin'
import Login from '@v/Login'
import AuthRouter from '@c/AuthRouter';
import './assets/style/base.less'

function App(props) {
  window.onbeforeunload = (e) => {
    console.log(props);
    sessionStorage.setItem('store', JSON.stringify(props.state));
  };
  return (
    <Router>
      <Switch>
        <Route path='/login' component={Login}></Route>
        <AuthRouter path='/' component={Admin}></AuthRouter>
        <Redirect to="/404" />
      </Switch>
    </Router>
  );
}
// 建立映射函數讀取redux中保存用戶登陸狀態
const mapStateToProps = (state) => {
  console.log(state);
  return {
    state
  }
}

// export default App;
export default connect(mapStateToProps)(App)
複製代碼

建立store實例的時候把本地存儲的數據加入createStore函數中

// applyMiddleware方法用於使用中間件
import { createStore, applyMiddleware } from 'redux'
import rootReducer from './reducers'
const initialState = sessionStorage.getItem('store') ? JSON.parse(sessionStorage.getItem('store')) : {}
console.log(initialState);
// 1.1 利用createStore方法建立store容器對象
let store = createStore(rootReducer, initialState)
sessionStorage.removeItem('store')
export default store
複製代碼

至此前兩天的學習完成,日後會陸續應用完善其餘功能 源碼地址 :github.com/panzekun/re…

相關文章
相關標籤/搜索