Vue实现编辑更新功能遇控制台警告,求解决方案
在实现Vue.js数据编辑/更新功能时,已成功获取所有数据,但控制台出现以下两个警告:
[Vue warn]: Extraneous non-emits event listeners (editedStation) were passed to component but could not be automatically inherited because component renders fragment or text root nodes. If the listener is intended to be a component custom event listener only, declare it using the "emits" option.
at <StationItem key=121 id=121 line="test" ... >
at <StationList onVnodeUnmounted=fn
[Vue warn]: Extraneous non-props attributes (id) were passed to component but could not be automatically inherited because component renders fragment
in console after sending the data from the row into form.
子组件(StationItem.vue)代码
<template> <tr> <td style="padding: 10px">{{ line }}</td> <td style="padding: 10px">{{ physLine }}</td> <td style="padding: 10px">{{ wsType }}</td> <td style="padding: 10px">{{ ws }}</td> <td style="padding: 10px">{{ name }}</td> <td style="padding: 10px">{{ isValid }}</td> <td style="padding: 10px">{{ stationGroupId }}</td> <td style="padding: 10px">{{ order }}</td> <td style="padding: 10px" class="actions"> <button @click="editStation($event)" v-bind:id="stationId"> {{ editIsClicked ? "Done" : "Edit" }} </button> </td> </tr> <tr v-if="editIsClicked" style="background-color: #ffda99"> <td colspan="3" style="padding: 30px 0px 30px 30px"> <div class="form-control" style="margin-bottom: 10px; width: 80%"> <label for="line" style="display: block; text-align: left; font-size: 14px" >Line Number</label > <input type="text" :value="station.lineNum" style="width: 90%; display: block; text-align: left" /> </div> <div class="form-control" style="margin-bottom: 10px; width: 80%"> <label for="physLine" style="width: 90%; display: block; text-align: left; font-size: 14px" > Phys line </label> <input type="text" :value="station.physLine" style="width: 90%; display: block; text-align: left" /> </div> <div class="form-control" style="margin-bottom: 10px; width: 80%"> <label for="wsType" style="width: 90%; display: block; text-align: left; font-size: 14px" >WS Type</label > <input type="text" :value="station.wsType" style="width: 90%; display: block; text-align: left" /> </div> <div class="form-control" style="width: 50%; margin-bottom: 10px; border: 1px solid #b5b5b5" > <label for="isValid" style="width: 40%; display: block; text-align: left; font-size: 14px" >is Valid</label > <input type="checkbox" :value="station.isValid" style="margin-left: -350px" /> </div> <p v-if="!isValid.isValid">{{ errorMessage }}</p> <tr v-for="(btn, index) in filteredButtonList" v-bind:key="btn.id"> <td>{{ btn.buttonListName }}</td> <td>{{ btn.buttonListDesc }}</td> <td> <select @change="onQuidoIdChange($event)" class="form-select form-control required" v-bind:id="index + 1" > <option class="quido-id" v-for="entry in selectedQuidoId" :value="entry.quidoId" selected :key="entry.quidoId" disabled hidden > {{ entry.quidoName }} </option> <option v-for="quido in filteredQuidosList" :value="quido.quidoId" v-bind:key="quido.quidoId" v-bind:id="quido.quidoId" > {{ quido.quidoName }} </option> </select> </td> <td> <tr> <td> <input @change="onInputChange($event)" type="number" class="input-custom" :placeholder="`Input ${index + 1}`" v-bind:id="index + 1" :value="station.stationButtons[index].input" /> </td> </tr> </td> </tr> </td> <td colspan="6" style="padding-bottom: 250px:"> <div class="form-control" style="margin-bottom: 10px"> <label style="display: block; text-align: left; font-size: 14px" for="wsName" >WS Name</label > <input type="text" :value="station.wsName" style="width: 80%; display: block; text-align: left" /> </div> <div class="form-control" style="margin-bottom: 10px"> <label style="display: block; text-align: left; font-size: 14px" for="wsName" >Position Name</label > <input type="text" :value="station.posName" style="width: 80%; display: block; text-align: left" /> </div> <div class="form-control" style="margin-bottom: 10px"> <label style="width: 80%; display: block; text-align: left; font-size: 14px" for="wsName" >Order</label > <input type="text" :value="station.order" style="width: 80%; display: block; text-align: left" /> </div> <div class="form-control" style="width: 82%"> <label for="stationGroupId">Choose Station Group</label> <select @change="changeStationGroupId($event)" style="width: 100%" v-bind:id="index + 1" class="required" > <option class="stgr-id" v-for="entry in selectedStationGroupId" :value="entry.stationGroupId" selected :key="entry.stationGroupId" disabled hidden > {{ entry.stationGroupsName }} </option> <option v-for="stGrId in filteredStationGroups" :value="stGrId.stationGroupsId" v-bind:key="stGrId.stationGroupsId" > {{ stGrId.stationGroupsName }} </option> </select> </div> </td> </tr> </template>
父组件代码
<template> <div class="section-global"> <station-form @save-data="saveData"> </station-form> </div> <div class="section-global"> <table v-if="hasStations" class="render-list-table"> <thead> <!-- <th><a href="">Line ID</a></th> --> <th>Line num</th> <th>Phys Line</th> <th>WS Type</th> <th>WS Name</th> <th>Pos. name</th> <th>isValid</th> <th>Station Group ID</th> <th>Order</th> <th>Actions</th> </thead> <tbody> <station-item v-for="station in filteredStations" :key="station.stationId" :id="station.stationId" :station-id="station.stationId" :line="station.lineNum" :phys-line="station.physLine" :ws-type="station.wsType" :ws="station.wsName" :name="station.posName" :order="station.order" :is-valid="station.isValid" :stationGroupId="station.stationGroupId" @editedStation="editFormData($event)" > </station-item> </tbody> </table> <h1 v-else>NO STATIONS FOUND</h1> </div> </template> <script> import StationItem from "@/components/stations/StationItem.vue"; import StationForm from "@/components/stations/StationForm.vue"; export default { data() { return { editedDataToBeSendToFormInputs: [], }; }, components: { StationItem, StationForm, }, computed: { filteredStations() { return this.$store.getters["stations/stations"]; }, hasStations() { return this.$store.getters["stations/hasStations"]; }, probaFiltered() { return this.$store.getters["stations/probaGetter"]; }, }, created() { this.loadStations(); this.loadProba(); }, methods: { editFormData(event) { const dataToBeEdited = event; // this.editedDataToBeSendToFormInputs.push(dataToBeEdited); const test = this.probaFiltered; test.push(dataToBeEdited); this.loadProba(); }, saveData(data) { this.$store.dispatch("stations/addStation", data); }, loadStations() { this.$store.dispatch("stations/loadStationList"); } }, }; </script>
解决方案
1. 解决自定义事件监听警告
第一个警告是因为子组件未声明接收的自定义事件,Vue无法确认editedStation是合法的自定义事件。需要在子组件的script部分添加emits选项:
<script> export default { // 声明组件触发的自定义事件 emits: ['editedStation'], props: ['stationId', 'line', 'physLine', 'wsType', 'ws', 'name', 'order', 'isValid', 'stationGroupId'], // ... 其他代码(data、methods等) } </script>
2. 解决非Prop属性警告
第二个警告是因为父组件传递了:id="station.stationId",但子组件未将id定义为Prop,同时子组件是片段根节点(两个<tr>),Vue无法自动继承该属性。
有两种处理方式:
方式一:移除多余的id传递
父组件中已经传递了:station-id="station.stationId",可以直接删除:id="station.stationId",避免传递不必要的属性。方式二:禁用属性继承并手动绑定
如果确实需要使用id属性,在子组件中设置inheritAttrs: false,然后手动将id绑定到需要的元素上:<script> export default { inheritAttrs: false, props: ['stationId', 'line', 'physLine', 'wsType', 'ws', 'name', 'order', 'isValid', 'stationGroupId'], // ... 其他代码 } </script>然后在模板中,比如绑定到第一个
<tr>:<tr v-bind="$attrs"> <!-- ... 其他内容 --> </tr>
这两种方式都能解决警告,同时不会破坏表格的渲染结构(不需要用div包裹tr)。
内容的提问来源于stack exchange,提问作者martin-den

