令人汗流浃背的小红书一面

令人汗流浃背的小红书一面

「一面19问!小红书前端面试官的压迫感我算领教了——当Tailwind配置、Vue响应式原理、HTTP/2队头阻塞连环砸来时,我CPU差点干烧了🔥 这份硬核到骨髓的考卷,本质上是在筛选『能造轮子更懂拆轮子』的工程师!」


面试深度解析(技术人黑话版)

┌───────────────┬───────────────────────────────┐  
| 必杀考题       | 面试官真正在意的底层能力     |  
├───────────────┼───────────────────────────────┤  
| Tailwind配置   | CSS原子化工程落地能力         |  
| 容器查询替代   | 突破媒体查询的性能思维         |  
| Vue响应式原理  | 框架源码逆向解剖能力           |  
| 手写并发控制   | 高并发场景的救火实力           |  
| HTTP/2优化     | 网络层深度调优意识             |  
└───────────────┴───────────────────────────────┘  

血泪经验

  1. 当被问「Tailwind如何解决样式冲突」,要说清层叠优先级算法!important陷阱,而不是扯作用域
  2. 「keep-alive」连环追击时,务必抛出组件缓存策略内存泄露监控方案的trade-off
  3. 算法题考二叉树遍历?遍历顺序=业务场景!前序对应DOM序列化,后序适合依赖收集

相关面试题:

1. 项目中为何选择 Tailwind CSS

考察点:对 Tailwind CSS 的优势和适用场景的理解。参考答案: Tailwind CSS 是一种实用主义的 CSS 框架,它提供了低级的工具类,让开发者能够快速构建自定义的设计,而无需编写自定义的 CSS。选择 Tailwind CSS 的原因主要有以下几点:

  • 开发效率高:它提供了丰富的预设样式类,如布局、颜色、间距等,开发者可以直接在 HTML 中使用这些类名来快速实现样式,无需手动编写 CSS 规则,大大提高了开发速度。
  • 可维护性强:由于其低级工具类的特性,项目中的样式逻辑更加清晰,易于理解和维护。同时,通过配置文件可以轻松地定制样式,满足项目的特定需求。
  • 响应式设计友好:Tailwind CSS 内置了强大的响应式设计功能,通过简单的类名前缀(如 sm:md:lg: 等)可以轻松实现不同屏幕尺寸下的样式调整,无需额外编写媒体查询。
  • 与现代前端框架兼容性好:它能够很好地与 Vue、React 等现代前端框架结合使用,为项目提供一致的样式解决方案。

2. Tailwind 如何解决样式冲突

考察点:对 Tailwind CSS 样式冲突处理机制的理解。参考答案: Tailwind CSS 主要通过以下几种方式解决样式冲突:

  • 类名优先级:Tailwind CSS 的类名具有明确的优先级规则。当多个类名应用于同一个元素时,后应用的类名会覆盖先应用的类名。例如,text-red-500 和 text-blue-500 同时应用于一个元素时,text-blue-500 的样式会生效。
  • 配置文件定制:通过 Tailwind CSS 的配置文件,可以自定义样式规则,避免与项目中其他样式库的冲突。例如,可以调整颜色、间距等配置项,使其与项目现有的样式体系保持一致。
  • 命名空间:在项目中使用多个样式库时,可以通过为 Tailwind CSS 添加命名空间来避免类名冲突。例如,将所有 Tailwind CSS 的类名前缀改为 tw-,从而与其他样式库的类名区分开来。

3. Tailwind 的响应式断点(如 md:)底层如何实现

考察点:对 Tailwind CSS 响应式断点实现原理的理解。参考答案: Tailwind CSS 的响应式断点是通过 CSS 媒体查询实现的。在 Tailwind CSS 的配置文件中,定义了一系列的断点,每个断点对应一个屏幕宽度范围。例如:

module.exports = {
  theme: {
    screens: {
      'sm''640px',
      'md''768px',
      'lg''1024px',
      'xl''1280px',
    },
  },
};

当在 HTML 中使用响应式类名时,如 md:text-lg,Tailwind CSS 会生成相应的媒体查询规则:

@media (min-width: 768px) {
  .md\:text-lg {
    font-size1.25rem;
  }
}

