恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Vue3电商实战:从登录鉴权到扫码支付的完整闭环

  • 首页
  • 资讯中心
  • /
  • Vue3电商实战:从登录鉴权到扫码支付的完整闭环

相关资讯

本地语音增强技术:从谱减法到深度学习的完整实现指南 2026/9/3 4:39:34
基于TensorFlow 2.x的FER2013表情识别:从数据预处理到CNN模型实战 2026/9/3 4:39:34
从零实现KNN算法:Matlab源码解析与实战指南 2026/9/3 4:39:34

最新资讯

AI辅助编程:从八岁孩子四小时开发游戏看低代码革命
Python爬虫技术详解:从HTTP协议到分布式架构的完整实践指南
舌苔语义分割数据集 舌苔识别 基于UNet模型的舌苔语义分割:从数据准备到模型训练到建立gui
基于QT C++与open62541的OPC UA客户端开发实战指南
c语言学习从0到1的那些事——准备与起步
AI 辅助 3D 建模、角色绑骨与 Agent 建模工作流

今日推荐

零基础装 OpenClaw 小龙虾 AI:Windows 一键部署教程与避坑要点
Hermes Agent 本地部署新方案:Windows 整合包减少依赖报错
实测 OpenClaw 一键包,5 分钟完成本地自动化环境搭建

本周热门

备战数据库管理工程师校招:索引、事务、备份恢复核心考点解析
数字电路时序基石:深入理解建立时间与保持时间
蓝桥杯国赛超声波测距机:从单片机原理到嵌入式系统实战

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

Vue3电商实战:从登录鉴权到扫码支付的完整闭环

