react项目--博客管理


本篇文章总结一个开发的react项目—博客系统

技术栈

React、react-redux、react-router 6,Ant Design,es6,sass,webpack

登录存信息

在这里插入图片描述

主要业务逻辑:redux存信息
redux如何使用,可以看这篇博客 redux使用
创建一个store文件夹
在这里插入图片描述
在index.js文件夹里面写总代理

import { configureStore } from "@reduxjs/toolkit"; // configureStore (): 包装 createStore 以提供简化的配置选项和良好的默认设置。它可以自动组合你的slice reducers,添加你提供的任何 Redux 中间件,默认包括 Redux-thunk,并启用 Redux DevTools 扩展。
import userReducer from "./modules/user";

export default configureStore({
    reducer:{
        user:userReducer
    }
})

在user.js里面写用户的状态管理

首先导入createSlice,用于接受 reducer 函数的对象、片名和初始状态值,并自动生成带有相应动作创建器和动作类型的 slice reducer。

import { createSlice } from "@reduxjs/toolkit";
import { setToken as _setToken,getToken, removeToken } from "@/utils";
import { loginAPI,getProfileAPI } from "@/apis/user"; //导入需要的其他api

const userStore = createSlice({
}),在createSlice写主要逻辑
定义数据状态

 initialState:{
        token:getToken() || '',
        userInfo:{}
    },

编写reducer逻辑

reducers:{
        setToken(state,action){
            state.token = action.payload

            _setToken(action.payload)
            //action.payload是这个action重新包装后的return返回结果,是createSlice特有的
        },
        setUserInfo(state,action){
            state.userInfo=action.payload
        },
        clearUserInfo(state){
            state.token = ''
            state.userInfo = {}
            removeToken()
        } 
    }
//解构出actionCreater
const { setToken,setUserInfo,clearUserInfo } =userStore.actions

调用接口,获取数据

//异步请求

const fetchLogin = (loginForm) =>{
 return async (dispatch)=>{
    const res = await loginAPI(loginForm)
    dispatch(setToken(res.data.token))
 }
}
//....其余获取个人信息的相关接口

导出reducer函数和封装的函数

//获取reducer函数
const userReducer = userStore.reducer

export {setToken,fetchLogin,fetchUserInfo,clearUserInfo}

export default userReducer

完整的redux就是这些了

配置token

在专门文件里面封装token配置

在这里插入图片描述

//封装token方法

const TOKENKEY='token_key'

function setToken(token){
    localStorage.setItem(TOKENKEY,token)
}

function getToken(){
    return localStorage.getItem(TOKENKEY)
}

function removeToken(){
    localStorage.removeItem(TOKENKEY)
}



export{
    setToken,
    getToken,
    removeToken
}

hooks使用

详细hooks可看这篇文章:react—hooks

获取列表示例,展示如何使用hooks

//获取频道列表的逻辑
import { useState,useEffect } from "react"
import { getChannelAPI }from '@/apis/article'


function useChannel(){
    const [channelList, setChannels] = useState([])

    // 调用接口
    useEffect(() => {
      const getChannelList = async () => {
        const res = await getChannelAPI()
        setChannels(res.data.channels)
  
      }
      getChannelList()
  
    }, [])
    return {channelList}
}

export {useChannel}

路由配置

配置路由守卫,无token信息跳转登录页

import { getToken } from '@/utils'
import { Navigate } from 'react-router-dom'

const AuthRoute = ({ children }) => {
  const isToken = getToken()
  if (isToken) {
    return <>{children}</>
  } else {
    return <Navigate to="/login" replace />
  }
}

export default AuthRoute

基本路由配置

import AuthRoute from "@/components/AuthRoute";

import { Suspense, lazy } from "react";
//<Suspense> 允许在子组件完成加载前展示后备方案。lazy 就是懒加载
import { createBrowserRouter } from "react-router-dom"; //创建路由
//createBrowserRouter底层是用到了h5的新特性history,这个方法可以实现修改地址栏地址而不会向后端发起请求,并且history这个对象本身就提供了很多控制页面跳转,前进后退等方法。而createHashRouter则是利用了锚点跳转不发起请求的特点,也就是你在网络地址后面加 上#,#后面的内容无论怎么改变都不会引起浏览器发起网络请求,然后通过监听onhashchange事件来监听这个锚点的变化,以此来匹配配置的路由。

//路由懒加载
const Home = lazy(()=>import('@/pages/Home'))
const Acticles = lazy(()=>import('@/pages/Acticles'))
//....其余路由



