前端新的面试题又来咯
拖拽与 DOM
问题 1:拖拽过程中,如果原本的元素消失了,onDrop 还能触发么?具体表现是什么? 答案: 能触发。onDrop 事件会在放置的目标元素上触发。具体表现是:拖拽源元素在拖拽过程中被移除(如设置为 display: none 或从 DOM 中删除),只要鼠标在有效的放置目标上释放,onDrop 仍会触发。但是,event.dataTransfer 对象中关于拖拽源的数据可能变得不可靠或丢失。
问题 2:如何改变拖拽预览图? 答案: 使用 DataTransfer.setDragImage() 方法。
javascript
element.addEventListener('dragstart', (e) => {
// 创建一个自定义图像元素
const dragImage = document.createElement('div');
dragImage.textContent = '自定义预览';
dragImage.style.background = 'blue';
document.body.appendChild(dragImage);
// 设置拖拽预览图,后两个参数是鼠标相对于预览图的偏移量
e.dataTransfer.setDragImage(dragImage, 10, 10);
});
问题 3:如何让拖拽预览图有圆角? 答案: 为你在 setDragImage 中使用的自定义预览元素添加 CSS border-radius 属性即可。
css
#customDragImage {
border-radius: 10px;
}
ECharts 与响应式布局
问题 4:你用了 echart,如何让 echart 的内容跟随容器大小而变化?onResize 的时候要怎么做? 答案: 调用 ECharts 实例的 .resize() 方法。
javascript
// 初始化图表
const myChart = echarts.init(document.getElementById('chart-container'));
// 监听窗口 resize 事件
window.addEventListener('resize', () => {
myChart.resize();
});
问题 5:如果有可伸缩侧边栏之类的,导致容器因为其他原因发生了改变,应该用什么事件监听? 答案: 使用 ResizeObserver API。它可以监听任意 DOM 元素尺寸的变化,而不仅仅是窗口。
javascript
const resizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
myChart.resize();
}
});
resizeObserver.observe(document.getElementById('chart-container'));
部署、缓存与 CDN
问题 6:如果是 toc 产品,后端为 index.html 设置了 1 h 的 max age,请问从你重新构建代码发布,大概多久之后,所有用户都可以看到新界面? 答案: 理论上最长需要 1 小时(max-age 时间)加上 CDN 边缘节点的缓存过期时间。但由于用户浏览器缓存、CDN 配置(如是否启用了 stale-while-revalidate)等因素,实际时间可能更长或更短。保守估计,1-2 小时内所有用户会逐渐看到新界面。
问题 7:如果用户访问出现了白屏,是什么原因? 答案: 常见原因包括:
- JavaScript 加载失败: 资源路径错误、CDN 故障、网络问题。
- JavaScript 执行错误: 新版本代码存在语法错误或运行时错误,导致应用初始化失败。
- 资源加载超时。
- 浏览器兼容性问题。
- 入口 HTML 文件被缓存(如 index.html),但引用的新版本 JS/CSS 文件无法加载。
问题 8:toc 产品大量使用 cdn,请问 cdn 的定价大概多少?针对这样的定价策略,前端应该进行什么样的优化? 答案:
- 定价: 主流云厂商(阿里云、腾讯云)的 CDN 通常按 流量 和 HTTP 请求次数 计费。流量费用约 0.1 - 0.3 元/GB,请求费用约 0.01 - 0.05 元/万次。上传(回源)流量通常比下载(分发)流量贵。
- 前端优化:
- 资源压缩: 启用 Gzip/Brotli 压缩,图片使用 WebP 格式。
- 减少请求: 合并小文件(雪碧图)、利用 HTTP/2 多路复用。
- 缓存优化: 为静态资源设置长的
Cache-Controlmax-age,并使用内容哈希命名实现"永不过期"缓存。 - 代码分割与懒加载: 只加载当前页面需要的代码。
- 使用 CDN 预热功能,将重要资源提前推送到边缘节点。
问题 9:前端应用中?上传贵还是下载贵?上传快还是下载快? 答案: 在 CDN 语境下,上传(回源)通常比下载(分发)贵 。对于普通网络连接,下载速度通常远快于上传速度(家庭宽带上下行不对称)。
HTTP 缓存与多媒体
问题 10:图片设置协商缓存后,浏览器会整体缓存,视频能设置协商缓存么?视频的 http 返回内容与图片有什么区别? 答案:
- 视频可以设置协商缓存。
- 区别: 视频通常支持 范围请求(Range Requests) 。HTTP 响应头会包含
Accept-Ranges: bytes,并且在请求部分视频时,状态码是206 Partial Content而不是200 OK。这使得客户端可以只请求视频的某一部分,而不是整个文件。
问题 11:如何降低视频展示的成本? 答案:
- 使用高效的视频编码: 如 H.265/HEVC 或 AV1,在相同质量下文件更小。
- 自适应码流: 使用 HLS 或 DASH 等技术,根据用户网速动态切换不同清晰度的视频流。
- CDN 加速。
- 视频压缩与优化: 在可接受的范围内降低码率和分辨率。
- 懒加载: 视频进入视口后再开始加载。
设计与视觉
问题 12:既然有那么多 4k,2k,屏幕,为啥设计师的图还是以 1280 为主?是什么原因导致的? 答案: 主要原因:
- 历史与兼容性: 1280px 是长期以来最主流和稳定的屏幕宽度基准。
- 开发效率: 提供一个标准尺寸便于设计和开发对齐。
- 内容可读性: 过宽的单行文本不利于阅读,设计稿更关注核心内容的布局。
- 响应式设计: 设计以 1280px 为"桌面端"基准,然后通过响应式规则适配更大或更小的屏幕,而非直接为 4K 设计。
问题 13:高分辨率图片在低分辨率屏幕上为什么会糊?为什么会有图片明明正常但是一旦有动画之后也糊了?遇到这种问题如何解决? 答案:
- 原因1(高分辨率图在低分辨率屏): 浏览器需要将高像素密度的图片压缩到更少的物理像素上显示,这个"下采样"过程可能导致模糊和细节损失。
- 原因2(动画后变糊): 浏览器为了动画性能,可能会将动画元素提升到独立的合成层(GPU 渲染)。在层创建或变换过程中,如果处理不当(如不是整数像素移动),抗锯齿算法可能导致暂时性模糊。
- 解决方案:
- 使用
srcset和sizes属性为不同屏幕提供合适的图片尺寸。 - 对动画元素应用
transform: translateZ(0)或will-change: transform来触发 GPU 加速,并确保动画属性(如transform)的值为整数像素。 - 检查图片的原始尺寸是否与显示尺寸匹配。
- 使用
问题 14:设计师给了一套 SVG 图片,图片在 macos 上显示正常,但在 windows 下十分模糊,是你的代码问题还是设计师出的图片的问题?如果有这样的情况,最后是如何解决的? 答案: 这通常是 代码或环境问题,而非 SVG 源文件问题。SVG 是矢量格式,理论上应在任何分辨率下都清晰。
- 常见原因与解决方案:
- CSS 尺寸问题: 确保 SVG 的容器尺寸是整数像素,避免非整数缩放。
width: 100.5px可能导致模糊。 - Viewport 和 ViewBox 不匹配: 检查 SVG 代码中的
viewBox属性,并确保其与显示尺寸成比例。 - 浏览器渲染引擎差异: macOS 和 Windows 的字体渲染和图形抗锯齿算法不同。可以尝试为 SVG 添加
shape-rendering: geometricPrecision;CSS 属性。 - 位图嵌入: 如果 SVG 内嵌了模糊的位图,那么在 Windows 上也会模糊。
- CSS 尺寸问题: 确保 SVG 的容器尺寸是整数像素,避免非整数缩放。
TypeScript
问题 15:ts 开发下,interface 和对象类型声明可不可以用来声明数组和函数? 答案: 可以。
typescript
// 声明数组
interface NumberArray {
[index: number]: number; // 索引签名
}
type NumberArrayType = number[]; // 更简单的方式
// 声明函数
interface SearchFunc {
(source: string, subString: string): boolean; // 调用签名
}
type SearchFuncType = (source: string, subString: string) => boolean;
问题 16:如果声明函数,函数名可不可以重复?函数名如果重复,意味着什么? 答案: 在同一个作用域内,不允许 直接声明同名函数(会报错)。但在接口或类中,可以通过 函数重载 来声明多个同名但参数/返回值不同的函数签名。
typescript
// 函数重载
function greet(name: string): string;
function greet(age: number): string;
function greet(value: string | number): string {
// 实现...
}
重复的函数名意味着重载,调用时会根据传入的参数类型来匹配对应的签名。
Vue.js 深度知识
问题 17:如果你用的是 Vue,在 Vue 的 ts 用法中,哪些 api 的用法利用了 ts 这一特性? 答案:
- Props 类型定义: 使用
defineProps<{ ... }>()进行严格的类型检查。 - 组件实例类型: 使用
InstanceType<typeof MyComponent>来获取模板引用(ref)的类型。 - Composables 类型推断:
ref,computed会自动推断类型,reactive也会基于对象字面量推断。 - 事件类型定义: 使用
defineEmits<{ ... }>()来定义和校验组件发出的事件及其载荷。
问题 18:Vue 中,是不是所有情况下,template 都会自动获取 ref 的 .value 属性?如果不是,列举不会自动获取的情况。 答案: 不是所有情况。
- 会自动解包 .value 的情况:
- 在
<script setup>中声明的顶级ref,在模板中直接使用无需.value。 ref在模板中作为响应式对象的属性时(如obj.count),如果该ref是通过reactive访问的,会自动解包。
- 在
- 不会自动解包的情况:
- 在 Options API 的
data()函数中返回的对象的属性不是ref,无需解包。 - 当
ref是数组或 Map 等原生集合类型 的一个元素时,不会自动解包。例如:const list = ref([1, 2, 3]),在模板中使用list[0]不会自动解包,需要list[0].value(但在模板中应避免这样写,通常应解构到响应式对象中)。
- 在 Options API 的
问题 19:在 Vue 响应式系统中,给对象的子属性对象赋值一个新对象,是否还有响应式?详细阐述一下。 答案: 有响应式,但需要理解其原理。
javascript
const state = reactive({
nested: { count: 0 }
});
// 这个操作是响应式的
state.nested = { count: 1 }; // Vue 能检测到 `nested` 属性被重新赋值了
Vue 的响应式系统通过 Proxy 跟踪对象属性的访问和设置 。当你给 state.nested 赋一个新值时,Vue 会检测到这个设置操作,并触发相应的更新。同时,新赋值的对象 { count: 1 } 会被自动转换为一个响应式对象。
问题 20:如果你使用 Vue 和 pinia 开发了一个应用,用到了 persist 插件进行持久化,结果发现应用卡顿难以接受,请问卡顿最有可能是什么原因导致的? 答案: 最可能的原因是 持久化的数据量过大或序列化/反序列化操作过于频繁。
- 具体原因:
- 大数据量: 将整个庞大的状态树(如大型列表、复杂嵌套对象)持久化到
localStorage(同步 API),每次读写都会阻塞主线程。 - 频繁存储: 插件配置为每次状态变化都立即持久化(
debounce配置不合理),导致频繁的同步写入操作。
- 大数据量: 将整个庞大的状态树(如大型列表、复杂嵌套对象)持久化到
- 解决方案:
- 只持久化必要的状态,使用
paths选项排除大数据。 - 增加防抖(debounce)时间,减少持久化频率。
- 考虑使用异步存储后端,如
sessionStorage或IndexedDB(如果插件支持)。
- 只持久化必要的状态,使用
问题 21:Vue 的响应式系统,可否用一个 class 初始化的对象声明 reactive?会有什么后果?(如果他了解 React,问 React 的 useState 可不可以) 答案:
-
Vue: 可以,但不推荐,可能会丢失响应性。
- 后果: Vue 的
reactive()API 基于 ES6 Proxy。如果一个 class 实例的属性是在构造函数中通过this.key = value定义的,Proxy 可以正常工作。但如果属性是在 class 的方法中动态添加的,或者涉及继承链,Vue 可能无法可靠地追踪这些变化。最佳实践是使用普通对象字面量。
javascriptclass MyState { constructor() { this.count = 0; } // 可响应 increment() { this.count++; } // 可响应 addNewProp() { this.newProp = 1; } // 可能无法被 Vue 自动追踪 } const state = reactive(new MyState()); - 后果: Vue 的
-
React: 可以,但极其不推荐。
- 原因: React 的状态更新依赖于
setState或 setter 函数触发的重新渲染。直接修改 class 实例的属性(myState.count++)不会触发渲染,因为 React 无法感知到变化。你必须创建一个新的对象引用来驱动更新。
- 原因: React 的状态更新依赖于
问题 22:Vue 中的 pinia 和 pinia 配合依赖注入使用,与单纯的依赖注入有哪些区别? 答案:
- 单纯的依赖注入(provide/inject): 是一种直接的父子组件间传递数据/方法的方式。它是局部的 ,需要在上层组件
provide,在下层组件inject。数据本身不一定是响应式的(除非你提供的是一个ref或reactive对象),并且缺乏像 Pinia 那样的集中式状态管理、DevTools 集成和时间旅行调试能力。 - Pinia: 是全局状态管理库。它提供了一个中心化的 store,任何组件都可以导入并使用。状态天生是响应式的,并具有完善的模块化、TypeScript 支持和调试工具。
- Pinia + 依赖注入: 结合两者,通常是为了 更好的测试和解耦。你可以在根组件提供一个 Pinia store 的实例,然后在子组件中注入它。这样做的好处是,在测试时,你可以轻松地提供一个模拟的 store 实例给组件,而不需要依赖真实的全局 store 实例,使得测试更加隔离和容易。
工程化与业务逻辑
问题 23:如果项目主包太大,有什么方法可以优化? 答案:
- 代码分割(Code Splitting): 使用动态
import()语法实现路由级和组件级懒加载。 - Tree Shaking: 确保引入的第三方库支持 ES 模块,并只引入需要的部分。
- 分析包体积: 使用
webpack-bundle-analyzer等工具找出体积过大的模块。 - 压缩资源: 使用 Terser 压缩 JS,CssMinimizer 压缩 CSS。
- 优化第三方库: 考虑用更轻量的替代品(如 day.js 代替 moment.js)。
- 图片等资源外部化。
问题 24:如何解决删除一个项目之后,表格分页为空的问题? 答案: 在成功删除一项后,需要重新计算当前页码。
javascript
// 伪代码
const handleDelete = async (id) => {
await api.deleteItem(id);
// 删除成功后
const currentPage = pagination.current;
const totalAfterDelete = totalCount - 1;
const pageSize = pagination.pageSize;
// 如果当前页不再是有效页(例如,最后一页的唯一一项被删除了),则跳回上一页
if (currentPage > Math.ceil(totalAfterDelete / pageSize)) {
pagination.current = currentPage - 1;
}
// 重新获取数据
fetchData();
};
问题 25:上传时,假设后端没有用对象存储,你应该如何与后端配合,实现上传和下载进度? 答案:
-
上传进度: 使用 XMLHttpRequest 或 Axios 等库的进度事件。
javascriptaxios.post('/upload', formData, { onUploadProgress: (progressEvent) => { const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); // 更新进度条 } }) -
*** 下载进度: 同样利用进度事件,但需要后端在响应头中正确设置
Content-Length。javascriptaxios.get('/download/file.pdf', { onDownloadProgress: (progressEvent) => { const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); // 更新进度条 }, responseType: 'blob' // 重要:对于文件下载 })
HTTP 缓存策略与服务端渲染
问题 26:stale-while-revalidate 这字段出现在 http 什么位置,用来做什么的?它和服务端渲染有什么关系? 答案:
- 位置: 它是
Cache-Control响应头的一个指令,例如:Cache-Control: max-age=600, stale-while-revalidate=30 - 作用: 在
max-age(600秒)过期后,还有一个stale-while-revalidate(30秒)的"宽限期"。在这30秒内,浏览器会立即使用已过期的缓存(stale) 来响应用户请求,同时在后台向服务器发起验证请求(revalidate) 以获取最新内容并更新缓存。这极大地提升了用户体验,实现了"瞬间加载"。 - 与 SSR 的关系: 在 SSR 应用中,HTML 页面本身是动态的。如果对 HTML 页面设置
stale-while-revalidate,可以让用户在页面缓存过期后依然能快速看到内容(即使是略微过时的内容),同时在后台获取最新的服务端渲染页面。这完美结合了缓存的性能和 SSR 的 SEO/首屏优势。
问题 27:国内的生态对这个字段的支持程度如何?你会在项目中使用服务端渲染么? 答案:
- 支持程度: 主流现代浏览器(Chrome, Edge, Firefox, Safari)均已支持。国内移动端浏览器(如 UC、QQ)内核较老,可能存在兼容性问题,需根据目标用户群体决定是否使用。
- 是否使用 SSR:
- 会用 SSR 的场景: 对 SEO 有极高要求的 ToC 网站(如官网、博客、电商列表页)、对首屏加载速度有极致追求的应用。
- 不会用 SSR 的场景: 复杂的后台管理系统、强交互型 Web App、对搜索引擎无需求的内部工具。在这些场景下,SSR 带来的服务器成本和架构复杂性可能得不偿失。
数据迁移与错误处理
问题 28:假设你在 localStorage 或者 indexedDB 中存储了数据结构,但是版本更新后需要用新的数据结构,你会用什么方法进行迁移? 答案: 实施一个版本化迁移策略。
- 存储版本号: 在存储数据时,同时存储一个版本号(如
dataVersion: '1.0')。 - 启动时检查: 应用启动时,读取当前存储的版本号,并与代码中期望的最新版本号比较。
- 执行迁移: 如果版本不一致,则按顺序执行从一个版本到下一个版本的迁移函数。
- 更新版本号: 迁移成功后,更新存储中的版本号为最新版。
javascript
// 迁移函数示例
const migrateUserData = (oldData) => {
const currentVersion = oldData.version || '1.0';
if (currentVersion === '1.0') {
// 从 1.0 迁移到 2.0
const newData = {
version: '2.0',
profile: { ...oldData.userInfo } // 转换数据结构
};
localStorage.setItem('userData', JSON.stringify(newData));
return newData;
}
// ... 其他版本迁移
};
问题 29:假设是混合应用,用户可能用着各种历史版本,又该如何迁移? 答案: 迁移策略需要是幂等 和向后兼容的。
- 幂等: 同一个迁移函数执行多次的结果应该是一样的。避免因重复执行导致数据错误。
- 向后兼容: 新版本的代码应该能够读取和理解旧版本的数据结构。在迁移完成前,应用逻辑应能处理新旧两种数据结构。
问题 30:如果用户因为数据迁移出现了白屏,如何定位问题?如何及时补救? 答案:
- 定位问题:
- 错误监控: 接入 Sentry 等错误监控平台,捕获迁移过程中抛出的异常。
- 日志记录: 在迁移的关键步骤中加入详细的日志,记录旧版本号、新版本号、迁移结果等。
- 用户反馈: 建立用户反馈渠道,收集出现白屏的用户的浏览器版本、操作步骤等信息。
- 及时补救:
- 安全模式: 在迁移代码外围使用
try-catch。如果迁移失败,捕获错误,清除损坏的存储数据或回退到旧版本数据结构,并引导用户重新初始化应用。 - 热修复: 如果问题普遍,通过热修复平台紧急下发修复补丁,修正迁移逻辑。
- 版本回退: 在严重情况下,可以考虑回退应用版本。
- 安全模式: 在迁移代码外围使用
富文本与协作
问题 31:在基础富文本开发中,如果光标跨越了多个节点,如何正确通过 getSelection 进行选取? 答案: 使用 Selection 和 Range API。
javascript
const selection = window.getSelection();
// 获取用户选中的范围
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
const startContainer = range.startContainer; // 起始节点
const startOffset = range.startOffset; // 起始偏移量
const endContainer = range.endContainer; // 结束节点
const endOffset = range.endOffset; // 结束偏移量
// 现在你可以操作这个 range,例如插入节点、获取选中内容等
const selectedContent = range.cloneContents();
}
问题 32:如果不使用浏览器默认高亮,如何让这些选区高亮起来? 答案: 手动高亮。
- 遍历范围内容: 使用
Range的.cloneContents()或.extractContents()方法获取选区内的 DOM 节点。 - 包裹高亮元素: 遍历这些节点,用带有高亮样式(如
background-color: yellow;)的 HTML 元素(如<mark>或<span>)将文本节点包裹起来。 - 重新插入: 将处理后的节点重新插入到 DOM 中。更高级的做法是使用
CSS.highlightsAPI(实验性)。
问题 33:多人共同编辑的文档,如何保证云端缓存和本地缓存的一致性?这方面如何与后端配合? 答案: 使用 操作转换(OT) 或 冲突-free 复制数据类型(CRDT) 算法。
- 核心思想: 不是简单地覆盖数据,而是将用户的操作(如"在位置5插入字符'A'")作为指令发送到服务端。
- 与后端配合:
- 版本控制: 每个操作都有一个版本号(或向量时钟)。
- 服务端协调: 后端作为中央协调者,接收来自各客户端的操作,进行转换(解决冲突,例如两个用户同时在位置5插入不同字符),然后广播转换后的操作给所有客户端。
- 确认机制: 客户端只有收到服务端确认的操作才会应用到本地文档,确保最终一致性。
浏览器安全与图形学
问题 34:如果使用浏览器用户标识,并进行插件开发,插件中有个 iframe,请问 iframe 里面的用户标识和插件的用户标识,是同一个,还是不同的? 答案: 这取决于 iframe 的源(origin)。
- 同源 iframe: 是同一个。它们共享相同的 origin,因此共享相同的存储(如 localStorage)、Cookie 和用户标识。
- 跨域 iframe: 是不同的 。由于浏览器的同源策略,插件的上下文和跨域 iframe 的上下文是相互隔离的。它们拥有独立的存储空间和用户标识。通信需要通过
postMessageAPI。
问题 35:如何实现提取图片主题颜色和分级颜色的功能,如果实现了,对后端有什么要求? 答案:
-
前端实现:
- 使用
CanvasAPI 将图片绘制到画布上。 - 通过
getImageData()方法获取像素数据(一个包含 RGBA 值的巨大数组)。 - 使用聚类算法(如 K-means)或量化算法对像素颜色进行分组,找出主要的颜色簇。
javascriptconst img = new Image(); img.onload = function() { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); const imageData = ctx.getImageData(0, 0, img.width, img.height).data; // 分析 imageData,提取主题色 }; img.src = 'image.jpg'; - 使用
-
对后端的要求: 如果在前端处理大图片可能性能不佳,可以将此任务卸载到后端。后端需要提供相应的图片处理接口,接收图片文件,返回分析出的主题色数组。这要求后端有强大的图片处理能力和计算资源。
问题 36:hsl 和 oklch 颜色表示法相比 rgb 好在哪里?有哪些前端 UI 框架使用了这些色彩表示法? 答案:
- 优势:
- HSL(Hue, Saturation, Lightness): 更直观,易于人类理解和调整。比如,想得到一个更深的蓝色,只需减小 L(明度)值。
- OKLCH: 是基于 OKLab 色彩空间的改进版,比 HSL 和 RGB 在感知上更均匀。这意味着,颜色数值上的等量变化,会带来人眼感知上接近的视觉变化。这对于生成和谐的调色板、无障碍设计和主题切换至关重要。
- 使用框架:
- Tailwind CSS v3.x 已在其默认调色板中使用了 OKLCH。
- Adobe 的 Spectrum 设计系统也在向 OKLCH 迁移。
- 许多新兴的 CSS-in-JS 库和主题系统开始支持 OKLCH。
问题 37:如果要你利用其做一个多主题系统,你会如何设计架构? 答案: 采用 CSS 变量(Custom Properties) + OKLCH/HSL 的架构。
-
定义设计令牌: 使用 OKLCH 定义一套核心的颜色、间距、字体等变量。
css:root { --primary-color: lch(60% 60 250); /* 使用 OKLCH */ --text-color: lch(20% 5 250); --spacing-unit: 8px; } -
主题派生: 通过覆盖这些 CSS 变量的值来创建不同的主题。
css.theme-dark { --primary-color: lch(80% 50 250); --text-color: lch(90% 5 250); } -
组件使用: 在所有组件中只使用这些设计令牌变量。
css.button { background-color: var(--primary-color); color: var(--text-color); padding: calc(var(--spacing-unit) * 2); } -
动态切换: 通过 JavaScript 切换根元素或 body 元素的类名,即可实现主题的动态切换。OKLCH 的感知均匀性保证了不同主题下的视觉和谐度。
HTTP RESTful API
问题 38:http RESTFUL 请求中,put 和 post 分别代表什么?put 分别代表新增和修改的情况下,前后端的数据和逻辑会有怎样的差异? 答案:
- POST: 用于创建 新资源。它是非幂等 的(多次调用会产生多个资源)。通常,URL 指向的是资源集合(如
/api/users),请求体包含新资源的详细信息。响应通常返回201 Created和新资源的 URI。 - PUT: 用于更新/替换 现有资源,或在客户端知道资源最终 URI 时创建 资源。它是幂等 的(多次调用效果相同)。
- 用于修改(更新): URL 指向一个特定的资源(如
/api/users/123)。请求体应包含该资源的完整表示 。后端逻辑是用请求体提供的数据完全替换 目标资源。如果资源不存在,可以返回404 Not Found,或者根据约定创建它(返回201 Created)。 - 用于新增(创建): 客户端明确指定新资源的 URI(如
/api/users/123)。后端检查该 URI 是否存在资源。如果不存在,则创建;如果已存在,则完全替换。这要求客户端拥有分配资源 ID 的能力。
- 用于修改(更新): URL 指向一个特定的资源(如