【问题标题】:VueJs watching deep changes in objectVueJs 观察对象的深层变化
【发布时间】:2018-07-12 22:44:19
【问题描述】:

我在 VueJS 中有这 3 个组件。我要解决的问题是:当我点击vehicle组件时,它需要被选中(selected = true)而其他车辆未被选中。 我需要为双向数据绑定做什么?因为我在VehiclesList.vue 组件中更改了这个selected 属性,并且它还需要在Monit.vue(它是父级)中进行更改,并且'Vehicle.vue' 需要观察这个属性以进行更改类。

还有更新车辆的问题。在Monit.vue 中,我不会像this.vehicles = response.vehicles 那样更新完整对象,但我会逐个更新,并且只更改monit 属性。

也许更容易的是为此使用商店。但我想在组件中做到这一点。

已编辑:数据结构

 {
   "m":[
      {
         "id":"v19",
         "regno":"ATECH DOBLO",
         "dt":"2017-10-09 13:19:01",
         "lon":17.96442604,
         "lat":50.66988373,
         "v":0,
         "th":0,
         "r":0,
         "g":28,
         "s":"3",
         "pow":1
      },
      {
         "id":"v20",
         "regno":"ATECH DUCATO_2",
         "dt":"2017-10-10 01:00:03",
         "lon":17.96442604,
         "lat":50.6698494,
         "v":0,
         "th":0,
         "r":0,
         "g":20,
         "s":"3"
      },
   ]
}

Monit.vue

<template>
  <div class="module-container">
      <div class="module-container-widgets">
        <vehicles-list :vehicles="vehicles"></vehicles-list>
      </div>
  </div>
</template>
<script>
import VehiclesList from '@/components/modules/monit/VehiclesList.vue';

export default {
  name: "Monit",
  data (){
      return {
          vehicles: null
      }
  },
  components: {
    VehiclesList
  },

  methods: {
    getMonitData(opt){
        let self = this;

        if (this.getMonitDataTimer) clearTimeout(this.getMonitDataTimer);

        this.axios({
            url:'/monit',
          })
          .then(res => {
                let data = res.data;
                console.log(data);
                if (!data.err){
                    self.updateVehicles(data.m);
                }

                self.getMonitDataTimer = setTimeout(()=>{
                    self.getMonitData();
                }, self.getMonitDataDelay);

              })
          .catch(error => {

          })
    },
    updateVehicles(data){
        let self = this;
        if (!this.vehicles){
            this.vehicles = {};
            data.forEach((v,id) => {
                self.vehicles[v.id] = {
                    monit: v,
                    no: Object.keys(self.vehicles).length + 1
                }
            });
        } else {
            data.forEach((v,id) => {
                if (self.vehicles[v.id]) {
                    self.vehicles[v.id].monit = v;
                } else {
                    self.vehicles[v.id] = {
                        monit: v,
                        no: Object.keys(self.vehicles).length + 1
                    }
                }
            });
        }
    },
  },
  mounted: function(){
     this.getMonitData();
  }
};
</script>

VehiclesList.vue

<template>
  <div class="vehicles-list" :class="{'vehicles-list--short': isShort}">
      <ul>
          <vehicle 
                v-for="v in vehicles" 
                :key="v.id" 
                :data="v"
                @click.native="select(v)"
          ></vehicle>
      </ul>
  </div>
</template>
<script>
import Vehicle from '@/components/modules/monit/VehiclesListItem.vue';

export default {
    data: function(){
        return {
            isShort: true
        }
    }, 
    props:{
        vehicles: {}
    },
    methods:{
        select(vehicle){
            let id = vehicle.monit.id;
            console.log("Select vehicle: " + id);
            _.forEach((v, id) => {
                v.selected = false;
            });
            this.vehicles[id].selected = true;
        }
    },
    components:{
        Vehicle
    }
}
</script>

Vehicle.vue

