华慧(广州) - 前端面试

面试时间:2025-08-08

公司全称:华慧(广州)有限公司

公司位置:海珠区 SIP 升海科创园 E 栋 201

面试题

说说你项目中遇到的难点?
查看答案
  1. 开发基础框架系统时,我需要考虑如何组织目录结构以及减少业务模块的上手难度,要保证基础框架代码与业务模块代码相互独立,基础框架的升级不影响业务代码的开发,还要想办法通过基础框架层面自动配置的方式免去业务模块手动去配置 Store、加载 SVG 资源的工作。我去网上找了一圈也没有发现有人在讨论这个问题,只看到大家有讨论 monorepo(多包单仓库)相关的问题,但是 monorepo 在多个完全没有库相互调用的项目团队之间并没有优势,而且对着项目数量增多,项目体积越来越大,单仓库的权限也不好分配,所以我没有使用这个方案。最后是在 src 下直接分了 base 和 business 两个目录,分别管理基础框架代码和业务模块代码,对于全局状态 Store,基础框架自动关联业务模块 Store,并且使用 requireContext 自动加载业务模块引入的 SVG 资源,免去业务模块手动配置的工作量。

  2. 开发 ERP 系统时,由于一个业务线涉及到的尺寸、金额的计算量很多且很复杂,开发完成后页面卡顿非常严重,经常就是输入一个值就要卡顿等待很久页面才重新渲染好。一开始的解决方案是将功能做更彻底的组件拆分,再借助 memo、useCallback、useMemo 做组件、函数、数据的缓存操作,虽然有明显效果,但是依然卡顿。后面用 React Devtools 分析用户输入时页面的渲染情况时发现,由于各个子组件之间的数据往来太过密切,基本上一个组件的值修改,其他的组件中很多值也要修改,所以导致渲染特别频繁,导致卡顿严重,既然渲染频繁,所以我的想法就是减少出发渲染的频次,于是将部分 useState 定义的数据修改为使用 useRef 来存储,在最后一次 useState 的数据修改前,将所有 useRef 定义的数据都先计算修改好,再执行 useState 的 set 操作,这样就解决了页面不卡顿的问题。为了进一步提高用户体验,我后续又引入了 web worker 让计算工作放到后台线程去执行,就不会影响主线程的渲染了。


如果说所有团队的技术栈都不一样,你对基础框架做技术选项时如何抉择呢?
查看答案

如果确实存在所有团队的技术栈不一致的情况,我首先考虑的会是大家对某个技术的熟悉程度,来决定技术选型的优先级。如果说大家普遍对 Vue2 的熟悉程度比较高,我会优先考虑开发 Vue2 版本的基础框架,先让大家都用起来,后续还是会再开发 Vue3、React 版本的基础框架,以提升开发体验和性能。总的来说,就是会根据大家的技术水平来决定技术栈选型的优先级。


你为什么不考虑使用 monorepo 的方式来开发你们的基础框架?
查看答案

monorepo(多包单仓库) 在项目多的时候会导致硬盘占用很大,而且项目的权限也不好分配。我在网上也看了别人对 monorepo 的看法,普遍认为如果多个项目之前不涉及到项目的库调用,其实是不适合使用 monorepo 的,一个项目一个仓库更方便管理。


项目中你遇到性能问题时,你是如何去检测和观察问题出在哪里的?
查看答案

使用 Vue Devtools、React Devtools 进行页面渲染的跟踪;

使用浏览器自带的 ​​Performance 面板录制完整加载过程,分析主线程活动。


是否了解浏览器 Performance 面板的功能,比如火焰图怎么看?
查看答案

Performance 面板核心功能包括性能录制与分析、关键性能指标视图。

火焰图基本结构:

  • ​​Y轴(垂直方向)​​:表示调用栈深度,顶层为当前执行函数,下方是其调用者;
  • X轴(水平方向)​​:表示时间分布,色块宽度与函数执行时间成正比(越宽=耗时越长);
  • 颜色编码​​:通常随机分配,仅用于区分不同函数(无特殊含义)。

Webpack 和 Vite 有什么区别?
查看答案

Webpack 基于打包器(Bundler)构建,开发阶段需要全量打包后才能启动;而 Vite 是基于原生 ESM 按需编译的,开发阶段通过原生 ESM 按需加载 + 预打包依赖的方式,启动更快。

Webpack 是基于 HMR 的全量/增量更新(100~500ms)实现的热更新;而 Vite 是原生 ESM 的即时 HMR(<50ms),框架组件支持边界更新(如只更新某个 .vue 文件的变化),热更新更快速。

Webpack 底层语言是 Javascript;Vite 底层部分核心模块是 Rust 语言编写的,速度更快,性能更好。


Vite 为什么快?
查看答案

Vite 利用了现代浏览器对 ES Modules 的原生支持,在开发环境下采用无打包过程、按需编译、依赖预构建、Go 和 Rust 编写核心模块、多级缓存等方式提升开发构建的速度。


说说 Webpack 的执行流程?
查看答案

首先是初始化阶段:读取配置、创建 Compiler 对象、加载插件;

其次是编译与依赖图构建阶段:解析入口文件、递归构建依赖图、模块封装;

再次是代码分割(Chunk 生成)阶段:按策略分块、优化处理;

