前端八股文面经大全:小红书前端日常实习(2026-1-5)·面经深度解析

前言

大家好,我是木斯佳。

在这个春节假期,当大家都在谈论返乡、团圆与休息时,作为一名技术人,我的思考却不由自主地转向了行业的「冬」与「春」。

相信很多人都感受到了,在AI浪潮的席卷之下,前端领域的门槛在变高,纯粹的"增删改查"岗位正在肉眼可见地减少。曾经热闹非凡的面经分享,如今也沉寂了许多。但我们都知道,市场的潮水退去,留下的才是真正在踏实准备、努力沉淀的人。学习的需求,从未消失,只是变得更加务实和深入。

正值春节,也是复盘与规划的好时机。结合CSDN这次「春节代码贺新年」活动所提倡的"用技术视角记录春节、复盘成长",我决定在这个假期持续更新专栏,帮助年后参加春招的同学。

这个专栏的初衷很简单:拒绝过时的、流水线式的PDF引流贴,专注于收集和整理当下最新、最真实的前端面试资料。我会在每一份面经和八股文的基础上,尝试从面试官的角度去拆解问题背后的逻辑,而不仅仅是提供一份静态的背诵答案。无论你是校招还是社招,目标是中大厂还是新兴团队,只要是真实发生、有价值的面试经历,我都会在这个专栏里为你沉淀下来。

温馨提示:市面上的面经鱼龙混杂,甄别真伪、把握时效,是我们对抗内卷最有效的武器。

在这个假期,让我们一起充电,为下一个技术春天做好准备。

面经原文内容

📍面试公司:小红书

🕐面试时间:1月5日

💻面试岗位:前端日常实习

⏱️面试时长:未提及

❓面试问题:

框架基础

  1. 自我介绍
  2. 讲讲对React框架的理解
  3. React和Vue的区别
  4. 详细讲讲React Fiber底层
  5. React Fiber时间优先级如何实现

构建工具

  1. Vite 和 Webpack区别

  2. 为什么Vite快

  3. 详细讲讲Webpack构建过程流程 和 Tree Shaking

网络协议

  1. http1.1 http2.0详细区别讲讲

TypeScript

  1. 大致聊聊TS

  2. TS的工具类型

JS核心

  1. 详细讲讲Promise
  • 手写Promise.all和Promise.allSettled
  1. 对闭包的了解
  • 手写防抖和节流

React Hooks

  1. 讲讲React常见的hooks

  2. 手写useMemo模仿useCallback

算法

  1. 算法题: 给你一个整数数组nums,找出一个具有最大和的连续子数组(至少包含一个元素),返回最大和

项目

  1. 讲一个你最熟悉的项目以及亮点难点

进阶

  1. 详细讲讲Web Worker的通信机制 除postmessage 和 onmessage外 回调之类的

反问

  1. 反问

来源:牛客网 雾泊屿by

📝 小红书日常实习前端一面·深度解析

🎯 面试整体画像

维度 特征
公司定位 小红书 - 社交电商/内容社区
面试风格 全面纵深型 + 原理追问型 + 手写实战型
难度评级 ⭐⭐⭐⭐(四星,实习面考察深度远超常规)
考察重心 React原理、构建工具、网络协议、手写能力
特殊之处 覆盖了从框架到工程化到算法全链路,且每个问题都要求"详细讲讲"

💡 面经关键点解读

面试官的潜台词 :虽然是日常实习,但我希望招到的是有扎实基础、能理解原理、有代码手感的候选人。每个问题都要"详细讲讲",不是背概念,而是要真正理解。Fiber底层、时间优先级、Web Worker通信机制,这些即使是正式岗也常被问倒。


🔍 逐题深度解析

一、对React框架的理解

问题:讲讲对React框架的理解
javascript 复制代码
// 基础回答:React是用于构建用户界面的库

// 完整回答框架:
React的核心可以从以下几个维度理解:

// 1. 设计理念
- 声明式编程:描述UI应该是什么样子,而不是如何操作DOM
- 组件化:将UI拆分成独立、可复用的组件
- 单向数据流:数据从父组件流向子组件,易于追踪

