十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Vue项目实战:MOBA攻略平台开发到打包部署全流程

Vue项目实战:MOBA攻略平台开发到打包部署全流程 简介这是一套面向Web全栈开发学习者与毕业设计参考者的MOBA游戏攻略分享平台完整项目源码聚焦于游戏社区类应用的前后端协同实现。项目采用Spring Boot构建后端服务集成MySQL持久化存储并支持JSP与Vue双前端方案兼顾传统服务端渲染与现代单页应用交互体验适合Java Web与前端进阶实践。压缩包共814个文件含115个Java业务逻辑与控制器代码、45个Vue组件涵盖导航、面包屑、用户中心等核心模块、164个JS交互脚本、79个GIF动效资源及53个CSS样式文件整体大小26.78MB结构清晰、模块划分明确。已有29373人学习下载资源中保留了.bak备份文件与多套批处理脚本如install.bat、run.bat便于环境快速搭建与版本回溯配套SQL建表语句、YML配置及完整目录层级可直接部署运行并深入理解MOBA类社区系统的权限管理、攻略发布与用户互动等典型业务流程。 我从一个实际做完并打包成 lw.zip 的前后端分离项目说起——基于 Vue 的 MOBA 类游戏攻略分享平台。这个项目本身不算“大”但涉及的链条很长Vue 组件化开发、路由设计、状态管理、后端接口对接、跨域处理、打包部署最后还要把整个工程压缩成 zip 交付或分发。任何一个环节没处理好后面都会连环踩坑。这篇文章就是把我从零到一实现这个平台的过程以及过程中踩过的坑、验证过的方案全部沉淀下来希望能给正在做 Vue 项目实战的同学一些能直接抄作业的参考。这项目适合谁看如果你正在学 Vue 但只停留在“照着视频敲代码”没真正从头规划过一个完整项目或者你刚接触前后端分离想搞明白 Vue 项目从开发到打包再到 zip 发布的全流程又或者你准备做毕设、作品集、面试项目——这篇内容都可以给你一个相对完整的参照系。1. 项目整体设计与技术选型思路1.1 需求定位MOBA 攻略平台到底需要什么先说项目背景。MOBA 类游戏比如英雄联盟、DOTA2、王者荣耀这类有一个很核心的玩家需求查攻略。这个需求细拆下来其实有三层一是英雄资料包括技能介绍、出装推荐、符文/天赋搭配二是玩法攻略包括对线技巧、团战思路、版本强势英雄排行三是个人数据比如自己常用英雄的胜率、场次记录。所以这个平台的前端核心模块可以归纳成四个英雄库、攻略列表、用户系统、个人数据中心。英雄库要解决的是“查得快”所以列表页要有搜索、筛选、排序攻略列表要解决“看得爽”所以要有分页、分类、富文本渲染用户系统要解决“进来能玩”所以要有注册登录、Token 鉴权个人数据中心要解决“数据看得懂”所以要用图表展示胜率趋势、英雄使用频率。这几个需求组合在一起用 Vue 来落地是非常合适的。组件化开发天然适合这种“页面多但结构相似”的场景——英雄卡片、攻略卡片、评论列表、筛选栏这些都可以抽象成独立组件复用避免每个页面都复制粘贴一大段模板代码。1.2 为什么选 Vue 而不是 React 或者原生 JS这可能是很多人在项目启动前纠结的问题。我当时的考虑很简单直接Vue 的上手曲线比 React 平缓对新手友好模板语法贴近传统 HTML团队里如果有人之前写 jQuery 或原生 JS迁移成本很低而且 Vue 的响应式系统是“自动追踪依赖”的思路写业务代码时不需要手动 memo心智负担小很多。如果拿原生 JS 实现同样的功能光是英雄列表的重新渲染——每次搜索关键词变化都要手动操作 DOM、拼接字符串、绑定事件——代码量至少翻一倍而且维护起来非常痛苦。Vue 的响应式机制把“数据变化 - 视图更新”这条链路封装掉了开发效率高得多。这里不是捧一踩一React 有 React 的优势但在这个以内容展示和表单交互为主的攻略平台上Vue 的模板语法和计算属性能让业务代码更直接。尤其是后面做胜率趋势图表时computed 属性对数据的派生计算非常顺手——我只要维护原始数据源所有展示层的计算都交给 computed 去自动推导不用手动同步。1.3 前后端分离架构与项目目录规划项目最终采用的是前后端分离架构前端 Vue 3 Vite后端 Spring Boot如果你不想上 Java用 Node.js Express 或者 Python FastAPI 也完全可以核心逻辑是一样的数据库用 MySQL。前后端通过 RESTful API 通信。这种架构的好处是前端开发和后端开发可以并行推进而且部署时可以分开——前端打包成静态文件丢给 Nginx后端按服务方式跑。前端目录结构我按“功能模块 资源类型”的方式组织这样一个人维护也不会乱src/ api/ # 接口请求封装 hero.js article.js user.js assets/ # 静态资源 components/ # 公共组件 HeroCard.vue ArticleCard.vue Pagination.vue SearchBar.vue router/ # 路由配置 index.js store/ # 状态管理我用的 Pinia user.js hero.js views/ # 页面级组件 Home.vue HeroList.vue HeroDetail.vue ArticleList.vue ArticleDetail.vue Login.vue Register.vue Profile.vue utils/ # 工具函数 request.js auth.js App.vue main.js这个结构看起来简单但实践下来有几个好处api 目录单独抽象页面里不直接写 axios 请求components 目录放复用组件views 目录只做“页面组装”业务逻辑尽量下沉到 composables 或者 store 里。2. 核心业务模块实现与关键技术点2.1 英雄列表页组件拆分与搜索筛选英雄列表页是这个平台的门面。需求就一句话把游戏里所有英雄展示出来支持按名字搜索、按定位筛选战士、法师、辅助等、按难度排序。但实现时牵扯到一个核心问题——怎么让搜索和筛选的效率最高。我在这里用的是computedref的组合。搜索关键词、筛选条件都定义成ref展示数据则是一个computed它把原始英雄列表从接口拿到的全量数据和当前搜索条件做匹配返回过滤后的结果。const heroes ref([]) const keyword ref() const category ref(全部) const filteredHeroes computed(() { let list heroes.value if (category.value ! 全部) { list list.filter(h h.category category.value) } if (keyword.value.trim()) { const kw keyword.value.trim().toLowerCase() list list.filter(h h.name.toLowerCase().includes(kw)) } return list })这里有个很关键的思考为什么要用 computed 而不是在搜索的时候手动执行一个 filter 方法并赋值给另一个 ref因为 computed 有缓存只有依赖项keyword、category、heroes变化时才会重新计算。如果手动方法执行性能上差距不大但代码里多了一个“需要手动同步”的状态一旦哪天某个触点忘了更新视图就会出现“数据改了页面没变”的诡异 bug。computed 把这种问题从根上杜绝了。组件拆分上我抽了SearchBar.vue输入框 下拉框和HeroCard.vue英雄卡片。父组件负责数据管理和状态协调子组件只管展示和抛事件。子组件通过defineProps接收数据、通过defineEmits向外通知这个模式一目了然!-- SearchBar.vue -- template div classsearch-bar input v-modelkeywordLocal placeholder搜索英雄名称 / select v-modelcategoryLocal option全部/option option战士/option option法师/option option辅助/option /select /div /template script setup defineProps({ keyword: String, category: String }) const emit defineEmits([update:keyword, update:category]) /script这种拆分的好处从第二次写页面时就开始显现了。后面做攻略列表页搜索栏和筛选栏几乎可以复用SearchBar的套路只是字段不同。如果不做组件化那就是两份几乎一样的代码互相复制——改一个 bug 要改两遍。2.2 英雄详情页动态路由与参数处理英雄详情页的 URL 设计为/hero/:id这样每个英雄都有独立地址方便分享。Vue Router 里配置动态路由{ path: /hero/:id, name: HeroDetail, component: () import(../views/HeroDetail.vue) }在详情页里获取路由参数最直接的方式是route.params.id。但这里有一个坑同一个组件被复用时——比如从“法师英雄列表”点进某个英雄再返回列表点另一个英雄——路由参数变了但组件实例不会重新创建onMounted里的初始化请求不会重新执行。这是新手最容易踩的坑。解决办法是在HeroDetail.vue里监听路由参数变化import { watch } from vue import { useRoute } from vue-router const route useRoute() const heroDetail ref(null) const fetchHeroDetail async (id) { const res await heroApi.getDetail(id) heroDetail.value res.data } watch( () route.params.id, (newId) { fetchHeroDetail(newId) }, { immediate: true } )这里immediate: true让组件首次挂载就会请求一次之后参数变化也会重新请求覆盖了所有场景。这也是我在实际开发中比较喜欢用的模式——别在onMounted里做数据初始化用watch immediate更稳妥。顺带提一下路由传参的另一个容易搞混的点query和params的区别。query 是写在 URL 问号后面的比如/search?keywordez刷新页面后依然存在params 是路径参数的一部分比如/hero/1刷新后也在。但如果用router.push({ name: HeroDetail, params: { id: 1 } })这种方式且配置了 props 接收刷新页面时参数仍然在 URL 上所以不会丢。真正会丢参数的是那些“靠组件内部状态记录的筛选条件”后面问题排查部分我会着重讲。2.3 攻略文章模块富文本渲染与分页攻略模块的核心是“文章列表 文章详情”。列表页数据是后端接口返回的带分页信息。这里要注意分页参数的设计我向后端传pageNum页码和pageSize每页大小后端返回total总数和list数据数组。前端封装了Pagination.vue组件通过v-model:page和v-model:size的方式绑定额外参数Pagination v-model:pagepageNum v-model:sizepageSize :totaltotal changeloadArticles /v-model的用法可能对刚接触 Vue 的人有点神秘说白了就是语法糖v-model:page等价于:pagepageNum update:pagepageNum $event。子组件里通过defineProps接收page通过defineEmits抛update:page和update:size当用户点击页码时抛事件父组件收到后更新页码并重新请求数据。文章详情页用的是富文本编辑器我推荐 wangEditor 或者 Quill看团队熟悉度保存的 HTML 内容前端直接用v-html渲染。这里有两个注意事项一是要把style的作用域限制好防止文章内容里的样式污染全局样式二是v-html渲染的内容里如果有图片要确认图片 URL 是否能正常访问——后端返回的富文本里存的可能是相对路径需要前端拼接出完整地址。我在这个项目里写了一个过滤器把文章内容里的src/upload/xxx.jpg替换成https://api.lw.com/upload/xxx.jpg这种完整路径。2.4 登录注册与权限控制Token 方案落地用户系统是很多功能的基础。注册、登录、个人中心这套逻辑很通用。我采用的方案是用户提交用户名密码后端校验通过后签发一个 Token我用的 JWT前端把 Token 存在localStorage里后续每个请求头都带上Authorization: Bearer token。后端根据 Token 判断当前用户身份。请求的封装是整个 Token 方案落地的关键。我用 axios 封装了一个request.js核心逻辑是请求拦截器里加 Token响应拦截器里统一处理 401 状态码——Token 过期或者非法时自动跳转登录页import axios from axios import router from ../router import { useUserStore } from ../store/user const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) service.interceptors.response.use( response response.data, error { if (error.response error.response.status 401) { const userStore useUserStore() userStore.clearUserInfo() router.push(/login) } return Promise.reject(error) } ) export default service到这里前端路由守卫按“是否需要登录”来划分页面。比如个人中心必须登录才能访问我配置了meta字段{ path: /profile, name: Profile, component: () import(../views/Profile.vue), meta: { requiresAuth: true } }路由前置守卫里判断router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next({ name: Login, query: { redirect: to.fullPath } }) } else { next() } })这里有个细节我把redirect参数带上了这样用户登录成功后可以自动跳回到原来想访问的页面而不是固定在首页。这个小体验在真实项目里很重要但很多教学示例都不会讲。2.5 数据可视化胜率趋势用 ECharts 展示个人中心里我加了两个图表英雄胜率趋势折线图、英雄使用频率柱状图。技术选型用的是 ECharts。在 Vue 3 里使用 ECharts 典型的做法是创建一个 div 容器在组件挂载后初始化实例然后监听数据变化重新 setOption。但要注意组件卸载时一定要调用dispose销毁实例否则会内存泄漏。import * as echarts from echarts import { onMounted, onUnmounted, ref, watch } from vue const chartRef ref(null) let chartInstance null const renderChart () { chartInstance.setOption({ xAxis: { type: category, data: props.dates }, yAxis: { type: value }, series: [{ type: line, data: props.rates, smooth: true }] }) } onMounted(() { chartInstance echarts.init(chartRef.value) renderChart() }) watch(() props.rates, renderChart) onUnmounted(() { chartInstance chartInstance.dispose() })实际开发中还有个兼容性问题——如果容器是隐藏状态比如 Tab 切换ECharts 初始化后宽度可能是 0图表显示不出来。解决办法是在容器可见后再调用resize()。这里我用的是一个简单方案切换到 Tab 时setTimeout(() chartInstance chartInstance.resize(), 0)。如果你用 el-tabs 这种组件监听 Tab 切换事件就可以了。3. 前后端联调、打包部署与 zip 发布3.1 开发环境下 Vite 代理配置与跨域搞定前后端分离开发时最烦的就是跨域。前端跑在http://localhost:5173后端跑在http://localhost:8080两个端口不一致浏览器会拦截跨域请求。解决跨域有两种常规思路后端开启 CORS或者前端通过代理转发。生产环境更推荐后端开 CORS但开发环境我更喜欢用前端代理因为它不需要后端改代码改完立刻生效。Vite 的代理配置在vite.config.js里很简单export default defineConfig({ server: { port: 5173, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, rewrite: path path.replace(/^\/api/, ) } } } })这样前端代码里请求/api/hero/listVite 开发服务器会自动转发到http://localhost:8080/hero/list。changeOrigin: true的意思是修改请求头里的 Host 字段为目标地址避免后端在反代场景下判断来源出错。3.2 生产构建Vite 打包与 Nginx 部署开发完成后执行npm run buildVite 会把项目打包到dist/目录。但生产部署没那么简单有几个配置必须提前想好。一是静态资源的 base 路径。如果你把 dist 部署在域名根路径下base: /默认值没问题但如果你要部署在子路径下比如https://example.com/lw/那就必须设置base: /lw/。这个配置直接影响 src 和 link 标签里静态资源的引用路径配错了页面会白屏控制台全是 404。二是历史路由模式下的 Nginx 配置。Vue Router 默认用 HTML5 History 模式URL 里没有#看起来更干净。但这种模式要求服务器把所有请求都重定向到index.html让前端路由自己决定展示哪个页面。Nginx 配置如下server { listen 80; server_name your-domain.com; root /var/www/lw/dist; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这段配置里两个 location 块第一个把前端路由丢给 index.html第二个把/api/开头的请求反向代理到后端服务。这样前端和后端就包在同一个域名下了生产环境也不存在跨域问题。3.3 在 Linux 服务器上压缩与解压 zip打包发布的常见操作项目做完后交付形式是lw.zip。这就要说 Linux 上 zip/unzip 的日常操作。把整个前端项目包含源码和构建产物压缩成 zip最常用的命令是zip -r lw.zip ./-r表示递归压缩子目录。但如果只是想压缩构建产物和部署配置不包含 node_modules那个东西七八千个文件压缩了也没意义可以这样zip -r lw-deploy.zip dist/ public/ package.json到了服务器上解压的命令是unzip lw.zip -d /var/www/lw/-d指定解压目标目录。如果没有 unzip 命令先装一下sudo apt install unzip # Ubuntu / Debian sudo yum install unzip # CentOS / RHEL还有一个很实用的操作如果 zip 包损坏了可以尝试用zip -FF修复。比如下载的 zip 包在传输过程中有字节损坏执行zip -FF damaged.zip --out fixed.zip这个命令会尝试扫描 zip 包的 central directory 并修复索引能救回不少数据。但注意修复后要检查一下内部文件的完整性不能保证 100% 恢复。3.4 压缩包在开发协作中的坑node_modules 和操作系统差异这个项目打包成 zip 分发给别人的时候最容易踩的坑就是把 node_modules 一起打进去了。node_modules 体积动辄几百 MB里面可能有上千个文件压缩和传输都极慢而且换一台电脑、换一个 Node.js 版本里面的原生模块很可能不兼容。正确做法是压缩包只包含源码、配置文件和 package-lock.json拿到的人自己执行npm install安装依赖。有锁文件的情况下npm 会安装到完全一致的依赖版本避免“我这跑得好好的你那报错”的扯皮现象。另一个针对 zip 的细节是操作系统的换行符和文件权限差异。Windows 下压缩的 zip 解压到 Linux 服务器后shell 脚本可能因为\r\n换行报错可执行文件可能丢失执行权限。如果项目里有 .sh 脚本解压后记得执行chmod x deploy.sh sed -i s/\r$// deploy.sh先把回车符清掉再加执行权限再运行避免被坑。4. 开发过程中遇到的典型问题与排查实录4.1 “file is not a zip file”到底怎么解决这是个非常经典的 zip 异常我身边不少人都遇到过。先说现象执行unzip xxx.zip时终端报错file is not a zip file或者invalid zip archive: could not find eocd。这两个报错的原因基本一样——文件其实根本不是标准的 zip 格式或者文件下载不完整。could not find eocd里的 eocd 是 End Of Central Directory 的缩写是 zip 文件末尾的目录索引块。如果文件下载到一半断了或者文件根本不是 zip只是改了扩展名就会出现这个错误。排查步骤我总结成三步第一步用file命令看真实文件类型file lw.zip如果输出显示HTML document或者ASCII text那说明下载落地的是网页内容可能是 404 页面或者拦截页根本不是 zip。第二步看文件大小是否合理ls -lh lw.zip前一个下载进度 90% 断掉后文件大小明显小于预期重新下载即可。第三步如果确认是 zip 但内容有损坏试一下zip -FF修复。另外如果你用的是 Windows 自带的“发送到压缩文件夹”生成的 zip偶尔解压器不兼容可以换 7-Zip 重新压缩一次很多时候就好了。4.2 项目解压后跑不起来的 node_modules 问题团队分发lw.zip后最常听到的一句话是“怎么我 npm run dev 报错了”点开报错通常写着The project can not found node_modules或者You can: 1. use npm install -g vue/cli。核心原因就一句话压缩包里没有 node_modules收包方没有执行依赖安装或者安装了但版本不匹配。做法很简单解压后先执行npm install如果网络环境差可以配置镜像源。个人建议先把 npm 镜像切到国内源再装npm config set registry https://registry.npmmirror.com npm install注意package-lock.json要和源码一起分发。锁文件记录了每个依赖包的精确版本只有带上它才能保证团队所有成员安装的依赖完全一致。如果只带 package.json可能你用的是 Vue 3.4队友装成了 3.5某些 API 用法对不上就会出现“我这边好好的你那边报错”的灵异事件。4.3 路由参数刷新丢失筛选条件不翼而飞功能开发完测试提了一个 bug在英雄列表页选了“法师”分类然后刷新浏览器分类又变成“全部”了。这个 bug 的原因非常典型——筛选条件存在组件内部的内存变量里刷新页面后 Vue 实例重新创建组件状态全部重置所有筛选条件自然清空。想让刷新后保留条件必须把筛选条件同步到 URL query 中。我当时的修复方案是把 category 和 keyword 同步到 URL 的 query 参数。组件挂载时从route.query读取初始值筛选变化时通过router.replace更新 URLimport { useRoute, useRouter } from vue-router const route useRoute() const router useRouter() const keyword ref(route.query.keyword || ) const category ref(route.query.category || 全部) watch([keyword, category], () { router.replace({ name: HeroList, query: { keyword: keyword.value, category: category.value } }) })这样筛选条件就在 URL 里“落地”了。刷新页面后Vue 从 URL 恢复条件用户甚至可以把筛选好的 URL 发给别人直接看到同样的列表。这个方案同时解决了“刷新丢失”和“分享快照”两个问题。4.4 keep-alive 切换路由后 el-table 滚回头部这是一个 Vue 细节优化但实际体验影响很大。项目里个人中心和攻略列表之间切换时我用keep-alive缓存了列表页防止每次切换都重新请求接口。但问题来了——当用户把 el-table 滚到很下面切到别的页面再回来时表格的滚动条位置变了不是变了是滚回了顶部。原因是 keep-alive 缓存的是组件实例包括了数据和 DOM 状态但 el-table 的滚动位置不是持久化到组件 data 里的而是在 DOM 节点的 scrollTop 属性上。keep-alive 缓存的是 VNode 和实例DOM 被移除了重新插入时表格的滚动容器是新的 DOM 节点scrollTop 自然归零。解决办法是在组件停用前记录滚动位置重新激活后恢复script setup import { onActivated, onDeactivated } from vue let scrollTop 0 const handleTableScroll (e) { scrollTop e.target.scrollTop } onActivated(() { // 恢复滚动位置 const wrap document.querySelector(.el-scrollbar__wrap) wrap (wrap.scrollTop scrollTop) }) /script关键点是onDeactivated时记录位置onActivated时恢复。这种问题纯看文档很难发现只有实际用过 el-table 配合 keep-alive 才会碰到。4.5 Vue DevTools 调试经验开发 Vue 项目强烈建议装 Vue DevTools 浏览器插件。这个工具能帮你实时查看组件树、Props、data、computed 状态排查“数据为什么没更新”“computed 为什么没重算”这类问题效率成倍提升。实际调试中我常用的三个功能一是组件面板点击页面上某个元素可以在组件树里定位到对应组件直接看到它的 props 和内部状态。二是 Pinia/Vuex 面板查看 store 里所有状态和 action 调用记录非常适合排查“store 数据被谁改了”这种问题。三是时间旅行调试在 DevTools 里可以回放状态变化记录定位是哪个操作导致状态突变。如果你装了插件但没生效先在浏览器地址栏确认插件已启用、Vue 项目运行在开发模式未压缩的 vue.js生产构建下不会显示 DevTools 面板。4.6 前后端联调和 Node.js 版本冲突的坑最后分享一个环境上的问题。有段时间项目跑着跑着 Vite 打包就报错查了半天发现是 Node.js 版本太老导致的。Vite 3 以上要求 Node.js 14.18 / 16Vite 5 要 Node 18 或 20。我本地原来是 Node 14后来一查 Vite 版本要求直接切到 Node 18 LTS 才顺畅。用nvmNode Version Manager管理多个 Node 版本非常方便nvm install 18.20.2 nvm use 18.20.2如果你拿到项目的压缩包后npm install各种警告或者编译原生模块失败第一反应先确认 Node 版本是否满足要求别先怀疑源码。因为很多原生依赖包在编译时会调用 node-gypNode 版本和 Python 环境都会影响结果。5. 项目实战收官从 lw.zip 到可持续迭代项目写到这一步功能已经跑通交付包也打好了。但作为一篇实战分享我还想聊几个项目后续会自然遇到的问题和我的应对思路这些决定了你做完的 demo 能不能真正升级成一个可以被别人用的平台。第一个是内容运营的问题。攻略平台的生死线在内容。技术上是增删改查但内容从哪来我的规划是给管理员做一个简单的后台发布页面富文本编辑器 图片上传同时允许注册用户投稿由管理员审核后发布。这样内容生产从“你一个人写”变成“社区用户在写”平台才有持续更新的动力。第二个是性能优化的问题。当英雄数量、攻略文章数量涨到几千甚至几万条时首页一次性拉全量列表就不现实了。我的做法是给列表接口强制分页前端加“加载更多”按钮而不是一页一页翻对英雄图谱这类数据变更频率不高的接口在 Nginx 层开启 gzip 压缩几个大 JSON 接口的传输体积直接能缩小 70% 以上。再加上后端给英雄详情接口做 Redis 缓存大多数情况下数据库都不会有压力。第三个是安全加固的问题。我实际操作中遇到的第一版安全隐患是对上传图片没做任何格式校验。实测直接传一个带脚本的 html 文件也能成功这个漏洞一旦被利用就是存储型 XSS。后来我改成了三件事限制上传扩展名白名单 .jpg/.png/.gif/.webp、校验文件头Magic Number而不是只看扩展名、上传目录禁止执行脚本。这三点做完风险面基本可控。还有很实际的一点打包发布前花十分钟把node_modules从 zip 包里排除掉同时把.env文件里的数据库密码、API 密钥等敏感信息全部移除。开发的时候图省事把密钥写在注入 env 文件里是常态但交付出去就是安全事故。如果你需要给别人演示用.env.example提供一份脱敏模板这才是正经做法。个人感觉做完这个 MOBA 攻略平台项目后学到最多的不是某个 API 怎么调而是“一个真实项目需要经历的全套流程”——需求拆分、组件抽象、路由设计、状态管理、跨域处理、打包部署、问题排查。这些东西单拎出来看知识点都不难但串起来却很考验对整体链路的理解。我把我的踩坑记录写出来就是希望你在做同类项目时能在这些地方少花点排查时间把精力放在更有技术含量的模块上。本文还有配套的精品资源点击获取
返回列表