这样,当屏幕宽度大于或等于 768px 时,md:text-lg 的样式就会生效。

4. CSS 容器查询了解么?能否替代媒体查询

考察点:对 CSS 容器查询(Container Queries)的理解及其与媒体查询的对比。参考答案: CSS 容器查询(Container Queries)是一种新的 CSS 特性,它允许基于容器的大小而不是视口的大小来应用样式。容器查询的基本语法如下:

@container (min-width: 300px) {
  p {
    font-size1.25rem;
  }
}

容器查询的容器需要通过 container-type 属性指定:

.container {
  container-type: inline-size;
}

容器查询与媒体查询的主要区别在于:

  • 作用范围:媒体查询基于视口大小,而容器查询基于容器大小。
  • 适用场景:媒体查询适用于全局的响应式设计,而容器查询适用于局部组件的响应式设计。 容器查询不能完全替代媒体查询,它们在不同的场景下各有优势。媒体查询更适合处理全局的布局和样式调整,而容器查询更适合处理局部组件的样式变化。

5. 响应式开发中如何避免窗口大小监听导致的重排抖动

考察点:对响应式开发中性能优化的理解。参考答案: 在响应式开发中,窗口大小变化会触发重排(Reflow)和重绘(Repaint),导致页面抖动。为了避免这种抖动,可以采取以下措施:

  • 使用节流(Throttle)或防抖(Debounce)技术:对窗口大小变化的监听事件进行节流或防抖处理,减少事件触发的频率。例如,使用 Lodash 的 throttle 或 debounce 方法:
    window.addEventListener('resize', _.throttle(handleResize, 100));
  • 减少 DOM 操作:尽量减少在窗口大小变化时对 DOM 的操作,避免触发不必要的重排和重绘。
  • 使用 CSS 动画:对于一些需要动态调整的元素,可以使用 CSS 动画来实现平滑的过渡效果,而不是直接修改样式。

6. requestAnimationFrame 与 requestIdleCallback 在渲染优化中的执行时机差异?谁优先触发

考察点:对 requestAnimationFrame 和 requestIdleCallback 的理解及其在渲染优化中的应用。参考答案

  • **requestAnimationFrame**:用于在浏览器重绘之前执行代码,通常用于动画和视觉效果的更新。它会在下一次重绘之前被调用,确保动画的平滑性。
  • **requestIdleCallback**:用于在浏览器空闲时执行代码,通常用于非关键任务,如数据处理、DOM 操作等。它会在浏览器完成当前任务后,且在下一次重绘之前有空闲时间时被调用。 在执行时机上,requestAnimationFrame 优先于 requestIdleCallback 触发。因为 requestAnimationFrame 用于确保动画的平滑性,而 requestIdleCallback 用于处理非关键任务,浏览器会优先保证动画的流畅性。

7. Vue 中 defineAsyncComponent 如何结合 Webpack 的代码分割(Code Splitting)实现懒加载

考察点:对 Vue 的异步组件和 Webpack 代码分割的理解。参考答案: 在 Vue 中,defineAsyncComponent 可以用来定义异步组件,结合 Webpack 的代码分割实现懒加载。具体实现如下:

import { defineAsyncComponent } from 'vue';

const AsyncComponent = defineAsyncComponent(() =>
  import('./components/MyComponent.vue')
);

Webpack 会自动将 import() 调用的模块分割成单独的代码块,并在需要时动态加载。这样,只有当 AsyncComponent 被实际使用时,对应的代码块才会被加载,从而实现懒加载。

8. 动态加载失败时的降级方案有没有

考察点:对动态加载失败处理机制的理解。参考答案: 在动态加载组件或模块时,可能会出现加载失败的情况。为了处理这种情况,可以提供降级方案:

  • 显示错误信息:在加载失败时,显示友好的错误信息提示用户。
  • 提供默认内容:加载失败时,提供一个默认的组件或内容作为替代。
  • 重试机制:在加载失败后,提供自动重试或手动重试的机制。 例如,在 Vue 中可以这样实现:
