最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • dayjs源码解析(一):概念、locale、constant、utils

    正文概述 掘金(林景宜)   2020-12-11   419

    插播一个新系列:时间库 dayjs 的源码解析。

    用官方的描述 “Day.jsMoment.js 的 2kB 轻量化方案,拥有同样强大的 API”。优点是如下三个:

    • 简易:Day.js 是一个轻量的处理时间和日期的 JavaScript 库,和 Moment.jsAPI 设计保持完全一样。
    • 不可变:所有的 API 操作都将返回一个新的 Dayjs 实例。这种设计能避免 bug 产生,节约调试时间。
    • 国际化:Day.js 对国际化支持良好。但除非手动加载,多国语言默认是不会被打包到工程里的。

    总的来说,dayjs 的优点就是 pluginlocale 手动按需加载,减少打包体积。

    dayjs 是饿了么的大佬 iamkun 开发维护的,大佬同时也是 ElementUI 的开发者。解析之前先从dayjs 源代码仓库 fork 了一份:github.com/MageeLin/da… 。

    时间是 2020 年 12 月 7 日,commitIDeb5fbc4c。解析时从 master 分支拉了一个新分支 analysis

    打算分五章完成,目录如下:

    1. dayjs 源码解析(一):概念、locale、constant、utils
    2. dayjs 源码解析(二):Dayjs 类
    3. dayjs 源码解析(三):插件(上)
    4. dayjs 源码解析(四):插件(中)
    5. dayjs 源码解析(五):插件(下)

    代码结构

    目录结构

    源代码的目录结构如下所示:

    dayjs
    │  .editorconfig // 编辑器配置
    │  .eslintrc.json // ESLint配置
    │  .gitignore // git忽略配置
    │  .npmignore // npm发布忽略配置
    │  .travis.yml // 持续集成配置
    │  babel.config.js // babel配置
    │  CHANGELOG.md // 更新日志
    │  CONTRIBUTING.md // 共建指南
    │  karma.sauce.conf.js // karma测试配置
    │  LICENSE // 许可声明
    │  package.json
    │  prettier.config.js // prettier 格式化配置
    │  README.md
    │
    ├─.github // github的一些配置
    ├─build // 构建打包
    ├─docs // 各语言的说明文档
    ├─src
    │  │  constant.js // 常量
    │  │  index.js // 主入口,定义Dayjs类
    │  │  utils.js // 工具函数
    │  │
    │  ├─locale // 国际化
    │  └─plugin // 插件
    ├─test // 测试
    └─types // TypeScript
    
    

    依赖结构

    入口 src/index.js 的依赖如下所示:

    dayjs源码解析(一):概念、locale、constant、utils

    可以发现依赖链特别简单,没有依赖到 localeplugin 目录下的语言包和插件。这也就是 dayjs 的核心优点。

    基础概念

    在分析源码之前,先理解下一些相关的基础概念。

    时间标准

    几种时间标准的解释来自维基百科。

    GMT

    格林尼治平均时间(Greenwich Mean Time,GMT)是指位于英国伦敦郊区的皇家格林尼治天文台当地的平太阳时,因为本初子午线被定义为通过那里的经线。

    自 1924 年 2 月 5 日开始,格林尼治天文台负责每隔一小时向全世界发放调时信息。

    格林尼治标准时间的正午是指当平太阳横穿格林尼治子午线时(也就是在格林尼治上空最高点时)的时间。由于地球每天的自转是有些不规则的,而且正在缓慢减速,因此格林尼治平时基于天文观测本身的缺陷,已经被原子钟报时的协调世界时(UTC)所取代。

    UTC

    协调世界时(英语:Coordinated Universal Time,法语:Temps Universel Coordonné,简称 UTC)是最主要的世界时间标准,其以原子时秒长为基础,在时刻上尽量接近于格林威治标准时间。

    协调世界时是世界上调节时钟和时间的主要时间标准,它与 0 度经线的平太阳时相差不超过 1 秒,并不遵守夏令时。

    现行的协调世界时根据国际电信联盟的建议《Standard-frequency and time-signal emissions》(ITU-R TF.460-6)所确定。UTC 基于国际原子时,并在必要时通过不规则的加入闰秒来抵消地球自转变慢的影响。

    如果本地时间比 UTC 时间快,例如中国、蒙古、菲律宾、新加坡、马来西亚、澳大利亚西部的时间比 UTC8 小时,就会写作 UTC+8,俗称东八区。相反,如果本地时间比 UTC 时间慢,例如夏威夷的时间比 UTC 时间慢 10 小时,就会写作 UTC-10,俗称西十区

    ISO

    国际标准 ISO 8601,是国际标准化组织的日期和时间的表示方法,全称为《数据存储和交换形式·信息交换·日期和时间的表示方法》。目前是 2004 年 12 月 1 日发行的第三版“ISO8601:2004”

    在 Javascript 中的 Date.prototype.toISOString() 中,返回的是 YYYY-MM-DDTHH:mm:ss.sssZ格式的字符串,时区总是 UTC(协调世界时),加一个后缀“Z”标识。

    Date 对象输出时间的格式

    Javascript 的 Date.prototype 上有很多种方式可以输出时间,以时间戳 1607561462990 为例,在 Chrome87 中返回值如下表:

    方法格式输出
    valueOf时间戳1607561462990getTimeGMT 时间戳1607561462990toString英语格式的本地时间字符串Thu Dec 10 2020 08:51:02 GMT+0800 (中国标准时间)toUTCString英语格式的 UTC 时间字符串Thu, 10 Dec 2020 00:51:02 GMTtoGMTString(标准已废弃)英语格式的 GMT 时间字符串Thu, 10 Dec 2020 00:51:02 GMTtoISOStringISO 格式的 UTC 时间字符串2020-12-10T00:51:02.990ZtoLocaleString字符串格式因不同语言而不同2020/12/10 上午 8:51:02toJSONtoISOString 相同2020-12-10T00:51:02.990Z

    语言(文化)代码

    不同语言对事物的描述方式肯定不同,即使同一种语言由于文化地区差异,对相同事物的描述也有区别,所以国际上就形成了一套标准来识别各种语言。

    先放一篇 Hax 的回答和 BCP47 规范,对于汉语代码来说,按照标准应该使用 zh-cmn-Hans-CNzh-cmn-Hant-HKzh-cmn-Hans-SGzh-cmn-Hant-TW。但是由于历史的原因,广泛应用的是zh-CNzh-HKzh-SGzh-TW

    引用一个通用的语言列表:

    语言代码
    国家|地区
    "" (空字符串)   无变化的文化
    af  公用荷兰语
    af-ZA   公用荷兰语 - 南非
    sq  阿尔巴尼亚
    sq-AL   阿尔巴尼亚 -阿尔巴尼亚
    ar  阿拉伯语
    ar-DZ   阿拉伯语 -阿尔及利亚
    ar-BH   阿拉伯语 -巴林
    ar-EG   阿拉伯语 -埃及
    ar-IQ   阿拉伯语 -伊拉克
    ar-JO   阿拉伯语 -约旦
    ar-KW   阿拉伯语 -科威特
    ar-LB   阿拉伯语 -黎巴嫩
    ar-LY   阿拉伯语 -利比亚
    ar-MA   阿拉伯语 -摩洛哥
    ar-OM   阿拉伯语 -阿曼
    ar-QA   阿拉伯语 -卡塔尔
    ar-SA   阿拉伯语 - 沙特阿拉伯
    ar-SY   阿拉伯语 -叙利亚共和国
    ar-TN   阿拉伯语 -北非的共和国
    ar-AE   阿拉伯语 - 阿拉伯联合酋长国
    ar-YE   阿拉伯语 -也门
    hy  亚美尼亚
    hy-AM   亚美尼亚的 -亚美尼亚
    az  Azeri
    az-AZ-Cyrl  Azeri-(西里尔字母的) 阿塞拜疆
    az-AZ-Latn  Azeri(拉丁文)- 阿塞拜疆
    eu  巴斯克
    eu-ES   巴斯克 -巴斯克
    be  Belarusian
    be-BY   Belarusian-白俄罗斯
    bg  保加利亚
    bg-BG   保加利亚 -保加利亚
    ca  嘉泰罗尼亚
    ca-ES   嘉泰罗尼亚 -嘉泰罗尼亚
    zh-HK   中国 -香港
    zh-MO   中国 -澳门
    zh-CN   中国 -中国
    zh-CHS  中国 (单一化)
    zh-SG   中国 -新加坡
    zh-TW   中国 -台湾
    zh-CHT  中国 (传统的)
    hr  克罗埃西亚
    hr-HR   克罗埃西亚 -克罗埃西亚
    cs  捷克
    cs-CZ   捷克 - 捷克
    da  丹麦文
    da-DK   丹麦文 -丹麦
    div Dhivehi
    div-MV  Dhivehi-马尔代夫
    nl  荷兰
    nl-BE   荷兰 -比利时
    nl-NL   荷兰 - 荷兰
    en  英国
    en-AU   英国 -澳洲
    en-BZ   英国 -伯利兹
    en-CA   英国 -加拿大
    en-CB   英国 -加勒比海
    en-IE   英国 -爱尔兰
    en-JM   英国 -牙买加
    en-NZ   英国 - 新西兰
    en-PH   英国 -菲律宾共和国
    en-ZA   英国 - 南非
    en-TT   英国 - 千里达托贝哥共和国
    en-GB   英国 - 英国
    en-US   英国 - 美国
    en-ZW   英国 -津巴布韦
    et  爱沙尼亚
    et-EE   爱沙尼亚的 -爱沙尼亚
    fo  Faroese
    fo-FO   Faroese- 法罗群岛
    fa  波斯语
    fa-IR   波斯语 -伊朗王国
    fi  芬兰语
    fi-FI   芬兰语 -芬兰
    fr  法国
    fr-BE   法国 -比利时
    fr-CA   法国 -加拿大
    fr-FR   法国 -法国
    fr-LU   法国 -卢森堡
    fr-MC   法国 -摩纳哥
    fr-CH   法国 -瑞士
    gl  加利西亚
    gl-ES   加利西亚 -加利西亚
    ka  格鲁吉亚州
    ka-GE   格鲁吉亚州 -格鲁吉亚州
    de  德国
    de-AT   德国 -奥地利
    de-DE   德国 -德国
    de-LI   德国 -列支敦士登
    de-LU   德国 -卢森堡
    de-CH   德国 -瑞士
    el  希腊
    el-GR   希腊 -希腊
    gu  Gujarati
    gu-IN   Gujarati-印度
    he  希伯来
    he-IL   希伯来 -以色列
    hi  北印度语
    hi-IN   北印度的 -印度
    hu  匈牙利
    hu-HU   匈牙利的 -匈牙利
    is  冰岛语
    is-IS   冰岛的 -冰岛
    id  印尼
    id-ID   印尼 -印尼
    it  意大利
    it-IT   意大利 -意大利
    it-CH   意大利 -瑞士
    ja  日本
    ja-JP   日本 -日本
    kn  卡纳达语
    kn-IN   卡纳达语 -印度
    kk  Kazakh
    kk-KZ   Kazakh-哈萨克
    kok Konkani
    kok-IN  Konkani-印度
    ko  韩国
    ko-KR   韩国 -韩国
    ky  Kyrgyz
    ky-KZ   Kyrgyz-哈萨克
    lv  拉脱维亚
    lv-LV   拉脱维亚的 -拉脱维亚
    lt  立陶宛
    lt-LT   立陶宛 -立陶宛
    mk  马其顿
    mk-MK   马其顿 -FYROM
    ms  马来
    ms-BN   马来 -汶莱
    ms-MY   马来 -马来西亚
    mr  马拉地语
    mr-IN   马拉地语 -印度
    mn  蒙古
    mn-MN   蒙古 -蒙古
    no  挪威
    nb-NO   挪威 (Bokm?l) - 挪威
    nn-NO   挪威 (Nynorsk)- 挪威
    pl  波兰
    pl-PL   波兰 -波兰
    pt  葡萄牙
    pt-BR   葡萄牙 -巴西
    pt-PT   葡萄牙 -葡萄牙
    pa  Punjab 语
    pa-IN   Punjab 语 -印度
    ro  罗马尼亚语
    ro-RO   罗马尼亚语 -罗马尼亚
    ru  俄国
    ru-RU   俄国 -俄国
    sa  梵文
    sa-IN   梵文 -印度
    sr-SP-Cyrl  塞尔维亚 -(西里尔字母的) 塞尔
    sr-SP-Latn  塞尔维亚 (拉丁文)- 塞尔维亚共
    sk  斯洛伐克
    sk-SK   斯洛伐克 -斯洛伐克
    sl  斯洛文尼亚
    sl-SI   斯洛文尼亚 -斯洛文尼亚
    es  西班牙
    es-AR   西班牙 -阿根廷
    es-BO   西班牙 -玻利维亚
    es-CL   西班牙 -智利
    es-CO   西班牙 -哥伦比亚
    es-CR   西班牙 - 哥斯达黎加
    es-DO   西班牙 - 多米尼加共和国
    es-EC   西班牙 -厄瓜多尔
    es-SV   西班牙 - 萨尔瓦多
    es-GT   西班牙 -危地马拉
    es-HN   西班牙 -洪都拉斯
    es-MX   西班牙 -墨西哥
    es-NI   西班牙 -尼加拉瓜
    es-PA   西班牙 -巴拿马
    es-PY   西班牙 -巴拉圭
    es-PE   西班牙 -秘鲁
    es-PR   西班牙 - 波多黎各
    es-ES   西班牙 -西班牙
    es-UY   西班牙 -乌拉圭
    es-VE   西班牙 -委内瑞拉
    sw  Swahili
    sw-KE   Swahili-肯尼亚
    sv  瑞典
    sv-FI   瑞典 -芬兰
    sv-SE   瑞典 -瑞典
    syr Syriac
    syr-SY  Syriac-叙利亚共和国
    ta  坦米尔
    ta-IN   坦米尔 -印度
    tt  Tatar
    tt-RU   Tatar-俄国
    te  Telugu
    te-IN   Telugu-印度
    th  泰国
    th-TH   泰国 -泰国
    tr  土耳其语
    tr-TR   土耳其语 -土耳其
    uk  乌克兰
    uk-UA   乌克兰 -乌克兰
    ur  Urdu
    ur-PK   Urdu-巴基斯坦
    uz  Uzbek
    uz-UZ-Cyrl  Uzbek-(西里尔字母的) 乌兹别克
    uz-UZ-Latn  Uzbek(拉丁文)- 乌兹别克斯坦
    vi  越南
    vi-VN   越南 -越南
    

    locale

    对于 day.js 来说,同样也是实现了很多种语言的国际化,都放置在 src/locale 目录下,跟语言代码稍微有点不同的就是命名全部小写。

    由于 day.js 是按需加载的,所以在使用某种语言前需要提前引入:

    import 'dayjs/locale/zh-cn';
    
    dayjs.locale('zh-cn'); // 全局使用
    dayjs().locale('zh-cn').format(); // 当前实例使用
    

    其实 dayjs/locale/xxx.js 种保存的是对应语言的各种模板和配置,以 zh-cn.js 为例:

    // Chinese [zh]
    import dayjs from 'dayjs';
    
    // locale 对象
    const locale = {
      name: 'zh', // 对象的名,关键
      // 数组都是用 split 实现
      // weekdays 数组
      weekdays: '星期日_星期一_星期二_星期三_星期四_星期五_星期六'.split('_'),
      // 可选,简写 weekdays 数组,没有就用前 3 个字符
      weekdaysShort: '周日_周一_周二_周三_周四_周五_周六'.split('_'),
      // 可选,最简写 weekdays 数组,没有就用前 2 个字符
      weekdaysMin: '日_一_二_三_四_五_六'.split('_'),
      // months 数组
      months: '一月_二月_三月_四月_五月_六月_七月_八月_九月_十月_十一月_十二月'.split(
        '_'
      ),
      // 可选,简写 months 数组,没有就用前 3 个字符
      monthsShort: '1月_2月_3月_4月_5月_6月_7月_8月_9月_10月_11月_12月'.split('_'),
      /**
       * @description: 返回例如3周,2日
       * @param {Number} number 第几个
       * @param {String} period 单位标志
       * @return {String}
       */
      ordinal: (number, period) => {
        switch (period) {
          case 'W':
            return `${number}周`;
          default:
            return `${number}日`;
        }
      },
      // 可选,设置一周的开始,默认周日,1 代表周一
      weekStart: 1,
      // 可选,设置一年的开始周,包含1月4日的那一周作为第一周
      yearStart: 4,
      // 格式化模板
      formats: {
        LT: 'HH:mm',
        LTS: 'HH:mm:ss',
        L: 'YYYY/MM/DD',
        LL: 'YYYY年M月D日',
        LLL: 'YYYY年M月D日Ah点mm分',
        LLLL: 'YYYY年M月D日ddddAh点mm分',
        // 小写或者简写
        l: 'YYYY/M/D',
        ll: 'YYYY年M月D日',
        lll: 'YYYY年M月D日 HH:mm',
        llll: 'YYYY年M月D日dddd HH:mm',
      },
      // 相对时间的格式化模板,保正 %s %d 相同
      relativeTime: {
        future: '%s后',
        past: '%s前',
        s: '几秒',
        m: '1 分钟',
        mm: '%d 分钟',
        h: '1 小时',
        hh: '%d 小时',
        d: '1 天',
        dd: '%d 天',
        M: '1 个月',
        MM: '%d 个月',
        y: '1 年',
        yy: '%d 年',
      },
      /**
       * @description: 根据时和分返回当前的时间阶段
       * @param {Number} hour 时
       * @param {Number} minute 分
       * @return {String} 时间阶段
       */
      meridiem: (hour, minute) => {
        const hm = hour * 100 + minute;
        if (hm < 600) {
          return '凌晨';
        } else if (hm < 900) {
          return '早上';
        } else if (hm < 1130) {
          return '上午';
        } else if (hm < 1230) {
          return '中午';
        } else if (hm < 1800) {
          return '下午';
        }
        return '晚上';
      },
    };
    
    // 把 locale 对象加载到locale的 Ls 中
    dayjs.locale(locale, null, true);
    
    export default locale;
    

    除了配置以外,可以发现,最后两步中首先把 locale 对象加载并保存,然后把 locale 对象默认导出。所以虽然官方没明说,但是也可以如下导入:

    import dayjs from 'dayjs';
    import zhCN from 'dayjs/locale/zh-cn';
    
    dayjs.locale(zhCN); // 全局使用
    dayjs().locale(zhCN).format(); // 当前实例使用
    

    constant

    src/constant.js 中存放的是一些常量和正则表达式。包括不同单位包含的秒数和毫秒数、标准的时间单位表达、默认格式化模板、无效时间和两个正则表达式。

    // 计算几个常量
    // 包含的秒数
    export const SECONDS_A_MINUTE = 60;
    export const SECONDS_A_HOUR = SECONDS_A_MINUTE * 60;
    export const SECONDS_A_DAY = SECONDS_A_HOUR * 24;
    export const SECONDS_A_WEEK = SECONDS_A_DAY * 7;
    
    // 包含的毫秒数
    export const MILLISECONDS_A_SECOND = 1e3;
    export const MILLISECONDS_A_MINUTE = SECONDS_A_MINUTE * MILLISECONDS_A_SECOND;
    export const MILLISECONDS_A_HOUR = SECONDS_A_HOUR * MILLISECONDS_A_SECOND;
    export const MILLISECONDS_A_DAY = SECONDS_A_DAY * MILLISECONDS_A_SECOND;
    export const MILLISECONDS_A_WEEK = SECONDS_A_WEEK * MILLISECONDS_A_SECOND;
    
    // 标准的 unit 写法
    export const MS = 'millisecond';
    export const S = 'second';
    export const MIN = 'minute';
    export const H = 'hour';
    export const D = 'day';
    export const W = 'week';
    export const M = 'month';
    export const Q = 'quarter';
    export const Y = 'year';
    export const DATE = 'date';
    
    // 默认时间格式是ISO 2020-12-06T20:11:43Z
    export const FORMAT_DEFAULT = 'YYYY-MM-DDTHH:mm:ssZ';
    
    // 无效时间
    export const INVALID_DATE_STRING = 'Invalid Date';
    
    // 正则表达式
    export const REGEX_PARSE = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[^0-9]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?.?(\d+)?$/;
    export const REGEX_FORMAT = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g;
    

    这两个正则表达式比较有意思,第一个正则表达式 REGEX_PARSE 是用来解析字符串格式的时间,便于生成 Dayjs 实例关联的 Date 对象;第二个正则表达式 REGEX_FORMAT 用于解析 format 参数,返回想要的时间格式。

    utils

    src/utils.js 中存放的是一些工具函数。其实在 index.js 中也放置了很多工具函数,只不过那些工具函数需要用到一些 index.js 的全局变量,所以不能定义在 utils.js 中。但是在 index.js 中最后还是把它们放在了一个 Utils 对象里共同管理。

    // C 是定义的常量 constant
    import * as C from './constant.js';
    
    /**
     * @description: 在 string 的开头补充 pad,直到长度为 length,相当于`string.padStart(length, pad)`
     * @param {String} string 被补充的字符串
     * @param {Number} length 最后的长度
     * @param {String} pad 填充的内容
     * @return {String} 补充后的字符串
     */
    const padStart = (string, length, pad) => {
      const s = String(string);
      if (!s || s.length >= length) return string;
      // 前面的数组join更简单的表示就是 `pad.repeat(length - string.length)`
      return `${Array(length + 1 - s.length).join(pad)}${string}`;
    };
    
    /**
     * @description: 返回实例的UTC偏移量(分钟)转化成的 [+|-]HH:mm的格式
     * @param {Dayjs} instance Dayjs的实例
     * @return {String} UTC偏移量 格式:[+|-]HH:mm
     */
    const padZoneStr = (instance) => {
      // 这里感觉用Number(instance.utcOffset())会更易读
      const negMinutes = -instance.utcOffset();
      const minutes = Math.abs(negMinutes);
      const hourOffset = Math.floor(minutes / 60);
      const minuteOffset = minutes % 60;
      return `${negMinutes <= 0 ? '+' : '-'}${padStart(
        hourOffset,
        2,
        '0'
      )}:${padStart(minuteOffset, 2, '0')}`;
    };
    
    /**
     * @description: 求两个实例的月份差
     * @param {Dayjs} a Dayjs的实例
     * @param {Dayjs} b Dayjs的实例
     * @return {Number} 返回两个实例的月份差
     */
    const monthDiff = (a, b) => {
      // 来自moment.js的函数,保证能返回相同的结果
      if (a.date() < b.date()) return -monthDiff(b, a);
      const wholeMonthDiff = (b.year() - a.year()) * 12 + (b.month() - a.month());
      const anchor = a.clone().add(wholeMonthDiff, C.M);
      const c = b - anchor < 0;
      const anchor2 = a.clone().add(wholeMonthDiff + (c ? -1 : 1), C.M);
      return +(
        -(
          wholeMonthDiff +
          (b - anchor) / (c ? anchor - anchor2 : anchor2 - anchor)
        ) || 0
      );
    };
    
    /**
     * @description: 向 0 取整
     * @param {Number} n 要取整的数字
     * @return {Number} 取整后的数字
     */
    const absFloor = (n) => (n < 0 ? Math.ceil(n) || 0 : Math.floor(n));
    
    /**
     * @description: 返回 u 对应的单位,能自动适配标准格式和缩写格式
     * @param {String} u M(month) y(year) w(week) d(day) D(date) h(hour) m(minute) s(second) ms(millisecond) Q(quarter) 或 其他字符串
     * @return {String} u 对应的单位
     */
    const prettyUnit = (u) => {
      const special = {
        M: C.M,
        y: C.Y,
        w: C.W,
        d: C.D,
        D: C.DATE,
        h: C.H,
        m: C.MIN,
        s: C.S,
        ms: C.MS,
        Q: C.Q,
      };
      return (
        // 返回 u 对应的单位
        special[u] ||
        // 或者是把 u 结尾的 字符s 删除,当作单位
        String(u || '')
          .toLowerCase()
          .replace(/s$/, '')
      );
    };
    
    /**
     * @description: 判断是否为 undefined
     * @param {Any} s
     * @return {Boolean} true: 是, false: 否
     */
    const isUndefined = (s) => s === undefined;
    
    export default {
      s: padStart,
      z: padZoneStr,
      m: monthDiff,
      a: absFloor,
      p: prettyUnit,
      u: isUndefined,
    };
    

    本篇内容完成,下一篇文章来分析 day.js 的核心 src/index.js 文件,学习 Dayjs 类的实现。


    前端记事本,不定期更新,欢迎关注!

    • 微信公众号: 林景宜的记事本
    • 博客:林景宜的记事本
    • 掘金专栏:林景宜的记事本
    • 知乎专栏: 林景宜的记事本
    • Github: MageeLin


    起源地下载网 » dayjs源码解析(一):概念、locale、constant、utils

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元