TLDR: Прежде чем Vue передается шаблон DOM, браузер выводит <measurement v-bind:name="i" v-bind:data="m">
за пределы <table>
(вне контекста v-for
), что приводит к ошибкам в Vue. Это известное предостережение при разборе шаблона DOM .
HTML spe c требует, чтобы <table>
содержал только определенные c дочерние элементы :
<caption>
<colgroup>
<thead>
<tbody>
<tr>
<tfoot>
<script>
или <template>
, смешанные с указанным выше
Аналогично, модель содержимого <tr>
это:
<td>
<th>
<script>
или <template>
, смешанные с выше
DOM Парсер совместимых браузеров автоматически выводит запрещенные элементы, такие как <measurement>
, за пределы таблицы. Это происходит за до стадии сценария (до того, как Vue даже увидит ее).
Например, эта разметка:
<table>
<tr v-for="(m,i) in obj">
<measurement v-bind:name="i" v-bind:data="m"></measurement>
</tr>
</table>
... становится такой после анализа DOM (перед любым сценарием):
<measurement v-bind:name="i" v-bind:data="m"></measurement> <!-- hoisted outside v-for -->
<table>
<tr v-for="(m,i) in obj">
</tr>
</table>
Обратите внимание, что i
и m
находятся вне контекста v-for
l oop, что приводит к Vue ошибкам времени выполнения о i
и m
не определено (если случайно ваш компонент случайно не объявил их уже). m
должен был быть связан с <measurement>
data
prop, но поскольку это не удалось, data
- это просто его начальное значение (также undefined
), что приводит к сбою рендеринга {{data.value}}
с Error in render: "TypeError: Cannot read property 'value' of undefined"
.
Чтобы продемонстрировать подъем без этих ошибок времени выполнения и без Vue, запустите приведенный ниже фрагмент кода:
<table style="border: solid green">
<tr>
<div>1. hoisted outside</div>
<td>3. inside table</td>
2. also hoisted outside
</tr>
</table>
... затем проверьте результат в DevTools вашего браузера, который должен выглядеть следующим образом:
<div>1. hoisted outside</div>
2. also hoisted outside
<table style="border: solid green">
<tr>
<td>3. inside table</td>
</tr>
</table>
Решение 1: Используйте <tr is="measurement">
Если вы предпочитаете шаблоны DOM, вы можете использовать атрибут is
в <tr>
, чтобы указать measurement
в качестве типа (как предложено в документах Vue и другим ответом ). Для этого сначала необходимо, чтобы шаблон <measurement>
использовал <td>
или <th>
в качестве элемента контейнера внутри <tr>
, чтобы быть действительным HTML:
<template id="measurement">
<tr>
<td>{{name}} -> {{data.value}}</td>
</tr>
</template>
<div id="app">
<table v-for="(m,i) in sortedMeters">
<tr is="measurement" v-bind:name="i" v-bind:data="m" v-bind:key="i"></tr>
</table>
</div>
Vue.component('measurement', {
template: '#measurement',
props: {
name: String,
data: Object
}
})
new Vue({
el: '#app',
data: {
sortedMeters: {
apple: {value: 100},
banana: {value: 200}
},
}
})
<script src="https://unpkg.com/vue@2.6.11"></script>
<template id="measurement">
<tr>
<td>{{name}} -> {{data.value}}</td>
</tr>
</template>
<div id="app">
<table v-for="(m,i) in sortedMeters">
<tr is="measurement" v-bind:name="i" v-bind:data="m" v-bind:key="i"></tr>
</table>
</div>
Решение 2: Обтекание <table>
в компоненте
Если вы предпочитаете шаблоны DOM, вы можете использовать компонент обертки для <table>
, который может содержать <measurement>
без предупреждения о подъеме.
Vue.component('my-table', {
template: `<table><slot/></table>`
})
<div id="app">
<my-table v-for="(m, i) in sortedMeters">
<measurement v-bind:name="i" v-bind:data="m"></measurement>
</my-table>
</div>
Vue.component('measurement', {
template: '#measurement',
props: {
name: String,
data: Object
}
})
Vue.component('my-table', {
template: `<table><slot/></table>`
})
new Vue({
el: '#app',
data: {
sortedMeters: {
apple: {value: 100},
banana: {value: 200}
},
}
})
<script src="https://unpkg.com/vue@2.6.11"></script>
<template id="measurement">
<tr>
<td>{{name}} -> {{data.value}}</td>
</tr>
</template>
<div id="app">
<my-table v-for="(m, i) in sortedMeters">
<measurement v-bind:name="i" v-bind:data="m"></measurement>
</my-table>
</div>
Решение 3: Переместить <table>
разметку в строку шаблона
Вы можете переместить весь <table>
в строку шаблона компонента, где можно избежать предостережений шаблона DOM. Точно так же вы можете переместить <table>
в отдельный файловый компонент , но я предполагаю, что вам вместо этого нужны шаблоны DOM.
Vue.component('my-table', {
template: `<div>
<table v-for="(m, idx) in sortedMeters">
<measurement v-bind:data="m"></measurement>
</table>
</div>`,
props: {
sortedMeters: Object
}
})
<div id="app">
<my-table v-bind:sorted-meters="sortedMeters"></my-table>
</div>
Vue.component('measurement', {
template: '#measurement',
props: {
name: String,
data: Object
}
})
Vue.component('my-table', {
template: `<div>
<table v-for="(m,i) in sortedMeters">
<measurement v-bind:name="i" v-bind:data="m" v-bind:key="i"></measurement>
</table>
</div>`,
props: {
sortedMeters: Object
}
})
new Vue({
el: '#app',
data: {
sortedMeters: {
apple: {value: 100},
banana: {value: 200}
},
}
})
<script src="https://unpkg.com/vue@2.6.11"></script>
<template id="measurement">
<tr>
<td>{{name}} -> {{data.value}}</td>
</tr>
</template>
<div id="app">
<my-table :sorted-meters="sortedMeters"></my-table>
</div>