Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【Question】数组对象更新时,是否有辅助工具缩小更新范围 #7

Open
fantasticsoul opened this issue Oct 17, 2024 · 9 comments

Comments

@fantasticsoul
Copy link

示例的 AutoListObj.tsx 文件,

对比 HeluxListObj.tsx 文件,helux提供了shallowCompare 来比较对象(支持比较深度节点变化的数据,因内部比较的是版本号,故比较效率也非常之高)
image

@zhangfisher
Copy link
Owner

AutoStore的工作原理进基于事件触发响应的,见这里

  • state.root.a.b.c=1时会触发event={type:'root.a.b.c",....}事件
  • 然后useState()时会侦听所有事件,当监听到'所有"事件,就会重新setState({...state})从而引起重新渲染

这样,当执行state.a=1,state.b=2,state.c=2时就会触发abc三个事件,从而引起来
3个setState({...state}),理论上可能会导致三次重新渲染。

因此,AutoStore内部支持batchUpdate方法,如下

store.batchUpdate(state=>{
     state.a=1
    state.b=2
    state.c=3
})

批量更新就会产生batch事件,useState只响应该事件,就优化为只需要执行一次setState({...state})

以上就是useState方法的工作机制,无论是对象或数组对象更新均是一样的。

同时使用useState(path)时,也可以将限制事件监听范围在path内部。

这与helux基于不可变数据的数据比对差别很大。
可以说,该机制下是不需要比对数据的。

@fantasticsoul
Copy link
Author

你的理解有误,我的问题是如何快速方便的在list场景里,修改深节点值时避免其他元素的冗余渲染
image

@zhangfisher
Copy link
Owner

如果使用以下方法:

()=>{
    const [state] = store.useState({
          orders:[
             {price:100,count:10,total:1000},
             {price:100,count:10,total:1000},
             {price:100,count:10,total:1000},
             {price:100,count:10,total:1000},
         ]
    })
   return {
        state.orders.map(order=>{
             return <div>{order.price}</div>
       })
  }
}

像上面这种用法,当修改数组中的任意一项order时,是会导致全部重新渲染的。

优化方式就是直接使用信号组件,如下:

()=>{
    const {state,$} = useStore({
          orders:[
             {price:100,count:10,total:1000},
             {price:100,count:10,total:1000},
             {price:100,count:10,total:1000},
             {price:100,count:10,total:1000},
         ]
    })
   return {
        state.orders.map(order=>{
             return <div>{$(‘order.price’)}</div>
       })
  }
}

这种方式内部也是使用了react.memo,只侦听数组项的变更事件。详细原理见这里

useStore返回的state就是一个经过proxy代理过的对象,与$配合使用。

useState更多的是对React API的兼容.

@zhangfisher
Copy link
Owner

与helux相对,原理基本一样

  • 均是捕获数组项更新,然后其局限在react.memo
  • helux为了不可变数据使用了数据比较,为了避免全局比较用了更多的技巧,就如同reactdiff算法一样。
  • autostore则比较简单,仅是变更事件的响应。

@fantasticsoul
Copy link
Author

fantasticsoul commented Oct 18, 2024

信号组件对老项目的代码模式破坏性较大(这也是helux提供这个功能但提倡针对静态ui多动态ui少的混合ui使用的原因,要不然会变成满天信号),传统的react组件模式都是( Smart Comp + Dumb Comp )模式,Dumb 就是纯纯的ui,对应纯函数(也方便jest单测时基于快照做测试),在list 元素场景里,其实大多数场景更新粒度到组件即可,即 List 变 + 某个ListItem 变,(其他ListItem均不变),建议也提供符合传统用户直觉编码方式的辅助工具。

@zhangfisher
Copy link
Owner

zhangfisher commented Oct 18, 2024

就如我在这篇文章里面介绍的一样。

信号组件简单直观,更新哪里就渲染哪里,API设计上也比较简单。、

AutoStore的信号组件是是可以组合的,所以更新粒度是可控的,可大可小,比如:

// 组合信号
{$(state=>state.user.firstName + ' ' + state.user.lastName)} 
// 自定义渲染
      <ColorBlock name="Age">{$(
        // 自定义渲染函数
        ({value})=>{
          return <div style={{position:'relative',display:'flex',alignItems:'center',color:'red',background:"white"}}>
            <span style={{flexGrow:1}}>{value}</span>
            <Button onClick={()=>state.user.age++}>+Age</Button>
          </div>
        },
        // 状态数据的路径
        "user.age"
      )}</ColorBlock> 

当然,任意技术均有其局限性。

@zhangfisher
Copy link
Owner

zhangfisher commented Oct 18, 2024

两种技术流派的较量:

  1. 基于Diff
  2. 基于signal

好坏见仁见智吧,都有可取之处吧。

我个人觉得,diff相对复杂。
signal会简单些,但是也不可能是万能的,当DOM完全由signal驱动重新渲染时,
一种很重要的问题是需要解决的,就是细粒度signal更新DOM导致DOM文档回流和重绘如何解决?

因为任何DOM元素的重新渲染均有可能导致父元素乃致祖先元素,甚至整个文档的回流和重绘。

如果,signal驱动重新渲染时没有任何调度,则大量的signal分发时,其导致的重新渲染则可能是不可控的。

也就是说,基于signal机制是需要调度signal分发过程的,否则大量的signal分发可能会有灾难。

但是,使用AutoStore的信号组件是没有这个问题的。
因为,本质上,AutoStore的信号组件是对严格意义上的signal模拟,是对react.memo的封装,
DOM渲染调度还是由React fiber提供的。
所以就不用担心这个问题了。

@fantasticsoul
Copy link
Author

fantasticsoul commented Oct 18, 2024

没有具体说哪种不好,哪种更合适,建议两种都提供,要不然对推广不太有利,老项目改造成本也很大。

helux 内部使用时(对接大量老项目)signal使用场景( 包含 Block $ 两种方式)仅占了30%,所以新增了版本化比较的 shallowCompare 来搞定迁移问题,同时也让一些传统的react用户更容易接受。

@fantasticsoul
Copy link
Author

signal cb 渲染出现了bug
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants