工欲善其事,必先利其器。应用在项目开发上就是:做好一个项目,前期的工作准备非常重要
。项目开发的前期准备无非都是合理规划项目结构
、按需编写构建代码
、批量创建入口文件
、复制粘贴工具函数
等。
在这里先推荐笔者写的一个React/Vue应用自动化构建脚手架bruce-cli,其零配置开箱即用的优点非常适合入门级、初中级、快速开发项目的前端同学使用,还可通过创建brucerc.js
文件来覆盖其默认配置,只需专注业务代码的编写无需关注构建代码的编写,让项目结构更简洁。
通过bruce-cli
能把常规的前期准备都解决了,但是我们经常会复制粘贴一些之前项目常用的工具函数过来新项目上,新开其他项目时又会重新执行这些操作。
项目开发过程中时常会重复使用一些工具函数,例如浏览器类型
、格式时间差
、URL参数反序列化
、过滤XSS
等,为了避免项目开发时重复的复制粘贴操作所带来不必要的麻烦,笔者将平时常用的一些工具函数按功能分类和统一封装,并发布到npm
上。每次项目开发时直接安装,提高开发效率,将时间用在正确的位置上。
npm i trample
yarn add trample
安装失败
- 将npm源镜像设置为淘宝镜像:
npm config set registry https://registry.npm.taobao.org
- 重新执行命令安装:
npm i trample
或yarn add trample
trample.js
根据Web和Node两种JS运行环境进行代码划分,生成两个bundle
文件。每个文件在不同的JS运行环境下运行,必须根据JS运行环境引用文件,否则会报错。
模块 | 工具库 | 运行环境 | 对应文件 | 兼容ES5的对应文件 |
---|---|---|---|---|
Common | 公共函数工具库 | 浏览器和服务器 | 被下面文件包含 | 被下面文件包含 |
Web | Web函数工具库 | 浏览器 | web.js |
web.es5.js |
Node | Node函数工具库 | 服务器 | node.js |
node.es5.js |
web.js
兼容状态:ES6web.es5.js
兼容状态:ES5- Chrome:
last 20 versions
- Safari:
>= 8
- Firefox:
last 20 versions
- Opera:
last 20 versions
- Explorer:
>= 10
- Android:
>= 5
- iOS:
>= 8
- Chrome:
node.js
兼容状态:node >= 10.0.0
node.es5.js
兼容状态:node >= 8.0.0
trample.js
使用UMD
通用模块规范进行打包,因此可使用IIFE
、AMD
、CJS
和ESM
四种方式引用。但是推荐使用IIFE
、CJS
、ESM
三种引用方式。工具库的代码使用ESM
的形式进行开发,使用export default {}
进行导出。
IIFE引用方式
适用于Web
,最简单最方便的引用方式没有之一。把node_modules/trample/web.js
复制出来,放到新建的js/trample
文件夹下,通过HTML的<script>
直接引用。
<body>
<script src="js/trample/web.js"></script>
<script>
trample.DataType("trample");
trample.BrowserType();
</script>
</body>
AMD引用方式
适用于Web
。把node_modules/trample/web.js
复制出来,放到新建的js/trample
文件夹下,需建立在require.js
下使用。
require.config({
paths: {
trample: "js/trample/web.js"
}
});
require(["trample"], function(trample) {
trample.BrowserType();
});
CJS引用方式
适用于Web
和Node
。
// Web
const TW = require("trample/web");
TW.DataType("trample");
TW.BrowserType();
// Node
const TN = require("trample/node");
TN.DataType("trample");
TN.NodeType();
ESM引用方式
适用于Web
和Node
。如果使用ESM
的形式进行项目开发,推荐使用按需导入
的方式,有利于Wepack启用TreeSharking
移除没有使用的代码。
- 全部导入
- Web
- Node
- 按需导入
- Web
- Node (报错
SyntaxError: Cannot use import statement outside a module
,待解决)
// Web:全部导入
import TW from "trample/web";
TW.DataType("trample");
TW.BrowserType();
// Web:按需导入
import { DataType } from "trample/common/index";
import { BrowserType } from "trample/web/index";
DataType("trample");
BrowserType();
// Node:全部导入
import TN from "trample/node";
TN.DataType("trample");
TN.NodeType();
// Node:按需导入
import { DataType } from "trample/common/index";
import { NodeType } from "trample/node/index";
DataType("trample");
NodeType();
在Web环境下,请使用以下导入方式⏬。trample
默认引用node_modules/trample/web.js
这个文件。
const TW = require("trample/web");
// 或
import TW from "trample/web";
// 『上面两段代码』 等价 『下面两段代码』(推荐)
const TW = require("trample");
// 或
import TW from "trample";
在Node环境下,请使用以下导入方式⏬。
const TN = require("trample/node");
// 或
import TN from "trample/node";
使用ESM的按需导入,请使用以下导入方式⏬。记得带上/index
以表明是一个文件,防止和同名的文件夹冲突。
// 公共函数工具库
import { DataType } from "trample/common/index";
// Web函数工具库
import { BrowserType } from "trample/common/index";
// Node函数工具库
import { NodeType } from "trample/common/index";
另外,以上示例都是基于JS最终运行环境是ES6
或后续使用webpack+babel
将代码打包成ES5
这两种情况才生效。如果JS最终运行环境是ES5
或不使用webpack+babel
将代码打包成ES5
,则需对导入路径进行如下修改,才能使用编译好的ES5文件。
trample
或trample/web
:修改成trample/web.es5
trample/node
:修改成trample/node.es5
以上的trample/web
和trample/node
都是引用编译好的ES6文件,项目如需编译成ES5,需配置webpack -> resolve -> alias
,直接引用编译好的ES5文件web.es5.js
或node.e
8000
s5.js
。
{
resolve: {
alias: {
trample: "node_modules/trample/web.es5.js",
// trample: "node_modules/trample/node.es5.js"
}
}
}
如需使用ESM模块的按需导入,需配置webpack -> module -> rules -> babel-loader
的include
,将trample
列入编译白名单,否则源码的ES6语法不被编译。
{
module: {
rules: [{
include: /src|node_modules\/trample/,
test: /\.js$/,
use: [{ loader: "babel-loader" }]
}]
}
}
暂时没有接入TypeScript进行函数入参校验,请遵循文档指定的入参类型传参
公共函数工具库:
trample/common/index
- MemberCount():统计成员个数
- arr:数组(
[]
)
- arr:数组(
- MemberGroup():分组成员特性
- arr:数组(
[]
) - key:属性(
""
)
- arr:数组(
- MemberKeyword():统计成员所含关键字
- arr:数组(
[]
) - keys:关键字集合(
[]
)
- arr:数组(
- MemberPosition():记录成员位置
- arr:数组(
[]
) - val:值(
""
)
- arr:数组(
const arr = [0, 1, 1, 2, 2, 2];
MemberCount(arr); // { 0: 1, 1: 2, 2: 3 }
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 }
];
MemberGroup(arr, "area"); // { GZ: Array(3), FS: Array(1) }
const text = [
"今天天气真好,我想出去钓鱼",
"我一边看电视,一边写作业",
"小明喜欢同桌的小红,又喜欢后桌的小君,真TM花心",
"最近上班喜欢摸鱼的人实在太多了,代码不好好写,在想入非非"
];
const keyword = ["偷懒", "喜欢", "睡觉", "摸鱼", "真好", "一边", "明天"];
MemberKeyword(text, keyword); // ["喜欢", "摸鱼", "真好", "一边"]
const arr = [2, 1, 5, 4, 2, 1, 6, 6, 7];
MemberPosition(arr, 2); // [0, 4]
- 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个月前"
- Ajax({ ... }):异步请求
- data:参数集合(
{}
) - error:失败回调函数(
null
) - success:成功回调函数(
null
) - type:类型(
"get"
,可选get、post
) - url: 地址(
""
)
- data:参数集合(
- 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:时延(
Ajax({
data: { a: 1, b: 2 },
error: err => console.log(err),
success: res => console.log(res),
type: "post",
url: "https://xxx.yyy"
});
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(
6D40
"trample"); // "elpmart"
StartScore(8, 10); // "★★★★★★★★☆☆"
- DataType():数据类型
- 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
- EnvType():环境类型
- IsNode():判断Node
- IsWeb():判断Web
DataType(168); // "number"
IsEmptyObject({ a: 1, b: 2 }); // false
IsString(168); // false
EnvType(); // "node"
IsNode(); // true
IsWeb(); // false
Web函数工具库:
trample/web/index
- 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:插入位置(
"head"
,可选head、body
)
- url:地址(
- ToastMsg():提示消息
- msg:消息(
""
) - delay:时延(
1000
) - classNames:类名(
""
) - id:ID(
"toast"
)
- msg:消息(
AutoResponse(640);
CopyPaste(document.getElementById("btn"));
DownloadFile("https://xxx.yyy/pig.jpg", "pig.jpg");
FilterXss("<script>alert(123)</script>"); // "<script>alert(123)</script>"
Img2Base64("https://xxx.yyy/pig.jpg", "image/jpeg"); // "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQAB..."
Jsonp("https://xxx.yyy/trample.js", "trample", () => console.log(window.trample));
LoadScript("https://xxx.yyy/trample.js", "body");
ToastMsg("hello world", 2000, "ellipsis");
- 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:参数集合(
{}
)
- search:参数集合(
ParseUrlSearch(); // { name: "young", sex: "male" }
RemoveUrlSearch("name", "sex");
SetUrlSearch({ name: "tong", sex: "female" });
StringifyUrlSearch({ name: "young", sex: "male" }); // "?name=young&sex=male"
Node函数工具库
- CopyDir():复制文件路径
- src:输入路径(
""
) - dist: 输出路径(
""
) - filter:过滤函数(
false
,返回函数
表示过滤规则,返回false
表示不复制),函数入参为stat
和path
- src:输入路径(
- CreateDir():创建文件路径
- dir:路径(
""
)
- 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"));
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
├─ build
│ ├─ index.js
│ └─ util.js
├─ 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
│ │ ├─ index.js
│ │ ├─ type.js
│ │ └─ url.js
│ ├─ node.js
│ └─ web.js
├─ .gitignore
├─ .npmignore
├─ license
├─ package.json
└─ readme.md
编译目录(
不包含以上的目录,可认为是编译后增加的目录
)
执行npm run build
后会增加以下文件
trample
├─ 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
│ ├─ index.js
│ ├─ type.js
│ └─ url.js
├─ node.js
├─ node.es5.js
├─ web.js
└─ web.es5.js
后续补发,构建编译过程正在完善当中......
后续补发,测试用例流程正在书写当中......
- 补全测试用例
- 接入TypeScript
trample.js
是笔者为了节省项目开发过程中常用工具函数复制粘贴的时间,而封装的一个Web/Node通用函数工具库。设计目的是为了减少无谓的复制粘贴动作
和统一管理项目开发中常用的工具函数
。
由于笔者是针对个人需求而定制的工具库,所以应用范围可能没有包含上你常用的工具函数,可在Issue上提出你的宝贵建议
或贴上你想增加的工具函数
。笔者会认真阅读你的宝贵建议和整合各位同学贡献的工具函数。如果觉得trample.js
不错,欢迎Start一个。
如果觉得trample.js
对大家有用,建议Fork本项目到自己的Github
上,在原有的基础上增加自己常用
、易忘
和代码量多
的工具函数,同时也可扩展原有的功能和构建方式,封装成自己熟悉的工具库,提升自己的开发能力,间接减少晚上加班时间
和增加上班摸鱼时间
。
关注公众号IQ前端
,更多前端小干货等着你喔!我是JowayYoung
,喜欢分享前端技术和生活纪事,学习与生活不落下,每天进步一点点,与大家相伴成长