2025-07-08 技术 422 次阅读

字节跳动前端面试心得

今天参加了字节跳动的一面,面试岗位是前端开发实习生。本次算是一次试水,看看自己掌握的技能和那些大厂要求的差距,补足一下自己的短板。记录了面试流程、代码题以及反思总结。

2563 字 20 分钟阅读
2025-06-20 JavaScript 189 次阅读

深入理解 Promise 与异步编程

从回调地狱到 Promise 链式调用,再到 async/await 语法糖,梳理 JavaScript 异步编程的演进脉络与核心原理。

1840 字 15 分钟阅读
2025-05-15 CSS 256 次阅读

响应式布局实战:从媒体查询到容器查询

梳理响应式布局的核心技术方案,包括 rem、vw/vh、媒体查询、flex 与 grid 布局,以及新一代容器查询的应用场景。

2100 字 18 分钟阅读
2025-04-02 Vue 310 次阅读

Vue3 组合式 API 最佳实践

分享在使用 Vue3 Composition API 开发中的一些心得体会,包括响应式数据设计、组合函数抽取与类型推导优化。

1720 字 14 分钟阅读

字节跳动前端面试心得

2025-07-08 19:20 技术 422 次阅读 2563 字 约 20 分钟
本文最后更新于 320 天前,其中的信息可能已经过时,如有错误请发送至评论区或邮箱 alore@can6.top

今天参加了字节跳动的一面,面试岗位是前端开发实习生。主要是在飞书的线上面试,虽然出现了耳机没带、电脑检测不到摄像头、网络卡等问题,不过其他都挺顺利的。

本次也算是一次试水,看看自己掌握的技能和那些大厂要求的差距,补足一下自己的短板,下面打算记录一下本次面试。

面试流程

自我介绍

中规中矩,简单介绍了一下我为什么选择前端开发,是因为出于兴趣和爱好。

聊了聊我的学习历程,在大学学习了 Vue(不小心说成"系统性的学习了 Vue",为后面拷打 Vue 的底层原理埋下了伏笔)。

还在此期间学了前后端的相关知识,另外做了一些项目,包括自己做的和老师做的。

介绍一下自己的项目经历

简单介绍了下自己的个人开发的网站,接着拿了一个 Uniapp 的项目举例,介绍了一下自己如何做技术选型,如何开发,如何部署。

小程序开发和网页开发有什么不一样

我主要回答了两点:

  • 响应式布局
  • 性能差异

围绕响应式布局问了一些 CSS 的问题:

问:如果我要根据手机屏幕来调整显示的字体大小,该怎么做?
答:使用 rem 做字体单位。
问:rem 具体是怎么实现的,或者说我该怎么确定 rem 的值?
答:应该是基于根节点的字体大小计算,具体怎么计算的我忘记了。
后期补充:基于根节点 font-size 的比例来计算,通常设置 html { font-size: 62.5%; } 使得 1rem = 10px,方便换算。
问:你知道 rpx 吗?
答:了解过一些,是一种像素单位吧,好像是根据不同的界面比例来确定像素大小,但具体的用法不太了解。
后期补充:1rpx = 屏幕宽度 / 750,这是微信小程序中的响应式像素单位。
问:说一说 display 的 value 有哪些?
答:block、inline、inline-block、none、flex、inline-flex、grid、inline-grid(只回答了一部分)。
问:你用过 flex 布局吗?
答:用过,可以用来调整子级元素的布局方式。
问:那它有那些属性,或者说有哪些操作方法?
答:能再问详细点吗?(写代码的时候只知道怎么写,描述的话脑子是蒙的)。
问:你知道主轴和副轴吗?
答:justify-content 什么的?(忘了怎么答的了,反正好像没怎么答出来)。
问:你还了解过其他什么响应式布局的方法?
答:媒体查询吧,我大部分是用的这个,其他的没怎么了解。

围绕性能差异又问了些:

问:那你觉得小程序性能方面有什么问题?
答:因为是面向移动端,所以在资源使用方面会被平台本身限制。(显然没答在点上)。
后期补充:小程序性能的核心限制在于通信限制、渲染层与逻辑层分离的双线程架构。
问:这是小程序本身平台的限制,有没有什么性能上的不足?
答:有些计算性能会变慢。(举了个手机端展示地图大量标签元素的例子)。
问:那怎么解决的?
答:一个是标记点位分级,根据缩放来显示不同分级标记。
问:地图是使用的小程序原生地图吗?
答:不是,因为我们业务涉及到地图的更加细致的操作,所以原生提供的地图组件不满足业务需求。使用的 webview 来另起一个地图业务。
问:怎么添加标记的?
答:我们使用的 openlayers 做的地图底层,调用的 openlayer 自带的 api,先通过点击事件获取坐标,再填写表单,再在相应位置添加元素。

接着问了些 Vue 相关的问题:

问:你觉得和 Vue2 相比 Vue3 有哪些优点?
答:性能更快,支持组合式 API,开发更加灵活,对 ts 的支持也更完善。
问:谈一谈你对 Vue 的认识。
答:(直接懵了,不知道从哪方面开始答)首先是采用组件开发,更加方便。
问:描述一下双向绑定。
答:数据更新会让视图更新,视图更新也会让数据同步更新。Vue2 是 Object.defineProperty,Vue3 是使用的 Proxy。(说出来当时就后悔了)。
问:那你说说 defineProperty 底层是怎么实现的?
答:不太清楚。(无奈)。
问:那说说你开发 Vue 的时候,编译器是怎么把 Vue 代码编译成原生代码的。
答:应该不同的编译器方法不同吧,我也不太清楚。(当时已经不知道说什么了)。
后期补充:Vue 编译流程:template 解析为 AST 抽象语法树,再转换为 render function,执行后生成虚拟 DOM,最终通过 patch 算法更新真实 DOM。

问一些前后端的问题

问:了解过跨域问题吗?
答:了解过,浏览器请求页面的地址和服务端响应的源地址不同的话,响应就会被浏览器拒绝。
问:浏览器 URL 有哪些部分组成?
答:协议类型、请求主机、端口、资源路径、请求参数。
问:那怎么解决跨域呢?
答:可以在前端的 config.js 中配置 proxy 代理,也可以在后端的响应头添加可接受源字段。
问:前端代理是怎么实现的?
答:前端也是一个简单的 node 服务嘛,浏览器请求 node 服务,node 再把请求代理至服务器,这样浏览器始终就是从 nodejs 服务发送和接收请求的。
问:那部署之后也是用 node 服务吗?
答:部署之后不会使用 node,那时就是用 nginx 这类工具做代理了。
问:那后端响应头这个方法,加一个响应头就行了吗?
答:(疑惑了一下)。
问:这个响应头的内容是什么?
答:就是允许跨域访问的源地址。
问:加一个这个内容,所有请求都能全部 work 了?
答:不能(既然都这样问了,不能)。
问:那哪些请求不能呢?
答:不知道。
后期补充:涉及预检请求(preflight)的复杂请求需要额外处理,简单加一个 Access-Control-Allow-Origin 头部并不够,还需要处理 Access-Control-Allow-MethodsAccess-Control-Allow-Headers 等。
问:那 img 和静态 js 请求受同源策略限制吗?
答:受限制的。
后期纠正:img 和 script 标签的加载是不受同源策略限制的,它们可以跨域加载资源,但不能访问响应内容。这是 JSONP 等技术的基础。
问:同源策略的目的是什么?
答:为了更加安全吧,不然任何请求都能成功通信。

代码题

1. 数组转嵌套对象

['a', 'b', 'c', 'd'] 转换成嵌套对象:{a: {b: {c: 'd'}}}

当时写了半天没写出来(数组方面都没怎么准备),说了下大致思路:做一个嵌套函数,每一层数组取最后两位转为对象。

后期解答:
// 输入:['a','b','c','d'] 输出:{a: {b: {c: 'd'}}}
                        function buildObject(arr) {
                        return arr.reverse().reduce((acc, cur) => ({ [cur]: acc }))
                        }

2. 写一个倒计时组件(Vue)

要求:显示 60 秒,有暂停按钮,有重置按钮。

用 setInterval 写了一个倒计时组件。之后追问:

问:你觉得还有什么地方能够优化?
答:在暂停功能上吧,我这里暂停是直接把计时器删除了,感觉会有一些不妥。但又说不上来能怎么优化。
问:如果我要显示毫秒怎么办?
答:setInterval 的时间间隔改为 10,再改一下起始值和时间格式化部分。
问:那 setInterval 10ms 和 1ms 有什么区别?
答:1ms 误差更大一点,浏览器调度不能精确到 1ms。
问:如何减小误差?
答:使用时间戳差值来解决。

基础代码实现:

<script setup>
                            import { ref } from 'vue'

                            const totalTime = 60 // 秒
                            const timeLeft = ref(totalTime)
                            let timer = null

                            const start = () => {
                            if (timer) return
                            timer = setInterval(() => {
                            timeLeft.value--
                            if (timeLeft.value <= 0) {
                            clearInterval(timer)
                            timer = null
                            }
                            }, 1000)
                            }

                            const pause = () => {
                            clearInterval(timer)
                            timer = null
                            }

                            const reset = () => {
                            clearInterval(timer)
                            timer = null
                            timeLeft.value = totalTime
                            }
                            </script>

                            <template>
                            <div>
                            <h2>{{ timeLeft }} 秒</h2>
                            <button @click="start">开始</button>
                            <button @click="pause">暂停</button>
                            <button @click="reset">重置</button>
                            </div>
                        </template>

显示毫秒的格式化函数:

function formatTime(ms) {
                            const sec = Math.floor(ms / 1000)
                            const ms2 = Math.floor((ms % 1000) / 10).toString().padStart(2, '0')
                            return `${sec}.${ms2}`
                        }

使用时间戳差值优化版本:

<script setup>
                            import { ref } from 'vue'

                            const total = 60000 // ms
                            const timeLeft = ref(total)
                            let timer = null
                            let startTime = 0
                            let pauseOffset = 0

                            const format = (ms) => {
                            const sec = Math.floor(ms / 1000)
                            const ms2 = Math.floor((ms % 1000) / 10).toString().padStart(2, '0')
                            return `${sec}.${ms2}`
                            }

                            const tick = () => {
                            const elapsed = Date.now() - startTime
                            timeLeft.value = Math.max(total - elapsed - pauseOffset, 0)
                            if (timeLeft.value <= 0) {
                            clearInterval(timer)
                            timer = null
                            }
                            }

                            const start = () => {
                            if (timer) return
                            startTime = Date.now() - (total - timeLeft.value)
                            timer = setInterval(tick, 16) // 60fps
                            }

                            const pause = () => {
                            clearInterval(timer)
                            timer = null
                            pauseOffset += Date.now() - startTime
                            }

                            const reset = () => {
                            clearInterval(timer)
                            timer = null
                            timeLeft.value = total
                            pauseOffset = 0
                            }
                            </script>

                            <template>
                            <div>
                            <h2>{{ format(timeLeft) }} 秒</h2>
                            <button @click="start">开始</button>
                            <button @click="pause">暂停</button>
                            <button @click="reset">重置</button>
                            </div>
                        </template>

反思

知识盲区

知识点 暴露问题
Vue 响应式原理 仅知 Proxy,不清楚 defineProperty 的底层
Vue 编译流程 完全不了解模板编译流程
CSS 响应式布局 了解 rem、rpx 用法,但不能解释细节
跨域 CORS 能答出头部字段,但不知道"加一个响应头并不够"

暴露出的问题

  • 基础知识不够系统、容易被"连环追问压垮"
  • 项目成果略模糊,缺乏"结果导向表达"
  • 临场描述卡顿,表达略缺乏术语与层次

下一次准备

  1. Vue 源码概览
  2. CSS 响应式布局语法梳理(rem / rpx / media query / flex / grid)
  3. 跨域 CORS 原理总结(理解预检请求、浏览器限制对象)
  4. 准备 Vue 常见高频问题(响应式、生命周期、组件通信、diff 算法、双向绑定)
  5. reduce 类结构题多练(嵌套构造、对象映射)
  6. 组件类题目(计时器、列表渲染、滑动窗口、表单绑定、节流防抖)

-- 全文完 --