Vue3和React版本-仿移动端点单App

前言

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

原型效果

shopping.png

目录结构

image.png

踩坑问题点及解决记录

1、样式穿透

在开发的过程中发现,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;

2、路由跳转

因为使用的 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;

3、动态样式

动态样式使用了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;

4、登录重定向

因为登录状态是使用的 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;

5、整体路由配置

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();

6、数据管理 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));

7、React 内置方法使用

7.1 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;

7.2 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 类似。

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


 本篇
Vue3和React版本-仿移动端点单App Vue3和React版本-仿移动端点单App
前言最近学习了慕课上 Vue3 移动端类似点单 App 的编码,想着用 ReactV6 版本也可以实现,模拟接口使用的是FastMock,于是开始了踩坑填坑之旅。首先粘一下两个版本库地址,有兴趣的小伙伴可以在 github 上 star✨
2023-03-31
下一篇 
西安旅游出行小攻略 西安旅游出行小攻略
一、准备建议❤️ 必备物品:身份证·学生证·士兵证·充电宝·洗漱用品·雨伞·防晒·适季衣服❤️ 西安地铁乘车方式: 在支付宝首页搜索西安地铁乘车码,根据提示一步步开通,便可以使用❤️ 疫情期间,根据西安防控规定,需要持有 48 小时内的核算
2021-12-04
  目录