资讯动态

Vue调用WebApi接口实战:环境搭建、跨域处理与部署全攻略

发布时间:2026/10/1 6:16:08 来源:尧图企业网站定制
前后端分离这个词很多人天天挂在嘴边但真正上手做第一个例子时卡住的地方往往不是某个高深算法而是“前端页面怎么把后端接口里的数据拿出来再规规矩矩地摆在页面上”。这篇文章就是围绕这个需求来的用Vue写一个完整的例子请求后端WebApi接口获取数据并显示出来环境怎么搭、接口怎么定、跨域怎么破、页面怎么写、最后打包部署会踩哪些坑一次讲透。这个例子适合三类人看刚学完Vue基础语法但没做过真实接口联调的新手之前一直在写页面假数据、想搞清楚前后端怎么配合的转岗同学以及想快速搭一个前后端分离Demo拿去面试或做毕设的人。我会用最简单的商品列表作为业务场景尽量不堆复杂概念但关键的“为什么这样做”都会交代清楚。1. 别急着敲代码先把Vue开发环境和后端接口跑通1.1 需要的软件和版本选择先说结论Vue这边你需要装好Node.js和npm后端我用ASP.NET Core WebApi举例标题里的WebApi通常就是指它但你完全可以用Spring Boot、FastAPI、Express等任何能返回JSON的后端替代联调思路一模一样。Node.js的安装我建议直接去官网下载LTS版本不要装最新的Current版也不要嫌版本老。LTS版本意味着周边工具链都验证过很多Vue项目跑不起来最后查下来就是Node版本太新导致的兼容问题。装完打开终端验证一下node -v npm -v能输出类似v20.x.x和10.x.x的版本号环境就算通了。如果你用的是macOS或者Linux建议顺手装个nvm来管理Node版本因为后面你很可能同时维护好几个项目有的项目要Node 16有的要Node 20没有版本管理器会很痛苦。Windows用户用nvm-windows用法类似。另外一个很多人忽略的点npm在国内网络环境下安装依赖可能非常慢甚至直接超时。这是网络原因而不是操作问题不用反复重装只需要把npm的registry临时指到镜像源提速装完想换回官方源也可以随时换。npm config get registry npm config set registry https://registry.npmmirror.com1.2 用Vite脚手架创建Vue工程而不是自己从零配现在创建Vue项目我推荐用官方脚手架create-vue它的底层是Vite启动速度和热更新都比老一代的Vue CLI快很多而且在脚手架阶段就会帮你把ESLint、TypeScript、路由这些选项问清楚省得后期手动加。在终端执行npm create vuelatest接着会有一系列交互式提问我建议这样选项目名称vue-webapi-demo是否使用TypeScript新手建议选No先把数据流转跑通TS可以后面再补是否启用Vue Router这个例子暂时不需要路由选No但如果你想练路由选Yes也不影响是否启用Pinia选No我们不涉及跨页面共享状态是否启用ESLint、Prettier选Yes早一点习惯代码规范没坏处等脚手架生成完成后进入目录装依赖cd vue-webapi-demo npm install npm run dev默认情况下Vite会启动在http://localhost:5173浏览器打开就能看到Vue官方的欢迎页面。看到这个页面说明整个前端骨架已经活了接下来要做的就是把它改造成我们自己的样子。1.3 项目目录里最该认识的几个文件脚手架会生成一整套目录新手最容易看懵但真正要动的主要就这几个src/App.vue根组件整个应用的第一层壳我们的页面入口就改这里。src/main.js应用启动入口负责创建应用实例、挂载根组件后面如果要全局注册什么插件都在这里做。src/components/组件目录我会在里面新建一个商品列表组件。vite.config.jsVite配置文件后面配置开发代理就靠它。package.json项目依赖清单和脚本命令比如npm run dev、npm run build都在这里定义。理解这几个文件的关系就够了不用急着把每个文件都看一遍。Vue的学习曲线本来就应该从“能跑通”开始再逐步深入原理一上来就啃源码反而容易劝退。2. 后端WebApi端返回什么数据前端才好接2.1 一个最小可用的ASP.NET Core WebApi接口前端要数据后端得先给数据。我用ASP.NET Core WebApi写一个商品接口尽量保持最小可运行。先建项目dotnet new webapi -n ProductApi cd ProductApi dotnet run --launch-profile http注意--launch-profile http因为默认模板经常配的是https本地调试时HTTPS证书可能没装好会白白多出一次让你头疼的证书错误。直接用http启动开发阶段完全够用。新建一个ProductController.cs用控制器的方式定义接口。之所以用控制器而不是最小API是因为控制器的路由规则更直观[Route(api/[controller])]这种写法前端一看就知道请求地址是/api/product。using Microsoft.AspNetCore.Mvc; namespace ProductApi.Controllers; [ApiController] [Route(api/[controller])] public class ProductController : ControllerBase { [HttpGet] public IActionResult GetProducts() { var products new[] { new { id 1, name 机械键盘, price 399.0, stock 128 }, new { id 2, name 无线鼠标, price 129.0, stock 86 }, new { id 3, name 显示器支架, price 259.0, stock 46 } }; return Ok(new { code 0, data products, message success }); } }这里新手最容易犯的错误是返回一个裸的List对象上去比如return Ok(products)。这样前端也能收到数据但后期你一旦碰到“接口报错”“需要分页”“需要带上操作结果提示”这些情况就会发现返回结构根本没有地方扩展。返回结构不稳定前后端就要反复改代码。2.2 统一返回结构为什么建议包一层code/message/data我习惯所有业务接口统一返回这样的JSON结构{ code: 0, data: [...], message: success }三个字段各管一件事code业务状态码0表示成功非0表示某种业务失败。注意HTTP状态码和业务码是两回事HTTP 200不代表业务一定成功比如“登录失效”这种业务状态很多团队照样返回HTTP 200用code来区分。message给前端提示用的文字成功是“success”失败是具体的错误说明。data真正的业务数据可以是数组、对象、分页结构随便你定。这个约定一旦定下来前端请求模块就可以写得很统一先判断code再决定是渲染data还是显示message不需要为每个接口单独写一遍错误处理。2.3 如果后端是Spring Boot写法差在哪很多人的后端其实是Spring Boot这里简单对照一下。Spring Boot的Controller几乎是一一对应的RestController RequestMapping(/api/product) public class ProductController { GetMapping public MapString, Object getProducts() { MapString, Object result new HashMap(); result.put(code, 0); result.put(message, success); result.put(data, List.of( Map.of(id, 1, name, 机械键盘, price, 399.0, stock, 128), Map.of(id, 2, name, 无线鼠标, price, 129.0, stock, 86) )); return result; } }你会发现前后端分离的项目里后端是什么语言其实没那么重要重要的是接口地址和返回结构。只要接口能返回固定的JSON格式前端一概不关心后端是C#、Java还是Python。这一点想通了你就不会被具体的后端技术栈绑住。3. 联调的命门接口地址怎么填、跨域问题和代理配置3.1 直接写后端地址不行吗先理解同源策略前后端第一次联调时新手最自然的一个想法是前端直接请求http://localhost:5289/api/product不就行了但你会发现浏览器控制台报错错误信息里一定有CORS这个关键词。这就是浏览器的同源策略在起作用。所谓“同源”指协议、域名、端口三者完全一致。前端跑在http://localhost:5173后端跑在http://localhost:5289端口不一样就是跨域浏览器默认会拦截前端发起的跨域XHR请求。拦截发生在浏览器这一层而不是后端没收到请求——实际上后端可能已经处理了只是浏览器不把响应交给你的页面代码。解决跨域常见两条路一是后端开启CORS二是前端用代理。开发阶段我强烈推荐代理方案原因后面说。如果你非要在开发时直接跨域访问后端需要在ASP.NET Core里加上CORS策略builder.Services.AddCors(options { options.AddPolicy(AllowVue, policy { policy.WithOrigins(http://localhost:5173) .AllowAnyHeader() .AllowAnyMethod(); }); }); app.UseCors(AllowVue);这样配置完直接请求也能通。但问题在于开发时是一套地址上线后前端的域名又变了到时候还得改后端代码很麻烦。所以更推荐下面这种代理方案因为它让前端在开发环境和生产环境用同一个相对地址完全不用关心后端在哪。3.2 用Vite devServer代理解决开发环境跨域在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:5289, changeOrigin: true } } } })配置的含义是前端页面发起的所有以/api开头的请求Vite开发服务器会替我们把请求转发到http://localhost:5289。也就是说前端代码里写axios.get(/api/product)浏览器看到的请求地址还是http://localhost:5173/api/product同源不触发跨域而Vite在背后已经把请求转给了后端的http://localhost:5289/api/product。这里有一个非常重要的点你必须记住代理只在开发环境npm run dev生效打包之后完全不生效。很多项目开发时一切正常部署上线后接口全部404就是因为把希望寄托在了代理上。生产环境的接口转发要么靠Nginx等反向代理要么就拼完整后端地址这个我在第5节详细讲。3.3 接口地址放哪环境变量与封装请求模块代理配好之后接口地址还有最后一个问题代码里到处写/api/product以后接口域名一变就要全局搜索替换。所以一般会做两件事。第一用环境变量管理接口基础地址。项目根目录新建.env.developmentVITE_API_BASE/api生产环境的.env.production就写VITE_API_BASE/api如果生产环境确实要跨域访问后端也可以写成VITE_API_BASEhttps://api.example.com。前端代码里用import.meta.env.VITE_API_BASE读取这样切换环境只需要改配置文件不用动业务代码。第二把axios实例统一封装。新建src/api/request.jsimport axios from axios const request axios.create({ baseURL: import.meta.env.VITE_API_BASE || /api, timeout: 10000 }) request.interceptors.response.use( (response) { if (response.data.code ! 0) { return Promise.reject(new Error(response.data.message || 业务处理失败)) } return response.data }, (error) { return Promise.reject(error) } ) export default request这段代码的核心价值在于所有接口的响应都先经过统一的拦截器把code非0的情况直接在拦截器里拦下来页面组件里就完全不需要重复判断业务状态了。这也是为什么第2节强调后端要统一返回结构——前端封装越干净后端返回越规范两者是互相成就的。4. 页面上的三件套数据加载、渲染列表和错误兜底4.1 用axios发请求挂到onMounted里先把axios装上npm install axios然后新建src/components/ProductList.vue这个组件就干一件事进入页面时请求商品列表接口把数据渲染成表格。在Vue 3的组合式API里请求的发起时机通常是onMounted。onMounted是组件挂载完成后执行的生命周期钩子这个阶段DOM已经准备好适合做数据初始化。也可以用await配合onMounted异步调用但要注意异步函数的错误处理否则接口报错会静默失败连控制台都没有像样的提示。script setup import { ref, onMounted } from vue import request from ../api/request const products ref([]) const loading ref(true) const error ref() async function fetchProducts() { loading.value true error.value try { const res await request.get(/product) products.value res.data } catch (e) { error.value e.message || 请求失败请稍后重试 } finally { loading.value false } } onMounted(fetchProducts) /script这里要注意一个细节我封装的axios实例的baseURL是/api所以请求方法里写的是request.get(/product)拼出来就是/api/product。如果你直接用原生axios没有封装这层那就得写全/api/product两种方式都可以但别混着写否则排查地址时会把自己绕晕。还有一点response.data.data这段很容易懵。由于我在拦截器里return response.data所以拦截器返回的已经是后端响应体的主体也就是{ code, data, message }这个对象。再取.data才是真正的商品数组。如果你没写拦截器这里就要写response.data.data.data三层data经典的前后端联调劝退点。4.2 v-for渲染表格key别用index拿到数据之后页面上用v-for循环渲染。我建议用v-if、v-else-if、v-else把“加载中”“加载失败”“正常显示”三种状态分开不要一上来就只写成功的那一种。template div classproduct-page h1商品列表/h1 p v-ifloading数据加载中请稍候.../p p v-else-iferror classerror-text加载失败{{ error }}/p table v-else classproduct-table thead tr thID/th th商品名称/th th价格/th th库存/th /tr /thead tbody tr v-foritem in products :keyitem.id td{{ item.id }}/td td{{ item.name }}/td td¥{{ item.price }}/td td{{ item.stock }}/td /tr /tbody /table /div /template写v-for时key的作用是帮助Vue识别每个节点的身份从而更精准地复用和更新DOM。新手常见的写法是keyindex也就是用循环下标当key。这个写法在“列表不增删、不排序”的静态场景下没问题但一旦数据发生增删或排序用index做key会导致列表项状态错位比如某个输入框里的内容跑到另一行去了。所以只要业务数据里有唯一字段比如id就老老实实用item.id当key。4.3 loading和error状态用户体验不能省很多新手联调成功后就觉得万事大吉但真实项目里接口不可能永远秒回、永远成功。你至少要想清楚三件事。第一loading状态。接口慢的时候用户看着空白页面会以为页面坏了。最简单的做法是显示“数据加载中”进阶做法是加骨架屏组件。这个例子里我用了一行文字但你理解了思路之后换成el-skeleton或者其他组件库的骨架屏就是分分钟的事。第二错误状态。接口挂了不能只靠浏览器控制台提示页面上必须给用户一个可读的提示。我的代码里把接口的错误信息存到error变量里页面显示“加载失败xxx”用户至少知道发生了什么。第三重试机制。这个例子没做但真实项目里“加载失败”页面配一个“重新加载”按钮是非常常见的交互。做法也很简单让按钮点击事件重新调用fetchProducts就行。你可以自己试一下相当于一次很好的状态管理练习。最后一步把组件挂到根组件里。打开src/App.vue把默认内容清掉引入ProductListscript setup import ProductList from ./components/ProductList.vue /script template main ProductList / /main /template保存后浏览器如果还开着http://localhost:5173应该能看到完整的商品表格。这个页面不用刷新就自动更新就是Vite热更新的效果也是现代前端开发体验里最舒服的一环。5. 从开发到上线打包后布局异常、接口404这些坑逐个拆5.1 npm run build后白屏多半是base路径问题开发环境跑通只是第一步项目最终要npm run build打包成静态文件扔到服务器上。执行npm run build打包完成后会生成dist目录里面是编译压缩后的HTML、JS、CSS。很多人高高兴兴把dist扔到服务器上一访问发现白屏控制台报错全是Failed to load resource感觉天塌了。这个问题的常见原因之一是Vite默认的base路径是/也就是打包出来的HTML里引用的资源是/assets/index-xxx.js。如果你的站点部署在域名根路径那没问题但如果你是把前端部署在某个子路径下比如http://example.com/web/那浏览器去找/assets/index-xxx.js就会404因为资源实际在/web/assets/下。解决办法是修改vite.config.js把base改成相对路径export default defineConfig({ base: ./, plugins: [vue()] })这样打包出来的资源引用就变成./assets/index-xxx.js相对路径的方式让前端静态文件无论部署在哪个子目录都能正确找到资源。这是解决“打包后布局异常”系列问题里最高频的一条建议直接记到笔记里。5.2 部署后接口404把/api交给反向代理第二个高频问题就是接口404。开发时Vite代理帮你转发打包后代理没了前端代码里request.get(/product)变成GET http://example.com/api/product如果你这个域名下根本没有后端服务自然404。生产环境的正确做法是让用户访问的域名同时承担静态资源服务和接口转发。用Nginx举例典型的配置是这样server { listen 80; server_name example.com; # 前端静态资源 location / { root /var/www/vue-webapi-demo/dist; try_files $uri $uri/ /index.html; } # 接口转发到后端WebApi location /api/ { proxy_pass http://localhost:5289; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }关键点在location /api/这段所有以/api开头的请求都转发到后端的http://localhost:5289而其他请求一律按静态资源处理。这样前端代码不用改开发环境和生产环境用的是同一个/api相对地址只是负责转发的东西从Vite代理变成了Nginx。这里要特别提醒如果你想在生产环境直接跨域访问后端API那Nginx这套方案就不适用你必须回到后端CORS方案把VITE_API_BASE改成后端完整地址同时前端和后端都要处理跨域。两条路都可行但混用会非常混乱我个人建议哪怕是上线也尽量通过Nginx把前后端整合到同一个域名下省掉跨域的各种破事。5.3 路由模式、接口超时和中文乱码的补充打包部署这块还有几个小坑值得一起说了。如果你在脚手架阶段选了Vue Router并且创建项目时用了默认的history模式那么部署后刷新页面可能遇到404。因为history模式的路由是纯前端在“演”路径比如/product/1服务器上并没有这个真实文件刷新时就会去服务器找这个路径找不到就404。解决方法有两种一是Nginx加try_files $uri $uri/ /index.html;让所有路径都回退到前端入口二是干脆用hash模式URL里带#看起来没那么优雅但永远不会404。入门阶段我建议先用hash模式跑通理解路由机制后再切history。接口超时问题也值得提一句。我在封装axios时设了timeout: 1000010秒没响应就自动断开。这个数字不是随便定的太短容易误伤慢接口太长用户要等很久。你可以根据你后端接口的实际耗时来调但一定要有超时保护不然接口挂死时前端会一直转圈。中文乱码的问题主要出现在后端响应头没设置UTF-8。ASP.NET Core的AddControllers一般默认UTF-8但如果你遇到页面上中文全部是???先去检查后端响应的Content-Type是不是application/json; charsetutf-8再检查数据库字符集别一上来就怪前端。下面把这节提到的高频问题汇总成一张表方便排查时对照现象主要原因解决办法打包后白屏控制台资源404Vite base路径默认/部署在子目录vite.config.js里base: ./开发正常部署后接口404开发代理不生效生产没有对应转发Nginx配置location /api/反向代理History模式刷新404服务器没有兜底到index.htmlNginx加try_files或改用hash模式接口中文乱码后端响应头或数据库字符集问题检查charsetutf-8统一UTF-8接口长时间不返回没有超时设置axios实例配置timeout6. 联调时的调试方法和小技巧6.1 用Vue DevTools看组件状态和请求排查问题时浏览器F12的Network面板看请求是否发出、状态码是多少、响应体长什么样这是最直接的。另外强烈建议装上Vue官方调试工具Vue DevTools扩展它可以在开发者工具里直观地看到每个组件的ref、computed、props当前值。比如你页面一直显示“数据加载中”但在Vue DevTools里能看到loading: false说明数据其实拿到了只是渲染条件写错了如果看到products: []说明接口返回的字段名和页面取的不一致。这种定位方式比在代码里到处console.log高效太多。6.2 前后端联调的顺序建议我自己的经验是不要把前端全部写完再联调也不要等后端全部写完再动工。正确节奏是先把接口返回结构定下来两边各自按这个约定开发前端拿到数据后先用固定的Mock数据把页面渲染调通后端接口一好前端只改一个baseURL或者环境变量就能切到真实接口。这个例子里其实已经体验到了这个思路后端返回{ code, data, message }前端封装了拦截器只要这个结构不变后端换语言、换框架、换实现前端代码都纹丝不动。接口约定就是前后端之间的“合同”把合同定清楚联调的时候能少吵很多架。6.3 这个例子接着可以怎么扩展这篇做的是最简单的“请求一次渲染一次”但它可以延伸出不少进阶练手方向把列表改成带搜索和分页的接口加上page和keyword参数把axios拦截器补上携带token的逻辑体验一下登录态传递引入Pinia把列表数据缓存成全局状态避免反复切换页面时重复请求再加一个vite-plugin-mock之类的插件在没有后端时本地模拟接口开发不再等后端。我自己带新人时通常要求他们把这个例子完整重写三遍第一遍照着抄熟悉流程第二遍不看文章自己从头写卡住的地方就是理解薄弱点第三遍在例子的基础上加一个“删除商品”的按钮调用后端的DELETE接口体验接口联动的闭环。三遍下来前后端分离的基本功就算真练扎实了。最后说一个我踩过多次的坑当你发现前端报错但怎么都查不到原因时先别急着怀疑框架把浏览器Network面板里那条请求的完整响应体打开看一遍。绝大多数数据没显示出来的问题不是请求没发出去就是返回的字段结构和前端预期不一致跟Vue本身一点关系都没有。排查链路清晰了这类问题基本十分钟内都能定位。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