【问题标题】:VueJS DOM not updating in componentVueJS DOM 未在组件中更新
【发布时间】:2019-03-01 10:14:32
【问题描述】:

我浏览了其他“DOM 未更新”的帖子,但未能找到解决方案。我正在尝试为初学者创建一个任务应用程序,并且该应用程序已经可以从 Firestore 加载,添加一个新的并删除一个。我有两个问题,但我想从第一个开始。当我添加、删除数据时,DOM 不会动态更新,尽管 Vuex 存储中的相应数组已正确更新。当我重新加载页面时,它也在更新。

我已经添加了组件和商店的代码,我希望我在正确的地方寻找,但我感觉我不是。

提前致谢

店铺代码:

import Vue from 'vue'
import Vuex from 'vuex'
// import firebase from 'firebase'
import router from '@/router'
import db from '@/db'
import firebase from '@/firebase'

Vue.use(Vuex)

export const store = new Vuex.Store({
  state: {
    appTitle: 'My Awesome App',
    addedTask: false,
    loadedTasks: false,
    deletedTasks: false,
    user: null,
    error: null,
    loading: false,
    tasks: [],
    task: {
      title: '',
      description: '',
      tags: [],
      dueTime: '',
      dueDate: '',
      reminderFlag: Boolean,
      quadrant: '',
      userId: '',
      timestamp: ''
    }
  },
  // Mutations (synchronous): change data in the store
  mutations: {
    setUser (state, payload) {
      state.user = payload
      state.task.userId = firebase.auth().currentUser.uid
    },
    setError (state, payload) {
      state.error = payload
    },
    setLoading (state, payload) {
      state.loading = payload
    },
    addedTask (state, payload) {
      state.addedTask = payload
    },
    loadedTasks (state, payload) {
      state.loadedTasks = true
    },
    deletedTask (state, payload) {
      state.deletedTask = payload
    }
  },
  // Actions (asynchronous/synchronous): change data in the store
  actions: {
    autoSignIn ({ commit }, payload) {
      commit('setUser', { email: payload.email })
    },
    userSignUp ({ commit }, payload) {
      commit('setLoading', true)
      firebase.auth().createUserWithEmailAndPassword(payload.email, payload.password)
        .then(firebaseUser => {
          commit('setUser', { email: firebaseUser.user.email })
          commit('setLoading', false)
          router.push('/home')
          commit('setError', null)
        })
        .catch(error => {
          commit('setError', error.message)
          commit('setLoading', false)
        })
    },
    userSignIn ({ commit }, payload) {
      commit('setLoading', true)
      firebase.auth().signInWithEmailAndPassword(payload.email, payload.password)
        .then(firebaseUser => {
          commit('setUser', { email: firebaseUser.user.email })
          commit('setLoading', false)
          commit('setError', null)
          router.push('/home')
        })
        .catch(error => {
          commit('setError', error.message)
          commit('setLoading', false)
        })
    },
    userSignOut ({ commit }) {
      firebase.auth().signOut()
      commit('setUser', null)
      router.push('/')
    },
    addNewTask ({ commit }, payload) {
      commit('addedTask', false)
      db.collection('tasks').add({
        title: payload.title,
        userId: firebase.auth().currentUser.uid,
        createdOn: firebase.firestore.FieldValue.serverTimestamp(),
        description: payload.description,
        dueDateAndTime: new Date(payload.dueDate & ' ' & payload.dueTime),
        reminderFlag: payload.reminderFlag,
        quadrant: payload.quadrant,
        tags: payload.tags
      })
        .then(() => {
          commit('addedTask', true)
          this.state.tasks.push(payload)
          commit('loadedTasks', true)
        })
        .catch(error => {
          commit('setError', error.message)
        })
    },
    getTasks ({ commit }) {
      commit('loadedTasks', false)
      db.collection('tasks').orderBy('createdOn', 'desc').get().then(querySnapshot => {
        querySnapshot.forEach(doc => {
          const data = {
            'taskId': doc.id,
            'title': doc.data().title,
            'quadrant': doc.data().quadrant
          }
          this.state.tasks.push(data)
        })
        commit('loadedTasks', true)
      })
    },
    deleteTask ({ commit }, payload) {
      db.collection('tasks').doc(payload).delete().then(() => {
        this.state.tasks.pop(payload)
        commit('deletedTask', true)
        commit('loadedTasks', true)
      }).catch(function (error) {
        console.error('Error removing document: ', error)
        commit('deletedTask', false)
      })
    }
  },
  // Getters: receive data from the store
  getters: {
    isAuthenticated (state) {
      return state.user !== null && state.user !== undefined
    }
  }
})

