Vue:道具值更改不更新 DOM

Posted

技术标签:

【中文标题】Vue:道具值更改不更新 DOM【英文标题】:Vue: Prop Value change not updating DOM 【发布时间】:2021-04-01 01:53:51 【问题描述】:

我有一个子组件

<script type="text/x-template" id="item-template">
  <li>
    <div
      class="item"
      :class="active: selected"
      @click="toggle">
      <span :style="margin">
        <i v-if="isFolder && !isOpen" class="fas fa-folder"></i>
        <i v-if="isFolder && isOpen" class="fas fa-folder-open"></i>
        <i v-if="!isFolder" class="fas fa-file"></i>
         item.name 
      </span>
      <b-dropdown no-caret>
        <template #button-content>
          <i class="fas fa-ellipsis-v"></i>
          <i class="fas fa-chevron-down"></i>
        </template>
        <b-dropdown-item v-if="isFolder" @click="$emit('show-add-file-modal', item.path)"><i class="fas fa-file-medical pr-1"></i> New File</b-dropdown-item>
        <b-dropdown-item v-if="isFolder" href="#"><i class="fas fa-file-upload pr-1"></i> Upload File</b-dropdown-item>
        <b-dropdown-item v-if="isFolder" @click="$emit('show-add-directory-modal', item.path)"><i class="fas fa-folder-plus pr-1"></i> New Directory</b-dropdown-item>
        <b-dropdown-divider v-if="isFolder"></b-dropdown-divider>
        <b-dropdown-item @click="$emit('rename-item', item)"><i class="fas fa-pencil-alt pr-1"></i> Rename</b-dropdown-item>
        <b-dropdown-item @click="$emit('delete-item', item)"><i class="fas fa-trash pr-1"></i> Delete</b-dropdown-item>
      </b-dropdown>
    </div>
    
    <ul v-show="isOpen" v-if="isFolder">
      <tree-item
        v-for="(child, index) in item.children"
        :key="index"
        :item="child"
        :tree-data-ui-helpers="treeDataUiHelpers"
        @set-active="$emit('set-active', $event)"
        @show-add-file-modal="$emit('show-add-file-modal', $event)"
        @show-add-directory-modal="$emit('show-add-directory-modal', $event)"
        @rename-item="$emit('rename-item', $event)"
        @delete-item="$emit('delete-item', $event)"
      ></tree-item>
    </ul>
  </li>
</script>

<script>
  Vue.component("tree-item", 
    template: "#item-template",
    props: 
      item: 
        type: Object,
        required: true,
      ,
      treeDataUiHelpers: 
        type: Object,
        required: true,
      
    ,
    data: function() 
      return 
        isOpen: false,
        isSelected: false
      ;
    ,
    computed: 
      treeDataUiHelpersComputed: function() 
        console.log(this.treeDataUiHelpers);
        return this.treeDataUiHelpers;
      ,
      isFolder: function() 
        return this.item.type === 'DIRECTORY';
      ,
      margin: function() 
        return "margin-left": (this.treeDataUiHelpers[this.item.path].level * 16) + 'px';
      ,
      selected: function() 
        console.log(this.treeDataUiHelpersComputed[this.item.path].selected);
        return this.treeDataUiHelpersComputed[this.item.path].selected
      ,
    ,
    watch: 
      treeDataUiHelpers: 
        handler(value) 
          console.log(value);
        ,
        deep: true
      ,
    ,
    methods: 
      toggle: function() 
        if (this.isFolder) 
          this.isOpen = !this.isOpen;
        
        else 
          this.$emit("set-active", this.item);
        
      ,
    
  );
</script>

比我在父组件中使用这个组件如下

<tree-item
  :item="treeData"
  :tree-data-ui-helpers="treeDataUiHelpers"
  @set-active="setActive"
  @show-add-file-modal="showAddFileModal"
  @show-add-directory-modal="showAddDirectoryModal"
  @rename-item="renameItem"
  @delete-item="deleteItem"
></tree-item>

每当我需要在事件处理程序中选择这样的活动项时,我都会更改 treeDataUiHelpers 的值

