做教育服务的,从小初高到大专大学,都有客户,卖教学硬件和捆绑的软件,同时还提供很多 Sass 软件

此次面试有三个问题没回答好,复盘一下

第三题别被标题迷惑了,邪门的很!

React 中在 setTimeout 内调用 setState

这是个非常常见的问题,一般会问为什么拿到的是旧值

这个面试官直接难度提上来一档,如果没研究过 React 原理,可能会吃哑巴亏

当时是这样的:

  • 面试官:嗯看来你对 React 的理解是没有 Vue 那么深入

  • 我:没看过 React 源码

  • 面试官:我们没想过招多牛的人,没看过没关系

当然研究过也可能翻车,看完下面问题和复盘实操就懂了

调用三次 setState,该组件会重新渲染几次?在 setTimeout 内调用三次呢?

  • 首先是正常的调用三次

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import React, { FC, useState } from 'react';

    const TestPage: FC = () => {
    let [count, setCount] = useState(0);
    function add() {
    setCount(count + 1);
    console.log(1, count);
    setCount(count + 1);
    console.log(2, count);
    setCount(count + 1);
    console.log(3, count);
    }
    console.log('render', count);
    return <button onClick={add}>{count}</button>;
    };

    export default TestPage;

    打开页面控制台会立即打印

    1
    render 0

    点击按钮后

    1
    2
    3
    4
    1 0
    2 0
    3 0
    render 1

    这里很简单,react 会合成多次 setState,所以这边只会渲染一次

    并且因为闭包的原因,add 函数拿到的是更新前 TestPage 函数内的 count,所以 count 一直是 0

    最后合并更新时会拿最后一次的结果,因为 count 一直为 0,所以结果是 0 + 1

  • 然后是 setTimeout

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    import React, { FC, useState } from 'react';

    const TestPage: FC = () => {
    let [count, setCount] = useState(0);
    setTimeout(() => {
    function add() {
    setCount(count + 1);
    console.log(1, count);
    setCount(count + 1);
    console.log(2, count);
    setCount(count + 1);
    console.log(3, count);
    }
    }, 1000);
    console.log('render', count);
    return <button onClick={add}>{count}</button>;
    };

    export default TestPage;

    这里就很迷了,我知道在 setTimeout 内更新状态会脱离 react 的事件流导致立刻渲染,所以猜测会渲染三次

    但是实际试了下,控制台是这样输出的:

    1
    2
    3
    4
    5
    6
    render 0
    render 1
    1 0
    render 1
    2 0
    3 0

    回调内次数一直为 0,很好理解,因为闭包的原因拿到的一直是一开始的 count

    但是啊,除去第一次渲染,setState 调用三次却只触发了两次渲染,经过测试最后一次必定不会触发渲染

    这里就非常非常的奇怪了,到底是为啥啊…

    TODO: 我到现在也是没搞明白,现在去啃 react 源码肯定不现实,维护了 N 年的庞然大物不是三下两下就能吃完的,以后再说吧

Vue3 副作用中使用 setTimeout,里面的响应式对象会被追踪吗?

当时我光速回答不会(看过源码老自信了)

然后:

  • 面试官:副作用是什么意思

  • 我:函数依赖了外部的变量,导致其执行结果的不确定性,这就是副作用

  • 面试官:看来你对函数式编程不太理解啊

  • 我心里:我简历里也没写熟悉函数式思维啊??(虽然我稍微钻研过)

  • 面试官:副作用就是纯函数相反的概念

  • 我心里:那我刚刚说的没错啊???

  • 我:那…

  • 面试官:下一个问题

还不等我反驳,直接开始下一个问题…挺无语的

而且听他的语气我这题回答错了(实际回答正确),当时挺心虚的,被狠狠的拿捏了(PUA)

直接来看代码:

1
2
3
4
5
import { reactive, effect } from 'vue';

const r = reactive({ count: 0 });
effect(() => setTimeout(() => console.log(r.count), 0));
r.count++;

控制台:

1
1

只执行了一次,并没有触发副作用

有人会问为什么是 1

啊这,很简单的事件循环问题…我甚至懒得回答,拜托你去看看我的这一篇文章:事件循环

左栏固定,右侧自适应撑满宽度的布局?

这个问题很简单,我就随口说了三种:

  • flex 布局

  • float: left + BFC 实现

  • inline-block 左栏固定宽度,右侧 calc(100% - width) 计算

那使用 flex 布局时,有哪些方法固定左侧宽度?

到这里还一切正常:

  • 使用 flex-basic 固定左侧宽度

  • 使用 width 也行

分别使用这两种方式固定左侧宽度,缩放浏览器窗口时会有什么不同的表现?

???????

这个问题真的是给我问到了,也太刁钻了

  • 我:不知道

  • 面试官:好吧,下一题

然后经过了狠狠的复盘发现:

TODO: ….好像完全没有区别???我人傻掉