本轮共 13 道题。答案默认折叠,便于先自行作答。
1. 讲一下你的低代码平台架构设计
题目要点
分层架构、组件引擎、JSON Schema、热更新机制
参考答案
低代码平台采用分层架构设计:底层是组件引擎,负责组件的注册、渲染和生命周期管理;中间层是可视化编辑器,提供拖拽、属性配置和预览功能;上层是业务逻辑层,处理数据绑定、事件处理和页面路由。技术栈选择 Vue 3 + TypeScript,通过 JSON Schema 描述组件结构,使用虚拟 DOM 进行高效渲染。同时建立了组件市场和模板库,支持组件的版本管理和热更新机制。
2. 动态加载第三方组件时,如何解决样式冲突与DOM管理问题?
题目要点
样式隔离、容器化渲染、事件总线、冲突检测
参考答案
样式隔离通过 CSS Modules 或 Shadow DOM 实现,为每个组件创建独立的样式作用域。建立样式优先级管理机制,通过 CSS 变量和主题系统统一管理全局样式。DOM管理采用容器化策略,每个第三方组件在独立的容器中渲染,通过事件总线进行通信。同时实现样式冲突检测工具,在组件加载时自动检测并解决潜在冲突。
3. 当业务需求超出平台预设组件能力时,你们是怎么扩展的
题目要点
插槽扩展、开发工具链、能力评估、贡献机制
参考答案
建立组件扩展机制,支持通过插槽(slot)和高阶组件进行功能扩展。提供自定义组件开发工具链,包括脚手架、调试工具和发布流程。实现组件能力评估系统,当检测到需求超出现有能力时,自动推荐相似组件或提供扩展建议。同时建立组件贡献机制,允许开发者提交自定义组件到组件库,经过审核后供全平台使用。
4. 组件的热更新策略是怎么处理的,在不影响用户的使用情况下
题目要点
渐进式更新、状态保持、回滚机制、用户确认
参考答案
采用渐进式热更新策略,通过版本控制和灰度发布确保更新的安全性。实现组件状态保持机制,在更新过程中保存组件的当前状态,更新完成后恢复状态。建立回滚机制,当检测到更新异常时自动回滚到稳定版本。同时提供更新通知和用户确认机制,让用户选择合适的时机进行更新,避免在关键操作时中断用户体验。
5. 组件通信机制如何设计
题目要点
多层次通信、消息路由、协议标准化、调试工具
参考答案
设计多层次的通信机制:父子组件通过 props 和 events 通信,兄弟组件通过事件总线或状态管理,跨层级组件使用 provide/inject 或全局状态。建立消息路由系统,支持组件间的异步通信和消息队列。实现通信协议标准化,定义统一的消息格式和处理流程。同时提供通信调试工具,帮助开发者追踪消息流向和排查通信问题。
6. 移动端适配是什么做的
题目要点
响应式设计、弹性布局、手势支持、性能优化
参考答案
采用响应式设计和移动优先策略,使用 rem/vw 单位实现弹性布局。建立设备适配规则库,根据屏幕尺寸和设备类型自动调整组件样式。实现触摸手势支持,优化移动端的交互体验。同时建立性能优化机制,针对移动设备的性能特点进行资源加载和渲染优化,确保在低性能设备上也能流畅运行。
7. 低代码平台如何结合AI技术
题目要点
智能推荐、自然语言转换、布局优化、代码生成
参考答案
集成智能组件推荐系统,根据用户行为和页面内容推荐合适的组件。实现自然语言转换功能,用户可以通过描述需求自动生成页面结构。建立智能布局优化,AI分析页面结构并提供布局改进建议。同时集成代码生成能力,将可视化配置自动转换为高质量的代码,支持多种框架和技术栈的输出。
8. 详细讲一下URL输入到渲染的整个流程
题库原题:简单描述从输入网址到页面显示的过程
题目要点
当输入URL到页面加载完成,发生了以下几个关键过程:
- DNS解析:浏览器将URL解析为对应的IP地址。这个过程涉及多级DNS服务器,从本地缓存开始,如果没有找到,则递归查询根域名服务器、顶级域名服务器,直到找到目标服务器的IP地址。
- TCP连接:浏览器通过三次握手与服务器建立TCP连接。一旦连接建立,浏览器可以发送HTTP请求。
- HTTP请求:浏览器构建HTTP请求报文,通过TCP连接发送到服务器。请求报文包含请求行、请求头和请求正文。
- 服务器处理请求:服务器接收HTTP请求,解析请求内容,执行相应的处理(如数据库查询、文件读取等),并构建HTTP响应报文。
- HTTP响应:服务器将响应报文通过TCP连接发送回浏览器。响应报文包含状态码、响应头和响应正文。
- 浏览器解析渲染:浏览器接收到HTTP响应后,解析HTML文档构建DOM树,解析CSS构建CSSOM树,合并两者形成渲染树,然后开始渲染页面。
- 连接结束:当浏览器完成页面渲染或收到服务器关闭连接的信号时,浏览器会发送TCP连接关闭的信号,服务器收到后,双方断开连接。
参考答案
很多大公司面试喜欢问这样一道面试题,输入URL到看见页面发生了什么?
简单来说,共有以下几个过程:
- DNS解析
- 发起TCP连接
- 发送HTTP请求
- 服务器处理请求并返回HTTP报文
- 浏览器解析渲染页面
- 连接结束
下面我们来看看具体的细节。
DNS解析
DNS解析实际上就是寻找你所需要的资源的过程。假设你输入www.baidu.com,而这个网址并不是百度的真实地址,互联网中每一台机器都有唯一标识的IP地址,这个才是关键,但是它不好记,乱七八糟一串数字谁记得住啊,所以就需要一个网址和IP地址的转换,也就是DNS解析。
DNS解析其实是一个递归的过程。
输入www.google.com网址后,首先在本地的域名服务器中查找,没找到去根域名服务器查找,没有再去com顶级域名服务器查找,,如此的类推下去,直到找到IP地址,然后把它记录在本地,供下次使用。大致过程就是.-> .com ->google.com. -> www.google.com.。 (最后这个.对应的就是根域名服务器,默认情况下所有的网址的最后一位都是.,为了方便用户,通常都会省略,浏览器在请求DNS的时候会自动加上)
DNS优化
既然已经懂得了解析的具体过程,我们可以看到上述一共经过了N个过程,每个过程有一定的消耗和时间的等待,因此我们得想办法解决一下这个问题!
- DNS缓存
DNS存在着多级缓存,从离浏览器的距离排序的话,有以下几种: 浏览器缓存,系统缓存,路由器缓存,ISP服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。
- DNS负载均衡
比如访问baidu.com的时候,每次响应的并非是同一个服务器(IP地址不同),一般大公司都有成百上千台服务器来支撑访问。DNS可以返回一个合适的机器的IP给用户,例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等,这种过程就是DNS负载均衡。
发起TCP连接
TCP提供一种可靠的传输,这个过程涉及到三次握手,四次挥手。
三次握手

