Skip to content

Git 分支与远程仓库(三) ​

本页对应 Git 分支与远程仓库,共 6 段视频,约 42 分钟。

学习目标 ​

  • 能够复述 JWT 验证流程,说明登录获取 token、缓存 token、请求携带 token、401 失效处理的关键环节。
  • 能够使用 axios 请求拦截器统一为请求头添加 token,并处理本地无 token 或 token 解析失败时不添加 token 的场景。
  • 能够使用 axios 响应拦截器统一处理 401 错误,完成提示重新登录、清除本地存储并跳转登录页。
  • 能够通过响应拦截器实现数据剥离,并同步修改登录、注册、首页等页面中对应的数据读取代码。
  • 能够使用 Gitee 新建远程仓库,并完成本地仓库推送、克隆和拉取操作。

知识点 ​

需求:页面少写一层 data ​

为什么需要:axios 默认返回的 response 不是服务端原始响应体,而是被 axios 包装过的对象,真正业务数据在 response.data 里。页面里如果每次写 res.data.data.xxx,既啰嗦又容易漏。

怎么想:在响应拦截器的成功回调里,把 axios 包装层剥掉,直接 return response.data。这样页面拿到的 res 就是服务端响应体,原来两次 .data 变成一次。

怎么做:先看效果对比:

响应拦截器数据剥离示意图

页面使用数据调整示意图

易错点:剥离后所有使用响应的页面都要同步改,否则会出现“数据渲染不出来/值不变”的现象。

在 commons.js 添加响应拦截器 ​

为什么需要:401 登录失效、数据剥离都是“响应回来之后”的公共逻辑,应该统一放在公共文件,而不是每个接口都写一遍 try/catch。

怎么想:请求拦截器处理“请求发送前”,响应拦截器处理“响应回来之后”。成功响应走第一个函数,非 2xx 走第二个函数。成功函数里做数据剥离;失败函数里判断 error.response.status === 401,提示、清缓存、跳登录。

怎么做:

javascript
// 添加响应拦截器
// 统一处理token过期
// 数据剥离
axios.interceptors.response.use(function (response) {
  // 2xx 范围内的状态码都会触发该函数。
  // 对响应数据做点什么,比如: 数据剥离
  // 剥离data属性,页面中少写.data属性,直接可以获取到数据
  return response.data;
}, function (error) {
  // console.dir(error)
  // 超出 2xx 范围的状态码都会触发该函数。
  // 对响应错误做点什么: 比如统一处理token失效
  // 统一处理token失效
  if (error.response.status === 401) {
    // 弹框提示用户
    showToast('请重新登录')
    // 删除缓存
    localStorage.removeItem('token')
    localStorage.removeItem('username')
    // 返回登录页
    setTimeout(() => {
      location.href = 'login.html'
    }, 1500)
  }
  return Promise.reject(error);
});

易错点:return Promise.reject(error) 不要删。转写中特别提醒:这两个 return 是拦截器把请求/响应继续交出去的关键,删掉会导致请求或响应失效。调试 401 时,可在失败回调打印 error,展开看到 error.response.status;转写中是在 common.js 79 行附近验证到 401 的。

调整首页数据使用:移除多余 .data ​

为什么需要:拦截器已经返回 response.data,首页若还按旧写法取两层 data,就拿不到数据。

怎么想:原来类似 res.data.data.overview,现在应变成 res.data.overview。只删 axios 包装的那一层,服务端响应体里的 data 字段仍保留。

怎么做:

javascript
// 首页-统计数据
async function getData() {
  // 调用接口(登录成功之后才可以调用)
  const res = await axios({
    url: '/dashboard',
    // 通过axios请求拦截器统一携带
  })
  const overview = res.data.overview

  // 渲染数据
  Object.keys(overview).forEach(key => {
    document.querySelector(`.${key}`).innerText = overview[key]
  }) 
}

易错点:转写调试时,改成 return response.data 后页面值不变,是因为 index.js 里还在用 res.data.data;把首页、登录、注册三处都改完才恢复。

调整注册与登录:try 中少写一层 .data ​

为什么需要:注册、登录的成功响应也经过同一个响应拦截器,成功时 res 已经是服务端响应体。

怎么想:成功提示用 res.message;登录缓存仍取 res.data.token、res.data.username,因为服务端响应体里仍有 data 字段。失败响应走失败回调,未被剥离,所以 catch 里仍是 error.response.data.message。

怎么做:

javascript
document.querySelector('#btn-register').addEventListener('click', async () => {
  // 1. 收集并校验数据
  const form = document.querySelector('.register-form')
  const data = serialize(form, { empty: true, hash: true })
  // console.log(data)
  const { username, password } = data
  console.log(username, password)
  // 非空校验
  if (username === '' || password === '') {
    showToast('用户名和密码不能为空')
    return
  }

  // 长度校验
  if (username.length < 8 || username.length > 30 || password.length < 6 || password.length > 30) {
    showToast('用户名的长度为8-30,密码的长度为6-30')
    return
  }

  // 2. 数据提交
  try {
    // .post 请求方法 post,参数1:请求URL,参数2:提交的数据
    const res = await axios.post('/register', { username, password })
    // console.log(res)
    showToast(res.message)
  } catch (error) {
    // console.dir(error)
    showToast(error.response.data.message)
  }
})
javascript
document.querySelector('#btn-login').addEventListener('click', async () => {
  // 1. 收集并校验数据
  const form = document.querySelector('.login-form')
  const data = serialize(form, { empty: true, hash: true })
  console.log(data)
  const { username, password } = data
  // 非空判断
  if (username === '' || password === '') {
    showToast('用户名和密码不能为空')
    return
  }

  // 格式判断
  if (username.length < 8 || username.length > 30 || password.length < 6 || password.length > 30) {
    showToast('用户名长度8-30,密码长度6-30')
    return
  }

  // 2. 提交数据
  try {
    const res = await axios.post('/login', { username, password })
    // console.log(res)
    showToast(res.message)
    // 3. 缓存响应数据
    localStorage.setItem('token', res.data.token)
    localStorage.setItem('username', res.data.username)
    // 4. 跳转首页
    // 延迟一会在跳转,让提示框显示
    setTimeout(() => {
      // login.html和index.html的相对关系
      location.href = './index.html'
    }, 1500)

  } catch (error) {
    // console.dir(error)
    showToast(error.response.data.message)
  }

})

