☰
前端地基四件套:HTTP、浏览器、跨域与Git实战指南
2026/10/6 8:24:01 网站建设 项目流程

老板在旁边看着,接口联调突然报了一堆跨域错误,浏览器控制台红成一片,Git 提交又卡在 SSH 认证上——这种场景但凡写过前端的人都懂。第 11~15 天这段学习内容,正好是前端入门之后最容易卡壳的“地基”阶段:HTTP、浏览器、跨域、Git。这四块东西不是孤立的知识点,而是每天写代码都在打交道的基础设施。我见过不少同事业务代码写得很溜,一被问到“HTTP 连接复用是怎么工作的”“为什么接口报 403”“Git 分支冲突怎么处理”就支支吾吾,包括我自己早期也踩过不少坑。把这四样吃透,前端面试题里至少三分之一能稳稳拿下,日常开发效率也会明显上一个台阶。这篇总结是我把这 5 天的学习内容串起来之后,再结合自己实际项目里的排错经验整理出来的,适合正在系统学习前端、准备面试,或者已经开始写业务但基础还不牢的朋友参考。

1. HTTP 核心知识点实战拆解

1.1 先理清 HTTP 的定位:浏览器和服务器之间的“点餐流程”

HTTP(HyperText Transfer Protocol,超文本传输协议)是浏览器和服务器之间交流的共同语言。你不需要关心底层网络怎么传输,只要按照 HTTP 的格式把请求发出去,服务器照着同样格式处理并返回,浏览器再解析渲染成页面,整套流程就完成了。

用一个生活场景类比:去餐厅点餐。你举手叫服务员(发起请求),服务员记录你要什么菜(请求头带上你的意图),后厨做菜(服务器处理),服务员把菜端上来(响应返回),你吃菜(浏览器解析渲染)。HTTP 就是这套沟通规则:什么时候举手、菜单上怎么写、服务员怎么端菜、哪些菜要额外处理(状态码)等等。

这里有一个新手容易忽略的关键特性:HTTP 是无状态的。意思是,HTTP 协议本身不记得你上一次和服务器的交互,每个请求之间互相独立。那网站为什么能记住你登录没登录?靠的是 Cookie、Session、Token 这类额外机制。理解这一点,后面看跨域、看缓存、看鉴权都会顺畅很多。

1.2 报文结构:请求行、请求头、请求体一个都不能少

一个完整的 HTTP 请求包含三部分:请求行、请求头、请求体。响应也一样,包含状态行、响应头、响应体。拿一个最常见的 POST 请求举例,抓包看到的东西大致长这样:

POST /api/login HTTP/1.1 Host: example.com Content-Type: application/json User-Agent: Mozilla/5.0 Authorization: Bearer xxxxx {"username": "admin", "password": "123456"}

第一行POST /api/login HTTP/1.1是请求行,明确了方法、路径和协议版本。中间是各种请求头,每个头都有专门用途。空行之后是请求体,也就是真正提交给服务器的数据。

请求头里最值得花时间研究的是Content-Type,它告诉服务器“我这个请求体的数据是什么格式”。实际开发中三个值最常用:

  • application/json:JSON 字符串,前后端分离项目的主流格式。
  • application/x-www-form-urlencoded:表单编码格式,key=value&key2=value2,传统表单提交默认使用。
  • multipart/form-data:文件上传专用,能携带二进制数据。

我踩过最经典的坑就是:后端接口文档写的是接收 JSON,但我用 fetch 时传了Content-Type: application/x-www-form-urlencoded,结果后端框架(比如 Spring)在解析请求体时拿到的是空对象,排查了整整一个小时。所以联调时第一步永远先看请求头里的 Content-Type 和后端期望的是否一致。

1.3 请求方法与状态码:面试和排错都靠它们

请求方法里,前端日常接触最多的就是 GET 和 POST。但面试官喜欢追问的是“GET 和 POST 的区别”,很多人的回答停留在“GET 参数在 URL 上,POST 参数在 body 里,POST 更安全”,其实并不全面。

更严谨的说法是:GET 和 POST 的主要区别在于语义和幂等性。GET 用于获取资源,是幂等的(同一个请求执行多少次结果都一样),所以浏览器可以缓存它、预加载它;POST 用于提交数据,不是幂等的,重复提交可能会创建多条记录。另外实际工程里 GET 请求的参数确实会拼在 URL 上,而 URL 长度有限制(不同浏览器、服务器限制不同),所以不适合传大量数据;POST 的请求体体积限制要宽松得多。至于安全性,HTTPS 加密之后两者都一样安全,裸 HTTP 都不安全,这个认知面试时最好纠正过来。

