学设计网站,做守望同人的网站,网架安装,昆明seo排名项目搭建 创建项目 # 使用npx创建项目
npx create-react-app my-react-app
# 进入项目目录
cd my-react-app
# 创建项目目录结构
mkdir -p src/{apis,assets,components,pages,store,utils}
touch src/{App.js,index.css,index.js} 使用npx create-react-app创建项目#xff0…项目搭建 创建项目 # 使用npx创建项目
npx create-react-app my-react-app
# 进入项目目录
cd my-react-app
# 创建项目目录结构
mkdir -p src/{apis,assets,components,pages,store,utils}
touch src/{App.js,index.css,index.js} 使用npx create-react-app创建项目进入项目目录后通过npm start启动。调整项目目录结构包括apis、assets、components、pages等多个文件夹。 使用技术 接入scss预处理器安装sass工具创建全局样式文件index.scss。 # 安装sass工具
npm i sass -D // 在src/index.scss中设置全局样式
body {font-family: Arial, sans-serif;background-color: #f4f4f4;
} 引入组件库antd安装后在Login页面测试Button组件。 # 安装antd组件库
npm i antd // 在src/pages/Login/index.jsx中使用Button组件
import React from react;
import { Button } from antd;const Login () {return (divButton typeprimary登录/Button/div);
};export default Login; 使用react-router-dom配置基础路由创建Layout和Login组件并配置路由规则。 # 安装react-router-dom
npm i react-router-dom // 在src/router/index.js中配置路由
import { createBrowserRouter } from react-router-dom;
import Login from ../pages/Login;
import Layout from ../pages/Layout;const router createBrowserRouter([{path: /,element: Layout /,},{path: /login,element: Login /,},
]);export default router; 通过craco工具包配置别名路径在craco.config.js中设置webpack别名并在jsconfig.json中配置VsCode提示。 # 安装craco工具包
npm i craco/craco -D // 在craco.config.js中配置别名
const path require(path);
module.exports {webpack: {alias: {: path.resolve(__dirname,src)}}
}; // 在package.json中修改scripts命令
scripts: {start: craco start,build: craco build,test: craco test,eject: react-scripts eject
} // 在src/router/index.js中使用别名
import { createBrowserRouter } from react-router-dom;
import Login from /pages/Login;
import Layout from /pages/Layout;const router createBrowserRouter([{path: /,element: Layout /,},{path: /login,element: Login /,},
]);export default router; // 在jsconfig.json中配置VsCode提示
{compilerOptions: {baseUrl: ./,paths: {/*: [src/*]}}
} 功能模块实现 登录模块 基本结构搭建 在Login/index.js创建登录页面结构引入antd组件使用/assets路径引入图片在Login/index.scss中设置样式。 import React from react;
import { Card, Form, Input, Button } from antd;
import logo from /assets/logo.png;
import ./index.scss;const Login () {return (div classNameloginCard classNamelogin-containerimg classNamelogin-logo src{logo} alt /FormForm.ItemInput sizelarge placeholder请输入手机号 //Form.ItemForm.ItemInput sizelarge placeholder请输入验证码 //Form.ItemForm.ItemButton typeprimary htmlTypesubmit sizelarge block登录/Button/Form.Item/Form/Card/div);
};export default Login; 表单校验实现 为Form组件设置validateTrigger为Form.Item组件设置name和rules属性进行表单校验。 import React from react;
import { Form, Input, Button } from antd;const Login () {return (Form validateTrigger{[onBlur]}Form.Itemnamemobilerules{[{ required: true, message: 请输入手机号 },{pattern: /^1[3-9]\d{9}$/,message: 手机号码格式不对}]}Input sizelarge placeholder请输入手机号 //Form.ItemForm.Itemnamecoderules{[{ required: true, message: 请输入验证码 },]}Input sizelarge placeholder请输入验证码 maxLength{6} //Form.ItemForm.ItemButton typeprimary htmlTypesubmit sizelarge block登录/Button/Form.Item/Form);
};export default Login; 获取登录表单数据 为Form组件设置onFinish属性在点击登录按钮时触发获取表单数据的函数。 import React from react;
import { Form, Input, Button } from antd;const Login () {const onFinish formValue {console.log(formValue);};return (Form onFinish{onFinish}Form.ItemInput sizelarge placeholder请输入手机号 //Form.ItemForm.ItemInput sizelarge placeholder请输入验证码 //Form.ItemForm.ItemButton typeprimary htmlTypesubmit sizelarge block登录/Button/Form.Item/Form);
};export default Login; 封装 request 工具模块 安装axios在utils/request.js中创建axios实例配置baseURL、请求拦截器和响应拦截器。 # 安装axios
npm i axios import axios from axios;const http axios.create({baseURL: http://example.com/api,timeout: 5000
});// 请求拦截器
http.interceptors.request.use(config {return config;
}, error {return Promise.reject(error);
});// 响应拦截器
http.interceptors.response.use(response {return response.data;
}, error {return Promise.reject(error);
});export { http }; 使用 Redux 管理 token 安装react-redux和reduxjs/toolkit在store中创建userStore切片设置token初始状态和setUserInfo等reducers封装fetchLogin异步方法。 # 安装react-redux和reduxjs/toolkit
npm i react-redux reduxjs/toolkit import { createSlice } from reduxjs/toolkit;
import { http } from /utils;const userStore createSlice({name: user,initialState: {token: },reducers: {setUserInfo(state, action) {state.token action.payload;}}
});const { setUserInfo } userStore.actions;
const userReducer userStore.reducer;const fetchLogin loginForm {return async dispatch {const res await http.post(/authorizations, loginForm);dispatch(setUserInfo(res.data.token));};
};export { fetchLogin };
export default userReducer; 实现登录逻辑 在Login组件中调用fetchLogin方法登录成功后跳转到首页并提示。 import React from react;
import { message } from antd;
import { useDispatch } from react-redux;
import { fetchLogin } from /store/modules/user;const Login () {const dispatch useDispatch();const onFinish async formValue {await dispatch(fetchLogin(formValue));message.success(登录成功);};return (divform onSubmit{onFinish}{/* 登录表单字段 */}/form/div);
};export default Login; token 持久化 封装setToken、getToken和clearToken方法在userStore中setUserInfo时将token存入本地。 // 在/utils/token.js中封装存取方法
const TOKENKEY token_key;function setToken(token) {return localStorage.setItem(TOKENKEY, token);
}function getToken() {return localStorage.getItem(TOKENKEY);
}function clearToken() {return localStorage.removeItem(TOKENKEY);
}export {setToken,getToken,clearToken
}; // 在userStore中使用token持久化方法
import { createSlice } from reduxjs/toolkit;
import { http } from /utils;
import { getToken, setToken } from /utils/token;const userStore createSlice({name: user,initialState: {token: getToken() || },reducers: {setUserInfo(state, action) {state.token action.payload;setToken(state.token);}}
});export default userStore; 请求拦截器注入 token 在request.js的请求拦截器中判断是否有token有则添加到请求头Authorization中。 // 在utils/request.js中注入token
import axios from axios;const http axios.create({baseURL: http://example.com/api,timeout: 5000
});http.interceptors.request.use(config {const token getToken();if (token) {config.headers.Authorization Bearer ${token};}return config;
}, error {return Promise.reject(error);
});http.interceptors.response.use(response {return response.data;
}, error {return Promise.reject(error);
});export { http }; 路由鉴权实现 在components/AuthRoute/index.jsx中创建路由鉴权高阶组件判断本地是否有token决定是否重定向到登录页面。 import React from react;
import { Navigate } from react-router-dom;
import { getToken } from /utils;const AuthRoute ({ children }) {const isToken getToken();if (isToken) {return {children}/;} else {return Navigate to/login replace /;}
};export default AuthRoute; // 在src/router/index.js中使用AuthRoute组件
import { createBrowserRouter } from react-router-dom;
import Login from /pages/Login;
import Layout from /pages/Layout;
import AuthRoute from /components/AuthRoute;const router createBrowserRouter([{path: /,element: AuthRouteLayout //AuthRoute,},{path: /login,element: Login /,},
]);export default router; Layout 模块 基本结构和样式 reset 在pages/Layout/index.js中使用antd/Layout组件创建页面结构引入antd的Menu和Popconfirm等组件设置样式并安装normalize.css进行样式 reset。 import React from react;
import { Layout, Menu, Popconfirm } from antd;
import { HomeOutlined, DiffOutlined, EditOutlined, LogoutOutlined } from ant-design/icons;
import ./index.scss;
import normalize.css;const { Header, Sider } Layout;const items [{label: 首页,key: 1,icon: HomeOutlined /,},{label: 文章管理,key: 2,icon: DiffOutlined /,},{label: 创建文章,key: 3,icon: EditOutlined /,},
];const GeekLayout () {return (LayoutHeader classNameheaderdiv classNamelogo /div classNameuser-infospan classNameuser-name用户名/spanspan classNameuser-logoutPopconfirm title是否确认退出 okText退出 cancelText取消LogoutOutlined / 退出/Popconfirm/span/div/HeaderLayoutSider width{200} classNamesite-layout-backgroundMenumodeinlinethemedarkdefaultSelectedKeys{[1]}items{items}style{{ height: 100%, borderRight: 0 }}/Menu/SiderLayout classNamelayout-content style{{ padding: 20 }}内容/Layout/Layout/Layout);
};export default GeekLayout; 二级路由配置 在pages目录创建Home、Article、Publish页面文件夹在router/index.js中配置嵌套子路由在Layout中配置二级路由出口使用Link修改左侧菜单内容实现路由切换。 // 在pages目录创建Home.jsx
import React from react;const Home () {return div首页内容/div;
};export default Home; // 在pages目录创建Article.jsx
import React from react;const Article () {return div文章管理内容/div;
};export default Article; // 在pages目录创建Publish.jsx
import React from react;const Publish () {return div发布文章内容/div;
};export default Publish; // 在src/router/index.js中配置二级路由
import { createBrowserRouter } from react-router-dom;
import Login from /pages/Login;
import Layout from /pages/Layout;
import Publish from /pages/Publish;
import Article from /pages/Article;
import Home from /pages/Home;
import { AuthRoute } from /components/AuthRoute;const router createBrowserRouter([{path: /,element: (AuthRouteLayout //AuthRoute),children: [{index: true,element: Home /,},{path: article,element: Article /,},{path: publish,element: Publish /,},],},{path: /login,element: Login /,},
]);export default router; // 在Layout组件中配置二级路由出口
import React from react;
import { Outlet } from react-router-dom;const GeekLayout () {return (Layout classNamelayout-content style{{ padding: 20 }}Outlet //Layout);
};export default GeekLayout; 路由菜单点击交互实现 为Menu组件设置onClick属性实现点击菜单跳转路由通过useLocation获取当前路由路径实现菜单反向高亮。 import React from react;
import { Outlet, useNavigate } from react-router-dom;
import { HomeOutlined, DiffOutlined, EditOutlined, LogoutOutlined } from ant-design/icons;const items [{label: 首页,key: /,icon: HomeOutlined /,},{label: 文章管理,key: /article,icon: DiffOutlined /,},{label: 创建文章,key: /publish,icon: EditOutlined /,},
];const GeekLayout () {const navigate useNavigate();const menuClick route {navigate(route.key);};return (LayoutHeader classNamemain-headerdiv classNamelogo /div classNameuser-infospan classNameuser-name用户名/spanspan classNameuser-logoutPopconfirm title是否确认退出 okText退出 cancelText取消LogoutOutlined / 退出/Popconfirm/span/div/HeaderLayoutSider width{200} classNamesite-layout-backgroundMenumodeinlinethemedarkselectedKeys{[1]}items{items}style{{ height: 100%, borderRight: 0 }}onClick{menuClick}/Menu/SiderLayout classNamelayout-content style{{ padding: 20 }}Outlet //Layout/Layout);
};export default GeekLayout; // 菜单反向高亮实现
import React from react;
import { Outlet, useLocation } from react-router-dom;
import { HomeOutlined, DiffOutlined, EditOutlined, LogoutOutlined } from ant-design/icons;const items [{label: 首页,key: /,icon: HomeOutlined /,},{label: 文章管理,key: /article,icon: DiffOutlined /,},{label: 创建文章,key: /publish,icon: EditOutlined /,},
];const GeekLayout () {const location useLocation();const selectedKey location.pathname;return (LayoutHeader classNamemain-headerdiv className 展示个人信息 在store/userStore.js中编写获取用户信息的逻辑在Layout组件中触发fetchUserInfo方法获取信息并渲染用户名。 // store/userStore.js
import { createSlice } from reduxjs/toolkit;
import { http } from /utils;
import { getToken, setToken } from /utils;const userStore createSlice({name: user,initialState: {token: getToken() || ,userInfo: {}},reducers: {setUserToken(state, action) {state.token action.payload;setToken(state.token);},setUserInfo(state, action) {state.userInfo action.payload;},clearUserInfo(state) {state.token ;state.userInfo {};clearToken();}}
});// 解构出actionCreater
const { setUserToken, setUserInfo, clearUserInfo } userStore.actions;
// 获取reducer函数
const userReducer userStore.reducer;const fetchLogin (loginForm) {return async (dispatch) {const res await http.post(/authorizations, loginForm);dispatch(setUserToken(res.data.token));};
};const fetchUserInfo () {return async (dispatch) {const res await http.get(/user/profile);dispatch(setUserInfo(res.data));};
};export { fetchLogin, fetchUserInfo, clearUserInfo };
export default userReducer; 退出登录实现 为Popconfirm添加确认回调事件在store/userStore.js中新增clearUserInfo方法删除token和用户信息在回调事件中调用该方法并返回登录页面。 // pages/Layout/index.js
import React, { useEffect } from react;
import { Layout, Menu, Popconfirm } from antd;
import {HomeOutlined,DiffOutlined,EditOutlined,LogoutOutlined,
} from ant-design/icons;
import { useDispatch, useSelector } from react-redux;
import { fetchUserInfo } from /store/modules/user;const { Header, Sider } Layout;const items [// 菜单配置项
];const GeekLayout () {const dispatch useDispatch();const name useSelector(state state.user.userInfo.name);useEffect(() {dispatch(fetchUserInfo());}, [dispatch]);const loginOut () {dispatch(clearUserInfo());// 假设这里有合适的导航函数替换为实际的导航逻辑// navigate(/login); };return (LayoutHeader classNameheaderdiv classNamelogo /div classNameuser-infospan classNameuser-name{name}/spanspan classNameuser-logoutPopconfirmtitle是否确认退出okText退出cancelText取消onConfirm{loginOut}LogoutOutlined / 退出/Popconfirm/span/div/HeaderLayoutSider width{200} classNamesite-layout-backgroundMenumodeinlinethemedarkdefaultSelectedKeys{[1]}items{items}style{{ height: 100%, borderRight: 0 }}/Menu/SiderLayout classNamelayout-content style{{ padding: 20 }}{/* 页面内容 */}/Layout/Layout/Layout);
};export default GeekLayout; 处理 Token 失效 在http.interceptors.response中判断响应状态码为401时清除token跳转到登录页面并刷新页面。 // 在http.js假设是配置axios请求相关的文件中处理Token失效
import axios from axios;const http axios.create({baseURL: http://example.com/api,timeout: 5000
});http.interceptors.response.use((response) {return response.data;
}, (error) {if (error.response error.response.status 401) {// 假设这里有合适的获取和清除token的函数替换为实际的逻辑const token getToken();if (token) {clearToken();}// 假设这里有合适的导航函数替换为实际的导航逻辑// navigate(/login); window.location.reload();}return Promise.reject(error);
});export { http };