前端高德一面

103 阅读7分钟

前序

白天上班时候本来想把自我介绍按照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