如何避免重新渲染由 v-for 指令创建的所有子组件

Posted

技术标签:

【中文标题】如何避免重新渲染由 v-for 指令创建的所有子组件【英文标题】:How to avoid rerendering all child components which are created by v-for directive 【发布时间】:2021-06-02 22:50:09 【问题描述】:

有一个子组件列表

 <question-list-item
        v-for="(item, index) in questionListParsed"
        :key="item.id"
        :both-question="item"
        :class-id="classId"
        :subject-id="subjectId"
        :index="index+1"
      />

questionListParsed 是 vuex 中的 getter。

  /**************************************************************************
   * getters
   **************************************************************************/
  get questionListParsed(): QuestionListItemRes[] 
    const  questionList, showingOriginalQuestion  = this
    const questionListParsed = questionList.map((e) => 
      const recommendQuestion = e.recommendedQuestions[0]
      const recommendQuestionIds = showingOriginalQuestion[e.questionNumber]
      let arr = []
      if (recommendQuestionIds) 
        arr = recommendQuestionIds.filter((item) => 
          return !this.removedRecommendQuestionIds.includes(item)
        )
      
      return 
        recommendQuestion: 
          ...recommendQuestion,
          stem: recommendQuestion.question,
          knowledges: splitMultiKnowledge(recommendQuestion.knowledge),
          questionSourceList: recommendQuestion.sources,
          categoryId: recommendQuestion.categoryId,
        ,
        originalQuestion: 
          ...e,
          id: e.questionNumber,
          stem: e.question,
          difficulty: e.complexity,
          knowledges: splitMultiKnowledge(e.knowledge),
        ,
        id: recommendQuestion.id, 
        questionSimilarId: e.questionNumber, 
        mistakeAnswerId: e.id,
        targetExerciseId: e.targetExerciseId,
        status: recommendQuestion.status,
      
    )

    return questionListParsed
  

questionListParsed 主要取决于状态 questionList 是来自服务器端的原始数据。现在我通过以下方式更改 questionList

  @Mutation
  updateQuestionListByIndex(data: UpdateParams): void 
    if (data.value) 
      const temp = [...this.questionList]
      temp[data.index] = data.value
      this.questionList = temp
    
  

并在这样的操作中提交突变

        this.context.commit('updateQuestionListByIndex', 
          index: targetIndex,
          value: originQuestion[0],
        )

我只想更改数组 questionList 中的一项,然后 questionListParsed 更改。 预期是只更新了一个组件,但所有子组件都更新了(在其更新的飞节中使用 console.log('updated'))。

怎么做?

【问题讨论】:

【参考方案1】:

您不需要阻止子组件重新渲染,Vue 会为您做到这一点。通过为每个列表元素 :key="item.id" 提供唯一键,您可以向 Vue 提供有关该项目的提示,因此 Vue 可以识别和重用已渲染的部分。

更多信息请参见https://vuejs.org/v2/api/#key。

【讨论】:

【参考方案2】:

之所以更新所有组件,是因为你使用了计算属性(Vuex getter 是 Vue 计算属性)。

每当questionList 中的任何内容发生更改时,questionListParsed 都会重新计算,并且因为您正在使用 map 并生成新对象,结果是一个包含全新对象的新数组 --> 列表中的每个子项都会更新

我不会认为这是一个问题,因为实际上只有更改项目的 DOM 元素会被更新(这就是虚拟 DOM 的美妙之处)。如果您确实发现了一些性能问题,解决方法是停止使用computed/getters,而是只在加载数据时进行一次转换,并继续仅使用questionListParsed

【讨论】:

关于虚拟 DOM,我完全同意你的看法。但是我需要点击一个按钮来调用服务器的api来更新questionList。所以我原本打算用getter。如果我只使用 questionListParsed 该怎么办 您真的发现您的应用程序有任何性能问题吗?有些操作很慢或类似的东西?

以上是关于如何避免重新渲染由 v-for 指令创建的所有子组件的主要内容,如果未能解决你的问题,请参考以下文章

避免v-if和v-for用在一起

Vue中使用v-for渲染数据为何要添加key属性?(原理及作用)

将新键添加到对象时如何重新渲染 v-for

v-for指令

七vue中v-for有时候对页面不会重新渲染,数组变化后如何到渲染页面

VUE指令-列表渲染v-for