工欲善其事,必先利其器。应用在项目开发上就是:做好一个项目,前期的工作准备非常重要
。项目开发的前期准备无非都是合理规划项目结构
、按需编写构建代码
、批量创建入口文件
、复制粘贴工具函数
等。
在此先推荐笔者写的一个React/Vue应用自动化构建脚手架bruce-cli,其零配置开箱即用的优点非常适合入门级、初中级、快速开发项目的前端同学使用,还可通过创建brucerc.js
文件来覆盖其默认配置,只需专注业务代码的编写无需关注构建代码的编写,让项目结构更简洁。
通过bruce-cli
能把常规的前期准备都解决了,但我们经常会复制粘贴一些之前项目常用的工具函数过来新项目上,新开其他项目时又会重新执行这些操作。
项目开发过程中时常会重复使用一些工具函数,例如浏览器类型
、格式时间差
、URL参数反序列化
、过滤XSS
等,为了避免项目开发时重复的复制粘贴操作带来不必要的麻烦,笔者将平时常用的一些工具函数按功能分类和统一封装,并发布到npm
上。每次项目开发时直接安装,提高开发效率,将时间用在正确的事情上。
- 使用
npm
安装:npm i trample
- 使用
yarn
安装:yarn add trample
trample
根据Web和Node两种JS运行环境划分代码,生成两种bundle.js
。每种文件在不同JS运行环境下运行,必须根据JS运行环境引用文件,否则会报错。
模块 | 工具库 | 运行环境 | 对应文件 | ESM的对应文件 |
---|---|---|---|---|
Web | Web函数工具库 | 浏览器 | web.js |
web.esm.js |
Node | Node函数工具库 | 服务器 | node.js |
node.esm.js |
- Web:
>= 95%
- Node:
>= 8.0.0
trample
使用rollup
打包,因此可使用IIFE
、AMD
、CJS
、UMD
和ESM
五种方式引用。但推荐使用IIFE
、CJS
、ESM
三种引用方式。工具库的代码使用ESM
规范开发,使用export {}
导出。
IIFE引用方式
适用于Web
,最简单最方便的引用方式没有之一。把node_modules/trample/dist/web.umd.js
复制出来,放到新建的js/trample
文件夹下,通过HTML的<script>
直接引用。
<body>
<script src="js/trample/web.umd.js"></script>
<script>
console.log(window.trample.TypeOf("trample"));
console.log(window.trample.BrowserType());
</script>
</body>
AMD引用方式
适用于Web
。把node_modules/trample/dist/web.umd.js
复制出来,放到新建的js/trample
文件夹下,需建立在require.js
下使用。
require.config({
paths: {
trample: "js/trample/web.umd.js"
}
});
require(["trample"], function(trample) {
console.log(trample.BrowserType());
});
CJS引用方式
适用于Web
和Node
。
// Web
const { BrowserType, TypeOf } = require("trample");
// const { BrowserType, TypeOf } = require("trample/dist/web");
console.log(TypeOf("trample"));
console.log(BrowserType());
// Node
const { NodeType, TypeOf } = require("trample/dist/node");
console.log(TypeOf("trample"));
console.log(NodeType());
ESM引用方式
适用于Web
和Node
。若使用ESM
规范开发项目,有利于Wepack启用TreeSharking
移除未使用的代码。
// Web
import { BrowserType, TypeOf } from "trample";
// import { BrowserType, TypeOf } from "trample/dist/web";
console.log(TypeOf("trample"));
console.log(BrowserType());
// Node
import { NodeType, TypeOf } from "trample/dist/node";
console.log(TypeOf("trample"));
console.log(NodeType());
在Web
环境下,请使用以下导入方式⏬。trample
默认引用node_modules/trample/dist/web.js
这个文件。
const { BrowserType, TypeOf } = require("trample");
// 或
import { BrowserType, TypeOf } from "trample";
// 『上面两段代码』 等价 『下面两段代码』(推荐)
const { BrowserType, TypeOf } = require("trample/dist/web");
// 或
import { BrowserType, TypeOf } from "trample/dist/web";
在Node
环境下,请使用以下导入方式⏬。
const { NodeType, TypeOf } = require("trample/dist/node");
// 或
import { NodeType, TypeOf } from "trample/dist/node";
trample
提供ESM
规范的index.esm.js
,在package.json
中已配置module
字段指向index.esm.js
。
若使用webpack
打包项目可利用Tree Sharking
特性移除未使用代码,有效减小打包体积。配置如下。
module.exports = {
// webpack其他配置
resolve: {
mainFields: ["module", "jsnext:main", "main"]
}
};
暂时未接入TypeScript实现函数入参校验,请遵循文档指定的入参类型传参
- GroupMemKey():分组成员特性
- arr:数组(
[]
) - key:属性(
""
)
- arr:数组(
- RecordMemPosition():记录成员位置
- arr:数组(
[]
) - val:值(
""
)
- arr:数组(
- StatMemCount():统计成员个数
- arr:数组(
[]
)
- arr:数组(
- StatMemKeyword():统计成员所含关键字
- arr:数组(
[]
) - keys:关键字集合(
[]
)
- arr:数组(
const arr = [
{ area: "GZ", name: "YZW", age: 27 },
{ area: "GZ", name: "TYJ", age: 25 },
{ area: "GZ", name: "LJY", age: 26 },
{ area: "FS", name: "LXY", age: 24 }
];
GroupMemKey(arr, "area"); // { GZ: Array(3), FS: Array(1) }
const arr = [2, 1, 5, 4, 2, 1, 6, 6, 7];
RecordMemPosition(arr, 2); // [0, 4]
const arr = [0, 1, 1, 2, 2, 2];
StatMemCount(arr); // { 0: 1, 1: 2, 2: 3 }
const text = [
"今天天气真好,我想出去钓鱼",
"我一边看电视,一边写作业",
"小明喜欢同桌的小红,又喜欢后桌的小君,真TM花心",
"最近上班喜欢摸鱼的人实在太多了,代码不好好写,在想入非非"
];
const keyword = ["偷懒", "喜欢", "睡觉", "摸鱼", "真好", "一边", "明天"];
StatMemKeyword(text, keyword); // ["喜欢", "摸鱼", "真好", "一边"]
- FormatCountdown():格式倒计时
- time:时间(
null
,格式为YYYY-MM-DD HH:mm:ss
,Safari格式为YYYY/MM/DD HH:mm:ss
) - 备注:用于
未来时间
- time:时间(
- FormatDiffTime():格式时间差
- time:时间(
null
,格式为YYYY-MM-DD HH:mm:ss
,Safari格式为YYYY/MM/DD HH:mm:ss
) - 备注:可用于
未来时间
或过去时间
- time:时间(
FormatCountdown("2021-01-31"); // "367天15时55分17秒"
FormatDiffTime("2019-03-31"); // "10个月前"
- AsyncTo():格式异步返回值
- pfn:Promise函数(
Promise.resolve(true)
) - 备注:必须在
async函数
或自执行async函数
下使用
- pfn:Promise函数(
- Debounce():防抖
- fn:函数(
v => v
) - dura:时延(
50
)
- fn:函数(
- Throttle():节流
- fn:函数(
v => v
) - dura:时延(
50
)
- fn:函数(
- WaitFor():等待
- dura:时延(
1000
) - 备注:必须在
async函数
或自执行async函数
下使用
- dura:时延(
document.body.addEventListener("click", () => Debounce(() => console.log("Click"), 2000));
document.body.addEventListener("scroll", () => Throttle(() => console.log("Scroll"), 2000));
(async() => {
const [err, res] = await AsyncTo(GetData());
await WaitFor(2000);
console.log(err, res);
})();
- ByteSize():字节大小
- byte:字节(
0
)
- byte:字节(
- FillNum():补零数值
- num:数值(
0
) - len:补位(
0
)
- num:数值(
- RandomNum():范围随机数
- min:最小数(
0
) - max:最大数(
10
)
- min:最小数(
- RandomNumPlus():N个范围随机数
- min:最小数(
0
) - max:最大数(
10
) - count:个数(
1
)
- min:最小数(
- RoundNum():精确数值(
四舍五入
和百分比
)- num:数值(
0
) - dec:小数个数(
2
) - per:是否百分比(
false
)
- num:数值(
- ThousandNum():千分数值
- num:数值(
0
)
- num:数值(
ByteSize(683468); // "667 KB"
FillNum(999, 4); // "0999"
RandomNum(0, 100); // 88
RandomNumPlus(0, 100, 3); // [40, 59, 27]
RoundNum(0.331234, 2, true); // "33.12%"
ThousandNum(12345.6789); // "12,345.6,789"
- GetKeys():读取属性
- obj:对象(
{}
) - keys:属性集合(
[]
)
- obj:对象(
const obj = { a: 1, b: 2, c: 3, d: 4 };
const keys = ["a", "d"];
GetKeys(obj, keys); // { a: 1, d: 4 }
- CheckText():校验文本
- type:类型(
""
,可选address地址、count数量、date日期、email邮件、idcard身份证、image图片、name名称、number计数、password密码、phone手机
) - text:文本(
""
) - 备注:内置以上几种常用校验文本,如不符合需求请使用以下的
CheckTextPlus()
- type:类型(
- CheckTextPlus():自定义校验文本
- regexp:正则(
new RegExp()
) - msg:提示(
""
) - text:文本(
""
)
- regexp:正则(
- MatchBracketText():匹配括号文本
- tgt:括号形式(
"(*)"
,提取的内容必须使用*
代替) - text:文本(
""
)
- tgt:括号形式(
CheckText("email", "young.joway@aliyun"); // { flag: false, msg: "邮箱只能由xxx@yyy.zzz形式组成" }
CheckTextPlus(/^(fe)?male$/g, "性别输入错误", "male"); // { flag: true, msg: "" }
MatchBracketText(
"<img src=\"*\">",
"<img src=\"pig.jpg\"><p>trample</p><img src=\"dada.png\">"
); // ["pig.jpg", "dada.png"]
- DesePhone():脱敏手机
- phone:手机(
""
)
- phone:手机(
- FormatPhone():格式手机
- phone:手机(
""
) - sign:标记(
"-"
,可选-、\s
)
- phone:手机(
- RandomColor():随机HEX色值
- RandomId():随机长度ID
- len:长度(
5
,在1~10
间)
- len:长度(
- RemoveTag():移除标签
- text:文本(
""
)
- text:文本(
- ReverseText():翻转文本
- text:文本(
""
)
- text:文本(
- StartScore():星级评分
- rate:星级(
0
,在0~len
间) - len:长度(
5
)
- rate:星级(
DesePhone("18866669999"); // "188****9999"
FormatPhone("18866669999", " "); // "188 6666 9999"
RandomColor(); // "#26f455"
RandomId(8); // "6ohsln3s"
RemoveTag("<script>alert(\"hello world\")</script>"); // "alert("hello world")"
ReverseText("trample"); // "elpmart"
StartScore(8, 10); // "★★★★★★★★☆☆"
- CompareObj():比较对象(
包含数组
)- obj1:对象1
- obj2:对象2
- EnvType():环境类型
- IsNode():判断Node
- IsWeb():判断Web
- IsEqual():判断相等
- data1:数据1
- data2:数据2
- TypeOf():数据类型
- data:数据
- type:类型
- IsArguments():判断Arguments
- IsArray():判断数组
- IsAsyncFunction():判断异步函数
- IsBoolean():判断布尔值
- IsClass():判断类
- IsDate():判断日期
- IsEmpty():判断空
- IsEmptyArray():判断空数组
- IsEmptyObject():判断空对象
- IsError():判断错误
- IsFunction():判断函数
- IsMap():判断Map
- IsNull():判断空值
- IsNumber():判断数值
- IsObject():判断对象
- IsRegExp():判断正则
- IsSet():判断Set
- IsString():判断字符串
- IsSymbol():判断Symbol
- IsSyncFunction():判断同步函数
- IsUndefined():判断未定义
- IsWeakMap():判断WeakMap
- IsWeakSet():判断WeakSet
CompareObj({ a: 1, b: 2 }, { b: 3, a: 1 }); // { a: true, b: false }
EnvType(); // "node"
IsNode(); // true
IsWeb(); // false
IsEqual({ a: 1, b: 2 }, { b: 2, a: 1 }); // true
TypeOf(168); // "number"
IsEmptyObject({ a: 1, b: 2 }); // false
IsString(168); // false
- GetCookie():读取Cookie
- RemoveCookie():删除Cookie
- key:键(
""
)
- key:键(
- SetCookie():设置Cookie
- key:键(
""
) - val:值(
""
) - day:过期时间(
1
,日)
- key:键(
GetCookie(); // { user_id: "12345", user_token: "abcde" }
RemoveCookie("user_id");
SetCookie("user_id", "123abc", 7);
- AutoResponse():自适应
- width:设计图宽度(
750
) - 备注:在DOM加载前使用,使DOM的尺寸自适应且使用
rem
定义(1rem=100px
),例如设计图里的按钮长宽是100px * 40px
,则在CSS上书写.btn{width:1rem;height:.4rem;}
- width:设计图宽度(
- CopyPaste():复制粘贴
- elem:节点(
document.body
)
- elem:节点(
- DownloadFile():下载文件
- url:地址(
""
) - name:文件名(
""
)
- url:地址(
- FilterXss():过滤XSS
- html:HTML内容(
""
)
- html:HTML内容(
- Img2Base64():图像转B64
- url:地址(
""
) - type:类型(
"image/png"
,可选image/jpeg、image/png
)
- url:地址(
- Jsonp():JSONP
- url:地址(
""
) - name:全局变量(
"jsonp"
) - cb:回调函数(
null
)
- url:地址(
- LoadScript():加载脚本
- url:地址(
""
) - pst:插入位置(
"body"
,可选head、body
)
- url:地址(
AutoResponse(640);
CopyPaste(document.getElementById("btn"));
DownloadFile("https://xxx.yyy/pig.jpg", "pig.jpg");
FilterXss("<script>alert(123)</script>"); // "<script>alert(123)</script>"
const img = await Img2Base64("https://xxx.yyy/pig.jpg", "image/jpeg"); // "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQAB..."
const flag = await Jsonp("https://xxx.yyy/trample.js", "trample", () => console.log(window.trample));
const flag = await LoadScript("https://xxx.yyy/trample.js", "body");
- Ajax({ ... }):异步请求
- data:参数集合(
{}
) - error:失败回调函数(
null
) - success:成功回调函数(
null
) - type:类型(
"get"
,可选get、post
) - url: 地址(
""
)
- data:参数集合(
Ajax({
data: { a: 1, b: 2 },
error: err => console.log(err),
success: res => console.log(res),
type: "post",
url: "https://xxx.yyy"
});
- ClearLStorage:清空LocalStorage
- ClearSStorage:清空SessionStorage
- GetLStorage:读取LocalStorage
- key:键(
""
)
- key:键(
- GetSStorage:读取SessionStorage
- key:键(
""
)
- key:键(
- RemoveLStorage:移除LocalStorage
- key:键(
""
)
- key:键(
- RemoveSStorage:移除SessionStorage
- key:键(
""
)
- key:键(
- SetLStorage:设置LocalStorage
- key:键(
""
) - val:值(
""
)
- key:键(
- SetSStorage:设置SessionStorage
- key:键(
""
) - val:值(
""
)
- key:键(
ClearLStorage();
ClearSStorage();
GetLStorage("love"); // "Love"
GetSStorage("love"); // "Love"
RemoveSStorage("love");
RemoveSStorage("love");
SetLStorage("love", "我爱你");
SetSStorage("love", "我爱你");
- BrowserType():浏览器类型(史上最全的浏览器类型判断,详情请戳《详细判断浏览器运行环境》)
- ua:用户代理(
navigator.userAgent.toLowerCase()
)
- ua:用户代理(
- IsElement():判断Element
- data:数据
BrowserType(); // { engine: "webkit", engineVs: "537.36", platform: "desktop", supporter: "chrome", supporterVs: "78.0.3904.108", system: "macos", systemVs: "10.14.6" }
IsElement(document.body); // true
- ParseUrlSearch():URL参数反序列化
- RemoveUrlSearch():删除URL参数
- search:参数集合(
...[]
,多参数输入)
- search:参数集合(
- SetUrlSearch():设置URL参数
- search:参数集合(
{}
)
- search:参数集合(
- StringifyUrlSearch():URL参数序列化
- search:参数集合(
{}
) - clear:是否清除假值(
false
,假值包含undefined、null、""、NaN
)
- search:参数集合(
ParseUrlSearch(); // { name: "young", sex: "male" }
RemoveUrlSearch("name", "sex");
SetUrlSearch({ name: "tong", sex: "female" });
StringifyUrlSearch({ address: "", name: "young", sex: "male" }, true); // "?name=young&sex=male"
- CopyDir():复制文件路径
- src:输入路径(
""
) - dist: 输出路径(
""
) - filter:过滤函数(
false
,返回函数
表示过滤规则,返回false
表示不复制),函数入参为stat
和path
- src:输入路径(
- CreateDir():创建文件路径
- dir:路径(
""
)
- dir:路径(
- ReadFileForBFS:BFS读取文件(
广度优先遍历
)- dir:路径(
process.cwd()
) - igonre:忽略文件正则(
/(node_modules|\.git|\.DS_Store)$/
)
- dir:路径(
- ReadFileForDFS:DFS读取文件(
深度优先遍历
)- dir:路径(
process.cwd()
) - igonre:忽略文件正则(
/(node_modules|\.git|\.DS_Store)$/
)
- dir:路径(
- RemoveDir():删除文件路径
- dir:路径(
""
)
- dir:路径(
import Path from "path";
function AbsPath(dir) {
return Path.join(__dirname, dir);
}
CopyDir(
AbsPath("./src"),
AbsPath("./trample"),
(stat, path) => !(stat === "file" && path.includes(".DS_Store"))
);
CreateDir(AbsPath("./assets/lib/trample"));
ReadFileForBFS(); // ["node.js", "web.js"]
ReadFileForDFS(); // ["node.js", "web.js"]
RemoveDir(AbsPath("./assets/lib/trample"));
- RunCmd():运行命令
- cmd:命令行(
"node -v"
) - 备注:只支持单行命令,多个命令同时执行可书写成
cmd1 && cmd2
- cmd:命令行(
RunCmd("npm -v"); // "6.11.3"
- NodeType():Node类型
NodeType(); // { nodeVs: "12.12.0", npmVs: "6.11.3", system: "macos", systemVs: "18.7.0" }
开发目录
trample
├─ src
│ ├─ common
│ │ ├─ array.js
│ │ ├─ date.js
│ │ ├─ function.js
│ │ ├─ index.js
│ │ ├─ number.js
│ │ ├─ object.js
│ │ ├─ regexp.js
│ │ ├─ string.js
│ │ └─ type.js
│ ├─ node
│ │ ├─ fs.js
│ │ ├─ index.js
│ │ ├─ process.js
│ │ └─ type.js
│ ├─ web
│ │ ├─ cookie.js
│ │ ├─ dom.js
│ │ ├─ function.js
│ │ ├─ index.js
│ │ ├─ storage.js
│ │ ├─ type.js
│ │ └─ url.js
│ ├─ node.js
│ ├─ web.js
│ └─ web.umd.js
├─ .gitignore
├─ .npmignore
├─ license
├─ package.json
├─ readme.md
└─ rollup.config.js
后续补发,构建编译过程正在完善中......
后续补发,测试用例流程正在书写中......
- 补全测试用例
- 接入TypeScript
trample
是笔者为了节省项目开发过程中常用工具函数复制粘贴的时间,而封装的一个Web/Node通用函数工具库。设计目的是为了减少无谓的复制粘贴动作
和统一管理项目开发中常用的工具函数
。
由于笔者是针对个人需求而定制的工具库,所以应用范围可能未包含上你常用的工具函数,可在Issue上提出你的宝贵建议
或贴上你想增加的工具函数
。笔者会认真阅读你的宝贵建议和整合各位同学贡献的工具函数。
也可Fork本项目到自己的Github
上,在原有的基础上增加自己常用
、易忘
和代码量多
的工具函数,同时也可扩展原有的功能和构建方式,封装成自己熟悉的工具库,提升自己的开发能力,间接减少晚上加班时间
和增加上班摸鱼时间
。
若觉得trample
对你有帮助,可在Issue上提出你的宝贵建议
,笔者会认真阅读并整合你的建议。喜欢trample
的请给一个Star,或Fork本项目到自己的Github
上,根据自身需求定制功能。
关注公众号IQ前端
,一个专注于CSS/JS开发技巧的前端公众号,更多前端小干货等着你喔