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


目录结构:

踩坑问题点及解决记录:
-
样式穿透
在开发的过程中发现,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类似。
最后,以上是在使用过程中会出现卡壳的情况,分享一下,希望有点帮助。有问题可以评论哦。
转载自:https://juejin.cn/post/7172475750400917518