【问题标题】:Vue components not being given unique idVue 组件没有被赋予唯一的 id
【发布时间】:2020-11-02 16:05:09
【问题描述】:

我有一个单页应用程序来显示房子周围的传感器信息。我有一个组件可以呈现使用 highcharts 库的仪表。根据显示的信息,不同房间有不同数量的仪表。我有显示存储在 vuex 状态数组中的仪表(最大值、最小值、格式等)的选项。房间在我的应用程序中都是不同的路线,在导航栏中都有自己的链接。

如果我点击第一个房间(比如办公室),则会呈现三个仪表 - 都很好。但是,如果我现在单击车库(只有一个仪表),那么第一个仪表的选项不会与办公室之前的显示有所不同。就好像组件被保存在内存中并且其设置没有更新。

这是帮助我解释的代码,首先是仪表组件:

<template>
    <div :ref="'gauge-'+_uid" class="border gauge-container"></div>
</template>

<script>
module.exports = {
    props: {
        'gValue': {type: Number, default: 20},
        'gMin': {type: Number, default: 0},
        'gMax': {type: Number, default: 100},
        'gUnits': {type: String, default: ""},
    },
    data: function () {
        return {
            // Data for chart
            chart: null,
            gaugeOptions: {
                chart: {
                    type: 'solidgauge'
                },
                title: null,
                pane: {
                    size: 200,
                    center: ['50%', '75%'],
                    startAngle: -90,
                    endAngle: 90,
                    background: {
                        backgroundColor:
                            Highcharts.defaultOptions.legend.backgroundColor || '#EEE',
                        innerRadius: '60%',
                        outerRadius: '100%',
                        shape: 'arc'
                    }
                },
                tooltip: {
                    enabled: false
                },
                // the value axis
                yAxis: {
                    stops: [
                        [0.1, '#55BF3B'], // green
                        [0.5, '#DDDF0D'], // yellow
                        [0.9, '#DF5353'] // red
                    ],
                    lineWidth: 0,
                    tickWidth: 0,
                    minorTickInterval: null,
                    tickAmount: 0,
                    title: {
                        y: -70
                    },
                    labels: {
                        y: 16
                    },
                    min: this.gMin,
                    max: this.gMax,
                    title: null,
                },
                plotOptions: {
                    solidgauge: {
                        dataLabels: {
                            y: 5,
                            borderWidth: 0,
                            useHTML: true
                        }
                    }
                },
                credits: {
                    enabled: false
                },
                series: [{
                    data: [this.gValue],
                    dataLabels: {
                    format:
                        '<div style="text-align:center">' +
                        '<span style="font-size:14px">{y} ' + this.gUnits + '</span>' +
                        '</div>'
                    },
                }],
            }
        }
    },
    mounted: function () {
        console.log(this._uid) // Look at the id that is generated -- this is not being updated!
        Highcharts.setOptions(
            {
                chart: {
                    style: {
                        fontFamily: 'roboto'
                    }
                }
            }
        )
        this.chart = Highcharts.chart(
            this.$refs['gauge-' + this._uid], 
            this.gaugeOptions
        )
    },
    watch: {
        gValue: function (val) {
            this.chart.series[0].points[0].update(val)
        },
    }
}
</script>

<style scoped>
.gauge-container {
    max-width: 250px;
    height: 180px;
}
</style>

我已经在其中放置了一个 console.log 来检查 id - 我认为这就是问题所在。只有当我从一个仪表的页面变为三个时它才会更新(它添加了另外两个 id)。当我离开那部分路线时,有没有办法破坏仪表组件?

现在是调用仪表组件的父组件:

