You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue实现编辑更新功能遇控制台警告,求解决方案

解决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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 12:30:44