- 第一次握手:
客户端发送syn包(Seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;
- 第二次握手:
服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(Seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
- 第三次握手:
客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。
四次挥手
数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,假设客户端主动关闭,服务器被动关闭。

- 第一次挥手:
客户端发送一个FIN,用来关闭客户端到服务器的数据传送,也就是客户端告诉服务器:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,客户端依然会重发这些数据),但是,此时客户端还可以接受数据。
FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
- 第二次挥手:
服务器收到FIN包后,发送一个ACK给对方并且带上自己的序列号seq,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。
此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
- 第三次挥手:
服务器发送一个FIN,用来关闭服务器到客户端的数据传送,也就是告诉客户端,我的数据也发送完了,不会再给你发数据了。由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。
- 第四次挥手:
主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。
服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
至此,完成四次挥手。
发送HTTP请求
发送HTTP请求,就是构建HTTP请求报文,并通过TCP协议,发送到服务器指定端口。
请求报文由请求行,请求报头,请求正文组成。
服务器处理请求并返回HTTP报文
对TCP连接进行处理,对HTTP协议进行解析,并按照报文格式进一步封装成HTTP Request对象,供上层使用。这一部分工作一般是由Web服务器去进行,比如Tomcat, Nginx和Apache等Web服务器。
HTTP报文也分成三段:状态码,响应报头和响应报文。
浏览器解析渲染页面

这个图就是Webkit解析渲染页面的过程。
- 解析HTML形成DOM树
- 解析CSS形成CSSOM 树
- 合并DOM树和CSSOM树形成渲染树
- 浏览器开始渲染并绘制页面
9. CDN加速静态资源的原理
题库原题:什么是CDN?
题目要点
CDN 是一种优化内容分发和提升网站性能的技术,通过将内容分发到离用户更近的服务器上,减少延迟,提升访问速度,并增加网站的可用性和安全性。
参考答案
CDN(Content Delivery Network,内容分发网络)是一种通过分布在全球的服务器网络来加速内容传输的技术。CDN 的主要目的是将网站或应用的内容缓存到离用户更近的服务器上,从而提高内容加载速度和用户体验。
主要特点
内容缓存:CDN 会在多个节点(边缘服务器)上缓存网站的静态资源(如图片、CSS、JavaScript 文件等),并在用户请求时从离用户最近的节点提供这些资源。
负载均衡:CDN 可以将用户的请求分散到多个服务器上,减轻源服务器的负担,提高系统的稳定性和可靠性。
加速访问:通过就近访问,减少了从用户到源服务器的延迟,提升了加载速度。
提高安全性:CDN 提供的防护机制(如 DDoS 防护、Web 应用防火墙)可以保护网站免受网络攻击。
动态内容加速:一些 CDN 服务还支持动态内容加速,通过优化传输路径和协议,提升动态内容的加载速度。
工作原理
内容请求:用户请求访问网站内容时,DNS 解析会将用户的请求路由到离用户最近的 CDN 节点。
缓存命中:如果请求的内容在 CDN 节点的缓存中存在,节点直接将缓存的内容返回给用户。
缓存未命中:如果请求的内容不在缓存中,CDN 节点会从源服务器获取内容,然后将内容返回给用户,并缓存到节点上以备将来使用。
内容更新:CDN 节点会定期或按需更新缓存的内容,确保用户获取的是最新的资源。
10. 浏览器解析CSSOM时遇到未加载的CDN资源会阻塞渲染吗?如何规避?
题目要点
CSS阻塞渲染、资源预加载、异步加载、CSS分割
参考答案
CSS资源会阻塞渲染,因为浏览器需要完整的CSSOM才能进行布局和绘制。未加载的CDN资源会导致渲染延迟。规避方法包括:使用资源预加载(preload)提前加载关键CSS,将非关键CSS设置为异步加载,使用内联CSS处理首屏关键样式。同时可以实现CSS分割,将首屏必需的样式内联,其他样式异步加载,避免阻塞首次渲染。
11. 频繁修改element.classList会造成重排吗?
题目要点
布局属性影响、批量修改、CSS动画优化、重排避免
参考答案
频繁修改classList可能触发重排,取决于修改的CSS类是否影响布局属性。如果类中包含width、height、margin等布局属性,每次修改都会触发重排。优化方法包括:批量修改DOM,使用documentFragment或一次性修改className,将多个样式变更合并为单个类。同时可以使用transform和opacity等不触发重排的属性,或者通过CSS动画和transition实现平滑的样式变化。
12. 如何减少布局抖动?
题目要点
批量DOM操作、预设尺寸、骨架屏、图层隔离
参考答案
减少布局抖动需要从多个方面优化:避免在循环中读取布局属性(offsetWidth、scrollTop等),使用requestAnimationFrame批量处理DOM操作,预设容器尺寸避免内容加载时的尺寸变化。实现骨架屏和占位符,在内容加载前保持页面结构稳定。同时使用CSS的contain属性限制重排范围,将频繁变化的元素提升到独立图层,减少对其他元素的影响。
13. 编程题:字段依赖关系解析
设计一个算法,解析动态表单中字段之间的依赖关系,并返回字段的合法渲染顺序。给定以下规则:
- 每个表单字段有一个唯一 id 和依赖的字段列表 dependencies(如 { id: ’email’, dependencies: [‘username’] } 表示 email 字段依赖 username 字段)。
- 若字段A依赖字段B,则字段B必须在字段A之前渲染。
- 可能存在循环依赖(如A依赖B,B依赖A),此时应抛出错误。
输入:
const fields = [
{ id: 'username', dependencies: [] },
{ id: 'email', dependencies: ['username'] },
{ id: 'password', dependencies: ['email'] },
{ id: 'confirm_password', dependencies: ['password'] }
];
输出:
合法渲染顺序数组(如 [‘username’, ’email’, ‘password’, ‘confirm_password’])或抛出循环依赖错误。
题目要点
拓扑排序算法、依赖图构建、循环依赖检测、队列处理
参考答案
function resolveDependencies(fields) {
// 构建依赖图和入度统计
const graph = new Map()
const inDegree = new Map()
const fieldMap = new Map()
// 初始化
fields.forEach(field => {
fieldMap.set(field.id, field)
graph.set(field.id, [])
inDegree.set(field.id, 0)
})
// 构建依赖关系
fields.forEach(field => {
field.dependencies.forEach(dep => {
if (graph.has(dep)) {
graph.get(dep).push(field.id)
inDegree.set(field.id, inDegree.get(field.id) + 1)
}
})
})
// 拓扑排序
const queue = []
const result = []
// 找到入度为0的节点
inDegree.forEach((degree, fieldId) => {
if (degree === 0) {
queue.push(fieldId)
}
})
while (queue.length > 0) {
const current = queue.shift()
result.push(current)
// 处理当前节点的邻接节点
graph.get(current).forEach(neighbor => {
inDegree.set(neighbor, inDegree.get(neighbor) - 1)
if (inDegree.get(neighbor) === 0) {
queue.push(neighbor)
}
})
}
// 检查是否存在循环依赖
if (result.length !== fields.length) {
throw new Error('存在循环依赖')
}
return result
}