// 2. 核心机制
- 虚拟DOM:用JS对象描述UI,减少直接DOM操作
- 调和(Reconciliation):对比新旧虚拟DOM,找出最小更新
- Fiber架构:可中断的渲染,实现优先级调度

// 3. 生态系统
- 状态管理:Redux/Mobx/Zustand
- 路由:React Router
- 跨端:React Native

// 4. 演进历程
- React 15:Stack Reconciler(递归更新,无法中断)
- React 16+:Fiber Reconciler(可中断、优先级)
- React 18:并发特性、Suspense、Server Components

二、React和Vue的区别

问题:React和Vue的区别
维度 React Vue
设计哲学 纯UI库,更灵活 渐进式框架,易上手
模板语法 JSX(JS中写HTML) 单文件组件(template+script+style)
响应式原理 手动setState触发更新 数据劫持(Proxy)自动追踪
更新粒度 根节点开始diff 组件级自动追踪
状态管理 Redux/Mobx(社区) Vuex/Pinia(官方)
学习曲线 较陡(需理解JSX、Hooks) 平缓
适用场景 大型应用、跨端开发 快速开发、中小型项目
javascript 复制代码
// React:一切皆JS
const element = <div className="container">{count}</div>

// Vue:模板语法
<template>
  <div :class="container">{{count}}</div>
</template>

三、React Fiber底层

问题:详细讲讲React Fiber底层
javascript 复制代码
// 1. 为什么需要Fiber?
// React 15的问题:
// - 递归遍历虚拟DOM,一旦开始无法中断
// - 如果组件树很大,会阻塞主线程(>16ms导致掉帧)
// - 用户输入、动画会出现卡顿

// 2. Fiber是什么?
// Fiber是一个工作单元,也是一个数据结构

// Fiber节点结构(简化)
{
  // 类型信息
  tag: 1,                    // 组件类型(函数组件/类组件/原生元素)
  type: 'div',               // 元素类型
  key: null,                 // 唯一标识
  
  // 关联信息
  stateNode: DOM节点,        // 真实DOM
  return: Fiber父节点,       // 父节点
  child: Fiber子节点,        // 第一个子节点
  sibling: Fiber兄弟节点,    // 下一个兄弟节点
  
  // 数据
  pendingProps: {},          // 新props
  memoizedProps: {},         // 当前props
  memoizedState: {},         // 当前state
  dependencies: null,        // 依赖(如context)
  
  // 副作用
  effectTag: 'UPDATE',       // 操作类型(更新/删除/新增)
  nextEffect: null,          // 下一个副作用
  firstEffect: null,         // 第一个副作用
  lastEffect: null,          // 最后一个副作用
  
  // 优先级
  lanes: 1,                  // 优先级车道
  childLanes: 0,             // 子节点优先级
  
  // 双缓冲
  alternate: null,           // 指向workInProgress树的对应节点
}

// 3. Fiber的工作原理
// 双缓冲技术:current树(当前屏幕显示) + workInProgress树(内存中构建)

// 4. 渲染阶段
// Render阶段(可中断)
function renderRoot(root) {
  let workInProgress = root.current.alternate
  
  while (workInProgress !== null) {
    // 执行工作单元,返回下一个要执行的工作单元
    workInProgress = performUnitOfWork(workInProgress)
    
    // 检查是否有更高优先级任务(如用户输入)
    if (shouldYield()) {
      break // 中断,让出主线程
    }
  }
}

// Commit阶段(不可中断)
function commitRoot(root) {
  // 一次性将副作用提交到DOM
  commitWork(root.current)
}

四、Fiber时间优先级实现

问题:React Fiber时间优先级如何实现
javascript 复制代码
// 1. 优先级体系(Lanes模型)
// React 18使用Lanes(车道)模型,是一个31位的二进制数

export const NoLanes = 0b0000000000000000000000000000000
export const SyncLane = 0b0000000000000000000000000000001 // 同步优先级
export const InputContinuousLane = 0b0000000000000000000000000000100 // 连续输入
export const DefaultLane = 0b0000000000000000000000000010000 // 默认
export const IdleLane = 0b0100000000000000000000000000000 // 空闲

// 2. 优先级对应的事件类型
// - 同步优先级:点击、输入、键盘事件
// - 连续输入优先级:拖拽、滚动
// - 默认优先级:网络请求、setTimeout
// - 空闲优先级:数据预加载、日志上报

// 3. 时间切片实现
const frameRate = 60 // 60fps
const frameInterval = Math.floor(1000 / frameRate) // 约16.6ms

let startTime = getCurrentTime()

function workLoop(deadline) {
  // deadline.timeRemaining() 返回当前帧剩余时间
  while (deadline.timeRemaining() > 1 && workInProgress) {
    workInProgress = performUnitOfWork(workInProgress)
  }
  
  if (workInProgress) {
    // 还有工作未完成,下一帧继续
    requestIdleCallback(workLoop)
  }
}

// 4. 调度器(Scheduler)
// 根据优先级将任务分配到不同的队列
const taskQueue = []      // 高优先级任务
const timerQueue = []     // 定时任务

function scheduleCallback(priorityLevel, callback) {
  const currentTime = getCurrentTime()
  const startTime = currentTime
  
  const task = {
    id: taskIdCounter++,
    callback,
    priorityLevel,
    startTime,
    expirationTime: startTime + timeout[priorityLevel]
  }
  
  if (startTime > currentTime) {
    // 延迟任务
    timerQueue.push(task)
  } else {
    // 立即执行任务
    taskQueue.push(task)
    // 按优先级排序
    taskQueue.sort((a, b) => a.expirationTime - b.expirationTime)
  }
  
  requestHostCallback(flushWork)
}

// 5. 中断与恢复
function performUnitOfWork(unitOfWork) {
  const next = beginWork(unitOfWork) // 开始处理
  
  if (next === null) {
    // 没有子节点,回溯到兄弟节点
    completeUnitOfWork(unitOfWork)
  }
  
  return next
}

// 检查是否需要让出主线程
function shouldYield() {
  const currentTime = getCurrentTime()
  // 如果当前帧剩余时间不足,就让出
  return currentTime >= deadline
}

五、Vite和Webpack区别

问题:Vite和Webpack区别
维度 Vite Webpack
核心原理 基于ES Module的Native import 打包所有模块成bundle
开发服务器 无需打包,按需编译 需要打包所有模块
冷启动 极快(秒级) 慢(可能需要几十秒)
热更新 精准更新,速度快 更新范围大,速度较慢
生产构建 使用Rollup打包 自研打包
配置复杂度 简单,开箱即用 复杂,配置项多
插件生态 较新,但增长快 非常成熟,插件丰富
问题:为什么Vite快?
javascript 复制代码
// 1. 开发环境:利用浏览器原生ES Module
// 启动时:
// Webpack:需要打包整个应用
// Vite:只启动开发服务器,浏览器请求什么就编译什么

// 2. 按需编译
// 当浏览器请求一个文件时:
// - 如果是普通JS,直接返回
// - 如果是Vue/React组件,实时编译后返回

// 3. 缓存策略
// - 依赖预构建:将commonjs模块转为ESM,缓存到node_modules/.vite
// - 浏览器缓存:利用HTTP缓存

// 4. 热更新优化
// - Webpack:更新模块+重新打包+刷新
// - Vite:通过WebSocket通知浏览器,浏览器重新请求变更模块

// Vite配置示例
export default {
  server: {
    port: 3000,
    open: true,
    proxy: {
      '/api': 'http://localhost:8080'
    }
  },
  build: {
    rollupOptions: {
      input: {
        main: 'index.html',
        admin: 'admin.html'
      }
    }
  }
}

六、Webpack构建过程和Tree Shaking

问题:详细讲讲Webpack构建过程流程 和 Tree Shaking
javascript 复制代码
// Webpack构建流程

// 1. 初始化参数
// 从配置文件读取参数,合并默认配置

// 2. 开始编译
// 初始化Compiler对象,注册插件,执行run方法

// 3. 确定入口
// 根据entry配置,找出所有入口文件

// 4. 编译模块
// - 从入口文件开始,递归解析依赖
// - 对不同文件类型使用对应的loader处理
// - 生成AST,分析依赖

class NormalModule {
  build() {
    // 读取源码
    const source = fs.readFileSync(this.path, 'utf8')
    
    // 使用loader处理
    const processedSource = this.runLoaders(source)
    
    // 解析AST
    const ast = parser.parse(processedSource)
    
    // 分析依赖
    this.dependencies = this.parseDependencies(ast)
    
    return {
      source: processedSource,
      ast,
      dependencies: this.dependencies
    }
  }
}

// 5. 完成模块编译
// - 所有模块编译完成
// - 生成模块依赖图

// 6. 输出资源
// - 根据依赖图,组合成chunk
// - 每个chunk生成一个文件
// - 执行插件钩子,优化输出

// 7. 写入文件系统
// 将生成的文件写入dist目录
Tree Shaking实现
javascript 复制代码
// Tree Shaking:移除未使用的代码

// 1. 前提条件
// - 使用ES Module(静态分析)
// - 在production模式下
// - 代码没有副作用

// 2. 实现原理
// - 标记阶段:标记哪些导出被使用
// - 摇树阶段:删除未使用的导出

// utils.js
export function usedFunction() {
  console.log('used')
}

export function unusedFunction() {
  console.log('unused') // 这个会被删除
}

// main.js
import { usedFunction } from './utils'
usedFunction()

// 编译后
function usedFunction() {
  console.log('used')
}
usedFunction()

// 3. 副作用标记
// package.json
{
  "sideEffects": [
    "*.css",
    "*.scss"
  ]
}

// 4. Webpack配置
module.exports = {
  mode: 'production',
  optimization: {
    usedExports: true,  // 标记使用到的导出
    sideEffects: true,  // 处理副作用
    minimize: true      // 压缩代码
  }
}

七、HTTP1.1 vs HTTP2.0

问题:http1.1 http2.0详细区别讲讲
特性 HTTP/1.1 HTTP/2.0 收益
连接复用 串行,一个连接一个请求 多路复用,一个连接并行多个请求 减少连接数,提高并发
头部压缩 明文传输,重复头部 HPACK压缩,减少体积 减少传输数据量
服务器推送 不支持 支持主动推送资源 减少请求次数
二进制分帧 文本协议 二进制协议 解析效率高
优先级 不支持 支持流优先级 关键资源优先加载
javascript 复制代码
// HTTP/1.1的问题
// - 队头阻塞:一个请求慢,后面的都得等
// - 头部冗余:每个请求都带重复的User-Agent、Cookie等
// - 连接限制:浏览器对同一域名限制并发连接数(通常6个)

// 解决方案:域名分片
// 将资源分布到多个子域名:static1.example.com, static2.example.com

// HTTP/2.0的改进
// 1. 多路复用
// 在一个TCP连接上同时发送多个请求
// 请求1: [frame1][frame2][frame3]
// 请求2:   [frame1][frame2]
// 请求3:        [frame1][frame2][frame3]

// 2. 服务器推送
// 浏览器请求index.html,服务器可以主动推送index.css和index.js
push('/', {
  'style.css': styles,
  'script.js': script
})

// 3. 头部压缩
// 第一次请求:Header: { method: 'GET', path: '/', user-agent: 'Chrome' }
// 第二次请求:Header: { method: 'GET', path: '/style.css' }
// 只发送差异部分

八、TypeScript工具类型

问题:TS的工具类型
typescript 复制代码
// 内置工具类型

// 1. Partial<T> - 将所有属性变为可选
interface User {
  name: string
  age: number
}
type PartialUser = Partial<User> // { name?: string; age?: number }

// 2. Required<T> - 将所有属性变为必选
type RequiredUser = Required<PartialUser> // { name: string; age: number }

// 3. Pick<T, K> - 从T中选取K属性
type UserName = Pick<User, 'name'> // { name: string }

// 4. Omit<T, K> - 从T中排除K属性
type UserWithoutAge = Omit<User, 'age'> // { name: string }

// 5. Record<K, T> - 创建键为K,值为T的对象
type PageInfo = Record<string, User> // { [key: string]: User }

// 6. Exclude<T, U> - 从T中排除可以赋值给U的类型
type T = Exclude<'a' | 'b' | 'c', 'a' | 'b'> // 'c'

// 7. Extract<T, U> - 提取T中可以赋值给U的类型
type T = Extract<'a' | 'b' | 'c', 'a' | 'b'> // 'a' | 'b'

// 8. NonNullable<T> - 从T中排除null和undefined
type T = NonNullable<string | null | undefined> // string

// 9. ReturnType<T> - 获取函数返回类型
function fn() { return { a: 1, b: 2 } }
type FnReturn = ReturnType<typeof fn> // { a: number; b: number }

// 10. Parameters<T> - 获取函数参数类型
type FnParams = Parameters<typeof fn> // []

// 11. Awaited<T> - 获取Promise的返回类型
type PromiseType = Awaited<Promise<string>> // string

// 自定义工具类型
type Nullable<T> = T | null
type ArrayElement<T> = T extends (infer U)[] ? U : never

九、Promise手写

问题:手写Promise.all和Promise.allSettled
javascript 复制代码
// Promise.all:全部成功才成功,一个失败就失败
Promise.myAll = function(promises) {
  return new Promise((resolve, reject) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError('参数必须是数组'))
    }
    
    const results = []
    let completed = 0
    
    if (promises.length === 0) {
      return resolve(results)
    }
    
    promises.forEach((promise, index) => {
      Promise.resolve(promise)
        .then(value => {
          results[index] = value
          completed++
          
          if (completed === promises.length) {
            resolve(results)
          }
        })
        .catch(reject)
    })
  })
}

// Promise.allSettled:等待所有完成,返回每个的状态
Promise.myAllSettled = function(promises) {
  return new Promise((resolve) => {
    if (!Array.isArray(promises)) {
      return reject(new TypeError('参数必须是数组'))
    }
    
    const results = []
    let completed = 0
    
    if (promises.length === 0) {
      return resolve(results)
    }
    
    promises.forEach((promise, index) => {
      Promise.resolve(promise)
        .then(value => {
          results[index] = {
            status: 'fulfilled',
            value
          }
        })
        .catch(reason => {
          results[index] = {
            status: 'rejected',
            reason
          }
        })
        .finally(() => {
          completed++
          if (completed === promises.length) {
            resolve(results)
          }
        })
    })
  })
}

// 使用示例
const p1 = Promise.resolve(1)
const p2 = Promise.reject('error')
const p3 = new Promise(r => setTimeout(() => r(3), 1000))

Promise.myAllSettled([p1, p2, p3]).then(console.log)
// [
//   { status: 'fulfilled', value: 1 },
//   { status: 'rejected', reason: 'error' },
//   { status: 'fulfilled', value: 3 }
// ]

十、闭包与防抖节流

问题:对闭包的了解 + 手写防抖节流
javascript 复制代码
// 闭包:函数 + 词法环境的引用
function createCounter() {
  let count = 0  // 被内部函数引用
  
  return function() {
    count++
    return count
  }
}

const counter = createCounter()
console.log(counter()) // 1
console.log(counter()) // 2
// count变量没有被销毁,形成闭包

// 防抖:只执行最后一次
function debounce(fn, delay) {
  let timer
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(() => fn.apply(this, args), delay)
  }
}

// 带立即执行的防抖
function debounceImmediate(fn, delay, immediate = false) {
  let timer
  return function(...args) {
    const callNow = immediate && !timer
    
    clearTimeout(timer)
    
    timer = setTimeout(() => {
      timer = null
      if (!immediate) fn.apply(this, args)
    }, delay)
    
    if (callNow) fn.apply(this, args)
  }
}

// 节流:时间戳版(立即执行)
function throttle(fn, interval) {
  let lastTime = 0
  return function(...args) {
    const now = Date.now()
    if (now - lastTime >= interval) {
      fn.apply(this, args)
      lastTime = now
    }
  }
}

// 节流:定时器版(延迟执行)
function throttleTimer(fn, interval) {
  let timer
  return function(...args) {
    if (!timer) {
      timer = setTimeout(() => {
        fn.apply(this, args)
        timer = null
      }, interval)
    }
  }
}

十一、React Hooks

问题:讲讲React常见的hooks + 手写useMemo模仿useCallback
javascript 复制代码
// 常见Hooks

// 1. useState - 状态管理
const [count, setCount] = useState(0)

// 2. useEffect - 副作用
useEffect(() => {
  document.title = `点击了${count}次`
  return () => { /* 清理 */ }
}, [count])

// 3. useContext - 上下文
const theme = useContext(ThemeContext)

// 4. useReducer - 复杂状态
const [state, dispatch] = useReducer(reducer, initialState)

// 5. useCallback - 缓存函数
const handleClick = useCallback(() => {
  doSomething(count)
}, [count])

// 6. useMemo - 缓存计算结果
const expensiveValue = useMemo(() => {
  return computeExpensive(count)
}, [count])

// 7. useRef - 引用
const inputRef = useRef(null)

// 8. useLayoutEffect - 同步执行
useLayoutEffect(() => {
  // DOM更新后同步执行
}, [])
手写useMemo模仿useCallback
javascript 复制代码
// useCallback 和 useMemo 的关系
// useCallback(fn, deps) 等价于 useMemo(() => fn, deps)

// 手写简易版useMemo
function useMemo(factory, deps) {
  const ref = React.useRef({
    deps: null,
    value: null
  })
  
  // 判断依赖是否变化
  const depsChanged = !ref.current.deps || 
    !deps || 
    deps.length !== ref.current.deps.length ||
    deps.some((dep, i) => !Object.is(dep, ref.current.deps[i]))
  
  if (depsChanged || !ref.current.value) {
    // 重新计算
    ref.current.value = factory()
    ref.current.deps = deps
  }
  
  return ref.current.value
}

// 使用useMemo实现useCallback
function useCallback(callback, deps) {
  return useMemo(() => callback, deps)
}

// 使用示例
const handleClick = useCallback(() => {
  console.log('clicked')
}, [])

十二、算法题:最大子数组和

问题:找出具有最大和的连续子数组
javascript 复制代码
// 题目:给定整数数组nums,找出连续子数组的最大和
// 例如:[-2,1,-3,4,-1,2,1,-5,4] 输出6([4,-1,2,1])

// 解法1:动态规划(Kadane算法)
function maxSubArray(nums) {
  let maxCurrent = nums[0]
  let maxGlobal = nums[0]
  
  for (let i = 1; i < nums.length; i++) {
    // 要么扩展当前子数组,要么从当前元素重新开始
    maxCurrent = Math.max(nums[i], maxCurrent + nums[i])
    
    // 更新全局最大值
    maxGlobal = Math.max(maxGlobal, maxCurrent)
  }
  
  return maxGlobal
}

// 解法2:返回子数组本身
function maxSubArrayWithIndex(nums) {
  let maxCurrent = nums[0]
  let maxGlobal = nums[0]
  let start = 0, end = 0, tempStart = 0
  
  for (let i = 1; i < nums.length; i++) {
    if (nums[i] > maxCurrent + nums[i]) {
      // 从当前元素重新开始
      maxCurrent = nums[i]
      tempStart = i
    } else {
      // 扩展当前子数组
      maxCurrent = maxCurrent + nums[i]
    }
    
    if (maxCurrent > maxGlobal) {
      maxGlobal = maxCurrent
      start = tempStart
      end = i
    }
  }
  
  return {
    sum: maxGlobal,
    subarray: nums.slice(start, end + 1)
  }
}

// 测试
const nums = [-2,1,-3,4,-1,2,1,-5,4]
console.log(maxSubArray(nums)) // 6
console.log(maxSubArrayWithIndex(nums)) 
// { sum: 6, subarray: [4,-1,2,1] }

十三、Web Worker通信机制

问题:详细讲讲Web Worker的通信机制 除postmessage 和 onmessage外 回调之类的
javascript 复制代码
// Web Worker通信机制

// 1. 基础通信:postMessage + onmessage
// main.js
const worker = new Worker('worker.js')
worker.postMessage({ type: 'start', data: [1,2,3] })
worker.onmessage = (e) => {
  console.log('收到:', e.data)
}

// worker.js
self.onmessage = (e) => {
  const result = e.data.data.map(x => x * 2)
  self.postMessage(result)
}

// 2. 错误处理
worker.onerror = (error) => {
  console.error('Worker错误:', error)
}

// 3. 终止Worker
worker.terminate()

