前端八股文面经大全:京东零售前端实习一面(2026-1-20)·面经深度解析

前言

大家好,我是木斯佳。

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

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

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

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

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

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

面经原文内容

📍面试公司:京东 - 零售部门

🕐面试时间:近期,于2026-1-20发布面经

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

⏱️面试时长:30分钟

💬面试风格:轻松闲聊 + 技术纵深(看似闲聊,实则考察技术深度)

❓面试问题:

  1. 面试官自我介绍,详细介绍了业务部门(好评)
  2. 结合项目自我介绍,说说前端学习过程
  3. vue2和vue3的区别
  4. vue router中的全局路由守卫有哪些
  5. canvas的详细使用(面试官发音有点不标准,一开始没听懂,后面才知道是canvas,不会)
  6. 解决跨域的方法有哪些
  7. jsonp的实现原理和过程
  8. cookie/sessionStorage/localStorage的区别,生命周期
  9. 流式响应的处理
  10. 断点续传的详细实现过程

👥面试官反问:

  • 会继续升学么
  • 能实习多久
  • 用过哪些大模型

💭候选人反问:

  • 组内氛围
  • 主要技术栈:vue

🙌面试感想:

就面了半个小时,,,基本都在闲聊

来源:牛客网 wowowo

📝 京东零售前端一面·面经深度解析

🎯 面试整体画像

维度 特征
部门定位 京东零售 - 核心电商业务前端
面试风格 看似闲聊,实则纵深 + 实战落地
难度评级 ⭐⭐⭐(三星,实习面考察了中高级知识点)
考察重心 Vue核心、浏览器存储、网络通信、文件处理
面试体验 ⭐⭐⭐⭐(面试官介绍业务详细,氛围轻松)

💡 面经关键点解读

面试官的潜台词 :虽然是日常实习,只面了半小时,但问到了Canvas、流式响应、断点续传 这些通常被认为是"中高级前端"才会深入的点。这说明京东零售部门对候选人的技术纵深和项目落地能力有期待,即使是实习生,也希望你有足够的"技术好奇心"和"实战潜力"。


🔍 非主观题深度解析

1. Vue2和Vue3的区别

面试官意图
  • 考察你对主流框架的理解深度
  • 判断你是否关注技术演进
  • 为后续Vue Router问题做铺垫
✅ 完整答案
维度 Vue2 Vue3 关键变化
响应式原理 Object.defineProperty Proxy 可监听新增/删除属性、数组索引
组合式API Options API Composition API + Options API 逻辑复用更优雅
TypeScript 支持有限 原生TS支持 源码用TS重写
性能 一般 更快(PatchFlag、静态提升) 编译时优化
Fragment 不支持(需单根节点) 支持多根节点 模板更灵活
Teleport 可将组件渲染到DOM任意位置
Suspense 实验性支持 异步组件加载状态
🔍 深挖点:Composition API vs Options API
javascript 复制代码
// Vue2 Options API
export default {
  data() { return { count: 0 } },
  methods: { increment() { this.count++ } },
  mounted() { console.log('mounted') }
}

// Vue3 Composition API
export default {
  setup() {
    const count = ref(0)
    const increment = () => count.value++
    onMounted(() => console.log('mounted'))
    return { count, increment }
  }
}

// 优势:相关逻辑可以组织在一起,而不是分散在options中
// 适用场景:复杂组件、逻辑复用

2. Vue Router中的全局路由守卫有哪些

面试官意图
  • 考察Vue生态的熟悉程度
  • 结合项目中的权限控制场景
  • 为后续项目深挖做铺垫
✅ 完整答案
javascript 复制代码
// 三种全局路由守卫
// 1. 全局前置守卫
router.beforeEach((to, from, next) => {
  // 常用场景:登录校验、权限控制
  const isLoggedIn = localStorage.getItem('token')
  if (to.meta.requiresAuth && !isLoggedIn) {
    next('/login')
  } else {
    next()
  }
})

// 2. 全局解析守卫
router.beforeResolve((to, from, next) => {
  // 在导航被确认之前,所有组件内守卫和异步路由组件被解析之后调用
  // 适合做一些数据预取
  next()
})

// 3. 全局后置钩子
router.afterEach((to, from) => {
  // 不能改变导航,适合做日志上报、页面标题修改等
  document.title = to.meta.title || '默认标题'
  sendAnalytics(to.fullPath)
})
💡 补充:路由守卫的执行顺序
复制代码
1. 导航被触发
2. 失活组件里调用 beforeRouteLeave
3. 调用全局 beforeEach
4. 重用组件里调用 beforeRouteUpdate
5. 路由配置里调用 beforeEnter
6. 解析异步路由组件
7. 激活组件里调用 beforeRouteEnter
8. 调用全局 beforeResolve
9. 导航确认
10. 调用全局 afterEach
11. DOM更新
12. 调用 beforeRouteEnter 的 next 回调

