Taro打造电商项目实战

Taro多端电商项目(仿京东)完整开发指南

本指南面向前端开发者,全面覆盖Taro多端电商项目(仿京东)从规划到落地的全流程。通过"需求分析-框架设计-模块实现-测试部署"的逻辑脉络,结合京东电商核心业务场景,提供可复用的代码示例与标准化的开发流程,助力开发者快速搭建支持微信小程序、H5、App多端运行的电商系统。

一、需求分析

需求分析是项目开发的基础,需明确目标用户、核心业务场景及功能优先级,为后续开发提供方向指引。

1.1 用户画像

  • 核心用户:普通消费者 - 年龄18-45岁,习惯线上购物,关注商品性价比、物流速度,常用微信小程序、H5或App访问电商平台,需求是快速找到商品、便捷下单、实时跟踪订单。

  • 次要用户:商家 - 需入驻平台发布商品、管理订单、处理售后,需求是简单易用的商家管理功能。

  • 平台运营者 - 负责平台活动策划、商品审核、数据监控,需求是完善的运营管理后台与数据统计功能。

1.2 核心业务场景

  1. 商品浏览与筛选:用户通过分类导航、搜索框查找商品,可按价格、销量、评分等条件筛选排序,查看商品详情(图文介绍、规格、评价)。

  2. 购物车管理:用户添加商品到购物车,支持修改商品数量、选择规格、删除商品,结算时自动计算金额。

  3. 下单与支付:用户确认订单信息(收货地址、商品清单、金额),选择支付方式(微信支付、支付宝)完成付款,生成订单记录。

  4. 订单管理:用户查看订单列表(待付款、待发货、待收货、已完成),跟踪物流信息,申请售后(退款、退货)。

  5. 用户中心:用户登录/注册、管理个人信息(头像、昵称)、收货地址、查看收藏商品、浏览历史。

  6. 营销活动:平台开展秒杀、优惠券、满减等活动,用户参与活动享受优惠。

1.3 功能优先级

优先级 功能模块 说明
P0(核心必做) 商品展示、搜索筛选、购物车、下单支付、订单列表、用户登录注册、收货地址管理 保障电商核心交易流程顺畅
P1(重要功能) 商品评价、物流跟踪、售后申请、收藏商品、浏览历史 提升用户购物体验,完善交易闭环
P2(次要功能) 营销活动(秒杀、优惠券)、商家管理、数据统计、消息推送 提升平台竞争力与运营能力,可后续迭代

二、功能说明

本节详细罗列核心模块功能,并明确多端适配要求,确保功能在不同平台均能正常运行且体验一致。

2.1 核心模块功能

2.1.1 商品展示模块

  • 首页商品推荐:展示热门商品、新品、活动商品,支持轮播图展示活动海报。

  • 商品分类:按一级分类、二级分类展示商品,支持分类导航切换。

  • 商品详情:展示商品图文介绍、规格参数、价格、库存、销量,提供加入购物车、立即购买按钮。

  • 商品评价:展示用户评价列表,支持按好评、中评、差评筛选,显示评价图片。

2.1.2 搜索筛选模块

  • 关键词搜索:支持输入商品名称、关键词搜索,显示搜索历史、热门搜索推荐。

  • 高级筛选:支持按价格区间、销量高低、评分高低、品牌、规格等条件筛选。

  • 搜索结果排序:支持默认、价格、销量、评分排序,显示筛选结果数量。

2.1.3 购物车模块

  • 商品管理:添加商品到购物车,支持修改商品数量(增减、直接输入)、选择商品规格。

  • 批量操作:支持全选/取消全选、批量删除商品。

  • 价格计算:自动计算选中商品的总金额、优惠金额,显示实付金额。

  • 库存校验:结算前校验商品库存,提示缺货商品。

2.1.4 订单管理模块

  • 订单创建:确认收货地址、商品清单、金额后生成订单,支持选择发票类型。

  • 订单列表:按订单状态(待付款、待发货、待收货、已完成、已取消)分类展示,显示订单编号、时间、金额、商品缩略图。

  • 订单详情:展示订单完整信息(商品清单、收货信息、支付信息、物流信息)。

  • 订单操作:待付款订单支持付款、取消;待收货订单支持确认收货;已完成订单支持申请售后、查看评价。

2.1.5 支付集成模块

  • 支付方式选择:支持微信支付(小程序/H5/App)、支付宝支付(H5/App)。

  • 支付流程:唤起支付接口,处理支付成功、失败、取消回调,更新订单状态。

  • 支付记录:查看支付历史,显示支付时间、金额、支付方式、订单编号。

2.1.6 用户中心模块

  • 登录/注册:支持手机号验证码登录、微信快捷登录(小程序/App)、账号密码登录。

  • 个人信息管理:修改头像、昵称、性别、生日等个人信息。

  • 收货地址管理:添加、编辑、删除收货地址,设置默认地址,支持地址智能联想。

  • 收藏与历史:查看收藏商品列表、浏览历史记录,支持取消收藏、清空历史。

2.2 多端适配要求

适配平台 适配要求 特殊处理
微信小程序 1. 遵循小程序界面规范,适配不同屏幕尺寸;2. 保证页面加载速度,避免过大图片;3. 兼容小程序基础库最新3个版本 1. 支付集成微信支付SDK;2. 登录使用微信小程序登录接口;3. 采用小程序分包加载优化体积
H5 1. 响应式布局,适配PC、平板、手机等不同设备;2. 兼容主流浏览器(Chrome、Firefox、Safari、Edge);3. 优化移动端触摸体验 1. 支付集成微信H5支付、支付宝H5支付;2. 处理浏览器兼容性问题(如CSS3属性、JS API);3. 支持地址栏分享功能
App(React Native) 1. 适配iOS、Android不同系统版本及机型;2. 保证页面流畅度,减少卡顿;3. 遵循原生App交互规范 1. 集成原生支付SDK;2. 使用原生组件优化体验(如导航栏、tabbar);3. 处理离线缓存、网络状态监听

三、项目框架设计

基于Taro框架特性,结合电商项目需求,进行技术栈选型、目录结构设计、状态管理等核心框架设计,确保项目架构清晰、可扩展。

3.1 技术栈选型依据

选型核心原则:多端适配兼容性好、开发效率高、生态完善、性能优异,具体选型如下:

  • 核心框架:Taro 3.x - 支持一套代码编译为多端应用,兼容性强,API设计贴近React,前端开发者易上手。

  • 开发语言:TypeScript - 提供类型校验,减少运行时错误,提升代码可维护性,适配大型电商项目开发。

  • UI组件库:Taro UI Vue3 / NutUI - 提供丰富的电商类组件(商品卡片、购物车、订单列表等),支持多端适配,可直接复用。

  • 状态管理:Redux Toolkit - 简化Redux使用流程,内置中间件,适合管理电商项目复杂状态(购物车、用户信息、订单状态等)。

  • 路由管理:Taro 内置路由 - 统一的路由配置方式,支持多端路由跳转、参数传递,适配不同平台路由特性。

  • API请求:Axios + Taro 请求适配 - Axios功能强大,支持拦截器、请求取消等,结合Taro适配多端请求特性。

  • 样式解决方案:SCSS + CSS Modules - SCSS支持嵌套、变量、混合等特性,CSS Modules避免样式冲突,提升样式可维护性。

  • 构建工具:Taro CLI - 内置构建打包能力,支持多端差异化构建,可配置自定义构建规则。

3.2 目录结构设计

采用模块化、分层的目录结构,便于团队协作与后期维护:

plaintext 复制代码
src/
├── api/                # API请求封装
│   ├── index.ts        # 请求拦截器、响应拦截器配置
│   ├── goods.ts        # 商品相关接口
│   ├── cart.ts         # 购物车相关接口
│   ├── order.ts        # 订单相关接口
│   └── user.ts         # 用户相关接口
├── assets/             # 静态资源
│   ├── images/         # 图片资源(分模块存放)
│   ├── styles/         # 全局样式、SCSS变量、混合
│   └── fonts/          # 字体资源
├── components/         # 公共组件
│   ├── common/         # 基础组件(按钮、输入框、加载中)
│   ├── goods/          # 商品相关组件(商品卡片、商品详情页组件)
│   ├── cart/           # 购物车相关组件(购物车项、数量选择器)
│   ├── order/          # 订单相关组件(订单项、订单状态标签)
│   └── user/           # 用户相关组件(地址项、登录表单)
├── pages/              # 页面组件(按模块划分)
│   ├── index/          # 首页
│   ├── goods/          # 商品相关页面(列表、详情、评价)
│   ├── search/         # 搜索页面
│   ├── cart/           # 购物车页面
│   ├── order/          # 订单相关页面(列表、详情、确认订单)
│   ├── pay/            # 支付页面
│   └── user/           # 用户中心相关页面
├── store/              # 状态管理(Redux Toolkit)
│   ├── index.ts        # store配置
│   ├── slices/         # 状态切片(userSlice、cartSlice、goodsSlice)
│   └── hooks.ts        # 自定义hooks(useAppDispatch、useAppSelector)
├── router/             # 路由配置
│   ├── index.ts        # 路由表配置
│   └── hooks.ts        # 路由相关hooks(导航守卫、跳转方法封装)
├── utils/              # 工具函数
│   ├── index.ts        # 通用工具(格式转换、正则校验)
│   ├── storage.ts      # 本地存储工具(适配多端)
│   ├── price.ts        # 价格计算工具
│   └── device.ts       # 设备信息工具(判断平台)
├── config/             # 项目配置
│   ├── index.ts        # 全局配置(接口基础地址、环境变量)
│   └── adapt.ts        # 多端适配配置
├── types/              # TypeScript类型定义
│   ├── goods.ts        # 商品相关类型
│   ├── cart.ts         # 购物车相关类型
│   ├── order.ts        # 订单相关类型
│   └── user.ts         # 用户相关类型
├── app.tsx             # 入口组件
├── app.config.ts       # 全局配置(页面路由、窗口样式)
└── app.scss            # 全局样式
    

3.3 状态管理方案

采用Redux Toolkit进行状态管理,按模块拆分状态切片,实现状态的集中管理与复用:

3.3.1 核心状态切片设计

  • userSlice:管理用户状态(登录状态、用户信息、收货地址列表、默认地址)。

  • cartSlice:管理购物车状态(购物车列表、选中商品数量、总金额、是否全选)。

  • goodsSlice:管理商品相关状态(商品列表、商品详情、搜索条件、筛选结果)。

  • orderSlice:管理订单相关状态(订单列表、当前订单信息、支付状态)。

3.3.2 Store配置实现

typescript 复制代码
// src/store/index.ts
import { configureStore } from '@reduxjs/toolkit';
import userSlice from './slices/userSlice';
import cartSlice from './slices/cartSlice';
import goodsSlice from './slices/goodsSlice';
import orderSlice from './slices/orderSlice';

// 配置store
export const store = configureStore({
  reducer: {
    user: userSlice,
    cart: cartSlice,
    goods: goodsSlice,
    order: orderSlice,
  },
  // 关闭序列化检查(适配多端存储复杂数据)
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false,
    }),
});

// 定义RootState和AppDispatch类型,方便组件使用
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;

3.3.3 自定义Hooks封装

typescript 复制代码
// src/store/hooks.ts
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './index';

// 自定义useDispatch和useSelector,自带类型提示
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

3.4 路由配置

基于Taro内置路由,配置统一的路由表,封装跳转方法,支持多端路由适配:

3.4.1 路由表配置

typescript 复制代码
// src/router/index.ts
import { RouteConfig } from '@tarojs/taro';

// 路由表
const routes: RouteConfig[] = [
  {
    path: '/pages/index/index',
    name: '首页',
    meta: {
      showTabBar: true, // 是否显示底部tab
      requireAuth: false, // 是否需要登录
    },
  },
  {
    path: '/pages/goods/list',
    name: '商品列表',
    meta: {
      showTabBar: false,
      requireAuth: false,
    },
  },
  {
    path: '/pages/goods/detail/:id',
    name: '商品详情',
    meta: {
      showTabBar: false,
      requireAuth: false,
    },
  },
  {
    path: '/pages/cart/index',
    name: '购物车',
    meta: {
      showTabBar: true,
      requireAuth: true,
    },
  },
  {
    path: '/pages/order/list',
    name: '订单列表',
    meta: {
      showTabBar: false,
      requireAuth: true,
    },
  },
  {
    path: '/pages/user/index',
    name: '用户中心',
    meta: {
      showTabBar: true,
      requireAuth: true,
    },
  },
  // 更多路由...
];

export default routes;

3.4.2 路由跳转封装

typescript 复制代码
// src/router/hooks.ts
import Taro from '@tarojs/taro';
import routes from './index';
import { useAppSelector } from '../store/hooks';

// 跳转方法封装
export const useNavigate = () => {
  // 获取登录状态
  const { isLogin } = useAppSelector((state) => state.user);

  // 导航跳转
  const navigateTo = (path: string, params?: Record<string, any>) => {
    // 检查是否需要登录
    const route = routes.find((item) => item.path === path);
    if (route?.meta.requireAuth && !isLogin) {
      // 未登录,跳转登录页
      Taro.navigateTo({ url: '/pages/user/login' });
      return;
    }

    // 拼接参数
    let url = path;
    if (params) {
      const queryStr = Object.entries(params)
        .map(([key, value]) => `${key}=${value}`)
        .join('&');
      url += `?${queryStr}`;
    }

    // 执行跳转
    Taro.navigateTo({ url });
  };

  // 重定向跳转
  const redirectTo = (path: string, params?: Record<string, any>) => {
    let url = path;
    if (params) {
      const queryStr = Object.entries(params)
        .map(([key, value]) => `${key}=${value}`)
        .join('&');
      url += `?${queryStr}`;
    }
    Taro.redirectTo({ url });
  };

  return { navigateTo, redirectTo };
};

3.5 API请求封装

基于Axios封装API请求,实现请求拦截、响应拦截、错误处理、多端适配:

3.5.1 请求配置

typescript 复制代码
// src/api/index.ts
import axios from 'axios';
import Taro from '@tarojs/taro';
import { baseUrl } from '../config';

// 创建axios实例
const service = axios.create({
  baseURL: baseUrl, // 接口基础地址
  timeout: 10000, // 请求超时时间
});

