Vue momentjs 从时间戳实时更新相对时间

Posted

技术标签:

【中文标题】Vue momentjs 从时间戳实时更新相对时间【英文标题】:Vue momentjs update relative time in real time from timestamp 【发布时间】:2020-08-01 10:59:42 【问题描述】:

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

  created() 
    setInterval(() => 
      this.messages = this.messages.map(m => 
        m.ago = moment(m.time).fromNow();
        return m;
      );
    , 1000);
  

但由于我直接从 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);
    ,
  ,
);

这是我的脚本:

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"])
;

【问题讨论】:

你能提供一个运行代码,就像我在这个answer中一样 @BoussadjraBrahim,我可以做到......但无论如何都不会工作,因为我从 firebase 带来了数据。但是,我可以用硬编码数据填充状态中的 cmets 数组,但我认为会错过问题点。 【参考方案1】:

最好将所有这些逻辑抽象到一个单独的组件中来显示日期,而不是需要跨代码边界触发更新。尽量减少所有日期呈现代码,以便随时更改日期呈现逻辑。

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

创建一个组件<from-now>,它以date 作为道具,并使用时刻的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')),
  ,
);
<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>

为简单起见,&lt;from-now&gt; 仅显示相对日期。我使用与此类似的组件,但更通用。它需要一个format 属性,它允许我选择我希望日期的显示方式(可以是相对时间,也可以是时刻接受的特定日期格式)。

【讨论】:

这是一个非常好的解决方案,但问题仍然存在......我不知道如何与我当前的代码集成,我怎样才能将我从 firebase 获得的时间戳作为道具传递给那个组件? 我不明白为什么它不起作用。在您的代码中,您正在执行moment(m.time),这意味着m.time 是与javascript 兼容的日期字符串或时间戳。我的组件做同样的事情。只需将您从 firebase 获得的未格式化的原始日期传递给组件上的 date 属性即可。 现在可以了,我传递的是格式化日期而不是原始数据,非常感谢。 工具提示是一个很棒的额外 XD,再次感谢您。【参考方案2】:

尝试添加另一个动作并每秒调度一次:

 mutations: 
    loadComments: (state, comments) => 
      state.comments = comments;
    ,
   update:(state)=>
    state.comments=state.comments.map(comment=>
     comment.ago= moment()
          .startOf(comment.timestamp)
          .fromNow();
        return comment;
     );
   
  ,
 actions: 
  updateComments(context)
   context.commit('update')
   ,  
  loadComments: async (context) => 
    ....

 created() 
    let user = fb.auth().currentUser;
    this.name = user.displayName;
 setInterval(() => 
      this.$store.dispatch("updateComments");
    , 1000);
  ,

【讨论】:

我尝试了您的解决方案,但出现此错误Uncaught ReferenceError: comments is not defined,这是error 的图像 对不起,我在comments之前错过了state,它应该是state.comments=state.comments.map(comment=&gt; 控制台没有错误了,但是还是不行……vue开发者工具中的vuex标签显示update突变的payload为未定义。

以上是关于Vue momentjs 从时间戳实时更新相对时间的主要内容,如果未能解决你的问题,请参考以下文章

vue中使用moment.js

PHP:从时间戳产生相对日期/时间

momentjs在vue中的用法

从mongodb获取数据时如何使用momentjs格式化时间

vue 时间戳转换

如何使用 Moment.js 返回当前时间戳?