时间:2023-09-07 09:43:28 点击次数:20
我想要用react搭建出一个基本的项目架子出来,目标:
集成typeScript按需加载antd自动配置路由、懒加载路由鉴权数据中心(useReducer,useContext)配置http请求(axios)在package.json配置
"babel": { "presets": [ "react-app" ], "plugins": [ [ "import", { "libraryName": "antd", "libraryDirectory": "es", "style": "css" } ] ] }配置完成,用什么组件直接在页面引入就行
什么是自动配置路由?
react配置路由是这样的:
<Switch> <Route path="/home" component={ UseRefDemof }></Route> <Route path="/day" component={ User }></Route> <Route path="/week" component={ FriendStatus}></Route> <Route path="/year" component={ CounterReducer}></Route> </Switch>如果新增一个页面在里面加route,
现在我们想要达到的效果是,新增一个ts文件,自动新增router。
实现思路:
利用require.context加载文件自动生产路由配置
实现步骤:
在src下新建router文件夹在router下新建index.tsx在router下新建home文件夹在home文件夹下配置路由信息,比如:import { BankOutlined } from @ant-design/icons; import { lazy} from react;//lazy是为了实现路由懒加载 const icon=<BankOutlined></BankOutlined>//设置图标 const home= { path: "/home", name: "首页", sort:1, component:lazy(()=>import("../../view/home/index")), meta: { title: "首页", iocn: icon, keepAlive: true } } export default home5.在router,index.tsx下利用require.context加载home里面的文件
const loadRoutes = (files: __WebpackModuleApi.RequireContext) => files .keys() .reduce((arr, key) => { const routes = files(key).default; return typeof routes === "object" ? arr.concat(routes) : arr; }, []) .sort((prev:{sort:number}, next:{sort:number}) => { return prev.sort-next.sort//根据sort 排序 }); const children = loadRoutes(require.context("./home", false, /\.ts$/));6.根据配置自动加载
// routes/index.jsx import React,{Suspense}from react import { HashRouter as Router, Route, Switch, Redirect} from react-router-dom import Head from ../view/head//导航栏 // require.context加载文件生成路由配置 const loadRoutes = (files: __WebpackModuleApi.RequireContext) => files .keys() .reduce((arr, key) => { const routes = files(key).default; return typeof routes === "object" ? arr.concat(routes) : arr; }, []) .sort((prev:{sort:number}, next:{sort:number}) => { return prev.sort-next.sort//根据sort 排序 }); const children = loadRoutes(require.context("./home", false, /\.tsx$/)); const RouteConfig = () => { return ( <Router> <Router> <Head></Head> </Router> <Router> <Suspense fallback={<div>Loading...</div>}> <Switch> {children.map((item:any)=>{ return <Route path={item.path} component={item.component} key={item.path}></Route> })} <Redirect to="/home" from="/"></Redirect> </Switch> </Suspense> </Router> </Router> ) } export {RouteConfig,children}7.在App.tsx引入路由配置
import React from react import ./App.scss; import {RouteConfig} from ./router/index function App() { return ( <div className="App"> <section> <RouteConfig></RouteConfig> </section> </div> ); } export default App;到现在路由自动加载和懒加载配置完成,我们用antd的Menu来生成导航栏。
import React,{useState} from react import { useHistory } from react-router-dom; import { Menu } from antd; import {children} from ../router/index const Head=()=>{ const [current, setCurrent] = useState<string>("/home"); const handleClick=(e:any)=>{ setCurrent(e.key) history.push(`${e.key}`) }; let history = useHistory(); return ( <Menu onClick={handleClick} selectedKeys={[current]} mode="horizontal"> {children.map((item:{path:string,meta:{title:string,iocn:any}})=>{ return <Menu.Item key={item.path} icon={item.meta.iocn}> {item.meta.title} </Menu.Item> })} </Menu> ); } export default Head这时候导航栏就根据路由配置生成完毕。
通常我们在实际项目中还会加一个登陆页面,登陆页面与主页平级,可以配置在 routes/index.jsx。
我们要实现的功能,默认跳转到登录页,登录成功跳转主页,没有登录的即使访问主页也会跳转登陆页。
路由配置:
import React,{useReducer,createContext} from react import ./App.scss; import { HashRouter as Router, Route,Redirect} from react-router-dom import {State,reducer} from ./store/index import {RouteConfig} from ./router/index import Login from "./view/login/index" let CountContext:any=null function App() { const [state, dispatch] = useReducer(reducer,State); CountContext = createContext({dispatch,state}) return ( <div className="App"> <section> <CountContext.Provider value={{dispatch,state}}> <Router> {state.isLogin?<Route path="/home" component={RouteConfig}></Route>:null} <Route path="/login" component={Login}></Route> <Redirect to="/login" from="/"></Redirect> </Router> </CountContext.Provider> </section> </div> ); } export {App,CountContext};登录页面
import { useHistory} from react-router-dom; import {useContext,useEffect} from "react" import {CountContext} from "../../App" const Login=()=>{ const {dispatch,state}= useContext(CountContext) let history = useHistory(); const goHome=()=>{ dispatch({type: login}) } useEffect(() => { if(state.isLogin){ history.push("/home") } }, [state.isLogin]) return <div> <div onClick={goHome}> 登陆</div> <div onClick={()=>dispatch({type: add})}>Add</div> </div> } export default Login我们用useReducer在在最顶部创建一个数据中心store,通过createContext,将数据store可以传递给任意子组件,子组件可以通过useContext获取到store的数据和方法。
这种方式的缺点是store的数据变化之后整个子树都会重新渲染。
上面的路由鉴权就是采用这种方式。
store代码部分:
type StateType = { isLogin:boolean, count: number, } type ActionType = { type: login|"outLogin"|"add" } const State = { isLogin:false,count: 1, } function reducer(state: StateType, action: ActionType) { switch (action.type) { case login: return {...state, isLogin:true} case outLogin: return {...state, isLogin:false} case "add": return {...state,count:state.count+1} default: return state } } export {State,reducer}配置请求拦截和响应拦截
import axios from "axios"; const service = axios.create({ baseURL, timeout: 5000 // request timeout }); // 发起请求之前的拦截器 service.interceptors.request.use( config => { // 如果有token 就携带tokon const token = window.localStorage.getItem("accessToken"); if (token) { config.headers.common.Authorization = token; } return config; }, error => Promise.reject(error) ); // 响应拦截器 service.interceptors.response.use( response => { const res = response.data; if (response.status !== 200) { return Promise.reject(new Error(res.message || "Error")); } else { return res; } }, error => { return Promise.reject(error); } ); export default service;使用:
import request from "../utils/reques"; request({url: "/profile ",method: "get"})配置代理
本地访问服务器代码可能会存在跨域问题,配置代理在package.json
"proxy": "http://123.56.85.24:5000",以上代码项目地址
https://github.com/huoqingzhu/react-hooksgithub.com/huoqingzhu/react-hooks