第1个
getters
在 Vuex 或 Pinia(Vue 的状态管理库)中,Getters 可以简单理解为 "状态仓库(Store)里的计算属性(Computed)" 。
它的核心作用是:基于已有数据(State)派生/计算出新的数据 ,并且具备缓存特性。
一、 为什么需要 Getters?
假设你的状态仓库(Store)里存了一个用户列表数据 users:
JavaScript
yaml
state: {
users: [
{ id: 1, name: '张三', age: 16 },
{ id: 2, name: '李四', age: 22 },
{ id: 3, name: '王五', age: 19 }
]
}
如果你在 项目里的 5 个不同页面 中,都需要获取"所有成年用户(age >= 18)",你该怎么办?
-
❌ 不用 Getters :你需要在 5 个页面组件里分别写
filter逻辑去过滤数据。万一以后"成年"的判定标准改成了 20 岁,你需要手忙脚乱地去修改 5 个页面的代码。 -
✅ 使用 Getters :把"过滤成年人"的逻辑抽离并写在 Store 的
getters中,所有页面直接读取这一个 Getter 结果即可。实现一次定义,处处共享。
二、 Getters 的三大核心特性
-
响应式与缓存:只要它依赖的 State 数据没有发生改变,多次读取同一个 Getter 会直接从缓存中取值,无需重复计算,性能极高。
-
逻辑集中:将数据的二次加工/计算逻辑统一收拢在 Store 内部,不把脏逻辑留在页面组件中。
-
只读性 :Getters 计算出的数据是只读的,不能直接修改,只能通过修改它依赖的底层的 State 数据来间接更新。
三、 在 Vuex 与 Pinia 中的代码示例
1. 在 Vuex 中定义与使用
JavaScript
php
// 在 store/index.js 中定义
const store = createStore({
state: {
users: [
{ id: 1, name: '张三', age: 16 },
{ id: 2, name: '李四', age: 22 }
]
},
getters: {
// 1. 基本用法:第一个参数默认就是 state
adultUsers: (state) => {
return state.users.filter(user => user.age >= 18)
},
// 2. 传参用法(返回一个函数):根据动态参数筛选
getUserById: (state) => (id) => {
return state.users.find(user => user.id === id)
}
}
})
在组件内部调用:
JavaScript
javascript
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
// 读取 getters 数据
const adultList = computed(() => store.getters.adultUsers)
// 调用可以传参的 getter
const userTwo = computed(() => store.getters.getUserById(2))
2. 在 Pinia(现代 Vue 3 推荐)中定义与使用
在 Pinia 中,Getters 的语法更加直观,直接使用 this 即可读取同一 store 下的各种数据:
JavaScript
javascript
// stores/user.js
import { defineStore } from 'pinia'
export const useUserStore = defineStore('user', {
state: () => ({
users: [
{ id: 1, name: '张三', age: 16 },
{ id: 2, name: '李四', age: 22 }
]
}),
getters: {
// 可以直接使用 this 访问当前 state 或其他 getter
adultUsers() {
return this.users.filter(u => u.age >= 18)
}
}
})
在组件内部调用:
JavaScript
javascript
import { useUserStore } from '@/stores/user'
const userStore = useUserStore()
// 直接点出来使用,天然保持响应式
console.log(userStore.adultUsers)
四、 快速记忆总结
| 概念/属性 | Vue 基础概念 | Vuex / Pinia 中的对应概念 |
|---|---|---|
| 存储原始数据 | 组件的 data() / ref() |
state |
| 计算衍生数据 | 组件的 computed |
getters |
| 同步修改数据 | 组件的 methods |
mutations(Pinia 已废弃,合并入 Actions) |
| 异步/业务逻辑 | 组件的 methods |
actions |
第2个
怎么看 Apifox 接口文档
一、 第一步:确定地址,接口类型功能
| 区域标识 | 含义与关注点 | 大白话解读 |
|---|---|---|
| 接口名称 | 接口是干嘛的(如:"用户登录"、"获取文章列表")。 | 知道了这个接口的功能。 |
| 请求方式 (Method) | 常见的有 GET / POST / PUT / DELETE / PATCH。 |
告诉你用哪种"动作"发请求。 • GET :查数据(拿菜单) • POST :提交新数据(提交订单) • PUT/PATCH :修改数据(修改备注) • DELETE:删除数据(取消订单) |
| 接口路径 (URL/Path) | 比如 /api/user/login。 |
接口的相对路径。结合左上角/顶部的前置 URL (基准地址,如 [https://api.example.com](https://api.example.com)),拼接起来就是完整的请求地址。 |
二、 第二步:看"请求参数 (Request)"(你必须给后台什么)
这是整个文档最重要的部分之一。它决定了你前端需要用表单收集哪些数据传给后台。
Apifox 里的请求参数通常分为以下几种标签页(Tabs):
1. Header(请求头)
-
看什么:传给服务器的"附加身份信息"。
-
常见字段 :
Authorization或token(用来证明你登录了)。 -
小白注意:很多需要登录才能调用的接口,文档里会注明需要在 Header 传 Token。
2. Params / Query(查询参数)
-
看什么 :通常出现在
GET请求 中,参数会直接拼在网址后面(如/search?keyword=vue&page=1)。 -
表格关键列:
-
参数名:写代码时传的键(Key)。
-
是否必填:带有红点或写着"必填"的,不传后台就会报错。
-
类型 :
string(文本)、integer/number(数字)等。 -
说明/示例:告诉你这个参数是干嘛的(如:"页码,默认1")。
-
3. Body(请求体)
-
看什么 :通常出现在
POST/PUT/PATCH请求 中,用来提交大量或复杂的数据。 -
数据格式类型:
-
json(最常见) :格式形如{"username": "admin", "age": 18}。 -
form-data:常用于文件上传(带图片/文档)或普通表单。 -
x-www-form-urlencoded:传统的表单格式。
-
三、 第三步:看"返回参数 / 响应数据 (Response)"(后台会给你什么)
当你的请求发过去后,后台会给你吐出一段 JSON 数据。这里展示了数据的结构规范。
1. HTTP 状态码(如 200 OK)
200表示网络请求成功到达了服务器。
2. 响应 Body(数据结构)
后台返回的 JSON 数据通常遵循统一的"三段式"套路:
JSON
json
{
"code": 0, // 1. 业务状态码(注意:非 HTTP 状态码,通常 0 或 200 代表业务成功)
"message": "success",// 2. 提示信息(如:"登录成功" 或 "密码错误")
"data": { ... } // 3. 核心数据(具体的业务对象、列表等)
}
-
怎么看树状结构表格?
在 Apifox 中,返回参数也是以表格形式列出的。如果
data是一个对象,下面缩进展开的就是data内部的属性字段(比如data.token,data.userInfo.nickname)。你写页面渲染时,就从data里面拿对应的字段名。
四、 第四步:看"请求示例代码 / 示例"(拷贝即用)
Apifox 页面右侧(或下方)通常有巨大的效率神器:
-
示例(Examples / 响应示例) :
-
包含"成功示例"和"失败/异常示例"。
-
作用:直观地展示真实的返回数据长什么样。前端写 Mock 数据或者对接口时直接看这个最快。
-
-
生成代码 (Generate Code) :
-
Apifox 可以自动将当前接口转换为各种语言的代码片段!
-
切换到 JavaScript -> Axios 或 Fetch,复制代码直接贴到你的 Vue/React 项目里就能跑。
-
五、 第五步:使用"调试 (Apifox 核心优势)"(在线测试接口)
如果你不确定接口好不好使,或者后台有没有开发完,不需要写任何代码 ,直接点击页面的 "调试" 按钮(或标签页):
-
在参数输入框里填入测试数据(如用户名
test,密码123456)。 -
点击右侧鲜艳的 "发送 (Send)" 按钮。
-
观察下方出来的 "响应结果 (Response)" :
-
如果返回了符合预期的 JSON,说明后台接口是通的,参数没问题。
-
如果报错(如
404地址错了,401没登录,500后台报错),你可以拿着错误信息直接找后台沟通。
-
💡 小白看文档速记指南(5 步走)
当你接到一个新接口任务时,按这个顺序看:
-
看名字和动作 :这是啥接口?用
GET还是POST? -
看请求地址:完整 URL 是什么?
-
看必填参数 :是在
Params还是在Body?哪些字段写着必填? -
看返回结果 :成功时
code是多少?要拿的数据在data的哪个字段里? -
点"调试"测一下:点发送按钮自己调一次,看到真实返回数据心里就有底了。
第3个
prettier,eslint,husky,pnpm
Prettier 是一款开源的代码格式化工具(Opinionated Code Formatter)。它的主要作用是自动重新排版你的代码,使其遵循统一、规范的视觉风格。
1. 它解决什么问题?
在团队开发或个人编写代码时,经常会出现以下痛点:
-
缩进与风格混乱:有人习惯 2 个空格,有人习惯 4 个空格;有人喜欢单引号,有人喜欢双引号。
-
无谓的争论:在 Code Review(代码审查)中,开发者常为"大括号要不要换行"、"句末要不要加分号"等格式细节争吵,浪费时间。
-
手动对齐低效:手动调整换行和缩进既繁琐又容易出错。
Prettier 接入后,开发者只需保存文件(Ctrl + S / Cmd + S),代码就会自动变整洁,无需人工干预。
2. 主要特性
-
强主见(Opinionated) :Prettier 故意只保留极少数可配置项(如缩进宽度、单双引号、句末分号等)。它的理念是"放弃争议,接受统一",直接帮你决定绝大多数格式规则。
-
语法感知(Syntax-Aware) :它不是简单的正则替换,而是先将代码解析为抽象语法树(AST),擦除原有的所有格式,再根据当前行宽(Line Length)重新优雅地打印出来。如果一行太长,它会自动拆分成多行。
-
多语言支持:原生支持 JavaScript、TypeScript、JSX、CSS/SCSS/Less、HTML、Vue、Angular、JSON、Markdown、YAML、GraphQL 等。
3. Prettier vs. ESLint 的区别
许多初学者容易混淆两者的定位:
| 工具 | 主要职责 | 关注重点 | 示例 |
|---|---|---|---|
| Prettier | 格式化 (Formatting) | 代码"长得好看" | 最大行宽、缩进空格数、单双引号、末尾逗号 |
| ESLint | 代码质量 (Linter) | 代码"写得正确" | 检查未使用的变量、未处理的 Promise、禁止潜在 Bug 的写法 |
最佳实践:通常在项目中同时使用两者,用 ESLint 检查代码质量,用 Prettier 负责纯粹的格式化。
4. 常见使用场景
-
编辑器插件 :在 VS Code 或 WebStorm 中安装 Prettier 插件,开启
Format On Save(保存时自动格式化)。 -
Git Hook (Husky / lint-staged) :在提交代码(
git commit)时自动运行 Prettier,确保提交到仓库的代码始终保持规范。 -
CI/CD 流水线:在自动化构建流程中检查代码格式是否合规。
husky和pnpm
Husky 和 pnpm 是前端工程化体系中非常核心的两个工具。简单来说:
-
pnpm 负责管理项目依赖(比 npm/yarn 更快、更省空间)。
-
Husky 负责拦截 Git 操作(在提交代码前自动运行检查或格式化)。
下面分别拆解它们的核心作用与使用场景:
1. pnpm:新一代高效包管理器
pnpm(Performance npm)是一个旨在替代 npm 和 yarn 的 JavaScript 包管理工具。
核心优势
-
极其节省磁盘空间(硬链接 + 软链接) :
-
传统 npm/yarn :每个项目安装依赖时,都会在本地磁盘的
node_modules中下载一套完整的副本。如果有 10 个项目都用了 React,磁盘上就会有 10 份相同的 React 文件。 -
pnpm :会将所有依赖全局存储 在本地的一个"全局内容寻址存储库(Global Store)"中。项目中的
node_modules只是指向全局 Store 的硬链接(Hard Link)和软链接(Symbolic Link) 。无论创建多少个项目,同一版本的包在磁盘上永远只占一份空间。
-
-
安装速度极快:
因为避免了重复下载和复杂的复制文件过程,安装依赖的速度通常是 npm 和 yarn 的数倍。
-
严格的依赖隔离(解决"幽灵依赖"问题) :
-
在 npm/yarn 中,依赖会被"扁平化 (Flat)"展开到
node_modules根目录。即使你的package.json只声明了 A 包,但如果 A 包依赖了 B 包,你依然可以在项目中直接import B(即幽灵依赖)。一旦 A 包未来删除了对 B 的依赖,你的项目就会崩溃。 -
pnpm 通过符号链接构建非扁平的
node_modules结构,项目只能引用在package.json中明确声明过的依赖,大幅提升代码安全性。
-
-
原生支持 Monorepo(多包/多项目管理) :
pnpm 内置了
pnpm-workspace.yaml,不需要额外安装复杂工具就能轻松在一个仓库里管理多个子项目。
1. 什么是"扁平化(Flat)"?
在早期的 npm (v2) 中,依赖是嵌套 安装的。如果你的项目依赖 A,A 依赖 B,node_modules 结构会像套娃一样:
Plaintext
css
node_modules/
└── pkg-A/
└── node_modules/
└── pkg-B/
这种结构会导致目录层级极深、路径过长(在 Windows 上经常报错),且大量的包会被重复下载。
为了解决这个问题,npm v3+ 和 yarn 引入了"扁平化(Flat)"策略:不管这个包是你直接引用的,还是你依赖的包引用的,通通提拔到 node_modules 的最顶层。
于是,目录结构变成了这样:
Plaintext
css
node_modules/
├── pkg-A/ <-- 你声明的依赖
└── pkg-B/ <-- A 依赖的包,被平铺到了最外层!
2. Node.js 的模块查找机制
当你在代码里写 import B from 'pkg-B' 时,Node.js 会去哪里找 pkg-B 呢?
它的查找规则非常简单:直接去离当前文件最近的 node_modules 根目录下找名为 pkg-B 的文件夹。
既然因为"扁平化",pkg-B 已经被放到了 node_modules 的根目录下,Node.js 就能顺手把它查出来,代码就能正常运行并打印数据!
3. 为什么这被称为"幽灵依赖"?
你的 package.json(类似于你的采购清单)里明明只写了依赖 pkg-A:
JSON
css
{
"dependencies": {
"pkg-A": "^1.0.0"
}
}
按照规范,你只应该 在代码里 import A。
但是,因为扁平化把 pkg-B 偷摸放到了根目录,你居然可以在代码里直接 import B from 'pkg-B',而且程序还能正常运行!
这个 pkg-B 就像一个未被记录、却幽灵般存在 的依赖------这就是幽灵依赖。
4. 致命危险:为什么说"项目会崩溃"?
你可能会想:"能直接用不是挺好的吗?我还少写了一个 pnpm add pkg-B。"
危险发生在未来 pkg-A 升级的时候:
-
今天 :
pkg-A的作者更新到了 v2.0,重构了代码,觉得pkg-B不好用,删掉了对pkg-B的依赖。 -
明天:C 本来也用了 B,但是因为之前幽灵依赖安装了,不会报错;但A如果不再用B,那么重新npm install就不会有B,而package.json一直没有声明B,C import B就会报错。
-
结果:
-
npm 重新计算依赖,发现没有任何包再依赖
pkg-B了。 -
npm 在安装时就会把
node_modules/pkg-B彻底删掉。 -
当你的代码运行到
import B from 'pkg-B'时,直接报红崩溃:Cannot find module 'pkg-B'(找不到模块)。
-
5. pnpm 是如何解决这个问题的?
pnpm 不采用简单粗暴的"把所有包平铺在根目录"的方式,而是使用了软链接(Symlink,新inode编号,存数据地址)结构:(总之就是不flat,声明什么安装什么)
Plaintext
css
node_modules/
├── .pnpm/ <-- 真实的包全在这里
└── pkg-A/ <-- 根目录下只放你 package.json 里声明的 pkg-A
-
你的
node_modules根目录下,只有你在package.json里写的pkg-A。 -
pkg-B被隐藏在.pnpm的深层结构中。 -
如果你在代码里尝试
import B from 'pkg-B',Node.js 在node_modules根目录下找不到pkg-B,在开发阶段就会直接报错提示你未安装该依赖,从而将隐患直接扼杀在摇篮里!
在操作系统中,硬链接(Hard Link)和软链接(Symbolic Link / Symlink,即符号链接/快捷方式)是两种指向文件的指针机制。
要彻底搞懂它们,我们需要先了解操作系统是如何存储文件的:
在 Linux/Unix 文件系统中,一个文件由两部分组成:
-
数据块(Data Block) :真正存储文件内容的地方(比如硬盘上的具体二进制数据)。
-
索引节点(Inode,Index Node) :存储文件的元数据(如文件大小、创建时间、文件所有者等)以及指向数据块的指针。每个文件都有一个独特的 Inode 编号。
而我们平常看到的"文件名",本质上只是一个映射关系(类似钥匙) ,指向对应的 Inode。
1. 硬链接(Hard Link)
硬链接本质上是为同一个 Inode 编号创建了一个新的文件名。
-
原理 :创建一个硬链接,就像是给同一个文件起了个"别名/名片"。原文件名和硬链接文件名指向的是同一个 Inode ,对应同一份物理数据。
-
特点:
-
删除不影响:因为两个文件名指向同一个 Inode,删除原文件,只是删除了其中一条"映射"。只要还有一个硬链接存在,文件数据就不会丢失。
-
完全平级:原文件和硬链接在地位上没有任何区别,无法区分谁是"原始文件"。
-
不占额外空间:因为没有创建新的数据块,只是多了一个映射名称。
-
-
局限:
-
不能跨文件系统/跨磁盘分区创建。
-
不能为目录(文件夹)创建硬链接(防止形成无限循环的目录环路)。
-
💡 现实比喻:
给一个人起小名。张三(原文件名)的小名叫三儿(硬链接)。无论是找到"张三"还是"三儿",指的都是同一个人(物理数据)。哪怕把"张三"这个名字废弃了,"三儿"这个人依然真实存在。
2. 软链接(Soft Link / Symlink / 符号链接)
软链接相当于 Windows 系统里的"桌面快捷方式"。
-
原理 :软链接本身是一个全新的独立文件,它有自己独立的 Inode 编号 。软链接的数据块里不存具体内容,而是专门存储目标文件的绝对或相对路径字符串。
-
特点:
-
依赖原路径:当访问软链接时,系统会读取软链接里记录的路径,然后再去寻找原文件。
-
原文件删除即失效:如果删除了原文件(或者把原文件重命名/移动了),软链接就会变成一个找不到目标的"死链接(Broken Link)"。
-
功能更广泛:可以跨文件系统创建,也可以为目录(文件夹)创建软链接。
-
-
局限:
- 会占用极小的物理空间(用于保存目标路径字符串)。
💡 现实比喻:
一张纸条上写着:"请前往北京市朝阳区建国门外大街1号"。你顺着纸条上的地址找到那栋楼(原文件)。如果那栋楼被拆了(原文件被删),纸条(软链接)还在,但你顺着地址找过去只能找到一片废墟。
3. 对比总结表
| 特性 | 硬链接 (Hard Link) | 软链接 (Soft Link / Symlink) |
|---|---|---|
| 本质 | 同一 Inode 的额外文件名(别名) | 一个指向目标路径的新文件(快捷方式) |
| Inode 编号 | 与原文件 相同 | 与原文件 不同 |
| 删除原文件 | 数据仍可用,可以通过硬链接访问 | 软链接报废(死链接) |
| 是否支持目录 | ❌ 不支持 |
2. Husky:Git Hooks 自动化管理工具
Husky 专门用来简化 Git Hooks(Git 钩子) 的配置与管理。
核心作用与场景
Git 提供了许多原生钩子(例如在 git commit 或 git push 时触发的脚本),但默认情况下,.git/hooks 文件夹下的配置无法同步提交到 Git 远程仓库。这意味着你无法让团队里的每个成员都自动共享这些检查规则。
Husky 的作用就是将 Git Hooks 的配置纳入版本控制,让团队所有人在 clone 项目并运行安装脚本后,自动具备相同的 Git 检查机制。
常见的配合链条(最佳实践)
Husky 通常不单独工作,而是与 lint-staged 、Prettier 和 Commitlint 协同作业:
-
提交前自动格式化(
pre-commit钩子) :-
流程 :当你输入
git commit时,Husky 触发pre-commit钩子。 -
动作 :借助 lint-staged (只针对本次
git add暂存区的文件进行检查,提升速度),自动运行 Prettier 格式化代码,运行 ESLint 检查错误。如果检查不通过,直接中断提交。 -
结果:保证进入 Git 历史的代码永远是格式化且无语法错误的。
-
-
规范 Commit 信息格式(
commit-msg钩子) :-
流程 :在输入提交信息时,Husky 触发
commit-msg钩子。 -
动作 :调用 Commitlint 校验你的提交日志格式是否符合规范(如
feat: 新增登录功能或fix: 修复首页卡顿)。 -
结果 :防止出现
git commit -m "fix"、git commit -m "111"这种无意义的提交记录。
-
3. 三者(Prettier + Husky + pnpm)在实际项目中的组合工作流
在一个标准的现代前端项目中,它们通常是这样协同工作的:
-
你使用 pnpm 安装依赖并管理项目的开发环境。
-
你修改了代码并保存,Prettier 插件在编辑器内帮你完成初步格式化。
-
你执行
git add .将更改加入暂存区。 -
你执行
git commit -m "feat: 新增首页图表"。 -
Husky 拦截该提交,触发
pre-commit和commit-msg钩子:-
对暂存区的代码再次运行 Prettier 和 ESLint,防止漏网之鱼。
-
检查 commit 信息是否规范。
-
-
校验通过,提交成功;若校验失败,终端报错并阻止提交,等待你修改后再提交。
第4个
两种路由模式
在 Web 开发(特别是 Vue Router、React Router 等单页面应用 SPA 框架)中,最常用的两种客户端路由模式是:Hash 模式 和 History 模式。
它们的核心目的都是:在改变浏览器 URL 的同时不刷新页面,并通过监听 URL 变化来动态渲染对应的视图组件。
1. Hash 模式
Hash 模式的 URL 中会带有一个 # 符号(例如:[https://example.com/#/user/profile](https://example.com/#/user/profile))。
📌 基本使用
在配置路由时直接指定模式为 hash(Vue Router 3 示例:mode: 'hash',Vue Router 4 示例:createWebHashHistory())。
⚙️ 实现原理
-
URL 特性 :
#及其后面的内容被称为 URL Hash(锚点) 。HTTP 请求在发送给服务器时,浏览器会自动忽略#后面的部分 。因此,无论#后面的路径怎么变,发送给服务器的永远是根路径 (如[https://example.com/](https://example.com/))。 -
核心 API 与事件:
-
改变路径 :通过修改
location.hash = '/user/profile'或点击带有#的<a>标签来切换路由,这会向浏览器的历史记录栈压入一条新记录。 -
监听变化 :浏览器提供了原生的
hashchange事件。当 URL 中的#后面部分发生变化时(无论是点击链接、调用 API 还是点击浏览器的前进/后退按钮),就会触发该事件:JavaScript
javascriptwindow.addEventListener('hashchange', () => { // 获取当前的路由路径:location.hash const currentPath = window.location.hash.slice(1); // 根据 currentPath 匹配并渲染对应的视图组件 renderView(currentPath); });
-
👍 优缺点
-
优点 :兼容性极佳(支持老旧浏览器);不需要后端/服务器做任何特殊配置,直接部署静态资源即可运行。
-
缺点 :URL 中带
#,不够美观;锚点功能(如页面内滚动定位#section1)会被占用;在某些第三方授权回调或 SEO 抓取时可能受到限制。
2. History 模式
History 模式基于 HTML5 引入的 History API,它的 URL 和传统的多页面网站完全一致(例如:[https://example.com/user/profile](https://example.com/user/profile)),没有 # 符号。
📌 基本使用
在配置路由时指定模式为 history(Vue Router 3 示例:mode: 'history',Vue Router 4 示例:createWebHistory())。
⚙️ 实现原理
-
核心 API 与事件:
-
改变路径但不刷页面 :利用 HTML5 提供的
history.pushState()和history.replaceState()方法。这两个 API 可以在修改浏览器地址栏 URL 的同时,不向服务器发送 HTTP 请求,也不触发页面刷新。JavaScript
php// 改变 URL 并添加历史记录,但不刷新页面 history.pushState({ page: 'profile' }, 'Profile Title', '/user/profile'); // 此时地址栏变为 https://example.com/user/profile -
监听前进/后退 :当用户点击浏览器的前进/后退按钮 或调用
history.back()/history.forward()时,会触发popstate事件:JavaScript
javascriptwindow.addEventListener('popstate', (event) => { // 获取当前路径:location.pathname const currentPath = window.location.pathname; // 根据 currentPath 渲染视图 renderView(currentPath); }); -
注意 :
pushState()和replaceState()本身不会 触发popstate事件,因此前端路由库(如 Vue Router)会在重写这两个方法或调用路由跳转逻辑时,手动去更新视图。
-
⚠️ 致命点与服务器配置(非常重要)
因为 History 模式的 URL 看起来像真实的后端接口路径,如果用户在 [https://example.com/user/profile](https://example.com/user/profile) 页面按 F5 刷新,或者直接复制这个链接在浏览器打开:
-
浏览器会直接向服务器发送 GET 请求:
/user/profile。 -
如果服务器上并没有对应
/user/profile的真实物理文件或路由,服务器就会返回 404 Not Found。
解决方案 :需要将服务器(如 Nginx、Apache)配置为兜底重定向 ------ 无论客户端请求什么路径,只要文件不存在,统统返回单页面应用的入口文件 index.html,然后交由前端路由解析路径并渲染视图。
Nginx 配置示例:
Nginx
bash
location / {
try_files $uri $uri/ /index.html; # 找不到文件时,重定向到 index.html
}
👍 优缺点
-
优点:URL 简洁美观,符合传统网址习惯;符合现代 Web 开发与 SEO 标准。
-
缺点 :兼容性相对要求高一些(需要 IE10+ 支持 HTML5 History API);必须配合服务器配置兜底重定向,否则刷新即 404。
3. 对比总结
| 特性 | Hash 模式 | History 模式 |
|---|---|---|
| URL 形式 | [example.com/#/user](https://example.com/#/user) |
[example.com/user](https://example.com/user) |
| 核心机制 | location.hash + hashchange 事件 |
history.pushState() + popstate 事件 |
| HTTP 请求 | 发送请求时自动忽略 # 后面的内容 |
路径作为真实 URL 发送到后端 |
| 服务端配置 | 不需要额外配置 | 必须配置 (将所有不存在的路径重定向到 index.html) |
| 美观度 | 较差(带 #) |
优秀(与传统 URL 无异) |
| 兼容性 | 极佳(兼容老旧浏览器) | HTML5 新特性(支持 IE10+ 及现代浏览器) |
第5个