我是差不多在年后一月初才开始陆续投简历,然后一边等面试一边准备,期间确实也没怎么刷太多 leetcode,而且自己算法也挺菜鸡的,简历上主要写了自己的几个小的玩具项目。
一个是参考 Build Your Own React 用 TypeScript 复刻实现的一个简易版的 React,通过这个项目熟悉了一下 React 的核心工作原理。然后前面的那篇文章里面只实现了 useState
这个 hook,我又参考看了下 Preact 试着实现了一个表现行为有些奇怪的 useEffect
。其实还想实现下 SolidJS 那样的真·响应式的 signal 跟 effect,不过暂时没有太多时间去学新东西了。
另一个是之前准备给 Elixir 的 PhoenixFramework 写的一套基于 WebComponents 技术实现的组件库,虽然目前还没几个组件。希望今年能在空余时间继续把这个项目开源做下去。
最后一个是上一个暑假看 Crafting Interperters 跟着用 Kotlin 实现的一个解释器,手写实现了 Lexer 和 Parser,通过遍历 AST 解释执行。实现的这门语言包含了 first-class function, closure, class-based OOP and inheritance 等特性。
Momenta 北京 前端实习
当时投的时候估计应该都在急招人,北京和苏州都有岗位,办事效率挺高,简历投过去没过两天 HR 就来约面试了。面试流程都比较简单,技术面总共面了两轮。
一面 01.09 (~30min)
首先自我介绍,然后面试官看了我简历上的项目叫我挑一个详细介绍一下,然后我就介绍了一下第一个复刻 React 的项目,就顺着把 React 的原理顺着讲了一遍。因为是刚开始第一次面试,的确还是有些紧张,刚开始脑子有点空不知道从哪里讲起,就磕磕绊绊的讲了一堆。反正讲得挺乱的,讲 Virtual DOM 树的时候比较抽象又没有相关演示,反正我自己都觉得挺尴尬的。
然后项目介绍大概讲了十几分钟接近二十分钟的样子,给了一道前序遍历二叉树的题,很简单的算法题,我先大概讲了下思路,用递归实现,然后最开始写完了一版,面试官又叫看能不能优化减少函数递归调用次数,然后这一环节完成。
最后还剩下点时间是反问环节,我问了下面试官对我面试表现的评价,有什么可以改善的地方,然后面试官也说了就讲解 React 原理的时候逻辑不大连贯,建议我其实可以写点可以实际展示 React 渲染过程的小 demo 配合演示讲解下效果会好很多,不过他说觉得我能够自己有兴趣去了解这样的底层原理也很不错了。之后就再问了下实习生的面试安排就结束了。
然后下午 HR 就给我打电话说一面过了,准备第二天二面。HR 其实人还挺好的,说看了我简历内容觉得很不错,还专门给我说了下面试官对我的评价反馈,叫我好好准备下二面。
二面 01.10 (~30 + 10min)
二面换了另一个面试官,面试的时间跟流程都类似。首先自我介绍了一下,然后再叫我分别介绍下简历上写的三个项目。然后针对第一个复刻的 React 项目,叫我描述了一下开发过程中最大的难点,我讲了一个是 Virtual DOM/ React Fiber 树的构建、Diff、渲染过程的实现,还有一个就是 Hook 的实现。之后又叫我讲了一下 React 的原理,在二面之前我又去稍微复盘了一下这个项目,所以二面讲解的效果好了很多。从 JSX 到 Virtual DOM 的创建,到基于 React Fiber 架构的可中断渲染的实现,然后是 reconcile/diff 的流程,讲解了一下 React 从 JSX 到渲染到实际页面的一个核心流程。讲解完后面试官给的评价是还挺好的。
之后又问了一下解释器那个项目,先问了一下闭包特性怎样实现的,刚好就在头天还看到了 Modern JavaScript Tutorial 里面的 closure 这一节,我实现的那个解释器闭包的实现原理其实和 JS 很类似,顺着讲了一下。之后又问我面向对象的继承是怎样实现的,这里最开始讲的时候讲到子类如何继承父类的字段的时候出了点问题,面试官在这里提了问后我才反应过来然后纠正了一下。之后就又问我实现 Lexer 和 Parser 的时候有没有用什么开源库,我回答的是因为实现的这门语言语法比较简单所以就是自己直接手写实现的。
项目经历讲完了大概花了二十分钟左右吧,之后面试官还特地问了我面试有没有刷 leetcode 之类的算法题,我回答的是才刚开始准备面试,还没有刷太多。。。然后就问我有没有了解过 LRU Cache,叫我写代码简单实现一个。很惭愧的是因为我确实之前没仔细去了解过,虽然面试官也给了实现思路,但我没搞清楚它这个要实现的数据结构具体是啥样的就一直愣了半天,最终就写出来个代码框架,还超时了十分钟。
下来搜了下就是这道 leetcode 的 LRU Cache 原题,但很遗憾的是我按分类顺序刷的题没刷到这里 Orz。
二面结束后当天下午 HR 就打电话联系我了,确认了实习的一些相关信息,然后发了口头 offer,而且还叫我不要担心保证一定会很快拿到正式通知的,跟 HR 交流的整个过程还是挺愉快的。
小红书 北京 机器学习平台前端实习
简历投了差不多有一星期后收到了 HR 约面试的电话通知。
一面 01.12 (~30min)
一面全问的八股,我看面试官还在一边看题库一边提问,我怀疑我的简历内容他都没仔细看过,有些问题感觉就是照着题库题目念的,有些没头没脑的我也不清楚想要问的点。
JavaScript
- var/let/const 的区别
- apply/call/bind 的区别
- 解释 0.1 + 0.2 = 0.3 是否成立
- 数据类型有哪些,常用的判断数据类型的方式
- 常用的操作数组的方法,合并对象的方法,我答了
Object.assign
,然后面试官问我知不知道 object spread 是怎样用的,讲了下具体使用的语法格式 - 讲一下 this 的理解,讲一下 new 操作符的作用,讲一下原型的理解,讲一下 JS 的继承可以怎样实现
- 常用的发 HTTP 请求方式(fetch/axios)
TypeScript
- 接口在 TS 中怎么定义,一个全是 number 类型的数组的类型怎么写,元组的类型怎么写
- 问了解过泛型没有,讲一下泛型的语法
吐槽:TS 的几个问题问得我都好无语
CSS
- CSS 选择器优先级(望着面试官愣了一会,然后他笑着问我是不是背了忘了,但其实我根本没背过。。。)
- 怎样实现隐藏一个元素
- 问是否了解 flex/grid 布局,问了下 flex-basis 属性的含义
- 垂直居中布局怎样实现(我答了 flex/grid/绝对定位三种)
React
- 常用的 hook 有哪些,useCallback 和 useMemo 用得多不多
- useEffect 的常见用法,主要就是想问依赖数组的用法
- 受控/非受控组件的理解,高阶组件的理解
- 组件传值的方式(我猜他想问的其实是父子组件通信的方式?我回答了传递 props 跟 context)然后他补充问我还有没有别的方式管理状态比如 Redux,我回答的是我只看过了解过 Redux 文档但没实际用过,我自己主要更偏向于用 Recoil/Jotai 这样的状态管理库。
- 有没有用 React 做过实际的项目,只是像你简历上写的用 React 封装过组件和 hooks 吗?(我内心:What?我简历上写的那个项目是自己复刻实现的 React)然后我回答说了在学习打比赛做过一些项目写前端网页用了 React,然后自己学 React 的时候写过一些 demo。
Vue
我简历上只写了熟悉 React 相关技术栈,问我有没有学过一点 Vue。我回答的是学过一点,但没有深入用过。之后又问我如果接手到一个 Vue 的项目能不能快速上手,看这个样子估计组里主要用的还是 Vue。
浏览器/HTTP
- 讲一下浏览器的跨域机制和前端解决跨域请求的方式
- 讲一下对 HTTP 的理解(???搞的我有些懵,这问得范围也太大了),HTTP 和 HTTPS 有什么区别。
总结一下,感觉这面试体验对我来说不是特别好,问得也太八股了。。。好多都是面试官给出一个问题期待我背出一个标准答案。而且我也还没怎么去背过面经这些,基本都是凭着自己平常写代码的经验答的。等后续 HR 通知了,反正我心里也不是很想去这个组的 233
二面 01.16 (~60min)
这个组应该业务主要用 Vue 的,而我又完全不熟悉 Vue,所以感觉面试流程体验不是特别好。。。感觉又是连我简历内容都没看过的,问的问题还是比较应试化,没去特意背过面经之类的话的确不好回答。
首先自我介绍,然后完事后问了下我熟悉 Vue 还是 React,然后我就讲了一遍自己仿写的 React 的项目,然后 React 的核心工作原理也讲了一遍。
之后问了一些 JS 相关比较杂的问题,CommonJS 和 ES Module 两种有什么区别;谈一下 Promise,讲一下 Promise.all
和 Promise.allSettled
的区别;讲一下深拷贝实现的思路;讲一下 Map 跟 Object 的区别;发起网络请求后需要取消,有哪些方式。之后又问了下 TS 相关的,就只问了下是否了解 TS 的断言,为什么需要断言,TS 为什么会有类型推到不出来的情况需要断言(不太确定我是否理解了面试官想要问的问题)。
然后问了一些比较偏向实际应用场景的问题,遇到需要渲染大量项目的列表时能够怎样优化;浏览器跨域问题的解决方案;谈一下怎样进行渲染优化(这问得比较宽泛。。。我随便答了几个相关的)。之后不知道面试官怎么又想起来问了个 React 相关的问题,我猜测他可能想问的是函数式组件跟传统的类组件有什么区别,但他实际问的是 React Hooks 跟直接继承 React.Component
有什么区别,不知道是不是他照着题库问的一个问题,然后我只能回答自己从来没写过类组件。。。另外因为我之前讲 React 工作流程的时候有提到 Fiber 任务的安排,然后面试官问了我 React 的调度任务优先级,我确实知道 React 是有一套自己的任务调度器的实现的,在源码 repo 中的 scheduler 这个包中,但我没有去深入了解到具体的调度策略,所以答曰不知道。
之后问了一些 CSS 相关的问题,CSS 有哪些定位属性,然后其中我回答到了有 sticky
,然后面试官就顺带问我 sticky
一般用于什么场景,有些什么坑,如果出现问题了会怎样尝试排查;是否了解 BFC;display: none
和 visibility: hidden
有什么区别;是否听说过高度坍缩,该怎样解决(原谅我这个 CSS 渣从没听说过这个问题)。
最后给了三道题:
- 第一道是字符串处理相关的小题,将形如
xxx.xxx.xxx.xxx
这种类似多个 CSS 类名选择器的字符串中出现的起始两个类名提取出来,如果不足两个就保留第一个,例如a.b.c -> a.b
,a.b.c.d -> a.b
,a.b -> a.b
,a -> a
。我也不知道当时怎么想的可能最近在想着写 Lexer 有点魔怔一上来用正则去匹配提取,其实搭配字符串的split
和join
就能简单实现。 - 第二道是数组处理相关的小题,将
[{ id: "a", obj: { foo: "bar" } }, { id: "b", obj: { foo: "bar" } }, { id: "a", obj: { baz: "foo"} }]
这样的数组中的对象按id
属性合并,也就是得到[{ id: "a", obj: { foo: "bar", bac: "foo" } }, { id: "b", obj: { foo: "bar" } }]
这样的结果,实现起来比较简单。 - 第三道算法题,下来之后我找到了是 leetcode 上的一道 medium 难度的原题,Number of Islands,没刷过,卒。
二面完隔了两天后 hr 打电话过来说面试通过了,问什么时候能入职。我觉得我当时面试回答得这么烂竟然还通过了,不过感觉面的部门岗位不是特别合适,所以就拒了。
即刻 上海 小宇宙业务部门
简历投了好像是有一两天后 HR 就来联系面试了,效率还是挺高的。面试下来整体上感觉面试体验跟团队技术氛围都挺好的,应该是我面的这几家公司里聊的最愉快的一家了。简单了解之后,部门所用的技术栈(React, Next.js, CSS in JS, TailwindCSS, Vite, WebAssembly, pnpm, etc.)都跟我个人比较对口。
一面 01.16 (~30min)
一面的面试官刚好遇到是自己学校的校友了,所以一上来就先聊了下学校,当然也没占太多时间。另外因为我面的是即刻下面小宇宙这个产品嘛,主要做播客的,就也问了下我有没有了解过使用过国内的播客产品,我之前在推上看到过播客还是挺流行的样子,很多人也在分享但之前没留意过具体是什么平台的,面完之后我看了下推上转发的播客相关的内容,挺多都是在小宇宙平台上的,没想到还是挺火的。
之后看到我简历上也写了主要以 React 技术栈为主,而且第一个项目也是 React 相关的,就先大概问了一下 React 的 render
函数(React 18 以后被 createRoot
替代)是如何将 JSX 渲染挂载到一个容器节点上的,就顺着这个然后我就又把 React 核心的工作流程原理讲了一遍,完了之后面试官说其实没想要问这么多的,不过也还是挺好的。
之后他本来想问我看 React 的源码有没有具体了解 hydrate
的工作原理,我回答的是 React 代码仓库对于我学习而且还是过于庞大,不好入手,所以我主要是去看了 Preact 的部分源码学习的,自己只是简单了解 hydration 这一过程会发生什么,然后这里我提到了 SSR,之后就面试官就顺着这里问了我简历里也提到了的 Next.js 相关的内容。
首先问了我是否了解 Next.js 的 ISR (incremental static generation) 的功能,就碰巧我自己还真用过这个特性,然后就问了我具体的运用场景。当时我是自己用 Next.js 写的个人博客,用 GitHub Repo Issues 当作一个 CMS 来管理博客文章,然后用 ISR 的功能进行增量生成博客文章的静态页面。之后面试官又问如果现在我们的业务场景不希望引入 Next.js 这样的框架,但也需要实现这样一个类似 ISR 的功能,问我会怎样实现,有什么思路。我大致讲了下自己一个很粗略的实现方案,之后他又问到我 SSR 的情况下 CSS 该怎样处理。问到这个问题我首先还是反应了一下,因为对于传统的 CSS 和 HTML 分离的开发方式的话,HTML 元素依赖预先写好的类名去应用对应的 CSS 样式,这种情况下就算是 SSR 也只需要将渲染出来的 HTML 和 CSS 分别从服务端发送给浏览器就行;之后我随口又答了如果是采用了 CSS in JS 这类需要依赖在运行时动态执行 JS 后才能得到具体的样式信息的方案的话,那么在做 SSR 的话在服务端也需要将组件渲染一次才能得到对应的样式信息,不知道面试官原本想问的是不是这个点 Orz。
之后我另一个组件库的项目因为写了是使用 ESbuild 构建打包的,而且面试官说看我很少使用 Webpack(我简历上也没写这样的信息啊,不知道面试官从哪里判断出来的,而且还是对的,我怀疑他是不是翻了我的 GitHub 从而进行了一个盒的开),然后面试官问了我选择 ESBuild 的原因。我回答说了是因为这个项目本身是给一个传统的类似于 SpringBoot 或 Django 这样搞服务端模板渲染的后端框架配套开发的(这个后端框架就是 Elixir 语言生态下的 PhoenixFramework,走过路过看到这里的对函数式编程感兴趣的强烈安利去了解下 Elixir),然后这个框架配套的前端设施采用了 ESBuild 所以就顺着用了。我对于自己的其它项目的话,如果采用 Next.js 这样有自己脚手架的框架那么就直接用对应的脚手架,不然的话就一律用 Vite。然后面试官就顺带也说了下业务部门里的情况大概也是这样。
再之后因为我简历项目上也提到了使用 pnpm 做 monorepo 管理,然后面试官又顺带问了下我主力使用 pnpm 的原因,我主要就是提了下 pnpm 的依赖管理策略更加省磁盘空间。然后面试官也顺着介绍了一下他们业务部门选用 pnpm 和使用 pnpm workspace 的原因,然后顺带给了一个在 monorepo 场景下配合 git 进行项目管理需要在 monorepo 下的不同包、同时 git 下的不同分支协作下的问题,问我会怎么解决。实际上我自己也没遇到过这么复杂的协作开发的场景。。。当然我也没具体深入研究过 pnpm workspace 还有哪些更复杂的功能,所以就回答了这种状况下我觉得传统的单包单仓库的开发方式管理起来可能会更加容易(或者我现在正在写面经的时候想起来造成这种情况可能是不是因为发版本的策略存在问题。。。)。
最后的反问环节,问了问部门具体的业务和相关的技术栈,面试官很详细地给我介绍了下组内的情况,总体感觉非常 ncie。一面刚结束完 HR 就来约二面了,定在了第二天。
二面 01.17 (~60min)
二面的时候首先是叫手写一道算法题,题目比较简单,将两个有序数组合并成为一个新的有序数组,其实我写的时候默认当非递减的数组处理了,面试官叫要注意一下,但也没太在意这个细节,就当非递减的数组写了。之后又叫写一个同样题目要求下,有多个数组作为输入的场景下的实现,在写这个的时候其中一个细节的地方卡了挺久,但最后跟面试官交流了思路,大致思路还是没问题的。然后手写代码的部分大概花了接近半小时,之后问了下简历相关的内容。之后还是讲了下简历里写的第一个仿写复刻 React 的项目,讲了下 React Fiber 架构的相关理解。之后面试官大概又问了下,看了简历里的项目都比较偏向底层,问实际有没有使用过 React,然后我回答了关于自己使用 React 的相关项目经历。之后又问了下自己学习前端的经历,为什么选择了深入学习 React。之后又问了下我对实习工作的期望,我回答的时候,大概想表达的意思就是说,希望在实习的时候能接触体验看看真实的前端开发工作流程到底是怎样的。这部分聊了应该有接近二十分钟吧,之后反问环境又具体问了问部门的业务之类的信息,然后二面结束。跟一面一样,二面也刚结束 HR 就来约晚上的三面,部门的大前端负责人要和我聊一下。
三面 01.17 (~30min)
三面就相当于和面试官简单地聊聊天,感觉气氛挺好的哈哈。首先面试官先介绍了下他自己,因为他自己本身是写 Kotlin 的 Android 开发出身的,所以 Web 前端这一块的具体技术相关问题就没怎么聊。首先是简单聊了下我的简历项目内容,感觉自己的项目都比较偏向于底层或者前端基础架构这一方向的。之后就聊了聊第一个复刻 React 的项目,问了下怎么想到要做这样一个项目,在实现项目过程中遇到的觉得最有意思的点是什么,然后问了下自己的学习的方式方法。然后因为我简历上写的解释器这个项目是用 Kotlin 实现的,同时我也在简历项目里提到了使用 pnpm 的经历,然后就叫我简单对比一下 pnpm 和 gradle 这两个包管理器有哪些区别,为什么选择了 pnpm。其实我也不是特别熟悉 gradle 的具体构建流程,就大概谈了一下自己的认识和理解。再然后面试官又提到说我简历上写的第二个项目目前在 GitHub 上的 star 数量最多(我没有想到过面试官真的会去看我简历上贴的项目链接,还有我的 GitHub Profile,内心还是挺感动的。。。),问了一下原因。我回答了说因为这个项目是我比较想要投入一些精力认真去做的一个开源项目,然后虽然目前还是 WIP 状态,但是在相关的论坛上有宣传过一波,然后收获了一些 star。之后又因为我写了这个组件库的一个设计目标是要实现无障碍,面试官问了问为什么想到要做这样一款的组件库。我谈了下这个项目的背景,以及自己对无障碍的一些理解。再然后就是因为在之前也提到说,我做的一些项目都比较偏向基础架构的方向,就问了下我对未来具体发展方向的规划,也问了下为什么要选择要做偏向基础架构方向的内容。最后又聊到了自己的学校,我提到了在学校里也加入了工作室,然后就聊了一下学校的工作室的一些情况。最后面试就这样愉快地结束了。
三面结束后的第二天 HR 来联系准备发 offer 了,但比较遗憾的是之前已经接了 momenta 的正式 offer 了,而且感觉跟那边聊得也还算不错,感觉推掉的话也不太好。但说实话的话即刻这边使用的技术栈跟个人十分契合,在面试交流的过程中也非常愉悦,而且能感受到团队的氛围应该是不错的。如果以后有机会的话还是会考虑选择即刻的,只是希望别因为这次拒了 offer 留下不好的印象了 Orz。