Vue v-for中json中Push,会导致v-bind中methods从头渲染一遍的问题

问题描述

代码如下

    <div v-for="(d,i) in data">
        <span :class="isFavorite(i) ? 'red' : ''">{{d.name}}</span>
    </div>

当首次加载页面时,会遍历data中所有的数据,每个数据都会判断一次isFavorite方法
但是如果data中push了新的对象,会从头渲染再重复判断一次isFavorite方法
我的项目中绑定的data量巨大,每次只要新增一小批内容,由于从头开始渲染,会导致cpu使用暴增。

问题出现的环境背景及自己尝试过哪些方法

尝试过
加:key 无效
加v-once 无效

相关代码

写了个demo完整代码如下

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Vue2 test</title>
    <style>
        .red{
            color: red;
        }
    </style>
</head>
<body>
<div id="app">
    <button @click="loadMore">loadMore</button>
    <div v-for="(d,i) in data">
        <span :class="isFavorite(i) ? 'red' : ''">{{d.name}}</span>
    </div>
</div>

<script src="https://cdn.bootcss.com/vue/2.5.22/vue.min.js"></script>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            data: [
                {
                    id: 1,
                    name: "one"
                },
                {
                    id: 2,
                    name: "two"
                },
                {
                    id: 3,
                    name: "three"
                }
            ],
            favorite: [
                {
                    id: 1,
                    name: "one"
                }
            ]
        },
        methods: {
            isFavorite: function (index) {
                console.log(index)
                for (let f of this.favorite) {
                    if(this.data[index].id == f.id)
                        return true
                }
                return false;
            },
            loadMore: function () {
                this.data.push(
                    {
                        id:100,
                        name:"100"
                    }
                )
            }
        }
    });
</script>
</body>
</html>

你期待的结果是什么?实际看到的错误信息又是什么?

运行效果如下图,期望是不要重复渲染,只渲染新增部分。
实际测试发现只要v-for下有v-bind,就会出现从头开始重新渲染的问题

运行效果如下

阅读 752
评论 2019-02-25 提问
    4 个回答

    我来回答吧,你可以把会发生重复渲染的模板提取成子组件,就能够避免函数重复触发这样的问题了,下面是我优化后的代码,希望对你有所帮助

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Vue2 test</title>
        <style>
            .red{
                color: red;
            }
        </style>
    </head>
    <body>
    <div id="app">
        <button @click="loadMore">loadMore</button>
        <div v-for="(d, i) in data">
            <item :name="d.name" :id="d.id" :favorite="favorite" :key="i"></item>
        </div>
    </div>
    
    <script src="https://cdn.bootcss.com/vue/2.5.22/vue.min.js"></script>
    <script>
    
      Vue.component('item', {
        props: ['name', 'id', 'favorite'],
        computed: {
          isFavorite: function () {
            console.log(this.id)
            for (let f of this.favorite) {
              if(this.id == f.id)
                return true
            }
            return false;
          }
        },
        template: `<span :class="isFavorite ? 'red' : ''">{{name}}</span>`
      })
    
    
      let vm = new Vue({
        el: '#app',
        data: {
          data: [
            {
              id: 1,
              name: "one"
            },
            {
              id: 2,
              name: "two"
            },
            {
              id: 3,
              name: "three"
            }
          ],
          favorite: [
            {
              id: 1,
              name: "one"
            },
            {
              id: 2,
              name: "two"
            }
          ]
        },
        methods: {
          // isFavorite: function (index) {
          //   console.log(index)
          //   for (let f of this.favorite) {
          //     if(this.data[index].id == f.id)
          //       return true
          //   }
          //   return false;
          // },
          loadMore: function () {
            this.data.push(
              {
                id:100,
                name:"100"
              }
            )
          }
        }
      });
    </script>
    </body>
    </html>

    这样就能保证只在新增的时候触发函数
    clipboard.png

    评论 赞赏 2019-02-25

      首先,感谢邀请。
      1.个人感觉可能是vue的机制有关系,vue会监听绑定的数据,在数据变化时渲染dom,这个时候调用了isFavorite方法。
      2.你可以尝试给数据添加个isFavorite属性默认false,如果添加对象的id等于this.favorite[0].id,就将isFavorite赋值为true。

      <div v-for="(d,i) in data">
          <span :class="d.isFavorite ? 'red' : ''">{{d.name}}</span>
      </div>
      
      loadMore: function () {
          var obj = {
              id:100,
              name:"100",
              isFavorite:false
          }
          if(obj.id == this.favorite[0].id){
              obj.isFavorite:false = true
          }
          this.data.push(obj)     
      }

      这样就只对新增的数据进行运算,方法的时间复杂度也降低了。
      有个弊端就是只对新增数据运算的话,你的this.favorite就不能变化了。

      评论 赞赏 2019-02-25

        加个key值,key值变化可以确定是否重新渲染

        评论 赞赏 2019-02-25

          主要还是key的问题,如果重复渲染了说明key加的不对。

          评论 赞赏 2019-02-26
            撰写回答

            登录后参与交流、获取后续更新提醒