易错点:不要把 catch 里的 error.response.data.message 也改成 error.response.message,错误响应没有走成功剥离逻辑。

验证与提交 ​

怎么做:登录成功后篡改 token 再刷新首页,观察是否提示“请重新登录”、清除缓存并跳回登录页;再正常登录,确认首页统计、注册提示、登录跳转都正常。最后提交:

bash
git add .
git commit -m"axios响应拦截器-数据剥离"

易错点:数据剥离和 401 统一处理都放在 commons.js 后,页面里原来的 token 失效判断可移除,避免重复逻辑。

远程仓库是什么 ​

远程仓库是指托管在因特网或其他网络中的项目版本库。它不是一个抽象概念,可以理解为:把本地 Git 仓库中的项目版本信息,放到一台网络上可访问的服务器上保存和管理。笔记中给出的文档地址是:Git 基础 - 远程仓库的使用。

Git远程仓库示意图

为什么需要:如果代码只放在本地电脑里,一旦换电脑、电脑不能带走,或者同事也要参与开发,代码的传递就会很麻烦。转写稿里举了一个很具体的场景:教室里的电脑不能拿走,但晚上回家还想继续巩固代码,于是要用 U 盘拷、用网盘传;如果同事也要用,还要再把网盘链接发给他,既麻烦又不方便协作。远程仓库解决的正是“代码放在哪里、谁能拿到、多人怎么一起用”的问题。

怎么想:远程仓库类似一个公共网盘,但它不是普通网盘,而是记录项目版本库的托管位置。只要和项目有关的人需要,就可以从远程仓库获取代码;自己在公司用电脑 A、回家用电脑 B,也可以从服务器上重新拿到项目继续工作。

怎么做:把本地仓库推送到互联网中的某个托管位置,让代码在远程服务器上有一份版本库。笔记中总结的作用有两点:一是本地仓库备份,二是多人协作。这里的“多人写作”应理解为“多人协作”。

易错点:不要把远程仓库只理解成“备份盘”。它更重要的价值是支持多人协作和多设备获取代码;同时,远程仓库托管的是 Git 版本库,不是随便上传一个压缩包。

常见远程仓库怎么选 ​

笔记列出了三类常见 Git 远程仓库:

  1. GitHub:面向开源及私有软件项目的托管平台,因为只支持 Git 作为唯一的版本库格式进行托管,故名 GitHub。它的特点是国内访问较慢。
  2. Gitee:开源中国推出的基于 Git 的代码托管服务。服务器在国内,访问迅速,课程中主要使用这个。转写稿中提到的“马云”应纠正为“码云(Gitee)”。
  3. GitLab:一个用于仓库管理系统的开源项目,使用 Git 作为代码管理工具,并在此基础上搭建 Web 服务。一般是公司内部部署并使用。

为什么需要了解平台:因为远程仓库必须依托具体的托管平台。不同平台的访问速度、使用场景不同,需要根据课程或团队情况选择。

怎么想:GitHub 很流行,但转写稿提醒它打开可能比较麻烦,有时会被限制,注册也不太容易,而且界面是英文的;Gitee 是国内版代码管理仓库,更适合当前课程使用;GitLab 常见于公司内部部署。

怎么做:课程中优先使用 Gitee。无论选择哪一种,只要基于 Git,用法大同小异,不必因为平台不同而认为要重新学一套完全不同的东西。

易错点:不要以为换了平台,Git 的核心概念就变了。远程仓库的托管平台不同,但 Git 管理版本库的基本思路一致;同时要注意 GitHub 国内访问较慢这一现实问题。

使用远程仓库前的准备 ​

为什么需要:要使用远程仓库,首先要在平台上拥有自己的账号,否则无法创建和管理网络上的仓库。

怎么想:转写稿中说明,使用公共仓库前要先注册自己的账号,注册完后最好绑定邮箱。绑定邮箱是为了账号信息更完整,也方便后续使用。

怎么做:到 Gitee 官网注册账号,注册后绑定邮箱。转写稿还提到平台可能会提示修改默认分支名,但当前不用做,因为现在默认分支名就是 master;Gitee 默认也是 master 分支。如果以后确实想改,可以在“仓库首选项”里修改默认分支名。

易错点:不要一看到“修改默认分支名”的提示就立刻去改。课程当前不需要改,默认 master 即可;真正要做的是注册账号、绑定邮箱,然后准备在远程仓库上新建一个网络仓库,把本地代码推送上去。

把本地代码推到远程的思路 ​

