【问题标题】:How to update tree view with VUE如何使用 VUE 更新树视图
【发布时间】:2021-04-27 15:06:45
【问题描述】:

我对树组件结构管理有疑问。 就像一棵树有很多节点一样,当我删除一个节点时,渲染函数运行,但页面没有根据树数据显示正确的信息。

我在这里附上了源代码,请任何人都可以帮助我摆脱这种令人困惑的情况。 非常感谢。

代码示例: 这是树渲染组件,树数据定义在store.state.tree中,我这里标记为reactive,每次删除树中的一个节点,都会调用render函数,我从console.log看到('渲染')

import Node from "./Node";
import {reactive, h} from 'vue';
import store from '@/store';

export default {
  name: 'Tree',
  components: {Node},
  data(){
    return reactive({
      tree: store.state.tree
    });
  },
  render(){
    console.log('render');
    let tree = this.tree;
  
    let renderChildNodes = (node)=>{      
      let vnodes = [];
      node.nextnodes.forEach(node => {
        vnodes.push(renderChildNodes(node));
      });
      let vnode = h(Node,{node: node}, 
        {
          default: ()=>{
            return vnodes;
          }
        }
      );
      return vnode;
    };
    return h('ul', null, renderChildNodes(tree));
  }  
}
</script>

这是节点代码,我从默认插槽的道具中获取节点,并将其标记为反应式。然后我只是从 store.state.tree 中删除节点。我希望树会刷新视图,但是看起来节点实际上被删除了,但是视图中删除的节点没有更新。

<template>
<li>
  <div class="node">
    {{state.node.id}}
    <input v-model="state.node.msg"/>
    <button @click="add(state.node)">add</button>
    <button @click="del(state.node)">del</button>
  </div>
  <ul>
    <li>
      <slot :node="node"></slot>
    </li>
  </ul>
</li>
</template>

<script>
import {onMounted, reactive} from 'vue';
import store from '@/store';

export default {
  name: 'Node',
  props: {
    node: {
      type: Object,
      required: true
    }
  },
  setup(props){
    console.log('node setup');
    const state = reactive({
      node: props.node,
    })
    onMounted(()=>{
      console.log('node onMounted');
    })
    function add(node){
      let sub_node = JSON.parse(JSON.stringify(node));
      sub_node.id = node.id + "-" + (node.nextnodes.length+1);
      sub_node.nextnodes = [];  
      node.nextnodes.push(sub_node);
    }
    function del(node){
      findAndRemoveNode(store.state.tree,node);
      console.log(store.state.tree.nextnodes);
    }
    return{
      state,
      add,
      del
    }
  }
}
function findAndRemoveNode(root, target){
  if(!root.nextnodes.findremove(target)){
    for(let i=0; i<root.nextnodes.length; i++){
      if(findAndRemoveNode(root.nextnodes[i], target))
        return true;
    }
  }else{
    console.log(`removed node:` + target.id);
    return true;
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style lang="scss" scoped>
.node{
  background-color: #e6e4e4;
  text-align:left;
  &:hover{    
    background-color: #c0bdbd;
  }
}
li{
  margin: .2em;
}
</style>

这是商店代码:

import { createStore } from 'vuex';

export default createStore({
    state:{
        tree: {
            msg: "this is root.",
            nextnodes: [{
                id:"1",
                msg: "node 1",
                nextnodes: []
            },{
                id:"2",
                msg: "node 2",
                nextnodes: []
            },{
                id:"3",
                msg: "node 3",
                nextnodes: []
            },{
                id:"4",
                msg: "node 4",
                nextnodes: []
            },{
                id:"5",
                msg: "node 5",
                nextnodes: []
            },{
                id:"6",
                msg: "node 6",
                nextnodes: []
            },{
                id:"7",
                msg: "node 7",
                nextnodes: []
            }]
        }
    },
    mutations: {},
    actions: {},
    modules: {
    }
});

这是main.js,我这里放了remove node函数。

import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App);
app.mount('#app')

//app.config.warnHandler = () => null;
Array.prototype.findone = function(val) {
    for (var i = 0; i < this.length; i++) {
//        if (this[i].id == val.id) return i;
        if (this[i] === val) return i;
    }
    return -1;
};
Array.prototype.findremove = function(val) {
    var index = this.findone(val);
    if (index > -1) {
        this.splice(index, 1);
        return true;
    }
    return false;
};

如果我删除第二个节点, 您可以看到第 7 个节点消失了,但 store.state.tree 实际上被删除了第 2 个节点。视图没有更新,只去掉了最后一个节点,只反映列表大小 delete 2nd node View updated

【问题讨论】:

  • 请提供一个可重现的示例而不是整个存储库,并展示您尝试过的内容、预期的内容、正在发生的事情。
  • 欢迎来到 Stack Overflow。请在问题中发布相关代码。仅将链接用作参考。
  • 对不起,我在帖子中添加了代码,提前感谢您的支持。

标签: vue.js render reactive slots


【解决方案1】:

我遇到了类似的问题,虽然我的实现有点不同。我的实现通过使用 v-for 循环枚举节点来工作。添加子节点按预期工作,但是当删除节点时,即使底层数据发生更改,DOM 也不会更新。

Vue 使用 for-each 循环的 key 来触发突变,因此我通过在树 VM 的根中添加一个名为“updateKey”(只是一个时间戳)的属性来解决这个问题。我的模型中的每个节点都已经有一个唯一的 ID,因此子节点的列表键是其 ID 和父节点 ID 的串联。

当 graphChanged 一直冒泡到树/图形组件时,updateKey 被设置为一个新的时间戳。

树根

    <div v-for="node in root.children" :key="node.item.id+updateKey" >
      <graph-node :node="node" @graphChanged="graphChanged"  />
    </div>

树节点 (图节点)

<div class="graph-block">
    <div class="card">{{node.item.text}</div> 


    <div v-if="node.children && node.children.length">  
        <template v-for="child in node.children" > 
            <graph-node :node="child" :key="`${node.item.id}-${child.item.id}`" @graphChanged="$emit('graphChanged')"  />
        </template>
    </div>
</div>

更多信息: https://vuejs.org/v2/guide/list.html

【讨论】:

    猜你喜欢
    • 2010-09-30
    • 1970-01-01
    • 2016-11-17
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    • 2022-10-23
    • 2011-12-11
    • 1970-01-01
    相关资源
    最近更新 更多