Vue3和ReactV6版本的移动端点单App


theme: fancy

最近学习了慕课上Vue3移动端类似点单App的编码,想着用ReactV6版本也可以实现,模拟接口使用的是FastMock,于是开始了踩坑填坑之旅。首先粘一下两个版本库地址,有兴趣的小伙伴可以在github上star✨哦。

先部分截图看一下效果:

image.png
image.png

目录结构:

image.png

踩坑问题点及解决记录:

  • 样式穿透

    在开发的过程中发现,React如果定义了相同的类名,样式会相互影响。所以要么定义不同的类名,要么采用其他的方式。我是使用了styled-components这个npm库,使用方式:

import styled from 'styled-components';

const Wrapper = styled.div`
  overflow-y: scroll;
  position: absolute;
  top: 0;
  bottom: 0.5rem;
  left: 0;
  right: 0;
  background: rgb(248, 248, 248);
`;
function ComponentDemo() {
    return <Wrapper>
        我是具有样式的div标签
    </Wrapper>
}

export default ComponentDemo;
  • 路由跳转

    因为使用的React版本是V6,所以与老版本的一些方法还是有一些区别的。可以随意跳转到其他页面,使用了react-router-dom库里面的useNavigate方法,使用方式:

import {useNavigate} from 'react-router-dom';

function ComponentDemo() {
    const navigate = useNavigate();
    const handleRouterClick = () => {
        navigate('/home');
    };
    return <div onClick={handleRouterClick}>
        点我跳转到首页
    </div>
}

export default ComponentDemo;
  • 动态样式

    动态样式使用了classnames库,使用方式:

import classnames from 'classnames';

function ComponentDemo() {
    const isDynamicClass = true;
    return <div className={classnames(
        'class__a',
        {'class__b': isDynamicClass}
    )}>
        我有动态样式 class__b
    </div>
}

export default ComponentDemo;
  • 登录重定向

    因为登录状态是使用的 localStorage,所以可以根据设置的状态值进行判断,例如在登录页面里面线获取localStorage.isLogin,如果isLogin为true,则代表已经登陆,那么访问login就需要跳转到首页(/home),使用方式如下:

import {Navigate} from 'react-router-dom';

function ComponentDemo() {
    const isLogin = localStorage.isLogin;

    return <div>
        { isLogin && <Navigate to={'/home'} replace />} {/*我要跳转到首页*/}
        {!isLogin && <div>我是登录页</div>}
    </div>
}

export default ComponentDemo;
  • 整体路由配置

    router下的index.js

import Home from '../views/home/Home'
import CartList from '../views/cartList/CartList'
import OrderList from '../views/orderList/OrderList'
import OwnPage from '../views/ownPage/OwnPage'
import OrderConfirmation from '../views/orderConfirmation/OrderConfirmation'
import Shop from '../views/shop/Shop'
import Login from '../views/login/Login'
import Register from '../views/register/Register'

const router = [
    {
        path: '/',
        element: <Home/>,
        exact: true
    },
    {
        path: '/cartList',
        element: <CartList/>,
        exact: true
    },
    {
        path: '/orderList',
        element: <OrderList />,
        exact: true
    },
    {
        path: '/ownPage',
        element: <OwnPage />,
        exact: true
    },
    {
        path: '/orderConfirmation/:id',
        element: <OrderConfirmation/>,
        exact: true
    },
    {
        path: '/shop/:id',
        element: <Shop />,
        exact: true
    },
    {
        path: '/login',
        element: <Login />,
        exact: true
    },
    {
        path: '/register',
        element: <Register />,
        exact: true
    }
];

export default router

主入口 index.js

import React from "react";
import ReactDOM from "react-dom/client";
import reportWebVitals from "./reportWebVitals";
import {BrowserRouter as Router, useRoutes} from "react-router-dom";
import router from './router/index'
import "./style/index.scss";

import {Provider} from 'react-redux'
import store from './store/index'

const GetRoutes = () => {
    return useRoutes(router)
}

const SetRoutes = () => {
    return (
        <Router>
            <GetRoutes/>
        </Router>
    )
}

ReactDOM.createRoot(document.getElementById("root")).render(
    // 根组件配置:Provider声明式开发,提供给子组件数据管理功能
    <Provider store={store}>
        <SetRoutes/>
    </Provider>
)
/*
* 如果你想开始测量你的应用程序的性能,传递一个函数
* 记录结果(例如:reportWebVitals(console.log))
* 或发送到分析端点。 了解更多:https://bit.ly/CRA-vitals
*/
reportWebVitals();
  • 数据管理Redux配置

    store文件夹下配置:

index.js

/**
* 主仓库配置
*/
import {createStore, compose, applyMiddleware} from "redux";
import thunk from 'redux-thunk'
import reducer from './reducer'

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose; // // 引入Redux可视化插件
const store = createStore(
   reducer, // 仓库数据
   composeEnhancers( // 组合中间件
       applyMiddleware(thunk) // 异步用中间件
   )
)

export default store

reducer.js

import {combineReducers} from 'redux';
import { reducer as CartListReducer } from '../views/shop/store/index';

export default combineReducers({
    cartList: CartListReducer
})

