一、HTML / CSS 基础
1. 语义化标签有哪些?为什么要用语义化?
常见标签: header、nav、main、section、article、aside、footer、figure、time 等。
作用:
- 结构更清晰,利于维护
- SEO 更友好,搜索引擎更好理解页面
- 无障碍更好(读屏软件)
- 团队协作成本更低
2. display: none、visibility: hidden、opacity: 0 区别?
| 属性 | 是否占位 | 是否响应事件 | 是否继承 |
|---|---|---|---|
display: none |
否 | 否 | 否 |
visibility: hidden |
是 | 否 | 是 |
opacity: 0 |
是 | 是(默认) | 否 |
3. 盒模型:content-box 和 border-box 区别?
- content-box:
width/height只算内容区,padding、border 额外加 - border-box:
width/height包含 content + padding + border
实际开发常用:
* { box-sizing: border-box; }
4. BFC 是什么?如何触发?解决什么问题?
BFC(块级格式化上下文):独立渲染区域,内外布局互不影响。
触发方式:
overflow: hidden/autodisplay: flex/inline-blockposition: absolute/fixedfloat不为 none
解决问题:
- 清除浮动(父元素高度塌陷)
- 阻止 margin 重叠
- 自适应两栏布局
5. 水平垂直居中常见写法?
Flex(推荐):
.parent {
display: flex;
justify-content: center;
align-items: center;
}
Grid:
.parent {
display: grid;
place-items: center;
}
绝对定位:
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
6. Flex 和 Grid 适用场景?
- Flex:一维布局(导航栏、表单行、左右结构)
- Grid:二维布局(整页、卡片矩阵、复杂仪表盘)
7. 移动端 1px 边框问题怎么解决?
高清屏下 1px 看起来偏粗。
常见方案:
transform: scaleY(0.5)box-shadow: 0 0.5px 0 #ccc- viewport + rem 适配
- 使用伪元素 + scale
8. rem、em、vw、vh 区别?
- em:相对父元素 font-size
- rem:相对根元素 html font-size
- vw/vh:相对视口宽/高 1%
- vmin/vmax:取 vw、vh 较小/较大值
9. CSS 选择器优先级?
权重: !important > 行内样式 > ID > 类/属性/伪类 > 标签/伪元素
计算:(a, b, c)
- a:ID 个数
- b:类/属性/伪类个数
- c:标签/伪元素个数
10. 两栏布局(左固定右自适应)?
Flex:
.container { display: flex; }
.left { width: 200px; flex-shrink: 0; }
.right { flex: 1; }
Grid:
.container { display: grid; grid-template-columns: 200px 1fr; }
二、JavaScript 基础
1. var、let、const 区别?
| var | let | const | |
|---|---|---|---|
| 作用域 | 函数 | 块级 | 块级 |
| 变量提升 | 有 | 无(TDZ) | 无 |
| 重复声明 | 可以 | 不可以 | 不可以 |
| 修改值 | 可以 | 可以 | 不可以(对象属性可改) |
2. 闭包是什么?应用场景?
定义: 函数能访问其词法作用域外的变量,即使外层已执行完毕。
场景:
- 防抖/节流
- 私有变量
- 柯里化
- 缓存计算结果
注意: 不当使用可能导致内存泄漏。
3. 原型链是什么?
每个对象有 __proto__,指向构造函数的 prototype。
查找属性时:自身 → 原型 → 原型的原型 → ... → null。
4. this 指向规则?
- 默认绑定:非严格模式指向
window - 隐式绑定:
obj.fn()中this指向obj - 显式绑定:
call/apply/bind new绑定:指向新实例- 箭头函数:继承外层
this,不可被call改变
5. 事件循环:宏任务、微任务顺序?
宏任务: setTimeout、setInterval、I/O、UI 渲染
微任务: Promise.then、MutationObserver、queueMicrotask
顺序:
- 执行同步代码
- 清空微任务队列
- 执行一个宏任务
- 再清空微任务
- 循环
6. Promise、async/await 区别?
- Promise:链式处理异步,状态 pending/fulfilled/rejected
- async/await:同步写法处理异步,本质是 Promise 语法糖
错误处理:
try {
const res = await api()
} catch (e) {
console.error(e)
}
7. 深拷贝和浅拷贝?
- 浅拷贝:只复制第一层,引用类型仍共享
- 深拷贝:递归复制所有层级
实现方式:
- 浅拷贝:
Object.assign、{...obj}、arr.slice() - 深拷贝:
structuredClone(现代浏览器)、JSON.parse(JSON.stringify())(有局限)、递归实现
8. 防抖和节流?
- 防抖 debounce:连续触发只执行最后一次(搜索框输入)
- 节流 throttle:固定时间内只执行一次(滚动、resize)
9. == 和 === 区别?
==:会类型转换后比较===:严格相等,类型和值都相同
开发推荐用 ===。
10. ES6+ 常用特性
- 解构赋值、展开运算符
- 箭头函数、模板字符串
let/const、块级作用域- Promise、async/await
- 模块化 import/export
- Class、Map/Set、可选链
?.、空值合并??
三、浏览器与网络
1. 从输入 URL 到页面展示?
- DNS 解析
- TCP 连接(三次握手)
- HTTPS 还要 TLS 握手
- 发送 HTTP 请求
- 服务器响应
- 浏览器解析 HTML → DOM
- 解析 CSS → CSSOM
- 合成渲染树 → 布局 → 绘制
- 加载 JS、图片等资源
- 可能触发重排/重绘
2. 强缓存、协商缓存?
强缓存(不发请求):
Cache-Control: max-age=xxxExpires
协商缓存(发请求,304 用缓存):
Last-Modified/If-Modified-SinceETag/If-None-Match(更精确)
流程: 先强缓存 → 过期后协商缓存 → 304 或 200
3. Cookie、SessionStorage、LocalStorage?
| Cookie | SessionStorage | LocalStorage | |
|---|---|---|---|
| 生命周期 | 可设过期 | 关标签页清除 | 永久(手动清) |
| 容量 | ~4KB | ~5MB | ~5MB |
| 随请求发送 | 是 | 否 | 否 |
| 作用域 | 可设 domain | 当前标签页 | 同源 |
4. 跨域及解决方案?
原因: 浏览器同源策略(协议、域名、端口相同)
方案:
- 后端 CORS
- 开发环境 proxy 代理
- Nginx 反向代理
- JSONP(仅 GET,老方案)
- postMessage(iframe 通信)
5. HTTPS 和 HTTP 区别?
- HTTPS = HTTP + TLS/SSL
- 数据加密传输
- 身份校验(证书)
- 默认端口 443
- 防止中间人篡改、窃听
6. 重排和重绘?
- 重排 reflow:几何属性变化(宽高、位置),开销大
- 重绘 repaint:外观变化(颜色),开销较小
优化: 批量改 DOM、用 transform 代替 top/left、DocumentFragment、避免频繁读布局属性
7. XSS、CSRF 防护?
XSS(跨站脚本): 注入恶意脚本
- 防护:转义输出、CSP、HttpOnly Cookie、避免
innerHTML插不可信内容
CSRF(跨站请求伪造): 借用户 Cookie 发请求
- 防护:Token 校验、SameSite Cookie、Referer 校验
四、Vue 常考
1. Vue2 和 Vue3 核心区别?
- 响应式:Object.defineProperty → Proxy
- 组合式 API:Composition API
- 性能更好,Tree-shaking 更好
- 多个根节点(Fragment)
- Teleport、Suspense 等新特性
2. 响应式原理?
Vue2: 递归遍历 data,用 Object.defineProperty 劫持 get/set,数组部分方法重写。
Vue3: Proxy 代理整个对象,可监听新增/删除属性,性能更好。
3. 组件通信方式?
- 父 → 子:
props - 子 → 父:
$emit - 跨级:
provide/inject - 全局:Vuex/Pinia、EventBus(Vue3 少用)
- ref 获取子组件实例
4. v-if 和 v-show 区别?
- v-if:条件渲染,false 时不渲染 DOM,切换开销大
- v-show:始终渲染,用
display:none切换,频繁切换更合适
5. computed 和 watch 区别?
- computed:有缓存,依赖不变不重新计算,适合派生数据
- watch:监听变化执行副作用,适合异步、复杂逻辑
6. 生命周期做什么?
Vue2:
created(请求数据)→ mounted(DOM 操作、ECharts)→ updated → beforeDestroy(清定时器、解绑)
Vue3:
setup → onMounted → onUpdated → onUnmounted
7. nextTick 原理?
DOM 更新是异步的。nextTick 在下次 DOM 更新循环结束后执行回调,用于拿到更新后的 DOM。
8. key 的作用?
帮助 Diff 算法识别节点,提高复用效率。
列表用 唯一 id 作 key,避免用 index(增删顺序会变导致错乱)。
9. 虚拟 DOM 是什么?
用 JS 对象描述 DOM 结构,通过 Diff 找出最小变更再更新真实 DOM。
不是一定更快,但能简化编程模型,批量更新减少直接操作 DOM 次数。
10. Pinia / Vuex?
- Vuex:Vue2 官方状态管理,mutations/actions/modules
- Pinia:Vue3 推荐,更轻、TS 友好、无 mutations
适合:多组件共享状态、用户信息、权限、购物车等。
11. 路由守卫与权限?
- 全局:
beforeEach判断 token/角色 - 路由独享:
beforeEnter - 组件内:
beforeRouteEnter等
按钮级权限: 路由 meta + 权限码 + 自定义指令 v-permission
五、React 常考(简要)
1. 类组件 vs Hooks?
函数组件 + Hooks 是主流,逻辑复用更方便,代码更简洁。
2. useState、useEffect 注意点?
- 不要异步读闭包里的旧 state,用函数式更新
useEffect依赖数组要写全,避免无限循环
3. useMemo、useCallback?
缓存计算结果和函数引用,避免子组件无效重渲染。不要滥用。
六、工程化
1. Webpack 和 Vite 区别?
- Webpack:打包器,开发时也打包,项目大时慢
- Vite:开发用 ES Module + 预构建,HMR 快;生产用 Rollup 打包
2. Tree Shaking?
基于 ES Module 静态分析,去掉未使用代码。
需要 sideEffects 配置,CommonJS 支持较差。
3. Git merge vs rebase?
- merge:保留完整历史,产生 merge commit
- rebase:线性历史,更整洁;公共分支慎用
七、性能优化
1. 首屏慢怎么优化?
- 路由/组件懒加载
- 图片懒加载、WebP、压缩
- CDN、Gzip/Brotli
- 减少首屏 JS 体积
- SSR/预渲染(按需)
- 接口合并、缓存
2. 长列表优化?
- 虚拟滚动(只渲染可视区)
- 分页
- 避免复杂 watcher
八、手写题参考答案
防抖
function debounce(fn, delay) {
let timer = null
return function (...args) {
clearTimeout(timer)
timer = setTimeout(() => fn.apply(this, args), delay)
}
}
节流
function throttle(fn, delay) {
let last = 0
return function (...args) {
const now = Date.now()
if (now - last >= delay) {
last = now
fn.apply(this, args)
}
}
}
深拷贝(简易版)
function deepClone(obj, map = new WeakMap()) {
if (obj === null || typeof obj !== 'object') return obj
if (map.has(obj)) return map.get(obj)
const result = Array.isArray(obj) ? [] : {}
map.set(obj, result)
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
result[key] = deepClone(obj[key], map)
}
}
return result
}
数组扁平化
const flat = arr => arr.reduce((acc, cur) =>
acc.concat(Array.isArray(cur) ? flat(cur) : cur), [])
九、项目实战(答题模板)
1. 最复杂的前端模块?
在 xx 项目中负责报表/权限/大屏模块。难点是大量表格数据、复杂筛选、与 SAP 接口对接。我用虚拟滚动 + 分页 + 请求防抖,表格渲染从卡顿优化到流畅;权限用路由 meta + 动态菜单 + 按钮级指令实现。
2. 登录态过期怎么处理?
axios 响应拦截器判断 401,清 token,跳转登录并带 redirect;请求拦截器自动带 token;refresh token 方案(如有)在过期前静默刷新。
3. 线上 bug 怎么定位?
复现 → 看控制台/Network → 对比环境 → 加日志 → 本地修 → 回归 → 总结(加校验/测试/Review)。
十、后台管理项目加分项
| 场景 | 回答要点 |
|---|---|
| Token 登录 | localStorage/sessionStorage 存 token,拦截器注入 Header |
| 表格 CRUD | 分页参数、loading、空态、错误提示 |
| 导出 Excel | 后端导出 或 前端 xlsx 库 |
| 文件上传 | FormData、进度条、大小/类型校验 |
| 字典下拉 | 接口缓存、label/value 映射 |