为什么需要:远程仓库建好后,如果里面没有项目内容,就无法实现备份和协作。因此需要把本地已有代码推送上去。

怎么想:转写稿的流程是:先有远程平台账号,再在远程仓库上新建一个网络上的仓库,然后把本地代码推送上去。这样远程服务器上就有了项目版本库,之后换设备或多人参与时,都可以围绕这个远程仓库获取代码。

怎么做:按“注册账号并绑定邮箱 → 在平台上新建远程仓库 → 将本地仓库代码推送到该远程仓库”的顺序推进。笔记没有展开具体 Git 命令,本节重点是把远程仓库的概念、作用和平台选择讲清楚。

易错点:不要跳过账号和远程仓库创建,直接谈推送;也不要把“新建远程仓库”和“本地已有仓库”混为一谈。先明确远程仓库的位置,再考虑把本地内容推上去。

先理解:远程仓库解决什么问题 ​

为什么需要:本地代码只存在当前电脑里。转写稿里举了一个很具体的场景:教室电脑不能带走,今天代码写完了,晚上回家还想继续巩固,怎么办?用 U 盘拷、用网盘传,都行,但多人协作时就麻烦了——同事也要用,还得再发网盘链接,下载速度还可能受限制。
怎么想:远程仓库就是把本地仓库推送到互联网上的某个位置,代码托管在服务器上,项目版本库也在服务器上记录。转写稿里把它类比成“公共网盘”,但要注意它不是普通网盘,而是带版本管理能力的代码仓库。这样公司电脑 A、家里电脑 B、甚至逛街时临时想写代码,都可以从服务器拉下来继续做。
怎么做:本节选择 Gitee。转写稿里提到 GitHub 也是代码管理仓库,但英文界面、注册和访问可能麻烦;Gitee 是国内常用的代码托管平台,转写稿里口播的“getE”应理解为 Gitee,“get”应理解为 Git,“马云”应理解为“码云”。
易错点:不要把“远程仓库”理解成本地 Git 仓库;远程仓库要先有平台账号,后面才能新建网络仓库并推送本地代码。

注册 Gitee 账号 ​

为什么需要:远程仓库要托管在平台上,账号就是你访问、创建、管理远程仓库的入口。
怎么想:先注册,再登录;没有账号,后面的绑定邮箱和仓库设置都无从谈起。
怎么做:打开 gitee,在右上角找到“注册”,按页面提示完成注册。对应笔记图片如下:

Gitee注册入口

易错点:注册时用户名、邮箱、密码要填写正确;注册完成后要确认已经登录,不要停留在注册成功页就以为准备工作结束了。

绑定邮箱 ​

为什么需要:转写稿里明确说,注册完自己的账号后“最好去绑定一下邮箱”。邮箱用于账号找回、通知和身份识别,是账号准备的重要一步。
怎么想:登录后从右上角进入账号相关设置,找到“添加绑定”,按提示新增邮箱并完成验证。
怎么做:登录之后,右上角点击“添加绑定”,根据提示新增邮箱即可。对应笔记图片如下:

Gitee绑定邮箱入口

易错点:只填写邮箱但没有完成验证邮件,通常不算绑定完成;验证邮件可能进垃圾箱,找不到时先检查垃圾箱。转写稿里强调“注册完账号,绑定邮箱”,所以这一步不要跳过。

设置默认分支名 ​

为什么需要:默认分支名影响新建仓库时的初始分支。本节笔记要求把默认分支名改为 main,目的是和后续项目约定保持一致,避免本地和远程分支名不统一。
怎么想:转写稿里实际找设置时,先翻到账号管理附近,发现不是那里,最后在“仓库首选项”里找到默认分支名设置。这说明操作路径不要凭记忆,要按“个人设置/账号设置→仓库首选项”去找。转写稿还补充:Gitee 默认分支是 master,如果项目不需要 main,不改也行;但本节笔记记录的是改为 main,所以我们按笔记统一设置。转写稿里口播的“me”应理解为 main。
怎么做:找到个人设置,进入“仓库首选项”,把默认分支名改为 main。对应笔记图片如下:

Gitee仓库首选项设置默认分支

易错点:不要把“仓库名”和“默认分支名”混为一谈;改的是默认分支名,不是仓库名称。分支名大小写要写准确,main 不是 master。另外,这个设置主要影响之后新建仓库的默认分支,已有仓库是否需要单独调整,以页面提示为准。

准备完成后的检查 ​

为什么需要:确认账号、邮箱、默认分支都准备好了,后面才能顺利新建远程仓库并推送本地代码。
怎么想:按“能登录、邮箱已绑定、默认分支符合约定”三项检查。
怎么做:重新登录 Gitee,确认右上角账号状态正常;进入绑定设置确认邮箱已添加;进入仓库首选项确认默认分支名为 main。
易错点:只注册不绑定邮箱;绑定邮箱后没验证;默认分支仍停留在 master 却以为已经改成 main。转写稿最后也说明,本节先完成“什么是远程仓库”和“注册远程仓库账号”的准备,下一步才是在远程仓库上新建网络仓库,把本地代码推送上去。

先理解:远程仓库到底解决什么问题 ​

为什么需要:本地仓库只能管住当前这台电脑。转写稿里举了一个很实际的场景:教室电脑不能带走,今天代码写完了,晚上回家还想继续改;用 U 盘拷、用网盘传,自己用还行,同事也要用时就麻烦了。远程仓库就是把本地仓库托管到互联网上的一个位置,只要和项目有关的人都能按权限使用。