3. Canvas的详细使用

面试官意图
  • 考察图形学基础
  • 了解你是否接触过可视化相关技术
  • 京东零售有大量可视化需求(数据看板、商品展示)

建议了解一些常用的三方库的实现原理,比如echarts、g6等可视化库。这里不展开说明

🔍 其他常考内容:Canvas vs SVG
维度 Canvas SVG
渲染方式 像素级(位图) 矢量图形
事件绑定 不支持(需手动计算) 支持(每个元素可绑定)
性能 适合大量元素 适合少量元素
缩放 会模糊 不失真
适用场景 游戏、数据可视化(大量点) 图标、地图(少量元素)

4. 解决跨域的方法有哪些

面试官意图
  • 考察网络基础知识
  • 了解项目中的实际部署经验
  • 为后续JSONP问题做铺垫
✅ 完整答案
javascript 复制代码
// 1. CORS(最常用)
// 后端设置响应头
res.setHeader('Access-Control-Allow-Origin', '*') // 或指定域名
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization')
res.setHeader('Access-Control-Allow-Credentials', 'true') // 允许携带cookie

// 2. JSONP(只支持GET)
// 利用script标签没有跨域限制
function jsonp(url, callback) {
  const script = document.createElement('script')
  const callbackName = 'callback_' + Date.now()
  
  window[callbackName] = (data) => {
    callback(data)
    delete window[callbackName]
    document.body.removeChild(script)
  }
  
  script.src = `${url}?callback=${callbackName}`
  document.body.appendChild(script)
}

// 3. 代理服务器(开发常用)
// webpack devServer配置
devServer: {
  proxy: {
    '/api': {
      target: 'http://localhost:3000',
      changeOrigin: true,
      pathRewrite: { '^/api': '' }
    }
  }
}

// 4. Nginx反向代理
// nginx配置
location /api/ {
  proxy_pass http://backend-server/;
}

// 5. postMessage(跨域窗口通信)
// 父页面
iframe.contentWindow.postMessage(data, 'http://child.com')
// 子页面
window.addEventListener('message', (e) => {
  if (e.origin === 'http://parent.com') {
    console.log(e.data)
  }
})

// 6. document.domain(同主域不同子域)
document.domain = 'example.com'

5. JSONP的实现原理和过程

面试官意图
  • 考察对跨域解决方案的深入理解
  • 了解script标签特性
  • 为后续手写铺垫
✅ 完整答案
javascript 复制代码
// JSONP实现原理
// 1. 核心:script标签的src请求不受同源策略限制
// 2. 过程:前端定义一个回调函数,后端返回函数调用