状态码是排错的核心线索,前端最常用到的可以整理成一张速查表:

状态码含义前端排查方向
200请求成功正常
201创建成功通常用于 POST 写入接口
204无内容返回删除类接口常见
301 / 302永久重定向 / 临时重定向地址是否变更为新域名
304协商缓存未修改命中缓存,不是错误
400请求参数有误检查请求体格式、参数名、Content-Type
401未认证Token 缺失、过期,需要重新登录
403无权限登录了但没权限,或跨域拦截
404路径不存在检查接口地址拼写
500服务器内部错误交给后端看日志,前端先抓请求参数
502 / 503网关错误 / 服务不可用后端服务挂了或部署中,等一等再试

排错思路也很直接:400 和 401、403 这种多半是前端请求本身的问题,先自己检查;500 以上是后端的问题,但前端要提供完整请求信息给对方,包括 URL、请求头、请求体、当时的浏览器版本,而不是干巴巴甩一句“接口报错了”。

1.4 HTTP 连接复用:从每次新建连接到 keep-alive 和 HTTP/2

很多老工程师提到性能优化必聊连接复用,这是有道理的。最早期的 HTTP/1.0 时代,每发一个请求都要经历一次完整的 TCP 三次握手,请求多了性能和资源消耗都扛不住。

HTTP/1.1 引入了一个关键机制:keep-alive,也就是默认开启连接复用。同一个域名下,浏览器和服务器建立一次 TCP 连接后,可以连续发送多个请求,不用反复握手。这就是热词“HTTP 连接复用”的实际含义。你打开 Chrome DevTools 的 Network 面板,点到某个请求,在 Headers 里能看到Connection: keep-alive,说明这次请求复用了之前的连接。

到了 HTTP/2,更进一步:一个连接上可以同时并发多个请求,叫多路复用,彻底解决了 HTTP/1.1 时代“队头阻塞”的问题(前一个请求慢,后面请求只能排队等)。

对前端而言,理解连接复用的现实意义是:不要为了“减少请求数”而把大量请求强行合并在一个接口里,尤其在 HTTP/2 环境下,多个小请求的代价比想象中低;但要注意静态资源域名拆分在 HTTP/2 下已经意义不大了,反而可能因为失去连接复用而变慢。这个认知能帮你避免用过时的方案优化现在的项目。

1.5 前端发请求的三种方式:XHR、fetch 与 axios

搞明白协议层之后,再看代码层面。前端发请求基本就是三条路:原生 XMLHttpRequest(老古董但面试会问)、fetch(浏览器原生支持)、axios(基于 XHR 的封装库,目前最主流)。

三者的关系可以这样理解:XHR 是最底层的浏览器 API,能力有但用起来繁琐;fetch 是新一代原生 API,写法 Promise 化更优雅,直接在浏览器里就能用而不用引第三方库;axios 是在 XHR 之上的封装,自动处理 JSON、拦截器、取消请求、超时等,代码更顺手。

实际开发里我比较推荐 axios,但 fetch 也建议掌握,因为很多轻量场景根本不需要引一个库。使用 fetch 有几个坑是文档不会提醒你的:

  • 默认不带 Cookie,跨域请求要带 Cookie 必须手动加credentials: 'include'。
  • 不主动抛超时错误,需要自己通过 AbortController 控制。
  • 收到 HTTP 错误状态码(比如 404、500)时不会走 reject,要手动判断response.ok。

这些细节在面试中也很值钱,因为面试官问“你知道 fetch 和 axios 有什么区别吗”,想听到的就是这类实战差异,而不是“axios 支持拦截器”这种一句话答案。

2. 浏览器工作机制与前端面试高频考点

2.1 从输入 URL 到页面显示,这 6 步是必备题

