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

在 vue3 中, computed 计算属性性能问题

  •  
  •   jahnsli · 2022-11-05 23:06:29 +08:00 · 2493 次点击
    这是一个创建于 750 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在下面的例子中,使用 vue3 的计算属性,我们应该将“computed”放在最外层还是单独对数据进行“computed”?哪个表现更好?

    const arr = computed(()=>[
      {options:cpuData.map((item)=>({...item,name:'tom'}))},
      {options:disData.map((item)=>({...item,name:'jerry'}))},
      {options:memoryData.map((item)=>({...item,name:'oh'}))},
      {options:testData.map((item)=>({...item,name:'test'}))},
      {options:[{name:'test'},{name:'test2'}]},
      {options:[{name:'test'},{name:'test2'}]},
      {options:[{name:'test'},{name:'test2'}]},
      {options:[{name:'test'},{name:'test2'}]},
    ])
    
    const arr = [
      {options:computed(()=>cpuData.map((item)=>({...item,name:'tom'}))),
      {options:computed(()=>disData.map((item)=>({...item,name:'tom'}))),
      {options:computed(()=>memoryData.map((item)=>({...item,name:'tom'}))),
      {options:computed(()=>testData.map((item)=>({...item,name:'tom'}))),
      {options:[{name:'test'},{name:'test2'}]},
      {options:[{name:'test'},{name:'test2'}]},
      {options:[{name:'test'},{name:'test2'}]},
      {options:[{name:'test'},{name:'test2'}]},
    ]
    
    
    23 条回复    2022-11-18 17:48:46 +08:00
    vivipure
        1
    vivipure  
       2022-11-06 00:13:05 +08:00
    直觉第二种,但是写还是写第一种,没啥必要。
    wu67
        2
    wu67  
       2022-11-06 00:38:30 +08:00
    // 我一般这么写
    const temp = computed(() => [
    { options: cpuData.map(item => ({ ...item, name: 'tom' })) },
    { options: disData.map(item => ({ ...item, name: 'jerry' })) },
    { options: memoryData.map(item => ({ ...item, name: 'oh' })) },
    { options: testData.map(item => ({ ...item, name: 'test' })) },
    ])

    const constantData = [
    { options: [{ name: 'test' }, { name: 'test2' }] },
    { options: [{ name: 'test' }, { name: 'test2' }] },
    { options: [{ name: 'test' }, { name: 'test2' }] },
    { options: [{ name: 'test' }, { name: 'test2' }] },
    ]

    const result = computed(() => {
    return temp.concat(constantData)
    })
    wu67
        3
    wu67  
       2022-11-06 00:47:47 +08:00
    一般情况下, 纯展示的页面, 前端不需要考虑性能问题.

    但是当你的数据量到达某种程度之后, 就需要考虑空间换时间了, 例如:
    1. 你的一维数组的行数到达了千量级, 动不动就是几千行的数据
    2. 你的数据需要对 2 维数组进行处理, 例如 2~6 千行, 每行里面还有 3 4 百列的
    3. 3 维或者更复杂的玩意...

    到了这种地步之后才需要考虑是否需要优化. 可能实际情况会有所不同, 不能纯纯的靠空间换时间, 因为可能占用内存过高, 浏览器本身卡了, 不是数据便利的卡...
    zcf0508
        4
    zcf0508  
       2022-11-06 00:51:17 +08:00 via Android   ❤️ 1
    一,第二种的 arr 不是响应式的
    二,如果你不需要 arr 变更触发响应式的话,那第二种只会在读取 computed 的 index 才会触发响应式,其他的不会触发响应式,理论上性能更好
    三,就这几行数据应该不会有什么性能的差别
    Finnn
        5
    Finnn  
       2022-11-06 01:33:39 +08:00
    其实你第一种应该是无效的?
    因为 computed 并不能自动深度响应对象内部值的变化
    chenjiangui998
        6
    chenjiangui998  
       2022-11-07 09:40:01 +08:00
    @Finnn computed 默认就是深度响应的
    xintianyou
        7
    xintianyou  
       2022-11-07 10:55:17 +08:00
    @Finnn 你记错了吧,vue2 的 watch 才是默认不会深度响应
    Finnn
        8
    Finnn  
       2022-11-12 22:32:52 +08:00
    @xintianyou 只是题主的 computed 返回的数据结构是新构建的, 不存在监听已有引用类型, 如果 computed 一个对象内部的变化自然是不行的
    chenjiangui998
        10
    chenjiangui998  
       2022-11-15 11:00:07 +08:00
    @Finnn 你这个例子不正说明了 computed 是深度响应的吗, 如果不是, push 对象界面根本不会响应
    chenjiangui998
        11
    chenjiangui998  
       2022-11-15 11:02:35 +08:00   ❤️ 1
    @Finnn 而且直接 return author.books.map(() => '1'), log 也会多次执行
    Finnn
        12
    Finnn  
       2022-11-15 14:25:21 +08:00   ❤️ 1
    @chenjiangui998 你可以打开控制台呢, 看看 computed 了没有
    Finnn
        13
    Finnn  
       2022-11-15 16:06:25 +08:00
    @chenjiangui998 你这例子可差远了, 完全不是一个概念
    Finnn
        15
    Finnn  
       2022-11-17 21:46:46 +08:00
    @mizuhashi computed 并不会勤快到自动响应引用类型内部的未观察属性的变化, 既不合理也没有意义
    mizuhashi
        16
    mizuhashi  
       2022-11-17 21:53:27 +08:00 via iPhone
    @Finnn 我覺得這個說法不對,computed 會觀察所有「用到的」的屬性,但本身不一定會總是重復運行,在保證返回結果正確的情況下。你的例子裏沒有重復執行,但是返回的結果是正確的,我的例子裏如果不重復執行就不能確定結果正確,所以就重復執行了
    Finnn
        17
    Finnn  
       2022-11-17 22:00:58 +08:00
    @mizuhashi 因为你监控了 books.length, 一个变化的基本类型, 上面 return author.books.map(() => '1') 的同样是监控了 books 的每一个值的变化
    Finnn
        18
    Finnn  
       2022-11-17 22:04:55 +08:00
    @mizuhashi 你这说法更不对,怎么会不能确定结果的正确性.
    computed 之所以响应是因为其回调内部的所有监控值变化了才会执行, 直接响应一个对象自然是不会深度检查内部的各个值的变化的
    mizuhashi
        19
    mizuhashi  
       2022-11-17 22:58:51 +08:00
    @Finnn 「深度檢查內部各個值」的具體標準是什麼?如果引用了.length ,那麼 books 的元素變化會導致 length 變化,所以 computed 也重復執行了,這在我看來應該算深度檢查了內部值的。如果只是引用了.books ,這個 computed 關心的就僅僅是 books 返回的數組的引用,由於這個引用沒變,所以 computed 不會重新執行。

    如果要證明「 computed 不會監視深度的數組內容變化」,那麼應該構造這麼一個 computed ,其監視了並使用了數組的內容,但是當數組改變的時候,這個 computed 沒有觸發。如果只是.books ,並沒有使用數組的內容,而使用了數組內容的例子(如.books.length ),computed 是有重新觸發的,所以還需要一個有效的反例才能證明原命題。
    Finnn
        20
    Finnn  
       2022-11-18 00:28:55 +08:00
    Finnn
        21
    Finnn  
       2022-11-18 00:40:41 +08:00
    @mizuhashi 我认为你理解的深度并不是 vue 概念上的 watch 意义的深度,watch 的深度是自动响应引用类型内部所有值的变化的,computed 首先是惰性的,然后是只响应回调函数内所有声明监控的基本类型的变化, 就像 watch/computed:“author.books[3].length” 一样直达某个对象内部的一个基本类型的变化,这不是 vue 意义上的深度
    mizuhashi
        22
    mizuhashi  
       2022-11-18 01:49:12 +08:00
    @Finnn 我大概理解你說的了,你的深度的意思是當內容改變的時候總是會觸發副作用( computed 函數體),從這個定義來說的話 computed 並不是深度的。不過 computed 應該本身不是設計來觸發副作用的,只要它返回的 ref 可以保證響應到正確的數據就行,你最新的例子裏 publishedBooksMessage 也響應到正確的數據了,所以我覺得如果 OP 的 computed 裏面沒有副作用,那麼這個函數會不會重新運行應該是無所謂的
    Finnn
        23
    Finnn  
       2022-11-18 17:48:46 +08:00   ❤️ 1
    @mizuhashi 是的, 我在 #8 说明了, 因为 OP 的计算值返回的是新构造的数据结构, 不存在监控不到要 map 的数组, 当然这个数组变化的项目也必须是基本类型
    computed 不产生也不应该产生副作用
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2354 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 16:05 · PVG 00:05 · LAX 08:05 · JFK 11:05
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.