<p style='text-align: center; color: #599FF0;'>Dayjs </p>
分享内容
框架结构
涉及技术
部分代码解读
总结
概述
Day.js 是一个轻量的处理时间和日期的 JavaScript 库,和 Moment.js 的 API 设计保持完全一样. 如果你曾经用过 Moment.js, 那么你已经知道如何使用 Day.js
优点
和Moment.js有着相同的API和模式 不可变、持久性 提供链式调用 国际化标准 超小的压缩体积,仅仅有2kb左右 极大多数的浏览器兼容
目录:
build/ -> 构建工具 index.js -> 入口文件 rollup.config.js -> rollup配置文件 dosc/ -> 文档 en -> 英文 ... -> 等等 src/ -> 源代码 locale/ -> 各种语言 zh-cn.js -> 中文 ... -> 等等 plugin/ -> 内部提供插件 advancedFormat/ -> 特色格式化 index.js -> index.js buddhistEra/ -> 支持农历 index.js -> .. isLeapYear/ -> 是否为闰年 index.js -> .. relativeTime/ -> 相对时间方法集合,包括 to、from、toNow、fromNow index.js -> .. constant.js -> 默认常量,包括时间单位,正则 index.js -> 主文件 util.js -> 工具方法 test/ -> jest自动化测试工具 ... -> 测试用例 .babelrc -> babel配置 .eslintrc.json -> eslint配置 .gitignore -> git忽略 .npmignore -> npm忽略 .travis.yml -> travis配置 CHANGELOG.md -> 自动输出的更新日志 CONTRIBUTING.md -> 贡献文档 LICENSE -> 协议 README.md -> readme index.d.ts -> ts静态检查声明文件 karma.saucs.conf.js -> karma配置文件 package.json -> 包配置
涉及技术:
| 名称 | 概述 |
|---|---|
| rollup | JavaScript 模块打包器,dayjs中仅仅用来打包,并没有用到太多的rollup特性,比如 Tree Shaking |
| jest | JavaScript 测试框架 |
| Karma | 测试过程管理工具,通常用于浏览器兼容 |
| travis | 持续集成服务,与github绑定,仓库中只要有新的代码,就会自动抓取。然后,提供一个运行环境,执行测试,构建,并且部署到服务器。 |
| semantic-release | 版本管理,自动化发布 |
| typescript | javascript超集,用来提供类型检查 |
| eslint | 代码检查 |
| bebel | 代码编译 |
rollup配置:
// rollup.config.jsconst babel = require('rollup-plugin-babel');const uglify = require('rollup-plugin-uglify');module.exports = config => { const { input, fileName, name } = config; return { input: {
input, external: ['dayjs'], // 外部文件,不进行打包
plugins: [
babel({ exclude: 'node_modules/**'
}),
uglify()
]
}, output: { file: fileName, format: 'umd', // 采用umd规范
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:内是一些英文语义变量的定义
utils.js:包含一些常用的工具方法。
padStart(string, length, pad): 补充前缀 padZoneStr(negMinuts): 转换时间格式为hh(hour):mm(minute) => 为了转换格林威治时间 monthDiff(a, b): 返回月份差,基于a absFloor(n):忽略符号的Math.floor prettyUnit(units): 统一化单位 units => unit isUndefined(s):是否为undefined
//utils.js中的monthDiff,精度 <= 月 const monthDiff = (a, b) => { // function from moment.js in order to keep the same result
// 代码优化来自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
// 锚点2
const anchor2 = a.clone().add(wholeMonthDiff + (c ? -1 : 1), 'months');
// 返回精度至day的时间差
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代表格林威治时间和本地时间之间的时差
&& (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})?$/ 见下图// 结果结构 => ["整体", "括号1的匹配", "括号2的匹配", "括号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)
}
}总结
结构简洁,清晰,在不失可用性的前提下,尽可能的简化体积
代码抽离彻底,无重复,无强耦合
使用了自动化测试,和版本控制、发布,大大解放人力
在阅读过程中,学到了如何规划中小型框架结构,如何使用自动化工具,也掌握了关于时间的一些细节,比如格林威治时间。
当然,dayjs也不是完美的,也有一些地方可以简化、优化,比如 undefined 替换为 void 0 以节约字节(rollup会将undefined替换为void 0)
作者:大西瓜叔叔
链接:https://www.jianshu.com/p/9f94c9caf09f
随时随地看视频