V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sillydaddy
V2EX  ›  React

React.useEffect 的效果,为什么还跟帧率有关? bug 到底出在哪里?

  •  1
     
  •   sillydaddy · 2022-11-10 12:56:44 +08:00 · 4115 次点击
    这是一个创建于 774 天前的主题,其中的信息可能已经有所发展或是发生改变。

    下面是最小 demo ,逻辑很简单:count 随着鼠标滚轮滚动,不断增加++,通过{divs}渲染出 count 。

    const useWheel = (count:number, setCount)=>{
        let refDiv = useRef<HTMLDivElement>(null);
        useEffect(()=>{
            let el = refDiv.current;
            let callback = (ev:WheelEvent)=>{
                ev.preventDefault();
                setCount(count+Math.floor(Math.abs(ev.deltaY/5)));
            };
            el.addEventListener("wheel", callback, {passive:false});
            return (()=>{el.removeEventListener('wheel', callback);});
        }, [count]);
        return refDiv;
    }
    
    export const Main = (props)=>{
        let [count, setCount] = useState<number>(0);
        let wheel = useWheel(count, setCount);
        let divs = (new Array<number>(25000)).fill(0).map((v,i)=>(<div key={i+count}>{i+count}</div>));
        return (<div ref={wheel}>
                    {divs}
                </div> );
    }
    

    但实际上,渲染的 count 会有「忽增忽减」的现象,感觉不正常。而如果把 25000 条改为 50 条,渲染帧率提高,就不会有同样的问题了。

    上面的例子里,useEffect 是依赖 count 的,按我的理解,count 更新会触发 useEffect 调用。而 useEffect 里面用到的 count 就是最新的值,那为什么结果会「忽增忽减」呢?

    修改 setCount 为函数形式,好像就解决这个问题了。

    setCount((pre)=>pre+Math.floor(Math.abs(ev.deltaY/5)));
    

    但我不理解这里的逻辑:为什么改之前的会有问题呢?难道 useEffect 用的不是最新的 count 吗?

    54 条回复    2023-03-02 18:01:31 +08:00
    cyitao
        1
    cyitao  
       2022-11-10 13:00:02 +08:00 via Android
    setCount(count => // 剩余代码)
    sillydaddy
        2
    sillydaddy  
    OP
       2022-11-10 13:05:53 +08:00 via Android
    @cyitao
    我知道可以这样改,但为什么主题里的代码会出问题呢?问题出在哪里我没有想明白。

    谢谢你的回答,不过如果只是为了寻找一个暂时的答案,我也不用整理最小 demo 发在这里询问大家了。
    gydi
        3
    gydi  
       2022-11-10 13:05:54 +08:00
    数据量大的话,useEffect 的执行就没有 callback 频繁了吧,你这个写法必须保证执行一次 callback 然后再执行一次 useEffect ,才能保证数据正常。
    TWorldIsNButThis
        4
    TWorldIsNButThis  
       2022-11-10 13:07:13 +08:00 via iPhone
    这代码的意思是每变一次 count
    都要新增一个 event listener ?
    gydi
        5
    gydi  
       2022-11-10 13:10:47 +08:00
    你在 callback 里再加一个 removeEventListener ,帧数可能不高,但这样写数据不会异常
    sillydaddy
        6
    sillydaddy  
    OP
       2022-11-10 13:16:59 +08:00 via Android
    @gydi
    但是 callback 本身会修改 count ,count 变了的话,就会触 useEffect 。这样 useEffect 用到的不就始终是最新的吗?
    westoy
        7
    westoy  
       2022-11-10 13:21:51 +08:00 via Android
    用 useLayoutEffect 和 requestAnimationFrame
    gydi
        8
    gydi  
       2022-11-10 13:22:21 +08:00
    @sillydaddy #6 是这么理解,但是 callback 可以在下一次调用 useEffect 之前就执行 n 次,至于 n 是多少,你如何保证。dom 的事件回调和 react 的 hook ,哪个优先级更高呢
    shuding
        9
    shuding  
       2022-11-10 13:27:19 +08:00
    有好几个问题,首先 event listener 不应该放在这个带 count 依赖的 useEffect 里面,否则每次 count 变化都会造成 event listener 被删除然后重新注册。其次如 @cyitao 所说,你的 callback 里面的 count 是一个固定的外部引用。由于 React 的状态更新是异步的,event callback 和 rerender 的触发并不是完全 1:1 运行的。

    建议读读 https://overreacted.io/zh-hans/a-complete-guide-to-useeffect 这篇文章。
    sillydaddy
        10
    sillydaddy  
    OP
       2022-11-10 13:43:31 +08:00
    @gydi > “数据量大的话,useEffect 的执行就没有 callback 频繁了吧”

    我测试了一下,25000 条渲染和 50 条的情况下,2 者的执行次数确实有差异。
    帧率高的时候 callback 和 useEffect 基本是交叉一次,偶尔有例外。帧率低时 callback 明显更频繁。

    这确实解释了为什么帧率低的时候能明显发现这个问题。帧率高时可能是不明显。

    现在发现这个问题,其实就是怎么在 hook 里获取某个 state 的最新值。useEffect 里面的回调要用某个 state 的话,那 useEffect 能提供的更新频率就不够了。

    @TWorldIsNButThis #4 也有 remove 的操作啊
    @westoy #7 我去了解下,这个看名字像是与 Dom 相关的吧。
    sillydaddy
        11
    sillydaddy  
    OP
       2022-11-10 13:47:06 +08:00
    @shuding #9
    callback 的重新创建,主要是希望能引用到最新的 count ,在最新 count 的基础上修改为新值。如果不重新创建 callback ,那 callback 闭包引用的 count 就是固定的旧值了。
    sweetcola
        12
    sweetcola  
       2022-11-10 14:02:22 +08:00
    有几种可能,一是 setCount 被 batch 了,又或者是因为渲染问题导致 wheel 信号发送不稳定。
    Manweill
        13
    Manweill  
       2022-11-10 14:24:31 +08:00
    你这依赖死循环了啊
    serco
        14
    serco  
       2022-11-10 14:36:38 +08:00
    1. setCount 第一次调用 --> 2. count 第一次更新 --> 3. setCount 第二次调用 --> 4. count 第二次更新

    由于 setCount 更新值是异步的,你完全没法保证 2/3 这两步的执行的顺序,那么就可能出现 3 执行时读取的 count 还是第一次更新前的值

    改成 setCount((pre)=>pre+Math.floor(Math.abs(ev.deltaY/5)));不会出错的原因是,你的 update 的方式从直接修改值,变成了"从前值加一个数",setCount 的修改是有队列的,那么修改就是有序的。

    你完全可以只添加一次 addEventListener ,并且在 callback 里面调用另一个真正需要执行的 function , 这个 function 用 useCallback 来根据 count 生成。或者只添加一次 addEventListener ,callback 里面用 setCount(count => ...)这种基于前值修改的方式。后者应该才是正确的方式。
    shawn158
        15
    shawn158  
       2022-11-10 14:48:30 +08:00
    用 useLayoutEffect
    sillydaddy
        16
    sillydaddy  
    OP
       2022-11-10 14:54:16 +08:00
    @serco > “。。在 callback 里面调用另一个真正需要执行的 function , 这个 function 用 useCallback 来根据 count 生成。”
    这个效果应该跟 useEffect 是一样的,dom 上的 callback 执行与 useCallback 执行不是 1:1 的。

    “callback 里面用 setCount(count => ...)这种基于前值修改的方式。”
    这种方式是可以,但有时很麻烦,比如说:
    ```
    if(count %3 == 0) {};
    if(count %3 == 1) setCount(pre=>pre+1);
    if(count %3 == 2) setCount(pre=>pre+2);
    ```

    if 语句里还是要读取 count 的最新值。用函数形式实现就很反直觉(本来是用 count 值来判断是否需要 setCount 的,现在必须调用 setCount 来获取 count 的最新值):

    setCount(pre=>{
    if(pre%3==0) return ???
    if(pre%3==1) return pre+1;
    ...
    });
    morelearn1990
        17
    morelearn1990  
       2022-11-10 15:01:06 +08:00   ❤️ 2
    react 垃圾,来用 vue3
    serco
        18
    serco  
       2022-11-10 15:24:05 +08:00
    @sillydaddy 确实,useCallback 也是有问题的。

    “用函数形式实现就很反直觉”更新方法可以用到前值为什么反直觉?

    你也可以参考这个回答,把传入 useWheel 的 count 更换成一个 ref
    yhxx
        20
    yhxx  
       2022-11-10 15:26:57 +08:00
    https://overreacted.io/zh-hans/a-complete-guide-to-useeffect/
    看这个,应该能解决一部分疑问
    daysv
        21
    daysv  
       2022-11-10 16:05:57 +08:00
    addEventListener 依赖于 count, 每次还在事件里动态改变 count, 哪有这么写代码的
    luvxy
        22
    luvxy  
       2022-11-10 16:58:15 +08:00
    el.addEventListener("wheel", callback, {passive:false});
    sillydaddy
        23
    sillydaddy  
    OP
       2022-11-10 17:18:01 +08:00
    @morelearn1990 #17 > “来用 vue3”
    vue 是基于 MVVM 吧,虽然我把 react 和 vue 都当作黑盒来用,但 react 的黑盒似乎更透明些,vue(还有 mobx)有点 magic 的意味。假如遇到性能问题,还是 react 更好调试和优化。

    @serco #18
    是的,用 useRef 也可以解决,我再去学习学习。。
    Envov
        24
    Envov  
       2022-11-10 17:20:30 +08:00
    这就是 Race Condition 吧,callback 加锁就好了
    rabbbit
        25
    rabbbit  
       2022-11-10 17:32:01 +08:00
    为啥 setCount 顺序会乱,react 的 setState 是无序的吗?
    Envov
        26
    Envov  
       2022-11-10 18:01:54 +08:00
    @Envov 加锁后确实好了
    ```
    const useWheel = (count: number, setCount:any) => {
    const refDiv = useRef<HTMLDivElement>(null);

    useEffect(() => {
    const el = refDiv.current;
    let called = false;
    const callback = (ev: WheelEvent) => {
    ev.preventDefault();
    // console.log(called, count, ev.deltaY);
    if (!called) {

    const beadd = Math.floor(Math.abs(ev.deltaY / 5));
    if (beadd){
    called = true;
    }
    setCount(count + Math.floor(Math.abs(ev.deltaY / 5)));
    }
    };
    el!.addEventListener("wheel", callback, { passive: false });
    return () => {
    called = false;
    el!.removeEventListener("wheel", callback);

    };
    }, [count]);
    return refDiv;
    };
    ```
    myl0204
        27
    myl0204  
       2022-11-10 18:14:12 +08:00
    尽量让 react 帮你处理一切。


    ```tsx
    export const Main = (props)=>{
    let [count, setCount] = React.useState<number>(0);
    const onWheel = (ev: WheelEvent) => {
    ev.preventDefault();
    setCount(count + Math.floor(Math.abs(ev.deltaY / 5)));
    };
    let divs = new Array<number>(25000)
    .fill(0)
    .map((v, i) => <div key={i + count}>{i + count}</div>);
    return <div onWheel={onWheel}>{divs}</div>;
    }
    ```
    skies457
        28
    skies457  
       2022-11-10 21:30:11 +08:00
    react 垃圾,来用 solid (逃
    ruxuan1306
        29
    ruxuan1306  
       2022-11-10 23:11:52 +08:00   ❤️ 1
    楼主代码大概是 Effect 异步顺序导致旧渲染帧的 Effect 后执行了。
    楼上那篇超长博文很有价值阅读。
    监听器一般只设置一次。


    https://codesandbox.io/s/fervent-taussig-x3mdkq?file=/src/App.jsx

    ```
    import { useEffect, useState, useRef, useCallback } from "react";

    import "./styles.css";

    const useWheel = (divRef, count, setCount) => {
    const countRef = useRef(0);
    countRef.current = count; // 每次 count 变化存入所有渲染帧共享的空间

    const callback = useCallback(
    // cb 的闭包被冻结,但可以通过所有渲染帧共享的 ref 获取变化
    (ev) => {
    ev.preventDefault();
    setCount(countRef.current + Math.floor(Math.abs(ev.deltaY / 5)));
    },
    [setCount]
    );

    useEffect(() => {
    const el = divRef.current; // useEffect 是协调之后异步触发,此时 divRef 必然已被填充
    if (!el) return; // 除非该节点渲染错误
    el.addEventListener("wheel", callback, { passive: false }); // 仅设置一次监听器
    return () => el.removeEventListener("wheel", callback); // 仅取消一次监听器
    }, [divRef, callback]);
    };

    export const Main = (props) => {
    let [count, setCount] = useState(0);
    let divs = new Array(25000)
    .fill(0)
    .map((v, i) => <div key={i + count}>{i + count}</div>);

    const refDiv = useRef(null);
    useWheel(refDiv, count, setCount); // ref 作为参数传入语义上更好
    return <div ref={refDiv}>{divs}</div>;
    };

    export default function App() {
    return (
    <div className="App">
    <Main />
    </div>
    );
    }
    ```
    ruxuan1306
        30
    ruxuan1306  
       2022-11-10 23:34:23 +08:00   ❤️ 1
    对于 React 每一个渲染帧:
    1State 变化触发 React 虚拟 DOM 渲染,整个虚拟 DOM 树渲染完毕后,启动协调(对齐虚拟 DOM 到浏览器 DOM ),协调完毕后,开始异步执行上一帧 Effect 的析构函数,最后再执行本帧所有的 Effect 函数。

    注意到了吗,因为本帧的 Effect 回收函数实际在下一帧前执行,也就是在上一帧协调完成的时刻,虽然上一帧的 State 已经渲染上屏用户可见了,但此时上一帧 Effect 的析构函数还没执行,注册在浏览器的还是上一帧的 cb 函数。
    如果此时浏览器触发的 wheel 事件,上一帧的那个 cb 还困在自己过去的闭包,setCount 就还在用上一帧的 count 。
    ruxuan1306
        31
    ruxuan1306  
       2022-11-10 23:36:30 +08:00
    #29 楼第一句描述不准确,#30 楼补充修正了。

    如果难以看懂,就好好读#20 楼的那篇博文。
    sillydaddy
        32
    sillydaddy  
    OP
       2022-11-11 08:25:00 +08:00 via Android
    @Envov 加锁这个办法也不错。

    @rabbbit 可以看 29 和 30 楼 @ruxuan1306 的解释

    @ruxuan1306 非常感谢写下这么详细的分析!
    sillydaddy
        33
    sillydaddy  
    OP
       2022-11-11 08:27:11 +08:00 via Android
    @myl0204 您这个才是最小 demo
    sillydaddy
        34
    sillydaddy  
    OP
       2022-11-11 09:44:55 +08:00
    @ruxuan1306 #30
    我加了一些打印,试图验证你说的 React 的渲染帧过程:

    打印 dom 上的 callback 执行时,对应的 count ,以及设置的新 count:
    ```
    let callback = (ev:WheelEvent)=>{
    console.log("callback called setcount called, count=" + count + ", set_count_to="+(count+Math.floor(Math.abs(ev.deltaY/5))));
    ev.preventDefault();
    ```

    打印 useEffect contruct 和 reconstruct ,以及分别对应的 count 值
    ```
    el.addEventListener("wheel", callback, {passive:false});
    console.log("useEffect called. callback added, now count="+count);
    return (()=>{el.removeEventListener('wheel', callback); console.log("useEffect destructed. callback removed. closure's count="+count);} );
    ```


    ```
    console.log("re-rendered count=" + count);
    return (<div ref={wheel}>
    {divs}
    </div> );
    ```
    sillydaddy
        35
    sillydaddy  
    OP
       2022-11-11 09:51:40 +08:00
    @sillydaddy #34
    不小心发出去了。

    截取了 20 多行打印,应该是验证了 @ruxuan1306 的说法:
    每次 useEffect destructed 后面都紧跟着 useEffect called 。
    count 值的交替变化,反映了这个复杂的异步流程,看着很烧脑。

    ```
    re-rendered count=0
    useEffect called. callback added, now count=0
    callback called setcount called, count=0, set_count_to=0
    callback called setcount called, count=0, set_count_to=2
    re-rendered count=2
    callback called setcount called, count=0, set_count_to=18
    useEffect destructed. callback removed. closure's count=0
    useEffect called. callback added, now count=2
    re-rendered count=18
    callback called setcount called, count=2, set_count_to=5
    useEffect destructed. callback removed. closure's count=2
    useEffect called. callback added, now count=18
    re-rendered count=5
    callback called setcount called, count=18, set_count_to=62
    useEffect destructed. callback removed. closure's count=18
    useEffect called. callback added, now count=5
    re-rendered count=62
    useEffect destructed. callback removed. closure's count=5
    useEffect called. callback added, now count=62
    callback called setcount called, count=62, set_count_to=62
    re-rendered count=62
    callback called setcount called, count=62, set_count_to=78
    re-rendered count=78
    callback called setcount called, count=62, set_count_to=66
    useEffect destructed. callback removed. closure's count=62
    useEffect called. callback added, now count=78
    re-rendered count=66
    ```
    ragnaroks
        36
    ragnaroks  
       2022-11-11 10:39:06 +08:00
    const useWheel = function(element,setCount) : void {
     const callback = useCallback(function(event:WheelEvent){
      event.preventDefault();
      setCount(function(state){return state + 1;});
     },[setCount]);

     useEffect(function(){
      if(!element || !element.current){return;}
      element.current.addEventListener("wheel", callback, {passive:false});
      return function(){
       element.current.removeEventListener('wheel', callback);
      };
     }, [element]);
    }
    ragnaroks
        37
    ragnaroks  
       2022-11-11 10:45:56 +08:00   ❤️ 1
    翻了你的记录发现你最近有很多疑问,我的建议是先不要纠结“为什么会这样”,直接看完文档花一天写个博客 demo ,大多数可能的疑问会在编码过程中自动解答。
    sillydaddy
        38
    sillydaddy  
    OP
       2022-11-11 10:57:11 +08:00
    @ragnaroks
    这些问题都是我从在做的项目中提炼出来的。。虽然花点时间,但可以保证学到背后的一些东西,防止后续还有重复的问题。
    ragnaroks
        39
    ragnaroks  
       2022-11-11 11:58:20 +08:00
    @sillydaddy 前端的话,我建议你面向 SOF 编程,不开玩笑,这可能是你最快提升的方式。
    sillydaddy
        40
    sillydaddy  
    OP
       2022-11-11 14:06:56 +08:00
    @ragnaroks 好好的技术帖子,你来教我做人?不开玩笑,我不是你爸,在外边没人惯着你。
    ragnaroks
        41
    ragnaroks  
       2022-11-11 14:16:29 +08:00   ❤️ 1
    @sillydaddy
    好强的攻击性,不知道我这里怎么让你误解了什么东西,不过我还是说清楚。

    SOF 是 stackoverflow ,一个全球知名技术问答站点,“面向 SOF 编程”是程序员的梗,我这里特地说明不是开玩笑,因为你历史记录里面遇到的问题在 SOF 上都能找到优质解答,而且绝大多编码大佬包括林纳斯都在 SOF 上回答别人的问题。

    百度: https://www.baidu.com/s?ie=UTF-8&wd=%E9%9D%A2%E5%90%91+stackoverflow+%E7%BC%96%E7%A8%8B

    谷歌: https://www.google.com/search?q=%E9%9D%A2%E5%90%91+stackoverflow+%E7%BC%96%E7%A8%8B

    必应: https://cn.bing.com/search?q=%E9%9D%A2%E5%90%91+stackoverflow+%E7%BC%96%E7%A8%8B
    ragnaroks
        42
    ragnaroks  
       2022-11-11 14:17:53 +08:00
    @sillydaddy 如果最终是你误解我了,我乐意接受你的道歉;如果确实是我惹怒你了,我可以给你道歉。
    sillydaddy
        43
    sillydaddy  
    OP
       2022-11-11 14:20:44 +08:00
    @ragnaroks
    我觉得你需要练习一下怎么说话,尤其是在网上。真的,不开玩笑。另外请不要再污染这个帖子,拜托🙏,从你一开始回帖就是在讨论与主题无关的。至于我是否误解你,我觉得倒不是那么重要,别人自有看法。
    ragnaroks
        44
    ragnaroks  
       2022-11-11 14:35:06 +08:00
    @sillydaddy 我重新审视,确实如你所说,你想知道的是 useEffect 在这为什么没有按照你的预想工作,有效回复就是给出 react 官方文档即可,我的第一条回复是给出了一个正常工作的样例代码,对你没有帮助。至于我是否需要练习一下怎么说话,尤其是在网上,我觉得倒不是那么重要,别人自有看法。这是我在你的贴子里最后一条回复,不会再污染你的任何贴子。也请你点进我的个人资料再点 block ,因为作为 react 贡献者我或多或少会在 react 节点中别人的贴子里再次出现。
    ruxuan1306
        45
    ruxuan1306  
       2022-11-11 14:41:59 +08:00 via iPhone
    @ragnaroks 只面向 StackOverflow 的话永远不知其所以然,永远外包水平。

    @sillydaddy 学习方法是对的,我是读了那个长长的博客里的很多篇才明白的。

    我认为关键是要搞清 React 一帧一帧渲染的思想,每帧渲染(函数组件执行返回),且上屏后(协调完成),才按顺序执行每帧各自的 Effect ,值得注意的是,这些 Effect 仍然处在其所属帧的闭包里。
    chenjiangui998
        46
    chenjiangui998  
       2022-11-11 15:12:16 +08:00
    居然扯了一大堆,还用到➕锁的, 这代码死循环了, 监听自身改变, 又自增, 这不扯淡吗
    Envov
        47
    Envov  
       2022-11-11 16:56:52 +08:00 via iPhone
    @chenjiangui998 这个代码虽然有问题,但是没有死循环
    Envov
        48
    Envov  
       2022-11-11 18:04:29 +08:00
    @chenjiangui998 https://codesandbox.io/s/divine-framework-28xq8s?file=/src/App.tsx
    这里有一个监听自身改变, 又自增的例子,和 OP 这个类似,useeffect 递归调用是没问题的,但是也会乱序
    sillydaddy
        49
    sillydaddy  
    OP
       2022-11-11 19:30:38 +08:00
    @ragnaroks

    对于#36 楼的回复,即使你没有看众多的评论,如果阅读了主题的话,也会知道我已经知道了这个 pre=>pre+1 的方法,而且我问的也不是这个问题。直接贴个这样的回复,即使不被认为无礼,也是毫无意义的。

    对于#37 楼的回复,提到了“翻了最近的疑问”,我想请问真的读过吗?还是只看了题目?上个 React 的帖子里,你的回复仍然是驴头不对马嘴。至于后面的建议我实在不理解,这个建议成立的前提是,我没有看过文档,也没有去编码实践,可是如果你翻过之前的帖子内容,怎么会不知道我是在做真实的项目?我甚至直接提到了「我的项目中」。你又凭什么推测我没有看文档呢?

    对于#39 楼的回复,我也搞不懂,你连这个主题和其他主题的内容都没有看,怎么知道我没有搜索过 stackoverflow ?你如果看了主题内容,就知道这些都不能在 stackoverflow 直接搜索出来。不说其他主题,就这个主题的内容,你给找出来一个 stackoverflow 的例子?另外据我所知,stackoverflow 中,也从来没有直接说你去读文档多练练代码的,至少要指出问题所在的点,给出文档对应的知识点,这才是有礼节有意义的回复吧。

    别人初看你的评论似乎很友好,可能还要说我刻薄。但通过上面的观察,你连题目都没看,根本没想着好好交流,而是一直在以居高临下的姿态在说教。你充满优越感不要紧,但不要贬低别人,不要以为说的话里有「建议」「可能」就是友好的。这也是我反感你的原因。

    #41 楼 #44 楼的回复我就不评论了。只能更加印证我的观点。
    sillydaddy
        50
    sillydaddy  
    OP
       2022-11-11 19:47:20 +08:00
    @ragnaroks 对于你在#44 楼有意无意提到的 react 贡献者,以你在这个帖子里表现,如果是真的话,反而显得更加讽刺。我觉得其他楼层的人**都**是你的榜样,无一例外。
    sillydaddy
        51
    sillydaddy  
    OP
       2022-11-14 10:57:38 +08:00
    对于楼上的公案,这个 @ragnaroks 在另外一个帖子里 /t/894861 里又发表了下面的评论,可以理解,毕竟已经说出大话再也不给我回帖了,再不回帖就要给憋坏了。

    他的评论:
    ```
    这个其实简单来说就是“我发帖是来找认同的,不是来找质疑”的。

    比如我的最近回复 /t/894109 ,我先入为主发了我觉得楼主能看懂的代码,对比其区别答案就显而易见,但是那个楼主其实看不懂,我又说面向 SOF 编程就行,被他理解为在教他做人,那我只能顺着他的意思给他道歉,辛亏没机会在现实共事。

    不过说回来,在网络上发言确实很多情况下没有考虑到别人的需要,很多时候是“我觉得你需要这个”或“不管你现在怎么想,听我的没错”。我自认为我就是这样的人,先入为主认为别人应该和我具有相同水平,能完全正确理解我的意思,不过我是不会改的,这是我的个性,不管是好是坏。

    实际上在网络上很多人中文都没法正确理解,比如把和“学力”看成“学历”。随着冲突加剧,网络论坛素质变低是无法避免的。以前有个笑话是只有黄网下面回复都是楼主好人,实际上 P 站评论区人种歧视起步。
    ```

    当然,贴出这个并不是为了反驳或羞辱他,而是因为他的新评论明显就是混淆黑白了(除非他的水平真的不懂我楼上的回复在说什么)。对于这种混淆黑白,我是一定会给予回击的,不要以为随便颠倒是非可以不受惩罚。
    morelearn1990
        52
    morelearn1990  
       2022-11-14 16:30:56 +08:00
    @sillydaddy 简单的可以理解为发布订阅模式。个人感觉 vue3 要比 react 相对较晚遇到性能瓶颈吧,react 一个小小的组件就有可能因为输入事件需要解决 useEffect 或者 useCallback 重复申明导致的性能问题。vue3 的 setup 只运行一次,其实更加符合直觉点。不过不同的框架都有不同框架的烦恼,vue3 跨不过去 ref 的坎,写起来挺麻烦的。
    sillydaddy
        53
    sillydaddy  
    OP
       2022-11-15 14:36:31 +08:00
    @ragnaroks
    今天又翻了翻你最近的回复记录,发现你的回复都很友好(虽然最近能从偶尔几个回复中感觉到一点变化)。我感觉前几天确实误解你的意思了。我向你道歉。
    误解的其中一个原因是上次翻看你的回复记录,恰好碰到你正在帖子 /t/894008 里跟其他人争论,我翻到那儿就没有继续往后翻了(因为感觉找到证据了)。
    跟人吵架对人心理影响是比较大的,起码对于我自己是这样。希望没有对你造成太大困扰,sorry 。
    realJamespond
        54
    realJamespond  
       2023-03-02 18:01:31 +08:00
    滚动后不停的 listen 又不停 remove 的意义是啥?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1554 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 73ms · UTC 16:48 · PVG 00:48 · LAX 08:48 · JFK 11:48
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.