发布时间:2026/9/3 4:44:34
Vue3电商实战:从登录鉴权到扫码支付的完整闭环 很多同学在写 Vue3 电商项目时经常卡在同一个地方登录能跳转商品能加购但到了“生成订单、拉起支付、确认支付结果”这条链路上就会断掉。后端返回了一个code_url前端不知道该怎么用H5 里调不起微信支付支付成功后订单状态又迟迟不刷新。其实从登录到支付并不是多个孤立功能而是一条完整的数据流转链路。本文以一套微型电商前端为主线从登录鉴权开始讲经过商品浏览、购物车、结算下单最后实现支付二维码展示与支付状态轮询闭环。文章会给出完整可运行的 Vue3 代码片段、接口设计约定和常见坑点。无论是准备 Vue3 面试、做毕业设计还是第一次接触真实电商业务都可以按这条链路把项目跑通。1. 需求分析与整体设计1.1 电商核心闭环拆解一个标准电商应用的最小闭环可以拆成以下环节用户登录并获取 token。前端保存登录态并通过请求拦截器自动携带 token。用户浏览商品列表。用户将商品加入购物车。用户选择商品并提交订单。后端创建未支付订单。前端调用支付预下单接口获取支付二维码链接。用户扫码完成支付。前端轮询或等待后端回调更新订单状态。很多教程只讲前两步或者只讲商品管理导致你跟着做出来后支付环节依然不知道怎么接。本文会围绕第 5 到第 9 步展开电商项目中真正容易出错的就是这一段。1.2 技术选型说明本文前端选择 Vue3 Vite Vue Router Pinia Axios Element Plus。这套组合是目前国内 Vue3 项目的主流组合也基本是 Vue3 面试中的常客。模块选型作用构建工具Vite本地开发与打包框架Vue3 组合式 API页面与组件开发路由Vue Router 4页面跳转与路由守卫状态管理Pinia保存用户 token、购物车数据HTTP 请求Axios请求接口、处理拦截器UI 组件Element Plus快速搭建表单、表格、弹窗二维码qrcode将 code_url 转为二维码图片这里要特别说明当前是 Vue3 的技术生态选项式 API 依然能用但新项目和企业实际开发中组合式 API script setup已经成为主流写法。本文统一使用script setup。1.3 支付方式如何选择国内常见的支付方式有微信 JSAPI 支付只能在微信内置浏览器中调起。微信 Native 支付适合 PC Web后端生成code_url前端展示二维码用户用手机微信扫码。支付宝当面付适合 PC Web 扫码后端返回二维码字符串。支付宝手机网站支付适合 H5 浏览器中跳转支付宝。本文以“扫码支付”为主线因为这种模式最容易在本地开发环境中验证不依赖微信内置浏览器等特殊环境。无论后端对接微信支付还是支付宝沙箱前端处理逻辑都类似先通过接口拿到支付二维码内容再展示二维码并轮询订单状态。2. 环境准备与项目初始化2.1 版本环境说明本文使用的核心版本如下Node.js建议使用 18 或 20 LTS 版本。npm随 Node.js 自带建议 9 以上。Vue3.4 或更高版本。Vite5 或更高版本。Pinia2.x。Vue Router4.x。由于不同版本之间 API 差异不大你的本地版本不需要完全一致但尽量使用 Node 18避免 Vite 启动时报错。2.2 创建 Vue3 项目使用 Vite 创建项目时可以执行以下命令npm create vitelatest vue3-shop-demo -- --template vue创建完成后进入项目目录cd vue3-shop-demo然后安装项目基础依赖npm install再安装后续需要使用的依赖npm install vue-router4 pinia axios element-plus qrcode其中qrcode用于将支付链接转换为二维码图片element-plus用于快速搭建后台风格页面。2.3 项目目录结构规划建议把业务代码按模块拆分不要全部堆在App.vue或某个页面组件中。src/ ├── api/ │ ├── auth.js │ ├── order.js │ └── product.js ├── assets/ ├── components/ ├── router/ │ └── index.js ├── stores/ │ ├── cart.js │ └── user.js ├── utils/ │ └── request.js ├── views/ │ ├── LoginView.vue │ ├── ProductListView.vue │ ├── CartView.vue │ ├── OrderConfirmView.vue │ ├── PayView.vue │ └── OrderDetailView.vue ├── App.vue └── main.js这里的api目录负责集中管理接口请求stores目录管理全局状态utils/request.js封装 Axios 实例和拦截器。这种分层方式也是中型前端项目比较标准的写法。3. 后端接口设计约定本文重点面向 Vue3 前端实战不展开完整的后端实现但前后端接口需要约定清楚。真实开发中你通常是照着后端 Swagger 文档写前端请求。为了方便演示这里假设后端提供以下接口。3.1 登录鉴权接口POST /api/auth/login Content-Type: application/json { mobile: 13800138000, password: 123456 }响应格式{ code: 0, message: success, data: { token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..., userInfo: { userId: 10001, nickname: 张三 } } }后续所有业务接口都需要在请求头中携带Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...3.2 商品、购物车与订单接口为了便于演示商品数据可以先使用静态列表或 Mock 数据。订单提交接口示例POST /api/orders Authorization: Bearer token Content-Type: application/json { items: [ { productId: 1001, quantity: 2 } ], receiver: { name: 张三, mobile: 13800138000, address: 北京市海淀区某街道 1 号 } }响应{ code: 0, message: success, data: { orderNo: SN20250101120000123, payAmount: 19900 } }这里注意payAmount单位是“分”不是“元”。金额的单位问题在电商项目中非常关键前端不要在后端计算金额更不要自己把元转成分后传回后端容易造成金额不一致。3.3 支付预下单与状态查询接口创建订单成功后前端需要调用支付预下单接口POST /api/payments/native Authorization: Bearer token Content-Type: application/json { orderNo: SN20250101120000123 }响应{ code: 0, message: success, data: { codeUrl: weixin://wxpay/bizpayurl?prxxxxx, payOrderNo: PAY20250101120000123 } }这里的codeUrl是给二维码插件使用的原始字符串。对于支付宝当面付后端返回的通常是一长串 URL前端同样把它变成二维码。支付状态查询接口GET /api/orders/SN20250101120000123/pay-status Authorization: Bearer token响应{ code: 0, message: success, data: { orderNo: SN20250101120000123, orderStatus: PAID } }orderStatus常见取值有PENDING、PAID、CANCELLED等。前端轮询时只需要关心是否变为PAID。4. 登录模块与前端鉴权实现4.1 Axios 封装与请求拦截器在真实项目中我们不会在每个组件里直接调用axios.get而是封装一个统一的request.js统一处理基础 URL、token、超时、错误码。这样当后端告诉你登录过期时你只改一处就能全局生效。// 文件路径src/utils/request.js import axios from axios; import { ElMessage } from element-plus; import router from /router; import { useUserStore } from /stores/user; const request axios.create({ baseURL: /api, timeout: 10000 }); // 请求拦截器自动携带 token request.interceptors.request.use( (config) { const userStore useUserStore(); if (userStore.token) { config.headers.Authorization Bearer ${userStore.token}; } return config; }, (error) { return Promise.reject(error); } ); // 响应拦截器统一处理业务错误 request.interceptors.response.use( (response) { const res response.data; if (res.code ! 0) { ElMessage.error(res.message || 请求失败); return Promise.reject(new Error(res.message || Error)); } return res.data; }, (error) { const status error.response?.status; if (status 401) { const userStore useUserStore(); userStore.clearLoginState(); router.push(/login); ElMessage.error(登录状态已过期请重新登录); } else { ElMessage.error(error.response?.data?.message || 网络异常); } return Promise.reject(error); } ); export default request;在响应拦截器中我们做了两件很重要的事情当后端返回code ! 0时直接弹出错误提示业务代码不需要再重复处理错误分支。当 HTTP 状态码为 401 时说明 token 失效自动清除登录状态并跳回登录页。这里要注意ElMessage和router的导入路径取决于你的项目结构如果request.js与router之间存在循环引用可以考虑把跳转逻辑放在登录页统一处理或使用window.location.href做兜底。4.2 Pinia 管理用户登录态用户登录后token 和用户信息属于全局状态适合放在 Pinia 中。同时我们需要实现 token 的持久化否则刷新页面后登录态会丢失。// 文件路径src/stores/user.js import { defineStore } from pinia; import { loginApi } from /api/auth; export const useUserStore defineStore(user, { state: () ({ token: localStorage.getItem(token) || , userInfo: JSON.parse(localStorage.getItem(userInfo) || null) }), getters: { isLoggedIn: (state) !!state.token }, actions: { async login(loginForm) { const data await loginApi(loginForm); this.token data.token; this.userInfo data.userInfo; localStorage.setItem(token, data.token); localStorage.setItem(userInfo, JSON.stringify(data.userInfo)); }, clearLoginState() { this.token ; this.userInfo null; localStorage.removeItem(token); localStorage.removeItem(userInfo); } } });这里将 token 直接存放在localStorage是小型项目的常见做法。生产级项目也可以使用 cookie 并设置httpOnly来防 XSS但鉴权流程思路是类似的。对应的登录接口api/auth.js可以这样写// 文件路径src/api/auth.js import request from /utils/request; export function loginApi(data) { return request.post(/auth/login, data); } export function getUserInfoApi() { return request.get(/user/info); }4.3 登录页面实现登录页的核心逻辑是调用userStore.login登录成功后跳转到 redirect 参数指定的页面否则跳转首页。!-- 文件路径src/views/LoginView.vue -- template div classlogin-page el-card classlogin-card h2电商系统登录/h2 el-form refformRef :modelloginForm :rulesrules label-width80px el-form-item label手机号 propmobile el-input v-modelloginForm.mobile placeholder请输入手机号 / /el-form-item el-form-item label密码 proppassword el-input v-modelloginForm.password typepassword placeholder请输入密码 show-password / /el-form-item el-form-item el-button typeprimary :loadingloading clickhandleLogin 登录 /el-button /el-form-item /el-form /el-card /div /template script setup import { ref, reactive } from vue; import { useRouter, useRoute } from vue-router; import { ElMessage } from element-plus; import { useUserStore } from /stores/user; const router useRouter(); const route useRoute(); const userStore useUserStore(); const formRef ref(null); const loading ref(false); const loginForm reactive({ mobile: , password: }); const rules { mobile: [{ required: true, message: 请输入手机号, trigger: blur }], password: [{ required: true, message: 请输入密码, trigger: blur }] }; async function handleLogin() { await formRef.value.validate(); loading.value true; try { await userStore.login(loginForm); ElMessage.success(登录成功); const redirect route.query.redirect; router.replace(redirect || /); } finally { loading.value false; } } /script style scoped .login-page { display: flex; justify-content: center; align-items: center; height: 100vh; background: #f5f7fa; } .login-card { width: 420px; } /style4.4 路由守卫控制页面访问登录页完成后还需要通过路由守卫控制哪些页面必须登录才能访问。这里使用的是 Vue Router 4 的全局前置守卫。// 文件路径src/router/index.js import { createRouter, createWebHistory } from vue-router; import { useUserStore } from /stores/user; const routes [ { path: /login, name: Login, component: () import(/views/LoginView.vue) }, { path: /, component: () import(/views/ProductListView.vue) }, { path: /cart, name: Cart, component: () import(/views/CartView.vue), meta: { requiresAuth: true } }, { path: /order/confirm, name: OrderConfirm, component: () import(/views/OrderConfirmView.vue), meta: { requiresAuth: true } }, { path: /pay, name: Pay, component: () import(/views/PayView.vue), meta: { requiresAuth: true } }, { path: /order/detail, name: OrderDetail, component: () import(/views/OrderDetailView.vue), meta: { requiresAuth: true } } ]; const router createRouter({ history: createWebHistory(), routes }); router.beforeEach((to) { const userStore useUserStore(); if (to.meta.requiresAuth !userStore.isLoggedIn) { return { path: /login, query: { redirect: to.fullPath } }; } return true; }); export default router;这里使用meta.requiresAuth标识需要登录的页面。当用户未登录时跳转到登录页并携带redirect参数登录成功后回到原页面。这种实现方式在真实后台管理项目和电商项目中非常常用。5. 商品浏览、购物车与订单提交5.1 商品列表页基础实现商品列表页不是本文核心但为了让支付链路完整可见这里给出一个最简实现思路请求商品列表接口渲染商品卡片点击“加入购物车”后存放到 Pinia。// 文件路径src/stores/cart.js import { defineStore } from pinia; export const useCartStore defineStore(cart, { state: () ({ items: [] }), getters: { totalCount: (state) state.items.reduce((sum, item) sum item.quantity, 0), totalAmount: (state) state.items.reduce( (sum, item) sum item.price * item.quantity, 0 ) }, actions: { addItem(product) { const existing this.items.find( (item) item.productId product.productId ); if (existing) { existing.quantity 1; } else { this.items.push({ productId: product.productId, title: product.title, price: product.price, image: product.image, quantity: 1 }); } }, removeItem(productId) { this.items this.items.filter( (item) item.productId ! productId ); } } });这里的price字段同样建议以“分”为单位。如果后端返回的是元前端可以在展示层用过滤器转换为元但计算时不要使用浮点数累加。5.2 订单提交流程购物车页面点击“去结算”后跳转到订单确认页。订单确认页需要展示商品清单、收货地址和应付金额然后调用创建订单接口。以订单确认页中的提交函数为例const submitLoading ref(false); async function submitOrder() { submitLoading.value true; try { const params { items: cartStore.items.map((item) ({ productId: item.productId, quantity: item.quantity })), receiver: { name: receiverForm.name, mobile: receiverForm.mobile, address: receiverForm.address } }; const data await createOrderApi(params); cartStore.clearCart(); router.push({ path: /pay, query: { orderNo: data.orderNo } }); } finally { submitLoading.value false; } }创建订单后前端不要本地修改订单状态应该携带orderNo跳转到支付页。这一步在很多项目中会被忽略导致用户直接刷新页面找不到订单号支付流程无法继续。api/order.js中定义// 文件路径src/api/order.js import request from /utils/request; export function createOrderApi(data) { return request.post(/orders, data); } export function getOrderDetailApi(orderNo) { return request.get(/orders/${orderNo}); } export function createNativePaymentApi(orderNo) { return request.post(/payments/native, { orderNo }); } export function getOrderPayStatusApi(orderNo) { return request.get(/orders/${orderNo}/pay-status); }6. 支付模块完整实战从预下单到状态轮询6.1 支付页核心逻辑支付页是整个流程中最关键的一环。页面接收订单号后需要完成以下操作根据orderNo请求支付预下单接口拿到codeUrl。使用qrcode库把codeUrl转为二维码图片。展示“订单金额”和“二维码区域”。启动定时器每隔 1 到 3 秒查询一次订单支付状态。当订单状态变为PAID后停止轮询并跳转到订单详情页。完整代码如下!-- 文件路径src/views/PayView.vue -- template div classpay-page el-card classpay-card template #header span订单支付/span /template div v-ifloading classpay-loading 正在获取支付信息... /div div v-else-ifqrCodeDataUrl classpay-content div classamount-text 应付金额 span classamount¥ {{ (payAmount / 100).toFixed(2) }}/span /div img classqr-img :srcqrCodeDataUrl alt支付二维码 / div classtip-text 请使用手机扫码完成支付 /div div classpay-status 当前状态{{ orderStatusText }} /div /div /el-card /div /template script setup import { ref, computed, onMounted, onBeforeUnmount } from vue; import { useRoute, useRouter } from vue-router; import { ElMessage } from element-plus; import QRCode from qrcode; import { createNativePaymentApi, getOrderDetailApi, getOrderPayStatusApi } from /api/order; const route useRoute(); const router useRouter(); const orderNo route.query.orderNo; const loading ref(true); const qrCodeDataUrl ref(); const payAmount ref(0); const orderStatus ref(PENDING); let timer null; const orderStatusText computed(() { const map { PENDING: 待支付, PAID: 已支付, CANCELLED: 已取消 }; return map[orderStatus.value] || orderStatus.value; }); onMounted(async () { if (!orderNo) { ElMessage.error(缺少订单号); return; } try { const detail await getOrderDetailApi(orderNo); payAmount.value detail.payAmount; const payData await createNativePaymentApi(orderNo); qrCodeDataUrl.value await QRCode.toDataURL(payData.codeUrl); timer setInterval(pollPayStatus, 2000); } catch (error) { console.error(获取支付信息失败, error); } finally { loading.value false; } }); async function pollPayStatus() { try { const res await getOrderPayStatusApi(orderNo); orderStatus.value res.orderStatus; if (res.orderStatus PAID) { clearInterval(timer); timer null; ElMessage.success(支付成功); router.replace({ path: /order/detail, query: { orderNo } }); } } catch (error) { console.error(查询支付状态失败, error); } } onBeforeUnmount(() { if (timer) { clearInterval(timer); timer null; } }); /script style scoped .pay-page { display: flex; justify-content: center; padding: 40px 16px; background: #f5f7fa; min-height: 100vh; } .pay-card { width: 480px; text-align: center; } .amount-text { font-size: 16px; margin-bottom: 24px; } .amount { color: #e6a23c; font-size: 28px; font-weight: 600; } .qr-img { width: 240px; height: 240px; border: 1px solid #ebeef5; padding: 8px; background: #fff; } .tip-text { color: #909399; font-size: 14px; margin: 16px 0; } .pay-loading { padding: 40px 0; color: #909399; } /style6.2 支付状态轮询的设计要点支付页中轮询的设计有几个容易被忽略的地方。第一轮询必须在组件销毁时关闭。用户直接关闭浏览器标签页会导致组件进入beforeUnmount所以要把clearInterval放在onBeforeUnmount中避免后台继续发起无意义的请求。第二轮询不能只查询一次就结束。二维码支付的特点是用户扫码时间不可控所以需要每隔一定时间发起查询。间隔建议设置为 2 到 3 秒太短会增加服务端压力太长会让用户感觉延迟明显。第三支付成功后要使用router.replace而不是router.push。原因是用户从支付页到达订单详情后如果点击浏览器返回按钮不应该再回到“正在支付”的页面否则可能出现重复支付提示。第四真实收款成功必须以服务端收到的支付回调为准前端轮询结果只能作为展示参考。比如后端服务在自己收到微信或支付宝的异步通知后才将订单状态更新为已支付。前端不能因为收到“PAID”就直接发货否则可能造成刷单漏洞。7. 订单详情页与支付结果的最终展示当支付状态轮询到PAID后页面跳转到订单详情页。订单详情页应该展示订单号、商品信息、实付金额、订单状态等信息。这个页面相对简单核心是调用订单详情接口把后端数据回显到页面上。如果支付成功后订单状态仍然没有更新可以增加一个手动“刷新状态”按钮让用户主动查询。!-- 文件路径src/views/OrderDetailView.vue -- template div classorder-detail el-card v-iforder classdetail-card template #header span订单详情/span /template el-descriptions :column1 border el-descriptions-item label订单号 {{ order.orderNo }} /el-descriptions-item el-descriptions-item label订单状态 {{ statusText }} /el-descriptions-item el-descriptions-item label支付金额 ¥ {{ (order.payAmount / 100).toFixed(2) }} /el-descriptions-item el-descriptions-item label收货信息 {{ order.receiverName }} {{ order.receiverMobile }} /el-descriptions-item el-descriptions-item label收货地址 {{ order.receiverAddress }} /el-descriptions-item /el-descriptions el-button v-iforder.orderStatus ! PAID typeprimary classrefresh-btn clickhandleRefresh 刷新状态 /el-button /el-card /div /template script setup import { ref, computed, onMounted } from vue; import { useRoute } from vue-router; import { ElMessage } from element-plus; import { getOrderDetailApi } from /api/order; const route useRoute(); const order ref(null); const orderNo route.query.orderNo; const statusText computed(() { const map { PENDING: 待支付, PAID: 已支付, CANCELLED: 已取消 }; return map[order.value?.orderStatus] || 未知; }); onMounted(async () { await loadDetail(); }); async function loadDetail() { try { order.value await getOrderDetailApi(orderNo); } catch (error) { console.error(获取订单详情失败, error); } } async function handleRefresh() { await loadDetail(); if (order.value.orderStatus PAID) { ElMessage.success(订单已支付); } } /script style scoped .order-detail { padding: 24px; max-width: 800px; margin: 0 auto; } .refresh-btn { margin-top: 16px; } /style至此一条从登录、加购、下单到支付的完整前端闭环就打通了。8. 常见问题排查8.1 高频率问题速查表问题现象常见原因解决思路登录成功后跳转正常刷新后登录态丢失token 只存在 Pinia没有持久化登录后写入 localStorage请求接口返回 401token 不存在、过期或格式错误检查拦截器 Authorization 头登录后重新获取 token后端返回了 code_url但二维码图片不显示二维码内容未正确传递或 qrcode 依赖未安装检查 codeUrl 是否为空安装并引入 qrcode支付二维码扫了但订单状态一直是待支付轮询间隔太长或后端状态更新依赖回调检查订单状态查询接口确认服务端是否收到支付回调页面提示缺少订单号创建订单后跳转支付页时没有拼接 orderNo使用 route.query.orderNo 接参跳转时携带 query支付页面返回上一页仍可重复支付没有在支付成功后清理页面缓存使用 router.replace 跳转金额计算出现小数误差前端直接使用浮点数计算金额金额统一使用分展示时再转换为元8.2 登录失败如何排查如果你在本地联调时遇到登录失败可以先确认几个位置请求地址是否正确。如果使用 Vite 开发代理检查vite.config.js中的 proxy 配置。后端返回的字段名是否与前端一致。有的后端返回token有的返回accessToken需要统一。密码是否在传输前需要加密。真实项目中密码通常不会明文传输可能需要先做 RSA 加密或使用 HTTPS。Vite 开发代理参考配置// 文件路径vite.config.js import { defineConfig } from vite; import vue from vitejs/plugin-vue; export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } });8.3 二维码不显示但接口正常如果后端返回正常codeUrl也有值但二维码区域空白可以检查安装的依赖是qrcode还是qrcode.vue。本文使用的是qrcode通过QRCode.toDataURL生成 base64 图片。如果你更习惯组件式写法也可以使用qrcode.vue用法如下template qrcode-vue :valuecodeUrl :size240 / /template script setup import QrcodeVue from qrcode.vue; import { ref } from vue; const codeUrl ref(https://example.com); /script两种方式都可以重点是根据项目习惯选择一种并保持一致。9. 工程化与安全最佳实践9.1 token 安全与权限控制在真实电商项目中token 的存储位置需要认真考虑。localStorage容易受到 XSS 攻击cookie中的httpOnly可以降低风险但要同时处理 CSRF 问题。建议不要把用户密码明文保存在前端任何位置。请求拦截器统一带上Authorization头。退出登录时清除本地 token 和用户信息。路由守卫只做页面级控制真正的权限校验必须在后端接口完成。9.2 支付安全注意事项支付功能涉及资金安全以下原则必须遵守订单金额以后端计算为准前端绝不能自行修改金额。支付预下单接口必须有登录鉴权不接收前端传入的金额和商品明细。订单状态更新以后端支付回调为准不能只信任前端轮询结果。测试环境使用支付宝沙箱或微信支付沙箱商户号不要使用真实商户信息调试。生产环境密钥由服务端保管前端不直接接触商户私钥和签名密钥。页面销毁时清除支付状态轮询定时器。9.3 代码规范与可维护性电商项目通常会持续迭代建议从第一天就保持清晰的代码分层。接口请求全部放入src/api目录按模块拆分文件。每次后端接口变更时只需要修改对应的 api 文件不影响页面代码。全局状态放入 Pinia不要在多个组件中各自保存购物车副本否则会出现各页面数据不同步的问题。金额计算封装成通用方法。例如// 文件路径src/utils/money.js export function formatPrice(cents) { return ¥ ${(cents / 100).toFixed(2)}; }所有金额展示统一走这个方法可以避免大量重复的toFixed(2)代码。10. 总结与下一步建议本文从零梳理了一套 Vue3 电商应用从登录到支付的完整流程重点代码集中在登录鉴权、Axios 拦截器、路由守卫、订单创建、支付二维码展示和支付状态轮询这几个阶段。从登录到支付最核心的思维不是分别写登录页和支付页而是理解接口数据如何流动登录拿到 tokentoken 通过拦截器自动注入后续请求下单拿到 orderNoorderNo 再用于支付预下单和状态轮询。只要这条链路通了电商前端的主流程就完成了一大半。如果你打算继续深入可以从以下方向扩展接入真实后端把 Mock 换成 Swagger 接口。增加商品 SKU 和库存逻辑。学习微信小程序中的wx.requestPayment对比 H5 扫码支付与小程序支付的区别。增加订单超时自动取消逻辑。完善异常情况例如支付成功回调延迟、退款流程、订单关闭流程。第一次实现支付流程时建议先用支付宝沙箱或微信 Native 支付跑通扫码流程不要一上来就挑战复杂的 H5 内支付。扫码支付的链路最简单也能让你更清晰地理解“预下单、展示二维码、轮询结果”这套后端与前端协作模型。等你理解以后再切换到 JSAPI 或 App 支付就会发现所有支付渠道的前端逻辑本质上是相同的。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号