news 2026/10/15 9:27:40

axios传参最佳实践:彻底搞懂get/post中的params与data使用与踩坑

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
axios传参最佳实践:彻底搞懂get/post中的params与data使用与踩坑

在日常前端开发中,axios 几乎是每个项目里都离不开的请求库。但就是这么个天天用的东西,我见过不少开发者(包括当年的我自己)在 get 和 post 的参数传递上栽过跟头——明明接口文档写得很清楚,代码照着写了,结果后端就是收不到参数,或者收到一堆[object Object]。最头疼的是,这类问题经常不是报错,而是静默地返回错误数据,排查起来非常耗时间。

这篇文章不打算讲 axios 的源码,也不聊拦截器的花活,就聚焦在最基础也最容易被忽略的一个点上:get/post 请求中params和data到底怎么用、什么时候用、用错了会怎样。我会把原理、写法、踩坑场景一次讲透,适合刚接触 axios 的新人,也适合写了好几年业务代码但没认真抠过细节的朋友。看完这篇文章,你再遇到接口传参问题,基本能一眼定位原因。

1. 先搞清楚 axios 的请求配置结构

axios 的 API 设计其实很简洁,核心就是一个config对象。你调用axios.get(url, config)或者axios.post(url, data, config),最终都会被统一到axios.request(config)这个入口上。理解这一点非常重要,因为很多同学只记“get 用 params、post 用 data”这个口诀,却不知道这两个字段在 axios 内部到底经历了什么。

1.1params和data的本质区别

params是URL 参数,它会附加在请求地址的?后面,比如axios.get('/api/user', { params: { id: 1 } })最终发出的请求是GET /api/user?id=1。

data是请求体(request body),它承载的是请求内容,比如axios.post('/api/user', { name: '张三' })最终发出的请求是POST /api/user,请求体里带着{"name":"张三"}。

用一个生活化的类比来解释:URL 就像是快递单上的收件地址,params是写在地址栏旁边的备注信息(比如“放门口快递柜”),而data是快递包裹里的实际物品。寄东西(post)当然要把物品放包裹里,不可能写在快递单上让快递员背下来。

注意:不是“post 不能用 params”,而是 post 的主要负载在 data 里。实际开发中,post 请求完全可以在 URL 上带查询参数(比如分页信息、版本号),同时在请求体里传业务数据,两者互不冲突。

1.2 为什么 get 没有data而 post 有params

这是 HTTP 协议本身的语义限制。GET 请求的语义是“获取资源”,按照 RFC 规范,GET 的请求体在语义上是没有意义的——虽然浏览器和服务器技术上允许 GET 带 body,但在实践中,绝大多数服务器框架和中间件根本不会解析 GET 请求的 body。而 POST 的语义是“提交数据”,数据放在 body 里是天然合理的。

所以,axios 的get方法签名是axios.get(url, config),config 里放params;而post方法签名是axios.post(url, data, config),第一个参数位就是 data。如果你在axios.get的 config 里写data,axios 确实会把它放在请求体里发送,但后端大概率读不到——这是第一类最常见的坑。

1.3 axios 内部是如何处理这两个字段的

axios 在发送请求前会做这么几件事:

  • 遍历config,把params序列化成查询字符串拼到 URL 上;
  • 把data根据Content-Type进行序列化,默认是 JSON 格式;
  • 如果data是普通对象且没有手动设置Content-Type,axios 会默认设置成application/json;charset=utf-8,并用JSON.stringify处理。

这里有一个关键细节:axios 的params序列化用的是paramsSerializer,默认情况下数组会变成key[]=value1&key[]=value2这种形式,而data序列化则是根据 Content-Type 决定。很多人以为数组传参是后端的问题,实际上前端完全可以控制序列化格式,后面我会专门讲。

2. get 请求 params 传参的完整拆解

get 请求的 params 传参,写起来非常简单,但越是简单的东西越容易含糊。我用几个实际场景带你走一遍。

2.1 基础写法:对象方式

最常规的写法是直接把参数对象传给params:

axios.get('/api/articles', { params: { page: 1, pageSize: 20, keyword: 'axios' } })

这条请求最终会发送到/api/articles?page=1&pageSize=20&keyword=axios。注意,axios 会自动对中文、特殊字符做 URL 编码,所以keyword: 'axios'或者keyword: '请求库'都不用担心乱码,axios 会用encodeURIComponent处理。

