面试后的亡羊补牢


微软实习了一个半月了,参与秋招并不是觉得自己不能转正,更多的是希望能把自己带入到应届生的身份中去,掂量掂量自己几斤几两。

也是一个好机会,通过面试去查漏补缺,看看自己的提升方向,在这儿整理一下自己回答的不好或者觉得有价值的问题。

虽说是提起批,但仍然问了相当多的八股文,很多问题我在开发中并没有关注,值得考究。

React 中 key 的作用是什么?具体渲染的时候是如何提升性能的?

key 是 React 中用来对组件进行标记的唯一标识(这里的唯一是指局部唯一)。简单来说 key 帮助 React 识别哪些元素被改变了,例如添加或删除。

React 官方的例子很好地解释了 key 能提升性能的原因。

在更改列表的时候,例如我希望往列表尾部插入一个新的元素:

<ul>
  <li>first</li>
  <li>second</li>
</ul>

<ul>
  <li>first</li>
  <li>second</li>
  <li>third</li>
</ul>

那么此时 React 自然知道只需要往后增加一个元素即可,不需要动前两个元素,这棵树实际上只多了一个 node。

但是,如果需要往列表头增加一个元素:

<ul>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

<ul>
  <li>Connecticut</li>
  <li>Duke</li>
  <li>Villanova</li>
</ul>

这棵树上原来两个 nodes 的位置也变了,所以 React 进行的操作不只是添加,而是变更了 1 + 2 个结点。

但是如果当使用了 key 之后,React 可以使用 key 去匹配树上的每个结点:

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

这样子,React 就知道,只有 key = "2014" 的元素需要被更改。

useMemo 和 useCallback 分别是用来做什么的?具体怎么使用?

useMemo 接收两个参数,第一个参数是个函数,返回值是需要保存的值,第二个参数是一个数组,作为依赖项,变化的时候会执行第一个函数,产生新的值。

应用场景:

  1. 缓存,减少运算量。

    useMemo(() => {
        // 大量运算
        return number;
    }, [props.n]) // 只有 props.n 变化,才会重新计算 number
    
  2. 减少 dom 循环

    // 保证只有在 selectList 的变化的时候,才会重新执行这个循环
    {useMemo(() => (
        <div>{
                selectList.map((i, v) => (
                    <span
                        className={style.listSpan}
                        key={v} >
                        {i.patentName} 
                    </span>
                ))}
        </div>
    ), [selectList])}
    
  3. 减少子组件渲染

    /* 只有当 props.list 列表改变的时候,子组件才渲染 */
    const  goodListChild = useMemo(()=> <GoodList list={ props.list } /> ,[ props.list ])
    

useCallback 接受的参数和 useMemo 类似,区别在于返回的是函数本身,而不是函数的运行结果。

const DemoUseCallback=({ id })=>{
    const [number, setNumber] = useState(1)
    /* 此时usecallback的第一参数 (sonName)=>{ console.log(sonName) }
     经过处理赋值给 getInfo */
    const getInfo  = useCallback((sonName)=>{
          console.log(sonName)
    },[id])
    return <div>
        {/* 点击按钮触发父组件更新 ,但是子组件没有更新 */}
        <button onClick={ ()=>setNumber(number+1) } >增加</button>
        <DemoChildren getInfo={getInfo} />
    </div>
}

类组件和函数组件有什么区别?

标准答案:函数式组件捕获了渲染所用的值。(Function components capture the rendered values.)

React 的 contributor 之一 Dan Abramov 的例子非常的清晰易懂。

假设有两个组件,一个是函数组件,一个是类组件,两个都通过 props.user 从父组件那边获取了一个值 user,并且执行了一个异步的操作(使用 setTimeout 延迟了 3 秒输出 user)。

对于类组件,通过 this.props.user 来获取当前的 user 信息,而函数组件通过 props.user

两者很大的差别就是有没有用 this,对于使用 this 的类组件,他们拿到的永远都是实例上最新的值,因为类组件是跟实例绑定的。

所以,在延迟 3 秒的期间,如果我们改变了 user 的值,那么,类组件最后输出的就是最新的 user 值。

但是,对于函数组件而言,即时等待期间改变 user 的值,最后输出的也是原 user 的值,因为函数式组件是和一个特定的渲染绑定在了一起。

在 React 中,props 是不可变的,而 this 是可变的。

React 的新旧版生命周期

old lifecycle

img

new lifecycle

img

HTTPS 是怎么做到加密的

  1. 浏览器向服务器发送client_random和加密方法列表。
  2. 服务器接收到,返回server_random、加密方法以及公钥。
  3. 浏览器接收,接着生成另一个随机数pre_random, 并且用公钥加密,传给服务器。(敲黑板!重点操作!)
  4. 服务器用私钥解密这个被加密后的pre_random

现在浏览器和服务器有三样相同的凭证:client_randomserver_randompre_random。然后两者用相同的加密方法混合这三个随机数,生成最终的密钥

然后浏览器和服务器尽管用一样的密钥进行通信,即使用对称加密

这个最终的密钥是很难被中间人拿到的,为什么呢? 因为中间人没有私钥,从而拿不到pre_random,也就无法生成最终的密钥了。

常见的 HTTP Headers

  • Accept: text/html, image/png
  • Authorization
  • Host: shopapi.ethanloo.cn
  • Connection: keep-alive
  • User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.57
  • Origin: https://mooc.ethanloo.cn
  • Allow: GET
  • Content-type: application/json; charset=utf-8

参考资料

「React进阶」 React全部api解读+基础实践大全(夯实基础2万字总结) (juejin.cn)

函数式组件与类组件有何不同? — Overreacted