Appearance
浏览器与网络
搜索引擎与 SEO、跨域、存储、渲染、缓存、传输协议(TCP / UDP / WebSocket)、HTTP 与常见 Web 安全。垃圾回收、事件冒泡 / 捕获 / 委托见 JavaScript。
性能边界:本章讲浏览器为什么卡(重排 / 重绘 / 合成)。项目「加载 / 打包 / 懒加载」清单见 工程化;Vue 组件 API 手段见 Vue3。
搜索引擎是怎么工作的?前端 SEO 怎么做?
搜索引擎(Google、百度等)不是地址栏那个搜索框,而是一套抓取 → 索引 → 排名的系统。SEO(搜索引擎优化)就是让页面更容易被抓到、看懂、排到前面。前端 常问:爬虫怎么拿页面、SPA(单页应用)为啥不利于 SEO、前端能改什么。
1. 搜索引擎三步:抓取、索引、排名
- 抓取(Crawl):爬虫像简化版浏览器去请求 URL,拿到 HTML,顺着
<a>继续发现新页。也会读robots.txt(爬虫协议),决定哪些路径不抓。 - 索引(Index):解析标题、正文、结构、链接,存进索引库。页面「说了什么」主要看 HTML 文本,不只看用户肉眼看到的画面。
- 排名(Rank):用户搜关键词时排出先后。相关度看内容和搜索词匹不匹配;站点权威看这个站靠不靠谱、有没有分量(别的正规站会不会链过来,前端几乎改不了);页面体验看速度、移动端、HTTPS。
⚠️ 关键点:用户用 Chrome 打开能看见内容,不代表爬虫一定能看见。爬虫优先吃 HTML;CSR(客户端渲染,页面靠浏览器跑 JS 再画出内容)的 SPA,首包经常是空的 #app,正文要等 JS 跑完才有。
2. 和浏览器 / SPA 的关系
| 渲染方式 | 爬虫拿到的 HTML | SEO 友好度 |
|---|---|---|
| 传统 MPA(多页应用)/ SSR(服务端渲染) | 首包就是完整正文 | 好 |
| SSG(静态站点生成)/ 预渲染 | 构建时已生成完整 HTML | 好 |
| CSR(客户端渲染,典型 Vue / React 单页应用) | 往往只有空壳 + JS | 差,除非再做 SSR / 预渲染 |
现代 Google 能执行一部分 JS(网页渲染服务,基于 Chrome),但不保证及时、完整;百度等对 JS 更弱。 默认答:不能把 SEO 赌在「爬虫会跑 JS」上,要收录的公开页该让 HTML 里先有正文。
怎么解(同一类问题,三种手段):
- SSR(服务端渲染):每次请求服务器当场渲成带正文的 HTML。适合内容常变、要个性化的页。
- SSG(静态站点生成):构建时生成完整 HTML。适合官网、文档、博客。
- 预渲染:仍是 SPA,打包时用无头浏览器把公开路由打开并存成 HTML。已有 Vite + Vue 官网改动最小:路由用 History(不要 hash),只预渲染要被搜到的页。
官网一般用 SSG 或预渲染即可,不必上 SSR。后台、登录页继续 CSR 没问题。
3. 前端能做的 SEO(按 优先级)
让爬虫「看见」内容
- 公开页用 SSR(服务端渲染)/ SSG(静态站点生成)/ 预渲染,不要只靠 CSR(客户端渲染)
- 语义化 HTML:一页一个
h1,用header/nav/main/article,别全是div(标签本身怎么写,见 HTML 语义化) - 重要文案写在 HTML 里,不要只画在 canvas(画布)或纯图片里
让爬虫「看懂」这页是什么
<title>、<meta name="description">:出现在搜索结果的标题和摘要- 图片写
alt(替代文本),链接用有意义的锚文本 canonical(规范网址)指定这一页的标准 URL,避免重复页抢权重- 结构化数据 JSON-LD(一种给搜索引擎读的标注格式,可选,用来做富摘要)
让爬虫「找得到、愿意排」
robots.txt(爬虫协议)声明哪些不抓;sitemap.xml(站点地图)把该收录的 URL 列出来- 站内链接能点到关键页(爬虫靠链接发现页面)
- HTTPS、移动端适配
- 性能:Core Web Vitals(网页核心体验指标:LCP 最大内容绘制、INP 交互响应、CLS 布局偏移)已是排名因素之一,和「页面体验」挂钩
🎤 口述背诵版
搜索引擎三步:爬虫抓 HTML、解析进索引、按相关度和体验排名。SEO(搜索引擎优化)就是让这三步都对你有利。
前端最容易踩的坑是 SPA(单页应用):用户能看见,爬虫首包可能是空壳。解法是让 HTML 里先有正文:SSR(服务端渲染)、SSG(静态站点生成)、预渲染都算,不是只有 SSR。官网优先 SSG 或预渲染。
除此之外:语义化标签、title / description、图片 alt(替代文本)、sitemap(站点地图)/ robots(爬虫协议)、HTTPS 和速度。能讲清「爬虫看的是 HTML,不是你渲染完的画面」,这题就过了。
什么是 XSS 和 CSRF?如何预防?
XSS(跨站脚本)
攻击者把恶意脚本注入到页面,浏览器当作正常页面逻辑执行。常见危害:窃取 Cookie / 本地存储、篡改页面、冒充用户发请求。
常见分类:
| 类型 | 特点 |
|---|---|
| 存储型 | 恶意内容写入服务端(评论、资料等),他人打开页面即中招 |
| 反射型 | 恶意内容挂在 URL 参数里,服务端原样反射进页面后执行 |
| DOM 型 | 不经过服务端,前端用 JS 把不可信数据写进 DOM(如 innerHTML)后执行 |
预防:
- 输入校验、输出编码 / 转义(把用户内容当数据,不当代码)
- 少用
innerHTML/v-html等会按 HTML 解析的 API;Cookie 可加HttpOnly(禁止 JS 读 Cookie),降低被脚本读取的风险 - 配 CSP(内容安全策略,Content-Security-Policy),限制脚本来源、减少内联脚本执行面
CSRF(跨站请求伪造)
在用户已登录的前提下,诱导其访问恶意站点或链接,借浏览器自动携带 Cookie 的特性,向目标站发起用户非本意的请求(改资料、转账等)。攻击方通常拿不到 Cookie 内容,只是「借身份办事」。
预防:
- CSRF Token(跨站请求令牌):敏感请求携带服务端下发、攻击站无法读取的 token,服务端校验
- 校验
Referer/Origin(来源),确认请求来自本站 - Cookie 设置
SameSite(同站策略:Lax/Strict),限制跨站请求自动带 Cookie - 关键操作可再加二次验证(密码、短信等)
XSS vs CSRF
| XSS | CSRF | |
|---|---|---|
| 本质 | 在页面里跑恶意脚本 | 借登录态发请求 |
| 典型结果 | 偷数据、改页面、进一步攻击 | 在不知情下完成敏感操作 |
| 防护重心 | 输入输出转义、CSP、避免危险 DOM API | Token、SameSite、来源校验 |
🎤 口述精简版
XSS 是往页面注入并执行恶意脚本,防转义 / 少用危险渲染、配 CSP、Cookie 可加 HttpOnly。CSRF 是借你已登录的 Cookie 冒充你发请求,防 CSRF Token、SameSite、校验 Origin / Referer。一个防「脚本被执行」,一个防「请求不是你本意」。
什么是跨域?跨域产生的原因?
1. 什么是跨域
当协议、域名、端口三者任意一个不一样,就是跨域。
- ✅ 同源:
http://a.com:8080→http://a.com:8080协议、域名、端口全部相同 - ❌ 跨域:只要协议 / 域名 / 端口,有任意一项不同,就属于跨域
注意:跨域是浏览器的安全限制;请求是正常发出去了,后端收到请求,浏览器拿到响应之后,被浏览器拦截,不给 JS 读取响应结果。不是请求发不出去。
2. 跨域产生的根本原因:浏览器的同源策略(Same-Origin Policy)
同源策略是浏览器的安全机制,目的:防止恶意网站窃取另外一个网站的敏感数据,保护用户安全。
它限制两件主要事情:
- JS 不能读取不同源的 DOM、Cookie、localStorage 等页面数据
- JS 无法正常读取跨域的 AJAX/Fetch 接口响应(接口请求可以发出,响应被浏览器拦截)
⚠️ 关键点:
- 同源策略只存在于浏览器环境,后端服务器之间互相请求不存在跨域;Postman、后端代码调用接口没有跨域问题
<img>、<script>、<link>标签加载资源不受同源策略限制(所以才有 JSONP 的实现思路)
🎤 口述简短背诵版
跨域是浏览器同源策略带来的现象。浏览器判断同源看协议、域名、端口,三者有一个不一样就是跨域。
同源策略是浏览器的安全保护机制,为了防止恶意站点窃取其他网站的数据。
跨域的时候,请求实际是发送到后端,后端也处理返回了,只是浏览器拦截了 JS 对响应的读取。
注意:后端与后端之间调用接口不存在跨域。
常见跨域解决方案:CORS、JSONP、代理、Nginx 的原理和优缺点?
重点:分清哪一端做改造、原理、优点、缺点。
1. CORS(跨域资源共享,后端主流方案)
改造位置:服务端(后端)
原理:
- 浏览器发现请求跨域,会自动带上
Origin请求头;后端在响应头增加Access-Control-Allow-Origin等系列响应头,告诉浏览器允许该源访问 - 简单请求:直接一次请求
- 非简单请求(PUT/DELETE、自定义 header 等):浏览器先发
OPTIONS预检请求,预检通过才发真实请求
优点:
- 支持所有请求方式 GET/POST/PUT/DELETE;支持 cookie 携带
- W3C 标准,现代浏览器全部支持,业务首选
缺点:
- 需要后端修改代码配置响应头,前端无改动
- 老 IE 浏览器不支持(IE8/9 部分兼容)
- 非简单请求会多出一次
OPTIONS预检请求,产生额外网络开销
2. JSONP
改造位置:前端 + 后端配合
原理:
- 利用
<script>标签不受同源策略限制的特性 - 前端动态创建
script标签,请求后端接口,后端返回一段回调函数调用的 JS 脚本,把数据作为回调参数传回前端
优点:
- 兼容非常老浏览器;没有预检请求
缺点:
- 只支持 GET 请求,不支持 POST/PUT 等
- 存在 XSS 安全风险,后端需要校验回调参数
- 不能携带 cookie
- 属于 hack 方案,现在新项目基本不用
3. 开发环境代理(Vue/Vite/Webpack devServer 代理)
改造位置:前端工程配置,仅本地开发阶段生效
原理:浏览器请求本地前端服务(同源),前端开发服务器充当中间转发服务器,把请求转发给真实后端接口;浏览器只和本地服务通信,不存在跨域。
⚠️ 重要:仅开发环境生效,打包上线后该代理配置完全失效!上线不能靠这个解决跨域。
优点:前端自己配置,不需要改后端代码,本地调试非常方便。
缺点:只用于本地开发,生产环境无效。上线必须 CORS/Nginx。
4. Nginx 反向代理
改造位置:运维 / Nginx 服务器
原理:部署 Nginx 服务器。前端页面和接口统一走同一个 Nginx 域名。浏览器访问同源 Nginx 地址,Nginx 收到请求,内部转发到真实后端服务。浏览器只跟 Nginx 通信,无跨域。
两种用法:
- 前端静态资源 + 接口全部由 Nginx 代理
- 单独把
/api路径转发到后端服务
优点:
- 不需要前后端改业务代码;对前端完全透明
- 支持所有请求方式,支持 cookie,生产环境稳定
缺点:需要运维配置 Nginx 服务器;需要服务器权限。
| 方案 | 修改方 | 适用环境 | 支持 POST | 主要缺点 |
|---|---|---|---|---|
| CORS | 后端 | 生产 / 开发 | ✅ | 需要后端修改,非简单请求有 OPTIONS 预检 |
| JSONP | 前后端配合 | 老浏览器兼容 | ❌ 仅 GET | XSS 风险,新项目废弃 |
| devServer 代理 | 前端工程配置 | 仅本地开发 | ✅ | 打包后失效,线上无效 |
| Nginx 反向代理 | 运维 Nginx | 生产环境 | ✅ | 需要服务器配置权限 |
🎤 口述背诵版
四种跨域方案:
CORS:后端配置响应头,W3C 标准,支持各种请求,是生产最常用;缺点需要后端改造,非简单请求会有 OPTIONS 预检。
JSONP:利用 script 标签不受同源限制;只能 GET,有 XSS 风险,新项目几乎不用。
前端工程代理:webpack/vite 的 devServer 代理,只用于本地开发,打包上线无效,不能解决线上跨域。
Nginx 反向代理:运维配置,统一域名转发请求,浏览器无跨域,不需要修改前后端业务代码,生产环境常用,但是需要服务器权限。
补充一句 高频踩坑:开发环境代理不能用于线上,线上跨域要么后端 CORS,要么 Nginx 代理。
cookie、localStorage、sessionStorage 的区别?
限定对比维度:存储大小、生命周期、是否随请求发送、作用域、设置方式。
注意:符合 domain、path 规则的 Cookie,浏览器才会自动在请求头带上 Cookie 字段发给服务端;不匹配则不会携带,不是无条件全部携带。domain / path 决定哪些请求会带上这个 cookie。
| 特性 | Cookie | localStorage | sessionStorage |
|---|---|---|---|
| 存储大小 | 约 4KB | 约 5MB | 约 5MB |
| 生命周期 | 可设置过期时间(expires/max-age);不设置则浏览器全关后失效 | 永久存储,需手动清除才消失 | 当前标签页关闭即销毁,刷新页面保留 |
| 是否随请求发送 | 匹配 domain、path 时自动携带到请求头 | 不会自动发送,仅前端 JS 操作 | 不会自动发送,仅前端 JS 操作 |
| 作用域 | domain + path 控制,配置后可在对应域名下访问 | 同源(协议 + 域名 + 端口),多标签页共享 | 同源,仅当前标签会话,同域名多标签互相隔离 |
| 设置方式 | 主要由后端通过响应头 Set-Cookie 设置;前端 JS 也可通过 document.cookie 读写(受 HttpOnly 限制) | 前端 JS 操作:setItem / getItem / removeItem / clear | 前端 JS 操作:setItem / getItem / removeItem / clear |
🎤 口述精简版
从存储大小、生命周期、是否随请求发送、作用域、设置方式五个维度来讲:
存储大小:cookie 只有 4KB,localStorage 和 sessionStorage 大概 5MB。
生命周期:cookie 可以设置过期时间;localStorage 永久保存,需要手动清除;sessionStorage 关闭当前标签页就销毁。
是否随请求发送:cookie 在满足 domain、path 匹配时,会自动携带到 http 请求头;localStorage、sessionStorage 不会随请求发送,只能前端 JS 操作。
作用域:cookie 受 domain 和 path 控制,可以在配置的域名下访问;localStorage 同源多标签页共享;sessionStorage 只在当前标签会话可用,同域名不同标签互相隔离。
设置方式:cookie 一般由后端通过 Set-Cookie 响应头设置,前端也能用 document.cookie 读写(有 HttpOnly 时 JS 读不到);localStorage 和 sessionStorage 都是前端用 setItem / getItem / removeItem / clear 操作。
浏览器页面渲染完整流程?
常问「从输入 URL 到页面展示发生了什么」,按网络 → 解析 → 渲染来讲:
- DNS 解析(域名 → IP,有缓存)
- TCP 三次握手(HTTPS 还多一次 TLS 握手)
- 发送 HTTP 请求
- 服务器响应 HTML
- 解析 HTML 建 DOM,并行请求 CSS / JS
- CSSOM + DOM → 渲染树
- 布局 Layout → 绘制 Paint → 合成 Composite 上屏
- 加载后续资源、执行 JS、绑定交互
串起来就是:
URL 地址解析(DNS 解析)→ TCP 连接 → HTTP 请求响应 → 解析 HTML 构建 DOM 树 → 解析 CSS 构建 CSSOM → 生成渲染树 → 布局 Layout(重排 reflow)→ 绘制 Paint(重绘 repaint)→ 分层合成整个界面 → 展示页面
🎤 口述背诵版
先 DNS 找 IP,再握手、发请求拿 HTML。HTTPS 还要多一次 TLS 握手。
接着解析 HTML 建 DOM,解析 CSS 生成 CSSOM,两者合成渲染树;同时并行拉 CSS / JS。
然后布局(重排)算位置大小,绘制(重绘)填像素,分层合成上屏;再执行 JS、绑交互。
重排和重绘的区别?如何优化?
概念
- 重排(Layout / reflow):元素几何信息(位置、宽高、布局)发生改变,浏览器需要重新计算页面上元素的布局位置。重排一定会触发重绘,开销大,性能代价高。
- 重绘(Paint / repaint):元素样式改变,但不改变布局几何信息,只重新绘制像素(颜色、背景、文字颜色等)。只改外观,不改动位置大小,开销比重排小。
- 合成(Composite):使用
transform、opacity,直接在 GPU 图层做变换,不触发重排、不触发重绘,性能最好。
触发场景举例
触发重排(同时也会重绘):
- 修改:
width、height、margin、padding、top、left、display - 获取
offsetTop/offsetWidth/clientWidth等布局属性,也会触发强制重排
只触发重绘:
- 修改:
color、background-color、box-shadow,不改变宽高位置
只触发合成:
transform、opacity
两者核心区别
- 重排:重新计算布局几何;重绘:只绘制像素,不计算布局
- 重排必然引发重绘;重绘不会引发重排
- 重排性能开销远大于重绘
优化手段
减少重排次数:
- 避免频繁操作 DOM;DOM 操作先离线处理(
DocumentFragment,或先display: none改完再显示),一次性渲染到页面 - 不要在循环里频繁读取布局属性(
offsetWidth、clientHeight),读写交替会触发强制同步重排;尽量读放一起、写放一起
使用 CSS 属性优先选择触发合成的属性:
- 动画优先用
transform、opacity,不要改top/left
开启 GPU 分层:
will-change: transform;提示浏览器提前做图层提升(不要滥用)
减少频繁重绘:
- 避免短时间大量修改颜色、阴影这类会触发重绘的样式
虚拟列表:
- 大数据长列表只渲染可视区域 DOM,减少 DOM 数量,降低重排重绘压力
🎤 口述精简版
简单说,重排是布局变了——宽高、位置一改,浏览器得重新算一遍几何;重绘是样子变了——颜色、背景这些,布局没动,只是把像素重新画一遍。
关系上记住三句:重排一定会带着重绘;重绘不会反过来触发重排;性能上重排最贵,重绘次之,最好是只走合成。
优化也按这个来:少折腾 DOM,读写布局属性分开别穿插;动画尽量用 transform、opacity,走合成别动布局;列表太长就上虚拟列表,少渲染 DOM。
浏览器缓存机制:强缓存 vs 协商缓存?
浏览器拿到资源会先找本地有没有可用缓存。 按「先强缓存、再协商缓存」这条线讲。
强缓存:有效期内直接用本地,不再问服务器
命中强缓存时,不会向服务器发请求(Network 里常见 from disk / memory cache)。
靠响应头告诉浏览器「能用多久」:
- 现在主流:
Cache-Control: max-age=秒数(相对时间,从收到响应算起) - 老写法:
Expires(绝对过期时间) - 两个都有时,以
Cache-Control为准
未过期 → 直接用本地;过期了 → 进入协商缓存。
协商缓存:带「上次的指纹」去问一句,变没变
强缓存失效后,浏览器还会发请求,但会带上上次缓存的标识,让服务器判断资源有没有改:
| 服务端当初给的 | 浏览器再请求时带的 | 怎么比 |
|---|---|---|
ETag(内容指纹) | If-None-Match | 精确,内容变了指纹就变 |
Last-Modified(上次修改时间) | If-Modified-Since | 粗一点,按时间 |
- 没变 → 返回 304 Not Modified,正文几乎不传,继续用本地缓存
- 变了 → 返回 200 + 新内容,并更新本地缓存
两个标识都能用时,一般 ETag 优先(更准)。
串起来的流程
请求资源 → 强缓存未过期?直接用本地(不发包) → 过期了 → 带 ETag / 修改时间去问 → 304 用旧的 / 200 换新的。
🎤 口述精简版
先看强缓存:Cache-Control: max-age 没过期就本地直接用,不问服务器。过期了再协商:带上 ETag 或上次修改时间问服务端变没变,没变回 304 继续用本地,变了回 200 换新的。
GET 与 POST 的区别(不只是传参长度)?
- 语义:GET 取数据、POST 提交
- GET 参数在 URL(有长度 / 历史 / 明文限制、可被缓存);POST 在 body(更安全、体量大)
- GET 幂等、可缓存;POST 非幂等
- 本质都是 HTTP 方法,底层都能带 body / 参数,区别主要在约定与语义
🎤 口述精简版
GET 是「取」、可缓存幂等、参数在 URL;POST 是「交」、放 body、非幂等;别只背长度区别。
HTTP 常见状态码?
- 2xx 成功:200 / 201 / 204
- 3xx 重定向:301 永久 / 302 临时 / 304 协商缓存
- 4xx 客户端错:400 参数错 / 401 未认证 / 403 禁止 / 404 不存在 / 405 方法不允许
- 5xx 服务端错:500 内部 / 502 网关坏 / 503 过载 / 504 超时
🎤 口述精简版
2 成功、3 重定向(304 是缓存)、4 你错了、5 服务器崩了。
HTTP 与 HTTPS 的区别?
HTTPS = HTTP + TLS/SSL 加密;HTTP 明文、端口 80、易被窃听篡改;HTTPS 端口 443、有证书鉴权与加密,防中间人。代价:多了一次 TLS 握手开销(可用 HTTP/2、会话复用缓解)。
🎤 口述精简版
HTTPS 就是给 HTTP 套了层 TLS 加密,防窃听防篡改,端口 443,多了次握手开销。
TCP 和 UDP 的区别?三次握手、四次挥手干什么?
都是传输层协议。HTTP / HTTPS / WebSocket 走 TCP;DNS 查询、直播、游戏同步常走 UDP。
| TCP | UDP | |
|---|---|---|
| 连接 | 面向连接,先握手再建 | 无连接,拿起就发 |
| 可靠 | 有确认、重传、保序 | 尽最大努力送达,可能丢、乱序、重复 |
| 速度 | 慢一点(握手 + 确认) | 快、头开销小 |
| 场景 | 网页、接口、文件、聊天通道 | DNS、音视频、游戏状态 |
三次握手(建立连接)
目的:双方都确认「我能发、你能收」,避免对着一个已失效的请求白建连接。
- 客户端 →
SYN:我想连 - 服务端 →
SYN + ACK:收到了,我也想连 - 客户端 →
ACK:确认,开始传数据
前面「输入 URL」里的 TCP 连接,指的就是这一步;HTTPS 还要再做一次 TLS 握手。
四次挥手(断开连接)
关闭是单向的:我发完了,你那边可能还有数据要发,所以要四次。
- 主动方 →
FIN:我发完了 - 对方 →
ACK:知道了 - 对方发完 →
FIN:我也发完了 - 主动方 →
ACK:断开
🎤 口述精简版
TCP 可靠、要握手,网页和接口都走它;UDP 不管丢不丢、图快,DNS、直播、游戏常用。三次握手是双方确认都能收发;四次挥手是因为关连接是半关的,另一边可能还有数据。
WebSocket 是什么?和 HTTP、TCP 什么关系?
WebSocket 是应用层协议,底层仍走 TCP(wss 再套 TLS)。用来做浏览器里的长连接、全双工:连上之后服务端也能主动推,不用前端一遍遍轮询。
怎么建连
先发一个特殊的 HTTP 请求升级协议,服务端回 101 Switching Protocols,这条 TCP 连接就从 HTTP 变成 WebSocket,之后按帧互发,不再是「一问一答」。
关键请求头:Upgrade: websocket、Connection: Upgrade。
和 HTTP 比
| HTTP | WebSocket | |
|---|---|---|
| 交互 | 客户端请求,服务端才响应 | 两边随时推 |
| 连接 | 一次请求一次响应(Keep-Alive 也是请求驱动) | 握手后一直挂着 |
| 开销 | 每次都带完整头 | 连上后帧头很小 |
| 场景 | 拉页面、调接口、提交表单 | 聊天、通知、协作、行情 |
关系记三层:TCP 负责可靠传输 → HTTP 用来完成一次升级握手 → 之后这条 TCP 上跑的是 WebSocket 帧。它不是 UDP,也不是「替代 TCP」。
前端:new WebSocket('wss://...'),听 onmessage / onclose。业务里通常要做心跳和断线重连。浏览器对 WebSocket 也看 Origin,跨域仍要服务端放行。
🎤 口述精简版
WebSocket 是长连接双向通道,底层还是 TCP。先用 HTTP 带 Upgrade 握手,服务端回 101 就升级成功,之后两边都能推。HTTP 适合一问一答,实时推送用 WebSocket,别靠轮询硬刷。
前端路由:Hash 和 History 有什么区别?实现原理?
SPA(单页应用)切页时不会整页刷新,靠前端路由改 URL、换组件。Vue Router 对应 createWebHashHistory 和 createWebHistory。 抓住:URL 长什么样、刷新找不找服务器、和 SEO 的关系;再能讲清实现就三步。
实现原理(两种模式共用一套骨架)
前端路由不是服务器换页面,是 JS 自己做这三件事:
- 拦住跳转:点
<a>时preventDefault,不让浏览器按传统方式去要一份新 HTML - 改地址栏:只改 URL,不刷新文档
- 匹配并渲染:用当前 path 去路由表里找组件,渲到
router-view上
前进 / 后退靠浏览器历史栈。首次打开或刷新时,页面从服务器拿到 index.html,JS 启动后再读一次当前 URL,走第 3 步。
Hash 模式怎么实现
地址类似 https://a.com/#/about。# 后面叫哈希片段(hash),不会发给服务器。
- 切页:改
location.hash(例如#/about) - 听
hashchange事件,再匹配路由、换组件 - 刷新:浏览器只请求
#前面那一段,拿到的还是index.html,JS 再读 hash 渲对应页
优点:部署简单,刷新也不用服务器配合。缺点:URL 带 #;搜索引擎基本不把 hash 当独立页面,不利于 SEO。
History 模式怎么实现
地址类似 https://a.com/about。用浏览器 History API:
- 切页:
history.pushState(...)改路径(不刷新、也不会自动触发popstate,所以改完要自己渲染) - 点后退 / 前进:听
popstate,再按location.pathname匹配渲染 - 替换当前记录用
replaceState(登录后丢掉登录页这类场景)
优点:URL 像真实路径,利于 SEO、也方便预渲染。缺点:用户刷新或直接打开 /about 时,浏览器会向服务器要这个路径。没有这个文件就会 404,需要 Nginx 等做成:找不到文件就回退到 index.html,交给前端路由。
| Hash | History | |
|---|---|---|
| 改 URL | location.hash | history.pushState |
| 监听变化 | hashchange | popstate(只覆盖前进后退;pushState 要自己渲) |
| 刷新 / 直开 | 服务器看不到 hash,一般不 404 | 必须服务器回退到 index.html,否则 404 |
| SEO | 差(多条路由像同一页) | 更好(每条是独立路径) |
⚠️ 关键点:pushState 只改地址栏和历史栈,不发请求、不刷新、不自动触发 popstate。所以 History 模式里「点菜单」要:改 URL + 立刻自己匹配渲染;「点返回」才走 popstate。
🎤 口述精简版
前端路由三步:拦住 a 标签默认跳转、改 URL、按 path 换组件。Hash 改 # 后面那段,听 hashchange,片段不给服务器,部署省事但 SEO 差。History 用 pushState 改真路径,前进后退听 popstate;刷新要服务器把路由都回落到 index.html,不然 404。官网要收录就用 History。
什么是 CDN?它怎么加速?
CDN 把资源缓存到离用户近的边缘节点,请求就近返回,降低延迟和源站压力。配合缓存策略(文件名 hash + 强缓存)效果最佳。
🎤 口述精简版
CDN 就是把资源复制到离用户近的节点,就近返回,又快又减负。
前端怎么做错误监控?
window.onerror/error事件捕获 JS 错误unhandledrejection抓 Promise 异常- 框架层(Vue
errorHandler)兜底 - 上报到监控平台(Sentry 等)
- 结合 sourcemap 还原压缩代码位置
🎤 口述精简版
全局 error / unhandledrejection 兜住异常,框架 errorHandler 兜底,配 sourcemap 上报到 Sentry 这类平台。