<template>
  <li class="vehicle" :id="data.id" :class="classes">
      <div class="vehicle-info">
        <div class="vehicle-info--regno font-weight-bold"><span class="vehicle-info--no">{{data.no}}.</span> {{ data.monit.regno }}</div>
      </div>
      <div class="vehicle-stats">
          <div v-if="data.monit.v !== 'undefined'" class="vehicle-stat--speed" data-name="speed"><i class="mdi mdi-speedometer"></i>{{ data.monit.v }} km/h</div>
      </div>

  </li>
</template>
<script>
export default {
    props:{
        data: Object
    },
    computed:{
        classes (){
           return {
               'vehicle--selected': this.data.selected
           }
        }
    }
}
</script>

【问题讨论】:

    标签: javascript css html vue.js


    【解决方案1】:

    在 VueJS 2.0 中不推荐使用双向组件数据绑定,以实现更多的事件驱动模型:https://vuejs.org/v2/guide/components.html#One-Way-Data-Flow

    这意味着,在父组件中所做的更改仍会传播到子组件(单向)。您在子组件中所做的更改需要通过自定义事件显式发送回父组件:https://vuejs.org/v2/guide/components.html#Custom-Events 或 2.3.0+ 中的 sync 关键字:https://vuejs.org/v2/guide/components.html#sync-Modifier

    编辑替代(也许更好)方法:

    Monit.vue:

    <template>
      <div class="module-container">
          <div class="module-container-widgets">
            <vehicles-list :vehicles="vehicles" v-on:vehicleSelected="onVehicleSelected"></vehicles-list>
          </div>
      </div>
    </template>
    <script>
    import VehiclesList from '@/components/modules/monit/VehiclesList.vue';
    
    export default {
      name: "Monit",
      data (){
          return {
              vehicles: null
          }
      },
      components: {
        VehiclesList
      },
    
      methods: {
          onVehicleSelected: function (id) {
              _.forEach((v, id) => {
                  v.selected = false;
              });
              this.vehicles[id].selected = true;
          }
          ...other methods
      },
      mounted: function(){
         this.getMonitData();
      }
    };
    </script>
    

    VehicleList.vue:

    methods:{
        select(vehicle){
            this.$emit('vehicleSelected', vehicle.monit.id)
        }
    },
    

    原帖: 对于您的示例,这可能意味着您需要在 select 方法中发出更改,并且您需要在 VehicleList.vue 中使用某种可变对象:

    export default {
        data: function(){
            return {
                isShort: true,
                mutableVehicles: {}
            }
        }, 
        props:{
            vehicles: {}
        },
        methods:{
            select(vehicle){
                let id = vehicle.monit.id;
                console.log("Select vehicle: " + id);
                _.forEach((v, id) => {
                    v.selected = false;
                });
                this.mutableVehicles[id].selected = true;
                this.$emit('update:vehicles', this.mutableVehicles);
            },
            vehilcesLoaded () {
                // Call this function from the parent once the data was loaded from the api. 
                // This ensures that we don't overwrite the child data with data from the parent when something changes.
                // But still have the up-to-date data from the api
                this.mutableVehilces = this.vehicles
            }
        },
        components:{
            Vehicle
        }
    }
    

    Monit.vue

    <template>
      <div class="module-container">
          <div class="module-container-widgets">
            <vehicles-list :vehicles.sync="vehicles"></vehicles-list>
          </div>
      </div>
    </template>
    <script>
    

    你还是应该更多地考虑责任。 VehicleList.vue 组件不应该负责加载和管理车辆吗?这可能会让思考变得容易一些。

    编辑 2: 尝试$set 内部对象,看看是否有帮助:

    self.$set(self.vehicles, v.id, {
        monit: v,
        no: Object.keys(self.vehicles).length + 1,
        selected: false
    });
    

    【讨论】:

    • 我需要Monit.vue 来管理vehicles,因为我将在其中添加更多组件来操作vehicles,例如地图、表格等
    • 好的。我添加了一个替代解决方案,它需要更少的开销,并且似乎更适合这个问题。
    • 更改selected 的事件正在运行。但是Vehicle.vue 组件没有监听这个属性
    • 从api获取数据时selected属性是否存在?那么它应该是被动的。
    • 即使我使用新数据从Monit.vue 中的 api 更新车辆,也没有任何反应。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-29
    • 2019-08-18
    相关资源
    最近更新 更多