@instaceof/b-util
v1.1.1
Published
common use js tools lib
Downloads
3
Maintainers
Readme
##结构
.eslintignore eslint 验证过滤
Install
npm install @instaceof/b-util -D
//1、
import {isArray,isString} from '@instaceof/b-util';
console.log(isArray([])); // ture
console.log(isString('String')); // ture
//2、
import B from '@instaceof/b-util';
console.dir(B);
console.log(B.isArray([])); // ture
console.log(B.isString('String')); // ture
//3、
import isArray from '@instaceof/b-util/isArray'; // [each,inArray,index,lastIndex,addDays,addYear,formatDate,getweek,now,nowTick,later]
import isString from '@instaceof/b-util/isString';
console.log(isArray([])); // ture
console.log(isString('String')); // ture
//4、
import isNo from '@instaceof/b-util/isNo'; // [arrayModel,dateModel,functionModel,isNo,link,media,objectModel,string,validate]
console.log(isNo)
console.log(isNo.isArray([])); // ture
console.log(isNo.isString('String')); // ture
examples
src
├── arrayModel
│ ├── each.js
│ ├── inArray.js
│ ├── index.js
│ └── lastIndex.js
├── dateModel
│ ├── addDays.js
│ ├── addYear.js
│ ├── formatDate.js
│ ├── getweek.js
│ ├── now.js
│ └── nowTick.js
├── functionModel
│ └── later.js
├── isNo
│ ├── is.js
│ ├── isArray.js
│ ├── isBoolean.js
│ ├── isDate.js
│ ├── isEmptyObject.js
│ ├── isFunction.js
│ ├── isHttps.js
│ ├── isNull.js
│ ├── isNumber.js
│ ├── isObject.js
│ ├── isRegExp.js
│ ├── isString.js
│ └── isUndefined.js
├── link
│ ├── cookie.js
│ ├── localStorage.js
│ ├── sessionStorage.js
│ ├── uri.js
│ ├── urlAddParam.js
│ ├── urlDecode.js
│ └── urlEncode.js
├── media
│ └── UA.js
├── objectModel
│ ├── _mix.js
│ ├── cleanObject.js
│ ├── clone.js
│ ├── keys.js
│ └── mix.js
├── string
│ └── strTemplate.js
└── validate
├── isBankCard.js
├── isIdCard.js
├── isPhoneNum.js
└── validateMobile.js
API
├── arrayModel
/**
* 循环操作
* @param object
* @param fn
* @param context
* @returns {*}
*/
│ ├── each.js
/**
* 检查是否包含在数组中
* @param item
* @param arr
* @returns {boolean}
* inArray(item,arr)
*/
│ ├── inArray.js
/**
* 检查指定的参数在数组中的位置 不存在 返回-1
* @param item
* @param arr
* @param fromIndex
* @returns {*}
* index(item, arr, fromIndex)
**/
│ ├── index.js
/**
* 与 index类似 arr倒序计算位置
* @param item
* @param arr
* @param fromIndex
* @returns {*}
* lastIndex (item, arr, fromIndex)
*/
│ └── lastIndex.js
├── dateModel
/**
* 添加天数
* @param date
* @param days
* @returns {*}
* addDays(date, days)
*/
│ ├── addDays.js
/**
* 添加年
* @param date
* @param days
* @returns {*}
* addYear(date, year)
*/
│ ├── addYear.js
/**
* 格式化时间
* @param date
* @param strFormat //yyyy.MM.dd hh:mm:ss q 月(M)、日(d)、小时(h)、分(m) 、秒(s)、 毫秒(S) 季度(q)
* @param sms 默认秒 // ms 毫秒 s 秒
* @returns {*}
* formatDate(date, strFormat, sms)
*/
│ ├── formatDate.js
/**
* 获取当前星期日期
* @param date
* @param isChEn // 默认中文 'en' 英文
* @returns {string}
* getweek (date, isChEn)
*/
│ ├── getweek.js
/**
* 现在的时间
* @returns {Date}
* now()
*/
│ ├── now.js
/**
* 现在的时间
* @returns {Date}
* now()
*/
│ └── nowTick.js
├── functionModel
/**
* 定时器
* @param method
* @param time
* @param isInterval
* @param context
* @param data
* @returns {{cancel: cancel, timer: number, isInterval: *}}
* timer:0
* timer= later(()=>{console.log(500) }, 500); // 500毫秒后执行
* timer= later(() => {}, 1000, true); // 1000秒 循环执行
* timer.cancel(); // 关闭定时器
*/
│ └── later.js
├── isNo
│ ├── is.js
/**
* 数组类型判断
* @param obj
* @returns {*|boolean}
* isArray(obj)
*/
│ ├── isArray.js
/**
* 布尔类型判断
* @param obj
* @returns {boolean|*|Boolean}
* isBoolean(obj)
*/
│ ├── isBoolean.js
/**
* 时间类型判断
* @param obj
* @returns {boolean|*|Boolean}
* isDate(obj)
*/
│ ├── isDate.js
/**
* 对象是否为空判断
* @param obj
* @returns {boolean|*}
* isEmptyObject(obj)
*/
│ ├── isEmptyObject.js
/**
* fun 类型判断
* @param obj
* @returns {*|boolean}
* isFunction(obj)
*/
│ ├── isFunction.js
/**
* 判断是否是https地址
* @param obj // 默认 window.document.location.protocol
* @returns {boolean}
* isHttps(obj)
*/
│ ├── isHttps.js
/**
* null类型判断
* @param obj
* @returns {*|boolean}
* isNull(obj)
*/
│ ├── isNull.js
/**
* 数字类型判断
* @param obj
* @returns {*|boolean}
* isNumber(obj)
*/
│ ├── isNumber.js
/**
* 对象类型判断
* @param obj
* @returns {*|boolean}
* isObject(obj)
*/
│ ├── isObject.js
/**
* 是否是正则表达式判断
* @param obj
* @returns {*|boolean}
* isRegExp(obj)
*/
│ ├── isRegExp.js
/**
* 字符串类型判断
* @param obj
* @returns {*|boolean}
* isString(obj)
*/
│ ├── isString.js
/**
* undefined 类型判断
* @param obj
* @returns {*|boolean}
* isUndefined(obj)
*/
│ └── isUndefined.js
├── link
/**
* cookie操作
* @type {{set: cookie.set, get: cookie.get, clear: cookie.clear}}
* cookie.set(name, value, minutes, domain)
* cookie.get(name)
* cookie.clear(name, domain)
*/
│ ├── cookie.js
/**
* 本地储存-浏览器关闭销毁
* @type {{set: set, get: (function(*): any|string), clear: clear}}
* sessionStorage.set(name, val)
* sessionStorage.get(name)
* sessionStorage.clear(name)
*/
│ ├── localStorage.js
/**
* 本地储存-永久
* @type {{set: set, get: (function(*=): any|string), clear: clear}}
* localStorage.set(name, val)
* localStorage.get(name)
* localStorage.clear(name)
*/
│ ├── sessionStorage.js
/**
* 获取页面参数
* @param url // 默认 location.href
* @returns {*}
* uri(url)
* uri() // 全部参数
* uri().name // 获取指定name参数
*/
│ ├── uri.js
/**
* 传入对象返回url参数
* @param data
* @param iSquestionMark // true 默认带 ? false 不带?
* @returns {string|string|string}
* urlAddParam({url1:'url1',url2:'url2'})
*/
│ ├── urlAddParam.js
/**
* url解码
* @param url
* @param {String} url 这是url的一部分被解码.
* @return {String} 解码的url部分字符串
* urlAddParam=(data, iSquestionMark=true)
* urlAddParam({url1:'url1',url2:'url2'})
*/
│ ├── urlDecode.js
/**
* url编码
* @param url
* @return {String} 已编码的url部分字符串
* urlEncode(url)
*/
│ └── urlEncode.js
├── media
/**
* 终端识别
* @type {{trident: undefined, opera: undefined, chrome: undefined, safari: undefined, os: undefined, phantomjs: undefined, firefox: undefined, android: undefined, mobile: undefined, nodejs: undefined, webkit: undefined, ios: undefined, iphone: undefined, ipod: undefined, core: undefined, gecko: undefined, shell: undefined, ipad: undefined, ie: undefined, presto: undefined, ieMode: undefined}}
* UA()
*/
│ └── UA.js
├── objectModel
/**
* 对象混合
* @param target
* @param resource
* @private
* _mix(target, resource)
*/
│ ├── _mix.js
/**
* 扩展
* @param target 当前对象
* @param resource 资源对象
* @param overwrite 是否重写
* @param whiteList 白名单
* @param deep 是否深度复制
* mix (target, resource, overwrite, whiteList, deep)
*/
│ ├── mix.js
/**
* 去除对象中value为空(null,undefined,'')的属性,举个栗子:
* @param object
* @returns {{}|*}
* B.cleanObject({"name":'','go':'123456'}
*/
│ ├── cleanObject.js
/**
* 克隆对象
* @param obj
* @returns {*}
* clone(obj)
*/
│ ├── clone.js
/**
* 获取对象属性名
* keys(obj)
*/
│ └── keys.js
├── string
/**
* 待删除
* 替换模块字符串中的占位符
* ,例如s = <span id="aaa">{{a}}</span>
* val = {a:111}
* 结果为:<span id="aaa">111</span>
* 如果,
* val = [{a:111}, {a:222}]
* split = <br/>
* 结果为:
* <span id="aaa">111</span><br/><span id="aaa">222</span>
*
* @param {string} s 模板字符串,例如<span>{{b}}</span>
* @param {Object|array} val 值
* @param {string} split 数组分割字符
*/
│ └── strTemplate.js
└── validate
/**
* 国际标准的银行卡号(储蓄卡和信用卡),注意:不包括各个银行的企业账户
* @param str
* @returns {boolean}
* isIdCard(str)
*/
├── isBankCard.js
/**
* 中国的身份证号码
* isIdCard(str)
*/
├── isIdCard.js
/**
* 中国三大运营商的电话号码
* isPhoneNum(str)
*/
├── isPhoneNum.js
/**
* 手机号码格式验证
* @param rule
* @param value
* @param callback
* validateMobile(rule, value, callback)
*/
└── validateMobile.js
bai-util.js
A small JavaScript utility library.
Package with rollup.js
, exported as iife amd cmd cjs umd es6
module.
Support most modern browsers, not include IE
. Support Node 8+.
框架搭建教程:打造一个类似于lodash的现代前端工具库
API
License
Released under the MIT Licenses.
参考博客:https://www.dgg.red/
一、分析借鉴目前最主流的前端工具库
我分析了github上多个前端工具库的设计,以lodash为例说明:
lodash的工程,有master、npm、npm-package、amd分支,以及 多种类型的tag:
4.17.10(umd)
4.17.10-npm
4.17.10-es
4.17.10-amd
这 4个分支 + 4个tag 的代码甚至工程结构,都不太一样。
我目前还看不明白它这么多套代码,是分开维护的,还是只维护一套,其他都是自动生成的?
因为所有8个分支的配置我都检查过了,并没有发现自动生成多份代码的相关配置。
而且lodash工程里面并没有babel的配置,难道lodash各个版本的代码是分开维护的?
本来想模仿lodash的工程脚手架,无奈看了半天看不懂。但是它的功能我基本清楚了:
1、umd模块,分为 lodash-core.js 和 lodash.js 、lodash-fp.js 三部分,提供了 未压缩包和压缩包(貌似没有看到source map)。
lodash-fp 即 (Functional Programming)函数式编程,对于_.map等部分方法,lodash提供了FP版本。
2、npm模块(也是CommonJS版),有各个主功能模块(比如array),都是require和module.export形式,除了lodash.js提供了min压缩版,其他都是源码无压缩。
3、AMD版,全部都是源码无压缩,有主功能模块(比如array),都是define引入/导出模式,但是没有lodash.js统一入口模块。
4、es模块,全部都是源码无压缩,主功能模块全部提供了 default引入形式(例如array.default.js)以便支持 import array from 'lodash/array'。
5、npm模块,分成很多子项目,每一个函数都可以单独install。
除了lodash,我还分析了vue和react的工程构建方式,并学习了rollup、jest等。
二、设计前端工具库
我想达到的目的:
1、要支持 es6、node-cjs 和 浏览器 script直接引入,暂时不单独考虑支持amd;
2、只维护一套代码,其他均有工具自动生成;
3、浏览器版本的,支持source map,其他版本都是源码形式提供;
4、cjs、es6版本,支持按函数和主功能模块(例如array、date)引入,es6版本还提供对import xxx 这种形式的支持;
5、浏览器版本的,暂时不分模块,只能支持全部引入,后面根据情况可以提供core等精简版本。
关于源码的书写和维护:
目前大多数工具库,都是用的es6语法,我看vue源码是用的带type类型的es6(好像叫flow),lodash源码好像是es6(master分支),而react用的好像也是自家的flow,只有谷歌的angular2用的typescript。
所以我考虑采用 es6来编写,但不排除以后换成ts。
具体做法:
使用es6语法来编写工具类,并把源代码原原本本提供出去,同时生成一份umd格式的,包含源码、压缩包和source map。
整个target(dist)内容示例如下:
// 所有函数
isArray.js
isString.js
...
collection.js // 主功能模块
collection.default.js // xxx.default.js用于支持es6的直接import
...
bai-util.esm.js // 主模块es源码,相当于 index.js、main.js
bai-util.esm.default.js // xxx.default.js用于支持es6的直接import xxx from 'xxx.js',并且用它来生成umd版本
bai-util.js // umd版本的源码,根据bai-util.esm.default.js自动生成
bai-util.min.js // umd版本压缩后的代码
bai-util.min.map // umd版本压缩代码的source map
xxx.default.js 说明
以collection.js为例:
collection.default.js代码如下:
import toMapKey from './toMapKey.js';
import toMapValue from './toMapValue.js';
// 支持单个引入 import { toMapKey, toMapValue } from 'collection.default'
// 也可以像这样全部引入 import * as coll from 'collection.default';
export default {
toMapKey,
toMapValue,
}
collection.js代码如下:
// 支持单个引入 import { toMapKey, toMapValue } from 'collection'
export { default as toMapKey } from './toMapKey.js';
export { default as toMapValue } from './toMapValue.js';
// 支持default(默认)引入 import coll from 'collection'
export { default } from './collection.default.js';
即 collection.default.js 只是供 collection.js 来引入的,其作用是为它提供 export default 的功能,以支持能直接 import coll from 'collection'。
上面是target(dist)的目录,那么源码src目录应该是怎样呢?
维护源码的时候,应该分模块,比如 集合相关的工具函数,放到 src/collection/ 目录下
src /
|--collection /
| |--- toMapValue.js
| |--- toMapKey.js
| |--- ......
|--array /
|-- .....
|--bai-util.js
|_______________
构建的时候,将所有模块都拷贝到target(dist)目录下,同时自动生成各模块的主module和总module。
关于构建工具
经我广泛的研究,结论如下:
1、应用类的工程,用webpack来打包;
2、工具类的工程,用rollup来打包,例如vue和react,都是用的rollup来打包。
所以,这个工程,决定用rollup来打包,而不是webpack。
rollup打包出来,代码更干净,就像自己写的代码一样,而webpack打包出来,会添加很多其他的代码。
其他功能
1、单元测试
使用jest来做单元测试。
2、持续集成
使用 travis。
git代码地址:https://github.com/instanceofs/B.js
npm仓库地址:https://www.npmjs.com/package/@instaceof/b-util
三、架构和技术说明
技术列表如下:
核心技术:
babel
rollup
jest
terser
eslint
辅助技术
cross-env
commitlint // 检查git commit,要求其符合规范
semantic-release
husky // 拦截不符合规范的git commit和push
travis-deploy-once
codecov
jshint
editorconfig
browserslist
技术说明:
使用rollup构建
配置在根目录下面的 rollup.config.js
在使用 rollup 构建之前,会先准备打包文件,会执行node build/prepare
build/prepare.js的基本作用是 把 src 下面的文件拷贝 到 tmp 目录,同时生成 子模块的js,例如 bai-util.esm.js、collection.js、collection.default.js
同时,会修正js中import的路径。
需要说明的是,rollup.config.js配置中的
globals(),
builtins(),
localResolve(),
resolve()
commonjs()
这些应该暂时还没用到。
export出的配置,是一个数组,不同的input文件,输出配置不一样,
例如 对于 tmp/main.js (实际上为bai-util.esm.js)文件,输出了 umd、iife、cjs 三种格式,
同时,在bai-util.esm.js的基础上,再次转换成 bai-util.es.js (es6 module的es5语法的文件),之所以要转换成 es5语法,其初衷是想它被其他模块引入时,不需要babel转换也能直接使用,bai-util.es.js会被当做本js库的package.json的main入口。(其实,我也不确定这样做到底有多少意义),具体参见这篇文章的描述:https://loveky.github.io/2018/02/26/tree-shaking-and-pkg.module/
其配置文档,参见:
深入学习rollup来进行打包
JS打包工具rollup——完全入门指南
使用模块化工具打包自己开发的JS库
https://rollupjs.org/guide/zh#npm-packages
https://github.com/rollup/rollup
使用terser压缩es6语法的 js
terser是uglify-es6的增强版,因为uglify不支持es6,而uglify-es6又停止更新了,故推出了terser
terser支持和rollup集成,但是我在rollup打包过程不包含js压缩,为更灵活的控制 js压缩,我单独写了个脚本 ,执行node build/minify来处理压缩。
我是用的terser默认的配置,只是添加了 sourcemap的生成。
terser使用文档如下:
https://github.com/terser-js/terser
babel 及 browserslist 配置
rollup构建时,引入了 rollup-plugin-babel 插件,会调用 babel 对js进行处理。
babel的配置文件为:.babelrc.js
我使用 的是 babel 7(@babel/[email protected]),其配置和 babel 6有很大的不一样。
目前,只配置了一个babel-preset-env,其说明参见:
https://babeljs.io/docs/en/babel-preset-env/#how-it-works
https://www.babeljs.cn/docs/setup#installation
另外,babel 默认使用 .browserslistrc 的配置,其内容如下:
> 5%
current node
not dead
等价于 在package.json中配置 [ "browserslist": "> 5%, current node, not dead" ]
对于browserslist的使用,参见其说明文档:https://github.com/browserslist/browserslist
jest 单元测试配置
配置放在 jest.config.js 文件中
主要设置了 "verbose": true 以报告每个测试的执行情况,显示执行时间。
其配置文档参见:https://jestjs.io/docs/zh-Hans/configuration
注意,jest 需要依赖 babel-jest,要依赖babel 6,需要安装 babel 6 和 babel 7的桥接。
babel-jest,文档:https://www.npmjs.com/package/babel-jest
文档中说,如果使用babel 7的话,需要安装babel 6~7的桥接版本'babel-core@^7.0.0-bridge'
单元测试的代码,统一放在了 __test__ 目录下,这个是 jest 默认扫描的目录之一(该目录下面的js文件都会执行),无需配置。
注意,jest 测试代码,我也用的是 es6语法。
IDE(VS Code)使用 eslint 插件检查和自动更正 js 代码
在 vs code 编辑器中 安装了 eslint 插件,
并在IDE中配置了如下(setting.json):
{
"terminal.integrated.shell.windows": "D:\\C\\Program Files\\Git\\bin\\bash.exe",
"eslint.autoFixOnSave": true,
"eslint.options": {
"plugins": ["html"],
"extensions": [".js", ".vue"]
},
"eslint.validate": [
"javascript",
"javascriptreact",
{
"language": "vue",
"autoFix": true
},
{
"language": "html",
"autoFix": true
}
],
"vue-peek.supportedLanguages": ["vue"],
"vue-peek.targetFileExtensions": [".vue", ".js"],
// 开启保存时自动format,一定要配合下面的format组件一起使用
"editor.formatOnSave": true,
// 使用prettier来format代码,相关配置如下:
"prettier.singleQuote": true,
"prettier.semi": false,
"vetur.format.defaultFormatterOptions": {
"prettier": {
"singleQuote": true,
"semi": false
}
},
// 使用js-beautify替换prettier
// "vetur.format.defaultFormatter.html": "js-beautify-html",
"explorer.autoReveal": false,
"workbench.editor.enablePreview": false,
"terminal.integrated.scrollback": 10000,
"javascript.updateImportsOnFileMove.enabled": "always",
"files.associations": {
".jshintrc": "jsonc",
".eslintrc": "jsonc"
}
}
注意到上面的,files.associations 这个配置是为了 在json中支持注释。
另外,我配置了 .eslintignore,用来排除 部分目录和代码,不进行eslint 校验。
另外,我安装了jshint插件(本工程无需这个插件),需要在工程根目录 新建 .jshintrc 文件,内容如下;
{
"undef": true,
"unused": true,
"esversion": 6,
"asi": true
}
jshint详细配置参见:https://jshint.com/docs/options/
命令行使用 eslint 检查 js 代码
运行 eslint --ext .js ./src 即可检查 js 源码是否符合规范。
eslint的配置文件为 .eslintrc,内容如下:
{
"extends": "airbnb-base",
"env": {
"jest": true,
"browser": true,
"node": true,
"es6": true
},
"rules": {
"semi": [0, "never"],
"semi-spacing": [2, {
"before": false,
"after": true
}]
}
}
semi": [0, "never"] 代表,js无 分号结尾,0代表有分号结尾也不报错。
其配置说明参见:
https://eslint.org/docs/rules/
https://blog.csdn.net/helpzp2008/article/details/51507428
使用 commitlint 检查 git commit规范
使用的@commitlint这个插件,其配置文件commitlint.config.js 如下:
module.exports = {
extends: ['@commitlint/config-angular']
};
其允许的注释前缀如下:
['build',
'ci',
'docs',
'feat',
'fix',
'perf',
'refactor',
'revert',
'style',
'test'
]
feat: feature缩写,用户相关的新功能,构建脚本功能除外
fix: 修改 bug
perf: 更改代码,以提高性能(在不影响代码内部行为的前提下,对程序性能进行优化)
refactor: 为了可读性或者性能,在不改变原有功能的前提下做的修改
docs: 文档的变更
style: 代码格式修改, 注意不是 css 修改(例如分号修改)
test: 增加或重构了测试代码,但没有增加产品代码
build: 影响项目构建或依赖项修改
revert: 恢复上一次提交
ci: 持续集成相关文件修改
chore: 更新构建脚本,但是不会更新产品代码
release: 发布新版本
workflow: 工作流相关文件修改
例如 git commit -a -m 'build: refactor' 是合格的注释。
具体参见:
https://www.npmjs.com/package/@commitlint/config-angular
https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/commitlint.config.js
使用 IDE 的 EditorConfig 插件规范 文件文本格式
vscode 请安装插件:CTRL+SHIFT+X 搜索 EditorConfig 并安装,
然后在项目中新建 .editorconfig 文件,内容如下:
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false
这个工具的一个重要作用是,将新建文件和保存的文件,自动更正 为 lf 换行符。(不然的话,windows默认的换行符是 crlf)