Vue momentjs 根据时间戳实时更新相对时间

Obe*_*lve 3 javascript momentjs vue.js vuex google-cloud-firestore

我正在构建一个论坛应用程序,并使用 v-for,我想显示所有评论何时以 momentjs 相对时间发布,问题是渲染永远不会从“几秒钟前”开始改变。在这个问题中,响应显示可以实现我想要的,基本上在创建组件时使用间隔:

  created() {
    setInterval(() => {
      this.messages = this.messages.map(m => {
        m.ago = moment(m.time).fromNow();
        return m;
      });
    }, 1000);
  }
Run Code Online (Sandbox Code Playgroud)

但由于我直接从 vuex 获取数据,我不知道如何将解决方案与我的代码集成。

这是我的 store.js:

export default new Vuex.Store({
  state: {
    comments: [],
  },
  mutations: {
    loadComments: (state, comments) => {
      state.comments = comments;
    },
  },
  actions: {
    loadComments: async (context) => {
      let snapshot = await db
        .collection("comments")
        .orderBy("timestamp")
        .get();
      const comments = [];
      snapshot.forEach((doc) => {
        let appData = doc.data();
        appData.id = doc.id;
        appData.timestamp = moment()
          .startOf(doc.data().timestamp)
          .fromNow();
        comments.push(appData);
      });
      context.commit("loadComments", comments);
    },
  },
});
Run Code Online (Sandbox Code Playgroud)

这是我的脚本:

import { db, fb } from "../firebase";
import { mapState } from "vuex";

export default {
  name: "Forum",
  data() {
    return {
      commentText: null,
    };
  },
  mounted() {
    this.getComment();
  },
  methods: {
    getComment() {
      this.$store.dispatch("loadComments");
    },
    async sendComment() {
      await db
        .collection("comments")
        .add({
          content: this.commentText,
          author: this.name,
          timestamp: Date.now()
        });
      this.commentText = null;
      this.getComment();
    }
  },
  created() {
    let user = fb.auth().currentUser;
    this.name = user.displayName;
  },
  computed: mapState(["comments"])
};
Run Code Online (Sandbox Code Playgroud)

Dec*_*oon 5

最好将所有这些逻辑抽象为一个用于显示日期的组件,而不是需要跨代码边界触发更新。尽可能减少所有日期呈现代码,以便随时轻松更改日期呈现逻辑。

日期本身是数据,但该日期的视觉表示形式不是。所以我不会将格式化日期存储在 Vuex 存储中。

创建一个<from-now>以 adate作为 prop 的组件,并使用 moment 的 渲染日期fromNow。它还负责定期(每分钟)更新自身。由于它是一个组件,因此您可以将完整日期显示为工具提示,以便用户将鼠标悬停在其上时可以知道确切的时间。

const components = new Set();

// Force update every component at the same time periodically
setInterval(() => {
  for (const comp of components) {
    comp.$forceUpdate();
  }
}, 60 * 1000);

Vue.component('FromNow', {
  template: '<span :title="title">{{ text }}</span>',
  props: ['date'],
  
  created() {
    components.add(this);
  },
  
  destroyed() {
    components.remove(this);
  },
  
  computed: {
    text() {
      return moment(this.date).fromNow();
    },
    
    title() {
      return moment(this.date).format('dddd, MMMM Do YYYY, h:mm:ss a');
    },
  },
});

new Vue({
  el: '#app',
  
  data: {
    // Test various seconds ago from now
    dates: [5, 60, 60 * 5, 60 * 60].map(sec => moment().subtract(sec, 'seconds')),
  },
});
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>

<div id="app">
  <p v-for="date of dates">
    <from-now :date="date"></from-now>
  </p>
</div>
Run Code Online (Sandbox Code Playgroud)

为了简单起见,<from-now>仅显示相对日期。我使用与此类似的组件,但更通用。它需要一个format道具,允许我选择如何显示日期(可以是相对时间,也可以是 moment 接受的特定日期格式)。