setActive: function(item) 
        if(!item) 
          this.setAllInActive();
          return;
        
        const helperItem = this.treeDataUiHelpers[item.path];
        if(!helperItem.selected)              
          this.setAllInActive();
          //helperItem.selected = true; // not working
          //this.treeDataUiHelpers[item.path] = ...this.treeDataUiHelpers[item.path], selected:true;// not working
          Vue.set(this.treeDataUiHelpers[item.path], 'selected' , true); //not working
          //this.treeDataUiHelpers[item.path]= Object.assign(, this.treeDataUiHelpers[item.path], selected:true)// Not working
          console.log(this.treeDataUiHelpers[item.path]);
          this.setOpenedFilesActive( item.name, item.path);
          const contentItem = this.treeDataContent[item.path];
          const model = this.monaco.editor.createModel(
            contentItem.content,
            contentItem.type
          );
          this.editor.setModel(model);
        
      ,

现在,我花了几个小时查看 *** 和 vue js 论坛,并尝试了所有解决方案。但是当我更改treeDataUiHelpers 的值时,我无法更新 dom。另外,treeDataUiHelpers 的手表也永远不会触发,无论我以哪种方式更改它在父级中的值。任何帮助将不胜感激。

PS,我可以在开发工具中看到 prop 中的值已更新,但 dom 未更新,计算值也未更新。

【问题讨论】:

这些 UI 助手是否用于在树上显示常用工具栏图标?比如编辑、删除等? 目前这些仅用于显示树中项目的级别和切换树项目的活动类。我只是不想弄乱 api 发送的对象,所以我为 ui 创建了一个辅助对象。 某处有 git repo 吗? 不,不可能,。 【参考方案1】:

好吧,Props 是不可变的,或者你可以说它具有单向数据流。

解决此问题的一种方法是订阅 treeItem 组件中的事件,然后在 treeDataUiHelpers 更改时发出该事件。

Vue.component("tree-item", 
    ...
created() 
      EventBus.$on('data-ui-helper-change', updatedTreeDataUiHelpers => 
      this.treeDataUiHelpers = updatedTreeDataUiHelpers 
   ) 
,
data() return  treeDataUiHelpers = null  
....

EventBus 是一个全局的,类似于

EventBus = new Vue();

但如果你有更多这样的场景,那么更喜欢 Vuex 用于组件之间的数据共享。

【讨论】:

但是为什么我必须为此使用事件总线。 props 从子组件是不可变的,从所有者(即父组件)改变它们并没有错。 我的简单问题是道具是反应性的,它在任何地方都非常清楚地说明了,那么为什么当父更改道具对象中的属性时不重新计算计算值。另外,为什么在更改时不触发监视功能。 你看错了,vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow 不,你没有正确理解它。我不想改变孩子的数据。我想更改来自父级的数据并确保它在数据更改时更新 dom。我知道数据会向下流动,但这不是这里的问题,问题是反应性。 哦,误会了 :(。刚刚滚动发现 // 不工作 cmets。【参考方案2】:

如果试图复制这种情况并在 setActive() 中应用双 $set: 在我的示例中(我可以根据要求提供),我从一个空的 treeDataUiHelpers 对象开始,以证明双重设置是有效的。所选属性将被所有树项节点检测到。

setActive(item)  
    this.$set(this.treeDataUiHelpers, item.path, this.treeDataUiHelpers[item.path] || );
    this.$set(this.treeDataUiHelpers[item.path], 'selected', true);

如果我查看您的 setActive 函数,我会看到您尝试这样做


if(helperItem.selected) 
     ...
     // various attempts to set treeDataUiHelpers selected to true
     ..
     Vue.set(this.treeDataUiHelpers[item.path], 'selected' , true); //not working


这看起来很奇怪,如果 helperItem.selected 是 true-ish,尝试将其设置为 true?

【讨论】:

抱歉,这行实际上是if(!helperItem.selected)。我会更新问题 " 我从一个空的 treeDataUiHelpers 对象开始,以证明双重集是有效的" Reagrding this。我不需要证明这一点,因为我确定对象在那里,因为我在创建对象的已创建钩子上添加了节点级别 好吧,这似乎是一个很难远程解决的问题。也许您可以提供有关父组件的更多上下文或提供代码不起作用的工作示例(jsfiddle、codesandbox 等)。 好的,我会尝试为它拼凑一个小提琴。也许它可以以更好的方式阐明

以上是关于Vue:道具值更改不更新 DOM的主要内容,如果未能解决你的问题,请参考以下文章

每当元素 clientWidth 更改时,Vue 都会更新数据道具

Vue cli 3道具(父到子)子在父变量更改后永远不会更新

当根组件中的值更改时,子组件中的 Vue.js props 值不会更新

道具更改时子组件不会更新

VueJS 在不更改父数据的情况下编辑道具的正确方法

如何通过选定的道具动态地更改 vue js 2 中的选项卡?