// 请求拦截器
service.interceptors.request.use(
  (config) => {
    // 添加请求头(如token)
    const token = Taro.getStorageSync('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    // 显示加载中
    Taro.showLoading({ title: '加载中...', mask: true });
    return config;
  },
  (error) => {
    // 隐藏加载中
    Taro.hideLoading();
    // 处理请求错误
    console.error('请求错误:', error);
    Taro.showToast({ title: '请求失败', icon: 'none' });
    return Promise.reject(error);
  }
);

// 响应拦截器
service.interceptors.response.use(
  (response) => {
    // 隐藏加载中
    Taro.hideLoading();
    const res = response.data;
    // 业务错误处理(如token过期、权限不足)
    if (res.code !== 200) {
      Taro.showToast({ title: res.msg || '请求失败', icon: 'none' });
      // token过期,跳转登录页
      if (res.code === 401) {
        Taro.removeStorageSync('token');
        Taro.navigateTo({ url: '/pages/user/login' });
      }
      return Promise.reject(res);
    }
    return res.data;
  },
  (error) => {
    // 隐藏加载中
    Taro.hideLoading();
    // 处理网络错误
    console.error('响应错误:', error);
    Taro.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
    return Promise.reject(error);
  }
);

export default service;

3.5.2 接口封装示例

typescript 复制代码
// src/api/goods.ts
import service from './index';
import { GoodsListParams, GoodsListRes, GoodsDetailRes } from '../types/goods';

// 获取商品列表
export const getGoodsList = (params: GoodsListParams) => {
  return service<GoodsListRes>({
    url: '/goods/list',
    method: 'GET',
    params,
  });
};

// 获取商品详情
export const getGoodsDetail = (id: string | number) => {
  return service<GoodsDetailRes>({
    url: `/goods/detail/${id}`,
    method: 'GET',
  });
};

// 添加商品收藏
export const collectGoods = (id: string | number) => {
  return service({
    url: '/goods/collect',
    method: 'POST',
    data: { goodsId: id },
  });
};

3.6 多端适配架构

基于Taro的多端编译能力,采用"统一代码+差异化适配"的架构,实现一套代码多端运行:

3.6.1 差异化适配方案

  • 组件适配:使用Taro内置组件或多端兼容的UI组件库,若需原生组件,采用条件编译区分平台。

  • API适配 :使用Taro封装的统一API,若平台特有API,通过process.env.TARO_ENV判断平台后调用。

  • 样式适配:使用CSS变量、媒体查询、Flex/Grid布局实现响应式,小程序端使用rpx单位,H5/App使用rem/vw单位。

  • 配置适配 :在app.config.ts或页面配置文件中,通过h5minirn字段配置不同平台的专属配置。

3.6.2 条件编译示例

typescript 复制代码
// 组件适配示例
import Taro from '@tarojs/taro';

const MyComponent = () => {
  return (
    <view>
      {/* 统一组件 */}
      <Button type="primary" onClick={handleClick}>
        统一按钮
      </Button>
      {/* 平台差异化组件 */}
      {process.env.TARO_ENV === 'weapp' ? (
        <weapp-native-component /> // 微信小程序原生组件
      ) : process.env.TARO_ENV === 'h5' ? (
        <h5-native-component /> // H5原生组件
      ) : null}
    </view>
  );
};

// API适配示例
const getLocation = () => {
  if (process.env.TARO_ENV === 'weapp') {
    // 微信小程序获取位置
    Taro.getLocation({ type: 'gcj02' });
  } else if (process.env.TARO_ENV === 'h5') {
    // H5获取位置(使用浏览器API)
    navigator.geolocation.getCurrentPosition((position) => {
      console.log(position);
    });
  }
};

3.6.3 多端配置示例

typescript 复制代码
// src/app.config.ts
export default {
  pages: [
    'pages/index/index',
    'pages/goods/list',
    // 其他页面...
  ],
  window: {
    backgroundTextStyle: 'light',
    navigationBarBackgroundColor: '#fff',
    navigationBarTitleText: '仿京东电商',
    navigationBarTextStyle: 'black',
  },
  // 微信小程序专属配置
  mini: {
    programName: '仿京东电商',
    appid: 'wx1234567890abcdef',
    subpackages: [
      {
        root: 'pages/goods/',
        pages: ['detail', 'list'],
      },
    ], // 分包加载
  },
  // H5专属配置
  h5: {
    router: {
      mode: 'hash', // 路由模式
    },
    devServer: {
      port: 3000, // 开发服务器端口
    },
  },
  // App专属配置
  rn: {
    navigationBar: {
      hidden: false, // 是否隐藏导航栏
    },
  },
};

四、模块分析

本节对核心业务模块进行详细分析,包括业务逻辑、数据流转与组件拆分思路,为开发实现提供清晰指引。

4.1 商品展示模块

4.1.1 业务逻辑

  1. 首页加载时,调用热门商品、新品、活动商品接口,展示商品列表与轮播图;2. 用户点击分类导航,加载对应分类的商品列表;3. 用户点击商品卡片,跳转商品详情页,加载商品详情、规格、评价数据;4. 用户查看商品评价,支持筛选不同类型评价。

4.1.2 数据流转

  1. 页面初始化 → 调用商品列表/详情接口 → 接口返回数据 → 存储到goodsSlice状态 → 组件从状态中获取数据渲染。

  2. 用户筛选评价 → 调用评价列表接口(携带筛选参数) → 返回筛选后数据 → 更新页面渲染。

  3. 用户收藏商品 → 调用收藏接口 → 接口返回成功 → 更新商品收藏状态 → 提示收藏成功。

4.1.3 组件拆分

  • 基础组件

    • GoodsCard:商品卡片组件,接收商品信息(图片、名称、价格、销量),展示商品卡片,支持点击跳转详情。

    • SwiperBanner:轮播图组件,接收图片列表,支持自动轮播、点击跳转。

    • CategoryNav:分类导航组件,接收分类列表,支持点击切换分类。

  • 页面组件

    • IndexPage:首页,集成SwiperBanner、CategoryNav、GoodsCard,展示各类商品。

    • GoodsListPage:商品列表页,接收分类ID参数,展示对应分类商品,支持分页加载。

    • GoodsDetailPage:商品详情页,展示商品图文、规格选择器、加入购物车/立即购买按钮,集成评价列表组件。

    • GoodsCommentPage:商品评价页,展示评价列表,支持筛选评价类型。

4.2 购物车模块

4.2.1 业务逻辑

  1. 用户进入购物车,加载购物车列表数据,展示商品信息、数量、价格;2. 用户可修改商品数量,系统自动计算总金额;3. 支持全选/取消全选,选中商品金额实时更新;4. 用户点击结算,校验商品库存,若缺货提示用户,否则跳转确认订单页。

4.2.2 数据流转

  1. 进入购物车页面 → 调用购物车列表接口 → 数据存储到cartSlice → 组件渲染购物车列表。

  2. 修改商品数量 → 调用更新购物车接口 → 接口返回成功 → 更新cartSlice状态 → 重新计算总金额。

  3. 点击全选 → 切换cartSlice中全选状态 → 更新所有商品的选中状态 → 重新计算总金额。

  4. 点击结算 → 校验选中商品库存 → 库存充足 → 跳转确认订单页并携带选中商品数据;库存不足 → 提示缺货商品。

4.2.3 组件拆分

  • 基础组件

    • CartItem:购物车项组件,展示商品信息、选中状态、数量选择器、删除按钮。

    • QuantitySelector:数量选择器组件,支持增减数量、直接输入,限制最小为1。

    • CartFooter:购物车底部组件,展示全选按钮、选中数量、总金额、结算按钮。

  • 页面组件

    • CartPage:购物车主页面,集成CartItem、CartFooter,处理购物车相关操作逻辑。

4.3 订单管理模块

4.3.1 业务逻辑

  1. 用户在确认订单页选择收货地址、商品清单,提交订单生成订单记录;2. 跳转到支付页面,选择支付方式完成支付;3. 支付成功后,更新订单状态为"待发货",返回订单列表;4. 用户可按状态查看订单列表,点击订单查看详情,进行对应操作(确认收货、申请售后)。

4.3.2 数据流转

  1. 确认订单页 → 获取购物车选中商品数据、用户收货地址 → 提交订单接口 → 生成订单ID → 跳转支付页面。

  2. 支付页面 → 调用支付接口 → 支付成功 → 调用更新订单状态接口 → 跳转订单详情页(显示支付成功)。

  3. 订单列表页 → 调用订单列表接口(携带状态参数) → 数据存储到orderSlice → 组件渲染订单列表。

  4. 确认收货 → 调用确认收货接口 → 接口返回成功 → 更新订单状态 → 重新加载订单列表。

4.3.3 组件拆分

  • 基础组件

    • OrderItem:订单项组件,展示订单编号、时间、商品缩略图、金额、订单状态。

    • OrderStatusTag:订单状态标签组件,根据订单状态显示不同颜色和文字(待付款、待发货等)。

    • OrderGoodsList:订单商品列表组件,展示订单中的商品清单(图片、名称、规格、数量、单价)。

    • AddressItem:地址项组件,展示收货地址信息,支持选择地址。

  • 页面组件

    • ConfirmOrderPage:确认订单页,集成AddressItem、OrderGoodsList,展示订单金额,提交订单。

    • PayPage:支付页面,展示支付金额,支持选择支付方式,唤起支付接口。

    • OrderListPage:订单列表页,集成OrderItem,支持按状态筛选订单。

    • OrderDetailPage:订单详情页,集成OrderGoodsList、OrderStatusTag,展示订单完整信息,提供订单操作按钮。

4.4 用户中心模块

4.4.1 业务逻辑

  1. 用户未登录时,用户中心显示登录/注册按钮;2. 登录后展示用户头像、昵称,提供个人信息修改入口;3. 支持查看收货地址列表,进行添加、编辑、删除、设置默认地址操作;4. 展示收藏商品列表、浏览历史,支持取消收藏、清空历史。

4.4.2 数据流转

  1. 用户登录 → 调用登录接口 → 返回token和用户信息 → 存储token到本地缓存,用户信息存储到userSlice → 刷新页面展示登录后状态。

  2. 修改个人信息 → 调用更新用户信息接口 → 接口返回成功 → 更新userSlice中用户信息 → 页面重新渲染。

  3. 加载收货地址 → 调用地址列表接口 → 数据存储到userSlice → 组件渲染地址列表。

  4. 添加/编辑地址 → 调用添加/更新地址接口 → 接口返回成功 → 重新加载地址列表。

4.4.3 组件拆分

  • 基础组件

    • UserAvatar:用户头像组件,展示头像,支持点击上传头像。

    • AddressForm:地址表单组件,用于添加/编辑收货地址,包含姓名、手机号、地址、邮编等字段。

    • CollectItem:收藏商品项组件,展示收藏商品信息,支持取消收藏。

  • 页面组件

    • UserIndexPage:用户中心首页,展示用户信息、功能入口(地址管理、收藏、历史、订单)。

    • LoginPage:登录页面,支持手机号验证码登录、微信快捷登录。

    • UserInfoPage:个人信息修改页面,集成UserAvatar,展示并修改用户信息。

    • AddressListPage:地址列表页,集成AddressItem,展示地址列表,提供添加地址入口。

    • AddressEditPage:地址编辑页,集成AddressForm,用于添加/编辑地址。

    • CollectPage:收藏商品页,集成CollectItem,展示收藏商品列表。

五、全流程实操指南

本节从环境安装到核心模块代码实现,提供 step-by-step 实操指南,包含关键代码片段与注释,确保开发者可直接复用。

5.1 环境安装与项目初始化

5.1.1 安装Taro CLI

bash 复制代码
# 全局安装Taro CLI(使用npm)
npm install -g @tarojs/cli

# 验证安装成功
taro -v
# 输出示例:Taro v3.6.12

5.1.2 创建Taro项目

bash 复制代码
# 创建项目(项目名称:taro-jd-ecommerce,模板:react-ts)
taro init taro-jd-ecommerce

# 项目创建过程中按如下选择:
# 1. 选择框架:React
# 2. 选择语言:TypeScript
# 3. 选择CSS预处理器:SCSS
# 4. 选择模板源:默认模板
# 5. 选择UI组件库:Taro UI Vue3(或NutUI)
# 6. 是否需要状态管理:Redux
# 7. 是否需要TypeScript类型检查:是
# 8. 是否初始化git仓库:是

5.1.3 项目配置调整

修改src/app.config.ts,配置页面路由、窗口样式、多端配置(参考3.6.3节多端配置示例)。

5.1.4 安装依赖包

bash 复制代码
# 进入项目目录
cd taro-jd-ecommerce

# 安装axios(API请求)、redux-toolkit(状态管理)
npm install axios @reduxjs/toolkit react-redux

5.2 核心基础模块实现

5.2.1 全局配置实现

typescript 复制代码
// src/config/index.ts
// 环境变量区分(开发/生产)
const isProd = process.env.NODE_ENV === 'production';

export default {
  // 接口基础地址
  baseUrl: isProd ? 'https://api.jd-demo.com' : 'http://localhost:3000/api',
  // 超时时间
  timeout: 10000,
  // 存储键名
  storageKey: {
    token: 'jd_ecommerce_token',
    userInfo: 'jd_ecommerce_userInfo',
  },
};

5.2.2 工具函数实现(本地存储)

typescript 复制代码
// src/utils/storage.ts
import Taro from '@tarojs/taro';
import { storageKey } from '../config';

// 存储工具类
class Storage {
  // 存储token
  setToken(token: string) {
    Taro.setStorageSync(storageKey.token, token);
  }

  // 获取token
  getToken() {
    return Taro.getStorageSync(storageKey.token) || '';
  }

  // 移除token
  removeToken() {
    Taro.removeStorageSync(storageKey.token);
  }

  // 存储用户信息
  setUserInfo(userInfo: any) {
    Taro.setStorageSync(storageKey.userInfo, userInfo);
  }

  // 获取用户信息
  getUserInfo() {
    return Taro.getStorageSync(storageKey.userInfo) || null;
  }

  // 清空存储
  clear() {
    Taro.clearStorageSync();
  }
}

export default new Storage();

5.2.3 API请求封装实现

参考3.5.1节和3.5.2节代码,实现API请求拦截、响应拦截及接口封装。

5.2.4 状态管理实现(以cartSlice为例)

Plain 复制代码
// src/store/slices/cartSlice.ts
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { getCartList, updateCartNum, deleteCartItem } from '../../api/cart';
import { CartItemType } from '../../types/cart';

// 定义初始状态
interface CartState {
  cartList: CartItemType[]; // 购物车列表
  totalNum: number; // 选中商品总数
  totalPrice: number; // 选中商品总金额
  isAllChecked: boolean; // 是否全选
  loading: boolean; // 加载状态
}

const initialState: CartState = {
  cartList: [],
  totalNum: 0,
  totalPrice: 0,
  isAllChecked: false,
  loading: false,
};

// 异步获取购物车列表
export const fetchCartList = createAsyncThunk(
  'cart/fetchCartList',
  async (_, { rejectWithValue }) => {
    try {
      const data = await getCartList();
      return data;
    } catch (error) {
      return rejectWithValue(error);
    }
  }
);

// 异步更新购物车商品数量
export const updateCartQuantity = createAsyncThunk(
  'cart/updateCartQuantity',
  async ({ id, num }: { id: number; num: number }, { rejectWithValue }) => {
    try {
      const data = await updateCartNum(id, num);
      return { id, num };
    } catch (error) {
      return rejectWithValue(error);
    }
  }
);

// 异步删除购物车商品
export const removeCartItem = createAsyncThunk(
  'cart/removeCartItem',
  async (id: number, { rejectWithValue }) => {
    try {
      await deleteCartItem(id);
      return id;
    } catch (error) {
      return rejectWithValue(error);
    }
  }
);

// 计算选中商品总数和总金额
const calculateSelected = (cartList: CartItemType[]) => {
  let totalNum = 0;
  let totalPrice = 0;
  cartList.forEach(item => {
    if (item.checked) {
      totalNum += item.num;
      totalPrice += item.price * item.num;
    }
  });
  return { totalNum, totalPrice };
};

// 创建购物车切片
const cartSlice = createSlice({
  name: 'cart',
  initialState,
  reducers: {
    // 切换商品选中状态
    toggleItemChecked: (state, action) => {
      const id = action.payload;
      const item = state.cartList.find(item => item.id === id);
      if (item) {
        item.checked = !item.checked;
      }
      // 重新计算总数和总金额
      const { totalNum, totalPrice } = calculateSelected(state.cartList);
      state.totalNum = totalNum;
      state.totalPrice = totalPrice;
      // 判断是否全选
      state.isAllChecked = state.cartList.every(item => item.checked);
    },
    // 全选/取消全选
    toggleAllChecked: (state) => {
      state.isAllChecked = !state.isAllChecked;
      state.cartList.forEach(item => {
        item.checked = state.isAllChecked;
      });
      // 重新计算总数和总金额
      const { totalNum, totalPrice } = calculateSelected(state.cartList);
      state.totalNum = totalNum;
      state.totalPrice = totalPrice;
    },
    // 清空购物车
    clearCart: (state) => {
      state.cartList = [];
      state.totalNum = 0;
      state.totalPrice = 0;
      state.isAllChecked = false;
    },
  },
  extraReducers: (builder) => {
    builder
      // 获取购物车列表
      .addCase(fetchCartList.pending, (state) => {
        state.loading = true;
      })
      .addCase(fetchCartList.fulfilled, (state, action) => {
        state.loading = false;
        state.cartList = action.payload.map((item: CartItemType) => ({ ...item, checked: false }));
        // 初始化计算总数和总金额(默认未选中)
        const { totalNum, totalPrice } = calculateSelected(state.cartList);
        state.totalNum = totalNum;
        state.totalPrice = totalPrice;
        state.isAllChecked = false;
      })
      .addCase(fetchCartList.rejected, (state) => {
        state.loading = false;
      })
      // 更新商品数量
      .addCase(updateCartQuantity.fulfilled, (state, action) => {
        const { id, num } = action.payload;
        const item = state.cartList.find(item => item.id === id);
        if (item) {
          item.num = num;
        }
        // 重新计算总数和总金额
        const { totalNum, totalPrice } = calculateSelected(state.cartList);
        state.totalNum = totalNum;
        state.totalPrice = totalPrice;
      })
      // 删除商品
      .addCase(removeCartItem.fulfilled, (state, action) => {
        const id = action.payload;
        state.cartList = state.cartList.filter(item => item.id !== id);
        // 重新计算总数和总金额
        const { totalNum, totalPrice } = calculateSelected(state.cartList);
        state.totalNum = totalNum;
        state.totalPrice = totalPrice;
        // 判断是否全选
        state.isAllChecked = state.cartList.every(item => item.checked);
      });
  },
});

// 导出actions
export const { toggleItemChecked, toggleAllChecked, clearCart } = cartSlice.actions;

// 导出reducer
export default cartSlice.reducer;
相关推荐
liliangcsdn2 小时前
SD稳定扩散模型理论基础的探索
人工智能·机器学习
KLW752 小时前
vue watch监听
前端·javascript·vue.js
deephub2 小时前
dLLM:复用自回归模型权重快速训练扩散语言模型
人工智能·python·语言模型·大语言模型
林恒smileZAZ2 小时前
总结 Next.js 中的 Server Actions
开发语言·javascript·ecmascript
中國龍在廣州2 小时前
2025,具身智能正在惩罚“持有者”
人工智能·深度学习·算法·自然语言处理·chatgpt
阿部多瑞 ABU2 小时前
第五章:林心
人工智能·ai·ai写作
itwangyang5202 小时前
AIDD-人工智能药物设计-字节跳动 PXDesign:AI 设计蛋白,82% 命中率惊艳业界
人工智能·python
ToB营销学堂2 小时前
百格活动《大型会议管理 & 执行指南》:大型会议如何从“事务交付”向“资产运营”转型?
人工智能·大会管理系统·会议活动执行·活动管理·大会执行·会议策划
晴殇i2 小时前
🎉 TRAE 一年使用的过程体验 🎉
前端