“浏览器从输入 URL 到页面展示发生了什么”是前端面试题中的常青树。完整流程可以拆成六步:

  1. DNS 解析:把域名解析成 IP 地址。浏览器会依次查浏览器缓存、系统 hosts、本地 DNS 服务器,最后才去上级 DNS 查。
  2. 建立 TCP 连接:通过三次握手确认双方都能收发数据。
  3. 发送 HTTP 请求:把请求行、请求头、请求体发给服务器。
  4. 服务器处理并返回响应:后端处理完业务逻辑后返回 HTML、CSS、JS、数据等。
  5. 浏览器解析和渲染:解析 HTML 生成 DOM 树,解析 CSS 生成 CSSOM 树,合在一起生成渲染树,然后布局、绘制。
  6. 连接断开或复用:HTTP/1.1 之后默认 keep-alive,很多请求不会被立刻断开。

其中第三步和第六步最容易引申出深聊点:比如“HTTPS 和 HTTP 有什么区别”“为什么会有连接复用”“DNS 缓存失效怎么办”。我建议把这几步当成一条线反复多讲几遍,讲到自己能不看笔记复述出来的程度,面试基本就稳了。

2.2 渲染流程:DOM、CSSOM、渲染树、回流与重绘

渲染这一步展开讲,知识点密度非常高。浏览器拿到 HTML 之后,会先把标签解析成一棵 DOM 树;同时解析 CSS 生成 CSSOM 树;然后两棵树合并成渲染树(Render Tree),只包含可见元素;接着进入布局阶段,计算每个节点在视口内的位置和尺寸;最后才是绘制阶段,把像素画到屏幕上。

这里面前端性能优化最关注的是“回流”和“重绘”这对概念:

  • 回流(重排,Reflow):当元素的尺寸、位置发生变化,影响到了布局时,浏览器需要重新计算几何属性。比如修改元素的width、height、margin、padding、display等。
  • 重绘(Repaint):当元素的颜色、背景、边框阴影等不影响布局的属性变化时,只需要重新画一次,代价比回流小。

优化原则很简单:尽量避免频繁触发回流。几个非常实用的做法是:批量修改 DOM 样式(可以用classList一次性更换类名,而不是一条一条改 style);用transform代替top/left做动画,因为transform不触发布局,走的是合成层;对需要多次读取布局属性的操作,先读后写,避免浏览器反复计算。

另外还有脚本加载的阻塞问题。普通<script>标签会阻塞 HTML 解析,所以要么放在</body>前面,要么用defer或async属性。这两个属性面试也爱问,记住一句话区别:defer会按顺序在 DOM 解析完成后执行,async下载完就立刻执行、不保证顺序。

2.3 浏览器缓存:强缓存、协商缓存,以及“版本号强制刷新”

浏览器缓存是前端头等大事。后端明明更新了接口数据,用户却还看到旧页面,或者改了 CSS 文件名,老用户刷新还是旧样式——这些都是缓存策略没配对导致的。

浏览器缓存分两类:强缓存和协商缓存。

强缓存的字段是Cache-Control(比如Cache-Control: max-age=3600,表示 1 小时内直接命本地缓存,不发请求)和老的Expires。命中强缓存的表现是,Network 面板里请求显示(from memory cache)或(from disk cache),状态码直接 200。

协商缓存的字段是ETag(文件内容哈希)和Last-Modified(最后修改时间)。浏览器发现强缓存过期后,会带着If-None-Match或If-Modified-Since去找服务器确认,服务器对比后发现没变化,返回 304,浏览器继续用本地缓存。

前端最常见的操作是“通过版本号的变更,让前端强制刷新页面”。实际做法有三层:

  1. 静态文件改名,文件名带上内容 hash,比如app.a3f2d1.js。内容变了 hash 就变,浏览器请求新路径,天然强制刷新,这是构建工具(Webpack、Vite)默认支持的方案。
  2. 在入口 HTML 里给静态资源手动加查询参数,比如app.js?v=1.0.1。改版本号后浏览器认为这是新资源会重新拉取。这个适合没有 hash 打包的简单项目。
  3. 后端配置Cache-Control: no-cache给 HTML 入口文件,这样每次都会回源校验,而 hash 命名的资源可以放心设置max-age一年,达到“入口实时更新、资源强缓存”的经典组合。

2.4 浏览器调试利器:DevTools 与内置浏览器

负责前端的浏览器调试,九成时间是在 Chrome DevTools 里度过的。建议优先熟悉的四个面板:

  • Elements:看 DOM 和样式,临时改样式排查布局问题。
  • Console:看日志和报错,也是 JS 运行时最直接的反馈出口。
  • Network:看所有网络请求、状态码、资源加载时间,跨域报错也在这里看响应头。
  • Application:看存储,包括 LocalStorage、SessionStorage、Cookie、IndexedDB,还有 Service Worker 和缓存。

