美文网首页好巧前端
源码阅读计划之timeago

源码阅读计划之timeago

作者: 临水照影233 | 来源:发表于2018-02-02 20:50 被阅读3次

    前言

    大概半个月之前我看到了这个小型的javascript库,这个库的功能就一个用来将datetime时间转化成类似于*** 时间前的描述字符串,例如:“3小时前”。 但是却非常火爆,因为作者围绕这个核心功能开发一些很实用的特性,当初保存这个库的时候就有个想法好好学习一下它的源码。而且作者也是个中国人,中文文档也是齐全的。测试也是写了的。这就很方便学习了。

    正文

    先去timeago的github地址下载整个源文件,然后来看下测试文件。 根据功能来看 timeago分

    1.设置相对日期

    2.格式化时间戳,字符串

    3.自动实时渲染

    4.本地化和多语言支持

    来直接看源码

    // 插件普遍起手写法,兼容exports导出

    /*

    // 这么写会报错,因为这是一个函数定义:

    function() {}()

    // 常见的(多了一对括号),调用匿名函数:

    (function() {})()

    // 但在前面加上一个布尔运算符(只多了一个感叹号),就是表达式了,将执行后面的代码,也就合法实现调用,而 + - || 都有这样的功能

    !function() {}()

    */

    !function (root, factory) {

      if (typeof module === 'object' && module.exports)

        module.exports = factory(root);

      else

        root.timeago = factory(root);

    }(typeof window !== 'undefined' ? window : this,

    function () {

      var cnt = 0, // timer计数

        // 英文 年月日时分秒转成数组

        indexMapEn = 'second_minute_hour_day_week_month_year'.split('_'),

        // 中文年 月日时分秒转成数组

        indexMapZh = '秒_分钟_小时_天_周_月_年'.split('_'),

        // 地区化,默认是en和zh_CN两种

        locales = {

          'en': function(number, index) {

            if (index === 0) return ['just now', 'right now'];

            var unit = indexMapEn[parseInt(index / 2)];

            if (number > 1) unit += 's';

            return [number + ' ' + unit + ' ago', 'in ' + number + ' ' + unit];

          },

          'zh_CN': function(number, index) {

            if (index === 0) return ['刚刚', '片刻后'];

            var unit = indexMapZh[parseInt(index / 2)];

            return [number + unit + '前', number + unit + '后'];

          }

        },

        // second, minute, hour, day, week, month, year(365 days)

        SEC_ARRAY = [60, 60, 24, 7, 365/7/12, 12],

        SEC_ARRAY_LEN = 6,

        ATTR_DATETIME = 'datetime';

      // 格式化日期,将日期,字符串,时间戳转为日期实例

      function toDate(input) {

        // 如果传进来的是日期实例则直接返回

        if (input instanceof Date) return input;

        // 如果是时间戳

        if (!isNaN(input)) return new Date(toInt(input));

        if (/^\d+$/.test(input)) return new Date(toInt(input, 10));

        // 如果是字符串

        input = (input || '').trim().replace(/\.\d+/, '') // remove milliseconds

          .replace(/-/, '/').replace(/-/, '/')

          .replace(/T/, ' ').replace(/Z/, ' UTC')

          .replace(/([\+\-]\d\d)\:?(\d\d)/, ' $1$2'); // -04:00 -> -0400

        return new Date(input);

      }

      // 强制转为数字整型

      function toInt(f) {

        return parseInt(f);

      }

      // 根据指定的本地化语言,格式化输出diff为'多少秒之前'

      function formatDiff(diff, locale, defaultLocale) {

        // 如果没指定则默认为en语言包

        locale = locales[locale] ? locale : (locales[defaultLocale] ? defaultLocale : 'en');

        // 判断传进来的是diff是当前时间之前还是之后

        var i = 0,

          agoin = diff < 0 ? 1 : 0; // 1之前 / 0之后

        // 取绝对值

        diff = Math.abs(diff);

        // SEC_ARRAY = [60, 60, 24, 7, 365/7/12, 12] 秒_分钟_小时_天_周_月_年

        // diff 是 时间差,用秒来做单位 diff一次从秒开始除直到无法整除时便计算出最终间隔(小时/月/年)

        for (; diff >= SEC_ARRAY[i] && i < SEC_ARRAY_LEN; i++) {

          diff /= SEC_ARRAY[i];

        }

        diff = toInt(diff);

        i *= 2;

        // 用来定位语言包的合适位置

        if (diff > (i === 0 ? 9 : 1)) i += 1;

        return locales[locale](diff, i)[agoin].replace('%s', diff);

      }

      // 返回指定时间与当前时间的时间差,用秒作为单位

      function diffSec(date, nowDate) {

        nowDate = nowDate ? toDate(nowDate) : new Date();

        return (nowDate - toDate(date)) / 1000;

      }

      // 计算下一个周期

      function nextInterval(diff) {

        var rst = 1, i = 0, d = Math.abs(diff);

        for (; diff >= SEC_ARRAY[i] && i < SEC_ARRAY_LEN; i++) {

          diff /= SEC_ARRAY[i];

          rst *= SEC_ARRAY[i];

        }

        // return leftSec(d, rst);

        d = d % rst;

        d = d ? rst - d : rst;

        return Math.ceil(d);

      }

      // 从给定的节点中获取date属性的值

      function getDateAttr(node) {

        if (node.getAttribute) return node.getAttribute(ATTR_DATETIME);

        if(node.attr) return node.attr(ATTR_DATETIME);

      }

      // 渲染函数

      function Timeago(nowDate, defaultLocale) {

        var timers = {};

        if (! defaultLocale) defaultLocale = 'en';

        // 渲染,加个cnt好分辨是哪个定时器,到时候清空好清空

        function doRender(node, date, locale, cnt) {

          var diff = diffSec(date, nowDate);

          node.innerHTML = formatDiff(diff, locale, defaultLocale);

          // 每秒渲染一次

          timers['k' + cnt] = setTimeout(function() {

            doRender(node, date, locale, cnt);

          }, nextInterval(diff) * 1000);

        }

        // 调用私有函数format

        this.format = function(date, locale) {

          return formatDiff(diffSec(date, nowDate), locale, defaultLocale);

        };

        // 调用私有函数render

        this.render = function(nodes, locale) {

          if (nodes.length === undefined) nodes = [nodes];

          for (var i = 0; i < nodes.length; i++) {

            doRender(nodes[i], getDateAttr(nodes[i]), locale, ++ cnt); // render item

          }

        };

        // 清楚渲染

        this.cancel = function() {

          for (var key in timers) {

            clearTimeout(timers[key]);

          }

          timers = {};

        };

        // 设置语言

        this.setLocale = function(locale) {

          defaultLocale = locale;

        };

        // 返回

        return this;

      }

      // 返回新对象

      function timeagoFactory(nowDate, defaultLocale) {

        return new Timeago(nowDate, defaultLocale);

      }

      // 重新指定语言类型

      timeagoFactory.register = function(locale, localeFunc) {

        locales[locale] = localeFunc;

      };

      return timeagoFactory;

    });

    结尾

    看完这个源码非常简单而且很适合新手学习它的组织结构。而且还可以深入思考一下,有些插件其实并不需要做的很全,只要你把核心功能写的明白了然,它也是独一无二的。

    相关文章

      网友评论

        本文标题:源码阅读计划之timeago

        本文链接:https://www.haomeiwen.com/subject/coxyzxtx.html