// 完整实现
function jsonp(options) {
  const { url, params, callback } = options
  
  return new Promise((resolve, reject) => {
    // 创建唯一回调函数名
    const callbackName = 'jsonp_' + Date.now() + Math.random().toString(36).substr(2)
    
    // 定义全局回调函数
    window[callbackName] = (data) => {
      resolve(data)
      // 清理
      delete window[callbackName]
      document.body.removeChild(script)
    }
    
    // 构建URL
    const queryParams = {
      ...params,
      callback: callbackName
    }
    
    const queryString = Object.keys(queryParams)
      .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(queryParams[key])}`)
      .join('&')
    
    const fullUrl = url + (url.includes('?') ? '&' : '?') + queryString
    
    // 创建script标签
    const script = document.createElement('script')
    script.src = fullUrl
    script.onerror = () => {
      reject(new Error('JSONP请求失败'))
      delete window[callbackName]
      document.body.removeChild(script)
    }
    
    document.body.appendChild(script)
  })
}

// 使用
jsonp({
  url: 'http://api.example.com/data',
  params: { id: 123 },
  callback: 'handleData'
}).then(data => {
  console.log(data)
})

// 后端返回格式
// handleData({ name: '张三', age: 18 })
⚠️ JSONP的局限性
  • 只支持GET请求
  • 需要后端配合返回特定格式
  • 有安全风险(XSS)

6. Cookie/SessionStorage/LocalStorage的区别,生命周期

面试官意图
  • 考察浏览器存储基础知识
  • 了解存储选型能力
  • 为后续断点续传做铺垫(存储上传进度)
✅ 完整对比
特性 Cookie localStorage sessionStorage
容量 4KB 5-10MB 5-10MB
生命周期 可设置expires,默认会话级 永久(除非手动清除) 标签页关闭即清除
作用域 同域名所有标签页 同域名所有标签页 同标签页(同源)
发送到服务端 每次请求自动携带 不会 不会
API document.cookie setItem/getItem setItem/getItem
javascript 复制代码
// Cookie操作
// 设置
document.cookie = "username=John; expires=Thu, 18 Dec 2025 12:00:00 UTC; path=/"
// 读取(需要手动解析)
function getCookie(name) {
  const cookies = document.cookie.split('; ')
  const cookie = cookies.find(c => c.startsWith(name + '='))
  return cookie ? cookie.split('=')[1] : null
}

// Web Storage操作
// 设置
localStorage.setItem('key', 'value')
// 读取
localStorage.getItem('key')
// 删除
localStorage.removeItem('key')
// 清空
localStorage.clear()
🔍 选型建议
  • 登录态 → Cookie(httpOnly增强安全)
  • 用户偏好 → localStorage(持久化)
  • 表单暂存 → sessionStorage(会话级)
  • 敏感信息 → 内存变量(用完即毁)

7. 流式响应的处理

面试官意图
  • 关键考点:实习面问到流式响应,说明团队在做AI相关功能
  • 考察对Fetch API的深入使用
  • 为后续断点续传做铺垫
✅ 完整答案
javascript 复制代码
// 流式响应(常用于AI对话、大文件下载)

// 1. Fetch API实现
async function fetchStream(url) {
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ prompt: 'Hello' })
  })
  
  const reader = response.body.getReader()
  const decoder = new TextDecoder()
  
  while (true) {
    const { done, value } = await reader.read()
    if (done) break
    
    // 解码并处理数据块
    const chunk = decoder.decode(value)
    console.log('收到数据块:', chunk)
    
    // 处理SSE格式(data: {"text": "hello"}\n\n)
    const lines = chunk.split('\n')
    lines.forEach(line => {
      if (line.startsWith('data: ')) {
        try {
          const data = JSON.parse(line.slice(6))
          updateUI(data)
        } catch (e) {
          console.error('解析失败', e)
        }
      }
    })
  }
}

// 2. 使用EventSource(SSE)
const eventSource = new EventSource('/api/stream')

eventSource.onmessage = (e) => {
  console.log('收到消息:', e.data)
  updateUI(JSON.parse(e.data))
}

eventSource.onerror = (e) => {
  console.error('连接错误', e)
  eventSource.close()
}

// 3. 模拟流式响应(面试手写)
function simulateStream(callback) {
  const data = ['Hello', ' ', 'World', '!']
  let index = 0
  
  const interval = setInterval(() => {
    if (index < data.length) {
      callback(data[index])
      index++
    } else {
      clearInterval(interval)
      callback(null, true) // 完成
    }
  }, 100)
}
💡 应用场景
  • AI对话(ChatGPT流式返回)
  • 大文件下载(显示进度)
  • 实时日志展示

8. 断点续传的详细实现过程

面试官意图
  • 核心考点:考察复杂业务场景的落地能力
  • 了解文件上传的深入知识
  • 判断是否有大文件处理经验
✅ 完整答案
javascript 复制代码
// 断点续传实现方案

// 1. 核心原理:将大文件切分成小块,记录已上传的块
// 2. 流程:文件切片 → 计算哈希 → 上传前校验 → 并行上传 → 合并通知

class ResumableUpload {
  constructor(file, chunkSize = 1024 * 1024) { // 默认1MB
    this.file = file
    this.chunkSize = chunkSize
    this.chunks = this.createChunks()
    this.uploadedChunks = new Set() // 已上传的块索引
  }
  
  // 文件切片
  createChunks() {
    const chunks = []
    let start = 0
    
    while (start < this.file.size) {
      const end = Math.min(start + this.chunkSize, this.file.size)
      const chunk = this.file.slice(start, end)
      chunks.push({
        index: chunks.length,
        blob: chunk,
        size: chunk.size
      })
      start = end
    }
    
    return chunks
  }
  
  // 计算文件哈希(用于校验)
  async calculateHash() {
    return new Promise((resolve) => {
      const worker = new Worker('/hash-worker.js')
      worker.postMessage(this.file)
      worker.onmessage = (e) => resolve(e.data)
    })
  }
  
  // 校验已上传的块
  async verifyUpload(fileHash) {
    const response = await fetch('/api/upload/verify', {
      method: 'POST',
      body: JSON.stringify({
        filename: this.file.name,
        fileHash,
        totalChunks: this.chunks.length
      })
    })
    
    const data = await response.json()
    // 返回已上传的块索引列表
    data.uploadedChunks.forEach(index => this.uploadedChunks.add(index))
  }
  
  // 上传单个块
  async uploadChunk(chunk, fileHash) {
    const formData = new FormData()
    formData.append('chunk', chunk.blob)
    formData.append('index', chunk.index)
    formData.append('fileHash', fileHash)
    formData.append('filename', this.file.name)
    
    try {
      const response = await fetch('/api/upload/chunk', {
        method: 'POST',
        body: formData
      })
      
      if (response.ok) {
        this.uploadedChunks.add(chunk.index)
        this.updateProgress()
      }
    } catch (error) {
      console.error(`块${chunk.index}上传失败`, error)
      // 失败重试
      await this.retry(chunk, fileHash)
    }
  }
  
  // 并发上传所有未上传的块
  async upload(fileHash, concurrency = 3) {
    const pendingChunks = this.chunks.filter(
      chunk => !this.uploadedChunks.has(chunk.index)
    )
    
    // 控制并发数
    const queue = [...pendingChunks]
    const workers = new Array(concurrency).fill(null).map(async () => {
      while (queue.length) {
        const chunk = queue.shift()
        await this.uploadChunk(chunk, fileHash)
      }
    })
    
    await Promise.all(workers)
    
    // 所有块上传完成,通知服务端合并
    if (this.uploadedChunks.size === this.chunks.length) {
      await this.mergeChunks(fileHash)
    }
  }
  
  // 合并文件
  async mergeChunks(fileHash) {
    const response = await fetch('/api/upload/merge', {
      method: 'POST',
      body: JSON.stringify({
        filename: this.file.name,
        fileHash,
        totalChunks: this.chunks.length
      })
    })
    
    return response.json()
  }
  
  // 进度更新
  updateProgress() {
    const progress = (this.uploadedChunks.size / this.chunks.length) * 100
    console.log(`上传进度: ${progress.toFixed(2)}%`)
    // 可存入localStorage,实现刷新后继续
    localStorage.setItem('uploadProgress', JSON.stringify({
      fileHash: this.fileHash,
      uploadedChunks: [...this.uploadedChunks]
    }))
  }
  
  // 失败重试
  async retry(chunk, fileHash, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
      try {
        await this.uploadChunk(chunk, fileHash)
        return
      } catch (error) {
        console.log(`重试第${i + 1}次`)
        await new Promise(r => setTimeout(r, 1000 * (i + 1))) // 递增等待
      }
    }
    throw new Error(`块${chunk.index}上传失败,已达最大重试次数`)
  }
}

// 使用
const uploader = new ResumableUpload(file)
const fileHash = await uploader.calculateHash()
await uploader.verifyUpload(fileHash)
await uploader.upload(fileHash)
🔍 面试官追问点
  1. 如何实现暂停/继续?

    • 维护一个paused状态,中断upload循环
    • 恢复时重新调用upload,会自动跳过已上传的块
  2. 如何实现秒传?

    • 上传前校验文件哈希,如果服务端已存在相同文件,直接返回成功
  3. 并发数如何选择?

    • 浏览器限制(通常6个),网络状况,服务器承受能力

🎁 附:京东零售面试复习清单

知识点 掌握程度 重点方向
Vue2/3区别 ⭐⭐⭐⭐ 响应式原理、Composition API
路由守卫 ⭐⭐⭐ 执行顺序、权限控制
Canvas ⭐⭐⭐ 基础API、动画、可视化
跨域 ⭐⭐⭐⭐ CORS、JSONP、代理
存储 ⭐⭐⭐ 区别、选型、安全
流式响应 ⭐⭐⭐⭐ Fetch API、SSE、AI场景
断点续传 ⭐⭐⭐⭐⭐ 切片、哈希、并发、秒传
大模型 ⭐⭐⭐ 使用经验、API调用

📌 最后一句:

京东零售的这场面试,是一场轻松外壳下的深度对话

半小时的"闲聊",实则是面试官在快速判断:
这个实习生,能不能在复杂的业务场景里,有解决问题的能力?

相关推荐
zheshiyangyang2 小时前
前端面试基础知识整理【Day-8】
前端·面试·职场和发展
a1117762 小时前
优雅简历(html开源)
前端·开源·html
Cache技术分享2 小时前
330. Java Stream API - 处理 Optional 对象:像流一样优雅地使用 Optional
前端·后端
感性的程序员小王2 小时前
别再手撸架构图了!我写了个 AI 工具,把 Spring Boot 代码一键变成 Draw.io 流程图
前端·后端
猪头男2 小时前
【从零开始学习Vue|第七篇】深入组件——Props
前端
孟健2 小时前
AI 团队翻车之后,我想告诉你这 3 件事
前端
木斯佳2 小时前
前端八股文面经大全:字节前端一面(2026-2-1)·面经深度解析
前端·状态模式
宇木灵2 小时前
C语言基础-四、函数
c语言·开发语言·前端·学习
We་ct2 小时前
LeetCode 114. 二叉树展开为链表:详细解题思路与 TS 实现
前端·数据结构·算法·leetcode·链表·typescript