第 1 轮 · 返回本次面经 · 已是最后一轮 →

本轮共 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到页面加载完成,发生了以下几个关键过程:

  1. DNS解析:浏览器将URL解析为对应的IP地址。这个过程涉及多级DNS服务器,从本地缓存开始,如果没有找到,则递归查询根域名服务器、顶级域名服务器,直到找到目标服务器的IP地址。
  2. TCP连接:浏览器通过三次握手与服务器建立TCP连接。一旦连接建立,浏览器可以发送HTTP请求。
  3. HTTP请求:浏览器构建HTTP请求报文,通过TCP连接发送到服务器。请求报文包含请求行、请求头和请求正文。
  4. 服务器处理请求:服务器接收HTTP请求,解析请求内容,执行相应的处理(如数据库查询、文件读取等),并构建HTTP响应报文。
  5. HTTP响应:服务器将响应报文通过TCP连接发送回浏览器。响应报文包含状态码、响应头和响应正文。
  6. 浏览器解析渲染:浏览器接收到HTTP响应后,解析HTML文档构建DOM树,解析CSS构建CSSOM树,合并两者形成渲染树,然后开始渲染页面。
  7. 连接结束:当浏览器完成页面渲染或收到服务器关闭连接的信号时,浏览器会发送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 的主要目的是将网站或应用的内容缓存到离用户更近的服务器上,从而提高内容加载速度和用户体验。

主要特点

  1. 内容缓存:CDN 会在多个节点(边缘服务器)上缓存网站的静态资源(如图片、CSS、JavaScript 文件等),并在用户请求时从离用户最近的节点提供这些资源。

  2. 负载均衡:CDN 可以将用户的请求分散到多个服务器上,减轻源服务器的负担,提高系统的稳定性和可靠性。

  3. 加速访问:通过就近访问,减少了从用户到源服务器的延迟,提升了加载速度。

  4. 提高安全性:CDN 提供的防护机制(如 DDoS 防护、Web 应用防火墙)可以保护网站免受网络攻击。

  5. 动态内容加速:一些 CDN 服务还支持动态内容加速,通过优化传输路径和协议,提升动态内容的加载速度。

工作原理

  1. 内容请求:用户请求访问网站内容时,DNS 解析会将用户的请求路由到离用户最近的 CDN 节点。

  2. 缓存命中:如果请求的内容在 CDN 节点的缓存中存在,节点直接将缓存的内容返回给用户。

  3. 缓存未命中:如果请求的内容不在缓存中,CDN 节点会从源服务器获取内容,然后将内容返回给用户,并缓存到节点上以备将来使用。

  4. 内容更新: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. 编程题:字段依赖关系解析

设计一个算法,解析动态表单中字段之间的依赖关系,并返回字段的合法渲染顺序。给定以下规则:

  1. 每个表单字段有一个唯一 id 和依赖的字段列表 dependencies(如 { id: ’email’, dependencies: [‘username’] } 表示 email 字段依赖 username 字段)。
  2. 若字段A依赖字段B,则字段B必须在字段A之前渲染。
  3. 可能存在循环依赖(如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
}

第 1 轮 · 返回本次面经 · 已是最后一轮 →