dayjs 分享

<p style='text-align: center; color: #599FF0;'>Dayjs </p>

分享內(nèi)容

  1. 框架結(jié)構(gòu)
  2. 涉及技術(shù)
  3. 部分代碼解讀
  4. 總結(jié)

概述

Day.js 是一個(gè)輕量的處理時(shí)間和日期的 JavaScript 庫(kù),和 Moment.js 的 API 設(shè)計(jì)保持完全一樣. 如果你曾經(jīng)用過(guò) Moment.js, 那么你已經(jīng)知道如何使用 Day.js

優(yōu)點(diǎn)

??  和Moment.js有著相同的API和模式
??  不可變拍摇、持久性
??  提供鏈?zhǔn)秸{(diào)用
??  國(guó)際化標(biāo)準(zhǔn)
??  超小的壓縮體積亮钦,僅僅有2kb左右
??  極大多數(shù)的瀏覽器兼容

目錄:

    build/                              -> 構(gòu)建工具
        index.js                         -> 入口文件
        rollup.config.js             -> rollup配置文件
    dosc/                               -> 文檔
        en                               -> 英文
        ...                                -> 等等
    src/                                   -> 源代碼
        locale/                        -> 各種語(yǔ)言
            zh-cn.js                     -> 中文
            ...                            -> 等等
        plugin/                       -> 內(nèi)部提供插件
            advancedFormat/  -> 特色格式化
                index.js                 -> index.js
            buddhistEra/            -> 支持農(nóng)歷
                index.js                 -> ..
            isLeapYear/           -> 是否為閏年
                index.js                 -> ..
            relativeTime/           -> 相對(duì)時(shí)間方法集合,包括 to授翻、from或悲、toNow孙咪、fromNow
                index.js                 -> ..
        constant.js                 -> 默認(rèn)常量堪唐,包括時(shí)間單位,正則
        index.js                         -> 主文件
        util.js                          -> 工具方法
    test/                                 -> jest自動(dòng)化測(cè)試工具
        ...                                -> 測(cè)試用例
    .babelrc                             -> babel配置
    .eslintrc.json                    -> eslint配置
    .gitignore                        -> git忽略
    .npmignore                     -> npm忽略
    .travis.yml                      -> travis配置
    CHANGELOG.md              -> 自動(dòng)輸出的更新日志
    CONTRIBUTING.md       -> 貢獻(xiàn)文檔
    LICENSE                         -> 協(xié)議
    README.md                    -> readme
    index.d.ts                        -> ts靜態(tài)檢查聲明文件
    karma.saucs.conf.js        -> karma配置文件
    package.json                     -> 包配置

涉及技術(shù):

名稱 概述
rollup JavaScript 模塊打包器翎蹈,dayjs中僅僅用來(lái)打包淮菠,并沒(méi)有用到太多的rollup特性,比如 Tree Shaking
jest JavaScript 測(cè)試框架
Karma 測(cè)試過(guò)程管理工具荤堪,通常用于瀏覽器兼容
travis 持續(xù)集成服務(wù)合陵,與github綁定,倉(cāng)庫(kù)中只要有新的代碼澄阳,就會(huì)自動(dòng)抓取拥知。然后,提供一個(gè)運(yùn)行環(huán)境碎赢,執(zhí)行測(cè)試低剔,構(gòu)建,并且部署到服務(wù)器。
semantic-release 版本管理襟齿,自動(dòng)化發(fā)布
typescript javascript超集姻锁,用來(lái)提供類型檢查
eslint 代碼檢查
bebel 代碼編譯

rollup配置:

// rollup.config.js
const babel = require('rollup-plugin-babel');
const uglify = require('rollup-plugin-uglify');

module.exports = config => {
    const { input, fileName, name } = config;
    return {
        input: {
            input,
            external: ['dayjs'], // 外部文件,不進(jìn)行打包
            plugins: [
                babel({
                    exclude: 'node_modules/**'
                }),
                uglify()
            ]
        },
        output: {
            file: fileName,
            format: 'umd', // 采用umd規(guī)范
            name: name || 'dayjs',
            // 重命名全局命名
            globals: { 
                dayjs: 'dayjs'
            }
        }
    };
};

// index.js核心代碼
async function build(option) {
    const bundle = await rollup.rollup(option.input);
    await bundle.write(option.output);
}

(async () => {
    try {
        // 打包 locale
        const locales = await promisifyReadDir(
            path.join(__dirname, '../src/locale')
        );
        locales.forEach(l => {
            build(
                configFactory({
                    input: `./src/locale/${l}`,
                    fileName: `./locale/${l}`,
                    name: `dayjs_locale_${formatName(l)}`
                })
            );
        });

        // 打包plugins
        const plugins = await promisifyReadDir(
            path.join(__dirname, '../src/plugin')
        );
        plugins.forEach(l => {
            build(
                configFactory({
                    input: `./src/plugin/${l}`,
                    fileName: `./plugin/${l}`,
                    name: `dayjs_plugin_${formatName(l)}`
                })
            );
        });

        // 打包index
        build(
            configFactory({
                input: './src/index.js',
                fileName: './dayjs.min.js'
            })
        );
    } catch (e) {
        console.error(e); // eslint-disable-line no-console
    }
})();

rollup-cli: https://github.com/a13821190779/rollup-cli


src部分

constant.js:內(nèi)是一些英文語(yǔ)義變量的定義

utils.js:包含一些常用的工具方法猜欺。
    
    padStart(string, length, pad): 補(bǔ)充前綴
    padZoneStr(negMinuts): 轉(zhuǎn)換時(shí)間格式為hh(hour):mm(minute) => 為了轉(zhuǎn)換格林威治時(shí)間
    monthDiff(a, b): 返回月份差位隶,基于a
    absFloor(n):忽略符號(hào)的Math.floor
    prettyUnit(units): 統(tǒng)一化單位 units => unit
    isUndefined(s):是否為undefined
//utils.js中的monthDiff,精度 <= 月 
const monthDiff = (a, b) => {
    // function from moment.js in order to keep the same result
    // 代碼優(yōu)化來(lái)自moment.js开皿,反而更難理解了涧黄。
    
    // 月份差
    const wholeMonthDiff = (b.year() - a.year()) * 12 + (b.month() - a.month());
    
    // 同化年月
    const anchor = a.clone().add(wholeMonthDiff, 'months');
    
    // 比day的大小
    const c = b - anchor < 0;  // => b < anchor
    
        
    // 錨點(diǎn)2
    const anchor2 = a.clone().add(wholeMonthDiff + (c ? -1 : 1), 'months');
    
    // 返回精度至day的時(shí)間差
    return Number(
        -(
            wholeMonthDiff +
            (b - anchor) / (c ? anchor - anchor2 : anchor2 - anchor)
        )
    );
};

index.js

    export default dayjs
    let L = 'en' // global locale
    const Ls = {} // global loaded locale
    const isDayjs = d => d instanceof Dayjs;
    
    const dayjs = (date, c) => {
            if (isDayjs(date)) {
                return date.clone()
            }
            const cfg = c || {}
            cfg.date = date
            return new Dayjs(cfg) // eslint-disable-line no-use-before-define
        }
// parseDate方法
    const parseDate = (date) => {
            let reg
            if (date === null) return new Date(NaN) // => Invalid Date
            if (Utils.isUndefined(date)) return new Date()
            if (date instanceof Date) return date
            if ((typeof date === 'string')
                && (/.*[^Z]$/i.test(date)) // Z代表格林威治時(shí)間和本地時(shí)間之間的時(shí)差
                && (reg = date.match(C.REGEX_PARSE))) { // ^(\d{4})-?(\d{1,2})-?(\d{0,2})(.*?(\d{1,2}):(\d{1,2}):(\d{1,2}))?.?(\d{1,3})?$/ 見(jiàn)下圖
// 結(jié)果結(jié)構(gòu) => ["整體", "括號(hào)1的匹配", "括號(hào)2的匹配", "括號(hào)3的匹配", .....]
                return new Date(
                    reg[1], reg[2] - 1, reg[3] || 1,
                    reg[5] || 0, reg[6] || 0, reg[7] || 0, reg[8] || 0
                )
            }
            return new Date(date) // timestamp
        }

// parseLocale方法
const parseLocale = (preset, object, isLocal) => {
            let l
            if (!preset) return null
            if (typeof preset === 'string') {
                if (Ls[preset]) {
                    l = preset
                }
                if (object) {
                    Ls[preset] = object
                    l = preset
                }
            } else {
                const { name } = preset
                Ls[name] = preset
                l = name
            }
            if (!isLocal) L = l
            return l
        }

[圖片上傳失敗...(image-e077f8-1531296814278)]

很方便的正則可視化工具

// Dayjs類
    class Dayjs {
            constructor(cfg) {
                this.parse(cfg)
            }

            parse(cfg) {
                this.$d = parseDate(cfg.date)
                this.init(cfg)
            }

            init(cfg) {
                this.$y = this.$d.getFullYear()
                this.$M = this.$d.getMonth()
                this.$D = this.$d.getDate()
                this.$W = this.$d.getDay()
                this.$H = this.$d.getHours()
                this.$m = this.$d.getMinutes()
                this.$s = this.$d.getSeconds()
                this.$ms = this.$d.getMilliseconds()
                this.$L = this.$L || parseLocale(cfg.locale, null, true) || L
            }

            $utils() {}
            isValid() {}
            isLeapYear() {}
            $compare(that) {}
            isSame(that) {}
            isBefore(that) {}
            isAfter(that) {}
            year() {}
            month() {}
            day() {}
            date() {}
            hour() {}
            minute() {}
            second() 
            millisecond() {}
            unix() {}
            valueOf() {}
            daysInMonth() {}
            $locale() {}
            locale(preset, object) {}
            clone() {}
            toDate() {}
            toArray() {}
            toJSON() {}
            toISOString() {}
            toObject() {}
            toString() {}
            startOf(units, startOf) {}
            endOf(arg) {}
            $set(units, int) {}
            set(string, int) {}
            add(number, units) {}
            subtract(number, string) {}
            format(formatStr) {}

            diff(input, units, float) {
                const unit = Utils.prettyUnit(units)
                const that = dayjs(input)
                const diff = this - that
                let result = Utils.monthDiff(this, that)
                switch (unit) {
                    case C.Y:
                        result /= 12
                        break
                    case C.M:
                        break
                    case C.Q:
                        result /= 3
                        break
                    case C.W:
                        result = diff / C.MILLISECONDS_A_WEEK
                        break
                    case C.D:
                        result = diff / C.MILLISECONDS_A_DAY
                        break
                    case C.H:
                        result = diff / C.MILLISECONDS_A_HOUR
                        break
                    case C.MIN:
                        result = diff / C.MILLISECONDS_A_MINUTE
                        break
                    case C.S:
                        result = diff / C.MILLISECONDS_A_SECOND
                        break
                    default: // milliseconds
                        result = diff
                }
                return float ? result : Utils.absFloor(result)
            }
        }

總結(jié)

  1. 結(jié)構(gòu)簡(jiǎn)潔,清晰副瀑,在不失可用性的前提下弓熏,盡可能的簡(jiǎn)化體積
  2. 代碼抽離徹底,無(wú)重復(fù)糠睡,無(wú)強(qiáng)耦合
  3. 使用了自動(dòng)化測(cè)試挽鞠,和版本控制、發(fā)布狈孔,大大解放人力

在閱讀過(guò)程中信认,學(xué)到了如何規(guī)劃中小型框架結(jié)構(gòu),如何使用自動(dòng)化工具均抽,也掌握了關(guān)于時(shí)間的一些細(xì)節(jié)嫁赏,比如格林威治時(shí)間。
當(dāng)然油挥,dayjs也不是完美的潦蝇,也有一些地方可以簡(jiǎn)化、優(yōu)化深寥,比如 undefined 替換為 void 0 以節(jié)約字節(jié)(rollup會(huì)將undefined替換為void 0)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末攘乒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子惋鹅,更是在濱河造成了極大的恐慌则酝,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闰集,死亡現(xiàn)場(chǎng)離奇詭異沽讹,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)武鲁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)爽雄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人沐鼠,你說(shuō)我怎么就攤上這事挚瘟。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵刽沾,是天一觀的道長(zhǎng)本慕。 經(jīng)常有香客問(wèn)我,道長(zhǎng)侧漓,這世上最難降的妖魔是什么锅尘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮布蔗,結(jié)果婚禮上藤违,老公的妹妹穿的比我還像新娘。我一直安慰自己纵揍,他們只是感情好顿乒,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著泽谨,像睡著了一般璧榄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吧雹,一...
    開(kāi)封第一講書(shū)人閱讀 49,741評(píng)論 1 289
  • 那天骨杂,我揣著相機(jī)與錄音,去河邊找鬼雄卷。 笑死搓蚪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的丁鹉。 我是一名探鬼主播妒潭,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼揣钦!你這毒婦竟也來(lái)了雳灾?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拂盯,失蹤者是張志新(化名)和其女友劉穎佑女,沒(méi)想到半個(gè)月后记靡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體谈竿,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年摸吠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了空凸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡寸痢,死狀恐怖呀洲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤道逗,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布兵罢,位于F島的核電站,受9級(jí)特大地震影響滓窍,放射性物質(zhì)發(fā)生泄漏卖词。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一吏夯、第九天 我趴在偏房一處隱蔽的房頂上張望此蜈。 院中可真熱鬧,春花似錦噪生、人聲如沸裆赵。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)战授。三九已至,卻和暖如春桨嫁,著一層夾襖步出監(jiān)牢的瞬間陈醒,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工瞧甩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留钉跷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓肚逸,卻偏偏與公主長(zhǎng)得像爷辙,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子朦促,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理膝晾,服務(wù)發(fā)現(xiàn),斷路器务冕,智...
    卡卡羅2017閱讀 134,628評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,732評(píng)論 25 707
  • 第2章 基本語(yǔ)法 2.1 概述 基本句法和變量 語(yǔ)句 JavaScript程序的執(zhí)行單位為行(line)血当,也就是一...
    悟名先生閱讀 4,131評(píng)論 0 13
  • 《你的知識(shí)需要管理》田志剛 這是彭小六知識(shí)管理訓(xùn)練營(yíng)推薦的一本書(shū)。 在這個(gè)知識(shí)共享禀忆、知識(shí)付費(fèi)的大熱時(shí)代臊旭,懂得梳...
    野貍閱讀 316評(píng)論 0 0
  • 心之淤泥而不染离熏,但現(xiàn)實(shí)卻是人在屋檐下,哪有不低頭〈骼裕現(xiàn)實(shí)的社會(huì)不是電視劇里完美的感情滋戳,而是金錢(qián)钻蔑、地位大于一切,畢竟俗...
    無(wú)人問(wèn)及閱讀 547評(píng)論 0 0