前序
白天上班时候本来想把自我介绍按照star原则写到自己的面试准备Markdown里,结果白天都在弄gitlab cicd的问题,只写了一半,晚上就只能自己发挥了,面试官是个女,嗯,看起来很严肃,想要给我压气场,可惜前端老司机你是压不住的,我慌的原因只能有一个,那就是八股没背好。
开始面试
面试开始介绍团队
总结就是高德最近在干美团生活娱乐一体化的事情
自我介绍
吃了上周京东的亏,这次按照白天写的脚本念了一半剩下的一半靠自己发挥,特意降下语速, 讲了差不多十五分钟, 按照star原则去讲,事实证明自己介绍将清楚了,能够引导面试官接下来要问你的问题
star 原则
- s: situation(情景); 这件事发生在什么时候?
- t: task(任务); 你要从事的工作任务是什么?
- a: action(行动); 接到任务后你怎么办?
- 你用了多长时间获得完成该任务所必须的知识?------深层次了解员工学习能力等
- 你在这个过程中遇见困难了吗? -------------------了解坚韧性,以及处理事件的灵活性
- r: result(结果); 你最后完成任务的情况如何?
根据自我介绍问了些问题
你说了做了H5的优化,能说说都有哪些优化手段么
嗯,这里要回答时候,不能自己乱序的回答那些优化点, 我这里只讲了客户端渲染优化csr,ssr因为运维不支持,公司内没法使用 了解不太深,可以按照网页的加载的过程去讲
- webview加载:预加载,或者缓存,美团有篇报告讲了这玩意启动时间能够耗时100-200ms tech.meituan.com/2017/06/09/…
- 缓存读取:制定规则,缓存那些不怎么更改的文件在app内,通过webview拦截请求直接返回文件
- dns解析:app -> 手机 -> 路由器,app内是我们可以控制的,可以定时预请求下域名,把dns解析记录下来,较少下一次解析事件。还有就是买的解析服务是不是免费的还是付费的,付费的能保证百分百的解析效率
- ip: 网络机器寻址,对于资源文件就cdn就近寻址加载
- ssl/tsl:运维去做,有优化手段的
- https:这年头应该没有不开启的了,可以开启http2或者3使用多路复用,减少队头阻塞,保证同时能请求多个资源文件
- html:文件一般比较小,可以和runtime.js合并,至于要不要cdn边缘计算合并html片段看场景了
- 图片:针对lcp,一般的头图可以使用new image预加载,针对非首次要看的图片可以onload后加载
- js:区分业务和通用的,通用的缓存到app上,业务的就要区分首屏需要的和非首屏,非首屏的比如游戏结果弹窗,onload事件后懒加载
- svga:动画效果文件,比较大,可以onload后看情况加载,比如游戏的结果才用到的,游戏后面几步才提前加载它
- 至于协商缓存和强缓存:活动类的webview一般不常驻,这两类缓存没啥意义;非活动类的比如商场啊,用户二级页面可以使用常驻的webview的缓存能力,一个是利用webpack持久化构建或者最新的vite都支持了持久化构建,只打包更新的部分,这样原来的部分文件名没变,可以走强缓存和协商缓存
做过跨域方案么,比如rn,了解跨端容器不
用过taro 不了解
说说 pc,h5和微信小程序开发的区别
- pc轻UI重逻辑,轻首屏加载,包都比较大
- h5轻逻辑(小游戏逻辑也不重)重UI,每个功能点必须有UI设计稿,重交互,重首屏加载,包比较小
- 小程序多了些和微信的交互,使用最新的taro开发,差别不大(了解不多)
你现在是react为主是吧,用的react哪一个版本
16或者17
你知道16的某一个版本后,react做了很大的改动,说说这些改动
记不太清了,印象最深的就是react-hook,组合优于继承的方式开发,大大提升了开发效率
提醒下你,比如react的某一些架构发生了很大的改变,大大提升了渲染效率
你是说fiber吧,我来说说fiber改动架构和时间分片调度
- 原先的结果是一棵树,每一次渲染从上到下,不可中断,大规模渲染情况下容易阻塞帧率
- 现在是变成链表,处理完一个fiber节点如果超过了5ms,终断等待下一次MessageChanel宏任务触发处理链表下一个节点
- 时间分片的实现是基于MessageChanel宏任务加上优先级队列,为啥不是setTimeout和promise,因为settimeout不稳定,promise没法中断
复盘:react的改动还有其他的
- 16的事件系统是委托到document,17是#root,这样可以支持多个不同react实例(微前端场景)
- jsx转化优化:17无须显示 import react form 'react';
- diff算法:添加了增量更新,找到变化的部分,更新它
你说到宏任务,微任务,你知道都有哪一些么,怎么识别一个任务是宏任务还是微任务手写代码我看看
- 宏任务:定时器类,dom事件回调,UI渲染,I/O处理,postMessage, MessageChannel
- 微任务:promise.then, MutatioinObserver, process.nextTick(node)
识别:
setTimeout(() => {
console.log('宏任务1执行了')
setTimeout(() => {
console.log('宏任务3执行了')
})
Promise.resolve().then(() => {
console.log('微任务1执行了')
})
Promise.resolve().then(() => {
console.log('微任务2执行了')
})
}, 1000)
setTimeout(() => {
console.log('宏任务2执行了')
Promise.resolve().then(() => {
console.log('微任务3执行了')
})
Promise.resolve().then(() => {
console.log('微任务4执行了')
})
}, 1000)
我们来练习个手写代码吧,实现一个功能要求可以控制并发请求数为3
这东西业务用的很多,实现方式可以是双队列实现(一个存没有执行的,一个存正在执行),也可以是基于生产者消费者模式(MQ消息中间件),这里手写的是双队列实现
class RequsetQueue {
private runTaskList: any[] = [] // 正在执行的任务数组
private taskList: any[] = [] // 任务数组
private errMsgs: string[] = [] // 错误信息数组
private runCount = 0 // 正在执行的任务数量
private runSuccessCount = 0 // 执行成功任务数量
private runTotal = 0 // 总任务数
private limit = 4 // 并发数
private success: Function = () => {}
private error: Function = () => {}
addTask(task: any) {
this.taskList.push({
...task,
uid: getUuid() // 任务唯一标识
})
}
// 检查是不是执行结束
checkOver(task: any) {
this.runCount++
this.runTaskList = this.runTaskList.filter((item) => item.uid !== task.uid)
if (this.runCount === this.runTotal) {
// 执行完所有任务后,触发promise回调
if (this.errMsgs.length) {
this.error(new Error(this.errMsgs.join('\n')))
} else {
this.success(`执行成功了${this.runCount}个`)
}
return
}
this.run()
}
runTask(task: any) {
task
.run()
.then(() => {
this.checkOver(task)
})
.catch((err: any) => {
this.errMsgs.push(err.message)
this.checkOver(task)
})
}
run() {
if (this.runTaskList.length < this.limit && this.taskList.length) {
const addCount = this.limit - this.runTaskList.length // 获取要新增执行的任务数
const addTasks = this.taskList.slice(0, addCount) // 获取要新增执行的任务
this.runTaskList = this.runTaskList.concat(addTasks) // 更新正在执行中的任务
// 更新未执行的任务,像队列一样先进先出
this.taskList = this.taskList.slice(addTasks.length, this.taskList.length)
// 只有未执行的任务才能执行,执行中的,不需要重复执行了
addTasks.forEach((task) => {
this.runTask(task)
})
}
}
start() {
this.errMsgs = []
this.runCount = 0
this.runTotal = this.taskList.length
return new Promise((resolve, reject) => {
this.success = resolve
this.error = reject
this.run()
})
}
}
了解node么,用node做过服务没有
除了开发构建处理脚本,用过nestjs开发基于无头浏览器做自动化任务的服务,自己私下实现过rbac和双token鉴权
其他的node框架呢
自己玩的express算么,应该不算吧,毕业设计用的也是express
我的问题结束了,有啥想问的
我觉得有必要了解下最后一个问题怎么问最好
结束
问了两三次跨端,我说了用开发electron win和mac版,不知道她理不理解这算不算跨端,这块领域还是需要去了解下。还有ssr,不懂也要说出个123