
做这类“游戏商城”项目的人不少但大部分人最后都卡在了环境配置、前后端联调这些基础却要命的环节上。这次我把一个基于 Node.js Vue 的原神主题游戏商城完整重做了一遍从环境搭建到部署把过程里踩过的坑和最终跑通的方案全部整理出来给正在做或准备做类似项目的朋友一个可以直接抄作业的参考。先交代一下技术选型Vue负责前端页面和交互Node.js负责后端接口和静态服务。选择这个组合不是因为赶时髦而是因为前后端都用 JavaScript减少了一门语言的维护成本对单人开发或小团队来说非常友好。整个项目包含商品展示、分类筛选、商品详情、购物车、模拟结算等完整流程代码量不算巨大但麻雀虽小五脏俱全非常适合用来彻底搞懂前后端分离项目的真实工作方式。1. 项目整体设计与技术选型1.1 为什么选 Node.js Vue 而不是别的方案先说结论这个组合最适合你“想快速看到完整产品”的需求也最适合一个人搞定全栈的场景。Vue 的优势在于轻量和渐进式。它不像某些重型框架那样要求你遵循一套严格的架构约束你可以从简单的模板渲染开始逐步引入路由、状态管理、组件库。对于商城这种以“看商品、选商品、加购物车”为主的项目来说Vue 的响应式系统能把“页面变化”和“数据变化”绑定得非常自然写起来很顺手。比如购物车角标的数字数据一变视图自动更新不需要你手动去操作 DOM。Node.js 这边选 Express 作为框架原因也简单Express 在 Node.js 世界里就像 Vue 在前端世界里一样生态成熟、教程多、踩坑案例多。而且 Express 的中间件机制非常直观从接收请求到返回响应的中间链路都是通过函数一层层剥开的这对理解 HTTP 服务的工作方式很有帮助。用 Node.js 做后端还有一个隐性优势你可以直接复用前端的数据结构和工具函数比如校验函数、价格格式化函数前后端共享一份代码完全没有障碍。1.2 需求拆解与功能模块划分做项目之前先别急着敲代码。把需求拆清楚了后面才会顺。我按一个正常商城的核心链路来拆浏览商品 → 查看详情 → 加入购物车 → 结算支付模拟 → 查看订单。围绕这条链路前端需要哪些页面基本就浮出水面了首页轮播图 推荐位 热销排行商品列表页分类筛选 排序商品详情页大图展示 参数说明 加入购物车按钮购物车页商品清单 数量修改 金额计算结算页模拟支付流程订单页订单列表展示后端则对应提供这些接口获取商品列表、根据分类筛选商品、获取商品详情、创建购物车数据、创建订单、获取订单列表。如果要做用户体系还得加上注册和登录但商城核心链路里先把商品和订单跑通才是第一优先级。1.3 目录结构与数据流设计用表格把项目结构列出来看一眼你就能明白整体布局目录/文件职责frontend/Vue 前端工程frontend/src/views页面组件frontend/src/components公共组件frontend/src/router前端路由配置frontend/src/store状态管理backend/Node.js 后端工程backend/routes接口路由backend/data数据存储文件backend/app.js后端入口数据流向很清晰Vue 组件里发起 axios 请求后端 Express 路由接收请求从数据文件里读取或写入数据然后返回 JSON 给前端组件组件把数据渲染到页面上。理解了这个数据流你就抓住了整个项目的命脉。2. 环境搭建Node.js 安装与环境变量配置全流程2.1 版本选择与下载Node.js 版本选择上我的建议很明确用官方标注 LTS 的版本。LTS 的意思是 Long Term Support长期维护版本稳定性优先。开发这类小项目没必要追最新版LTS 就足够了。下载路径是 Node.js 官网Windows 用户下载 .msi 格式的安装包macOS 用户下载 .pkg 格式。安装过程基本就是一路 Next但有一个细节要留意在安装向导里有一个 “Add to PATH” 的选项务必确认它是选中的。这一步如果漏了装完之后命令行里找不到 node 命令你还得手动补环境变量。安装完成后打开命令提示符或终端验证一下结果node -v npm -v能输出版本号说明核心安装成功。比如node -v输出v18.20.3npm -v输出10.7.0这都是正常的。2.2 环境变量配置的细节如果node -v提示“不是内部或外部命令”或者“command not found”说明 Node.js 没有被正确加到系统的 PATH 环境变量中。环境变量的作用打个比方你告诉系统“去这些目录里找可执行程序”。系统在接到node这个命令时就会在 PATH 记录的一堆目录里挨个找找到了就执行找不到就报错。要是安装时漏选了“Add to PATH”可以手动补上右键“此电脑”选“属性”然后在“系统属性”里找到“环境变量”。把 Node.js 的安装目录比如D:\Program Files\nodejs\分别追加到“用户变量”的 Path 和“系统变量”的 Path 里面。注意是追加不是覆盖改完之后要重新打开一个命令行窗口再试因为环境变量修改后需要新窗口才能生效。提示安装目录有空格也没关系像D:\Program Files (x86)\nodejs\这种路径在环境变量里是可以被正确识别的。2.3 npm 镜像源配置npm 是 Node.js 自带的包管理器负责下载第三方包。默认的源服务器在国外国内网络环境下下载速度往往很慢所以我会在安装完之后立刻配置镜像源换成国内源。npm config set registry https://registry.npmmirror.com配置完用下面这条命令验证npm config get registry看到输出的是镜像源地址就说明配置成功了。这一步不是必选项但如果你不想把大量时间花在等依赖下载上建议新手还是先配置好。2.4 前端脚手架创建项目环境就绪后我创建项目的方式用的是官方脚手架。Vue 3 比较推荐用 Vite 方式创建当然 Vue CLI 也没问题。npm create vitelatest frontend -- --template vue命令执行完成后进入 frontend 目录安装基础依赖cd frontend npm install然后按需安装路由、状态管理和 HTTP 请求库npm install vue-router4 pinia axios这几步跑完前端项目的基础框架就立起来了。这里有个选型心得Vue 3 的项目建议直接上 Pinia 替代 VuexPinia 的 API 设计更简洁而且 Vue 3 对 Pinia 的支持是官方推荐的。3. Vue 前端开发实战3.1 路由设计与页面导航商城项目的路由基本上是“一眼定终身”。我把路由拆成两级一级是导航栏级别的页面切换二级是商品详情、结算页面这种需要带参数进入的页面。import { createRouter, createWebHistory } from vue-router const routes [ { path: /, component: () import(/views/Home.vue) }, { path: /goods, component: () import(/views/GoodsList.vue) }, { path: /goods/:id, component: () import(/views/GoodsDetail.vue), props: true }, { path: /cart, component: () import(/views/Cart.vue) }, { path: /checkout, component: () import(/views/Checkout.vue) }, { path: /orders, component: () import(/views/Orders.vue) } ]这里有个细节/goods/:id是动态路由用来传递商品 ID。页面跳转的时候用router.push({ path: /goods/ id })详情页拿到 ID 后再请求接口获取商品数据。路由模式上我用了createWebHistory即 History 模式。这种模式 URL 里不会出现#符号看起来更干净。但要注意如果后端起的是静态服务刷新页面时会找不到对应路径后面 Node.js 部分会讲怎么配置支持 History 模式的后端服务。3.2 组件化拆分把页面拆成积木前端开发的核心思路就是组件化。把页面里可以复用的部分拆出来变成独立组件然后像拼积木一样组装页面。以首页为例我拆成了这几个部分顶部导航栏组件 NavBar全站通用首页轮播图组件 Banner商品卡片组件 GoodsCard商品列表里也复用底部信息栏 Footer商品卡片组件是复用率最高的它接收一个goods对象作为 props渲染出图片、名称、价格、标签点击卡片跳转到详情页。写好这个组件首页、商品列表页、搜索结果页就都不用重复写了。template div classgoods-card clicktoDetail img :srcgoods.image :altgoods.name / div classgoods-name{{ goods.name }}/div div classgoods-price¥{{ goods.price }}/div /div /template script setup import { useRouter } from vue-router const props defineProps({ goods: { type: Object, required: true } }) const router useRouter() function toDetail() { router.push(/goods/ props.goods.id) } /script这种写法非常直观。Vue 3 的script setup语法让代码更紧凑不需要到处写defineComponent和setup()函数体心智负担小了很多。3.3 状态管理购物车数据的正确打开方式购物车数据有一个特点多个页面都会用到。商品详情页要往购物车加数据购物车页面要读数据、改数量导航栏还要显示购物车物品总数。这种跨页面共享的数据必须放在全局状态里而不是存在某个页面的局部变量中。用 Pinia 来管理购物车状态import { defineStore } from pinia export const useCartStore defineStore(cart, { state: () ({ items: JSON.parse(localStorage.getItem(cartItems) || []) }), getters: { totalCount: (state) state.items.reduce((sum, item) sum item.count, 0), totalPrice: (state) state.items.reduce((sum, item) sum item.price * item.count, 0) }, actions: { addItem(goods) { const existed this.items.find(item item.id goods.id) if (existed) { existed.count } else { this.items.push({ ...goods, count: 1 }) } localStorage.setItem(cartItems, JSON.stringify(this.items)) } } })这里我做了数据持久化把购物车数据同步到localStorage。好处是用户刷新页面之后购物车里的东西还在非常实用的体验细节。开发者工具里许多项目的购物车刷新就空了看似小事体验上却是天壤之别。3.4 axios 请求封装与 API 调用商城项目里所有接口请求都走 axios我不会在每个组件里重复写请求代码而是统一封装成一个 API 模块。import axios from axios const http axios.create({ baseURL: http://localhost:3000/api, timeout: 10000 }) http.interceptors.response.use( (res) res.data, (err) { console.error(请求异常:, err.message) return Promise.reject(err) } ) export function fetchGoodsList(params) { return http.get(/goods, { params }) }封装的关键点在于统一配置 baseURL 和超时时间响应拦截器里直接取出res.data这样调用方只需关注数据本身。如果前后端服务端口不同还要在 Vue 项目的 vite.config.js 里配置开发代理把/api前缀的请求转发到后端服务地址避免跨域问题。3.5 主要页面的实现要点商品列表页是核心页面。我需要让用户通过左侧分类栏切换“角色”“武器”“圣遗物”“材料”等分类这就需要分类筛选功能。我实现的方式是在列表页维护一个currentCategory响应式变量点击分类时更新这个变量然后重新请求接口。首页的视觉策划是商城项目的重头戏。因为原神主题的视觉风格比较强商品的渲染展示上需要优先突出游戏特色元素。轮播图就直接使用了游戏默认的视觉素材做展示位下方是“热门角色”和“推荐商品”两个区块数据的来源统一走后端接口这样后期替换内容只需要改数据库数据。商品详情页要展示商品的大图、名称、描述、价格信息以及关键参数表。关键参数表我用一个数组来渲染每一项是标签和值的对应关系这样数据结构清晰后期新增参数只往数组里加对象就行。结算页面的设计要考虑业务逻辑的完整性我设置了一个页面跳转校验如果购物车是空的就提示用户先加购商品。4. Node.js 后端开发与接口设计4.1 Express 服务搭建后端相对简单但简单不等于粗糙。我用了 Express 框架搭建服务的骨架。const express require(express) const cors require(cors) const goodsRouter require(./routes/goods) const orderRouter require(./routes/orders) const app express() app.use(cors()) app.use(express.json()) app.use(/api/goods, goodsRouter) app.use(/api/orders, orderRouter) app.listen(3000, () { console.log(Server is running at http://localhost:3000) })cors()解决跨域问题express.json()解析前端传来的 JSON 请求体。路由文件拆分让代码结构清晰商品接口和订单接口互不干扰。一个我在很多新手项目里都见到过的问题把后端入口文件写得巨长路由、静态服务、数据库连接全塞在 app.js 一个文件里。项目一大就只能不断下滑页面维护体验非常差。路由文件拆分这个思路一定要尽早养成。4.2 数据存储方案选择商城的商品数据和订单数据存哪里这个项目我选择的是最轻量的方案——本地 JSON 文件存储。新增一个data/db.json文件里面按 JSON 格式组织数据{ goods: [ { id: 1, name: 刻晴, category: 角色, price: 199, image: /images/keqing.webp, desc: 璃月七星之一玉衡星。, params: { 稀有度: 5, 元素: 雷 } } ], orders: [] }然后在后端封装一个简单的读写函数把读取数据和写入数据的逻辑统一起来。后端启动时读一次文件到内存接口从内存里查数据写入时同步回写文件。这种方案的优点是零配置不需要额外安装数据库软件开发调试的效率很高非常适合教学演示或小型展示项目。缺点是数据量大之后性能会降低并发写入也可能产生问题所以生产环境还是要换 MySQL 或 MongoDB。但我作为开发者在项目早期用 JSON 文件把业务逻辑跑通再迁移到真正的数据库这个进度安排是最高效的。4.3 商品接口与详情接口设计商品列表接口设计成支持分类和排序两个参数router.get(/, (req, res) { const { category, sort } req.query let list db.goods if (category) { list list.filter(item item.category category) } if (sort price_asc) { list [...list].sort((a, b) a.price - b.price) } res.json({ code: 0, data: list }) })商品详情接口用动态参数接收商品 IDrouter.get(/:id, (req, res) { const goods db.goods.find(item item.id Number(req.params.id)) if (goods) { res.json({ code: 0, data: goods }) } else { res.status(404).json({ code: 1, msg: 商品不存在 }) } })这里有个细节URL 里的参数是字符串而数据里的 id 是数字所以要提前做Number()转换否则1 1永远不成立接口会一直返回商品不存在。这个坑特别隐蔽新手排查起来很可能在 find 逻辑里打转很久其实只是一个类型不一致的问题。4.4 订单接口与结算逻辑用户提交结算后前端把购物车商品快照和用户填写的收货信息一起 POST 到/api/orders。后端创建订单对象包含订单号、商品列表、总金额、创建时间、状态。router.post(/, (req, res) { const { items, receiver } req.body if (!items || items.length 0) { return res.status(400).json({ code: 1, msg: 订单商品不能为空 }) } const order { id: Date.now(), items, totalPrice: items.reduce((sum, i) sum i.price * i.count, 0), receiver, status: 待支付, createTime: new Date().toISOString() } db.orders.unshift(order) saveDb(db) res.json({ code: 0, data: order }) })订单号用Date.now()生成——在生产环境肯定要用更严谨的方案比如 UUID 或基于雪花算法生成的分布式 ID但展示项目这样写已经足够。这里我也让前端发起支付模拟操作支付状态更新后即可在“订单列表”看到数据。4.5 前端静态资源托管与 History 路由支持开发阶段前端跑在 Vite 提供的 dev server 上后端跑在 3000 端口两者通过代理联调。但上线部署时更简单的方案是让 Node.js 直接把 Vue 构建出来的静态文件一并托管。先在前端目录执行npm run build构建完成后会生成dist目录里面就是压缩好的 HTML、CSS、JS 文件。在后端加几行代码把 dist 目录交给 Express 托管并配置一个兜底路由支持前端 History 模式const path require(path) app.use(express.static(path.join(__dirname, ../frontend/dist))) app.get(*, (req, res) { res.sendFile(path.join(__dirname, ../frontend/dist/index.html)) })这样访问http://localhost:3000看到的就是整个商城页面/goods/1这样的路径刷新也不会 404。这个配置是前后端一体化部署的关键。5. 高频问题排查实录5.1 npm.ps1 无法加载文件问题做 Vue 项目时几乎每个 Windows 用户在安装依赖的时候都会撞上这个报错npm : 无法加载文件 D:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本这个报错的成因是 Windows 系统默认禁止运行 PowerShell 脚本而npm.ps1正是 PowerShell 脚本文件。系统默认的执行策略是 Restricted也就是什么都不允许执行。解决办法有两种。第一种以管理员身份打开 PowerShell执行Set-ExecutionPolicy RemoteSigned这条命令的意思本地创建的脚本可以运行从网上下载的脚本需要数字签名才能运行。执行时系统会询问是否确认更改输入Y回车即可。改完之后重新打开命令行窗口npm 就能正常使用了。第二种不使用 PowerShell而是改用命令提示符 CMD。CMD 不执行 PowerShell 脚本策略直接输入npm -v就能运行。这个方法属于临时绕过但如果不想动系统策略用 CMD 完全没毛病。注意不要一见到报错就图省事用-ExecutionPolicy Bypass永久绕过正常开发环境RemoteSigned已经足够改得过松会把系统置于不必要的风险中。5.2 跨域问题前端请求不到后端接口前端访问http://localhost:3000/api/goods浏览器报跨域错误这是一个非常典型的问题。浏览器阻止的是“前端页面地址”和“请求接口地址”在“协议、域名、端口”任一层面不一致时发起的请求。开发阶段有两条路。一条是在后端用cors()中间件这是最省事的方式。cors是一个 npm 包安装后挂载到 Express 上即可。它本质上是给响应头加Access-Control-Allow-Origin: *告诉浏览器“这个接口允许所有来源访问”。另一条是在前端 Vite 配置里开启代理把/api开头的请求转发到后端地址。这种方式更加推荐因为它从“前端视角”解决了跨域问题而且不需要后端做任何联动配置。两种方案结合场景使用开发初期可以用 cors 快速联调后期上线如果前后端分离部署再改用 Nginx 代理。5.3 端口占用错误后端启动时经常会碰到一启动就报端口被占用。Windows 下用下面的命令排查netstat -ano | findstr :3000在输出结果里看最后一列的 PID然后在任务管理器里找到对应进程右键结束掉。或者用命令行直接干掉指定 PIDtaskkill /PID 1234 /F也可以直接把后端监听端口改掉app.listen(3001)。但要注意如果前端代理里写死了 3000 端口改了后端端口后代理记得同步修改。5.4 Vue Devtools 装不上或不显示Vue 开发者工具是排查 Vue 组件状态、路由、事件的重要工具。装了插件却发现不生效常见原因是浏览器里插件被浏览器策略禁用了。新版 Chrome 对扩展程序有严格审核机制有的版本默认不允许插件运行在本地文件或特殊标签页上。解决办法是进入浏览器的扩展管理页面确认 Vue Devtools 已启用同时勾选“允许访问文件网址”。再一个常见原因是开发页面用了旧版 Vue 开发模式而 Devtools 版本和 Vue 版本匹配不上注意确认开发项目用的是 Vue 3 还是 Vue 2下载对应的 Devtools 版本。5.5 商品图片加载不出来开发过程中我遇到过商品图片全部裂开的情况排查后发现是图片引用路径吃掉了服务器地址。把图片路径改成完整 URL 之后问题迎刃而解。建议所有商品图片的路径在接口层就处理成完整地址不要在渲染层拼接前缀。另一种可能的原因是目录位置不对Express 静态资源默认一个地址前后端目录结构一变就 404最好的定位方式就是直接在浏览器里打开图片 URL 看返回什么状态码。5.6 路由刷新 404 的处理上线后发现在商品详情页面或购物车页面按一下 F5页面变成了 404。原因之前提到过History 模式的路由交给后端时后端不知道你要访问哪一页。解决方式就是早前那段兜底代码把所有 GET 请求都交给index.html处理。如果静态资源和 API 都在同一个端口要特别注意兜底代码的位置不能让 API 请求也被get(*)拦截路由注册顺序上 API 应当放在静态资源之前。6. 项目部署与后续扩展方向6.1 部署流程简记整个项目部署的关键步骤其实并不复杂。前端先构建出dist目录后端用 Express 托管该目录同时保证 API 服务正常。把整个backend目录和frontend/dist目录一起复制到服务器装上 Node.js 环境执行npm install和node app.js一个商城项目就可以访问了。如果想让它长期稳定运行推荐用进程管理器守护 Node.js 进程挂了能自动重启。要对外访问还需要配置反向代理把域名流量转发到 3000 端口。6.2 从展示项目到生产系统的扩展清单项目跑通之后可以逐步往真正的生产级系统靠拢。以下是我根据自己的经验列出的优先级清单扩展项优先级说明用户注册登录体系高引入会话管理或 JWT 令牌真实数据库高从 JSON 文件迁移到 MySQL鉴权中间件高订单接口要校验用户身份统一错误处理中码出更具语义的错误码和请求提示列表分页中商品数量上去后必须加分页搜索功能中按商品名关键词模糊搜索后台管理低商品上下架和库存管理接入真实支付低支付宝/微信沙箱环境对接后端在从 JSON 存储切换到 MySQL 时建议不要复用当前的接口逻辑手动拼接查询语句更推荐马上把数据库访问层独立出来用现成的 ORM 工具统一管理数据表模型。前端这边也可以借机做性能优化给商品图片加懒加载列表页使用 keep-alive 缓存切换过的页面这些优化离生产环境会更近一步。6.3 我给新手的几条建议把一个完整项目从零到一跑通我认为以下几点对你比任何框架知识都重要。第一先复制主干流程别追求一步到位。一个能跑通的简单流程比一堆没连通的代码有价值得多。第二看接口返回的原始数据这是排查所有前后端问题的第一信源。第三写代码时立刻想清楚“这个值从哪来、要存到哪去”带着数据流去写代码效率高出很多。第四出现报错认真读报错内容它往往已经把答案和修复方向写在第一行里了不要急着重新安装环境。我在实际开发这个项目的过程里最大的感受是环境问题和联调问题占掉的调试时间比写业务逻辑的时间还要多。所以本文才会花那么大的篇幅去记录 npm 脚本限制、跨域配置、路由刷新这些“小事”。只要把这几个最容易踩的坑提前避开这个项目做起来会很顺手。最后分享一个小技巧开发阶段把前端 DevTools 和后端接口返回值的打印同时打开每次操作都观察数据在哪里发生改变、哪里没有变这种习惯会帮你快速定位八成以上的疑难杂症。