Firebase:为什么在添加新的子引用之前触发了value事件

时间:2019-05-25 18:16:04

标签: firebase vue.js firebase-realtime-database vuejs2

以下代码是一个非常简单的Firebase-VueJS应用(codeSandBox demo

app.vue

<template>
  <div class="container">
    <!-- Adding Quote -->
    <add-quote/>

    <!-- Display Quotes -->
    <quote-list/>
  </div>
</template>

<script>
import addQuote from "./components/AddQuote.vue";
import quoteList from "./components/QuoteList.vue";

export default {
  components: {
    addQuote,
    quoteList
  },
  methods: {
    get_allQuotes: function() {
      // var vm = this;
      var localArr = [];
      quotesRef
        .once("value", function(snapshot) {
          snapshot.forEach(function(snap) {
            localArr.push({
              key: snap.key,
              category: snap.val().category,
              quoteTxt: snap.val().quoteTxt
            });
          });
        })
        .then(data => {
          this.$store.commit("set_allQuotes", localArr);
        });
    }
  },
  mounted() {
    this.get_allQuotes();
    console.log("App: mounted fired");
  }
};
</script>

store.js(vuex商店)

import Vue from "vue";
import Vuex from "vuex";

Vue.use(Vuex);

export const store = new Vuex.Store({
    state: {
        quotesList: []
    },
    getters: {
        get_quotesList(state) {
            return state.quotesList;
        }
    },
    mutations: {
        set_allQuotes(state, value) {
            state.quotesList = value;
        }
    }
});

AddQuote.vue

<template>     
    <div class="row quote-edit-wrapper">         
        <div class="col-xs-6">           
            <textarea v-model.lazy="newQuoteTxt" 
                rows="4" 
                cols="50"></textarea>
            <button @click="addQuote">Add Quote</button>
        </div>
    </div>
</template>

<script>

export default {   
  data() {
    return {
            newQuoteTxt: '',
      }
    },
    computed: {
    allQuotes() {
      return this.$store.getters.get_quotesList;
        },
        newQuoteIdx() {
            var localArr = [...this.allQuotes]

            if(localArr.length > 0) {
                var highestKEY, currKEY

                localArr.forEach((element, idx) => {
                    currKEY = parseInt(element.key)
                    if(idx == 0) {
                        highestKEY = currKEY
                    } else {
                        if(highestKEY < currKEY) {
                            highestKEY = currKEY
                        }
                    }
                })
                return highestKEY + 1
            } else {
                return 1
            }
        }
    },
  methods: {
        // ADD new Quote in DB
    addQuote: function() {
            var vm = this
            var localArr = [...this.allQuotes]

            //1. First attach 'value' event listener,
            // Snapshot will contain data from that ref 
            // when any child node is added/updated/delete
            quotesRef.on('value', function (snapshot) {
                snapshot.forEach(function(snap) {
                var itemExists = localArr.some(function (item, idx) {
                        return item.key == snap.key
                    })
                // If newly added item doesn't yet exists then add to local array
                if (!(itemExists)) {
                    localArr.push({
                        key: snap.key,
                        category: snap.val().category,
                        quoteTxt: snap.val().quoteTxt })

                    vm.$store.commit('set_allQuotes', localArr)
                    }
                })
            })

            //2. Second set/create a new quotes in Firebase, 
            // When this quote gets added in Firebase,
            // value event (attached earlier) gets fired  
            //  with 
            var newQuoteRef = quotesRef.child(this.newQuoteIdx)
            newQuoteRef.set({
                category: 'motivation',
                quoteTxt: this.newQuoteTxt
            })
        }
    }
}
</script>

quoteList.vue

<template>
    <div class="row">
        <div class="col-xs-12 quotes-list-wrapper">
            <template v-for="(quote,idx) in allQuotes">

                <!-- Quote block -->
                    <div class="quote-block-item">
                        <p class="quote-txt"> {{quote.quoteTxt}} </p>
                    </div>
            </template>
        </div>
    </div>  
</template>

<script>
    export default {
        computed: {
            allQuotes() {
                return this.$store.getters.get_quotesList;
            }
        }
    }

</script>

注意:关注的主要代码是 addQuote.vue

用户输入newQuoteTxt作为addQuote()下的报价项添加到Firebase(quotesRef)中。一旦在Firebase上添加了报价,Firebase客户端SDK的value事件就会触发,并将新的报价(通过回调)添加到localArray(allQuotes)中。然后VueJS使用新添加的Quote更新了DOM。

addQuote()方法以下列方式工作:

  1. 首先,在quotesRef的“值”事件上附加一个回调/监听器
quotesRef.on('value', function (snapshot) {
   ....
})

  1. 接下来,创建一个ID为quotesRef的firebase ref(this.newQuoteIdx的子代)

    var newQuoteRef = quotesRef.child(this.newQuoteIdx)

然后调用set()(在这个新创建的Ref上),将新引用添加到firebase RealTime DB中。

  1. value事件被触发(从步骤1开始),并调用侦听器/ callback。

回调通过匹配localArr和snap.key的键在现有项目列表中查找此新引号的键(如果未找到,则将新引号添加到localArr)。 localArr提交到vuex存储。

`vm.$store.commit('set_allQuotes', localArr)`

VueX然后更新该阵列的所有订户组件。然后VueJS将新报价添加到现有报价列表中(更新DOM)

在调试addQuote方法时,我注意到的问题是,脚本的执行/流(通过chrome调试器中的F8)首先进入代码{之前附加到value事件的侦听器/回调中{1}}添加了新的报价(在Firebase上),这又将触发“值”事件。

我不确定为什么会这样。 anybuddy能否解释为什么在创建引号之前调用侦听器/回调。

(QuotesRef的)子节点是否缓存在客户端,这样即使在添加新的报价之前也会触发“值”。

谢谢

1 个答案:

答案 0 :(得分:1)

如果我正确理解您的问题(您的代码不太容易遵循!:-)),这是正常的行为。如documentation中所述:

  

值事件将使用存储在以下位置的初始数据一次触发   此位置,然后每次数据再次触发   更改

您的沙盒演示实际上并未显示该应用程序的工作原理,但是通常您不应该在将新节点保存到数据库的方法中设置侦听器。这两件事应该分开。

一种常见的方法是将侦听器设置在组件的created钩子中(请参见https://vuejs.org/v2/guide/instance.html#Instance-Lifecycle-Hookshttps://vuejs.org/v2/api/#created),然后在您的addQuote方法中将其写入数据库。一旦您写完,监听器就会被解雇。

相关问题