Angular/Vue/React 联合教程(五)嵌套对象变更__Vue.js
发布于 21 天前 作者 banyungong 80 次浏览 来自 分享
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利

变更一个嵌套对象,因为对象是可变的,所以在 js 中成为了一个问题

const a = {b:{c:1}}
const d = a
a.b.c = 2
console.log(Object.is(a,d))

image.png

a 属性的变化,反应不到 a 上

话说这不是废话么?都用的 const 声明的

但是这个常识,在代码中并不显而易见

在 React/Vue 中,因为是数据驱动的(Angular zone.js 为事件驱动,下面单独讨论),所以,如果数据的变化没有被检测到,那么视图必定不会更新

const [state, setState] = useState({b:{c:1}})
useEffect(()=>{
  setState(res => {
    res.b.c = 2
    return res
  })
},[]}

扑街!

因为 setState 的返回值与 state 并没有变化,变更无效

那怎么办?

一位奇怪的同学突然站起来:好办!

const [state, setState] = useState({b:{c:1}})
useEffect(()=>{
  setState(res => {
    res.b.c = 2
    return lodash.cloneDeep(res)
  })
},[])

……

注意,任何时候,除非是跨平台(跨平台优先序列化,比如JSON),否这 ——

深复制应该被明令禁止!

你的逻辑只需要通知视图变更,并不是要一个复杂度非常高的多实例

单实例和多实例,是两个完全不同的逻辑,如果对象特别大,涉及图像,视频的话?你敢这么操作么?

那么修改为,只需要视图知道我变化了,即,只需要返回一个新 state

export default function App() {
  const [state, setState] = useState({ b: { c: 1 } });
  useEffect(() => {
    setState((res) => {
      res.b.c = 2;
      return { ...res };
    });
  }, []);
  return <h2>{state.b.c}</h2>;
}

image.png

没问题,实现了!

等等,真的实现了么?

return {…res} 的操作,实际上是个浅拷贝,只要是拷贝,在数据量大的情况下,都很危险!

换句话说,深拷贝有问题,难道浅拷贝就是对的?

不不不,深拷贝浅拷贝都是错误答案!那什么才是正确答案呢?

只通知框架进行变更,才是正确答案

即,只需要返回一个新值(新的引用),不要产生其它逻辑:

export default function App() {
  const [state, setState] = useState({value: { a: { b: { c: 1 } } }});
  useEffect(() => {
    setState((res) => {
      res.value.a.b.c = 2
      return {value: res.value}
    });
  }, []);
  return <h2>{state.value.a.b.c}</h2>;
}

这样就可以了

等等,有没有发现什么不得了的东西?

// vue
const data = ref({a:{b:{c:1}}})
data.value.a.b.c = 2

是的,开心的事情发生了,ref 的 value 好像有什么神奇的功效

不过,因为本身 data 就是被代理的,所以 ref 和 reactive 在这里没有本质区别

react 还可以利用函数,来进行变更(返回新的函数一样可以)

image.png

只是注意,useState 传函数的话,默认是惰性初始化,所以,需要再来一下箭头函数

这样,就避免了任何复制

等等,这就完了么?

还没完呢,着啥急!

如果每次访问和变更数据,都需要 state().a.b.c.d.e... 那岂不是每个组件都要写一大长串的代码?

如何将依赖进行聚焦,让共享状态的子组件,只考虑自己需要的数据呢?

对象代理

// react
const {data,setData} = useContext(Parent)

const setPartial = useCallback((res)=>{
  setData(res=>{
    const value = res()
    value.a.b.c = res
    return ()=>value
  })
},[])

// 此时运行函数,仅仅是获取引用的计算,复杂度1
const currentData = data()
const partial = useMemo(()=>{
  currentData.a.b.c
},[currentData])

// vue
const {data} = inject('parent')
const partial = toRef(toRef(toRef(data,'a'),'b'),'c')

此时,React 的 partial,setPartial 和 Vue 的 partial,形成了跨组件的响应性!并做好了关注度分离

这个代理过程,我们叫做:

数据上的 Aggregation (分形聚合)

这里出现了数据上的 Aggregation,而上一篇文章出现了事件的 Aggregation

两种 Aggregation 又会碰处怎样的火花呢?请大家拭目以待~

Angular?

对不起,Angular 这里可以聊的很少,为什么?

在上一篇文章中,大家知道,配合 Rxjs 使用的 Angular,终极目标之一就是,搞掉所有的中间状态

因此,set 是不需要进行代理的 ……

而 get 方法,对于 Rxjs 来说,是个流操作 pipe(pluck('a','b','c'))

并且,事件发生,变更检测发生,因此,你没有通知框架数据变更的需求……

好像 Angular + Rx 确实很厉害,当然,只是头发掉的多,哈哈哈

不要使用全局状态管理库!

是的,你想各方面做到极致的话,不要使用全局状态管理库!

原因很简单,看到前面对象代理的问题没?请问关注度如何分离?

即便可以 mapGetters, 异步,惰性加载什么,代理之后还可能附加更多逻辑,难道还有弄个 mapSetter,mapSetterAysnc,mapSetterAftterEffect 不成?

废代码太多,会干扰你的开发的~

okay,数据流,事件流分形已过,现在又加上了数据结构分形

很快啊!

心潮澎湃的超级功能,就要来了

敬请期待 DDD/微服务 (DDD 的本质是一种软件设计方法,而微服务架构是具体的实现方式)

版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: 杜帅在掘金 原文链接:https://juejin.im/post/6944382681400475678

回到顶部