VueJS 不会从组件中重新渲染列表
Posted
技术标签:
【中文标题】VueJS 不会从组件中重新渲染列表【英文标题】:VueJS does not rerender list from components 【发布时间】:2019-12-08 02:24:05 【问题描述】:尝试通过“v-for”从 vuex 渲染一个数组。
“玩家卡”组件未呈现。 但是“td”解决方案可以正常工作。
我在JSFiddle上的例子
HTML:
<div id="app">
<button v-on:click="moveItem">
Move Item
</button>
<table cellspacing="2" border="1" cellpadding="5">
<tr>
<td v-for="(item, item_idx) in getItems" v-bind:key="item.col"> (item.card)? item.card.name : 'none' </td>
</tr>
<tr>
<player-card v-for="(item, item_idx) in getItems" v-bind:key="item.col" v-bind:item="item"></player-card>
</tr>
</table>
<br/>
<p>msg</p>
</div>
商店:
const store = new Vuex.Store(
state:
items: [ col: 0, row: 0 ,
col: 1, row: 0 ,
col: 2, row: 0, card: name: "hello" ]
,
getters:
getterItems: state => return state.items;
,
mutations:
MOVE_ITEM: state =>
state.items[0].card = state.items[2].card;
delete state.items[2].card;
state.message = JSON.stringify(state.items);
);
组件:
Vue.component('player-card',
props:
item:
type: Object,
required: true
,
template: '<td> (item.card)? item.card.name : "none" </td>'
);
应用:
new Vue(
el: '#app',
store,
data: function()
return
msg: ''
,
computed:
getItems() return this.$store.getters.getterItems;
,
mounted: function()
this.msg = JSON.stringify(this.getItems);
,
methods:
moveItem()
this.$store.commit('MOVE_ITEM');
this.msg = JSON.stringify(this.getItems);
);
我已经尝试了很多解决方案,但没有找到一个简单的解决方案。也许有人会提供不同的架构解决方案。
【问题讨论】:
【参考方案1】:你只需要改变这一行:
<player-card v-for="(item, item_idx) in getItems" v-bind:key="item.col" v-bind:item="item"></player-card>
到这里:
<td is="player-card" v-for="(item, item_idx) in getItems" v-bind:key="item.col" v-bind:item="item"></td>
这是必需的,因为您的模板是在 DOM 中指定的。浏览器会在 Vue 靠近它之前解析模板标记。 html 解析规则只允许某些元素成为<tr>
的直接子元素。任何其他元素都将被拉出<table>
。当 Vue 解析模板时,<player-card>
元素已经被移到了<table>
之外。
如果您使用其他技术之一来指定模板,这将不是问题。
解决方法是使用is
属性来指定组件而不是标签名称。
这里的文档对此进行了解释:
https://vuejs.org/v2/guide/components.html#DOM-Template-Parsing-Caveats
【讨论】:
【参考方案2】:您需要使用特殊的is
属性来绕过强加的元素结构,其中<tr>
期望<td>
作为子元素,但得到<player-card>
(预转换)。此外,您更改 Array 的方式会导致反应性问题。考虑一下我的改变。
const store = new Vuex.Store(
state:
items: [
col: 0,
row: 0
,
col: 1,
row: 0
,
col: 2,
row: 0,
card:
name: "hello"
]
,
getters:
getterItems: state =>
return state.items;
,
mutations:
MOVE_ITEM: state =>
// Move the last element to the front
state.items = [
...state.items.slice(-1),
...state.items.slice(0, -1)
];
state.message = JSON.stringify(state.items);
);
Vue.component('player-card',
props:
item:
type: Object,
required: true
,
template: '<td> (item.card)? item.card.name : "none" </td>'
);
new Vue(
el: '#app',
store,
data: function()
return
msg: ''
,
computed:
getItems()
return this.$store.getters.getterItems;
,
mounted: function()
this.msg = JSON.stringify(this.getItems);
,
methods:
moveItem()
this.$store.commit('MOVE_ITEM');
this.msg = JSON.stringify(this.getItems);
);
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<button v-on:click="moveItem">
Move Item
</button>
<table cellspacing="2" border="1" cellpadding="5">
<tr>
<td v-for="(item, item_idx) in getItems" v-bind:key="item.col"> (item.card)? item.card.name : 'none' </td>
</tr>
<tr>
<td is="player-card" v-for="(item, item_idx) in getItems" v-bind:key="item.col" v-bind:item="item"></td>
</tr>
</table>
<br/>
<p>msg</p>
</div>
【讨论】:
【参考方案3】:我的问题也是更新 vuex 中的数组的问题。
关于此"Common Beginner Gotchas"的信息
mutations:
MOVE_ITEM: state =>
// Move card
state.items[0].card = state.items[2].card;
delete state.items[2].card;
// Simple deep array cloning
state.items = JSON.parse(JSON.stringify(state.items));
state.message = JSON.stringify(state.items);
更新JSFiddle
非常感谢大家。
【讨论】:
以上是关于VueJS 不会从组件中重新渲染列表的主要内容,如果未能解决你的问题,请参考以下文章
在 Vuejs 中使用 beforemount 进行数据渲染,每次打开该组件/页面时都会不断增加渲染的数据列表