怎么想:不要把远程仓库想复杂,它类似一个“公共网盘”,但它记录的是项目版本库,不只是普通文件。这样你在公司用电脑 A,回家用电脑 B,甚至逛街时临时想写代码,都可以从服务器拿代码;多人协作也有了统一入口。

怎么做:本节选择 Gitee(码云)作为远程仓库。转写稿提到 GitHub 也常用,但可能访问受限、注册相对麻烦、界面偏英文;Gitee 是国内代码管理仓库,更适合当前练习。

易错点:远程仓库不是“把文件夹压缩上传”就完事,它要和本地 Git 仓库建立对应关系;后面推送时命令要以新建仓库页面显示为准,不要照抄别人的仓库地址。

准备账号:注册、绑定邮箱、默认分支先别乱改 ​

为什么需要:没有账号就无法在 Gitee 上创建自己的远程仓库,也无法区分仓库归属和权限。

怎么想:先注册账号,再绑定邮箱。转写稿特别说明:注册完账号后最好绑定邮箱。至于“默认分支名”,备课环境里可能叫 main,但我们现在本地默认是 master,所以不用跟着改。

怎么做:进入 Gitee 官网注册账号并绑定邮箱。若以后确实想改默认分支名,可在“账号设置”里找到“仓库首选项”进行修改;本节不要求改。因为当前本地分支默认是 master,Gitee 默认也是 master,保持一致更省事。

易错点:不要一上来就改默认分支名,否则本地和远程分支名不一致,后续推送容易多出额外操作;邮箱没绑定也可能影响账号后续使用。

新建远程仓库:点右上角“+”,填好必填项 ​

为什么需要:远程仓库要先在服务器上创建出来,本地代码才有推送目标。

怎么想:新建仓库时,平台会让填仓库名、可见性等必填信息。这里的目标不是研究所有选项,而是先创建一个能接收本地代码的空仓库。

怎么做:登录后点击右上角的“+”,选择新建仓库;按页面要求设置必填项,完成新建。核心步骤可对照下图:

核心步骤示意图

点击右上角的“+”,选择新建仓库:

点击加号新建仓库

设置必填项,完成新建:

设置必填项完成新建

易错点:仓库名和路径要看清;不要把“新建仓库”和“导入仓库”混掉;如果页面提示初始化 README 等选项,按当前课程要求选择,别随意勾选导致本地推送时还要额外处理。

推送本地代码:在项目根目录执行页面给出的命令 ​

为什么需要:远程仓库只是空壳,本地代码还没上去。推送就是把本地提交同步到远程仓库。

怎么想:新建完成后,Gitee 页面通常会给出 2–3 行命令,告诉你如何关联远程仓库并推送。我们要做的不是背命令,而是确认当前终端位置正确,然后按页面提示执行。

怎么做:在项目根目录打开 Git Bash 或终端,依次执行页面给出的 2–3 行命令。命令内容以你新建仓库后页面显示为准,不要凭空改写。

在项目根目录执行页面命令

易错点:第一,必须在项目根目录执行,不要在桌面上随便开终端;第二,命令要按页面复制,仓库地址每个人不同;第三,如果本地还没有提交,推送可能没有内容可推,应先确认本地仓库状态;第四,推送后回到 Gitee 页面刷新,确认文件已经出现。

小结:把“新建”和“推送”连成一条线 ​

本节完成的是“新建 Gitee 远程仓库,并把本地代码推送到服务器”。流程可以记成:注册并绑定邮箱 → 右上角“+”新建仓库 → 填必填项完成创建 → 在项目根目录打开 Git Bash/终端 → 执行页面给出的 2–3 行命令 → 回远程仓库刷新验证。远程仓库的价值在于多设备、多人协作和统一托管;操作上最关键的是别改乱默认分支、别在错误目录执行、别照抄别人的仓库命令。

先理解:克隆到底解决什么问题 ​

为什么需要: 转写稿里用了一个很直观的场景:人在教室,电脑不能带走,但晚上回家还想继续写代码;用 U 盘拷、用网盘传都行,但多人协作时再互相发网盘就很麻烦。远程仓库就像放在互联网上的“公共网盘”,代码托管在服务器上,换一台电脑可以从服务器拿下来,项目相关的人也可以围绕同一个仓库协作。本节的需求很明确:克隆(clone)——获得一份已经存在了的 Git 仓库的拷贝。

怎么想: 克隆不是“新建一个空项目”,而是把一个已经存在的远程仓库复制到本地。所以第一步不是急着敲命令,而是先确认远程仓库地址。

怎么做: 打开远程仓库首页,找到“克隆/下载”,选择 HTTPS,复制仓库地址。笔记里的核心步骤图如下:

Git 克隆核心步骤示意图

易错点: 笔记提醒“不是 URL 地址,选择克隆/下载,找到 HTTPS,点击复制”。这里要理解为:不要随手复制浏览器地址栏里的网页 URL,而要到仓库页面的“克隆/下载”区域复制平台提供的 HTTPS 克隆地址。git clone 后面仍然需要一个仓库地址,只是这个地址要从正确位置复制。

执行克隆命令 ​

为什么需要: 地址准备好后,必须用 Git 命令把远程仓库拉到本地,否则本地只有一个网页地址,并没有真正的项目目录。

怎么想: 命令结构很直接:git clone 加上远程仓库地址。笔记给出的形式是:

bash
git clone 远程仓库地址

怎么做: 在命令行工具中进入你希望存放项目的目录,执行上面的命令。执行完成后,本地会得到一份远程仓库的拷贝。

