工欲善其事,必先利其器。应用在项目开发上就是:做好一个项目,前期的工作准备非常重要。项目开发的前期准备无非都是合理规划项目结构、按需编写构建代码、批量创建入口文件、复制粘贴工具函数等。
在此先推荐笔者写的一个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开发技巧的前端公众号,更多前端小干货等着你喔