const AsyncComponent = defineAsyncComponent({
loader() =>import('./components/MyComponent.vue'),
errorComponent() =>import('./components/ErrorComponent.vue'),
delay200,
timeout3000,
onError(error, retry, fail) => {
    if (error.name === 'NetworkError') {
      retry();
    } else {
      fail();
    }
  },
});

9. keep-alive 的原理讲一下

考察点:对 Vue 中 keep-alive 的原理和实现机制的理解。参考答案keep-alive 是 Vue 中的一个内置组件,用于缓存动态组件,避免重复渲染。它的原理如下:

  • 缓存机制keep-alive 会将缓存的组件实例存储在一个缓存对象中。当组件被切换时,keep-alive 会检查缓存对象中是否已经存在该组件的实例。如果存在,则直接从缓存中取出实例并复用;如果不存在,则创建一个新的实例并将其存储到缓存中。
  • 生命周期钩子keep-alive 提供了两个生命周期钩子:activated 和 deactivated。当组件被缓存时,会触发 deactivated 钩子;当组件从缓存中被激活时,会触发 activated 钩子。
  • 配置选项keep-alive 提供了一些配置选项,如 include 和 exclude,用于指定哪些组件需要被缓存。

10. 多级路由下如何通过 include 精准控制组件缓存

考察点:对 Vue 路由和 keep-alive 的结合使用。参考答案: 在多级路由下,可以通过 include 属性精准控制组件缓存。include 可以是一个字符串数组,指定哪些组件需要被缓存。例如:

<keep-alive :include="['Home', 'About']">
  <router-view />
</keep-alive>

在多级路由中,可以通过动态计算 include 的值来实现精准控制。例如:

computed: {
  include() {
    return this.$route.matched.map(route => route.name);
  },
}

这样,只有当前路由及其子路由对应的组件会被缓存。

11. 缓存过多导致内存泄露如何排查

考察点:对内存泄露排查方法的理解。参考答案: 缓存过多可能导致内存泄露,可以通过以下方法排查:

  • 使用开发者工具:在浏览器的开发者工具中,使用内存分析工具(如 Chrome 的 Memory tab)来查看内存使用情况,找出占用内存较多的对象。
  • 监控缓存大小:在代码中添加监控逻辑,定期检查缓存的大小。如果缓存大小超过一定阈值,可以触发警告或清理操作。
  • 分析代码逻辑:检查代码中是否存在未正确释放缓存的逻辑,例如未正确调用 deactivated 钩子或未正确清理定时器等。

12. vite 的热更新讲一下

考察点:对 Vite 热更新机制的理解。参考答案: Vite 的热更新(HMR)是一种在开发过程中自动更新代码的技术,无需重新加载整个页面。Vite 的 HMR 机制基于 WebSocket 和模块热替换(HMR)协议。具体工作原理如下:

  • WebSocket 通信:Vite 通过 WebSocket 与客户端建立通信,当文件发生变化时,服务器会通过 WebSocket 向客户端发送更新信息。
  • 模块热替换:客户端接收到更新信息后,会根据 HMR 协议加载新的模块代码,并替换掉旧的模块代码,从而实现热更新。

13. vite 热更新(HMR)速度为何优于 Webpack

考察点:对 Vite 和 Webpack 热更新机制的对比。参考答案: Vite 的 HMR 速度优于 Webpack 的主要原因如下:

  • 原生 ES 模块:Vite 使用原生 ES 模块进行代码加载,避免了 Webpack 的打包和模块解析过程,大大提高了加载速度。
  • WebSocket 通信:Vite 使用 WebSocket 与客户端通信,通信效率更高,减少了更新信息的传输延迟。
  • 增量更新:Vite 只会更新发生变化的模块,而不是重新打包整个项目,进一步提高了更新速度。

14. http2.0 相对于 http1.0 有哪些优化

考察点:对 HTTP/2 和 HTTP/1.0 的对比。参考答案: HTTP/2 相对于 HTTP/1.0 的优化主要包括以下几点:

  • 多路复用(Multiplexing):HTTP/2 支持在一个 TCP 连接上同时发送多个请求和响应,解决了 HTTP/1.1 的队头阻塞问题。
  • 二进制分帧:HTTP/2 使用二进制分帧机制,将 HTTP 消息分割成多个帧,提高了传输效率。
  • 头部压缩(HPACK):HTTP/2 使用 HPACK 算法对 HTTP 头部进行压缩,减少了头部信息的传输量。
  • 服务器推送(Server Push):HTTP/2 支持服务器主动向客户端推送资源,减少了客户端的请求次数。