易错点: 不要把命令写成只复制一半地址;也不要把“克隆/下载”按钮旁边的说明文字一起复制进去。地址应当来自 HTTPS 那一栏。转写稿里把 Git 说成“get”是口误,实际命令是 git。

进入克隆出来的项目目录 ​

为什么需要: 克隆完成后,当前命令行所在目录不一定就是项目目录。后续查看分支、切换分支都要在仓库目录里执行,否则 Git 找不到仓库。

怎么想: 克隆会生成一个仓库目录,需要先进入它。笔记写的是“通过 cd 仓库目录 进入项目”。

怎么做:

bash
cd 仓库目录

这里的“仓库目录”要换成你实际克隆出来的目录名。

易错点: 不要停留在上一级目录直接执行分支命令;也不要想当然认为目录名一定和远程仓库名完全一样,应以实际生成的目录名为准。本节没有给出具体仓库名,所以教程里保留“仓库目录”这个占位。

查看全部分支 ​

为什么需要: 克隆下来后,需要知道仓库里有哪些分支。只看本地分支可能不够,因为远程分支也要确认。

怎么想: 笔记要求用 git branch -a 查看全部分支,包括本地分支和远程分支。注意笔记里写成 git branh -a 是拼写错误,正确命令是 branch。

怎么做:

bash
git branch -a

执行后,远程分支通常会以类似 remotes/origin/dev 的形式显示出来。

易错点: 第一,命令拼写别少字母;第二,看到 remotes/origin/dev 不要慌,它表示远程仓库 origin 下的 dev 分支,不是让你把整串都当作切换命令。

切换远程分支 ​

为什么需要: 查看分支是为了进入正确的开发分支。如果远程有 dev 分支,而你要在 dev 上工作,就需要切换过去。

怎么想: 笔记给了一个关键点:切换远程分支时,只需要分支名即可。例如显示的是 remotes/origin/dev,切换命令不是写一整串,而是:

bash
git checkout dev

怎么做: 在仓库目录中执行 git checkout dev,然后按实际状态继续工作。

易错点: 不要把命令写成 git checkout remotes/origin/dev。笔记明确说“切换远程分支,只需要分支名即可”。另外,origin 是远程仓库的默认名称,remotes/origin/dev 只是 git branch -a 的显示形式,不是切换时要照抄的完整分支名。

把流程串起来 ​

本节完整流程可以归纳为:先在远程仓库首页的“克隆/下载”里选择 HTTPS 并复制地址;执行 git clone 远程仓库地址;用 cd 仓库目录 进入项目;用 git branch -a 查看本地和远程全部分支;看到 remotes/origin/dev 这类远程分支后,用 git checkout dev 切换。转写稿里还提到远程仓库平台注册账号、绑定邮箱、默认分支为 master 时不必修改等内容,这些属于使用远程仓库平台的背景准备;就本节“克隆”而言,核心仍是上面的命令流程。转写稿中“马云”是口误,实际说的是码云/Gitee。本节没有给出具体终端回显和报错调试记录,因此不虚构调试输出;验证方式就是按笔记顺序执行命令,并用 git branch -a 确认分支显示,再按分支名切换。

一、先理解“拉取”和远程仓库 ​

为什么需要: 本地代码只存在自己电脑上,换一台电脑、回家继续写、或者让同事一起协作,就不方便。转写稿里用一个很直白的场景说明:教室电脑不能带走,可以用U盘或网盘拷走;但多人协作时,最好把代码放到互联网上的一个位置,这个位置就是远程仓库。远程仓库可以理解为“公共网盘 + 版本库”,记录项目的版本,支持多设备、多人协作。转写稿中提到的“吉他”应纠正为GitHub,“马云”应指码云(Gitee);国内更推荐使用Gitee。

怎么想: 本节的核心不是重新讲本地提交,而是先解决“远程仓库能不能被访问”的问题。笔记明确写了拉取(pull)的作用:从远程仓库拉取代码并合并到本地。也就是说,拉取不是单纯“下载一份文件”,而是把远程仓库的代码取回来并合并到当前本地仓库。要让其他人访问自己的仓库,笔记特别提醒:需要把仓库设置为开源。

怎么做: 先确认自己已经有远程仓库,并且仓库里有内容;再按本节图示完成拉取相关操作。笔记中的关键步骤见下图:

远程仓库拉取关键步骤

易错点: 不要把“拉取”理解成只下载压缩包;笔记强调的是拉取并合并到本地。另一个易错点是:远程仓库是否能被其他人访问,和“开源设置”有关,不是把本地路径发过去就行。

二、为什么要把仓库设置为开源 ​

为什么需要: 笔记中写得很明确:如果要让其他人访问自己的仓库,需要设置为开源。也就是说,别人要能看到、访问你的仓库,仓库的访问权限必须允许。否则即使你把地址发过去,对方也未必能正常访问。

怎么想: 这里要先区分两件事:第一,仓库里要有内容;第二,仓库要允许别人访问。笔记特别标注“仓库需要有内容才可以设置开源”,所以不要新建一个空仓库就去设置开源,否则容易找不到可勾选的选项或设置不成功。

怎么做: 进入仓库首页,点击“管理”,拉到页面底部,勾选对应选项,然后保存。笔记中的设置位置见下图:

Gitee仓库开源设置选项

易错点: 最常见的问题是仓库还是空的就去设置开源;其次是设置完忘记保存;还有就是以为“公开”等于“自动把地址发给别人”,其实还需要把仓库URL给协作者。

