V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
supermao
V2EX  ›  前端开发

抄了一个框架玩玩。。。

  •  
  •   supermao ·
    hongmaoxiao · 2017-12-01 17:12:24 +08:00 · 2409 次点击
    这是一个创建于 2583 天前的主题,其中的信息可能已经有所发展或是发生改变。

    now.js 是什么

    now.js是一个 javascript 的时间操作小工具,类似date-fnsmoment

    长啥样

    简单的把示例放这,更多用法请到github去发现。

    import Now from 'now.js'; // for node(browser do not need this)
    
    now = new Now() // "2017-11-20 22:23:00.285"
    
    now.beginningOfMinute()   // "2017-11-20 22:23:00"
    now.beginningOfHour()     // "2017-11-20 22:00:00"
    now.beginningOfDay()      // "2017-11-20 00:00:00"
    now.beginningOfWeek()     // "2017-11-19 00:00:00"
    now.firstDayMonday = true // Set Monday as first day, default is Sunday
    now.beginningOfWeek()     // "2017-11-20 00:00:00"
    now.beginningOfMonth()    // "2017-11-01 00:00:00"
    now.beginningOfQuarter()  // "2017-10-01 00:00:00"
    now.beginningOfYear()     // "2017-01-01 00:00:00"
    
    now.endOfMinute()         // "2017-11-20 22:23:59.999"
    now.endOfHour()           // "2017-11-20 22:59:59.999"
    now.endOfDay()            // "2017-11-20 23:59:59.999"
    now.endOfWeek()           // "2017-11-25 23:59:59.999"
    now.firstDayMonday = true // Set Monday as first day, default is Sunday
    now.endOfWeek()           // "2017-11-26 23:59:59.999"
    now.endOfMonth()          // "2017-11-30 23:59:59.999"
    now.endOfQuarter()        // "2017-12-31 23:59:59.999"
    now.endOfYear()           // "2017-12-31 23:59:59.999"
    
    All the above functions return String type. You can pass 'self' to return Now instance:
    
    var beginningOfMinute = now.beginningOfMinute('self') // return Now instance
    beginningOfMinute.format('ddd, Ah') // "Mon, PM10"
    beginningOfMinute.format('LLLL') // "Monday, November 20, 2017 10:23 PM"
    beginningOfMinute.isMonday() // true
    
    

    为什么要写这个库

    因为学习underscore源码的过程中,感觉无聊。想写个库调剂一下,当做学习的机会。
    我是照着underscore源码一个个 commit 敲的.代码放在这里,刚看了一眼,有 668 次 commit,两个月,敲到了 1.4.3 版本,可能还没到一半。
    敲久了有点无聊。想动手写一个库作为调剂。碰巧之前做防健忘短信提醒的时候是用 go 写的后台,用了gorm,在作者jinzhu的 github 主页上发现了now,是一个 go 的时间帮助库,觉得很有意思。clone 之,学之。
    偷了now的思想。造一个 javascript 版的就显得很容易。很快我就写完了除了ParseMustParse之外的所有方法。跟原库对比一下。觉得 now.js 就是个玩具。这不怪我,javascript 对时间处理的支持远不如 go。比如 go 原生支持format、字符串解析成时间以及Duration等等。
    想着加上format。该怎么去写?立马就想到的方法是平时经常写的根据不同条件做字符串拼接。确实是个方法。但得多少 switch case 才能涵盖所有情况,想想都可怕。显然这是最蠢也是工作量最大的方法。
    不会,那就借鉴别人的。github 上发现了 date-fns 和 moment,支持 i18n 国际化。

    date-fns:

    import { formatRelative } from 'date-fns'
    import { es, ru } from 'date-fns/esm/locale'
    
    formatRelative(subDays(new Date(), 3), new Date(), { locale: es })
    //=> "el viernes pasado a las 19:26"
    
    formatRelative(subDays(new Date(), 3), new Date(), { locale: ru })
    //=> "в прошлую пятницу в 19:26"
    

    moment:

    moment.locale('fr');
    moment(1316116057189).fromNow(); // il y a une heure
    moment.locale('en');
    moment(1316116057189).fromNow(); // an hour ago
    

    我的审美告诉我应该选择 moment,调用一次 locale(),这之后的 format 都是基于该 locale 的。当然它也还支持每次单独指定 locale 的:

    moment.duration(1, "minutes").locale("en").humanize(); // a minute
    moment.duration(1, "minutes").locale("fr").humanize(); // une minute
    moment.duration(1, "minutes").locale("es").humanize(); // un minuto
    

    进一步学习发现 moment 的 format 和 i18n 国际化高度耦合。要用它的 i18n 意味着基本上也得用它的 format。当然 i18n 也不是核心的 moment 库作者写的,他们也精通不了那么多国家的语言,那都是 github 上许许多多人贡献的。开源就有这好处。看下 LICENCE 是 MIT 的,代码可用,抄。
    now.js 也是 MIT 协议的,负罪感少了点(其实 MIT 协议是相当宽松的,就算你拿它代码去商业化也是没有问题的)。况且我写库的主要目的是为了学习。
    开干!虽说是抄,毕竟代码要整合到我的库,直接复制粘贴是不行的。所有的代码细节未必都需要全知道,但看懂整体运行的逻辑是必须的,下点功夫,整合成功,开源。

    思维脑图

    moment:

    当然,moment 的东西不止上图这些,我只取了一部分来画。

    now.js:

    now.js 的 Duration 和 moment 的不一样,现在还不支持单独使用,只是给内部方法elapse使用,以后可能会支持单独使用。

    对比

    1. moment 是大而全,now.js 是刚够用。
    2. moment 的 parse 相当强大,now.js 就暂时不支持了,只支持和new Date(args)相同的args参数类型。不过format应该都基本上和 moment 的一样,不过测试用例现在还没有写太全,如果谁用了并且发现 bug,可以到github去提 issues。不胜感激。
    3. moment 是页面一加载的时候会把所有的 i18n 都初始化了,这点我个人认为不好,加载时间长,网络情况不好的时候,差不多需要 10 秒我才能在 devtool 上调试(当然这也包括官网加载的其他很多东西)。now.js 只加载默认的,需要的时候按需加载。
    4. moment 做什么操作前都要检测一下 date 是否合法的( isvalid )。now.js 在 parse 的时候如果不合法就直接抛出错误,以免后续没玩没了的检测。当然这可能损失了用户友好性,但是对减少代码量是很有帮助的。

    结语

    这个库不是我一个人写的,是许多开源工作者共同完成的。感谢 moment 的所有开源贡献者,我从中学习了很多东西。后续还会继续研究 moment 的代码细节,偷偷它的思想。

    写代码什么最重要?思想最重要!

    7 条回复    2017-12-20 22:10:13 +08:00
    Pastsong
        1
    Pastsong  
       2017-12-01 17:25:20 +08:00
    设全局状态这件事情太不 functional 了...
    supermao
        2
    supermao  
    OP
       2017-12-01 17:32:10 +08:00
    @Pastsong 没错,现在这个只是第二个小版本,主要还是学习学习,慢慢再想怎么提高
    wyc829
        3
    wyc829  
       2017-12-04 15:52:20 +08:00
    支持
    supermao
        4
    supermao  
    OP
       2017-12-05 13:17:18 +08:00
    @wyc829 多谢!
    brooky
        5
    brooky  
       2017-12-20 14:44:09 +08:00
    小白一个, 才知道原来是这样学习别人代码的. 但是看了下没有注释啊, 看不懂啊
    underscore 有 2000 多 commits 怎么迅速找到第一个 commit, 而不是一个个点击`older`
    supermao
        6
    supermao  
    OP
       2017-12-20 22:08:37 +08:00 via iPhone
    @brooky 其实我觉得我的命名已经能够很好的理解意思了,所以没加注释,等 1.0.0 再加吧
    supermao
        7
    supermao  
    OP
       2017-12-20 22:10:13 +08:00 via iPhone
    @brooky clone 库 然后 git log 就行 当然可以配合 shell 脚本的 tail 等来看 自己发挥了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2916 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 11:19 · PVG 19:19 · LAX 03:19 · JFK 06:19
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.