这是组件(Vue app -> Home -> Grid -> Tasklist (this):

<template>

  <v-card>

    <v-card-title primary class="title">{{title}}</v-card-title>

    <v-flex v-if="tasks.length > 0">
      <v-slide-y-transition class="py-0" group tag="v-list">
        <template v-for="(task, i) in tasks" v-if="task.quadrant === Id">
          <v-divider v-if="i !== 0" :key="`${i}-divider`"></v-divider>

          <v-list-tile :key="`${i}-${task.text}`">
            <v-list-tile-action>
              <v-checkbox v-model="task.done" color="info darken-3">
                <div slot="label" :class="task.done && 'grey--text' || 'text--primary'" class="text-xs-right" v-text="task.title +' ('+ task.taskId +')'"></div>
              </v-checkbox>
            </v-list-tile-action>
            <v-spacer></v-spacer>
            <v-scroll-x-transition>
              <v-btn flat icon color="primary" v-show="!deleteConfirmation" @click="deleteConfirmation = !deleteConfirmation">
                <v-icon>delete</v-icon>
              </v-btn>
            </v-scroll-x-transition>

            <v-scroll-x-transition>
              <v-btn flat icon color="green" v-show="deleteConfirmation" @click="deleteConfirmation = !deleteConfirmation">
                <v-icon>cancel</v-icon>
              </v-btn>
            </v-scroll-x-transition>

            <v-scroll-x-transition>
              <v-btn flat icon color="red" v-show="deleteConfirmation" @click="deleteConfirmation = !deleteConfirmation; deleteTask(task.taskId)">
                <v-icon>check_circle</v-icon>
              </v-btn>
            </v-scroll-x-transition>

          </v-list-tile>
        </template>
      </v-slide-y-transition>
    </v-flex>
  </v-card>
</template>

<script>

export default {
  data () {
    return {
      deleteConfirmation: false
    }
  },
  props: ['Id', 'title'],
  computed: {
    tasks () {
      return this.$store.state.tasks
    }
  },
  methods: {
    deleteTask (taskId) {
      console.log(taskId)
      this.$store.dispatch('deleteTask', taskId)
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
    <style scoped>
.container {
  max-width: 1200px;
}
</style>

【问题讨论】:

    标签: javascript vue.js vuex vuetify.js


    【解决方案1】:

    DOM 现在可以正确更新删除。我稍微改变了组件的结构。现在是:Vue app -> Home -> Grid -> Tasklist -> TaskListItem。我仍然面临以下问题:添加项目会更新 DOM,但是当我添加多个条目时,例如使用不同的标题,它们被正确写入 Firestore,但在 DOM 中,它们显示为相同,直到我完全重新加载。

    请在下面找到代码:

    商店:

    import Vue from 'vue'
    import Vuex from 'vuex'
    // import firebase from 'firebase'
    import router from '@/router'
    import db from '@/db'
    import firebase from '@/firebase'
    
    Vue.use(Vuex)
    
    export const store = new Vuex.Store({
      state: {
        appTitle: 'My Awesome App',
        taskFormVisible: false,
        addedTask: false,
        loadedTasks: false,
        deletedTasks: false,
        user: null,
        error: null,
        loading: false,
        tasks: [],
        task: {
          title: '',
          description: '',
          tags: [],
          dueTime: '',
          dueDate: '',
          reminderFlag: Boolean,
          quadrant: '',
          userId: '',
          timestamp: ''
        }
      },
      // Mutations (synchronous): change data in the store
      mutations: {
        setUser (state, payload) {
          state.user = payload
          state.task.userId = firebase.auth().currentUser.uid
        },
        setError (state, payload) {
          state.error = payload
        },
        setLoading (state, payload) {
          state.loading = payload
        },
        addedTask (state, payload) {
          state.addedTask = payload
        },
        loadedTasks (state, payload) {
          state.loadedTasks = true
        },
        deletedTask (state, payload) {
          state.deletedTask = payload
        },
        DELETE_TASK (state, payload) {
          state.tasks = state.tasks.filter(task => {
            return task.taskId !== payload
          })
        },
        ADD_NEW_TASK (state, payload) {
          // algorithm to remove, e. g. by id
          // console.log(payload)
          state.tasks.push(payload)
          /* state.tasks = state.tasks.filter(e => {
            return e.id !== payload
          }) */
        },
        GET_TASKS (state, payload) {
          state.tasks = payload
        },
        MAKE_TASKFORM_VISIBLE (state, payload) {
          state.taskFormVisible = payload
        }
    
      },
      // Actions (asynchronous/synchronous): change data in the store
      actions: {
        autoSignIn ({ commit }, payload) {
          commit('setUser', { email: payload.email })
        },
        userSignUp ({ commit }, payload) {
          commit('setLoading', true)
          firebase.auth().createUserWithEmailAndPassword(payload.email, payload.password)
            .then(firebaseUser => {
              commit('setUser', { email: firebaseUser.user.email })
              commit('setLoading', false)
              router.push('/home')
              commit('setError', null)
            })
            .catch(error => {
              commit('setError', error.message)
              commit('setLoading', false)
            })
        },
        userSignIn ({ commit }, payload) {
          commit('setLoading', true)
          firebase.auth().signInWithEmailAndPassword(payload.email, payload.password)
            .then(firebaseUser => {
              commit('setUser', { email: firebaseUser.user.email })
              commit('setLoading', false)
              commit('setError', null)
              router.push('/home')
            })
            .catch(error => {
              commit('setError', error.message)
              commit('setLoading', false)
            })
        },
        userSignOut ({ commit }) {
          firebase.auth().signOut()
          commit('setUser', null)
          router.push('/')
        },
        makeTaskformVisible ({ commit }) {
          if (this.state.taskFormVisible === false) {
            commit('MAKE_TASKFORM_VISIBLE', true)
          } else {
            commit('MAKE_TASKFORM_VISIBLE', false)
          }
        },
        addNewTask ({ commit }, payload) {
          db.collection('tasks').add({
            title: payload.title,
            userId: firebase.auth().currentUser.uid,
            createdOn: firebase.firestore.FieldValue.serverTimestamp(),
            description: payload.description,
            dueDateAndTime: payload.dueTimestamp,
            reminderFlag: payload.reminderFlag,
            quadrant: payload.quadrant,
            tags: payload.tags,
            updatedOn: firebase.firestore.FieldValue.serverTimestamp()
          })
            .then(() => {
              commit('ADD_NEW_TASK', payload)
            })
            .catch(error => {
              commit('setError', error.message)
            })
        },
        getTasks ({ commit }) {
          // commit('loadedTasks', false)
          db.collection('tasks').orderBy('createdOn', 'desc').get().then(querySnapshot => {
            querySnapshot.forEach(doc => {
              const data = {
                'taskId': doc.id,
                'title': doc.data().title,
                'quadrant': doc.data().quadrant
              }
              this.state.tasks.push(data)
            })
            // commit('loadedTasks', true)
            commit('GET_TASKS', this.state.tasks)
          })
        },
        deleteTask ({ commit }, payload) {
          db.collection('tasks').doc(payload).delete().then(() => {
            commit('DELETE_TASK', payload)
          }).catch(function (error) {
            // console.error('Error removing document: ', error)
            commit('setError', error.message)
          })
        }
      },
      // Getters: receive data from the store
      getters: {
        isAuthenticated (state) {
          return state.user !== null && state.user !== undefined
        }
      }
    })
    

    任务列表:

    <template>
    
      <ul>
        <li>
          <h4>{{title}}</h4>
        </li>
        <li v-for="task in tasks" :key="task.taskId" v-if="task.quadrant === Id" class="collection-item">
          <task-list-item v-bind:task="task"></task-list-item>
        </li>
      </ul>
    </template>
    
    <script>
    import TaskListItem from './TaskListItem'
    
    export default {
      components: {
        'task-list-item': TaskListItem
      },
      data () {
        return {
    
        }
      },
      props: ['Id', 'title'],
      computed: {
        tasks () {
          return this.$store.state.tasks
        }
      },
      methods: {
    
      }
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
        <style scoped>
    
    </style>
    

    任务列表项:

    <template>
      <div>{{task.title}} ({{task.taskId}})<v-spacer></v-spacer>
        <v-scroll-x-transition>
          <v-btn flat icon color="primary" v-show="!deleteConfirmation">
            <v-icon>edit</v-icon>
          </v-btn>
        </v-scroll-x-transition>
        <v-scroll-x-transition>
          <v-btn flat icon color="primary" v-show="!deleteConfirmation" @click="deleteConfirmation = !deleteConfirmation">
            <v-icon>delete</v-icon>
          </v-btn>
        </v-scroll-x-transition>
    
        <v-scroll-x-transition>
          <v-btn flat icon color="green" v-show="deleteConfirmation" @click="deleteConfirmation = !deleteConfirmation">
            <v-icon>cancel</v-icon>
          </v-btn>
        </v-scroll-x-transition>
    
        <v-scroll-x-transition>
          <v-btn flat icon color="red" v-show="deleteConfirmation" @click="deleteConfirmation = !deleteConfirmation; deleteTask(task.taskId)">
            <v-icon>check_circle</v-icon>
          </v-btn>
        </v-scroll-x-transition>
        </li>
      </div>
    
    </template>
    
    <script>
    
    export default {
      data () {
        return {
          deleteConfirmation: false
        }
      },
      props: ['task'],
      computed: {
    
      },
      methods: {
        deleteTask (taskId) {
          this.$store.dispatch('deleteTask', taskId)
        }
      }
    }
    </script>
    
    <!-- Add "scoped" attribute to limit CSS to this component only -->
        <style scoped>
    
    </style>
    

    【讨论】:

    • 你的代码中没有addTask或类似的函数,你能展示一下吗?
    • 谢谢!该函数名为 addNewTask,在商店代码中
    【解决方案2】:

    您没有实际更改 tasks 数组的突变,例如 e。 G。 this.state.tasks.pop(payload) 发生在 deleteTask 操作中,而不是突变。

    在 Vuex 存储中实际更改状态的唯一方法是提交突变。 https://vuex.vuejs.org/guide/mutations.html

    这也是您在重新加载后看到正确结果的原因:Firestore 实现很好并且更新了它的值。它将显示更新和新获取的 tasks 数组 after 重新加载,但 Vuex 状态永远不会改变 before 重新加载,因此 Vue 显示的 tasks 数组保持不变一样。

    解决方案非常简单:

    创建一个新的突变:

    REMOVE_FROM_TASKS (state, payload) {
        // algorithm to remove, e. g. by id
        state.tasks = state.tasks.filter(e => {
            return e.id !== payload;
        });
    },
    

    在你的行动中使用这个突变:

    deleteTask ({ commit }, payload) {
      db.collection('tasks').doc(payload).delete().then(() => {
        commit('REMOVE_FROM_TASKS', payload)
        commit('deletedTask', true)
        commit('loadedTasks', true)
      }).catch(function (error) {
        console.error('Error removing document: ', error)
        commit('deletedTask', false)
      })
    }
    

    【讨论】:

    • 一开始它不起作用,但在我删除了所有错误的突变后,它就起作用了。当我没有索引但没有 ID 时,如何正确地从数组中删除条目?
    • 好的,我现在把它删除了——这个就像一个魅力。我尝试遵循相同的添加逻辑。但它显示了另一种奇怪的行为。它会更新 DOM,但是当我添加多个条目时,例如具有不同的标题,它们被正确地写入firestore,但在DOM中它显示它们都是相同的,直到我完全重新加载。
    • @thimplicity 你在v-for 中使用key 吗? vuejs.org/v2/guide/list.html#key
    • 我愿意,我会添加一个答案,包括我的代码的最新版本
    猜你喜欢
    • 2017-11-27
    • 1970-01-01
    • 2018-08-28
    • 1970-01-01
    • 1970-01-01
    • 2018-01-13
    • 2020-06-19
    • 2017-08-30
    • 2021-08-12
    相关资源
    最近更新 更多