在学了一遍node.js基础之后,现在回到后台管理系统,现在服务端也要写,不过只是根据前台需要的数据返回就可以了。是直接写死的,并没有连接数据库,之前的项目架构以及完成,包括请求拦截器的axios请求封装以及storage封装等。现在我们就需要开始去设置登录页面还有欢迎页面,以及用状态管理系统去保存用户信息。
1.静态页面布局
编辑
javascript
import {
createBrowserRouter,
Navigate,
useRoutes,
redirect
} from 'react-router-dom'
import Login from '@/views/login/Login'
import Welcome from '@/views/Welcome'
import Error404 from '@/views/Error404'
import Error403 from '@/views/Error403'
import Layout from '@/layout/index'
const router = [
{
element: <Layout />,
children: [
{
path: '/welcome',
element: <Welcome />
}
]
},
{ path: '/', element: <Navigate to='/welcome' /> },
{ path: '/login', element: <Login /> },
{ path: '*', element: <Navigate to='/404' /> },
{ path: '/404', element: <Error404 /> },
{ path: '/403', element: <Error403 /> }
]
export default createBrowserRouter(router)
// export default function () {
// return useRoutes(router)
// }
我们通过路由表可以看出我们现在主要页面是login以及welcome,我们希望login登录之后可以切换到welcome以及登出后回到login。这里我们给welcome路由嵌套了一个父路由布局路由layout因为我们如果要设置路由守卫的话,那么跳转所有子路由都需要经过布局路由的渲染。可以设置一些权限比如查看token。我们看代码。
javascript
import React, { useEffect } from 'react'
import {
UploadOutlined,
UserOutlined,
VideoCameraOutlined
} from '@ant-design/icons'
import { Layout, theme, Watermark } from 'antd'
import NavHeader from '@/components/NavHeader'
import NavFooter from '@/components/NavFooter'
import SideMenu from '@/components/Menu'
import { Outlet } from 'react-router-dom'
import styles from './index.module.less'
import api from '@/api'
import storage from '@/utils/storage'
import store, { useBearStore } from '@/store'
const { Content, Sider } = Layout
const App: React.FC = () => {
const state = useBearStore()
useEffect(() => {
getUserInfo()
}, [])
const getUserInfo = async () => {
const data = await api.getUserInfo()
store.updataUserInfo(data)
state.updataUserInfo(data)
console.log(' ', data.userName)
}
return (
<Watermark content='React'>
<Layout>
<Sider>
<SideMenu />
</Sider>
<Layout>
{/* <Header style={{ padding: 0, background: colorBgContainer }}>
<NavHeader />
</Header> */}
<NavHeader />
<Content className={styles.content}>
<div className={styles.warpper}>
<Outlet />
</div>
<NavFooter />
</Content>
</Layout>
</Layout>
</Watermark>
)
}
export default App
我们这里没有进行权限设置,还没开始写路由守卫,只是嵌套了我们要展示出的各种导航栏组件以及我们的展示组件welcome。
编辑
这是效果图。我们是直接用的antd ui库中的组件设置,所以我们不再说静态页面布局的内容了。我们在加载布局组件之后发送请求拿到用户数据,因为子路由需要展示,我们在父路由布局路由挂载就拿到数据存到状态管理工具resso或者zustand中。
2.登录(登出)以及在地址栏保存登出之前的路径字符串
我们总是希望我们的页面如果失去登录token身份验证的时候跳转到登录页面,我们重新登录后可以回到之前的页面。
那么我们就需要在登出的时候也就是跳转到登录页面的时候携带点字符串。
javascript
import { MenuUnfoldOutlined } from '@ant-design/icons'
import { Breadcrumb, Dropdown, Switch } from 'antd'
import type { MenuProps } from 'antd'
import styles from './index.module.less'
import store, { useBearStore } from '@/store'
import storage from '@/utils/storage'
export default function NavHeader() {
const userInfo = useBearStore(state => state.userInfo)
const breadList = [
{
title: '首页'
},
{
title: '工作台'
}
]
const items: MenuProps['items'] = [
{
key: 'emali',
label: 邮箱:${store.userInfo.userEmail}
},
{
key: 'logout',
label: 退出
}
]
const onClick: MenuProps['onClick'] = ({ key }) => {
if (key === 'logout') {
storage.remove('token')
//location.href = '/login?...' 表示重定向到登录页面,并把当前页面地址编码后作为参数传过去。
location.href = '/login?callback=' + encodeURIComponent(location.href)
}
}
return (
<div className={styles.navHeader}>
<div className={styles.left}>
<MenuUnfoldOutlined />
<Breadcrumb items={breadList} style={{ marginLeft: '10px' }} />
</div>
<div className='right'>
<Switch
checkedChildren='暗黑'
unCheckedChildren='默认'
style={{ marginRight: '10px' }}
/>
<Dropdown menu={{ items, onClick }} trigger={['click']}>
<span className={styles.nickName}>{userInfo.userName}</span>
</Dropdown>
</div>
</div>
)
}
我们在跳转出去的时候用拼接字符串的形式,拿到跳转之前地址栏的路径location.href然后用encodeURICpmponent包裹拼接到callback这个key上面。那么我们登录的时候就可以通过拿地址栏上面的参数去跳转回去。
csharp
const params = new URLSearchParams(location.search)
location.href = params.get('callback') || '/welcome'
3.状态管理工具zustand以及resso
在前端开发中,合理的状态管理可以让我们的组件更加简洁、逻辑更清晰。除了大家熟悉的 Redux、Vuex 等大型状态管理库外,resso 和 zustand 是两个轻量级、易上手的 React 状态管理工具。在这篇文章中,我将简单对比这两种工具的基本用法,并通过一个用户信息管理的例子,展示它们在实际项目中的使用方式。
typescript
import resso from "resso";
import {create} from 'zustand'
import type {User} from '@/types/api'
//resso 状态管理有类型推导 直接token:''会认为是string
//resso 直接在resso({})对象中配置存的状态管理 赋值直接通过引入store对象直接.=赋值就可以了
//操作方法也可以直接写在resso中
const store = resso({
token:'',
userInfo:{
userEmail:'',
userName:''
},
updataUserInfo(userInfo:User.UserItem){
store.userInfo = userInfo
}
})
//zustand 更新方法必须要用回调函数然后里面用set函数更新
export const useBearStore =create<{
token:string
userInfo:{
userEmail:string
userName:string
},
updataUserInfo:(userInfo:User.UserItem)=>void
}>((set)=>({
token:'',
userInfo:{
userEmail:'',
userName:''
},
updataUserInfo:(userInfo:User.UserItem)=>set({userInfo})
}))
export default store
我们很直观的看出resso明显简单的多,类型推导我们都不需要去声明类型。zustand需要用hook获取状态useBearStore()然后更新状态必须用set()不可以直接复制,更接近react思维方式。
scss
const App: React.FC = () => {
const state = useBearStore()
useEffect(() => {
getUserInfo()
}, [])
const getUserInfo = async () => {
const data = await api.getUserInfo()
store.updataUserInfo(data)
state.updataUserInfo(data)
console.log(' ', data.userName)
}
javascript
import { MenuUnfoldOutlined } from '@ant-design/icons'
import { Breadcrumb, Dropdown, Switch } from 'antd'
import type { MenuProps } from 'antd'
import styles from './index.module.less'
import store, { useBearStore } from '@/store'
import storage from '@/utils/storage'
export default function NavHeader() {
const userInfo = useBearStore(state => state.userInfo)
const breadList = [
{
title: '首页'
},
{
title: '工作台'
}
]
const items: MenuProps['items'] = [
{
key: 'emali',
label: 邮箱:${store.userInfo.userEmail}
},
{
key: 'logout',
label: 退出
}
]
const onClick: MenuProps['onClick'] = ({ key }) => {
if (key === 'logout') {
storage.remove('token')
//location.href = '/login?...' 表示重定向到登录页面,并把当前页面地址编码后作为参数传过去。
location.href = '/login?callback=' + encodeURIComponent(location.href)
}
}
return (
<div className={styles.navHeader}>
<div className={styles.left}>
<MenuUnfoldOutlined />
<Breadcrumb items={breadList} style={{ marginLeft: '10px' }} />
</div>
<div className='right'>
<Switch
checkedChildren='暗黑'
unCheckedChildren='默认'
style={{ marginRight: '10px' }}
/>
<Dropdown menu={{ items, onClick }} trigger={['click']}>
<span className={styles.nickName}>{userInfo.userName}</span>
</Dropdown>
</div>
</div>
)
}
这里就是简单的使用。存储zustand需要用钩子引入多了一步,然后都需要声明类型在书写的时候,而且更新状态要用set获取。
编辑