// 4. 回调模式(通过消息类型模拟)
// main.js
const callbacks = new Map()
let messageId = 0

worker.onmessage = (e) => {
  const { id, result, error } = e.data
  const callback = callbacks.get(id)
  if (callback) {
    if (error) {
      callback.reject(error)
    } else {
      callback.resolve(result)
    }
    callbacks.delete(id)
  }
}

function sendWithCallback(type, data) {
  return new Promise((resolve, reject) => {
    const id = ++messageId
    callbacks.set(id, { resolve, reject })
    worker.postMessage({ id, type, data })
  })
}

// worker.js
self.onmessage = async (e) => {
  const { id, type, data } = e.data
  
  try {
    let result
    switch(type) {
      case 'process':
        result = await processData(data)
        break
      case 'compute':
        result = await computeHeavy(data)
        break
    }
    self.postMessage({ id, result })
  } catch (error) {
    self.postMessage({ id, error: error.message })
  }
}

// 使用
const result = await sendWithCallback('process', [1,2,3])

// 5. Transferable Objects(转移而非拷贝)
// 对于大文件,使用转移可以避免拷贝
const buffer = new ArrayBuffer(1024 * 1024 * 100) // 100MB
worker.postMessage({ buffer }, [buffer]) // buffer被转移,主线程不可用

// 6. SharedArrayBuffer(共享内存)
const sharedBuffer = new SharedArrayBuffer(1024)
const sharedArray = new Int32Array(sharedBuffer)

// 7. 消息通道(Channel Messaging)
const channel = new MessageChannel()
worker.postMessage({ port: channel.port2 }, [channel.port2])

channel.port1.onmessage = (e) => {
  console.log('通过通道收到:', e.data)
}

// 8. 广播通信(BroadcastChannel)
const bc = new BroadcastChannel('worker_channel')
bc.postMessage('广播消息')

// 9. 导入脚本
// worker.js
importScripts('utils.js', 'config.js')

📚 知识点速查表

知识点 核心要点
React理解 声明式、组件化、虚拟DOM、Fiber
React/Vue区别 设计哲学、模板、响应式、学习曲线
Fiber底层 可中断渲染、双缓冲、优先级
时间优先级 Lanes模型、时间切片、调度器
Vite/Webpack 基于ESM vs 打包、按需编译
Webpack流程 入口→编译→依赖图→输出
Tree Shaking 静态分析、usedExports、副作用
HTTP/1.1 vs 2.0 多路复用、头部压缩、服务器推送
TS工具类型 Partial、Pick、Omit、Record
Promise all、allSettled手写
闭包 函数+词法环境、防抖节流
React Hooks useState、useEffect、useCallback
Web Worker postMessage、回调模拟、Transferable

📌 最后一句:

小红书的这场面试,是一场全链路的技术深度检验

从React Fiber底层到Web Worker高级通信,从构建工具原理到手写Promise------

每一个问题都在问:你是真的懂,还是只是背过?

日常实习能有这样的面试强度,说明小红书对前端实习生有很高的期待。
把这份面经吃透,你就能达到这份期待。

相关推荐
HelloReader1 小时前
Trunk + Tauri 前端配置Rust/WASM 项目如何稳定接入桌面与移动端(Trunk 0.17.5)
前端
小碗细面1 小时前
告别996!Claude Code 6个实用工作流程
前端·人工智能·ai编程
HelloReader1 小时前
Vite + Tauri 2 一套配置同时搞定桌面开发、调试体验、iOS 真机联调(Vite 5.4.8)
前端
顾青1 小时前
仅仅一行 CSS,竟让 2000 个节点的页面在弹框时卡成 PPT?
前端·vue.js·性能优化
用户4099322502121 小时前
如何在Vue3中优化生命周期钩子性能并规避常见陷阱?
前端·vue.js·trae
微风起皱1 小时前
企业级WEB应用服务器TOMCAT
java·前端·tomcat
__不想说话__1 小时前
前端开发者的 AI 时代生存指南:大模型如何重塑岗位要求与技能
前端·人工智能·面试
青青家的小灰灰1 小时前
深入React源码:解析setState的批量更新与异步机制
前端·javascript·react.js