三、把仓库URL交给协作者 ​

为什么需要: 设置开源只是让仓库具备被访问的条件,协作者还需要知道仓库在哪里。笔记给出的做法是:接下来直接将仓库的Gitee的URL地址给其他小伙伴即可。

怎么想: 远程仓库的地址就是协作入口。对方拿到URL后,才能访问仓库、继续后续的拉取等操作。这里不要发本地文件路径,也不要发自己电脑上的目录,要发Gitee仓库地址。

怎么做: 在Gitee仓库页面复制仓库URL,发给需要访问的小伙伴。对方通过这个URL访问仓库。若对方后续要拉取代码,仍要按远程仓库的拉取流程操作,关键步骤参考本节第一张图。

易错点: 不要把“仓库已开源”当成“URL已经发送”;也不要把浏览器地址栏里的无关页面地址发过去。笔记要求的是“仓库的Gitee的URL地址”。

四、转写稿补充:远程仓库使用前的准备 ​

为什么需要: 转写稿介绍了远程仓库的使用背景:需要有自己的账号,注册后最好绑定邮箱;Gitee默认分支是master,特别说明当前不用去改默认分支名。若以后要改,可在“仓库首选项”里修改默认分支名。

怎么想: 这些准备不是拉取命令本身,但属于拉取前的前置条件:账号可用、邮箱已绑定、仓库可访问。转写稿还提到,接下来要在远程仓库上新建网络仓库,把本地代码推送上去;本节笔记聚焦“拉取”,所以具体拉取动作以笔记图片为准,不额外编造命令细节。

怎么做: 注册并登录Gitee,绑定邮箱;确认默认分支为master即可,不必按转写稿中提到的旧分支名去修改;进入目标仓库,按“管理→拉到底→勾选→保存”设置开源;复制仓库URL发给协作者。

易错点: 不要把“默认分支名”随意改掉;转写稿明确说当前不用改。也不要把账号注册、绑定邮箱、仓库开源、发送URL这几步混在一起,按顺序检查更稳妥。

课件图示 ​

课件第17页:Git远程仓库-新建仓库&推送 新建gitee远程仓库,并且把本地的代码推送到服

课件第14页:axios响应拦截器-数据剥离 服务器 调用接口1 调用接口2 调用接口3 请求

课件第13页:axios响应拦截器-统一处理token失效 服务器 调用接口1 调用接口2 调

课件第20页:拉取(pull): 从远程仓库拉取代码并合并到本地 注意: 如果要让其他人访问自

课件第18页:Git远程仓库-开发及推送 本地开发代码,并推送到远程仓库 电脑A 本地Git仓

课件第19页:Git远程仓库-克隆 克隆(clone): 获得一份已经存在了的Git 仓库的拷

课件第22页:Git远程仓库-重新上传 电脑 Git仓库 gitee服务器 1.新建远程G

课件第21页:SSH是一种网络协议,用于计算机之间的加密登录 文档地址 Git远程仓库-配置S

代码示例 ​

1. commons.js —— 请求拦截器 + 响应拦截器(401 统一处理 + 数据剥离) ​

javascript
/* commons.js:所有页面都要引入 <script src="./commons.js"></script> */

/* 简易提示框,项目里用的是 utils 中的 showToast,这里给出等价实现,保证可直接运行 */
function showToast(msg) {
  let box = document.querySelector('#toast')
  if (!box) {
    box = document.createElement('div')
    box.id = 'toast'
    box.style.cssText =
      'position:fixed;left:50%;top:30%;transform:translateX(-50%);padding:10px 18px;' +
      'background:rgba(0,0,0,.75);color:#fff;border-radius:6px;font-size:14px;z-index:999;'
    document.body.appendChild(box)
  }
  box.innerText = msg
  box.style.display = 'block'
  clearTimeout(box.timer)
  box.timer = setTimeout(() => (box.style.display = 'none'), 1500)
}

/* 0. 公共数据抽取:先取 token,取不到就是空字符串,避免后面 JSON.parse 空值报错 */
const token = localStorage.getItem('token') || ''

/* 1. 请求拦截器:每次请求发送之前都会走这里 */
axios.interceptors.request.use(
  function (config) {
    // 只有拿到了 token 才往请求头里加;登录页还没有 token,加了会出错
    if (token) {
      // 官方推荐用中括号语法
      config.headers['authorization'] = token
    }
    return config // 必须 return,否则请求发不出去
  },
  function (error) {
    return Promise.reject(error)
  }
)

/* 2. 响应拦截器 */
axios.interceptors.response.use(
  function (response) {
    // 数据剥离:直接返回 response.data,页面里少写一层 .data
    return response.data
  },
  function (error) {
    // 超出 2xx 的状态码走这里,统一处理 token 失效
    if (error.response && error.response.status === 401) {
      showToast('请重新登录')
      // 清除缓存
      localStorage.removeItem('token')
      localStorage.removeItem('username')
      // 返回登录页(延迟一会儿,让提示框显示)
      setTimeout(() => {
        location.href = 'login.html'
      }, 1500)
    }
    return Promise.reject(error) // 必须 return,错误才能继续抛给页面
  }
)

