<p style='text-align: center; color: #599FF0;'>Dayjs </p>
分享內(nèi)容
- 框架結(jié)構(gòu)
- 涉及技術(shù)
- 部分代碼解讀
- 總結(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
}
})();
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é)
- 結(jié)構(gòu)簡(jiǎn)潔,清晰副瀑,在不失可用性的前提下弓熏,盡可能的簡(jiǎn)化體積
- 代碼抽離徹底,無(wú)重復(fù)糠睡,無(wú)強(qiáng)耦合
- 使用了自動(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)