<template>
    <div v-if="hasRooms" id="room">
        <b-container>
            <h2>{{ roomName }}</h2>
            <b-row align-h="center">
                <b-col sm="3" class="text-center p-0 ml-1 mr-1"  v-for="device in deviceList" v-bind:key="deviceList.name">
                    <p>{{ device.name }} - {{ device.function }}</p>
                    <highchart v-if="device.format == 'default'" :g-value="device.value">
                        <template v-slot:title>
                            {{ device.function | capitalize }}
                        </template>
                    </highchart>
                    <highchart v-else
                        v-bind:g-value="device.value"
                        v-bind:g-min="device.format.min"
                        v-bind:g-max="device.format.max"
                        v-bind:g-units="device.format.units"
                    >
                        <template v-slot:title>
                            {{ device.function | capitalize }}
                        </template>
                    </highchart>
                </b-col>
            </b-row>
        </b-container>
    </div>
</template>

<script>
module.exports = {
    name: 'room',

    /** Load external component files
     *  Make sure there is no leading / in the name
     *  To load from the common folder use like: 'common/component-name.vue' */
    components: {
        'highchart': httpVueLoader('components/HighCharts.vue'),
    }, // --- End of components --- //
    
    data() {
        return {

        };
    },
    computed: {
        hasRooms() {
            return this.$store.getters['rooms/nRooms'] > 0;
        },
        roomName() {
            return this.$store.getters['rooms/getRoomById'](this.$route.params.roomId);
        },
        deviceList() {
            return this.$store.getters['rooms/getDevicesinRoom'](this.$route.params.roomId);
        },
    },
    filters: {
        capitalize: function (value) {
            if (!value) return ''
            value = value.toString()
            return value.charAt(0).toUpperCase() + value.slice(1)
        },
        tonumber: function (value){
            if (!value) return ''
            return parseFloat(value)
        }
    },
}
</script>

房间由路由器生成并从以下位置渲染:

<template>
    <div id="rooms">
        <b-alert variant="info" :show="!hasRooms">
            <p>
                There are no rooms available yet. Pass a message that defines a room id and device id 
                to the uibuilder node first. See <router-link :to="{name: 'usage_info'}">the setup information</router-link> 
                for instructions on how start using the interface.
            </p>
        </b-alert>
        <router-view></router-view>
    </div>
</template>

<script>
module.exports = {
    name: 'RoomsOverview',
    data() {
        return {

        };
    },
    computed: {
        hasRooms() {
            return this.$store.getters['rooms/nRooms'] > 0;
        },
        roomList() {
            return this.$store.getters['rooms/getAllRooms'];
        },
    },
}
</script>

【问题讨论】:

  • 你不应该使用_uidgithub.com/vuejs/vue/issues/5886
  • 使用动态引用的原因是什么?
  • 嗨,谢谢。由于页面上有多个组件,我认为我需要一个唯一的 id 来渲染仪表(我使用的是静态 id,只会出现一个仪表)。有更好的方法吗?我需要一个 id 来在模板中生成图表。
  • more than one component on a page 无论如何,静态引用应该没问题,只要确保该组件中只有一个(实际上多个也可以,你会得到一个数组)。

标签: vue.js vue-component


【解决方案1】:

好的,经过一番搜索,我使用了此页面上的最后一种方法(https://michaelnthiessen.com/force-re-render/)。每当路由更改时,它都会调用我父组件中的 forceRerender() 方法。以下是更新的组件:

Room.vue:

<template>
    <div v-if="hasRooms" id="room">
        <b-container>
            <h2>{{ roomName }}</h2>
            <b-row align-h="center">
                <b-col sm="3" class="text-center p-0 m-1"  v-for="device in deviceList" v-bind:key="deviceList.name">
                    <highchart v-if="device.format == 'default'" :g-value="device.value" :key="componentKey">
                        <template v-slot:title>
                            {{ device.function | capitalize }}
                        </template>
                    </highchart>
                    <highchart v-else
                        :g-value="device.value"
                        :g-min="device.format.min"
                        :g-max="device.format.max"
                        :g-units="device.format.units"
                        :key="componentKey"
                    >
                        <template v-slot:title>
                            {{ device.function | capitalize }}
                        </template>
                    </highchart>
                </b-col>
            </b-row>
        </b-container>
    </div>
</template>

<script>
module.exports = {
    name: 'room',

    /** Load external component files
     *  Make sure there is no leading / in the name
     *  To load from the common folder use like: 'common/component-name.vue' */
    components: {
        'highchart': httpVueLoader('components/HighCharts.vue'),
    }, // --- End of components --- //
    
    data() {
        return {
            componentKey: 0,
        };
    },
    methods: {
        forceRerender() {
            this.componentKey += 1
        },
    },
    computed: {
        hasRooms() {
            return this.$store.getters['rooms/nRooms'] > 0;
        },
        roomName() {
            return this.$store.getters['rooms/getRoomById'](this.$route.params.roomId);
        },
        deviceList() {
            return this.$store.getters['rooms/getDevicesinRoom'](this.$route.params.roomId);
        },
    },
    watch: {
        '$route.params.roomId' () {
            this.forceRerender()
        }
    },
    filters: {
        capitalize: function (value) {
            if (!value) return ''
            value = value.toString()
            return value.charAt(0).toUpperCase() + value.slice(1)
        },
        tonumber: function (value){
            if (!value) return ''
            return parseFloat(value)
        }
    },
}
</script>

和 Gauge.vue

<template>
    <b-card no-body class="bg-secondary text-light">
        <b-card-body class="bg-secondary m-0 pt-0 pb-0">
            <h5 class="pt-2"><slot name="title">Title</slot></h5>
            <div ref="gauge" class="gauge-container"></div>
        </b-card-body>
    </b-card>
</template>

<script>
module.exports = {
    props: {
        'gValue': {type: Number, default: 20},
        'gMin': {type: Number, default: 0},
        'gMax': {type: Number, default: 100},
        'gUnits': {type: String, default: ""},
    },
    data: function () {
        return {
            // Data for chart
            chart: null,
            gaugeOptions: {
                chart: {
                    type: 'solidgauge'
                },
                title: null,
                pane: {
                    size: 200,
                    center: ['50%', '75%'],
                    startAngle: -90,
                    endAngle: 90,
                    background: {
                        innerRadius: '60%',
                        outerRadius: '100%',
                        shape: 'arc'
                    }
                },
                tooltip: {
                    enabled: false
                },
                // the value axis
                yAxis: {
                    stops: [
                        [0.1, '#55BF3B'], // green
                        [0.5, '#DDDF0D'], // yellow
                        [0.9, '#DF5353'] // red
                    ],
                    lineWidth: 0,
                    tickWidth: 0,
                    minorTickInterval: null,
                    tickAmount: 0,
                    title: {
                        y: -70
                    },
                    labels: {
                        y: 16
                    },
                    min: this.gMin,
                    max: this.gMax,
                    title: null,
                },
                plotOptions: {
                    solidgauge: {
                        dataLabels: {
                            y: 5,
                            borderWidth: 0,
                            useHTML: true
                        }
                    }
                },
                credits: {
                    enabled: false
                },
                series: [{
                    data: [this.gValue],
                    dataLabels: {
                    format:
                        '<div style="text-align:center">' +
                        '<span style="color:#FFF; font-size:16px">{y} ' + this.gUnits + '</span>' +
                        '</div>'
                    },
                }],
            }
        }
    },
    mounted: function () {
        Highcharts.setOptions(
            {
                chart: {
                    style: {
                        fontFamily: 'roboto',
                    },
                    backgroundColor:'#6C757D',
                }
            }
        )
        this.chart = Highcharts.chart(
            this.$refs['gauge'], 
            this.gaugeOptions
        )
    },
    watch: {
        gValue: function (val) {
            this.chart.series[0].points[0].update(val)
        },
    }
}
</script>

<style scoped>
.gauge-container {
    max-width: 250px;
    height: 180px;
}
</style>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-29
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 2020-01-16
    • 2020-08-10
    相关资源
    最近更新 更多