要点

  • axios.interceptors.request.use(成功回调, 失败回调),两个回调都必须 return:return config 把请求配置交回去,return Promise.reject(error) 把错误继续抛出,删掉这两个 return 请求和响应都会"失效"。
  • config.headers['authorization'] = token 用中括号写法(官网推荐),不要写成 config.headers.authorization。
  • 请求拦截器里一定要先 if (token) 再添加:登录页此时还没登录,没有 token,直接添加会拿到 undefined。
  • return response.data 之后,页面里拿到的 res 就已经是后端返回的 { message, data },所有用到的地方必须同步少点一次 .data,否则数据全是 undefined。
  • error.response 在网络断开时是 undefined,判断 401 前加个 error.response && 更稳。

2. login.html —— 登录页(配合数据剥离后的取值) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>登录</title>
    <style>
      .login-form { width: 320px; margin: 80px auto; display: flex; flex-direction: column; gap: 12px; }
      .login-form input, .login-form button { height: 36px; padding: 0 10px; }
    </style>
  </head>
  <body>
    <form class="login-form">
      <input type="text" name="username" placeholder="用户名(8-30位)" />
      <input type="password" name="password" placeholder="密码(6-30位)" />
      <button type="button" id="btn-login">登录</button>
    </form>

    <!-- 依赖:项目中的 axios、form-serialize,以及公共的 commons.js -->
    <script src="./lib/axios.min.js"></script>
    <script src="./lib/form-serialize.js"></script>
    <script src="./commons.js"></script>
    <script>
      document.querySelector('#btn-login').addEventListener('click', async () => {
        // 1. 收集并校验数据
        const form = document.querySelector('.login-form')
        const data = serialize(form, { empty: true, hash: true })
        const { username, password } = data

        // 非空判断
        if (username === '' || password === '') {
          showToast('用户名和密码不能为空')
          return
        }
        // 格式判断
        if (username.length < 8 || username.length > 30 || password.length < 6 || password.length > 30) {
          showToast('用户名长度8-30,密码长度6-30')
          return
        }

        // 2. 提交数据
        try {
          const res = await axios.post('/login', { username, password })
          // 剥离后 res 就是 { message, data },直接 res.message
          showToast(res.message)
          // 3. 缓存响应数据
          localStorage.setItem('token', res.data.token)
          localStorage.setItem('username', res.data.username)
          // 4. 跳转首页(延迟一会儿,让提示框显示)
          setTimeout(() => {
            location.href = './index.html'
          }, 1500)
        } catch (error) {
          // 拦截器里 return Promise.reject(error) 后,这里拿到的仍是原始 error
          showToast(error.response.data.message)
        }
      })
    </script>
  </body>
</html>

要点

  • 成功分支里 res.message、res.data.token:剥离的是 axios 最外层包装的那层 data,后端自己的 data 字段还在,所以 res.data.token 保留、res.data.data.token 才是多余的。
  • catch 里的 error 是错误对象,不是响应数据,必须写 error.response.data.message,写成 error.message 拿不到后端提示。
  • 登录成功后 localStorage.setItem('token', ...) 的 key 必须和 commons.js 里 localStorage.getItem('token') 的 key 一致,否则请求拦截器永远拿不到 token。
  • setTimeout 延迟跳转是为了让 showToast 提示能被看到,不加会"一闪而过"。

3. index.html —— 首页统计数据渲染(移除多余的 .data) ​