因为功能并不复杂,全局只配置了一个购物车数据,并对他进行操作。在相对应的文件夹下建立store,并配置对应的方法,以下是实现的方式:在shop文件夹下新建

  • actionCreator.js:负责统一管理数据状态改变的函数执行,给reducer分配相应的action
import * as actionTypes from './constants'

export const changeCartItemInfo = (data) => ({
    type: actionTypes.CHANGE_CART_ITEM_INFO,
    data
});

export const changeCartItemCheckInfo = (data) => ({
    type: actionTypes.CHANGE_CART_ITEM_CHECK_INFO,
    data
});

export const setCartItemCheckedInfo = (data) => ({
    type: actionTypes.SET_CART_ITEM_CHECKED,
    data
});

export const cleanCartProductInfo = (data) => ({
    type: actionTypes.CLEAN_CART_PRODUCT,
    data
});

export const clearCartDataInfo = (data) => ({
    type: actionTypes.CLEAR_CART_DATA_INFO,
    data
});

export const changeShopNameInfo = (data) => ({
    type: actionTypes.CHANGE_SHOP_NAME,
    data
});
  • constants.js:定义的常量
export const CHANGE_CART_ITEM_INFO = 'CHANGE_CART_ITEM_INFO';
export const CHANGE_CART_ITEM_CHECK_INFO = 'CHANGE_CART_ITEM_CHECK_INFO';
export const CLEAN_CART_PRODUCT = 'CLEAN_CART_PRODUCT';
export const SET_CART_ITEM_CHECKED = 'SET_CART_ITEM_CHECKED';
export const CHANGE_SHOP_NAME = 'CHANGE_SHOP_NAME';
export const CLEAR_CART_DATA_INFO = 'CLEAR_CART_DATA_INFO';
  • reducers.js:负责根据action值,做相应操作,以实现数据流管理
import * as actionTypes from './constants'

const setLocalCartList = (state) => {
    const {cartList} = state
    const cartListString = JSON.stringify(cartList)
    localStorage.cartList = cartListString
};

const getLocalCartList = () => {
    try {
        return JSON.parse(localStorage.cartList)
    } catch {
        return {}
    }
};
const defaultState = {
    cartList: getLocalCartList()
};

const reducers = (state = defaultState, action) => {
    switch (action.type) {
        case actionTypes.CHANGE_CART_ITEM_INFO:
            // 执行一些操作
            return {
                ...state
            };
        case actionTypes.CHANGE_CART_ITEM_CHECK_INFO:
            // 执行一些操作
            return {...state};

        case actionTypes.CHANGE_SHOP_NAME:
            // 执行一些操作
            return {...state};

        case actionTypes.CLEAR_CART_DATA_INFO:
            // 执行一些操作
            return {...state};

        default:
            return {...state};
    }
}

export default reducers;
  • index.js:负责整理仓库功能,并统一向外输出
import reducer from './reducers'
import * as actionCreators from './actionCreator'
import * as constants from './constants'

export {
    reducer,
    actionCreators,
    constants
}

在组件里面使用:

import {memo} from 'react';
import {connect} from "react-redux";
import {changeCartItemInfo} from './store/actionCreator';
function ComponentDemo(props) {
    const {changeCartItemInfoDispatch} = props;

    const changeCartItem = (data) => {
        changeCartItemInfoDispatch(data);
    };
    return <div onClick={() => changeCartItem(data)}>
        我是具有样式的div标签
    </div>
}

const mapStateToProps = (state) => {
    return {...state};
};

const mapDispatchToProps = (dispatch) => {
    return {
        changeCartItemInfoDispatch(state) {
            dispatch(changeCartItemInfo(state));
        }
    }
};
export default connect(mapStateToProps, mapDispatchToProps)(memo(ComponentDemo));
  • React内置方法使用

  • useEffect

import {useEffect, useState} from "react";
import {get} from '../../utils/request'

function ComponentDemo() {
    const [data, setData] = useState({
        currentTab: 'all'
    });
    useEffect(() => {
        setData({shopId: location.pathname.split('/')[2]});
        const getContentData = async () => {
            const res = await get(`/api/shop/${data.shopId}/products`, {
                tab: data.currentTab
            })
            if (res?.errno && res?.data) {
                setData({list: res.data.filter(item => item.tab === data.currentTab)})
            }
        };
        getContentData();
    }, [data.currentTab]);
    return <div>
        {data}
    </div>
}
export default ComponentDemo;
  • useState

    在使用过程中发现,useState里面可能会定义多个变量,如果只想更新其中的一个或者两个变量,使用useState会变得不够方便,所以utils文件夹下添加了useMergeState.js文件,可以修改其中一个变量但是不影响其余变量的使用:

import {useState} from 'react';

const useMergeState = (initialState) => {
    const [state, setState] = useState(initialState);
    const setMergeState = (newState) => 
    setState((prevState) => ({...prevState, ...newState}));
    return [state, setMergeState];
}

export default useMergeState;

使用setMergeState和使用useState类似。

最后,以上是在使用过程中会出现卡壳的情况,分享一下,希望有点帮助。有问题可以评论哦。

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容