本文也发布在了掘金, 可以的话帮忙点个赞嘛~
前言
我真的太后知后觉了..., 原本以为实习是大四的事情, 暑假安心在家里摸鱼, 三月初某天和同样是17级的同学聊天时, 突然知晓对方已经到了字节跳动前端终面, 心里一惊, 问了才知道原来有暑假实习这个东西, 还仅限大三(研二), 于是火速开始复习前端基础以及复盘之前的项目. 当时的规划大概是这样的:
- 非科班, 计算机基础软肋, 重点补计算机网络, 算法和数据结构只准备了一些, lc30道题大概.
- 无社会实习经历, 但有不少项目经历, PC/小程序/后端均有完整项目. 重点复盘其中的亮点难点.
- 对基础设施/工程化/智能化有兴趣, 落地过基于
GraphQL
的BFF, CI/CD/Docker/Serverless/错误监控/埋点都有实践..., 重点准备并作为简历亮点(后来事实证明这一块真的挺重要).
确定了规划之后我就开始8107的准备了, 大概在3-18投出了简历, 经历20天5轮技术+1轮HR成功上岸.
一面
这一面主要问了基础部分, 一部分题目我会带上提示
TCP 三次握手/四次挥手/等待2MSL意义/建立连接但客户端故障
我这里还提了握手失败/泛洪攻击/RST包
TCP 慢启动/拥塞控制, 快速重传/快速恢复, 这也是我准备比较多的, 毕竟之前没接触过.
TCP UDP QUIC(QUIC是Http3的底层协议)
Http2相对于Http1.1新增的
浏览器渲染过程, 注意只是渲染过程, 就是从解析DOM树到展示在屏幕这个过程
主要是 令牌化/建树/收集样式表/布局渲染树/绘制列表/栅格化/绘制图块/...这些过程, 推荐阅读浏览器的工作原理:新式网络浏览器幕后揭秘
强缓存与协商缓存, 主要讲了下
E-Tag
和Last-Modified
以及对应的标识, 强缓存方案与协商缓存方案的场景, 比如index.html
该用哪个?CSS的水平居中与垂直居中, 这个由于我平时CSS写的不多, 通常是在UI库基础上做微调, 只回答了之前记录的方案.
移动端1px, 老问题啦.
Git操作, 主要是revert与reset, 我们工作室使用的是Git Flow, 并且区分Master和Dev分支这种, 小哥说了一个Git Flow无法handle的场景, 即一个feature还未合并到Master, 但是后面的一个feature已经通过提测, 这时要如何处理?
React: 新旧生命周期的问题, 为什么要废弃旧版的(约束开发者以及
Fiber
架构可能会将其打断), 新版的有什么特点(getDerivedStateFromProps(nextProps, prevState)
, 这个方法是静态方法, 也就说你无法在里面获取到this, 还有就是入参为prevState
, 这样就保证state
和props
之间更隔离). 还有就是我觉得很好玩的getDerivedStateFromErrors
和componentDidCatch
的协作.React: PureComponent, 浅比较, 关于浅比较这个我在自己之前一篇讲useSelector的文章中提到过. 放一下
shouldComponentUpdate
中shallowEuqal
的源码:const hasOwn = Object.prototype.hasOwnProperty; function is(x, y) { if (x === y) { return x !== 0 || y !== 0 || 1 / x === 1 / y; } else { return x !== x && y !== y; } } export default function shallowEqual(objA, objB) { if (is(objA, objB)) return true; if ( typeof objA !== "object" || objA === null || typeof objB !== "object" || objB === null ) { return false; } const keysA = Object.keys(objA); const keysB = Object.keys(objB); if (keysA.length !== keysB.length) return false; for (let i = 0; i < keysA.length; i++) { if (!hasOwn.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) { return false; } } return true; }
React: setState之后发生的, 这个当时没怎么看, 只是回答了一下批量更新, 还有在合成事件与原生异步事件中的不同表现, 我个人认为其本质还是同步的哈.
多次setState的合并与获取最新的state, 其实这俩个是同一处代码(好像是particalState
), 内部对参数的Object
类型和Function
类型做了不同的处理, 为函数的情况下, 会在setState调用完成并且组件开始rerender的时候被调用.Node: stream和Buffer, 面试前不久刚写过流式的文件上传所以记忆犹新, 回答了四种流(可写/可读/可写可读/可转换), 以及常用的几个pipe方法.
Buffer的话主要提了一下它是堆外内存(V8的常驻内存由代码区/栈/堆/堆外内存组成)啥的.
Node: 内存管理, 这个也是面试前看到过通过启动命令更改堆内存上限的文章所以了解的多了一下, 主要关键词有 新生代/老生代假说, Scavenge算法(采用复制实现内存回收, 典型的牺牲空间换时间), From/To空间, 标记清除, 标记整理, 增量标记(将标记阶段拆分为控制在5ms内的小步骤, 每隔一段时间执行, 提高程序流畅性.)
一面主要问的都是基础, 面试官给我的感觉就像学长一样! 还给我解答了一哈回答得不好的问题. 一面时间大概一个多小时, 感觉问题都问到我心坎上了? 就是没有问到不太会的问题.
- Web安全, XSS与XSRF攻击. 这里还扯了一下Axios的
X-XSRF-TOKEN
机制. 可惜是在面试后才看到冴羽大大的预测最近面试会考 Cookie 的 SameSite 属性, 不然还能加一波分?
二面
可能因为简历上写的工程化比较多, 二三四面都问了比较多工程化的东西.
微信小程序, 这个是二面的重点提问之一, 包括以下几个方面:
- 生命周期, 包括应用级的和页面级的.
- 架构, View - Native - JavaScript的层级, 系统层能力, 如微信开放能力/离线存储/网络请求等. 视图层的话, 安卓下是腾讯自主研发的基于Blink的X5内核, IOS的话则是自带的WebKit-Webview
- 使用async/await, 小程序目前好像并不能原生支持async/await语法, 需要引入FB的Regenerator库. 说到这个, 我很好奇Taro/Remax这些方案中是如何处理async/await的, 降级为Promise吗?
- 鉴权, code2session这个api, 使用
sessionKey
生成token, openid作为主键入数据库, 再返回自定义的登录态标识. - 与 H5/RN/Flutter/PWA 这些差异, 主要是和 H5/PWA , 我其实不太认同 PWA是小程序祖师爷 这种说法, 甚至认为不是一个性质, 只是他们的目的/玩法是相似的.
- WXS, 我顺便提了一哈, 因为当时小程序也用到了, 主要特点就是运行在View层的逻辑, 并且由于JavaScript在IOS下运行在JSCore, 安卓下运行在V8的原因, 在IOS上WXS可以达到JS数十倍的性能, 但在安卓上和JS持平.
- setState, 很神奇的一点: 数据改变同步而视图更新异步, 主要也是因为上面提到的架构的原因. 有兴趣的可以再查阅相关资料.
- 小程序如何做到禁止访问Dom, 我的理解是小程序压根就没提供DOM/BOM的API, 并且在打包编译的JS里也获取不到Window对象.
@Penumbra/cli, 这是我自己写的一个思路类似
Feflow
的脚手架, 也是 脚手架核心+模板插件包+构建器 的一个组合, 模板插件包的话, 前端包含Webpack
/Parcel
+TypeScript
/JavaScript
的组合, 后端包含Koa
/Egg
+RESTFul
/GraphQL
的组合. 主要问了这些问题:- 为什么要整这个东西, 解决了哪些问题?
- 带来了什么好处?
- 为什么还添加了
Parcel
, 这个主要是因为一些小项目小demo用Parcel
是真的简单, 比如这个我自己搭的一个Parcel-Tsx-Template, 真-开箱即用, 真-零配置. 但是要写什么正式项目的话, 还是老老实实Webpack.
Parcel和Webpack, 由上面的展开问的, 说了一下二者的差异(内置HMR与代码分割, 预置配置), 还有就是
Parcel
其实也有Loader
和Plugin
, 之前翻了一下源码, 叫@Parcel/transformer-xxx
这种. 还有就是Parcel
打包前会做资源树分析优化, 并且过于黑盒, 内部写死了一套配置(好像叫config.json
).还顺便提了下Umi, 因为简历上有个
Umi
+Dva
+Antd Pro
的项目, 分析了下Umi
啥的: 我个人觉得Umi是"框架的框架", 即在Webpack
的基础上做了一套性能调优到极限的配置和满足大部分开发需求的生态(插件)等等.错误监控, 我们目前使用的方式是
Sentry
以及Release时上传Source-Map
文件的方式. 自己实现的话, React的思路主要是一个最外层的<ErrorBoundry />
组件, 借助getDerivedStateFromError
与componentDidCatch
来捕获错误.Https加密机制
Git Rebase 与 Git Merge
Flutter 与 React Native底层, 我只讲到Skia引擎, 毕竟Flutter还没写过完整项目...
Serverless, 这一块我主要讲了
FaaS
以及BaaS
, 还有Serverless
对前端意义, 这个问题千人千面, 我自己的理解不一定是对的, 就不展开来讲了.
二面主要针对项目进行发问, 不得不说果然是前辈, 很多项目死角都被揪出来了, 还好的确之前花了时间思考了下也答上来了. 建议复盘项目可以找个有经验的同学来帮你想想这个项目会从哪些角度被提问, 毕竟当局者迷嘛.
三面&四面
三面和四面发问的角度和提出的问题比较类似, 因此就放在一起讲了.
介绍项目, 我通常会问面试官是对业务型项目还是设施型项目比较感兴趣, 业务型的话就介绍小程序, 设施型就介绍
@Penumbra/cli
.小程序由于 UI/交互/前后端都是自己搞的, 所以能讲的真的蛮多, 但是通常会更聚焦于这个小程序的业务场景(图书资源整合), 这也是在介绍项目时我觉得比较好的一种方式:
不要东扯一点西扯一点, 以 技术栈 -> 业务场景 -> 亮点 -> 难点 -> 提炼总结 -> 自我提升 这几个步骤来叙述会更加条理清晰, 其中亮点/难点以
STAR
法则介绍最佳.@Penumbra/cli
这个, 上面已经介绍过, 就不再赘述. 主要为了体现 新工程目录建立繁琐 -> 应当成员之间统一目录结构 这个意识.GraphQL, 难得遇到会问这个的, 要知道我可是把这个作为简历亮点的, 但是却无人问津..., 主要介绍了这些:
- GraphQL的意义, 与RESTFul的差异
- 对后端的影响, 其实吧我觉得现在不太可能用
GraphQL
作为应用的主API(除非出现了为图式查询而生的数据库, 至于FB和GitHub的GraphQL API我觉得是其内部有自研的方案), 也就是让后端同学来搞, 大部分场景应该还是后端同学搞微服务, 然后前端同学自己来写一个BFF层做接口的聚合/清洗/鉴权等等, 也就是说并不会"这个世界充满了对后端的压迫, 后端什么时候才能站起来, 气抖冷"hhhh. 既然让前端写, 那肯定是用Node了, 这个时候Apollo
这些方案就真的很香了(下面会多介绍下.) - Apollo生态, 在这里我想大胆猜测下, 未来的BFF层一定少不了这三个东西:
Apollo-Server
&TypeGraphQL
&Apollo-Rest-Datasource
, 至于它们是什么感兴趣同学可以去查查. Apollo不仅提供了服务端支持, 也提供了客户端支持, 即Apollo-Client
, 同时使用Server
和Client
来构建应用真的能起到1+1>2的效果, 因为二者就像是一体的. - 推广阻力, 其实只要一个团队比较年轻就没有什么阻力, 主要是可能有一定的学习/开发/维护成本~ 嚷嚷着"学不动了"在前端世界里可能真的寸步难行.
Webpack性能调优, 我从 打包速率 / 打包大小 / 交互友好 三个方面入手的, 这里可以稍微列举一下我觉得比较好用/有趣的Plugin:
- friendly-errors-webpack-plugin, 主要是对抛出的错误做了界面优化, 很多cli都在用.
- speed-measure-webpack-plugin, 测量各个环节的打包耗时, 也可以找出哪一个loader/plugin耗时最久.
- terser-webpack-plugin, 压缩JS
- webpack-bundle-analyzer, 分析打包产物
- webpack-visualizer-plugin, 同样是分析打包产物, 但是更直观
- webpack-parallel-uglify-plugin, 并行压缩, 好像和terser功能重复了.
- webpackbar, 强烈推荐! 在打包时会有进度条(
VuePress
就用的这个) - preload-webpack-plugin, 预加载
至于从配置入手的话, 主要是减少查找文件时间和减少build代码体积, 前者可以通过
resolve
字段中配置extension
, loader中配置exclude
, 后者的话则主要是Tree-Shaking(注意, CSS也可以做摇树优化), 代码分割(动态加载以及Lodash/Antd这种庞大的模块), Source-Map模式, 压缩代码等等.React函数式组件, 我觉得以后会是主流?
React Hooks:
useState
useEffect, 不传dep与传入[], 分别对标类组件的哪个生命周期.
useLayoutEffect
useRef, 还有useImperativeHandle与forwardRef, 摘抄一下之前的笔记, 也可参考[译]React高级话题之Forwarding Refs
useRef,使函数式组件也能够享受到获取 DOM 元素或者自定义组件,父组件获取子组件引用而后调用子组件方法,如 focus 等。
forwardRef,可以获取父组件的 ref 实例作为子组件的参数(与 props 同级),然后再把这个 ref 绑定到自己内部节点,就可以实现 ref 的透传了!
useImperativeHandle,常与 forwardRef 搭配使用,可以控制向父组件暴露的属性以及方法,第一个参数即为 forwardRef 包裹后得到的父组件 ref 实例。
useMemo与useCallback
其他的没怎么用到过就老实交代不记得了.
Hooks思想, 比如Vue3的新API, 社区React生态也在纷纷拥抱Hooks思想, 比如上面提到的的
React-Redux
的useSelector和useDispatch
,React-Use
还有Umi-Hooks
等等.Node的Cluster模块, 主从模式, 底层的Libuv.
CI/CD, 我们工作室的流程还是挺完善的, 包括commitlint -> Husky + Lint-Staged + Code Review*n + ZEIT/now测试环境, 然后才是Gitlab pipeline到OSS.
埋点, 这一块我之前调研过, 可以读读我之前写的这篇关于埋点的一些思考, 主要是以GA为代表的一键式埋点方案, 以MixAlpha/神策数据为代表的可视化埋点等.
测试, Jest/Enzyme/Puppeteer编写单元/集成/E2E测试. 稍微问了下单测覆盖率, 很没底气的说了可能就50%不到hhh.
Flutter, 感觉这种不是比较熟练的技术放到简历上不太好, 比如我用Flutter只能写写简单的Widget和页面这种, 于是就没问得太多.
五面
五面的面试官可能比较忙, 因此整个面试过程大概就二十分钟左右. 也是介绍了一下小程序与脚手架, 面试官应该是高P, 主要关注我在团队中的角色, 我对自己的定义集中在 参与前端技术栈选型&推动新的前端架构&参与对新人培训指导等, 这一块的话也是以自己的经历为主, 如果你是独行侠, 也可以讲一讲自己在社区的贡献等等, 不要直接说你喜欢独来独往一个人全干.
HR面
这一面就是常见的问题啦:
- 大学成绩
- 兴趣爱好
- 有没有女朋友 俩人以后的职业规划
- 个人职业规划
- 成就感
- 团队协作经历
- 从小到大比较顺利还是坎坷(?)
- 为什么学习前端
- 手上有别的offer吗
- 为什么想来阿里, 当然是因为阿里是前端的宝地了
这些问题比较主观, 为了避免误导我就不放自己答案了.
总结&未来前端展望
只能说面试真的是很玄学的东西, 如果每一面都能遇见和你相当match的面试官, 那整个面试流程真的会很轻松愉快. 春招逐渐接近尾声, 也希望大家都能拿到自己满意的offer, 还在面试的同学可以读读我整理的前端基础, 感觉有用的话就点个⭐吧~
对未来前端的展望是二面问到的问题, 我个人的想法主要分这么三点:
- 多端方案, 随着5G的发展, 物联网设备也在逐渐成熟, 到时候前端程序员要适配的屏幕可能又多了几种? 我觉得未来可能会出现真正的多端解决方案, 即更全面的
Rax
或者Taro
, 真正的一次编写处处运行. 当然理想是好的, 在它还未乘着七彩祥云到来前, 还是专心学好每一端吧~ - 侵入后端,
Serverless
无疑是前端仔的下一个风口, 它给予了我们向后端进发的能力, 让我们"自己和自己联调", 也无需操心自己写的服务被流量打爆掉. 后端同学则能够解放出来, 去做一些更有意义的事情.(真的不是抢饭吃) - 智能化, 虽然现在到处都是赋能这个概念, 但是我还是觉得, 前端的终极之一就是赋能其他岗位, 让运营mm能够自己搭自己想要的活动页, 让产品爸爸自己选要对哪些控件/事件/指标进行埋点, 让不想从零学前端的后端直接拖拖拽拽配一个界面出来, 还有就是前端同学, 直接设计图生成UI代码(梦还是要有的). 虽然现在业界已经有一些方案, 但都还存在着或多或少的问题. 这也是我最感兴趣的方向之一.
全部评论
(7) 回帖