如果用的是 HBuilderX、VS Code 这类带内置浏览器的工具,内置浏览器 debug 也可以做快速验证,但真正到发布前的兼容性检查,还是得回到 Chrome 和真实设备上。尤其是移动端,谷歌浏览器提供“远程调试”能力,手机开 USB 调试连电脑,在chrome://inspect里就能看到手机页面的 Console 和 Network,排查线上问题非常高效。

一个我常用的快速验证缓存的小技巧:在 Network 面板勾选Disable cache,再配合右键请求选Clear browser cache,可以快速还原“用户第一次打开页面”的状态,用来验证首屏加载性能,比手动清缓存快得多。

3. 跨域问题全解与实用方案

3.1 同源策略和跨域报错一眼看懂

跨域(CORS,跨域资源共享)是前后端分离开发中绕不开的坎。先明确同源的定义:协议、域名、端口三者完全一致,才是同源。换句话说,https://a.example.com和http://a.example.com不同源,http://example.com:8080和http://example.com:8081也不同源。

浏览器强行规定:前端页面只能自由请求同源地址,跨源请求默认会被拦截。这样做的核心目的是安全——防止你登录了 A 网站后,B 网站的恶意脚本偷偷替你去操作 A 的接口,也就是防止 CSRF(跨站请求伪造)这类攻击。

前端看到的最典型报错长这样:

Access to XMLHttpRequest at 'https://api.example.com/data' from origin 'https://www.example.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

这个英文报错翻译一下就是:我请求了一个跨域地址,响应里没有带上允许我这个源访问的响应头,所以浏览器把结果拦下来了。

3.2 简单请求与预检请求:OPTIONS 是什么鬼

跨域里有两个概念面试必问:简单请求和预检请求(Preflight)。

满足下面所有条件的请求才是简单请求:

  • 方法为 GET、HEAD、POST 之一;
  • 请求头里没有自定义头(Authorization、X-Requested-With这种都不行);
  • 且Content-Type只能是application/x-www-form-urlencoded、multipart/form-data、text/plain三者之一。

只要不满足其中任意一条,浏览器就会先发一个OPTIONS预检请求,问服务器“我这次跨域请求带这些头和方法,你允许吗”?服务器确认允许后,浏览器才发真正的业务请求。

这就是为什么你用 axios 传application/json、或者带了Authorization头时,Network 面板能看到两个请求:一个是OPTIONS(状态通常 204),一个是实际请求。很多后端第一次联调时看到 OPTIONS 请求会很慌,直接给个 404,那前端真正请求也会被拦截。所以后端接口一定要针对 OPTIONS 放行。

3.3 后端 CORS 配置:绝大多数场景的标准答案

跨域最正规的解决方案是后端配置 CORS 响应头。只要后端在响应里带上浏览器需要的头,浏览器就不会拦截,这是彻底解决跨域的关键。

核心响应头配置如下:

Access-Control-Allow-Origin: https://www.example.com Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS Access-Control-Allow-Headers: Content-Type, Authorization Access-Control-Allow-Credentials: true Access-Control-Max-Age: 86400

后端用 Node.js(Express)写的话,加一个中间件就能实现:

app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', 'https://www.example.com'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization'); res.setHeader('Access-Control-Allow-Credentials', 'true'); if (req.method === 'OPTIONS') { res.sendStatus(204); return; } next(); });

PHP 后端也可以直接在入口文件加头输出:

header('Access-Control-Allow-Origin: https://www.example.com'); header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type, Authorization');

配置里有几个隐藏的坑:当需要携带 Cookie(withCredentials: true)时,Access-Control-Allow-Origin不能写成*,必须写具体域名;还有如果用了Access-Control-Allow-Origin: *,前端带自定义头照样会预检失败。遇到“后端说配了 CORS 但还是报错”的情况,优先检查响应头里的 Allow-Origin 是否和当前页面源完全一致,其次检查 Allow-Headers 是否覆盖了前端发送的所有自定义头。

3.4 JSONP 原理与适用场景:老方法但面试爱问