15. HTTP/2 的多路复用(Multiplexing)如何解决队头阻塞?对比 HTTP/1.1 的长连接优化

考察点:对 HTTP/2 多路复用机制的理解及其与 HTTP/1.1 长连接的对比。参考答案: HTTP/2 的多路复用通过在同一个 TCP 连接上同时发送多个请求和响应,解决了 HTTP/1.1 的队头阻塞问题。在 HTTP/1.1 中,由于协议限制,一个 TCP 连接在同一时间只能处理一个请求,如果一个请求被阻塞,后续的请求也会被延迟。HTTP/2 的多路复用机制允许同时处理多个请求,即使某个请求被阻塞,其他请求仍然可以继续进行,从而提高了资源加载效率。 与 HTTP/1.1 的长连接优化相比,HTTP/2 的多路复用更加高效。HTTP/1.1 的长连接虽然可以减少连接建立的开销,但仍然无法解决队头阻塞问题。而 HTTP/2 的多路复用从根本上解决了这一问题,提高了网络利用率和资源加载速度。

16. HTTPS 的混合加密机制如何防止中间人攻击

考察点:对 HTTPS 加密机制的理解。参考答案: HTTPS 使用混合加密机制来防止中间人攻击,主要包括以下步骤:

  • 证书验证:客户端通过验证服务器的 SSL/TLS 证书来确认服务器的身份。证书由受信任的证书颁发机构(CA)签发,包含服务器的公钥和身份信息。
  • 密钥交换:客户端和服务器通过密钥交换算法(如 RSA、ECDH 等)协商出一个对称加密密钥。这个过程使用服务器的公钥进行加密,确保密钥交换的安全性。
  • 对称加密:协商出的对称加密密钥用于加密客户端和服务器之间的通信数据。对称加密算法(如 AES)的效率较高,适合大量数据的加密。 通过证书验证和密钥交换,HTTPS 确保了通信的机密性和完整性,防止中间人攻击。

17. 浏览器的渲染流程详细讲一下

考察点:对浏览器渲染流程的理解。参考答案: 浏览器的渲染流程主要包括以下步骤:

  1. 加载 HTML:浏览器从服务器加载 HTML 文件。
  2. 构建 DOM 树:浏览器解析 HTML 文件,构建 DOM 树。
  3. 加载 CSS:浏览器加载并解析 CSS 文件,构建 CSSOM 树(CSS 对象模型树)。
  4. 构建渲染树:浏览器将 DOM 树和 CSSOM 树合并,构建渲染树。渲染树表示了页面中可见的元素及其样式。
  5. 布局(Layout):浏览器计算每个元素的位置和大小,这个过程也称为重排(Reflow)。
  6. 绘制(Paint):浏览器将渲染树中的元素绘制到屏幕上,这个过程也称为重绘(Repaint)。
  7. 合成(Composite):浏览器将绘制好的图层合成到屏幕上,最终显示页面内容。

18. 重排(Reflow)与重绘(Repaint)的触发条件及性能影响

考察点:对重排和重绘的理解及其对性能的影响。参考答案

  • 重排(Reflow):当 DOM 树的结构或元素的几何属性发生变化时,会触发重排。例如,改变元素的宽度、高度、位置等。重排是一个相对耗时的操作,因为它需要重新计算元素的布局。
  • 重绘(Repaint):当元素的样式发生变化,但不涉及几何属性时,会触发重绘。例如,改变元素的颜色、背景等。重绘相对重排来说,性能影响较小,但它仍然会消耗一定的资源。 为了优化性能,应尽量减少重排和重绘的次数。可以通过以下方法优化:
  • 批量操作 DOM:将多个 DOM 操作合并在一起,减少重排和重绘的次数。
  • 使用 CSS 动画:使用 CSS 动画来实现视觉效果,而不是直接修改元素的样式。
  • 避免过度使用绝对定位和浮动:这些布局方式容易导致重排。