最后是输出阶段:生成运行时代码、写入文件系统、统计输出。


什么是事件循环?
查看答案

事件循环是 JS 处理异步操作的核心机制,用于防止主线程阻塞。

从调用栈顶部开始执行同步代码,遇到异步操作时:宏任务(setTimeout 等)的回调放入宏任务队列;微任务(Promise 等)的回调放入微任务队列。继续将同步代码执行完,当调用栈为空时,检查并执行所有微任务,然后执行一个宏任务,后续一直重复上传过程,这就是事件循环。


说说你对 HTTP 协议的理解?
查看答案

HTTP 协议是客户端与服务器之间进行通信的规则,它底层基于 TCP/IP 协议。


TCP 通信的流程?
查看答案

TCP 通信需要经过三次握手建立连接,四次挥手断开连接。

三次握手:第一次是客户端向服务器发送请求连接的状态码包;第二次是服务器向客户端发送允许连接的状态码包;第三次是客户端向服务器发送已接受到服务器消息的状态码吧。三次握手完成,双方开始进行通信。

四次挥手:第一次是客户端向服务器发送申请断开连接的状态码包;第二次是服务器接收到客户端断开连接的请求后,立即给客户端发送一个已接收到断开申请的状态码包,此时服务器还需要将剩余未处理完的数据处理完成并返回给客户端;第三次服务器给客户端是发送一个数据已全部发送完毕是否确定断开的状态码包;第四次是客户端向服务器发送确认断开连接的状态码包。四次挥手完成,双方断开连接。


HTTP 和 HTTPS 的区别?HTTPS 进行加密传输的原理?
查看答案

区别:

HTTP 的数据是明文传输,HTTPS 是密文传输;

HTTP 更快(无加密开销),HTTPS 稍慢(加密/解密需要计算)。

HTTPS 进行加密传输的原理:使用非对称加密加密对称加密的密钥,双方拿到对称加密的密钥后,使用对称加密进行通信。


浏览器的缓存策略有哪些?
查看答案

强缓存:在资源设定的有效期内,浏览器直接从本地获取资源,完全不与服务器交互。

协商缓存(304 缓存):浏览器向服务器验证资源是否有更新,无更新时返回 304 状态码且使用本地资源;有更新时返回 200 状态码且返回最新资源。


如何浏览器的缓存行为进行控制?
查看答案

服务端通过设置 HTTP 响应头来控制浏览器缓存行为:

  • HTTP1.0 通过 Expires 字段进行设置;
  • HTTP1.1 通过 Cache-Control 字段进行设置。(常用值包括:max-age=3600:设置资源有效期no-cache:禁用强缓存,使用协商缓存no-store:完全禁用缓存

Vue2 和 Vue3 的区别?
查看答案

写法:Vue2 仅支持选项式 API 方式;Vue3 既支持选项式 API 又支持组合式 API;

响应式:Vue2 使用 Object.definePropety,对象新增属性默认无响应;Vue3 使用 Proxy 自动劫持并监听整个对象变化;

编译:Patch Flag/Block Tree 减少 Diff 范围;

体积:Tree Shaking 支持更好。


Vue2 对一个已经定义了的响应式对象添加新属性,如何保证新添加的属性也是响应式的?
查看答案
  1. 通过 this.$set(obj, 'newKey', newValue)
  2. 通过新对象覆盖旧对象的方式 this.obj = {...obj, newKey: newValue}
  3. 通过在 data 中预先声明所有可能属性的方式(即使初始值为 null 或 undefined)。
data() {
  return {
    obj: {
      key1: 'value1',
      key2: 'value2',
      newKey: undefined, // 预先声明
    }
  }
}

TypeScript 中你常用的语法有哪些?
查看答案

type 定义类型别名;interface 定义接口;

| 联合类型;& 交叉类型;[key: string]: value 索引签名;[key in XXX]: value 映射类型;<T> 泛型;Partial<Interface> 属性可选;Readonly<Interface> 只读类型;enum 枚举;as xxx 类型断言;keyof 获取键类型;Record<K, V> 定义动态对象类型;? 条件类型。


TypeScript 如何解决命名冲突问题?
查看答案
  1. 通过 namespace(命名空间)避免命名冲突;
namespace MyNamespace {
    export class MyClass {
        // ...
    }
}

namespace OtherNamespace {
    export class MyClass {
        // ...
    }
}

const obj1 = new MyNamespace.MyClass();
const obj2 = new OtherNamespace.MyClass();
  1. 通过 ES6 模块系统避免命名冲突;
// file1.ts
export class MyClass {
    // ...
}

// file2.ts
import { MyClass as MyClass1 } from './file1';
import { MyClass as MyClass2 } from 'some-library';

const obj1 = new MyClass1();
const obj2 = new MyClass2();
  1. 使用类型别名 as 避免命名冲突
import { SomeType as LibraryType } from 'some-library';

type MyType = {
    // 自定义类型
};

function process(value: LibraryType | MyType) {
    // ...
}

你是通过哪些国内外的途径去学习新技术的?
查看答案

国内的包括掘金、CSDN、简书、博客园、知乎、哔哩哔哩、Gitee等;

国外的包括Stack Overflow、YouTube、GitHub等。


笔试题

Contributors: tanqin