【JS代码分析】前端鉴权基础:Token 的本地存储与状态重置实践

在前端日常开发中,用户鉴权是绕不开的环节。出于体验考虑,我们通常会将用户的登录凭证(Token)持久化到浏览器的本地存储(localStorage)中,以便在刷新页面或重新打开标签页时恢复用户的登录状态。

为了方便调用,我们通常会对 localStorage 的原生方法进行二次封装。但是在实现"退出登录"功能时,有一个经常被忽视的细节:清除 Token 时,到底该用 localStorage.removeItem彻底删除,还是用 localStorage.setItem**('') 覆盖一个空值?**

很多开发者觉得二者效果一样,甚至为了少写一个封装方法,直接用 setToken('') 来代替删除操作。本文将结合实际代码,深入剖析这个容易搞错的细节,告诉你为什么在状态重置时,必须选择"彻底删除"。

代码场景复现

假设我们有以下最常见的 Token 管理封装代码:

javascript 复制代码
// 封装本地数据存储写入的方法:
export const setToken = (token: string) => {
  localStorage.setItem('TOKEN', token)
}

// 封装本地数据存储读取的方法:
export const getToken = () => {
  return localStorage.getItem('TOKEN')
}

// 封装本地数据存储删除的方法:
export const removeToken = () => {
  localStorage.removeItem('TOKEN')
}

在退出登录的逻辑中,我们有两种看似等价的做法:

  • 做法 A(推荐) :调用 removeToken()
  • 做法 B(伪删除) :调用 setToken('')

表面上看,执行完这两种操作后,再去读取 Token,都无法拿到有效的登录凭证。那么它们真的可以互相替代吗?

深度分析:为什么不能使用 setToken('')

如果深入探究底层数据结构和实际业务场景,这两种操作有着本质的区别。使用 setToken('') 不仅会留下数据残留,还会埋下诸多隐患。

1. 底层存储机制的差异:物理删除 vs 逻辑覆盖
  • removeToken() :调用 localStorage.removeItem,会从浏览器本地存储中物理删除 TOKEN 这个键。此时不仅值没了,键本身也不存在了。
  • setToken('') :调用 localStorage.setItem,会保留 TOKEN 这个键,只是将其值覆盖 为空字符串 ""

打开浏览器的开发者工具 -> Application -> Local Storage,你能直观地看到区别:前者干干净净,后者会残留一条 "TOKEN": "" 的垃圾数据。

2. 返回值的本质区别:null vs ""

由于底层存储状态的差异,后续通过 getToken() 读取时,得到的返回值完全不同:

javascript 复制代码
removeToken()
console.log(getToken())  // 输出: null

setToken('')
console.log(getToken())  // 输出: "" (空字符串)
3. 业务代码中的隐藏 Bug

正是由于返回值 null"" 的不同,在结合具体的业务逻辑时,setToken('') 极易引发难以排查的 Bug。

隐患一:条件判断失效

虽然在 JavaScript 中 null"" 都是 falsy 值(if (token) 都能正确拦截),但如果是涉及严格判断,就会踩坑:

javascript 复制代码
const token = getToken()
// 如果同事写了这种严格不等于 null 的判断
if (token !== null) { 
  // 如果用 setToken(''),此时 "" !== null 为 true,会错误进入此分支!
  // 导致误认为用户还处于登录状态
}

隐患二:JSON.parse 解析报错

在复杂业务中,我们有时会把多个用户信息打包成 JSON 字符串存在 Token 里。如果调用了 setToken(''),后续读取时直接解析就会引发白屏崩溃:

javascript 复制代码
setToken('')
JSON.parse(getToken()!)  
// ❌ 报错: SyntaxError: Unexpected end of JSON input

removeToken()
const t = getToken()
if (t) JSON.parse(t)     // ✅ null 是 falsy 值,安全跳过解析

隐患三:Axios 请求拦截器发送脏数据

在全局请求拦截器中,我们通常会自动为请求头携带 Token。退出登录后如果只是用空字符串覆盖,可能会向后端发送残缺的请求头:

javascript 复制代码
axios.interceptors.request.use(config => {
  const token = getToken()
  // 如果不巧写成了严格判断
  if (token !== null && token !== undefined) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})

执行 setToken('') 后,拦截器会向后端发送 Authorization: Bearer 这样的无效头部。后端中间件解析到空的 Bearer 值,可能会抛出 401 Unauthorized 异常,导致退出登录后控制台依然报一堆红色的网络错误。

最佳实践与总结

综上所述,删除就是删除,切勿用空值污染存储

在处理前端鉴权与状态重置(如退出登录)时,标准的最佳实践应该是:

javascript 复制代码
export const logout = () => {
  // 1. 彻底物理清除本地 Token
  removeToken()
  
  // 2. 重置全局状态(如 Pinia / Redux 中的用户信息)
  // userStore.$reset()
  
  // 3. 跳转到登录页
  router.push('/login')
}

总结:

  1. removeToken() 是真正的状态重置,而 setToken('') 只是状态覆盖。
  2. 封装本地存储方法时,setgetremove 三个方法缺一不可,不要为了少写几行代码而埋下隐患。
  3. 始终使用 removeItem 来清理本地凭证,保持本地存储的整洁与业务逻辑的严谨。
相关推荐
颜进强1 小时前
14 - OpenSpec 老页面改造骨架:定位 + 增量 + 回归三件套
前端·后端·ai编程
做前端的娜娜子1 小时前
async/await 错误处理:try...catch vs .catch() 完全指南
前端·面试·掘金·金石计划
半个落月1 小时前
React 性能优化入门:用 memo 避免无关的子组件重复渲染
前端·react.js
wuyk5551 小时前
7.AVL 树:第一个自平衡二叉搜索树
开发语言·stm32·单片机·算法
北城笑笑1 小时前
Server 18 ,Nginx + Vite 前端部署排错实战:从 `/api/gpt/chat` 404 到 9012 后端服务的完整定位过程
linux·运维·前端·nginx·ubuntu·vue
江华森1 小时前
从抓包到嗅探:用 C 语言把《计算机网络》第 9-13 章全部跑一遍
前端
SomeB1oody1 小时前
【RustyML入门】5.3. 聚类指标
开发语言·后端·机器学习·rust·教程
OPEN-F1 小时前
Python进阶教程:Web开发入门(Flask)
开发语言·python
神仙别闹1 小时前
基于C++ MFC实现动态分区分配存储管理
开发语言·c++·mfc