为什么推荐传对象而不是手动拼字符串?因为手动拼接需要你自己处理编码、空值、顺序等问题。尤其是关键词里如果带了&或=这类字符,手动拼接轻则导致参数错乱,重则直接破坏 URL 结构。传对象交给 axios 处理,既安全又省事。

2.2 URL 上已经带了查询参数怎么办

有时候接口地址本身就带参数,比如/api/articles?sort=latest,你又需要追加page参数:

// 这种写法的结果是:/api/articles?sort=latest&page=1 axios.get('/api/articles?sort=latest', { params: { page: 1 } })

axios 会把已有的查询字符串和params合并,不会把原来的sort覆盖掉。但我个人的建议是:能不放 URL 上就别放。把查询参数统一放在params里,代码可读性更高,后续维护时接口地址一目了然。如果你从后端拿到的接口地址已经带参数,建议找后端确认能不能拆开。

2.3 数组参数的传递与序列化陷阱

这是一个非常高频的坑。假设接口需要一个标签数组tags: ['javascript', 'axios'],很多人会这么写:

axios.get('/api/articles', { params: { tags: ['javascript', 'axios'] } })

axios 默认的序列化结果是tags[]=javascript&tags[]=axios,注意,数组名后面多了一对中括号[]。后端如果是 Spring 系(Java)的接口,用@RequestParam List<String> tags接,大概率能绑上;但如果是用 PHP 或其他语言写的接口,后端可能会拿到一个叫tags[]的参数而不是tags,导致数据为空。

解决方案有几种:

  • 让后端改接口,接受tags[]这种格式(不太推荐,耦合太重);
  • 前端用paramsSerializer自定义序列化规则,把数组序列化成tags=javascript&tags=axios(多个同名参数),这是兼容性最好的方式;
  • 直接用qs库的stringify方法指定arrayFormat: 'repeat'。

我自己在项目中更倾向于用 qs 库统一配置。下面是一个实际可用的配置:

import qs from 'qs' axios.defaults.paramsSerializer = function (params) { return qs.stringify(params, { arrayFormat: 'repeat' }) }

这样tags: ['a', 'b']就会变成tags=a&tags=b,绝大多数后端都能正确解析。建议在项目初期就把 paramsSerializer 配置好,不要等出了问题再补救——因为改序列化规则是全局性的,项目大了之后影响面会很大。

2.4 嵌套对象的传参方式

如果接口要求的查询参数是嵌套结构,比如filter[category]=tech&filter[status]=published,你需要这么写:

axios.get('/api/articles', { params: { filter: { category: 'tech', status: 'published' } } })

默认情况下,axios 会把嵌套对象序列化成filter[category]=tech&filter[status]=published。这种格式对很多后端框架来说并不友好,尤其是一些简单的服务端脚本,它们期望的是扁平化的参数名。如果你遇到这种问题,同样可以用 qs 库的arrayFormat: 'indices'或allowDots: true来调整序列化格式。不同后端框架对参数格式的容忍度差异很大,这种情况下,接口联调时最好和后端确认清楚到底要哪种格式。

2.5 空值和 null 的处理

这也是个容易让人困惑的点。当你给params传了null或undefined的值时,axios 的行为是:默认会忽略null和undefined,不把它们拼到 URL 上。