JSONP 是早期的跨域方案,原理非常巧妙:浏览器加载<script>标签请求外部资源是不受同源策略限制的。所以前端可以动态创建一个script标签,把跨域接口地址作为src,后端返回一段 JavaScript 代码(通常是函数调用),这个函数就是前端预先定义好的回调函数,数据作为参数传进来。

前端核心实现差不多是这样:

function jsonp(url, callbackName, onSuccess) { const script = document.createElement('script'); script.src = `${url}?callback=${callbackName}`; window[callbackName] = onSuccess; document.body.appendChild(script); } jsonp('https://api.example.com/user', 'handleUser', (data) => { console.log(data); });

后端 PHP 配合方式也很简单:

$callback = $_GET['callback'] ?? ''; $data = json_encode(['code' => 0, 'msg' => 'ok', 'data' => []]); if ($callback) { header('Content-Type: application/javascript'); echo $callback . '(' . $data . ');'; }

JSONP 的局限非常明显:只能发 GET 请求,没法 POST;并且依赖后端配合写回调。现在主流方案基本都用 CORS 了,但面试官还是喜欢考察这个概念,因为能检验你对浏览器加载机制和网络请求原理的理解深度。

3.5 代理方案:开发环境、线上 Nginx、Fiddler 抓包

跨域的另一种常用思路是“绕开跨域”,代理就是最典型的做法。开发环境里前端跑在本地localhost:3000,后端接口在http://192.168.1.100:8080,这种场景我几乎从不在代码里硬写后端完整地址,而是在构建工具配置代理,让浏览器始终只跟本地服务通信,由本地服务去请求后端。

Webpack devServer 配置:

devServer: { proxy: { '/api': { target: 'http://192.168.1.100:8080', changeOrigin: true } } }

Vite 大同小异:

server: { proxy: { '/api': { target: 'http://192.168.1.100:8080', changeOrigin: true, rewrite: path => path.replace(/^\/api/, '') } } }

配置完以后,前端代码里所有请求都写/api/xxx,因为是同源请求,浏览器不拦,代理层再把请求转发到后端。changeOrigin: true的作用是把请求头里的 Host 改掉,避免后端做域名校验时拒绝请求。

线上环境的代理通常是 Nginx 反向代理,配置类似:

location /api/ { proxy_pass http://backend-server:8080/; proxy_set_header Host $host; }

另外还有一类工具:Fiddler。它是抓包代理工具,常用于本地调试线上接口或者老项目的跨域问题。原理是让所有浏览器请求都经过 Fiddler,Fiddler 在转发过程中改写请求头 Host,或者给响应临时注入 CORS 头。这个方案适合调试和多环境切换,但因为是客户端代理,不会带到生产环境。

3.6 跨域相关的扩展知识与记忆点

除了 CORS、JSONP、代理这些主流方案,还有几个偏冷门但面试偶尔提到的:

  • postMessage:iframe 跨域通信的标准方式,两个不同源页面之间传消息。
  • document.domain:把两边页面的 domain 设置为同一个父域,适合主域名相同、子域名不同的情况。因为设置后可跨子域读取 Cookie 和 DOM,所以越来越受安全策略限制。
  • WebSocket:不受同源策略限制,因为 WebSocket 不走 HTTP 的 CORS 校验流程,适合需要实时数据的跨域场景。

前端 SDK 上报类需求也常碰到跨域:比如第三方埋点 SDK 需要给不同客户的页面上报数据,如果被上报方没有配置 CORS,请求就会被拦截。稳妥做法是让后端上报接口配置好宽松的Access-Control-Allow-Origin,或者在 SDK 内用sendBeacon配合降级方案,确保前端主动发起的统计请求不被漏掉。

4. Git 实战:安装配置、日常命令与分支合并

4.1 版本控制的定位与 Git 的“三区”模型

Git 是目前前端开发绕不开的版本控制工具,它的核心价值是:不丢代码、可回溯、多人协作不打架。写错代码想看到昨天的版本?用 Git 找回;两个人改了同一个文件?用 Git 合并并解决冲突。

理解 Git 要从它的存储模型入手。最经典的是“三区”模型:工作区(你编辑器里看到的文件)、暂存区(通过git add放进去的待提交变更)、版本库(通过git commit永久保存的历史记录)。

平时开发里最标准的一次提交流程是这类命令的组合:

git status # 查看当前工作区状态 git diff # 查看具体改了什么 git add src/pages/Home.vue # 把指定文件放入暂存区 git commit -m "feat: 新增首页轮播组件" # 提交到版本库 git push # 推送到远程仓库

这个模型理解后,很多操作就顺理成章了:git checkout -- file是把工作区文件恢复成暂存区版本,git reset --hard是把三个区全部恢复到指定版本,git stash是先保存手头工作留出干净目录。

4.2 Git 安装与全局配置:环境搭好,后面少踩坑

Git 安装本身不难。Windows 直接下载官方安装包,安装过程中建议勾选“Git Bash Here”和“Add to PATH”这两个选项,后面在终端里直接用git命令会舒服很多。macOS 可以brew install git,Linux 发行版用各自包管理器安装,比如apt install git。

装完第一件事不是急着 clone 仓库,而是配置全局用户信息,这两个配置不设置,commit 会失败或者提交的人名完全不对:

git config --global user.name "yourname" git config --global user.email "you@example.com"

Windows 用户最好再配一行换行符转换:

git config --global core.autocrlf true

原因是 Windows 换行符是 CRLF,Linux/macOS 是 LF,如果不做转换,换行符差异会导致整个文件被标记为已修改,diff 看哪儿都是红的。macOS/Linux 用户可以设成input,效果是提交时自动转成 LF。

还可以给常用命令配别名,比如:

git config --global alias.st status git config --global alias.lg "log --oneline --graph --all"

之后敲git lg就能看到清晰的提交历史分支图。配置完了用git config --list校验一下所有配置项是否正确加载。

4.3 日常开发高频命令:clone、add、commit、push、pull、log

前端日常用 Git,说白了就是一套固定的组合拳。我按使用频率整理成一张速查表:

命令场景说明
git clone <url>拉取远程仓库到本地首次接手项目
git status随时查看状态最常用的命令,没有之一
git add .暂存所有改动确认无误用,否则指定文件
git commit -m "message"提交代码message 要写清楚做了啥
git pull --rebase拉取远程更新推荐加 --rebase,避免多余 merge 记录
git push推送到远程推送前先 pull 保持同步
git log --oneline查看提交历史压缩显示,一眼看清
git branch -a查看所有分支本地和远端分支一起列出
git checkout -b new-branch新建并切换分支开发新功能常用
git merge xxx合并分支把 xxx 分支合入当前分支

这里重点说git pull --rebase。直接git pull默认执行的是 merge 操作,会在提交历史里多出一个Merge branch ...的提交记录,长期下来历史会很乱。用--rebase,Git 会把当前分支没推送的提交临时拿下来,拉取远程最新提交后再按顺序放回去,历史是一条干净的直线。团队多人协作时,这种干净历史对回溯很有用。

4.4 分支合并实战:merge、rebase、冲突解决

分支是 Git 最强大的能力之一。我习惯的团队分支模型是:main(或者master)保留稳定可发布的版本,开发需求从main拉出feature/xxx分支,修 bug 从main拉出bugfix/xxx分支,合并时再合并回main。

合并分支有两种方式:git merge和git rebase。简单说:merge 会保留所有人的完整提交历史,形成分叉再汇合的网络;rebase 会把当前分支的提交“搬家”到目标分支的历史后面,形成线性历史。初学阶段用 merge 就好,逻辑清楚、不容易出问题;等理解深了再在个人开发分支上用 rebase 整理历史。

真正考验人的是冲突处理。当两个分支改了同一个文件的同一块代码,Git 无法自动判断取谁,就会标出冲突。典型场景是,我在feature/login分支改了一段样式,main分支也改了这个文件的同一行代码,合并时就会看到:

<<<<<<< HEAD console.log('本地分支的代码'); ======= console.log('feature/login 分支的代码'); >>>>>>> feature/login

<<<<<<< HEAD到=======之间是当前分支的内容,=======到>>>>>>> feature/login之间是被合并分支的内容。手动改成想要的结果,比如console.log('合并后的代码');,删掉冲突标记符,然后执行:

git add 文件路径 git commit -m "fix: 解决登录页样式冲突"

冲突处理有个原则:不要一个人闷头乱改。如果两个人各改了一半逻辑,最好和对方沟通确认保留哪部分,再定最终版本。合并后立刻跑一遍测试,因为冲突解决错了不会报错,只会埋下运行时 bug。

4.5 常见报错排查:SSH 认证失败与撤销操作

Git 用久了总会碰到各种报错,最经典的就是 SSH 认证失败,报错信息类似Permission denied (publickey)。我第一次遇到时还以为是密码错了,后来才发现是 SSH 公钥没配上。

排查步骤按顺序来:

  1. 检查本地是否生成过 SSH key:
ls -al ~/.ssh

正常情况下能看到id_rsa或id_ed25519和对应的.pub文件。

  1. 没生成过就重新生成,用 ed25519 更快更安全:
ssh-keygen -t ed25519 -C "you@example.com"

一路回车即可,文件默认生成在~/.ssh/id_ed25519。

  1. 把公钥内容复制出来(~/.ssh/id_ed25519.pub文件里整段以ssh-ed25519开头的内容),添加到 Git 平台的 SSH Keys 设置里。

  2. 测试连接是否打通:

ssh -T git@github.com

能看到类似Hi xxx! You've successfully authenticated的提示,说明认证通过。国内常用的 Gitee、GitLab 验证命令也类似,只是域名不同。

还有一个高发问题:如果当初 clone 用的是 HTTPS 方式,而在某些托管平台上密码验证已关闭,git push会报认证失败。解决办法是改用 Personal Access Token(个人访问令牌)作为密码输入,或者改用 SSH 远程地址重新添加 remote。

撤销操作也是必备技能。我做一次完整整理:

  • 撤销工作区修改(未 add):git checkout -- 文件,恢复成上次提交/暂存的内容。
  • 撤销暂存(已 add 未 commit):git reset HEAD 文件,把文件从暂存区退回工作区。
  • 撤销上一次提交但保留改动:git reset --soft HEAD~1。
  • 彻底丢弃最近 N 次提交:git reset --hard HEAD~N,这个要非常小心,会丢失提交内容。
  • 已经 push 到远程了,要安全撤销:用git revert <commit>,它会生成一个反向提交把代码改回去,又保留了历史记录,适合团队协作时使用。

我个人的经验是,git reset --hard这类命令只在自己的个人分支上使用,公共分支一律用revert,否则会把同事的本地历史搞得很难看,甚至导致代码丢失。

4.6 团队协作常见配合与提交规范

前面说了命令,但团队真正讲究的是“提交习惯”。我现在带的团队要求 commit message 遵循统一格式,比如:

feat: 新增登录页记住密码功能 fix: 修复移动端键盘遮挡输入框问题 docs: 更新 README 部署说明 refactor: 重构订单列表状态管理

简单一句话:类型 + 冒号 + 描述。这样git log --oneline一眼能看出每个提交做了什么,回滚时也能快速定位到具体某个功能或修复。

写代码时有个好习惯是频繁提交、小而清晰。一次提交只做一件事,一个提交只包含相关的文件改动。这比一天憋一个“更新代码”的大提交要健康得多,审查代码和回溯都容易。

用 IDEA、VS Code 这类 IDE 开发的朋友,我建议命令和图形化配合着来。日常 add、commit、push 用 IDE 内嵌的 Git 面板没问题,操作直观友好;但遇到冲突解决、rebase、reset 这类高风险操作,我会切到命令行,因为能看到更完整的输出信息,不容易被图形界面隐藏关键细节。

最后提一个容易被忽略的配置:.gitignore。新项目初始化时一定要尽早建立这个文件,把node_modules/、dist/、.env、日志文件等不需要提交的内容排除掉。我见过同事把node_modules提交进仓库,一次 push 好几百兆,后面所有人 clone 都变慢,这些都是可以提前规避的低级问题。

总得来说,这 15 天学下来,最值钱的不是记了多少具体答案,而是把 HTTP、浏览器、跨域、Git 这四块拼图连成了一整条线:发请求时想着 HTTP 报文和连接复用,调试时想着浏览器缓存和渲染流程,遇到跨域时先分辨是简单请求还是预检请求,每次提交代码时想着分支规范和排查路径。按我个人经验,基础部分最忌讳死记硬背,而是要多在真实项目里踩坑、记录、复述,面试时才讲得出有细节的实战答案。接下来如果你正好有条件,建议找几个线上接口多模拟联调和代理配置,或者在团队仓库里主动承担分支管理和冲突解决的部分,这种脚下踩泥巴式的学习,效果比看十篇教程都有用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询