前言
大家好,我是木斯佳。
在这个春节假期,当大家都在谈论返乡、团圆与休息时,作为一名技术人,我的思考却不由自主地转向了行业的「冬」与「春」。
相信很多人都感受到了,在AI浪潮的席卷之下,前端领域的门槛在变高,纯粹的"增删改查"岗位正在肉眼可见地减少。曾经热闹非凡的面经分享,如今也沉寂了许多。但我们都知道,市场的潮水退去,留下的才是真正在踏实准备、努力沉淀的人。学习的需求,从未消失,只是变得更加务实和深入。
正值春节,也是复盘与规划的好时机。结合CSDN这次「春节代码贺新年」活动所提倡的"用技术视角记录春节、复盘成长",我决定在这个假期持续更新专栏,帮助年后参加春招的同学。
这个专栏的初衷很简单:拒绝过时的、流水线式的PDF引流贴,专注于收集和整理当下最新、最真实的前端面试资料。我会在每一份面经和八股文的基础上,尝试从面试官的角度去拆解问题背后的逻辑,而不仅仅是提供一份静态的背诵答案。无论你是校招还是社招,目标是中大厂还是新兴团队,只要是真实发生、有价值的面试经历,我都会在这个专栏里为你沉淀下来。
温馨提示:市面上的面经鱼龙混杂,甄别真伪、把握时效,是我们对抗内卷最有效的武器。
在这个假期,让我们一起充电,为下一个技术春天做好准备。

面经原文内容
📍面试公司:小红书
🕐面试时间:1月5日
💻面试岗位:前端日常实习
⏱️面试时长:未提及
❓面试问题:
框架基础
- 自我介绍
- 讲讲对React框架的理解
- React和Vue的区别
- 详细讲讲React Fiber底层
- React Fiber时间优先级如何实现
构建工具
-
Vite 和 Webpack区别
-
为什么Vite快
-
详细讲讲Webpack构建过程流程 和 Tree Shaking
网络协议
- http1.1 http2.0详细区别讲讲
TypeScript
-
大致聊聊TS
-
TS的工具类型
JS核心
- 详细讲讲Promise
- 手写Promise.all和Promise.allSettled
- 对闭包的了解
- 手写防抖和节流
React Hooks
-
讲讲React常见的hooks
-
手写useMemo模仿useCallback
算法
- 算法题: 给你一个整数数组nums,找出一个具有最大和的连续子数组(至少包含一个元素),返回最大和
项目
- 讲一个你最熟悉的项目以及亮点难点
进阶
- 详细讲讲Web Worker的通信机制 除postmessage 和 onmessage外 回调之类的
反问
- 反问
来源:牛客网 雾泊屿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------
每一个问题都在问:你是真的懂,还是只是背过?
日常实习能有这样的面试强度,说明小红书对前端实习生有很高的期待。
把这份面经吃透,你就能达到这份期待。