axios.get('/api/articles', { params: { page: 1, keyword: undefined, // 被忽略 category: null // 被忽略 } }) // 实际请求:/api/articles?page=1

这个特性有两个含义:一方面,它方便你在条件查询时直接传对象而不用手动删掉空字段;另一方面,如果你确实需要向后端传递一个值为空字符串的筛选条件(比如category=),要传''而不是null。

// 如果接口需要 category 字段,但允许为空字符串 axios.get('/api/articles', { params: { category: '' // 结果:/api/articles?category= } })

3. post 请求 data 传参的完整拆解

post 的data传参比 get 复杂,核心在于Content-Type的选择。不同的 Content-Type 对应不同的序列化方式,后端接收的方式也完全不同。这是整个 axios 传参体系中最容易出错、也最需要理解原理的部分。

3.1 默认的 JSON 格式

axios 默认情况下,如果你传一个普通 JavaScript 对象作为data,Content-Type 会自动设置为application/json,请求体是JSON.stringify后的字符串:

axios.post('/api/login', { username: 'admin', password: '123456' }) // Headers 里自动带上 Content-Type: application/json // 请求体:{"username":"admin","password":"123456"}

绝大多数现代后端框架(Spring Boot、Express、Django、Flask 等)都能直接解析 JSON 格式。这也是最推荐的传参方式,因为数据结构清晰,支持嵌套对象、数组,后端用@RequestBody就能接到一个完整的对象。

实际开发中遇到的坑:如果你把 data 直接写成JSON.stringify({...})字符串,同时没有手动设置Content-Type: application/json,axios 无法识别这是一个 JSON 字符串,会把 Content-Type 设置成text/plain或者application/x-www-form-urlencoded(取决于浏览器环境),后端一接收就发现是字符串,解析失败。所以尽量直接传对象,让 axios 替你处理序列化。

3.2 表单格式 x-www-form-urlencoded

如果后端接口是传统的表单接收方式(比如用$_POST或@RequestParam接收),你需要把data设置成URLSearchParams或者用qs.stringify处理后的字符串,同时把 Content-Type 设置成application/x-www-form-urlencoded:

import qs from 'qs' axios.post('/api/login', qs.stringify({ username: 'admin', password: '123456' })) // 请求体:username=admin&password=123456

这种方式下,请求体是一串key=value&key=value格式的字符串。后端不需要额外注解,直接用表单参数就能收到。

为什么不用对象直接传?因为如果你直接传一个普通对象而不做处理,axios 默认会把它序列化成 JSON 格式,Content-Type 也是 JSON,后端用表单方式接不到数据。这里要特别提醒:不要以为传了 URLSearchParams 就万事大吉,同时还要确保请求头里的 Content-Type 是 application/x-www-form-urlencoded。如果你用第三方的请求拦截器统一改过 Content-Type,要留意它改的是什么值。

如果你不想引入 qs 库,也可以用浏览器原生提供的URLSearchParams:

const params = new URLSearchParams() params.append('username', 'admin') params.append('password', '123456') axios.post('/api/login', params)

URLSearchParams 是 Web API,序列化结果也是key=value&key=value格式。区别在于 URLSearchParams 不能很好地处理嵌套对象和数组,所以复杂表单依旧建议用 qs。

3.3 FormData 与文件上传

当data传一个FormData实例时,axios 会自动把 Content-Type 设置为multipart/form-data,并附带 boundary 边界字符串。这是文件上传的标准做法:

const formData = new FormData() formData.append('file', fileInput.files[0]) formData.append('type', 'avatar') axios.post('/api/upload', formData) // 浏览器自动设置 Content-Type: multipart/form-data; boundary=...

重要提示:使用FormData时,千万不要手动设置Content-Type: application/json,否则上传会失败或者文件数据到达后端时不可用。正确的做法是由浏览器自动生成带 boundary 的 Content-Type,你只需要保证不覆盖它即可。

很多同学在文件上传时习惯手动设置headers: { 'Content-Type': 'multipart/form-data' },这其实是个隐患——缺少了 boundary,部分后端解析会出错。把 FormData 直接传给 axios,让它自动处理,比手动设置要可靠得多。

3.4 同时使用 params 和 data

post 请求完全可以同时带 URL 查询参数和请求体参数。这在业务上是很有用的,比如分页信息放在 URL 上,筛选条件放在请求体里:

axios.post('/api/articles', { keyword: 'axios', status: 'published' }, { params: { page: 1, pageSize: 20 } }) // 最终请求:POST /api/articles?page=1&pageSize=20 // 请求体:{"keyword":"axios","status":"published"}

这个写法非常实用,能有效区分“资源的定位信息”和“要提交的业务数据”。我在实际项目中经常用这种方式:查询类的 post 接口(有些后端喜欢用 post 做复杂查询),把分页参数放 params,把查询条件放 data,语义清晰,后端也容易处理。

3.5 data 传入字符串和原始字节的场景

如果data是字符串,axios 会原样发送,不做任何序列化。这在对接某些特殊接口时很有用,比如发送 JSON 字符串到网关、发送 XML 报文到第三方服务:

// 发送 JSON 字符串,手动指定 Content-Type axios.post('/api/gateway', '{"userId": 1001}', { headers: { 'Content-Type': 'application/json' } }) // 发送 XML 字符串 axios.post('/api/xml', '<xml><name>张三</name></xml>', { headers: { 'Content-Type': 'application/xml' } })

当你主动传字符串并指定了 Content-Type,axios 就不再干预序列化,body 就是那个字符串本身。

4. 高频踩坑场景与排查方法

理论和基础写法讲完了,下面专门列几个我在项目实战中遇到过的高频问题。这些坑几乎每个月都能在答疑群里看到一遍,整理成速查表,方便你直接对照排查。

4.1 参数传错位置的典型表现

错误现象可能原因排查方向
get 请求后端收不到任何参数把参数放在了data里检查代码里是params还是data,看 Network 面板请求 URL 尾部有没有?xx=xx
post 请求后端报“缺少必填字段”把业务参数写进了params,请求体为空看 Network 面板的 Request Payload,如果为空就说明 data 没传对
后端收到了参数但值是[object Object]把对象直接嵌套在表单字符串里检查是不是用了URLSearchParams传对象,改用 qs 序列化
文件上传后端收到空文件手动设置了 Content-Type 导致 boundary 丢失去掉手动设置,让 axios 自动处理 FormData

最快的排查方式永远是打开浏览器开发者工具的 Network 面板,点开请求查看实际发出的 URL 和 Request Payload。很多时候代码看十遍不如抓包一眼。我见过太多同学盯着代码发呆,其实请求早就发出去了,就是格式不对。

4.2 相同接口 get 和 post 的切换

一个很隐蔽的坑:有些项目里,同一个接口可能既支持 get 又支持 post(比如某些网关系统)。前后端联调时,前端把请求从 get 改成 post,但代码只改了方法名,没把params迁移到data:

// 改之前的 get 请求 axios.get('/api/search', { params: { keyword: 'test' } }) // 改成 post,但参数还在 params 里 axios.post('/api/search', {}, { params: { keyword: 'test' } // 后端用 body 接收的话,这里就丢了 })

如果后端在 post 版本里改用@RequestBody接收,那这个请求的参数就全在 URL 上,body 是空的,结果自然不对。遇到接口切换时,一定要把参数从params挪到data,除非后端明确说查询参数继续走 URL。

4.3 拦截器里动了 Content-Type

很多项目会在 axios 拦截器里统一处理参数和 Content-Type。比如下面的写法:

axios.interceptors.request.use(config => { config.data = qs.stringify(config.data) config.headers['Content-Type'] = 'application/x-www-form-urlencoded' return config })

这个拦截器如果一视同仁地处理所有请求,那问题就来了:get 请求没有config.data,qs.stringify 处理 undefined 不会有问题,但 Content-Type 被改成表单格式后,不会影响 get 请求(get 没有 body),问题不大;但如果你用 FormData 做文件上传,这个拦截器会把 FormData 用 qs.stringify 转成一个空对象字符串,文件直接丢了。

解决方案是拦截器里做条件判断:

axios.interceptors.request.use(config => { if (config.method === 'post' && config.data instanceof FormData) { // FormData 不需要处理,直接放行 return config } if (config.method === 'post' && config.data && !(config.data instanceof URLSearchParams)) { config.data = qs.stringify(config.data) config.headers['Content-Type'] = 'application/x-www-form-urlencoded' } return config })

这个细节我在生产环境里踩过不止一次,每次都是花费大量时间排查才定位到拦截器身上。写全局拦截器之前,先想清楚它会覆盖哪些请求类型。

4.4 params 被序列化后出现 null、undefined 的排查

有些后端要求查询参数即使为空也要带上key=,但 axios 默认会丢弃null和undefined。如果你的接口文档里明确写了“传空字符串而非 null”,就用'';如果后端允许 null,可以自定义paramsSerializer让 null 变成字符串'null'(不太推荐,建议和后端统一约定)。

排查这类问题,最简单的办法是在paramsSerializer里临时打日志,把序列化前后的参数打出来,一眼就能看出什么问题。这类问题大概率不是参数写错了,而是 axios 的序列化策略与后端解析策略不匹配。

4.5 URL 编码导致的鉴权失败

如果 params 里有 token、签名等特殊字符(比如+、/、=),axios 默认会用encodeURIComponent处理,+会被编码成%2B,这通常是正确行为。但如果后端在验签时用的是原始字符串,没有做 URL decode,就会导致签名不匹配。

这种问题的坑不在于 axios,而在于前后端对“编码后的字符串拿去验签”和“原始字符串拿去验签”的认知不一致。排查方式:先在 Network 面板看实际 URL,把 URL 上那串参数复制出来对比签名字段是否一致。如果确认是编码问题,前端可以先把参数序列化成字符串,然后用decodeURIComponent再包一层,但这种做法非常罕见,多数情况下问题是出在后端验签逻辑上。

5. 工程化实践:统一封装才是最终出路

前面讲了这么多 params 和 data 的细节,如果你只是在一个项目里零散地调用 axios,那每次都要小心翼翼。在真实项目中,我强烈建议做一层统一的请求封装,把这些细节在封装层全部处理掉,业务代码里只关心“传什么参数”而不是“用什么格式”。

5.1 一个实用的请求封装设计

我在某个管理后台项目里的封装思路如下(简化版):

import axios from 'axios' import qs from 'qs' const service = axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL, timeout: 15000 }) // 统一处理 params 序列化 service.defaults.paramsSerializer = function (params) { return qs.stringify(params, { arrayFormat: 'repeat' }) } // 请求拦截器:统一注入 token,统一处理 form 格式 service.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers['Authorization'] = `Bearer ${token}` } // 如果请求方法为 post,默认使用 form-urlencoded 格式 if (config.method === 'post') { if (config.data instanceof FormData) { // FormData 原样放行 return config } if (typeof config.data === 'object') { config.data = qs.stringify(config.data) config.headers['Content-Type'] = 'application/x-www-form-urlencoded' } } return config }, error => { return Promise.reject(error) }) // 响应拦截器:统一处理业务错误码 service.interceptors.response.use(response => { const res = response.data if (res.code !== 0) { // 统一弹错误提示 return Promise.reject(new Error(res.message || '请求失败')) } return res.data }, error => { // 统一处理 HTTP 错误状态码 return Promise.reject(error) }) export default service

这个封装有几个关键点:

  • params 序列化用arrayFormat: 'repeat',解决了和后端数组参数不兼容的问题;
  • post 请求默认用表单格式,因为当时后端接口基本都是表单接收;如果某些接口要传 JSON,单独在调用时覆盖Content-Type和data格式即可;
  • FormData 单独判断,不去干扰文件上传。

这里的核心思路是:把团队里约定俗成的规矩固化成代码,避免每个开发都凭感觉传参。你要注意,这些默认值是和后端团队讨论后的决定,并非放之四海皆准。

5.2 调用层的最佳实践

封装的最终效果是让业务代码变得非常简洁:

// 查询列表 - get service.get('/api/articles', { params: { page: 1, pageSize: 20, tags: ['a', 'b'] } }) // 创建文章 - post service.post('/api/articles', { title: '标题', content: '内容', tags: ['a'] })

你看,业务代码根本不需要关心 Content-Type、不需要手动序列化,只要记住“get 用 params、post 用 data”这条规则就足够了。这也是封装的价值所在:把复杂性和规则约束下沉到基础设施层,业务开发只需要遵循最简单的心智模型。

5.3 什么情况下不建议用统一封装

有一类场景不建议套用上面的封装:对接第三方 API 时。第三方接口的格式通常五花八门,有的要 JSON、有的要 XML、有的要签名后的原始字符串。这种场景更适合直接使用 axios 实例单独创建,或者针对每个接口单独配置 headers 和 data 格式,不要走全局的拦截器默认逻辑。

5.4 请求取消与重复请求控制

虽然不在 params/data 的核心范围内,但既然聊到工程化封装,还是要提一下请求取消。如果你的页面里有搜索联动的场景(用户每输入一个字就触发一次请求),快速输入时会出现请求竞态——后发的请求先返回,先发的请求后返回,最终页面数据被旧请求覆盖。axios 提供了AbortController的方式来取消请求:

const controller = new AbortController() axios.get('/api/search', { params: { keyword: 'axios' }, signal: controller.signal }) // 下一次搜索前取消上一次 controller.abort()

在封装层可以这样处理:每次发起新请求前,先取消上一次未完成的类似请求。这个细节能避免大量诡异的 bug。

6. 最后再分享一个调试小技巧

整个 axios 环境里,最有用的调试手段就是配置axios.defaults.validateStatus或者在全局 response 拦截器里打日志。我曾经加过一行代码,把每个请求的 method、url、params、data 都打印到控制台,调试效率瞬间提升一个档次:

service.interceptors.request.use(config => { console.group(`请求:${config.method.toUpperCase()} ${config.url}`) console.log('params:', config.params) console.log('data:', config.data) console.groupEnd() return config })

经验之谈,当后端跟你说“我这边一个参数都没收到”的时候,不要急着争执,先打开控制台看这行日志确认前端实际发了什么,再截个 Network 面板的图发给后端。整个排查链路会变得非常清晰。

回到标题本身,“get/post 请求 params/data 传参总结”看起来是个入门级话题,但实际上牵扯到 HTTP 协议语义、序列化规则、后端框架差异、工程化封装设计等多个层面的知识。我写这篇文章的目的,就是希望你在实际开发中不要被困在这些表面上简单、实际却暗藏陷阱的问题上。把原理吃透,把封装做好,剩下的业务代码自然就清爽了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 9:27:12

从零实现计算图:反向传播原理与迷你自动微分框架

1. 为什么需要计算图&#xff1a;从数值微分到反向传播的必然选择如果你手写过神经网络的反向传播代码&#xff0c;大概率经历过这样的场景&#xff1a;对着损失函数一层层求导&#xff0c;链式法则写到第三层就开始怀疑人生&#xff0c;维度对不上、转置搞反、梯度消失得莫名其…

作者头像 李华
网站建设 2026/10/15 9:26:27

Odoo 16 实施应用:从零搭建小型企业 ERP 的落地路径

简介&#xff1a;这份资源是面向Odoo16初学者与实施人员的入门指南&#xff0c;围绕开源智造理念下的GoodERP&#xff08;Odoo中国版&#xff09;展开&#xff0c;帮助读者理解从基础资料配置到复杂生产、部署优化的完整实施路径。压缩包内仅含1个PDF文件&#xff0c;大小约9.0…

作者头像 李华
网站建设 2026/10/15 9:26:25

K8s离线交付:flannel镜像包从拉取到导入的全流程避坑指南

简介&#xff1a;在离线或网络受限环境下安装Kubernetes集群时&#xff0c;flannel网络插件镜像的获取常成为阻塞项&#xff0c;特别是国内用户常因拉取超时或版本缺失而中断部署。压缩包内含flannel v0.21.5与flannel-cni-plugin v1.1.2两个tar归档&#xff0c;以及配套的kube…

作者头像 李华
网站建设 2026/10/15 9:25:58

SSM框架在智能餐厅管理系统的实战应用

1. 项目背景与核心价值每次走进餐厅看到服务员手写点单、前台堆积如山的纸质账单时&#xff0c;我都在想&#xff1a;这套流程真的不能更高效吗&#xff1f;三年前接手某连锁餐饮品牌数字化改造项目时&#xff0c;这套基于SSM框架的智能餐厅管理系统就是我的解决方案。它不仅将…

作者头像 李华
网站建设 2026/10/15 9:25:49

OpenStack部署实战:核心组件、网络规划与Kolla-Ansible自动化

1. 部署前的规划与架构设计1.1 节点划分与资源评估很多人一上来就急着安装&#xff0c;结果装到一半发现磁盘不够、内存爆掉&#xff0c;或者网络配置一塌糊涂。OpenStack部署真正考验人的不是命令本身&#xff0c;而是前期规划。它就像装修房子&#xff0c;先把水电布局想清楚…

作者头像 李华
网站建设 2026/10/15 9:25:42

Cocos Creator 工程 .gitignore 配置指南:避免仓库膨胀与协作陷阱

1. 从一次版本库膨胀事故说起团队里新来的同事把整个library和temp目录一股脑提交进了 Git&#xff0c;两周之后仓库体积从 80MB 涨到 1.2GB&#xff0c;git clone一次要等好几分钟&#xff0c;CI 拉代码的时间直接翻倍。这件事之后&#xff0c;我把 Cocos Creator 工程的忽略规…

作者头像 李华