以前一直開發的技術棧主要是
VueJS
相關,平時也有閱讀React
文檔,可是沒有把React
相關技術棧串聯起來,造成一個後臺管理系統的模板。在學習的過程之中,基於React
開發推薦的create-react-app
腳手架搭建管理系統模板。css
antd: "^3.19.1",
axios: "^0.19.0",
bizcharts: "^3.5.3",
react: "^16.8.6",
react-dom: "^16.8.6",
react-redux: "^7.0.3",
react-router-dom: "^5.0.0",
react-transition-group: "^4.1.0",
redux: "^4.0.1"
複製代碼
開發這個管理系統主要目的是理清React-Router v4
、React-Redux
、Redux
等經常使用類庫的佈置和使用。在掘金瀏覽許多關於React
技術開發的文章。關於管理系統對於用戶登陸權限認證和系統路由佈置使用以及對ajax
第三方類庫二次封裝等文章比較少。因此下面會比較着重描述一下我的開發過程當中這部分的實現。react
開發對於請求使用的是第三方請求庫axios
,對於axios
的封裝主要在平時Vuejs
開發時候對請求封裝實踐。開發主要部分是util/fakeAuth.js
和router/PrivateRoute.js
,這部分是對用戶登陸狀態驗證。而components/ContentMain
是登陸後的主要路由設置。ios
├─public(靜態資源目錄)
├─screenShot(開發現階段運行截圖)
├─service(service服務Koa)
│ ├─bin
│ ├─config
│ ├─controller
│ ├─model
│ ├─routes
│ └─util
└─src
├─api(請求二次封裝)
├─assets(開發靜態資源目錄)
│ └─images
├─components(圖標類組件)
│ ├─AreaMap
│ ├─Chart
│ ├─ContentMain
│ ├─Diagram
│ ├─Line
│ ├─Map
│ └─SiderNav(sider側邊導航欄)
├─redux(store組件)
├─router(路由組件)
├─util(權限驗證函數)
└─views(主要開發組件)
├─Alert
├─Avatar
├─Basic
├─Cascader
├─Checkbox
├─DataChart
├─Form
├─Index
├─Login
├─Message
├─NoMatch
├─Progress
└─Spin
複製代碼
對React-Router
的路由組件Route
,包裝路由組件使須要登陸獲取權限的路由,須要自動認證再進行渲染。渲染組件須要經過權限認證函數fakeAuth.authenticate()
進行驗證,該函數會判別是否存在後端接口返回的token值,分別渲染不一樣組件。git
import React from 'react';
import {Route,Redirect} from 'react-router-dom';
import {fakeAuth} from '../util/fakeAuth';
const PrivateRoute = ({component:Component,...rest})=>{
return (
<Route
{...rest}
render = {props => (fakeAuth.authenticate())?(<Component {...props}/>):(
<Redirect to={{
pathname:"/login",
state:{from:props.location}
}}/>
)}
></Route>
)
}
export default PrivateRoute;
複製代碼
fakeAuth
驗證函數fakeAuth
文件包含三個函數authenticate
、setToken
、signout
,以上分別對應驗證登陸權限、設置登陸token、清除登陸token。github
export const fakeAuth = {
authenticate() {
const token = sessionStorage.getItem("loginToken");
return !!token ? true : false;
},
setToken(token) {
sessionStorage.setItem("loginToken", token);
return true;
},
signout() {
sessionStorage.removeItem('loginToken');
}
};
複製代碼
login.js
經過fakeAuth.authenticate()
驗證,而後分別渲染不一樣組件。<Redirect>
組件在這裏主要有兩個做用:ajax
/login
路由會自動返回權限路由路由首頁。import React,{Component} from 'react';
import {Redirect} from 'react-router-dom';
import {fakeAuth} from '../../util/fakeAuth';
import LoginForm from './index';
export default class Login extends Component{
render(){
return (
fakeAuth.authenticate()?<Redirect to="/"></Redirect>:<LoginForm />
)
}
}
複製代碼
登陸認證請求代碼在views/login/loginForm
中,handleSubmit
是登陸事件函數,主要對獲取用戶權限接口獲取token,而後進行路由跳轉.this.props.history.push
是React-Router v4
版本特有的寫法,其餘版本能夠參考官方文檔。要獲取到this.props.history
須要withRouter
對組件傳入location,match,history
等信息。vue-router
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
//這裏須要對後端接口請求,調試中
fakeAuth.setToken("zxcvbnmasdfghjkl");
this.props.history.push('/dataCount');
message.success('登錄成功',1);
return;
}
});
}
複製代碼
App.js
入口文件login
是公開的路由,其餘須要權限的路由包含在PrivateRoute
組件中,最後一個若是沒有組件匹配,最後匹配的是咱們自定義的404頁面
。redux
import React from 'react';
import {BrowserRouter as Router,Route,Switch} from 'react-router-dom';
import {Provider} from 'react-redux';
import {store} from './redux/index.js';
import {CSSTransition} from "react-transition-group";
import Login from './views/Login/login';
import Index from './views/Index';
import PrivateRoute from './router/PrivateRoute';
import NoMatch from './views/NoMatch';
import history from './api/history';
import './App.css';
function App() {
return (
<Provider store={store}>
<Router basename="/echo" history={history}>
<div className="entryWrap">
<CSSTransition
classNames="fade"
timeout={1000}
>
<Switch>
<Route path="/login" component={Login} exact/>
<PrivateRoute path="/" component={Index}/>
<Route component={NoMatch}/>
</Switch>
</CSSTransition>
</div>
</Router>
</Provider>
);
}
export default App;
複製代碼
ContentMain
權限路由渲染頁面ContentMain
主要是配置了權限路由的配置。使用PrivateRoute
包裝路由,方便性因而可知。配置路由沒有比較難邏輯,以下所示:axios
import React,{Component} from 'react';
import {Switch,withRouter,Route} from 'react-router-dom';
import PrivateRoute from '../../router/PrivateRoute';
import {routes} from '../../router/route';
import NoMatch from '../../views/NoMatch';
import './index.css';
class ContentMain extends Component{
render(){
return(
<div className="routeWrap">
<Switch>
{
routes.map(item=>{
return (
item.path?<PrivateRoute path={item.path} exact component={item.component}/>:<Route component={NoMatch}/>
)
})
}
</Switch>
</div>
)
}
}
export default withRouter(ContentMain);
複製代碼
route
路由配置上面權限路由全部配置在router/route
中,有點相似於vue-router
的配置方式。
import DataChart from '../views/DataChart';
import Basic from '../views/Basic';
import Form from '../views/Form';
import Message from '../views/Message';
import Alert from '../views/Alert';
import Spin from '../views/Spin';
import Progress from '../views/Progress';
import Checkbox from '../views/Checkbox';
import Cascader from '../views/Cascader';
export const routes = [
{
path: '/dataCount',
component: DataChart
},
{
path: '/basic',
component: Basic
},
{
path: '/form',
component: Form
},
{
path: "/alert",
component: Alert
},
{
path: '/message',
component: Message
},
{
path: '/spin',
component: Spin
},
{
path: '/progress',
component: Progress
},
{
path: '/checkbox',
component: Checkbox
},
{
path: '/cascader',
component: Cascader
}]
複製代碼
axios
二次封裝統一請求處理在api/index.js
主要包含了對axios
第三方請求庫的二次封裝,包含錯誤統一處理以及不一樣請求方法GET、POST、DELETE、PUT
等統一風格的api。關於對token
請求權限過時,返回請求狀態碼401
,清除token
,重定向到/logn
處理邏輯也會在這裏。在這一部分,Vuejs
的處理邏輯也很相似,可是比較熟悉能夠直接經過如下方式進行重定向:
import router from "../router/index.js";
router.replace({
path: "/login",
query: {
redirect: router.currentRoute.path
}
});
複製代碼
可是對於React-Router
,網上瀏覽比較多的文章,嘗試多比較多遍,都沒有找到一個不是強制刷新的路由方案,使用window.location.href
能夠強制跳轉,可是這裏使用了createBrowserHistory
方法進行重定向。若是有其餘的方案能夠給我留言。菜雞的我才能茁壯成長。下面是關於封裝的代碼部分:
import axios from "axios";
import {fakeAuth} from '../util/fakeAuth';
import {message as Message} from 'antd';
import {timeout,baseURL} from "./config.js";
import history from './history';
axios.defaults.timeout = timeout;
axios.defaults.baseURL = baseURL;
axios.interceptors.request.use(
config => {
if (fakeAuth.authenticate()) {
config.headers.Authorization = `Bearer ${sessionStorage.getItem('loginToken')}`;
}
return config;
},
error => {
return Promise.reject(error);
}
);
axios.interceptors.response.use(
response => {
return response;
},
error => {
if (error.response) {
switch (error.response.status) {
case 401:
fakeAuth.signout();
history.push('/login');
break;
default:
}
const message = error.response.data.message ?error.response.data.message :"服務器異常";
Message.error(message);
}
return Promise.reject(error);
}
);
const gl_ajax = params => {
return axios({
method: params.method.toLowerCase(),
url: `${axios.defaults.baseURL}${params.url}`,
data: params.method !== "get" ? params.data : "",
params: params.method === "get" ? params.data : "",
responseType: params.file ? "blob" : ""
})
.then(res => {
params.success && params.success(res);
})
.catch(err => {
params.error && params.error(err);
});
};
export default gl_ajax;
複製代碼
history
文件部分是參考了網友給的技術方案,利用了createBrowserHistory({forceRefresh:true})
,這裏show
出來給你們參考一下:
// history.js
import { createBrowserHistory } from "history";
export default createBrowserHistory({forceRefresh:true});
// config.js
export const timeout = 5000;
export const baseURL =
process.env.NODE_ENV === "production"
? "https://echo/api/v1"
: "http://localhost:8080/api/v1";
複製代碼
用redux
之類的第三方存儲,不須要藉助router
, 只要收到401請求的時候, 修改redux auth.isTokenTimeout
爲 true, 界面就跳到超時登錄了, 並且url沒改動,這樣登錄成功後界面還能依據url自動還原。
上面token過時路由跳轉方案是在react-china
論壇一位網友提出,感受很不錯。解決方案地址。
export const AuthorizeApplication = connect(x=> x.auth)(function Authorize({isLogin, isTokenTimeout}){
if (isTokenTimeout) { return <TokenTimeoutSign /> }
if (!isLogin) { return <Sign /> }
return <Application />
})
複製代碼
若是以爲喜歡能夠給個小星星(star)嗎?
參考文章
[譯] 2019 React Redux 徹底指南
[譯]React中的用戶認證(登陸態管理)
React Router 4.x開發,這些雷區咱們都幫你踩過了