javascript - 如何在移除列表項目後刷新 Vue 組件?
我知道如何從 Vue 實例中移除列表項目。然而,當列表項目被傳遞到 Vue 組件時,如何在移除列表項目的同時保持組件與列表數據同步?
這是一個使用案例。考慮一個具有 Markdown 編輯器的線上論壇。我們有一個 Vue 實例,其數據是一個從伺服器獲取的已保存評論列表。這些評論應該是用 Markdown 撰寫的。
為了方便編輯和預覽,我們還有一個組件列表。每個組件都包含可編輯的輸入緩衝區以及預覽區域。Vue 實例中保存的評論內容用於初始化輸入緩衝區,並在用戶取消編輯時重置它。預覽是輸入緩衝區內容的轉換結果。
以下是一個測試實作:
<template id="comment">
<div>
Component:
<textarea v-model="input_buffer" v-if="editing"></textarea>
{{ preview }}
<button type="button" v-on:click="edit" v-if="!editing">edit</button>
<button type="button" v-on:click="remove" v-if="!editing">remove</button>
<button type="button" v-on:click="cancel" v-if="editing">cancel</button>
</div>
</template>
<div id="app">
<ol>
<li v-for="(comment, index) in comments">
<div>Instance: {{comment}}</div>
<comment
v-bind:comment="comment"
v-bind:index="index"
v-on:remove="remove">
</comment>
</li>
</ol>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.js"></script>
<script>
let comments = ['111', '222', '333']
Vue.component('comment', {
template: '#comment',
props: ['comment', 'index'],
data: function() {
return {
input_buffer: '',
editing: false,
}
},
mounted: function() { this.cancel() },
computed: {
preview: function() {
// This is supposed to be a transformation of the input buffer,
// but for now, let's simply output the input buffer
return this.input_buffer
},
},
methods: {
edit: function() { this.editing = true },
remove: function() { this.$emit('remove', this.index) },
cancel: function() { this.input_buffer = this.comment; this.editing = false },
//save: function() {}, // submit to server; not implemented yet
},
})
let app = new Vue({
el: '#app',
data: { comments: comments },
methods: {
remove: function(index) { this.comments.splice(index, 1); app.$forceUpdate() },
},
})
</script>
問題是,如果我們刪除一條評論,組件不會相應地刷新。例如,在上述實現中,我們有 3 條評論。如果你刪除評論 2,項目 3 的預覽仍然會顯示項目 2 的內容。只有在我們按下 edit 然後 cancel 後,它才會更新。
我嘗試過 app.$forceUpdate(),但那沒有幫助。
答案#1
你只需要在 v-for 迴圈中添加 key 屬性,如下所示:
<li v-for="(comment, index) in comments" :key="comment">
查看可運作的 fiddle:https://fiddle.jshell.net/mimani/zLrLvqke/
Vue 嘗試優化渲染,通過提供 key 屬性,它將這些視為完全不同的元素並正確重新渲染它們。
請參閱 key 文件以獲取更多信息。
答案#2
嘗試以下:
Vue.component('comment', {
template:
`<div>
{{ comment }}
<button v-on:click="remove"> X </button>
</div>`,
props: ['comment', 'index'],
methods: {
remove: function() {
this.$emit('remove', this.index);
}
},
});
vm = new Vue({
el: '#app',
data: {
comments: ['a','b','c','d','e']
},
methods: {
remove: function(index) {
this.comments.splice(index, 1);
},
},
});<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.1.8/vue.min.js"></script>
<div id="app">
<ol>
<li v-for="(comment, index) in comments">
<comment
v-bind:comment="comment"
v-bind:index="index"
v-on:remove="remove">
</comment>
</li>
</ol>
</div>