19. 微任务(Microtask)与宏任务(Macrotask)的执行顺序

考察点:对 JavaScript 事件循环机制的理解。参考答案: 在 JavaScript 的事件循环中,微任务和宏任务的执行顺序如下:

  1. 执行同步代码:首先执行当前执行栈中的同步代码。
  2. 执行微任务队列:在每次事件循环的末尾,执行微任务队列中的所有微任务。微任务包括 Promise 的回调、MutationObserver 的回调等。
  3. 执行宏任务:在微任务队列执行完毕后,执行宏任务队列中的下一个宏任务。宏任务包括 setTimeoutsetIntervalrequestAnimationFrame 等。
  4. 再次执行微任务队列:在每次宏任务执行完毕后,再次执行微任务队列中的所有微任务。 这个过程会不断循环,直到所有任务都执行完毕。

20. requestAnimationFrame 属于哪一类

考察点:对 requestAnimationFrame 的理解。参考答案requestAnimationFrame 属于宏任务。它用于在浏览器重绘之前执行代码,通常用于动画和视觉效果的更新。requestAnimationFrame 会在下一次重绘之前被调用,确保动画的平滑性。

21. 实现发布订阅模式

考察点:对发布订阅模式的理解和实现能力。参考答案: 发布订阅模式是一种设计模式,用于实现对象之间的解耦。以下是发布订阅模式的实现:

class EventEmitter {
constructor() {
    this.events = {};
  }

  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }

  emit(event, ...args) {
    if (this.events[event]) {
      this.events[event].forEach(callback => {
        callback(...args);
      });
    }
  }

  off(event, callback) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter(cb => cb !== callback);
    }
  }
}

const emitter = new EventEmitter();

emitter.on('message', (msg) => {
console.log(`Received message: ${msg}`);
});

emitter.emit('message''Hello, world!');

22. 实现并发控制

考察点:对并发控制的理解和实现能力。参考答案: 并发控制可以通过多种方式实现,以下是使用 async 和 await 实现并发控制的示例:

async function runTasks(tasks, concurrency{
const results = [];
const runningTasks = [];

for (const task of tasks) {
    const runningTask = task().then(result => {
      runningTasks.splice(runningTasks.indexOf(runningTask), 1);
      results.push(result);
    });

    runningTasks.push(runningTask);

    if (runningTasks.length >= concurrency) {
      awaitPromise.race(runningTasks);
    }
  }

returnPromise.all(runningTasks).then(() => results);
}

// 示例任务
const tasks = [
() =>newPromise(resolve => setTimeout(() => resolve('Task 1'), 1000)),
  () => newPromise(resolve => setTimeout(() => resolve('Task 2'), 500)),
  () => newPromise(resolve => setTimeout(() => resolve('Task 3'), 2000)),
];

runTasks(tasks, 2).then(results => {
console.log(results);
});

在这个示例中,runTasks 函数接受一个任务数组和并发数,通过 Promise.race 控制同时运行的任务数量。

面试感受

这场面试像一次全方位的技术扫描:

  1. 工具链深度:Tailwind的配置扩展、Webpack/Vite的优化差异等问题,直指工程化能力的核心;
  2. 框架底层理解:Vue响应式原理与动态组件设计,要求吃透源码级逻辑;
  3. 性能优化实战:从防抖策略到容器查询替代方案,都是高并发场景的硬核考点;
  4. 手写代码环节(并发控制/发布订阅)尤为关键——思路清晰比完美实现更重要。

整体感受:小红书对候选人要求是「深度优先」 ,既要掌握技术选型背后的权衡,又能把基础知识(如事件循环/HTTPS握手)映射到真实业务场景。

最后

还没有使用过我们刷题网站(https://fe.ecool.fun/)或者刷题小程序的同学,如果近期准备或者正在找工作,千万不要错过,题库已经更新1600多道面试题,除了八股文,还有现在面试官青睐的场景题,甚至最热的AI与前端相关的面试题已经更新,努力做全网最全最新的前端刷题网站。


有会员购买、辅导咨询的小伙伴,可以通过下面的二维码,联系我们的小助手。

图片