```javascript
const router= createBrowserRouter([
    {
        path:"/",
        element:<AuthRoute> <Layout/> </AuthRoute>,// 路由守卫嵌套
        children:[
            {
                path:'all',
                element:<Suspense fallback={'加载中'}><Home/></Suspense>,
                children:[
                    {
                        index:true, //默认加载路由
                        element:<Suspense fallback={'加载中'}><All/></Suspense> 
                    }, ...........其余配置

在页面设置路由出口

import { Outlet } from 'react-router-dom'
<div style={{backgroundColor:'white'}}>
        <Menu
            mode="horizontal"
            selectedKeys={selectkey}
            onClick={onMenuClick}
            items={items}
            ></Menu>
            <Outlet></Outlet> //设置子路由出口
 </div>)

各页面技术总结

首页

利用useLocation进行反向高亮

 //反向高亮
  const location = useLocation();
  const selectkey  =location.pathname


//触发个人信息的action

 const dispatch = useDispatch()
  useEffect(()=>{
    dispatch(fetchUserInfo())
  },[dispatch])
//获取store内的个人信息
  const name= useSelector(state=>state.user.userInfo.name)

在这里插入图片描述
选择时,高亮效果没实现,增加以下两行代码

const isHomeSelected = location.pathname.startsWith('/all');
selectedKeys={[isHomeSelected ? '/all' : selectkey]}

发布文章

回填数据

const [searchParams]=  useSearchParams()
  const articleId  =searchParams.get('id')
  // console.log(articleId);
  const [form]= Form.useForm()
  useEffect(()=>{
    //通过id获取数据
   async function getArticleDetail(){
    const res=  await getArticleById(articleId)
    const data = res.data
    form.setFieldsValue({...data,
    type:data.cover.type,
    })
    //回填图片列表
    setImageType(data.cover.type)
    setImageList(data.cover.images.map(url=>{
      
      return {url}
    }))
   }
   //只有有id才能回填
   if(articleId){
    getArticleDetail()
   }
   
  },[articleId,form])

文章详情页

在这里插入图片描述
内容是html形式,转化为文本类型,封装了一个函数

 function removeHTMLTags(html) {
    const doc = new DOMParser().parseFromString(html, 'text/html');
    return doc.body.textContent || '';
  }

个人主页

在这里插入图片描述
先调用redux里面的方法 ,获取个人信息

useEffect(() => {
    dispatch(fetchUserInfo());
  }, [dispatch]);

问题:由于使用了一次useEffect,当页面再次渲染时,数据回填不上
解决方法:
进行if判断,有id机进行回填,实现异步操作

useEffect(()=>{
    if (data) {
      //进行数据回填
      form.setFieldsValue({ name, gender: gender === 0 ? '男' : '女', intro });
      //回填照片
      console.log(photo);
      let url = [{
        uid: '-1',
        name: 'image1.png',
        status: 'done',
        url:data.photo,
        description: '这是第一张图片'
      }]
      console.log(url);
      setImageUrl(url)
      
  }},[data])

问题:生日时间第一次获取到null,数据回填成默认形式,不是应有的数据
解决方法:

//在组件内设置key,当key里面值变化时,组件重新渲染
<Form.Item
          label="生日"
        >
          <DatePicker defaultValue={defaultValue} key={defaultValue} onChange={getDate} />
        </Form.Item>

问题:头像设置问题,在有头像时,不显示上传部分
解决方法:

//用三元表达式进行判断
<Upload
               name="avatar"
              listType="picture-circle"
              className="avatar-uploader"
              showUploadList
              action="https://660d2bd96ddfa2943b33731c.mockapi.io/api/upload"
              onChange={onChange}
              maxCount={1}
              fileList={imageUrl}
            >
              {count===0?<div style={{ marginTop: 8 }}>
                <PlusOutlined />
              </div>:''}
            </Upload>

分类页

在这里插入图片描述
由于页面一样,数据不一样,先封装个模板,等待传来的参数
在这里插入图片描述

const Item = (props) => {
    //得到传来的list
    const { parameter } = props;
    // console.log('/',parameter);
    const navigate = useNavigate()
    .....其余代码
    }

问题:分类是根据标签列表实现的,所以要先进行判断,以前端页面举例

const Before=()=>{
 
    const [belist,setBelist] = useState([])
    //判断
    getList().then(res=>{
    let list = res.filter((item)=>{
        return item.channel_id===1 || item.channel_id===6 || item.channel_id===15 || item.channel_id===17 || item.channel_id===23
    })
    setBelist(list)
    })
    
    
    return (
        <div>
        //传参
            <Item parameter= {belist}></Item>
        </div>
    )

}

其他页面类似

  • 10
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值