核心八股

CSS

居中方案

水平

行内 => text-align: center
块级 => margin: 0 auto

垂直

line-height = height
flex: align-items: center
position + top: 50% + transform(-50%)

水平垂直

flex + justify-content + align-items
position + top/left 50% + transform(-50%)
grid + place-items: center`

选择器优先级

!important > 内联 > ID > 类 > 标签 > 继承

盒子模型

标准模型:width = content
IE模型:width = border + padding + content、box-sizing:border-box
BFC:独立渲染区域,防止外边距合并、清除浮动 / 触发:overflow: hidden、flex

position定位

static: 默认
relative:相对自身定位
absolute:相对最近祖先定位
fixed:相对视口定位
sticky:滚动到临界点后变为 fixed

响应式布局实现方式

@media 媒体查询
rem:根字体大小动态设置
vw/vh:视口单位,1 => 1%

em 和 rem 区别

em 相对元素字体大小
rem(root em) 相对元素字体大小 (更适合响应式)

JS

事件循环 (Event Loop)

主线程执行完后
清空微任务队列 (Promise.then)
取一个宏任务执行 (setTimeout、I/O)

异步方案

Promise (异步基础【ES6】):

  • 解决回调地狱,支持链式调用 .then/.catch
    fetch (基于 Promise 的浏览器原生API):
  • 用于发送网络请求
  • 返回一个 Promise,请求完成时 resolve (包括4xx/5xx响应)
  • 网络错误 (离线/DNS错误) 返回 reject
    async/await (基于 Promise 的语法糖【ES8】):
  • 让异步代码像同步一样书写,await 会暂停函数执行
  • 避免链式调用
    axios (第三方库,基于 XMLHttpRequest / fetch 的封装)
  • 代替 feach
  • 返回一个 Promise,但 4xx/5xx 自动 reject
  • 支持拦截器,请求响应转换,取消请求等

原型原型链

对象通过 proto_ 方法 向上查找属性,直到 Object.prototype 或 null
new 的执行过程: 创建新对象 - 原型链接 - 绑定this - 返回实例

作用域

var:函数作用域,有变量提升;let/const:块级作用域,有暂时性死区

箭头函数

无 this,一旦被创建 this 无法改变
无 arguments 对象,而是通过 …arg 获取剩余参数
无 prototype 对象,不能作为构造函数

箭头函数可以 消除 函数二义性(函数可以作为普通函数调用/函数可以作为构造函数调用)

创建者不知道未来的调用者如何调用,可能直接调用,也有可能通过new方法调用,这就会存在很大的安全隐患

ES6+ 新特性

箭头函数
块级作用域(防变量污染)
解构赋值
promise
set/map

模板字符串
模块化
Set、Map

深浅拷贝

浅(shallowCopy):只复制第一层

  • null、typeof 非对象 return obj
  • 数组 return Object.assign([], obj)
  • return Object.assign({}, obj)

深(deepCopy):递归复制所有层级

  • null、typeof 非对象
  • instanceof Data return new Data(obj)
  • RegExp return new RegExp(obj)
  • Array.isArray for(length) arrCopy[i] = deepCopy(obj[i])
  • other for(key in obj) objCopy[key] = deepCopy(obj[key])

Symbol

创建 唯一 不可变 的值,用作对象属性的键,避免命名冲突,模拟私有属性
是原始类型,非对象

instanceof 和 typeof

typeof:判断值的基本数据类型
instanceof:判断对象是否是某构造函数类实例

数组常用方法

map:映射新数组
filter:筛选符合条件的元素
reduce:累计计算,求和、扁平化
forEach:遍历(不可中断)

TS

好处

静态检查
类型安全
TS编译阶段监控
可维护性

interface / type

type

可定义联合类型
支持 元组、映射、条件 类型

interface

可继承
可合并

泛型

<T 实现类型复用,比如 Promise<T / ArrayT> 不预先指定具体的类型,能“记住”你传入的数组是什么类型,并确保返回值是该类型的元素,分为泛型函数和泛型接口

泛型约束:函数(参数)类型继承接口

场景

API请求

浏览器

输入 URL - 页面展示

DNS解析 - TCP连接 - 发送HTTP请求 - 服务器响应 - 浏览器解析HTML - 构建 DOM/CSSOM - 渲染树+布局+绘制

遇到 JS 阻塞解析、CSS 阻塞渲染

缓存

强制缓存

根据响应头中的 Cache-Control / Expires,判断资源是否“还有效”
在资源有效期内浏览器完全不发请求到服务器,直接使用本地缓存
速度快,性能最好

协商缓存

强制缓存失效后,浏览器会向服务器发送带标识的请求,询问资源是否更新
资源没有变化则返回304,浏览器继续使用本地缓存
已更新则返回200和新资源

跨域

浏览器阻止前端JS向不同源的服务器发请求

同源:协议 + 域名 + 端口 相同
解决:CORS、JSONP、代理

  • 后端配CORS在响应头添加特定字段
  • 用前端服务器作为中间人,转发Proxy代理(仅开发)
  • JSONP,动态创建script标签(script不受同源策略限制)

hash / history

hash 是 URL 的一个属性
history 是 浏览器提供的用来操作浏览历史记录API 对象
二者都是无刷新导航的解决方案

hash

特指 URL 中 ‘#‘ 后面的内容

  • 最原始的作用:让浏览器滚动到页面内具有对应 ‘id’ 的元素位置
  • 路由:单页面应用中,利用 hash 值不改变不会导致页面刷新的特性,来模拟不同的页面/视图,实现无刷新的导航

使用场景:简单单页应用使用 / 兼容老浏览器 / 不方便配置服务器

history

浏览器内置的 JS API 对象,属于 window 对象的一部分 (window.history),提供了操作浏览器会话历史记录的方法和属性

  • 作用:允许 JS 代码操作导航 ‘前进’/‘后退’ 等方法属性,添加或修改历史记录条目,从而控制跳转行为

使用场景:现代单页 / 要求SEO / 配置了服务器设置

差异

特性 Hash History API
触发页面刷新
更改 hash 不会向服务器发送请求,页面不会重新加载

使用 pushState 或 replaceState 修改 URL 也不会刷新页面。
被浏览器视为新历史记录
每次 hash 改变,浏览器都会将其记录为一个新的历史条目,“前进/后退”按钮可以导航。

pushState 会添加一个新条目,replaceState 会替换当前条目。
向服务器发送请求
只有 # 前面的部分(协议、域名、路径、查询参数)会被发送给服务器。# 及之后的内容永远不会发往服务器

通过 History API 修改的 URL 在页面不刷新的情况下,也不会立即触发对服务器的新请求。
SEO 友好性 较差 ❌
搜索引擎可能难以正确索引基于 hash 的 SPA 页面,因为内容是动态加载的,且 URL 结构不标准
较好 ✅
配合服务端渲染(SSR)或预渲染,基于 History API 的路由更符合传统网站结构,对 SEO 更友好
服务端配置要求  ✅
无论用户直接访问哪个带 # 的 URL,服务器都只需返回同一个 HTML 文件(如 index.html前端路由负责解析 hash 并显示相应内容
 ❌
必须配置服务器,确保当用户直接访问 /user/profile 这样的路径时,服务器能返回 index.html 文件,而不是返回 404 错误。这通常需要设置“fallback”或“rewrites”规则

浏览器缓存

强制缓存:Catch-Control:max-age,不发请求
协商缓存:Last-Modified / If-Modified-Since 或 ETag / If-None-Match,服务器判断是否更新

本地存储

localStorage (<4KB):持久化本地存储,数据永不过期 (除非手动删除),存储的信息同域共享,只能存入字符串,不能直接存对象 ||||| 用户偏好设置、离线缓存

sessionStorage (5–10MB):同 localStorage,但页面/会话关闭删除存储数据 ||||| 临时数据(如表单草稿)

cookie (<4KB):小型文本文件,网站为了辨别用户身份而发送给用户并存储在用户本地终端上的数据,为了解决 HTTP 无状态问题,自动随每个同域请求发送请求,由 Name名、Value值、有效期、安全性、使用范围 组成 ||||| 身份认证(Session ID)、跟踪用户行为

IndexedDB (数百 MB 到 GB):持久化对象/文件 ||||| 用于离线应用

网络

状态码

1 信息性响应

2 成功

3 资源重定向
301/302 重定向

4 客户端错误响应

5 服务器错误响应

请求方法区别

GET:参数在 URL
POST:参数在 body,传数据更安全,

HTTP/HTTPS 区别

HTTP 明文传输、HTTPS 非对称加密 (大数取模不可逆)
端口 HTTP(80) / HTTPS(443)
证书
SEO HTTP不利

HTTP 版本区别

HTTP / 1:

  • 每次请求响应后 TCP 都会关闭

每次建立新连接开销极大👇

HTTP / 1.1:

  • 持久连接/管道化 (同一 TCP 连接上发送多个请求响应[但按顺序,会产生阻塞])

HTTP / 2:

  • 多路复用 (同一 TCP 连接上交错发送多个请求响应[每个请求/响应被分解成更小的帧(Frame),不同流(Stream)的帧可以混合在一起传输,接收方根据帧头中的流ID重新组装)]

比喻:持久连接是“一次说一句完整的话”,多路复用是“把多句话拆成词,混在一起说,对方再按句子拼起来”。后者效率远高于前者

HTTP / 1.1:

  • 头部未压缩 (大量头部信息 Cookie、User-Agent、Accept)

对于包含大量小资源的页面,头部传输的开销非常大

HTTP / 2:

  • 头部压缩 使用专门设计的 HPACK 算法来压缩请求和响应头部,分为静态表动态表,分别存储常见头部名和值最近传输过的头部

比喻:HTTP/1.1 是“每次都写全地址”,HTTP/2 是“第一次写全地址,存为‘家’,以后只说‘寄到家’”

此外

  • HTTP在 1 -> 2 后从 文本协议 转换成 二进制帧层,对机器来说解析更快
  • 1:客户端必须先下载 HTML,解析后发现需要 CSS、JS、图片等资源,再逐一发起请求——2:服务器可以主动向客户端发送它预测客户端即将需要的资源,而无需客户端显式请求。例如,当客户端请求 index.html 时,服务器可以立即推送 style.css 和 app.js

HTTPS加密

混合加密:非对称加密协商密钥,后续通信用对称加密
数字证书

访问 HTTPS 网站

(访问) 用户访问网站
(连接) 浏览器与服务器建立连接
(取证) 浏览器获取服务器发来的证书
(验证) 浏览器验证证书 - 是否由可信CA签发(在信任列表)、是否过期、是否和域名匹配
(非对称加密) 浏览器生成’对话密钥‘,用证书里的公钥加密后发给服务器,服务器用证书里的公钥解密
(对称加密) 传输数据

一切正常就建立加密连接,地址栏左边显示🔒

XSS / CSRF

XSS:注入恶意脚本,盗取 Cookie
防御:转义输出、CSP、HTTPOnly

CSRF:伪造用户请求
防御:SameSite Cookie、CSRF Token、Referer 校验

CDN 加速原理

将静态资源缓存到离用户最近的边缘节点,减少源站请求延迟

SSR / CSR

SSR (服务端渲染):首屏快,SEO好,服务器压力大

CSR (客户端渲染):首屏慢,后续交互快,依赖JS

Vue

vue2 / vue3

选项式 / 组合式

TS 支持

生命周期

createdmountedbeforeDestroydestroyed

setup()beforeCreate 和 created),onMountedonUnmounted

响应式

vue2:defineProperty,无法动态添加属性/无法删除属性/无法监听数组索引和长度变化/无法支持众多数据结构(Map/Set/Class)

原因:defineProperty 只能劫持已存在的属性,新添加的属性不会自动变成响应式

vue3:Proxy 实现响应式 (代理整个对象,拦截所有操作)

监听:动态增删改自动触发 set - trigger 深度更新
懒执行响应式:只有访问到的属性才会变成响应式

虚拟DOM与Diff算法

虚拟DOM:用 JS 对象描述真实 DOM
key:帮助 Diff 算法识别节点是否复用,避免错乱
Diff 核心:同层比较,双指针比对,先头尾后中间

TreeShaking 原理

基于 ESM 静态分析,打包时移除未被引用的代码,减少体积

nextTick / keep-alive

nextTick:等待 DOM 更新后执行回调,基于 Promise / MutationObserver

keep-alive:缓存组件实例,避免重复渲染,配合 activated / deactivated 使用

MVVM 模型

View 和 Model 通过 ViewModel 双向绑定,Vue 是典型实现

diff算法

**key 是节点的唯一标识用于快速查找可复用节点

同级比较(第一个和最后一个元素是否相同),相同直接复用,然后移动指针继续比较
对比不上:
用 key 建立一个旧节点的索引映射
遍历新节点,在旧节点中查找是否有可复用的
需要移动的节点,Vue3 会计算出一个最长递增子序列

旧列表是 [A, B, C, D],新列表是 [D, A, B, C]
Vue3 通过头尾比对发现 D 和 C 可以复用,再通过 LIS 算法发现 A、B、C 的相对顺序没变,只需要把 D 移到最前面,其他节点原地不动就行

工程化

Webpack 核心概念

Entry:入口文件
Ouput:输出路径
Loader:转换非 JS 文件

bable

把环境不认识的语法转译成可认识的语法

打包优化

体积:splitChunks 代码分割、Tree Shaking、Terser 压缩、图片压缩
速度:catch:type:’filesystem’、thread-loader 多进程

模块化

CommonJS:require 同步加载,用于 Node
ESM:import 静态分析,支持 Tree Shaking,浏览器原生支持

性能

优化方法

网络:CDN、压缩、缓存、HTTP2
构建:代码分割Tree Shaking
代码:防抖节流、懒加载、虚拟列表

白屏问题

JS 阻塞解析
资源加载失败
服务端渲染异常
首屏资源过大

内存占用过大

Memory 面板拍快照
内存泄漏:闭包、事件监听未解绑、定时器未清除

性能监控指标

FP:首次绘制
FCP:首次内容绘制
LCP:最大内容绘制
TTI:可交互时间

前端埋点:Perdormance + 上报服务

AI 提效

文档说明测试用例正则表达式,解释复杂逻辑

LLM & RAG
LLM:训练的语言模型,用于生成类人文本
RAG:检索知识库,再交给LLM生成答案

写好 Prompt:角色、任务、格式、限制

手撕

防抖

延迟执行,重新计时
输入框

  • 如果有 timeoutId 直接 clear 掉
    ![[Pasted image 20251027194444.png]]

节流

一段时间只执行一次

  • 如果没有 timeoutId 则添加,绑定执行后清除
    ![[Pasted image 20251027201525.png]]

Promise.all

  • 返回新 Promise
  • 记录任务数/结果
  • 遍历数组

![[Pasted image 20251027203005.png]]

算法

字母异位分组

array = Array.from(str) 分割字母成数组
array.sort()
key = array.toString()

最长连续序列

const st = new Set(nums)
for(x of st)

  • st.has(x-1) continue
    y = x+1
  • st.has(x+1) y++
    ans = Math.max(ans, y-x)

移动零

双指针
[nums[i], nums[i0]] = [nums[i0], nums[i]]

盛水最多的容器

双指针
移短边

接雨水

双指针
记最高
移短边

无重复最长字串

const map = new Set()
l = 0
for

  • let c = s[r]
  • while (map.has(c)) map.delete(s[l]) l++ // 滑动左边l
  • map.add(c)
  • ans = r-l+1

和为k的子数组

前缀和

相交链表

p = headA
q = headB

while (p !== q){
p = p ? p.next : headB
q = q ? q.next : headA
}

反转链表

pre = null // 指针
cur = head // 指针
while(cur){
next = cur.next
cur.next = pre
pre = cur
cur = next
}

中序遍历

const ans = []

if(!root){
return
}
dfs(root.left)
ans.push(rool.val)
dfs(root.right)

dfs(root)

翻转二叉树

left = invertTree(root.left)
right = invertTree(root.right)

root.left = right
root.right = left