html
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>首页</title>
    <style>
      .card { display: flex; gap: 24px; margin: 40px; }
      .card div { padding: 16px 24px; border: 1px solid #ddd; border-radius: 8px; }
    </style>
  </head>
  <body>
    <div class="card">
      <div>用户总数:<span class="userTotal"></span></div>
      <div>今日新增:<span class="todayNew"></span></div>
    </div>

    <script src="./lib/axios.min.js"></script>
    <script src="./commons.js"></script>
    <script>
      // 首页-统计数据(登录成功之后才可以调用)
      async function getData() {
        const res = await axios({
          url: '/dashboard'
          // 通过 axios 请求拦截器统一携带 token,这里不用再写
        })
        // 剥离前:res.data.data.overview;剥离后:res.data.overview
        const overview = res.data.overview

        // 渲染数据:key 就是页面元素的类名
        Object.keys(overview).forEach((key) => {
          document.querySelector(`.${key}`).innerText = overview[key]
        })
      }

      getData()
    </script>
  </body>
</html>

要点

  • 数据剥离后最容易错的一行就是这里:res.data.data.overview → res.data.overview。改完拦截器后如果页面数字不显示,先回来检查这里有没有多写一层 .data。
  • Object.keys(overview).forEach 的 key 要和 HTML 里元素的 class 一一对应,querySelector('.userTotal') 找不到元素会报 Cannot set properties of null。
  • 首页调用接口时不需要再手动加 token,请求拦截器已经统一携带;但前提是 commons.js 在 index.js 逻辑之前引入且 localStorage 里有 token。
  • 三处(登录、注册、首页)要一起改:注册页 res.data.message → res.message,登录页 res.data.data.message → res.message,首页 res.data.data.overview → res.data.overview,漏改一处就会出现局部数据为 undefined。

常见错误 ​

1. 响应拦截器成功回调忘了 return,或 return 了整个 response

  • 现象:所有接口 res 变成 undefined,页面数据全部不渲染;或"剥离没生效",业务里仍要写两次 .data。
  • 原因:interceptors.response.use 第一个回调的返回值就是后续 await 拿到的东西,不写 return 等于返回 undefined;写成 return response 则根本没剥离。
  • 修法:成功回调固定 return response.data;;失败回调固定保留 return Promise.reject(error);,这两个 return 都不能删。

2. 只改了拦截器,没同步改业务代码里的 .data

  • 现象:首页统计数字一直停在默认值(如 15500),或报 Cannot read properties of undefined (reading 'overview')。
  • 原因:拦截器剥掉了一层,业务里仍按两层写 res.data.overview、res.data.message。
  • 修法:全局搜 res.data.,逐个改成 res.,重点是 login.js、register.js、index.js 三处;error.response.data.message 属于错误分支,不走成功回调,保持不动。

3. 请求拦截器无条件加 token

  • 现象:未登录打开 login.html,控制台报 JSON.parse 或 Cannot read properties of null,登录请求发不出去,页面卡在登录页。
  • 原因:localStorage 里没有 token 时 getItem 返回 null,直接 JSON.parse 或直接取 .token 就报错。
  • 修法:先取再判断——const data = JSON.parse(localStorage.getItem('userMsg') || '{}'),再 if (token) config.headers.Authorization = token,只在有 token 时才设置请求头。

4. token 读取散落各处、执行时机错乱

  • 现象:控制台打印出来的 data 是空对象,刚登录成功却取不到 token。
  • 原因:取值语句写在 common.js 不同位置,且 common.js 与业务 js 的引入顺序不一致,执行时缓存还是空的。
  • 修法:在 common.js 最外层统一取一次并转成对象,业务里直接解构;确认 common.js 在所有业务 js 之前引入。

5. 401 处理写了两份

  • 现象:token 过期后刷新页面,连弹两次"请重新登录",或跳转两次、先跳走又被打断。
  • 原因:拦截器里已加 401 统一处理,index.js 里旧的 try/catch 判断 401 没删。
  • 修法:删掉业务层的 401 分支、手动清缓存和跳转代码,只留拦截器一份;判断写成 error.response.status === 401(不是 error.status,也不是 error.response.data.status)。

6. 推送显示成功,远程仓库首页还是空的

  • 现象:本地 git push 无报错,刷新 gitee 仓库页面没有文件,别人 clone 下来是空目录。
  • 原因:没 git add . + git commit 就 push;或本地分支是 master、远程默认分支却是 main;或推的分支不是默认分支。
  • 修法:先 git status 确认已提交,再 git push -u origin 当前分支名,用 git branch -a 核对本地与远程分支名,必要时在仓库设置里改默认分支。

7. clone 时复制错地址,或仓库没开源

  • 现象:git clone 报仓库不存在 / 403;别人打开链接看不到代码。
  • 原因:复制的是浏览器地址栏 URL,而不是"克隆/下载"里的 HTTPS 地址;仓库未设为开源。
  • 修法:只复制 HTTPS 克隆地址;在仓库"管理"里勾选开源选项(仓库需先有内容)。

练习 ​

练习 1|请求拦截器 + 未登录不报错 在 common.js 中写请求拦截器:从缓存取用户信息,有 token 才写入请求头,无 token 直接放行。验收:清空缓存后打开 login.html,控制台无报错,登录请求能正常发出并在 Network 的请求头中看不到 Authorization。

练习 2|数据剥离后修复三处 给响应拦截器成功回调加数据剥离,然后同步修改 login.js、register.js、index.js 的数据使用逻辑。验收:登录、注册提示正常,首页统计数字正常渲染,全局搜索 res.data. 无残留。

练习 3|模拟 401 统一处理 登录后手动篡改 localStorage 中的 token 再刷新首页,观察行为。验收:只弹一次"请重新登录",1.5 秒后跳回登录页,缓存被清空;index.js 中不再有 401 判断代码。

练习 4|新建仓库并推送 在 gitee 新建仓库,把本地项目推上去,再在另一个目录 git clone 下来运行。验收:远程仓库首页能看到文件列表,克隆下来的项目能正常运行,git log 中有完整提交记录。

练习 5|分支核对 在克隆下来的项目中执行 git branch -a,找到远程的 dev 分支并切换过去。验收:能说明本地分支与 remotes/origin/xxx 的区别,切换命令只写分支名即可成功。

原视频对照 ​

#视频时长
1总结-JWT验证过程02:16
2案例-增加请求拦截器09:49
3案例-请求拦截器完成09:14
4案例-添加响应拦截器08:20
5案例-响应拦截器-数据剥离07:22
6远程仓库介绍05:16

小结 ​

  1. 拦截器回调里的 return 就是"交给下游的东西",成功回调不 return 或 return 错对象,全站接口一起挂,排查时先看这一行。
  2. 数据剥离是一次"契约变更",改拦截器的同时必须同步改所有调用点,否则症状是"接口有返回但页面不更新"。
  3. 请求拦截器加 token 必须带存在性判断,未登录态是必测场景,不能只在登录后的页面上验证。
  4. 缓存读取统一收口到 common.js 最外层,散落读取 + 脚本加载顺序是"取到空对象"类问题的主要来源。
  5. 公共逻辑一旦上移到拦截器,业务层同类代码必须删除,否则会出现重复提示、重复跳转这类"看起来像 bug 的实现问题"。
  6. 401 的判断路径是 error.response.status,错误分支拿到的是 error 而不是 response,这点和成功分支完全对称。
  7. 远程仓库的价值是备份 + 协作,push 前先 git status 看有没有未提交内容,分支名不一致是"推了但看不到"的第一嫌疑。
  8. 遇到"代码没生效"先别改逻辑,按三步定位:拦截器有没有执行(打日志)→ 返回值对不对(打印结构)→ 调用点有没有跟着改。