老虎证劵二面

77 阅读4分钟

自我介绍

....

你说你过这个首屏优化,你是怎么做的,最终效果如何

把首屏优化看成是一个大问题,对问题进行抽象、分层和分治

  • 缓存
    • 强缓存
    • 协商缓存
    • pwa
    • cdn
    • 客户端缓存
  • 网络链路
    • DNS
    • TCP/UDP
    • SSL/TLS
    • HTTP
  • 渲染
    • html
      • 预渲染
      • DNS 预解析
      • 压缩
    • css
      • 拆分动态加载
      • 压缩
    • js
      • 通用
      • 业务
        • 首次加载必须
        • 非首次加载必须
    • 其他资源
      • 图片
        • 懒加载
        • 预加载
        • 渐进式加载
        • 压缩
      • 动画文件 SVGA
        • 预加载

最后效果:首屏时间 2s,T95 线达标

你简历上这个多业务线模版规范,你是怎么做的

业务背景....

还有什么项目能和我说说么

那就说下复杂度比较高的数据平台吧...(对方围着着这里又问了几个问题)

你这个广告投放项目的动态联动表单是怎么做的

实现一个 JSON Schema 的表单生成器,然后通过 JSON Schema 生成表单...

能举个例子么

。。。

还有什么项目能和我说说么

说一下海外投放的指纹浏览器吧。。。

那么我们来考考一些基础吧,常用的 react hook 有哪一些

useState,useEffect,useContext,useReducer,useCallback,useMemo,useRef,useImperativeHandle

useCallback,useMemo 有啥区别

useCallback 返回一个 memoized 回调函数,useMemo 返回一个 memoized 值

useState 里面放置一个函数,会怎么样

会执行这个函数,然后返回执行结果作为初始化值

// React 内部 useState 实现(简化版本)
function useState(initialState) {
  // 获取当前 Hook 索引的 Fiber 节点上的状态
  const hook = getHook()

  if (!hook) {
    // 如果是第一次渲染,初始化 Hook,并设置初始状态
    hook = {
      state: typeof initialState === 'function' ? initialState() : initialState,
      queue: []
    }
    mountHook(hook)
  }

  // 返回状态和用于更新状态的函数
  const setState = (action) => {
    // 将新状态入队
    const newState = typeof action === 'function' ? action(hook.state) : action
    hook.state = newState
    // 触发组件的重新渲染
    scheduleUpdate()
  }

  return [hook.state, setState]
}

什么是受控和非受控组件

平时项目都用什么第三方库

  • antd
  • g2
  • g6
  • echarts
  • lodash
  • moment
  • axios
  • days ...

那我们就来说说 antd form 组件是怎么拿到 input 控件的 value 值的

来做做题吧,题 1, 实现一个高阶组件,CheckPropsWrapper(Comp, schema), schema 是一个 yua 库生成的对象,用于校验 props 的值是不是合法,newPropOrErr = schema.validate({ name: 'key' }),如果 err 为空,抛出错误,否则返回 Comp 组件, 注意 newPropOrErr 可能是修正后的值,需要传入给 Comp 组件

function CheckPropsWrapper(Comp: any, schema: any) {
  return function (props: any) {
    const obj = useMemo(() => {
      const newProps: Record<string, any> = { ...props }
      const errs = []
      for (const key in props) {
        try {
          const newPropOrErr = schema.validate({ [key]: props[key] })
          newProps[key] = newPropOrErr[key]
        } catch (e: any) {
          errs.push(e.message)
        }
      }
      console.log(newProps, errs)
      return {
        props: newProps,
        errs
      }
    }, [props])
    return obj.errs.length ? <div>{obj.errs.join(',')}</div> : <Comp {...obj.props} />
  }
}

function TestComp(props: any) {
  return <div>{props.name}</div>
}

const RightTestComp = CheckPropsWrapper(TestComp, {
  validate(obj: Record<string, any>) {
    if (obj.name === 'test') {
      return {
        ...obj,
        name: 'hello'
      }
    }
    return new Error('name is not test')
  }
})

2025 年了怎么判断 error

const error = new Error('error')
console.log(error instanceof Error) // true
console.log(toString.call(error) === '[object Error]') // true

(你倒是说 2025 年了怎么判断 error 啊,然后对方跳过了)

上面的 Comp 如果打印拿到的是什么东西

VNODE对象,对方觉得不对,我线下试了下,如果传入的是一个hook组件,则是一个渲染函数里面包含了react.createElement,如果是class组件,则是一个class组件实例

题目二,实现一个 hook,[ref, hovering] = useHover()

function useHover() {
  const [hovering, setHovering] = useState(false)
  const ref = useRef(null)
  useEffect(() => {
    const node = ref.current
    if (node) {
      const handleMouseOver = () => setHovering(true)
      const handleMouseOut = () => setHovering(false)
      node.addEventListener('mouseover', handleMouseOver)
      node.addEventListener('mouseout', handleMouseOut)

      return () => {
        node.removeEventListener('mouseover', handleMouseOver)
        node.removeEventListener('mouseout', handleMouseOut)
      }
    }
  }, [])
  return [ref, hovering]
}

useRef 有啥使用场景

题目三,实现下面ts的类型,<table data={[{ a: '1', b: '2', c: '3' }]} columns={[{ title: 'a', dataIndex: 'a' }, { title: 'b', dataIndex: 'b' }, { title: 'c', dataIndex: 'c' }]}> >

- 方式一
 
```ts
type TableDataRow<T> = {
  [P in keyof T]: T[P]
}

type TableColumn<T> = {
  title: string
  dataIndex: keyof TableDataRow<T>
}

type TableProps<T> = {
  data: TableDataRow<T>[]
  columns: TableColumn<T>[]
}

type Row = {
  name: string
  age: number
}

const tableProps: TableProps<Row> = {
  data: [{ name: '1', age: 18 }],
  columns: [{ title: 'name', dataIndex: 'name' }]
}
  • 方式二
type TableProps<T> = {
  data: T[]
  columns: {
    title: string
    dataIndex: keyof T
  }[]
}

const data = { name: '1', age: 18 }
const tableProps: TableProps<typeof data> = {
  data: [data],
  columns: [{ title: 'name', dataIndex: 'name' }]
}

方式三

type TableDataRow<T> = Record<string, T>

type TableColumn<D> = {
  title: string
  dataIndex: keyof D
}

type TableProps<T, D> = {
  data: TableDataRow<T>[]
  columns: TableColumn<D>[]
}

const data = [{ name: '1', age: 18 }]
const tableProps1: TableProps<string | number, typeof data[0]> = {
  data,
  columns: [{ title: 'name', dataIndex: 'name' }]
}

> 怎么限制T 只能是stringnumber

忘了

> ts 中nevervoidunknown 的区别

只说了voidneverunknown不知道

- nerver 
  - 一个从来不会有返回值的函数,即死循环
  - 一个总是会抛出错误的函数
- void 没有返回值的函数
- unknown 
  - any类型相似,但更安全,因为对未知值做任何事情都是不合法的。
  - 需要通过类型范围缩小或类型断言,才能在 unknown 类型上进行任何操作

> 有什么想问的么

进去干啥之类的,反正你也不会说

## 总结

都二面了,不应该问问框架原理么,咋抓住语法糖不放过...,还有手写代码时候,面试官可能是个ts类型体操重度患者,看你用js写,就不断diss你,不是大哥,我写的是js,不是ts...