├── README.md
├── babel.config.js
├── build.sh
├── dist
├── .DS_Store
├── class
│ ├── cache.js
│ ├── lru-cache.js
│ └── queue.js
├── index.cjs.js
├── index.d.ts
├── index.esm.js
├── index.js
├── js
│ ├── array.js
│ ├── bom.js
│ ├── dom.js
│ ├── format.js
│ ├── index.js
│ ├── random.js
│ ├── reg.js
│ ├── url.js
│ └── utils.js
├── package.json.js
└── utils
│ └── index.js
├── package.json
├── pnpm-workspace.yaml
├── rollup.config.ts
├── scripts
├── build.ts
├── publish.ts
├── release.ts
└── utils.ts
├── src
├── billd-utils.d.ts
├── class
│ ├── cache.ts
│ ├── lru-cache.ts
│ └── queue.ts
├── demo.html
├── index.ts
├── js
│ ├── array.ts
│ ├── bom.ts
│ ├── dom.ts
│ ├── format.ts
│ ├── index.ts
│ ├── random.ts
│ ├── reg.ts
│ ├── url.ts
│ └── utils.ts
└── utils
│ └── index.ts
├── test
├── computeBox.html
└── test.js
├── tsconfig.json
└── typedoc.config.json
/README.md:
--------------------------------------------------------------------------------
1 |
2 | Bi-Utils
3 |
4 |
5 |
6 | 基于rollup + pnpm + esbuild搭建的Bi-Utils
7 |
8 |
9 | # 简介
10 |
11 | 积累常用的 js 方法
12 |
13 | # 安装
14 |
15 | ```sh
16 | npm install bi-utils
17 | ```
18 |
19 | # 使用
20 |
21 | ```ts
22 | import { isBrowser } from 'bi-utils';
23 |
24 | console.log(isBrowser());
25 | ```
26 |
27 | # 本地调试
28 |
29 | > 本地调试不会构建 umd
30 |
31 | ```sh
32 | pnpm run dev
33 | ```
34 |
35 | # 本地构建
36 |
37 | ```sh
38 | pnpm run build
39 | ```
40 |
41 | # 生成文档
42 |
43 | > 使用 [typedoc](https://typedoc.org/) 生成,文档会生成在项目根目录的 doc 目录
44 |
45 | ```sh
46 | pnpm run doc
47 | ```
48 |
49 | # 发版
50 |
51 | ## 0.确保 git 工作区干净
52 |
53 | 即确保本地的修改已全部提交(git status 的时候会显示:`nothing to commit, working tree clean` ),否则会导致执行 `release:local` 脚本失败
54 |
55 | ## 1.执行本地发版脚本
56 |
57 | ```sh
58 | pnpm run release:local
59 | ```
60 |
61 | > 该脚本内部会做以下事情:
62 |
63 | 1. 根据用户选择的版本,更新 package.json 的 version
64 | 2. 开始构建
65 | 3. 对比当前版本与上个版本的差异,生成 changelog日志
66 | 4. 提交暂存区到本地仓库:git commit -m 'chore(release): v 当前版本'
67 | 5. 生成当前版本 tag:git tag v 当前版本
68 |
69 | ## 2.执行线上发版脚本
70 |
71 | ```sh
72 | pnpm run release:online
73 | ```
74 |
75 | > 该脚本内部会做以下事情:
76 |
77 | 1. 提交当前版本:git push
78 | 2. 提交当前版本 tag:git push origin v 当前版本
79 | 3. 发布到 npm
80 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | ],
6 | ],
7 | };
8 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # 生成头部文件快捷键:ctrl+cmd+i
4 |
5 | # 静态部署的项目,一般流程是在jenkins里面执行build.sh进行构建,
6 | # 构建完成后会连接ssh,执行/node/sh/frontend.sh,frontend.sh会将构建的完成资源复制到/node/xxx。
7 | # 复制完成后,frontend.sh会执行清除buff/cache操作
8 |
9 | # node项目,一般流程是在jenkins里面执行build.sh进行构建,
10 | # 构建完成后会连接ssh,执行/node/sh/node.sh,node.sh会将构建的完成资源复制到/node/xxx,并且执行/node/xxx/pm2.sh。
11 | # 最后,node.sh会执行清除buff/cache操作
12 |
13 | # 注意:JOBNAME=$1,这个等号左右不能有空格!
14 | JOBNAME=$1 #约定$1为任务名
15 | ENV=$2 #约定$2为环境
16 | WORKSPACE=$3 #约定$3为Jenkins工作区
17 | PORT=$4 #约定$4为端口号
18 | TAG=$5 #约定$5为git标签
19 | PUBLICDIR=/node #约定公共目录为/node
20 |
21 | echo 删除node_modules:
22 | rm -rf node_modules
23 |
24 | echo 查看node版本:
25 | node -v
26 |
27 | echo 查看npm版本:
28 | npm -v
29 |
30 | echo 设置npm淘宝镜像:
31 | npm config set registry https://registry.npm.taobao.org/
32 |
33 | echo 查看当前npm镜像:
34 | npm get registry
35 |
36 | if ! type pnpm >/dev/null 2>&1; then
37 | echo 'pnpm未安装,先全局安装pnpm'
38 | npm i pnpm -g
39 | else
40 | echo 'pnpm已安装'
41 | fi
42 |
43 | echo 查看pnpm版本:
44 | pnpm -v
45 |
46 | echo 设置pnpm淘宝镜像:
47 | pnpm config set registry https://registry.npm.taobao.org/
48 | #TODO registry
49 | pnpm config set @billd:registry http://registry.../
50 |
51 | echo 查看当前pnpm镜像:
52 | pnpm config get registry
53 | pnpm config get @billd:registry
54 |
55 | echo 开始安装依赖:
56 | pnpm install
57 |
58 | if [ $ENV = 'beta' ]; then
59 | echo 开始构建测试环境:
60 | elif [ $ENV = 'preview' ]; then
61 | echo 开始构建预发布环境:
62 | elif [ $ENV = 'prod' ]; then
63 | echo 开始构建正式环境:
64 | else
65 | echo 开始构建$ENV环境:
66 | fi
67 |
68 | pnpm run doc
69 |
70 | echo 将doc移动到项目根目录:
71 | mv doc dist
72 |
--------------------------------------------------------------------------------
/dist/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nemo-crypto/bit-utils/849a8749385c3fbcfb0f19270025d493633f663e/dist/.DS_Store
--------------------------------------------------------------------------------
/dist/class/cache.js:
--------------------------------------------------------------------------------
1 | import{debugLog as e}from"../utils/index.js";class t{prefix="";constructor(e){this.prefix=e}handlePrefix=e=>""===this.prefix?e:`${this.prefix}${e}`;getItem=e=>localStorage.getItem(this.handlePrefix(e));setItem=(e,t)=>localStorage.setItem(this.handlePrefix(e),t);removeItem=e=>localStorage.removeItem(this.handlePrefix(e));getStorage=t=>{try{const e=this.getItem(t);if(e){const r=JSON.parse(e);return r.createTime?r.value:(this.clearStorage(t),null)}return null}catch(r){return e("error",r),this.clearStorage(t),null}};setStorage=(t,r)=>{try{const e=+new Date;this.setItem(t,JSON.stringify({value:r,createTime:e}))}catch(r){e("error",r),this.clearStorage(t)}};clearStorage=t=>{try{this.removeItem(t)}catch(t){e("error",t)}};getStorageExp=t=>{try{const e=this.getItem(t);if(e){const r=JSON.parse(e),a=r.expireTime,i=a<+new Date;return!a||i?(this.clearStorage(t),null):r.value}return null}catch(r){return e("error",r),this.clearStorage(t),null}};setStorageExp=(t,r,a)=>{try{if([t,r,a].includes(void 0))return void e("error","请检查传入的参数!");const i=+new Date,o=i+60*a*60*1e3;this.setItem(t,JSON.stringify({value:r,createTime:i,expireTime:o}))}catch(r){e("error",r),this.clearStorage(t)}}}export{t as CacheModel};
2 |
--------------------------------------------------------------------------------
/dist/class/lru-cache.js:
--------------------------------------------------------------------------------
1 | class t{capacity;data=new Map;constructor(t){if(t<1)throw new Error("capacity必须大于1!");this.capacity=t}get(t){const e=this.data,a=e.get(t);return e.has(t)?(e.delete(t),e.set(t,a),a):null}put(t,e){const a=this.data;a.has(t)&&a.delete(t),a.set(t,e),a.size>this.capacity&&a.delete(a.keys().next().value)}}export{t as LRUCache};
2 |
--------------------------------------------------------------------------------
/dist/class/queue.js:
--------------------------------------------------------------------------------
1 | import{debugLog as t}from"../utils/index.js";class s{tasks=[];max=0;total=0;delay=0;done;constructor(t){let{max:s=5,done:a,delay:i=0}=t;this.tasks=[],this.total=0,this.max=s,this.done=a,this.delay=i,setTimeout((()=>{this.run()}),0)}addTask(t){this.tasks.push(t),this.total+=1}run(){if(0===this.tasks.length)return Promise.resolve("");const s=Math.min(this.tasks.length,this.max);for(let a=0;a{})).catch((s=>{t("error",s)})).finally((()=>{setTimeout((()=>{this.max+=1,this.total-=1,this.run(),0===this.total&&this.done?.()}),this.delay)}))}}export{s as ConcurrentPoll};
2 |
--------------------------------------------------------------------------------
/dist/index.cjs.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | Object.defineProperty(exports, '__esModule', { value: true });
4 |
5 | var version$1 = "0.0.01";
6 |
7 | const arrayUnique = arr => {
8 | return [...new Set(arr)];
9 | };
10 |
11 | const isIPad = () => {
12 | const ua = navigator.userAgent.toLowerCase();
13 | const res = ua.match(/iPad/i);
14 | if (res?.length) {
15 | return true;
16 | }
17 | if (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1) {
18 | return true;
19 | }
20 | return false;
21 | };
22 |
23 | const copyToClipBoard = text => {
24 | const oInput = document.createElement("input");
25 | oInput.value = text;
26 | document.body.appendChild(oInput);
27 | oInput.select();
28 | document.execCommand("Copy");
29 | oInput.parentElement?.removeChild(oInput);
30 | };
31 |
32 | const formatDate = timetamp => {
33 | function addDateZero(num) {
34 | return num < 10 ? `0${num}` : num;
35 | }
36 | const date = new Date(timetamp);
37 | return {
38 | year: date.getFullYear(),
39 | month: addDateZero(date.getMonth() + 1),
40 | day: addDateZero(date.getDate()),
41 | hour: addDateZero(date.getHours()),
42 | minutes: addDateZero(date.getMinutes()),
43 | seconds: addDateZero(date.getSeconds())
44 | };
45 | };
46 |
47 | const getRangeRandom = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
48 | const getRandomOne = arr => arr[Math.floor(Math.random() * arr.length)];
49 | const getRandomString = length => {
50 | const str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
51 | let res = "";
52 | for (let i = 0; i < length; i += 1) {
53 | res += str.charAt(getRangeRandom(0, str.length - 1));
54 | }
55 | return res;
56 | };
57 | const getRandomInt = length => {
58 | if (length > 16 || length < 1) throw new Error("length\u7684\u8303\u56F4:[1,16]");
59 | let num = +`${Math.random()}`.slice(2, 2 + length);
60 | if (String(num).length !== length) {
61 | num = getRandomInt(length);
62 | }
63 | return num;
64 | };
65 |
66 | const debugLog = function (type) {
67 | for (var _len = arguments.length, data = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
68 | data[_key - 1] = arguments[_key];
69 | }
70 | console[type]("bi-utils", ...data);
71 | };
72 |
73 | function isPureNumber(str) {
74 | const regex = /^\d+$/;
75 | return regex.test(str);
76 | }
77 | const regVerify = (str, type) => {
78 | try {
79 | switch (type) {
80 | case "email":
81 | return /[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+/.test(str);
82 | case "phone":
83 | return /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/.test(str);
84 | }
85 | } catch (error) {
86 | debugLog("error", error);
87 | return false;
88 | }
89 | };
90 | const judgeStringSpace = value => {
91 | const reg1 = /^\s+/g;
92 | const reg2 = /\s+$/g;
93 | if (reg1.test(value) || reg2.test(value)) {
94 | return true;
95 | }
96 | return false;
97 | };
98 |
99 | const getUrlParams = key => {
100 | const url = decodeURIComponent(window.location.href);
101 | const str = url.split("?")[1];
102 | const obj = {};
103 | if (str) {
104 | const keys = str.split("&");
105 | keys.forEach(item => {
106 | const arr = item.split("=");
107 | obj[arr[0]] = arr[1];
108 | });
109 | }
110 | return key ? obj[key] : obj;
111 | };
112 |
113 | function strBtoa(str) {
114 | return window.btoa(window.encodeURIComponent(str));
115 | }
116 |
117 | class CacheModel {
118 | prefix = "";
119 | constructor(prefix) {
120 | this.prefix = prefix;
121 | }
122 | handlePrefix = key => {
123 | if (this.prefix === "") {
124 | return key;
125 | } else {
126 | return `${this.prefix}${key}`;
127 | }
128 | };
129 | getItem = key => {
130 | return localStorage.getItem(this.handlePrefix(key));
131 | };
132 | setItem = (key, value) => {
133 | return localStorage.setItem(this.handlePrefix(key), value);
134 | };
135 | removeItem = key => {
136 | return localStorage.removeItem(this.handlePrefix(key));
137 | };
138 | getStorage = key => {
139 | try {
140 | const res = this.getItem(key);
141 | if (res) {
142 | const data = JSON.parse(res);
143 | if (!data.createTime) {
144 | this.clearStorage(key);
145 | return null;
146 | } else {
147 | return data.value;
148 | }
149 | }
150 | return null;
151 | } catch (error) {
152 | debugLog("error", error);
153 | this.clearStorage(key);
154 | return null;
155 | }
156 | };
157 | setStorage = (key, value) => {
158 | try {
159 | const createTime = +new Date();
160 | this.setItem(key, JSON.stringify({
161 | value,
162 | createTime
163 | }));
164 | } catch (error) {
165 | debugLog("error", error);
166 | this.clearStorage(key);
167 | }
168 | };
169 | clearStorage = key => {
170 | try {
171 | this.removeItem(key);
172 | } catch (error) {
173 | debugLog("error", error);
174 | }
175 | };
176 | getStorageExp = key => {
177 | try {
178 | const res = this.getItem(key);
179 | if (res) {
180 | const data = JSON.parse(res);
181 | const expireTime = data.expireTime;
182 | const isExpired = expireTime < +new Date();
183 | if (!expireTime || isExpired) {
184 | this.clearStorage(key);
185 | return null;
186 | } else {
187 | return data.value;
188 | }
189 | }
190 | return null;
191 | } catch (error) {
192 | debugLog("error", error);
193 | this.clearStorage(key);
194 | return null;
195 | }
196 | };
197 | setStorageExp = (key, value, expires) => {
198 | try {
199 | if ([key, value, expires].includes(void 0)) {
200 | debugLog("error", "\u8BF7\u68C0\u67E5\u4F20\u5165\u7684\u53C2\u6570\uFF01");
201 | return;
202 | }
203 | const createTime = +new Date();
204 | const expireTime = createTime + expires * 60 * 60 * 1e3;
205 | this.setItem(key, JSON.stringify({
206 | value,
207 | createTime,
208 | expireTime
209 | }));
210 | } catch (error) {
211 | debugLog("error", error);
212 | this.clearStorage(key);
213 | }
214 | };
215 | }
216 |
217 | class LRUCache {
218 | capacity;
219 | data = /* @__PURE__ */new Map();
220 | constructor(capacity) {
221 | if (capacity < 1) throw new Error("capacity\u5FC5\u987B\u5927\u4E8E1\uFF01");
222 | this.capacity = capacity;
223 | }
224 | get(key) {
225 | const data = this.data;
226 | const value = data.get(key);
227 | if (!data.has(key)) return null;
228 | data.delete(key);
229 | data.set(key, value);
230 | return value;
231 | }
232 | put(key, value) {
233 | const data = this.data;
234 | if (data.has(key)) {
235 | data.delete(key);
236 | }
237 | data.set(key, value);
238 | if (data.size > this.capacity) {
239 | data.delete(data.keys().next().value);
240 | }
241 | }
242 | }
243 |
244 | class ConcurrentPoll {
245 | tasks = [];
246 | max = 0;
247 | total = 0;
248 | delay = 0;
249 | done;
250 | constructor(_ref) {
251 | let {
252 | max = 5,
253 | done,
254 | delay = 0
255 | } = _ref;
256 | this.tasks = [];
257 | this.total = 0;
258 | this.max = max;
259 | this.done = done;
260 | this.delay = delay;
261 | setTimeout(() => {
262 | this.run();
263 | }, 0);
264 | }
265 | addTask(task) {
266 | this.tasks.push(task);
267 | this.total += 1;
268 | }
269 | run() {
270 | if (this.tasks.length === 0) {
271 | return Promise.resolve("");
272 | }
273 | const min = Math.min(this.tasks.length, this.max);
274 | for (let i = 0; i < min; i += 1) {
275 | this.max -= 1;
276 | const task = this.tasks.shift();
277 | task().then(() => {}).catch(error => {
278 | debugLog("error", error);
279 | }).finally(() => {
280 | setTimeout(() => {
281 | this.max += 1;
282 | this.total -= 1;
283 | this.run();
284 | if (this.total === 0) {
285 | this.done?.();
286 | }
287 | }, this.delay);
288 | });
289 | }
290 | }
291 | }
292 |
293 | var utils = /*#__PURE__*/Object.freeze({
294 | __proto__: null,
295 | arrayUnique: arrayUnique,
296 | isIPad: isIPad,
297 | copyToClipBoard: copyToClipBoard,
298 | formatDate: formatDate,
299 | getRangeRandom: getRangeRandom,
300 | getRandomOne: getRandomOne,
301 | getRandomString: getRandomString,
302 | getRandomInt: getRandomInt,
303 | isPureNumber: isPureNumber,
304 | regVerify: regVerify,
305 | judgeStringSpace: judgeStringSpace,
306 | getUrlParams: getUrlParams,
307 | strBtoa: strBtoa,
308 | CacheModel: CacheModel,
309 | LRUCache: LRUCache,
310 | ConcurrentPoll: ConcurrentPoll
311 | });
312 |
313 | const version = version$1;
314 |
315 | exports.CacheModel = CacheModel;
316 | exports.ConcurrentPoll = ConcurrentPoll;
317 | exports.LRUCache = LRUCache;
318 | exports.arrayUnique = arrayUnique;
319 | exports.copyToClipBoard = copyToClipBoard;
320 | exports["default"] = utils;
321 | exports.formatDate = formatDate;
322 | exports.getRandomInt = getRandomInt;
323 | exports.getRandomOne = getRandomOne;
324 | exports.getRandomString = getRandomString;
325 | exports.getRangeRandom = getRangeRandom;
326 | exports.getUrlParams = getUrlParams;
327 | exports.isIPad = isIPad;
328 | exports.isPureNumber = isPureNumber;
329 | exports.judgeStringSpace = judgeStringSpace;
330 | exports.regVerify = regVerify;
331 | exports.strBtoa = strBtoa;
332 | exports.version = version;
333 |
--------------------------------------------------------------------------------
/dist/index.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 数组去重,缺点不能去除{}
3 | * @param {Array} arr
4 | * @return {*} 不修改原数组,返回新数组
5 | */
6 | declare const arrayUnique: (arr: Array) => any[];
7 |
8 | /** 判断是否是ipad */
9 | declare const isIPad: () => boolean;
10 |
11 | /**
12 | * @description 将内容复制到剪切板
13 | * @param {string} text
14 | * @return {*}
15 | */
16 | declare const copyToClipBoard: (text: string) => void;
17 |
18 | /**
19 | * @description 格式化时间
20 | * @param {number} timetamp
21 | */
22 | declare const formatDate: (timetamp: number) => {
23 | year: number;
24 | month: string | number;
25 | day: string | number;
26 | hour: string | number;
27 | minutes: string | number;
28 | seconds: string | number;
29 | };
30 |
31 | /**
32 | * @description 获取[min,max]之间的随机整数。
33 | * @example: getRangeRandom(-10,100) ===> -8
34 | * @param {number} min
35 | * @param {number} max
36 | * @return {*}
37 | */
38 | declare const getRangeRandom: (min: number, max: number) => number;
39 | /**
40 | * @description: 随机数组的一个元素
41 | * @example: getRandomOne([10,2,4,6]) ===> 6
42 | * @param {any} arr
43 | * @return {*}
44 | */
45 | declare const getRandomOne: (arr: any[]) => any;
46 | /**
47 | * @description 获取随机字符串(ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789)
48 | * @example: getRandomString(4) ===> abd3
49 | * @param {number} length
50 | * @return {*}
51 | */
52 | declare const getRandomString: (length: number) => string;
53 | /**
54 | * @description: 获取随机整数
55 | * @example: getRandomInt(4) ===> 3251
56 | * @param {*} length
57 | * @return {*}
58 | */
59 | declare const getRandomInt: (length: any) => number;
60 |
61 | /**
62 | * @description 正则是否是纯数字(纯整数)
63 | * @example: isPureNumber(abc) ===> false;isPureNumber(1.23) ===> false;isPureNumber(123) ===> true;
64 | * @param {string} str
65 | * @return {*}
66 | */
67 | declare function isPureNumber(str: string): boolean;
68 | /**
69 | * @description 正则验证手机号、邮箱是否合法
70 | * @param {string} str
71 | * @param {*} type
72 | * @return {*}
73 | */
74 | declare const regVerify: (str: string, type: 'phone' | 'email') => boolean;
75 | /**
76 | * @description 判断字符串的开头和结尾是否有空格,有空格就返回true,否则返回false
77 | * @param {string} value
78 | * @return {*}
79 | */
80 | declare const judgeStringSpace: (value: string) => boolean;
81 |
82 | /**
83 | * @description 获取地址栏参数(注意:请确保url是http://aaa.com/ds/?aa=1&bb=323这样子的)
84 | * @return {*}
85 | */
86 | declare const getUrlParams: (key?: string) => any;
87 |
88 | /**
89 | * @description: 字符串编码
90 | * @param {string} str
91 | * @return {*}
92 | */
93 | declare function strBtoa(str: string): string;
94 |
95 | declare class CacheModel {
96 | prefix: string;
97 | constructor(prefix: string);
98 | handlePrefix: (key: string) => string;
99 | getItem: (key: string) => string | null;
100 | setItem: (key: string, value: any) => void;
101 | removeItem: (key: string) => void;
102 | /**
103 | * @description 获取缓存
104 | * @param {string} key
105 | * @return {*}
106 | */
107 | getStorage: (key: string) => T | null;
108 | /**
109 | * @description 设置缓存
110 | * @param {*} key
111 | * @param {*} value
112 | */
113 | setStorage: (key: string, value: any) => void;
114 | /**
115 | * @description 清除缓存
116 | * @param {*} key
117 | */
118 | clearStorage: (key: string) => void;
119 | /**
120 | * @description 获取缓存,如果缓存已过期,会清除该缓存,并返回null
121 | * @param {*} key
122 | */
123 | getStorageExp: (key: string) => T | null;
124 | /**
125 | * @description 设置缓存以及缓存时长
126 | * @param {*} key
127 | * @param {*} value
128 | * @param {*} expires 缓存时长,单位:小时
129 | */
130 | setStorageExp: (key: string, value: any, expires: number) => void;
131 | }
132 |
133 | declare class LRUCache {
134 | capacity: number;
135 | data: Map;
136 | constructor(capacity: number);
137 | get(key: any): any;
138 | put(key: any, value: any): void;
139 | }
140 |
141 | declare class ConcurrentPoll {
142 | /** 任务队列 */
143 | tasks: any[];
144 | /** 最大并发数 */
145 | max: number;
146 | total: number;
147 | delay: number;
148 | done: () => void;
149 | constructor({ max, done, delay }: {
150 | max?: number | undefined;
151 | done: any;
152 | delay?: number | undefined;
153 | });
154 | addTask(task: any): void;
155 | run(): Promise | undefined;
156 | }
157 |
158 | declare const utils_arrayUnique: typeof arrayUnique;
159 | declare const utils_isIPad: typeof isIPad;
160 | declare const utils_copyToClipBoard: typeof copyToClipBoard;
161 | declare const utils_formatDate: typeof formatDate;
162 | declare const utils_getRangeRandom: typeof getRangeRandom;
163 | declare const utils_getRandomOne: typeof getRandomOne;
164 | declare const utils_getRandomString: typeof getRandomString;
165 | declare const utils_getRandomInt: typeof getRandomInt;
166 | declare const utils_isPureNumber: typeof isPureNumber;
167 | declare const utils_regVerify: typeof regVerify;
168 | declare const utils_judgeStringSpace: typeof judgeStringSpace;
169 | declare const utils_getUrlParams: typeof getUrlParams;
170 | declare const utils_strBtoa: typeof strBtoa;
171 | type utils_CacheModel = CacheModel;
172 | declare const utils_CacheModel: typeof CacheModel;
173 | type utils_LRUCache = LRUCache;
174 | declare const utils_LRUCache: typeof LRUCache;
175 | type utils_ConcurrentPoll = ConcurrentPoll;
176 | declare const utils_ConcurrentPoll: typeof ConcurrentPoll;
177 | declare namespace utils {
178 | export {
179 | utils_arrayUnique as arrayUnique,
180 | utils_isIPad as isIPad,
181 | utils_copyToClipBoard as copyToClipBoard,
182 | utils_formatDate as formatDate,
183 | utils_getRangeRandom as getRangeRandom,
184 | utils_getRandomOne as getRandomOne,
185 | utils_getRandomString as getRandomString,
186 | utils_getRandomInt as getRandomInt,
187 | utils_isPureNumber as isPureNumber,
188 | utils_regVerify as regVerify,
189 | utils_judgeStringSpace as judgeStringSpace,
190 | utils_getUrlParams as getUrlParams,
191 | utils_strBtoa as strBtoa,
192 | utils_CacheModel as CacheModel,
193 | utils_LRUCache as LRUCache,
194 | utils_ConcurrentPoll as ConcurrentPoll,
195 | };
196 | }
197 |
198 | declare const version: string;
199 |
200 | export { CacheModel, ConcurrentPoll, LRUCache, arrayUnique, copyToClipBoard, utils as default, formatDate, getRandomInt, getRandomOne, getRandomString, getRangeRandom, getUrlParams, isIPad, isPureNumber, judgeStringSpace, regVerify, strBtoa, version };
201 |
--------------------------------------------------------------------------------
/dist/index.esm.js:
--------------------------------------------------------------------------------
1 | var version$1 = "0.0.01";
2 |
3 | const arrayUnique = arr => {
4 | return [...new Set(arr)];
5 | };
6 |
7 | const isIPad = () => {
8 | const ua = navigator.userAgent.toLowerCase();
9 | const res = ua.match(/iPad/i);
10 | if (res?.length) {
11 | return true;
12 | }
13 | if (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1) {
14 | return true;
15 | }
16 | return false;
17 | };
18 |
19 | const copyToClipBoard = text => {
20 | const oInput = document.createElement("input");
21 | oInput.value = text;
22 | document.body.appendChild(oInput);
23 | oInput.select();
24 | document.execCommand("Copy");
25 | oInput.parentElement?.removeChild(oInput);
26 | };
27 |
28 | const formatDate = timetamp => {
29 | function addDateZero(num) {
30 | return num < 10 ? `0${num}` : num;
31 | }
32 | const date = new Date(timetamp);
33 | return {
34 | year: date.getFullYear(),
35 | month: addDateZero(date.getMonth() + 1),
36 | day: addDateZero(date.getDate()),
37 | hour: addDateZero(date.getHours()),
38 | minutes: addDateZero(date.getMinutes()),
39 | seconds: addDateZero(date.getSeconds())
40 | };
41 | };
42 |
43 | const getRangeRandom = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
44 | const getRandomOne = arr => arr[Math.floor(Math.random() * arr.length)];
45 | const getRandomString = length => {
46 | const str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
47 | let res = "";
48 | for (let i = 0; i < length; i += 1) {
49 | res += str.charAt(getRangeRandom(0, str.length - 1));
50 | }
51 | return res;
52 | };
53 | const getRandomInt = length => {
54 | if (length > 16 || length < 1) throw new Error("length\u7684\u8303\u56F4:[1,16]");
55 | let num = +`${Math.random()}`.slice(2, 2 + length);
56 | if (String(num).length !== length) {
57 | num = getRandomInt(length);
58 | }
59 | return num;
60 | };
61 |
62 | const debugLog = function (type) {
63 | for (var _len = arguments.length, data = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
64 | data[_key - 1] = arguments[_key];
65 | }
66 | console[type]("bi-utils", ...data);
67 | };
68 |
69 | function isPureNumber(str) {
70 | const regex = /^\d+$/;
71 | return regex.test(str);
72 | }
73 | const regVerify = (str, type) => {
74 | try {
75 | switch (type) {
76 | case "email":
77 | return /[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+/.test(str);
78 | case "phone":
79 | return /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/.test(str);
80 | }
81 | } catch (error) {
82 | debugLog("error", error);
83 | return false;
84 | }
85 | };
86 | const judgeStringSpace = value => {
87 | const reg1 = /^\s+/g;
88 | const reg2 = /\s+$/g;
89 | if (reg1.test(value) || reg2.test(value)) {
90 | return true;
91 | }
92 | return false;
93 | };
94 |
95 | const getUrlParams = key => {
96 | const url = decodeURIComponent(window.location.href);
97 | const str = url.split("?")[1];
98 | const obj = {};
99 | if (str) {
100 | const keys = str.split("&");
101 | keys.forEach(item => {
102 | const arr = item.split("=");
103 | obj[arr[0]] = arr[1];
104 | });
105 | }
106 | return key ? obj[key] : obj;
107 | };
108 |
109 | function strBtoa(str) {
110 | return window.btoa(window.encodeURIComponent(str));
111 | }
112 |
113 | class CacheModel {
114 | prefix = "";
115 | constructor(prefix) {
116 | this.prefix = prefix;
117 | }
118 | handlePrefix = key => {
119 | if (this.prefix === "") {
120 | return key;
121 | } else {
122 | return `${this.prefix}${key}`;
123 | }
124 | };
125 | getItem = key => {
126 | return localStorage.getItem(this.handlePrefix(key));
127 | };
128 | setItem = (key, value) => {
129 | return localStorage.setItem(this.handlePrefix(key), value);
130 | };
131 | removeItem = key => {
132 | return localStorage.removeItem(this.handlePrefix(key));
133 | };
134 | getStorage = key => {
135 | try {
136 | const res = this.getItem(key);
137 | if (res) {
138 | const data = JSON.parse(res);
139 | if (!data.createTime) {
140 | this.clearStorage(key);
141 | return null;
142 | } else {
143 | return data.value;
144 | }
145 | }
146 | return null;
147 | } catch (error) {
148 | debugLog("error", error);
149 | this.clearStorage(key);
150 | return null;
151 | }
152 | };
153 | setStorage = (key, value) => {
154 | try {
155 | const createTime = +new Date();
156 | this.setItem(key, JSON.stringify({
157 | value,
158 | createTime
159 | }));
160 | } catch (error) {
161 | debugLog("error", error);
162 | this.clearStorage(key);
163 | }
164 | };
165 | clearStorage = key => {
166 | try {
167 | this.removeItem(key);
168 | } catch (error) {
169 | debugLog("error", error);
170 | }
171 | };
172 | getStorageExp = key => {
173 | try {
174 | const res = this.getItem(key);
175 | if (res) {
176 | const data = JSON.parse(res);
177 | const expireTime = data.expireTime;
178 | const isExpired = expireTime < +new Date();
179 | if (!expireTime || isExpired) {
180 | this.clearStorage(key);
181 | return null;
182 | } else {
183 | return data.value;
184 | }
185 | }
186 | return null;
187 | } catch (error) {
188 | debugLog("error", error);
189 | this.clearStorage(key);
190 | return null;
191 | }
192 | };
193 | setStorageExp = (key, value, expires) => {
194 | try {
195 | if ([key, value, expires].includes(void 0)) {
196 | debugLog("error", "\u8BF7\u68C0\u67E5\u4F20\u5165\u7684\u53C2\u6570\uFF01");
197 | return;
198 | }
199 | const createTime = +new Date();
200 | const expireTime = createTime + expires * 60 * 60 * 1e3;
201 | this.setItem(key, JSON.stringify({
202 | value,
203 | createTime,
204 | expireTime
205 | }));
206 | } catch (error) {
207 | debugLog("error", error);
208 | this.clearStorage(key);
209 | }
210 | };
211 | }
212 |
213 | class LRUCache {
214 | capacity;
215 | data = /* @__PURE__ */new Map();
216 | constructor(capacity) {
217 | if (capacity < 1) throw new Error("capacity\u5FC5\u987B\u5927\u4E8E1\uFF01");
218 | this.capacity = capacity;
219 | }
220 | get(key) {
221 | const data = this.data;
222 | const value = data.get(key);
223 | if (!data.has(key)) return null;
224 | data.delete(key);
225 | data.set(key, value);
226 | return value;
227 | }
228 | put(key, value) {
229 | const data = this.data;
230 | if (data.has(key)) {
231 | data.delete(key);
232 | }
233 | data.set(key, value);
234 | if (data.size > this.capacity) {
235 | data.delete(data.keys().next().value);
236 | }
237 | }
238 | }
239 |
240 | class ConcurrentPoll {
241 | tasks = [];
242 | max = 0;
243 | total = 0;
244 | delay = 0;
245 | done;
246 | constructor(_ref) {
247 | let {
248 | max = 5,
249 | done,
250 | delay = 0
251 | } = _ref;
252 | this.tasks = [];
253 | this.total = 0;
254 | this.max = max;
255 | this.done = done;
256 | this.delay = delay;
257 | setTimeout(() => {
258 | this.run();
259 | }, 0);
260 | }
261 | addTask(task) {
262 | this.tasks.push(task);
263 | this.total += 1;
264 | }
265 | run() {
266 | if (this.tasks.length === 0) {
267 | return Promise.resolve("");
268 | }
269 | const min = Math.min(this.tasks.length, this.max);
270 | for (let i = 0; i < min; i += 1) {
271 | this.max -= 1;
272 | const task = this.tasks.shift();
273 | task().then(() => {}).catch(error => {
274 | debugLog("error", error);
275 | }).finally(() => {
276 | setTimeout(() => {
277 | this.max += 1;
278 | this.total -= 1;
279 | this.run();
280 | if (this.total === 0) {
281 | this.done?.();
282 | }
283 | }, this.delay);
284 | });
285 | }
286 | }
287 | }
288 |
289 | var utils = /*#__PURE__*/Object.freeze({
290 | __proto__: null,
291 | arrayUnique: arrayUnique,
292 | isIPad: isIPad,
293 | copyToClipBoard: copyToClipBoard,
294 | formatDate: formatDate,
295 | getRangeRandom: getRangeRandom,
296 | getRandomOne: getRandomOne,
297 | getRandomString: getRandomString,
298 | getRandomInt: getRandomInt,
299 | isPureNumber: isPureNumber,
300 | regVerify: regVerify,
301 | judgeStringSpace: judgeStringSpace,
302 | getUrlParams: getUrlParams,
303 | strBtoa: strBtoa,
304 | CacheModel: CacheModel,
305 | LRUCache: LRUCache,
306 | ConcurrentPoll: ConcurrentPoll
307 | });
308 |
309 | const version = version$1;
310 |
311 | export { CacheModel, ConcurrentPoll, LRUCache, arrayUnique, copyToClipBoard, utils as default, formatDate, getRandomInt, getRandomOne, getRandomString, getRangeRandom, getUrlParams, isIPad, isPureNumber, judgeStringSpace, regVerify, strBtoa, version };
312 |
--------------------------------------------------------------------------------
/dist/index.js:
--------------------------------------------------------------------------------
1 | import{version as a}from"./package.json.js";import*as s from"./js/index.js";import{arrayUnique as r}from"./js/array.js";import{isIPad as o}from"./js/bom.js";import{copyToClipBoard as e}from"./js/dom.js";import{formatDate as m}from"./js/format.js";import{getRandomInt as t,getRandomOne as i,getRandomString as n,getRangeRandom as j}from"./js/random.js";import{isPureNumber as g,judgeStringSpace as p,regVerify as d}from"./js/reg.js";import{getUrlParams as c}from"./js/url.js";import{strBtoa as f}from"./js/utils.js";import{CacheModel as l}from"./class/cache.js";import{LRUCache as u}from"./class/lru-cache.js";import{ConcurrentPoll as R}from"./class/queue.js";const C=a;export{l as CacheModel,R as ConcurrentPoll,u as LRUCache,r as arrayUnique,e as copyToClipBoard,s as default,m as formatDate,t as getRandomInt,i as getRandomOne,n as getRandomString,j as getRangeRandom,c as getUrlParams,o as isIPad,g as isPureNumber,p as judgeStringSpace,d as regVerify,f as strBtoa,C as version};
2 |
--------------------------------------------------------------------------------
/dist/js/array.js:
--------------------------------------------------------------------------------
1 | const e=e=>[...new Set(e)];export{e as arrayUnique};
2 |
--------------------------------------------------------------------------------
/dist/js/bom.js:
--------------------------------------------------------------------------------
1 | const a=()=>!!(navigator.userAgent.toLowerCase().match(/iPad/i)?.length||"MacIntel"===navigator.platform&&navigator.maxTouchPoints>1);export{a as isIPad};
2 |
--------------------------------------------------------------------------------
/dist/js/dom.js:
--------------------------------------------------------------------------------
1 | const e=e=>{const o=document.createElement("input");o.value=e,document.body.appendChild(o),o.select(),document.execCommand("Copy"),o.parentElement?.removeChild(o)};export{e as copyToClipBoard};
2 |
--------------------------------------------------------------------------------
/dist/js/format.js:
--------------------------------------------------------------------------------
1 | const t=t=>{function e(t){return t<10?`0${t}`:t}const n=new Date(t);return{year:n.getFullYear(),month:e(n.getMonth()+1),day:e(n.getDate()),hour:e(n.getHours()),minutes:e(n.getMinutes()),seconds:e(n.getSeconds())}};export{t as formatDate};
2 |
--------------------------------------------------------------------------------
/dist/js/index.js:
--------------------------------------------------------------------------------
1 | import{arrayUnique as a}from"./array.js";import{isIPad as r}from"./bom.js";import{copyToClipBoard as s}from"./dom.js";import{formatDate as o}from"./format.js";import{getRandomInt as e,getRandomOne as m,getRandomString as t,getRangeRandom as i}from"./random.js";import{isPureNumber as n,judgeStringSpace as g,regVerify as d}from"./reg.js";import{getUrlParams as p}from"./url.js";import{strBtoa as c}from"./utils.js";import{CacheModel as f}from"../class/cache.js";import{LRUCache as l}from"../class/lru-cache.js";import{ConcurrentPoll as u}from"../class/queue.js";export{f as CacheModel,u as ConcurrentPoll,l as LRUCache,a as arrayUnique,s as copyToClipBoard,o as formatDate,e as getRandomInt,m as getRandomOne,t as getRandomString,i as getRangeRandom,p as getUrlParams,r as isIPad,n as isPureNumber,g as judgeStringSpace,d as regVerify,c as strBtoa};
2 |
--------------------------------------------------------------------------------
/dist/js/random.js:
--------------------------------------------------------------------------------
1 | const t=(t,n)=>Math.floor(Math.random()*(n-t+1))+t,n=t=>t[Math.floor(Math.random()*t.length)],a=n=>{const a="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";let r="";for(let e=0;e{if(t>16||t<1)throw new Error("length的范围:[1,16]");let n=+`${Math.random()}`.slice(2,2+t);return String(n).length!==t&&(n=r(t)),n};export{r as getRandomInt,n as getRandomOne,a as getRandomString,t as getRangeRandom};
2 |
--------------------------------------------------------------------------------
/dist/js/reg.js:
--------------------------------------------------------------------------------
1 | import{debugLog as t}from"../utils/index.js";function e(t){return/^\d+$/.test(t)}const r=(e,r)=>{try{switch(r){case"email":return/[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+/.test(e);case"phone":return/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/.test(e)}}catch(e){return t("error",e),!1}},s=t=>!(!/^\s+/g.test(t)&&!/\s+$/g.test(t));export{e as isPureNumber,s as judgeStringSpace,r as regVerify};
2 |
--------------------------------------------------------------------------------
/dist/js/url.js:
--------------------------------------------------------------------------------
1 | const o=o=>{const t=decodeURIComponent(window.location.href).split("?")[1],n={};return t&&t.split("&").forEach((o=>{const t=o.split("=");n[t[0]]=t[1]})),o?n[o]:n};export{o as getUrlParams};
2 |
--------------------------------------------------------------------------------
/dist/js/utils.js:
--------------------------------------------------------------------------------
1 | function o(o){return window.btoa(window.encodeURIComponent(o))}export{o as strBtoa};
2 |
--------------------------------------------------------------------------------
/dist/package.json.js:
--------------------------------------------------------------------------------
1 | var r="0.0.01";export{r as version};
2 |
--------------------------------------------------------------------------------
/dist/utils/index.js:
--------------------------------------------------------------------------------
1 | const o=function(o){for(var n=arguments.length,e=new Array(n>1?n-1:0),r=1;r RegExp(`^${name}($|/)`)); // 在打包esm和cjs的时候,排除所有依赖,让上层应用去处理(如果不排除就会有重复的polyfill)
27 |
28 | const esbuildPlugin = (options?: ESBuildOptions) =>
29 | esbuild({
30 | minify: false,
31 | // sourceMap: true, // 默认就是true
32 | ...options,
33 | });
34 |
35 | // my-name转化为MyName
36 | export const toPascalCase = (input: string) => {
37 | const res = input.replace(input[0], input[0].toUpperCase());
38 | return res.replace(/-(\w)/g, function (all, letter) {
39 | return letter.toUpperCase();
40 | });
41 | };
42 |
43 | const umdConfig = (prod = false): RollupOptions => ({
44 | input: './src/index.ts',
45 | output: {
46 | format: 'umd',
47 | file: prod ? 'dist/index.min.js' : 'dist/index.js',
48 | name: toPascalCase(pkg.name),
49 | /**
50 | * exports默认值是auto,可选:default、none。https://rollupjs.org/guide/zh/#exports
51 | * 如果我们源代码默认导出和具名导出一起使用,编译的时候会报警告(!) Mixing named and default exports
52 | * 设置exports: 'named'就不会报警告了(实际上只是不会报警告了,设不设置named对实际打包的结果都没影响)
53 | * 如果我们源代码没有默认导出和具名导出一起使用,但是设置了exports: 'named',会生成:exports["default"] = BilldUtils;
54 | * 别人通过cjs导入的话,就得const BilldUtils = require("billd-utils").default;才能拿到默认导出;如果不使用exports: 'named',
55 | * 默认会生成:module.exports = BilldUtils;别人通过cjs导入的话,就正常的const BilldUtils = require("billd-utils");即可
56 | */
57 | exports: 'named', // babel-utils默认导出和具名导出混用了,因此需要设置exports: 'named'
58 | },
59 | plugins: [
60 | commonjs(),
61 | nodeResolve(),
62 | esbuildPlugin({ minify: prod ? true : false }),
63 | babel({
64 | exclude: 'node_modules/**', // 只编译我们的源代码,最好加上它,否则打包umd可能会报错
65 | extensions: [...DEFAULT_EXTENSIONS, '.ts'],
66 | // 这里面的plugins如果和babel.config.js里的plugins冲突,
67 | // 会执行这里的plugins,不会执行babel.config.js里的plugins
68 | plugins: [
69 | [
70 | /**
71 | * @babel/plugin-transform-runtime
72 | * useBuiltIns和polyfill选项在 v7 中被删除,只是将其设为默认值。
73 | */
74 | '@babel/plugin-transform-runtime',
75 | {
76 | // absoluteRuntime: false, // boolean或者string,默认为false。
77 | /**
78 | * corejs:false, 2,3或{ version: 2 | 3, proposals: boolean }, 默认为false
79 | * 设置对应值需要安装对应的包:
80 | * false npm install --save @babel/runtime
81 | * 2 npm install --save @babel/runtime-corejs2
82 | * 3 npm install --save @babel/runtime-corejs3
83 | */
84 | corejs: 3,
85 | /**
86 | * helpers: boolean, 默认true。在纯babel的情况下:
87 | * 如果是true,就会把需要他runtime包给引进来,如:import _defineProperty from "@babel/runtime/helpers/defineProperty"
88 | * 如果是false,就会把需要的runtime包里面的代码给嵌进bundle里,如function _defineProperty(){}
89 | * 设置false的话,会导致同一个runtime包里面的代码被很多文件设置,产生冗余的代码。而且因为虽然是同一
90 | * 份runtime包里面的代码,但是他们在不同的文件(模块)里面,都有自己的作用域,因此在使用类似webpack之类的
91 | * 打包工具打包的时候,不会做优化。因此推荐设置true,这样可以通过静态分析的手段进行打包,减少打包后的代码体积。
92 | */
93 | // helpers: true, // 当helpers设置true的时候,babelHelpers需要设置为runtime
94 | helpers: false, // 当helpers设置false的时候,babelHelpers需要设置为bundled
95 | // regenerator: true, // 切换生成器函数是否转换为使用不污染全局范围的再生器运行时。默认为true
96 | version: babelRuntimeVersion,
97 | },
98 | ],
99 | ],
100 | /**
101 | * babelHelpers,建议显式配置此选项(即使使用其默认值)
102 | * runtime: 您应该使用此功能,尤其是在使用汇总构建库时,它结合external使用
103 | * bundled: 如果您希望生成的捆绑包包含这些帮助程序(每个最多一份),您应该使用它。特别是在捆绑应用程序代码时很有用
104 | * 如果babelHelpers设置成bundled,@babel/plugin-transform-runtime的helpers得设置false!
105 | * 如果babelHelpers设置成runtime,@babel/plugin-transform-runtime的helpers得设置true!
106 | * 在打包esm和cjs时,使用runtime,并且配合external;在打包umd时,使用bundled,并且不要用external,如果打包umd时使
107 | * 用了runtime但是没有配置external,会导致打包重复的polyfill,虽然打包的时候不报错,但是引入包使用的时候会报错
108 | */
109 | babelHelpers: 'bundled', // 默认bundled,可选:"bundled" | "runtime" | "inline" | "external" | undefined
110 | // babelHelpers: 'runtime', // 默认bundled,可选:"bundled" | "runtime" | "inline" | "external" | undefined
111 | }),
112 | json(),
113 | prod && terser(),
114 | ].filter(Boolean),
115 | });
116 |
117 | export default defineConfig([
118 | // esm
119 | {
120 | input: './src/index.ts',
121 | output: {
122 | format: 'esm',
123 | file: 'dist/index.esm.js',
124 | },
125 | external: allDep,
126 | plugins: [
127 | /**
128 | * @rollup/plugin-commonjs插件主要是将commonjs转换为esm
129 | * @rollup/plugin-commonjs一般和@rollup/plugin-node-resolve一起使用
130 | * @babel/plugin-transform-runtime使用corejs:3(即@babel/runtime-corejs3),
131 | * 而@babel/runtime-corejs3源码是使用commonjs规范写的,因此需要添加@rollup/plugin-commonjs插件
132 | * 让rollup支持commonjs 规范,识别 commonjs 规范的依赖。
133 | * 打包esm和cjs时,当前的billd-monorepo里面的源码基本都没有依赖第三方的包,并且排除了所有外部依赖,因此
134 | * 这里其实不需要用@rollup/plugin-commonjs插件
135 | * @rollup/plugin-commonjs和@rollup/plugin-babel一起用的使用,先使用@rollup/plugin-commonjs,再用@rollup/plugin-babel
136 | * https://github.com/rollup/plugins/blob/master/packages/babel/README.md#using-with-rollupplugin-commonjs
137 | */
138 | commonjs(),
139 | /**
140 | * @rollup/plugin-node-resolve只对引入node_modules包的代码起作用
141 | * 不使用@rollup/plugin-node-resolve插件的话,import { ref } from 'vue';就不会把node_modules包
142 | * 里的vue的ref的代码引进来,而是会原封不动的把import { ref } from 'vue';放到打包的代码里面;
143 | * 使用@rollup/plugin-node-resolve插件的话,会将node_modules包里的vue的ref的代码都引进来
144 | * 打包esm和cjs时,当前的billd-monorepo里面的源码基本都没有依赖第三方的包,并且排除了所有外部依赖,因此
145 | * 这里其实不需要用@rollup/plugin-node-resolve插件
146 | */
147 | nodeResolve(),
148 | esbuildPlugin(),
149 | json(),
150 | babel({
151 | exclude: 'node_modules/**', // 只编译我们的源代码,最好加上它,否则打包umd可能会报错
152 | extensions: [...DEFAULT_EXTENSIONS, '.ts'],
153 | // 这里面的plugins如果和babel.config.js里的plugins冲突,
154 | // 会执行这里的plugins,不会执行babel.config.js里的plugins
155 | plugins: [
156 | [
157 | /**
158 | * @babel/plugin-transform-runtime
159 | * useBuiltIns和polyfill选项在 v7 中被删除,只是将其设为默认值。
160 | */
161 | '@babel/plugin-transform-runtime',
162 | {
163 | // absoluteRuntime: false, // boolean或者string,默认为false。
164 | /**
165 | * corejs:false, 2,3或{ version: 2 | 3, proposals: boolean }, 默认为false
166 | * 设置对应值需要安装对应的包:
167 | * false npm install --save @babel/runtime
168 | * 2 npm install --save @babel/runtime-corejs2
169 | * 3 npm install --save @babel/runtime-corejs3
170 | */
171 | corejs: 3,
172 | /**
173 | * helpers: boolean, 默认true。在纯babel的情况下:
174 | * 如果是true,就会把需要他runtime包给引进来,如:import _defineProperty from "@babel/runtime/helpers/defineProperty"
175 | * 如果是false,就会把需要的runtime包里面的代码给嵌进bundle里,如function _defineProperty(){}
176 | * 设置false的话,会导致同一个runtime包里面的代码被很多文件设置,产生冗余的代码。而且因为虽然是同一
177 | * 份runtime包里面的代码,但是他们在不同的文件(模块)里面,都有自己的作用域,因此在使用类似webpack之类的
178 | * 打包工具打包的时候,不会做优化。因此推荐设置true,这样可以通过静态分析的手段进行打包,减少打包后的代码体积。
179 | */
180 | helpers: true, // 当helpers设置true的时候,babelHelpers需要设置为runtime
181 | // helpers: false, // 当helpers设置false的时候,babelHelpers需要设置为bundled
182 | // regenerator: true, // 切换生成器函数是否转换为使用不污染全局范围的再生器运行时。默认为true
183 | version: babelRuntimeVersion,
184 | },
185 | ],
186 | ],
187 | /**
188 | * babelHelpers,建议显式配置此选项(即使使用其默认值)
189 | * runtime: 您应该使用此功能,尤其是在使用汇总构建库时,它结合external使用
190 | * bundled: 如果您希望生成的捆绑包包含这些帮助程序(每个最多一份),您应该使用它。特别是在捆绑应用程序代码时很有用
191 | * 如果babelHelpers设置成bundled,@babel/plugin-transform-runtime的helpers得设置false!
192 | * 如果babelHelpers设置成runtime,@babel/plugin-transform-runtime的helpers得设置true!
193 | * 在打包esm和cjs时,使用runtime,并且配合external;在打包umd时,使用bundled,并且不要用external,如果打包umd时使
194 | * 用了runtime但是没有配置external,会导致打包重复的polyfill,虽然打包的时候不报错,但是引入包使用的时候会报错
195 | */
196 | // babelHelpers: 'bundled', // 默认bundled,可选:"bundled" | "runtime" | "inline" | "external" | undefined
197 | babelHelpers: 'runtime', // 默认bundled,可选:"bundled" | "runtime" | "inline" | "external" | undefined
198 | }),
199 | ],
200 | },
201 | // cjs
202 | {
203 | input: './src/index.ts',
204 | output: {
205 | format: 'cjs',
206 | file: 'dist/index.cjs.js',
207 | /**
208 | * exports默认值是auto,可选:default、none。https://rollupjs.org/guide/zh/#exports
209 | * 如果我们源代码默认导出和具名导出一起使用,编译的时候会报警告(!) Mixing named and default exports
210 | * 设置exports: 'named'就不会报警告了(实际上只是不会报警告了,设不设置named对实际打包的结果都没影响)
211 | * 如果我们源代码没有默认导出和具名导出一起使用,但是设置了exports: 'named',会生成:exports["default"] = BilldUtils;
212 | * 别人通过cjs导入的话,就得const BilldUtils = require("billd-utils").default;才能拿到默认导出;如果不使用exports: 'named',
213 | * 默认会生成:module.exports = BilldUtils;别人通过cjs导入的话,就正常的const BilldUtils = require("billd-utils");即可
214 | */
215 | exports: 'named', // babel-utils默认导出和具名导出混用了,因此需要设置exports: 'named'
216 | },
217 | external: allDep,
218 | plugins: [
219 | /**
220 | * @rollup/plugin-commonjs插件主要是将commonjs转换为esm
221 | * @rollup/plugin-commonjs一般和@rollup/plugin-node-resolve一起使用
222 | * @babel/plugin-transform-runtime使用corejs:3(即@babel/runtime-corejs3),
223 | * 而@babel/runtime-corejs3源码是使用commonjs规范写的,因此需要添加@rollup/plugin-commonjs插件
224 | * 让rollup支持commonjs 规范,识别 commonjs 规范的依赖。
225 | * 打包esm和cjs时,当前的billd-monorepo里面的源码基本都没有依赖第三方的包,并且排除了所有外部依赖,因此
226 | * 这里其实不需要用@rollup/plugin-commonjs插件
227 | * @rollup/plugin-commonjs和@rollup/plugin-babel一起用的使用,先使用@rollup/plugin-commonjs,再用@rollup/plugin-babel
228 | * https://github.com/rollup/plugins/blob/master/packages/babel/README.md#using-with-rollupplugin-commonjs
229 | */
230 | commonjs(),
231 | /**
232 | * @rollup/plugin-node-resolve只对引入node_modules包的代码起作用
233 | * 不使用@rollup/plugin-node-resolve插件的话,import { ref } from 'vue';就不会把node_modules包
234 | * 里的vue的ref的代码引进来,而是会原封不动的把import { ref } from 'vue';放到打包的代码里面;
235 | * 使用@rollup/plugin-node-resolve插件的话,会将node_modules包里的vue的ref的代码都引进来
236 | * 打包esm和cjs时,当前的billd-monorepo里面的源码基本都没有依赖第三方的包,并且排除了所有外部依赖,因此
237 | * 这里其实不需要用@rollup/plugin-node-resolve插件
238 | */
239 | nodeResolve(),
240 | esbuildPlugin(),
241 | json(),
242 | babel({
243 | exclude: 'node_modules/**', // 只编译我们的源代码,最好加上它,否则打包umd可能会报错
244 | extensions: [...DEFAULT_EXTENSIONS, '.ts'],
245 | // 这里面的plugins如果和babel.config.js里的plugins冲突,
246 | // 会执行这里的plugins,不会执行babel.config.js里的plugins
247 | plugins: [
248 | [
249 | /**
250 | * @babel/plugin-transform-runtime
251 | * useBuiltIns和polyfill选项在 v7 中被删除,只是将其设为默认值。
252 | */
253 | '@babel/plugin-transform-runtime',
254 | {
255 | // absoluteRuntime: false, // boolean或者string,默认为false。
256 | /**
257 | * corejs:false, 2,3或{ version: 2 | 3, proposals: boolean }, 默认为false
258 | * 设置对应值需要安装对应的包:
259 | * false npm install --save @babel/runtime
260 | * 2 npm install --save @babel/runtime-corejs2
261 | * 3 npm install --save @babel/runtime-corejs3
262 | */
263 | corejs: 3,
264 | /**
265 | * helpers: boolean, 默认true。在纯babel的情况下:
266 | * 如果是true,就会把需要他runtime包给引进来,如:import _defineProperty from "@babel/runtime/helpers/defineProperty"
267 | * 如果是false,就会把需要的runtime包里面的代码给嵌进bundle里,如function _defineProperty(){}
268 | * 设置false的话,会导致同一个runtime包里面的代码被很多文件设置,产生冗余的代码。而且因为虽然是同一
269 | * 份runtime包里面的代码,但是他们在不同的文件(模块)里面,都有自己的作用域,因此在使用类似webpack之类的
270 | * 打包工具打包的时候,不会做优化。因此推荐设置true,这样可以通过静态分析的手段进行打包,减少打包后的代码体积。
271 | */
272 | helpers: true, // 当helpers设置true的时候,babelHelpers需要设置为runtime
273 | // helpers: false, // 当helpers设置false的时候,babelHelpers需要设置为bundled
274 | // regenerator: true, // 切换生成器函数是否转换为使用不污染全局范围的再生器运行时。默认为true
275 | version: babelRuntimeVersion,
276 | },
277 | ],
278 | ],
279 | /**
280 | * babelHelpers,建议显式配置此选项(即使使用其默认值)
281 | * runtime: 您应该使用此功能,尤其是在使用汇总构建库时,它结合external使用
282 | * bundled: 如果您希望生成的捆绑包包含这些帮助程序(每个最多一份),您应该使用它。特别是在捆绑应用程序代码时很有用
283 | * 如果babelHelpers设置成bundled,@babel/plugin-transform-runtime的helpers得设置false!
284 | * 如果babelHelpers设置成runtime,@babel/plugin-transform-runtime的helpers得设置true!
285 | * 在打包esm和cjs时,使用runtime,并且配合external;在打包umd时,使用bundled,并且不要用external,如果打包umd时使
286 | * 用了runtime但是没有配置external,会导致打包重复的polyfill,虽然打包的时候不报错,但是引入包使用的时候会报错
287 | */
288 | // babelHelpers: 'bundled', // 默认bundled,可选:"bundled" | "runtime" | "inline" | "external" | undefined
289 | babelHelpers: 'runtime', // 默认bundled,可选:"bundled" | "runtime" | "inline" | "external" | undefined
290 | }),
291 | ],
292 | },
293 | // .d.ts
294 | {
295 | input: './src/index.ts',
296 | output: {
297 | file: './dist/index.d.ts',
298 | name: pkg.name,
299 | },
300 | plugins: [dtsPlugin()],
301 | },
302 | // umd
303 | umdConfig(),
304 | // umd prod
305 | umdConfig(true),
306 | ]);
307 |
--------------------------------------------------------------------------------
/scripts/build.ts:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process';
2 |
3 | import { chalkERROR } from './utils';
4 |
5 | const watch = process.argv.includes('--watch');
6 |
7 | // rollup打包
8 | const rollupBuild = () => {
9 | execSync(`pnpm run build:rollup${watch ? ' --watch' : ''}`, {
10 | stdio: 'inherit',
11 | });
12 | };
13 |
14 | (() => {
15 | try {
16 | rollupBuild();
17 | // npm publish默认会带上根目录的LICENSE、README.md、package.json
18 | // copyFile();
19 | } catch (error) {
20 | console.log(chalkERROR(`!!!本地构建失败!!!`));
21 | console.log(error);
22 | console.log(chalkERROR(`!!!本地构建失败!!!`));
23 | }
24 | })();
25 |
--------------------------------------------------------------------------------
/scripts/publish.ts:
--------------------------------------------------------------------------------
1 | import { execSync } from 'child_process';
2 |
3 | import pkg from '../package.json';
4 | import { chalkERROR, chalkSUCCESS } from './utils';
5 |
6 | const command = 'npm publish';
7 |
8 | // git push
9 | execSync(`git push origin v${pkg.version}`, { stdio: 'inherit' });
10 | execSync(`git push`, { stdio: 'inherit' });
11 |
12 | try {
13 | execSync(command, {
14 | stdio: 'inherit',
15 | });
16 | console.log(chalkSUCCESS(`发布${pkg.name}@${pkg.version}成功!`));
17 | } catch (error) {
18 | console.log(error);
19 | console.log(chalkERROR(`发布${pkg.name}@${pkg.version}失败!`));
20 | }
21 |
--------------------------------------------------------------------------------
/scripts/release.ts:
--------------------------------------------------------------------------------
1 | import { exec, execSync } from 'child_process';
2 | import path from 'path';
3 |
4 | import { readJSONSync, writeJSONSync } from 'fs-extra';
5 | import inquirer from 'inquirer';
6 | import semver from 'semver';
7 |
8 | import { chalkERROR, chalkINFO, chalkSUCCESS } from './utils';
9 |
10 | const { version: currentVersion } = readJSONSync('package.json'); // 项目根目录的package.json
11 |
12 | export const DIR_ROOT = path.resolve(__dirname, '..');
13 | export const DIR_PACKAGES = path.resolve(__dirname, '../packages');
14 |
15 | const preId =
16 | semver.prerelease(currentVersion) && semver.prerelease(currentVersion)[0];
17 |
18 | const versionChoices = [
19 | 'patch',
20 | 'minor',
21 | 'major',
22 | ...(preId ? ['prepatch', 'preminor', 'premajor', 'prerelease'] : []),
23 | ];
24 |
25 | const inc = (i: string): string => semver.inc(currentVersion, i, preId);
26 | let targetVersion: string;
27 |
28 | const selectReleaseVersion = async () => {
29 | const { release } = await inquirer.prompt([
30 | {
31 | type: 'list',
32 | name: 'release',
33 | message: 'Select release type',
34 | choices: versionChoices.map((i) => `${i} (${inc(i)})`),
35 | },
36 | ]);
37 | const pkg = readJSONSync(path.resolve(__dirname, '../package.json')); // 项目根目录的package.json
38 | targetVersion = release.match(/\((.*)\)/)[1];
39 |
40 | const { confirmRelease } = await inquirer.prompt([
41 | {
42 | type: 'confirm',
43 | name: 'confirmRelease',
44 | default: false,
45 | message: `Confirm release v${targetVersion}?`,
46 | },
47 | ]);
48 |
49 | if (confirmRelease) {
50 | console.log(chalkINFO(`开始本地发布v${targetVersion}...`));
51 |
52 | // 更新根目录的package.json版本号
53 | writeJSONSync(
54 | 'package.json',
55 | { ...pkg, version: targetVersion },
56 | { spaces: 2 }
57 | );
58 |
59 | // pnpm run build
60 | execSync(`pnpm run build`, { stdio: 'inherit' });
61 |
62 | // 生成changelog
63 | execSync(`pnpm run changelog`, { stdio: 'inherit' });
64 |
65 | // git commit
66 | execSync(`git add .`, { stdio: 'inherit' });
67 | execSync(`git commit -m 'chore(release): v${targetVersion}'`, {
68 | stdio: 'inherit',
69 | });
70 |
71 | // git tag
72 | execSync(`git tag v${targetVersion}`, { stdio: 'inherit' });
73 | } else {
74 | console.log(chalkERROR(`取消本地发布!`));
75 | }
76 | };
77 |
78 | function gitIsClean() {
79 | return new Promise((resolve, reject) => {
80 | exec('git status -s', (error, stdout, stderr) => {
81 | if (error || stderr) {
82 | reject(error || stderr);
83 | }
84 | if (stdout.length) {
85 | reject('请确保本地代码已经提交git');
86 | } else {
87 | resolve('ok');
88 | }
89 | });
90 | });
91 | }
92 |
93 | (async () => {
94 | await gitIsClean();
95 | await selectReleaseVersion();
96 | })().then(
97 | () => {
98 | console.log(chalkSUCCESS(`本地发布v${targetVersion}成功!`));
99 | },
100 | (rej) => {
101 | console.log(rej);
102 | console.log(chalkERROR(`!!!本地发布v${targetVersion}失败!!!`));
103 | }
104 | );
105 |
--------------------------------------------------------------------------------
/scripts/utils.ts:
--------------------------------------------------------------------------------
1 | import nodeChalk from 'chalk';
2 |
3 | export const chalk = nodeChalk;
4 | export const chalkINFO = (v) =>
5 | `${chalk.bgBlueBright.black(' INFO ')} ${chalk.blueBright(v)}`;
6 | export const chalkSUCCESS = (v) =>
7 | `${chalk.bgGreenBright.black(' SUCCESS ')} ${chalk.greenBright(v)}`;
8 | export const chalkERROR = (v) =>
9 | `${chalk.bgRedBright.black(' ERROR ')} ${chalk.redBright(v)}`;
10 | export const chalkWARN = (v) =>
11 | `${chalk.bgHex('#FFA500').black(' WARN ')} ${chalk.hex('#FFA500')(v)}`;
12 |
--------------------------------------------------------------------------------
/src/billd-utils.d.ts:
--------------------------------------------------------------------------------
1 | declare global {
2 | import utils from 'bi-utils';
3 | }
4 |
--------------------------------------------------------------------------------
/src/class/cache.ts:
--------------------------------------------------------------------------------
1 | // TIP: ctrl+cmd+t,生成函数注释
2 | import { debugLog } from '../utils/index';
3 |
4 | export class CacheModel {
5 | prefix = '';
6 |
7 | constructor(prefix: string) {
8 | this.prefix = prefix;
9 | }
10 |
11 | handlePrefix = (key: string) => {
12 | if (this.prefix === '') {
13 | return key;
14 | } else {
15 | return `${this.prefix}${key}`;
16 | }
17 | };
18 |
19 | getItem = (key: string) => {
20 | return localStorage.getItem(this.handlePrefix(key));
21 | };
22 |
23 | setItem = (key: string, value: any) => {
24 | return localStorage.setItem(this.handlePrefix(key), value);
25 | };
26 |
27 | removeItem = (key: string) => {
28 | return localStorage.removeItem(this.handlePrefix(key));
29 | };
30 |
31 | /**
32 | * @description 获取缓存
33 | * @param {string} key
34 | * @return {*}
35 | */
36 | getStorage = (key: string): T | null => {
37 | try {
38 | const res = this.getItem(key);
39 | if (res) {
40 | const data = JSON.parse(res);
41 | // 如果createTime没有值,则判断该缓存不合法;清除
42 | if (!data.createTime) {
43 | this.clearStorage(key);
44 | return null;
45 | } else {
46 | return data.value;
47 | }
48 | }
49 | return null;
50 | } catch (error) {
51 | debugLog('error', error);
52 | this.clearStorage(key);
53 | return null;
54 | }
55 | };
56 |
57 | /**
58 | * @description 设置缓存
59 | * @param {*} key
60 | * @param {*} value
61 | */
62 | setStorage = (key: string, value: any) => {
63 | try {
64 | const createTime = +new Date();
65 | this.setItem(key, JSON.stringify({ value, createTime }));
66 | } catch (error) {
67 | debugLog('error', error);
68 | this.clearStorage(key);
69 | }
70 | };
71 |
72 | /**
73 | * @description 清除缓存
74 | * @param {*} key
75 | */
76 | clearStorage = (key: string) => {
77 | try {
78 | this.removeItem(key);
79 | } catch (error) {
80 | debugLog('error', error);
81 | }
82 | };
83 |
84 | /**
85 | * @description 获取缓存,如果缓存已过期,会清除该缓存,并返回null
86 | * @param {*} key
87 | */
88 | getStorageExp = (key: string): T | null => {
89 | try {
90 | const res = this.getItem(key);
91 | if (res) {
92 | const data = JSON.parse(res);
93 | const expireTime = data.expireTime;
94 | const isExpired = expireTime < +new Date();
95 | // 如果expireTime没有值,则判断该缓存不合法;清除
96 | // 如果expireTime有值,但小于当前时间,则代表已过期;清除
97 | if (!expireTime || isExpired) {
98 | this.clearStorage(key);
99 | return null;
100 | } else {
101 | return data.value;
102 | }
103 | }
104 | return null;
105 | } catch (error) {
106 | debugLog('error', error);
107 | this.clearStorage(key);
108 | return null;
109 | }
110 | };
111 |
112 | /**
113 | * @description 设置缓存以及缓存时长
114 | * @param {*} key
115 | * @param {*} value
116 | * @param {*} expires 缓存时长,单位:小时
117 | */
118 | setStorageExp = (key: string, value: any, expires: number) => {
119 | try {
120 | if ([key, value, expires].includes(undefined)) {
121 | debugLog('error', '请检查传入的参数!');
122 | return;
123 | }
124 | const createTime = +new Date();
125 | const expireTime = createTime + expires * 60 * 60 * 1000;
126 | this.setItem(key, JSON.stringify({ value, createTime, expireTime }));
127 | } catch (error) {
128 | debugLog('error', error);
129 | this.clearStorage(key);
130 | }
131 | };
132 | }
133 |
--------------------------------------------------------------------------------
/src/class/lru-cache.ts:
--------------------------------------------------------------------------------
1 | // TIP: ctrl+cmd+t,生成函数注释
2 |
3 | export class LRUCache {
4 | capacity: number;
5 | data = new Map();
6 |
7 | constructor(capacity: number) {
8 | if (capacity < 1) throw new Error('capacity必须大于1!');
9 | this.capacity = capacity;
10 | }
11 |
12 | get(key) {
13 | const data = this.data;
14 | const value = data.get(key);
15 |
16 | // 如果缓存里没有这个key,则返回null
17 | if (!data.has(key)) return null;
18 |
19 | // 如果缓存里有这个key,则删了旧的缓存,再设置新缓存(目的是让读取的这个key移到最后面)
20 | data.delete(key);
21 | data.set(key, value);
22 |
23 | return value;
24 | }
25 |
26 | put(key, value) {
27 | const data = this.data;
28 |
29 | if (data.has(key)) {
30 | // 如果缓存里有,则删了旧的缓存
31 | data.delete(key);
32 | }
33 |
34 | // 不管缓存里有没有,put操作都要设置缓存
35 | data.set(key, value);
36 |
37 | // 最后判断缓存是否超过capacity,如果超过则删掉最久没使用的缓存(也就是第一个)
38 | if (data.size > this.capacity) {
39 | data.delete(data.keys().next().value);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/class/queue.ts:
--------------------------------------------------------------------------------
1 | // TIP: ctrl+cmd+t,生成函数注释
2 | import { debugLog } from '../utils/index';
3 |
4 | export class ConcurrentPoll {
5 | /** 任务队列 */
6 | tasks: any[] = [];
7 | /** 最大并发数 */
8 | max = 0;
9 | total = 0;
10 | delay = 0;
11 | done: () => void;
12 |
13 | constructor({ max = 5, done, delay = 0 }) {
14 | this.tasks = [];
15 | this.total = 0;
16 | this.max = max;
17 | this.done = done;
18 | this.delay = delay;
19 | setTimeout(() => {
20 | /** 函数主体执行完后立即执行 */
21 | this.run();
22 | }, 0);
23 | }
24 |
25 | addTask(task) {
26 | this.tasks.push(task);
27 | this.total += 1;
28 | }
29 |
30 | run() {
31 | /** 原型任务运行方法 */
32 | if (this.tasks.length === 0) {
33 | /** 判断是否还有任务 */
34 | return Promise.resolve('');
35 | }
36 |
37 | /** 取任务个数与最大并发数最小值 */
38 | const min = Math.min(this.tasks.length, this.max);
39 |
40 | for (let i = 0; i < min; i += 1) {
41 | /** 执行最大并发递减 */
42 | this.max -= 1;
43 | /** 从数组头部取任务 */
44 | const task = this.tasks.shift();
45 | task()
46 | .then(() => {
47 | // 重:此时可理解为,当for循环执行完毕后异步请求执行回调,此时max变为0
48 | })
49 | .catch((error) => {
50 | debugLog('error', error);
51 | })
52 | .finally(() => {
53 | /** 重:当所有请求完成并返回结果后,执行finally回调,此回调将按照for循环依次执行,此时max为0. */
54 | setTimeout(() => {
55 | /** 超过最大并发10以后的任务将按照任务顺序依次执行。此处可理解为递归操作。 */
56 | this.max += 1;
57 | this.total -= 1;
58 | this.run();
59 | if (this.total === 0) {
60 | this.done?.();
61 | }
62 | }, this.delay);
63 | });
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
10 |
11 |
12 |
13 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { version as __VERSION__ } from '../package.json';
2 | import * as utils from './js/index';
3 |
4 | export * from './js/index';
5 |
6 | export const version = __VERSION__;
7 |
8 | export default utils;
9 |
--------------------------------------------------------------------------------
/src/js/array.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 数组去重,缺点不能去除{}
3 | * @param {Array} arr
4 | * @return {*} 不修改原数组,返回新数组
5 | */
6 | export const arrayUnique = (arr: Array) => {
7 | return [...new Set(arr)];
8 | };
9 |
10 | /**
11 | * @description 洗牌算法
12 | * @param {Array} arr
13 | * @return {*} 不修改原数组,返回新数组
14 | */
15 | export const arrayShuffle = (arr: Array) => {
16 | const result = [...arr];
17 | for (let i = result.length - 1; i >= 0; i -= 1) {
18 | const randomIndex = Math.floor(Math.random() * (i + 1)); // 随机下标
19 | const randomVal = result[randomIndex]; // 随机下标的值
20 | // 互换位置
21 | result[randomIndex] = result[i];
22 | result[i] = randomVal;
23 | }
24 | return result;
25 | };
26 |
27 | /**
28 | * @description 获取数组交集
29 | * @param {any} a
30 | * @param {any} b
31 | * @return {*}
32 | */
33 | export const getArrayIntersection = (a: any[], b: any[]) => {
34 | return a.filter((v) => {
35 | return b.indexOf(v) > -1;
36 | });
37 | };
38 |
39 | /**
40 | * @description 获取数组的对称差集(不修改原数组)
41 | * @example
42 | * a[1,2,3,4,5],b[3,4,5,6,7],a和b的对称差集:getArraySymmetricDifference(a,b) ===> [1,2,6,7]
43 | * @param {any} a
44 | * @param {any} b
45 | * @return {*}
46 | */
47 | export function getArraySymmetricDifference(arr1: any[], arr2: any[]) {
48 | const result: any[] = [];
49 | const diff = arr1.filter((x) => !arr2.includes(x));
50 | const diff2 = arr2.filter((x) => !arr1.includes(x));
51 | result.push(...diff);
52 | result.push(...diff2);
53 | return result;
54 | }
55 |
56 | /**
57 | * @description 获取数组差集(不修改原数组)
58 | * @example
59 | * a[1,2,3,4,5],b[2,4,6,8,10],a和b的差集:getArrayDifference(a,b) ===> [1,3,5]
60 | * a[1,2,3,4,5],b[2,4,6,8,10],b和a的差集:getArrayDifference(b,a) ===> [6,8,10]
61 | * @param {any} a
62 | * @param {any} b
63 | * @return {*}
64 | */
65 | export const getArrayDifference = (a: any[], b: any[]) => {
66 | return a.filter((v) => {
67 | return b.indexOf(v) === -1;
68 | });
69 | };
70 |
71 |
--------------------------------------------------------------------------------
/src/js/bom.ts:
--------------------------------------------------------------------------------
1 | /** 判断是否是ipad */
2 | export const isIPad = () => {
3 | const ua = navigator.userAgent.toLowerCase();
4 | // iOS13以前navigator.platform返回"iPhone"或"iPad";iOS13以后的iPad,navigator.platform返回"MacIntel"
5 | const res = ua.match(/iPad/i);
6 | if (res?.length) {
7 | return true;
8 | }
9 | if (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) {
10 | return true;
11 | }
12 | return false;
13 | };
14 |
15 | /**
16 | * @description 判断是否是移动端(判断比较粗糙)
17 | * @return {*}
18 | */
19 | export const isMobile = () => {
20 | // iOS13以前navigator.platform返回"iPhone"或"iPad";iOS13以后的iPad,navigator.platform返回"MacIntel"
21 | if (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1) {
22 | return true;
23 | }
24 | return /android|ios|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(
25 | navigator.userAgent
26 | );
27 | };
28 |
29 | /**
30 | * @description 判断是否是微信浏览器
31 | * @return {*}
32 | */
33 | export const isWechat = () => {
34 | return /MicroMessenger/i.test(window.navigator.userAgent);
35 | };
36 |
37 | /**
38 | * @description 判断设备类型
39 | * @return {*}
40 | */
41 | export const judgeDevice = () => {
42 | const ua = navigator.userAgent;
43 | const isAndroid = /(Android)/i.test(ua);
44 | const isIphone = /(iPhone|iPad|iPod|iOS)/i.test(ua);
45 | const isIPadRes = isIPad();
46 |
47 | return { isAndroid, isIphone, isIPad: isIPadRes };
48 | };
49 |
50 | /**
51 | * @description 判断是否是浏览器环境
52 | * @param {*} boolean
53 | * @return {*}
54 | */
55 | export const isBrowser = () =>
56 | typeof window !== 'undefined' &&
57 | typeof window.document !== 'undefined' &&
58 | typeof window.document.createElement !== 'undefined';
59 |
60 | /**
61 | * @description 判断是否是Safari浏览器
62 | * @return {*}
63 | */
64 | export const isSafari = () => {
65 | // mac下的Chrome浏览器的navigator.userAgent既有Safari也有Chrome,因此得排除mac下的Chrome浏览器
66 | return (
67 | /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent)
68 | );
69 | };
70 |
71 | /**
72 | * @description 判断是否是ie浏览器
73 | * @return {*}
74 | */
75 | export const isIe = () => {
76 | return (
77 | navigator.userAgent.indexOf('MSIE') !== -1 ||
78 | navigator.userAgent.indexOf('Trident') !== -1
79 | );
80 | };
81 | /**
82 | * @description 判断是否是Firefox浏览器
83 | * @return {*}
84 | */
85 | export const isFirefox = () => {
86 | return navigator.userAgent.indexOf('Firefox') !== -1;
87 | };
88 |
--------------------------------------------------------------------------------
/src/js/dom.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 计算两个dom是否有交集
3 | * @param {Element} element1
4 | * @param {Element} element2
5 | * @return {*}
6 | */
7 | export function elementsIsIntersect(element1: Element, element2: Element) {
8 | const rect1 = element1.getBoundingClientRect();
9 | const rect2 = element2.getBoundingClientRect();
10 |
11 | return (
12 | rect1.left <= rect2.right &&
13 | rect1.right >= rect2.left &&
14 | rect1.top <= rect2.bottom &&
15 | rect1.bottom >= rect2.top
16 | );
17 | }
18 |
19 | /**
20 | * @description 获取dom元素的样式值,注意:如果获取的样式值没有显示的声明,
21 | * 会获取到它的默认值,比如position没有设置值,获取它的position就会返回static
22 | * @param {Element} ele
23 | * @param {*} styleName
24 | * @return {*}
25 | */
26 | export const getStyle = (ele: Element, styleName: string) => {
27 | if (window.getComputedStyle) {
28 | return window.getComputedStyle(ele, null)[styleName];
29 | } else {
30 | // 兼容ie
31 | // @ts-ignore
32 | return ele.currentStyle[styleName];
33 | }
34 | };
35 |
36 | /**
37 | * @description 将内容复制到剪切板
38 | * @param {string} text
39 | * @return {*}
40 | */
41 | export const copyToClipBoard = (text: string): void => {
42 | const oInput = document.createElement('input');
43 | oInput.value = text;
44 | document.body.appendChild(oInput);
45 | oInput.select(); // 选择对象
46 | document.execCommand('Copy'); // 执行浏览器复制命令
47 | oInput.parentElement?.removeChild(oInput);
48 | };
49 |
50 | /**
51 | * @description 获取滚动条宽度
52 | * @copy https://github.com/iview/iview/blob/2.0/src/utils/assist.js#L19
53 | * @return {*}
54 | */
55 | export const getScrollBarSize = () => {
56 | const inner = document.createElement('div');
57 | inner.style.width = '100%';
58 | inner.style.height = '200px';
59 |
60 | const outer = document.createElement('div');
61 | const outerStyle = outer.style;
62 |
63 | outerStyle.position = 'absolute';
64 | outerStyle.top = '0px';
65 | outerStyle.left = '0px';
66 | outerStyle.pointerEvents = 'none';
67 | outerStyle.visibility = 'hidden';
68 | outerStyle.width = '200px';
69 | outerStyle.height = '150px';
70 | outerStyle.overflow = 'hidden';
71 |
72 | outer.appendChild(inner);
73 |
74 | document.body.appendChild(outer);
75 |
76 | const widthContained = inner.offsetWidth;
77 | outer.style.overflow = 'scroll';
78 | let widthScroll = inner.offsetWidth;
79 |
80 | if (widthContained === widthScroll) {
81 | widthScroll = outer.clientWidth;
82 | }
83 |
84 | document.body.removeChild(outer);
85 |
86 | return widthContained - widthScroll;
87 | };
88 |
--------------------------------------------------------------------------------
/src/js/format.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 格式化内存大小(要求传入的数字以byte为单位)
3 | * @param {number} val
4 | * @param {*} num 显示几位小数,默认2
5 | * @return {*}
6 | */
7 | export const formatMemorySize = (val: number, num = 2) => {
8 | const oneByte = 1;
9 | const oneKb = oneByte * 1024;
10 | const oneMb = oneKb * 1024;
11 | const oneGb = oneMb * 1024;
12 | const oneTb = oneGb * 1024;
13 | const format = (v: number) => v.toFixed(num);
14 |
15 | if (val < oneKb) {
16 | return `${format(val / oneByte)}byte`;
17 | }
18 | if (val < oneMb) {
19 | return `${format(val / oneKb)}kb`;
20 | }
21 | if (val < oneGb) {
22 | return `${format(val / oneMb)}mb`;
23 | }
24 | if (val < oneTb) {
25 | return `${format(val / oneGb)}gb`;
26 | }
27 | return `${format(val / oneTb)}tb`;
28 | };
29 |
30 | /**
31 | * @description 格式化时间
32 | * @param {number} timetamp
33 | */
34 | export const formatDate = (timetamp: number) => {
35 | function addDateZero(num: number) {
36 | return num < 10 ? `0${num}` : num;
37 | }
38 | const date = new Date(timetamp);
39 | return {
40 | year: date.getFullYear(),
41 | month: addDateZero(date.getMonth() + 1),
42 | day: addDateZero(date.getDate()),
43 | hour: addDateZero(date.getHours()),
44 | minutes: addDateZero(date.getMinutes()),
45 | seconds: addDateZero(date.getSeconds()),
46 | };
47 | };
48 |
--------------------------------------------------------------------------------
/src/js/index.ts:
--------------------------------------------------------------------------------
1 | export * from './array';
2 | export * from './bom';
3 | export * from './dom';
4 | export * from './format';
5 | export * from './random';
6 | export * from './reg';
7 | export * from './url';
8 | export * from './utils';
9 | export * from '../class/cache';
10 | export * from '../class/lru-cache';
11 | export * from '../class/queue';
12 |
--------------------------------------------------------------------------------
/src/js/random.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 获取[min,max]之间的随机整数。
3 | * @example: getRangeRandom(-10,100) ===> -8
4 | * @param {number} min
5 | * @param {number} max
6 | * @return {*}
7 | */
8 | export const getRangeRandom = (min: number, max: number) =>
9 | Math.floor(Math.random() * (max - min + 1)) + min;
10 |
11 | /**
12 | * @description: 随机数组的一个元素
13 | * @example: getRandomOne([10,2,4,6]) ===> 6
14 | * @param {any} arr
15 | * @return {*}
16 | */
17 | export const getRandomOne = (arr: any[]) =>
18 | arr[Math.floor(Math.random() * arr.length)];
19 |
20 | /**
21 | * @description 获取随机字符串(ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789)
22 | * @example: getRandomString(4) ===> abd3
23 | * @param {number} length
24 | * @return {*}
25 | */
26 | export const getRandomString = (length: number): string => {
27 | const str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
28 | let res = '';
29 | for (let i = 0; i < length; i += 1) {
30 | res += str.charAt(getRangeRandom(0, str.length - 1));
31 | }
32 | return res;
33 | };
34 |
35 | /**
36 | * @description: 获取随机整数
37 | * @example: getRandomInt(4) ===> 3251
38 | * @param {*} length
39 | * @return {*}
40 | */
41 | export const getRandomInt = (length) => {
42 | if (length > 16 || length < 1) throw new Error('length的范围:[1,16]');
43 | let num = +`${Math.random()}`.slice(2, 2 + length);
44 | if (String(num).length !== length) {
45 | num = getRandomInt(length);
46 | }
47 | return num;
48 | };
49 |
--------------------------------------------------------------------------------
/src/js/reg.ts:
--------------------------------------------------------------------------------
1 | import { debugLog } from '../utils/index';
2 |
3 | /**
4 | * @description 正则是否是纯数字(纯整数)
5 | * @example: isPureNumber(abc) ===> false;isPureNumber(1.23) ===> false;isPureNumber(123) ===> true;
6 | * @param {string} str
7 | * @return {*}
8 | */
9 | export function isPureNumber(str: string) {
10 | const regex = /^\d+$/;
11 | return regex.test(str);
12 | }
13 |
14 | /**
15 | * @description 正则验证手机号、邮箱是否合法
16 | * @param {string} str
17 | * @param {*} type
18 | * @return {*}
19 | */
20 | export const regVerify = (str: string, type: 'phone' | 'email') => {
21 | try {
22 | switch (type) {
23 | case 'email':
24 | // https://ihateregex.io/expr/email
25 | return /[^@ \t\r\n]+@[^@ \t\r\n]+\.[^@ \t\r\n]+/.test(str);
26 | case 'phone':
27 | return /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/.test(
28 | str
29 | );
30 | }
31 | } catch (error: any) {
32 | debugLog('error', error);
33 | return false;
34 | }
35 | };
36 |
37 | /**
38 | * @description 判断字符串的开头和结尾是否有空格,有空格就返回true,否则返回false
39 | * @param {string} value
40 | * @return {*}
41 | */
42 | export const judgeStringSpace = (value: string) => {
43 | const reg1 = /^\s+/g; // 匹配开头空格
44 | const reg2 = /\s+$/g; // 匹配结尾空格
45 | if (reg1.test(value) || reg2.test(value)) {
46 | return true;
47 | }
48 | return false;
49 | };
50 |
--------------------------------------------------------------------------------
/src/js/url.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 获取地址栏参数(注意:请确保url是http://aaa.com/ds/?aa=1&bb=323这样子的)
3 | * @return {*}
4 | */
5 | export const getUrlParams = (key?: string) => {
6 | const url = decodeURIComponent(window.location.href);
7 | const str = url.split('?')[1];
8 | const obj = {};
9 | if (str) {
10 | const keys = str.split('&');
11 | keys.forEach((item) => {
12 | const arr = item.split('=');
13 | obj[arr[0]] = arr[1];
14 | });
15 | }
16 | return key ? obj[key] : obj;
17 | };
18 |
--------------------------------------------------------------------------------
/src/js/utils.ts:
--------------------------------------------------------------------------------
1 | import { debugLog } from '../utils/index';
2 |
3 | /**
4 | * @description: 字符串编码
5 | * @param {string} str
6 | * @return {*}
7 | */
8 | export function strBtoa(str: string) {
9 | return window.btoa(window.encodeURIComponent(str));
10 | }
11 |
12 | /**
13 | * @description: 字符串解码
14 | * @param {string} str
15 | * @return {*}
16 | */
17 | export function strAtob(str: string) {
18 | return window.decodeURIComponent(window.atob(str));
19 | }
20 |
21 | /**
22 | * @description: 异步更新
23 | * @param {*} fn
24 | * @return {*}
25 | */
26 | export const asyncUpdate = (fn, delay?) => {
27 | return new Promise((resolve) => {
28 | setTimeout(() => {
29 | const res = fn();
30 | resolve(res);
31 | }, delay || 0);
32 | });
33 | };
34 |
35 | /**
36 | * @description: 模拟ajax请求
37 | * @param {*} param1
38 | * @return {*}
39 | */
40 | export const mockAjax = ({ flag = true, delay = 500 }) => {
41 | return new Promise<{ code: number; data: { id: number }; msg: string }>(
42 | (resolve, reject) => {
43 | setTimeout(() => {
44 | if (flag) {
45 | resolve({
46 | code: 200,
47 | data: {
48 | id: 1,
49 | },
50 | msg: '请求成功',
51 | });
52 | } else {
53 | reject({
54 | code: 400,
55 | msg: '请求失败',
56 | });
57 | }
58 | }, delay);
59 | }
60 | );
61 | };
62 |
63 | /**
64 | * @description: 将里面盒子等比例适配外层盒子
65 | * 如果里层盒子的宽或高有一边大于外层盒子的宽或高,可不设置minWidth和minHeight,
66 | * 如果里层盒子的宽和高都小于外层盒子的宽和高需要设置maxWidth和minWidth一致,maxHeight和minHeight一致
67 | * @param width 里面的盒子宽度
68 | * @param height 里面的盒子高度
69 | * @param maxWidth 外面的盒子最大宽度
70 | * @param maxHeight 外面的盒子最大高度
71 | * @param minWidth 外面的盒子最小宽度
72 | * @param minHeight 外面的盒子最小高度
73 | * @returns {{width: number, height: number}} 返回适配好的盒子宽高
74 | */
75 | export function computeBox({
76 | width,
77 | height,
78 | maxWidth,
79 | maxHeight,
80 | minWidth,
81 | minHeight,
82 | }) {
83 | // w = h / ratio, h = w * ratio
84 | const ratio = height / width;
85 | // eslint-disable-next-line
86 | const _minWidth = minWidth ? minWidth : 0;
87 | // eslint-disable-next-line
88 | const _minHeight = minHeight ? minHeight : 0;
89 | // eslint-disable-next-line
90 | let _width = width;
91 | // eslint-disable-next-line
92 | let _height = height;
93 |
94 | if (_width < _minWidth) {
95 | _width = _minWidth;
96 | _height = _minWidth * ratio;
97 | }
98 | if (_height < _minHeight) {
99 | _width = _minHeight / ratio;
100 | _height = _minHeight;
101 | }
102 |
103 | if (_width > maxWidth) {
104 | _width = maxWidth;
105 | _height = maxWidth * ratio;
106 | }
107 | if (_height > maxHeight) {
108 | _width = maxHeight / ratio;
109 | _height = maxHeight;
110 | }
111 |
112 | return {
113 | width: _width,
114 | height: _height,
115 | };
116 | }
117 |
118 | /**
119 | * @description: 下载图片
120 | * @param {string} src
121 | * @param {string} name
122 | * @return {*}
123 | */
124 | export const downloadImg = (src: string, name: string) => {
125 | const imgEl = new Image();
126 | imgEl.src = src;
127 | imgEl.setAttribute('crossOrigin', 'anonymous'); // 跨域
128 | imgEl.onload = function () {
129 | const canvas = document.createElement('canvas');
130 | canvas.width = imgEl.width;
131 | canvas.height = imgEl.height;
132 |
133 | const context = canvas.getContext('2d');
134 | context!.drawImage(imgEl, 0, 0, imgEl.width, imgEl.height);
135 | const url = canvas.toDataURL('image/png');
136 |
137 | // 生成一个a元素
138 | const a = document.createElement('a');
139 | // 创建一个单击事件
140 | const event = new MouseEvent('click');
141 |
142 | a.download = name || '下载图片名称';
143 | // 将生成的URL设置为a.href属性
144 | a.href = url;
145 | // 触发a的单击事件
146 | a.dispatchEvent(event);
147 | };
148 | imgEl.onerror = function (e) {
149 | debugLog('error', '下载图片出错', e);
150 | };
151 | };
152 |
153 | /**
154 | * @description: 跳转(window.location.href)
155 | * @param {array} arg
156 | * @return {*}
157 | */
158 | export const hrefToTarget = (url: string) => {
159 | window.location.href = url;
160 | };
161 |
162 | /**
163 | * @description: 跳转(window.open)
164 | * @param {array} arg
165 | * @return {*}
166 | */
167 | export const openToTarget = (...arg) => {
168 | window.open(...arg);
169 | };
170 |
171 | /**
172 | * @description: 刷新页面(window.location.reload)
173 | */
174 | export const windowReload = () => {
175 | window.location.reload();
176 | };
177 |
178 | /**
179 | * @description: 获取文件后缀
180 | * @param {string} filename
181 | * @return {*}
182 | */
183 | export const getFileExt = (filename: string) => {
184 | const arr = filename.split('.');
185 | const ext = arr[arr.length - 1];
186 | return ext;
187 | };
188 |
189 | /**
190 | * @description: 生成style标签,并挂载到head
191 | * @example: generateStyle({ '.a': { color: 'red' }, '#b': { color: 'blue' } });
192 | * 最终会将挂载到head里
193 | * @param {number} styleObj
194 | * @return {*}
195 | */
196 | export const generateStyle = (styleObj: Record) => {
197 | const styleEle = document.createElement('style');
198 | styleEle.type = 'text/css';
199 | let textContent = '';
200 | function getStyleVal(obj: string) {
201 | let str = '';
202 | Object.keys(obj).forEach((key: string) => {
203 | // eslint-disable-next-line
204 | str += `${key}:${obj[key]};`;
205 | });
206 | return str;
207 | }
208 | Object.keys(styleObj).forEach((key) => {
209 | textContent += `${key}{`;
210 | textContent += getStyleVal(styleObj[key]);
211 | textContent += '}';
212 | });
213 | styleEle.textContent = textContent;
214 | document.head.appendChild(styleEle);
215 | };
216 |
217 | /**
218 | * @description: 图片预加载
219 | * @example: imgPrereload(['aaa.com/a.webp', 'aaa.com/b.webp']);
220 | * @param {string} imgList
221 | * @return {*}
222 | */
223 | export const imgPrereload = (imgList: string[]) => {
224 | return imgList.map((url) => {
225 | return new Promise((resolve, reject) => {
226 | const img = new Image();
227 | img.src = url;
228 | img.onload = () => resolve({ url });
229 | img.onerror = (error) => reject({ url, error });
230 | });
231 | });
232 | };
233 |
234 | /**
235 | * @description: 是否支持0.5px
236 | * @return {*}
237 | */
238 | export const supportHairlines = () => {
239 | const fakeBody = document.createElement('body');
240 | const testElement = document.createElement('div');
241 | testElement.style.border = '.5px solid transparent';
242 | fakeBody.appendChild(testElement);
243 | document.documentElement.appendChild(fakeBody);
244 | if (testElement.offsetHeight === 1) {
245 | return true;
246 | } else {
247 | return false;
248 | }
249 | };
250 |
251 | /**
252 | * @description: 让系统卡死一段时间
253 | * @param {*} duration
254 | * @return {*}
255 | */
256 | export const sleep = (duration = 1000) => {
257 | const oldTime = +new Date();
258 | // eslint-disable-next-line
259 | for (; +new Date() - oldTime < duration;) { }
260 | };
261 |
262 | /**
263 | * @description: 按屏幕375为基准,生成对应的px值,默认返回单位(px)
264 | * @param {number} val
265 | * @param {*} flag
266 | * @return {*}
267 | */
268 | export const pxToDesignPx = (val: number, flag = true) => {
269 | // window.screen.availWidth,值是固定的,怎么跳转浏览器大小,值都是屏幕的大小
270 | // window.document.documentElement.clientWidth,值是不定的,根据文档宽度决定
271 | // window.screen和window.document兼容性一致,兼容ie6及以上,不兼容安卓4.3及以下,其余基本没有兼容性问题。
272 | const px = window.document.documentElement.clientWidth * (val / 375);
273 | return flag ? `${px}px` : px;
274 | };
275 |
276 | /**
277 | * @description: 按屏幕375为基准,生成对应的vw值,默认返回单位(vw)
278 | * @param {number} val
279 | * @param {*} flag
280 | * @return {*}
281 | */
282 | export const pxToDesignVw = (val: number, flag = true) => {
283 | const vw = ((val / 375) * 100).toFixed(5);
284 | return flag ? `${vw}vw` : vw;
285 | };
286 |
287 | /**
288 | * @description: 删除对象中值为: null, undefined, NaN, ''的属性
289 | * @param {any} obj
290 | * @return {*}
291 | */
292 | export const deleteUseLessObjectKey = (obj: any) => {
293 | Object.keys(obj).forEach((key) => {
294 | if ([null, undefined, NaN, ''].includes(obj[key])) {
295 | delete obj[key];
296 | }
297 | });
298 | return obj;
299 | };
300 |
301 | /**
302 | * @description: 替换占位符
303 | * @example: replaceKeyFromValue('Hello {name}',{name:'Word'}) => Hello Word
304 | * @param {string} str
305 | * @param {object} obj
306 | * @return {*}
307 | */
308 | export const replaceKeyFromValue = (str: string, obj: Record) => {
309 | let res = str;
310 | Object.keys(obj).forEach((v) => {
311 | res = res.replace(new RegExp(`{${v}}`, 'ig'), obj[v]);
312 | });
313 | return res;
314 | };
315 |
316 | /**
317 | * @description: 判断数据类型
318 | * @return {*}
319 | */
320 | export const judgeType = (
321 | obj: any
322 | ):
323 | | 'boolean'
324 | | 'number'
325 | | 'string'
326 | | 'function'
327 | | 'array'
328 | | 'date'
329 | | 'regExp'
330 | | 'undefined'
331 | | 'null'
332 | | 'object' => {
333 | const map = {
334 | '[object Boolean]': 'boolean',
335 | '[object Number]': 'number',
336 | '[object String]': 'string',
337 | '[object Function]': 'function',
338 | '[object Array]': 'array',
339 | '[object Date]': 'date',
340 | '[object RegExp]': 'regExp',
341 | '[object Undefined]': 'undefined',
342 | '[object Null]': 'null',
343 | '[object Object]': 'object',
344 | };
345 | return map[Object.prototype.toString.call(obj)];
346 | };
347 |
348 | /**
349 | * @description: myName或者MyName转化为my-name
350 | * @copy https://github.com/vueComponent/ant-design-vue/blob/HEAD/antd-tools/generator-types/src/utils.ts
351 | * @param {string} input
352 | * @return {*}
353 | */
354 | export const toKebabCase = (input: string): string =>
355 | input.replace(
356 | /[A-Z]/g,
357 | (val, index) => (index === 0 ? '' : '-') + val.toLowerCase()
358 | );
359 |
360 | /**
361 | * @description: myName或者MyName转化为my_name
362 | * @param {string} input
363 | * @return {*}
364 | */
365 | export const toKebabCase2 = (input: string) =>
366 | input.replace(
367 | /[A-Z]/g,
368 | (val, index) => (index === 0 ? '' : '_') + val.toLowerCase()
369 | );
370 |
371 | /**
372 | * @description: my-name转化为myName
373 | * @param {string} input
374 | * @return {*}
375 | */
376 | export const toCamelCased = (input: string) =>
377 | input.replace(/-(\w)/g, function (all, letter) {
378 | return letter.toUpperCase();
379 | });
380 |
381 | /**
382 | * @description: my_name转化为myName
383 | * @param {string} input
384 | * @return {*}
385 | */
386 | export const toCamelCased2 = (input: string) =>
387 | input.replace(/_(\w)/g, function (all, letter) {
388 | return letter.toUpperCase();
389 | });
390 |
391 | /**
392 | * @description: my-name转化为MyName
393 | * @param {string} input
394 | * @return {*}
395 | */
396 | export const toPascalCase = (input: string) => {
397 | const res = input.replace(input[0], input[0].toUpperCase());
398 | return res.replace(/-(\w)/g, function (all, letter) {
399 | return letter.toUpperCase();
400 | });
401 | };
402 |
403 | /**
404 | * @description: my_name转化为MyName
405 | * @param {string} input
406 | * @return {*}
407 | */
408 | export const toPascalCase2 = (input: string) => {
409 | const res = input.replace(input[0], input[0].toUpperCase());
410 | return res.replace(/_(\w)/g, function (all, letter) {
411 | return letter.toUpperCase();
412 | });
413 | };
414 |
415 | /**
416 | * @description: 使用json进行深克隆
417 | * @param {*} obj
418 | * @return {*}
419 | */
420 | export const deepCloneByJson = (obj: T): T =>
421 | JSON.parse(JSON.stringify(obj));
422 |
423 | /**
424 | * @description: 手写深拷贝,解决循环引用
425 | * @param {*} object
426 | * @return {*}
427 | */
428 | export const deepClone = (object: T): T => {
429 | function clone(obj: any, hash: any) {
430 | const newobj: any = Array.isArray(obj) ? [] : {};
431 | // eslint-disable-next-line
432 | hash = hash || new WeakMap();
433 | if (hash.has(obj)) {
434 | return hash.get(obj);
435 | }
436 | hash.set(obj, newobj);
437 |
438 | Object.keys(obj).forEach((i) => {
439 | if (obj[i] instanceof Object) {
440 | newobj[i] = clone(obj[i], hash);
441 | } else {
442 | newobj[i] = obj[i];
443 | }
444 | });
445 | return newobj;
446 | }
447 | return clone(object, undefined);
448 | };
449 |
450 | /**
451 | * @description: 防抖函数(Promise)
452 | * @param {Function} fn 函数
453 | * @param {number} delay 延迟时间
454 | * @param {boolean} leading 首次立即执行
455 | * @return {Promise}
456 | */
457 | export const debounce = (fn: any, delay: number, leading = false) => {
458 | let timer;
459 | const debounceFn = function (...args) {
460 | if (timer) {
461 | clearTimeout(timer);
462 | }
463 | return new Promise((resolve) => {
464 | if (leading) {
465 | let isFirst = false;
466 | if (!timer) {
467 | // @ts-ignore
468 | resolve(fn.apply(this, args));
469 | isFirst = true;
470 | }
471 | timer = setTimeout(() => {
472 | timer = null;
473 | if (!isFirst) {
474 | // @ts-ignore
475 | resolve(fn.apply(this, args));
476 | }
477 | }, delay);
478 | } else {
479 | timer = setTimeout(() => {
480 | // @ts-ignore
481 | resolve(fn.apply(this, args));
482 | }, delay);
483 | }
484 | });
485 | };
486 |
487 | debounceFn.cancel = function () {
488 | clearTimeout(timer);
489 | timer = null;
490 | };
491 | return debounceFn;
492 | };
493 |
494 | /**
495 | * @description: 节流函数(Promise)
496 | * @param {Function} fn 函数
497 | * @param {number} interval 间隔
498 | * @param {boolean} trailing 最后一次执行
499 | * @return {Promise}
500 | */
501 | export const throttle = (fn: any, interval: number, trailing = false) => {
502 | let lastTime = 0;
503 | let timer;
504 | return function (...args) {
505 | const newTime = new Date().getTime();
506 | if (timer) {
507 | clearTimeout(timer);
508 | }
509 |
510 | let result;
511 | return new Promise((resolve) => {
512 | if (newTime - lastTime > interval) {
513 | // @ts-ignore
514 | result = fn.apply(this, args);
515 | resolve(result);
516 | lastTime = newTime;
517 | } else if (trailing) {
518 | timer = setTimeout(() => {
519 | // @ts-ignore
520 | result = fn.apply(this, args);
521 | resolve(result);
522 | }, interval);
523 | }
524 | });
525 | };
526 | };
527 |
528 | /**
529 | * @description: 生成uuid(16位)
530 | * @example: generate() ===> 9d24f135-3e33-46b7-b51f-dc5b8121d60a
531 | * @return {*}
532 | */
533 | export const generateUuid = () => {
534 | const uuid = URL.createObjectURL(new Blob()); // blob:null/9d24f135-3e33-46b7-b51f-dc5b8121d60a
535 | URL.revokeObjectURL(uuid); // 在使用完对象 URL 后调用此方法,让浏览器知道不再保留对该文件的引用。
536 | return uuid.split('/')[1].length;
537 | };
538 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export const debugLog = (type: 'log' | 'warn' | 'error', ...data) => {
2 | console[type]('bi-utils', ...data);
3 | };
4 |
--------------------------------------------------------------------------------
/test/computeBox.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Document
9 |
21 |
22 |
23 |
24 |
25 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/test/test.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @description 获取随机整数
3 | * @example: getRandomInt(4) ===> 3251
4 | */
5 | const getRandomInt = (length) => {
6 | if (length > 16 || length < 1) throw new Error('length的范围:[1,16]');
7 | let num = +`${Math.random()}`.slice(2, 2 + length);
8 | if (String(num).length !== length) {
9 | num = getRandomInt(length);
10 | }
11 | console.log(num);
12 | return num;
13 | };
14 |
15 | const getRandomOne = (arr) => arr[Math.floor(Math.random() * arr.length)];
16 |
17 | const getRangeRandom = (min, max) =>
18 | Math.floor(Math.random() * (max - min + 1)) + min;
19 |
20 | const getFileExt = (filename) => {
21 | const arr = filename.split('.');
22 | const ext = arr[arr.length - 1];
23 | return ext;
24 | };
25 |
26 | const generateStyle = (styleObj) => {
27 | // const styleEle = document.createElement('style');
28 | // styleEle.type = 'text/css';
29 | let textContent = '';
30 | function getStyleVal(obj) {
31 | let str = '';
32 | Object.keys(obj).forEach((key) => {
33 | // eslint-disable-next-line
34 | str += `${key}:${obj[key]};`;
35 | });
36 | return str;
37 | }
38 | Object.keys(styleObj).forEach((key) => {
39 | console.log(key, 333, getStyleVal(styleObj[key]));
40 | textContent += `${key}{`;
41 | textContent += getStyleVal(styleObj[key]);
42 | textContent += '}';
43 | });
44 | console.log(textContent, 222);
45 | // styleEle.textContent = textContent;
46 | // document.head.appendChild(styleEle);
47 | };
48 |
49 | const generateUuid = () => {
50 | const tempUrl = URL.createObjectURL(new Blob());
51 | const uuid = tempUrl.toString(); // blob:null/9d24f135-3e33-46b7-b51f-dc5b8121d60a
52 | URL.revokeObjectURL(tempUrl);
53 | console.log(uuid.split('/')[1]);
54 | return uuid.split('/')[1];
55 | };
56 |
57 | // my-name转化为MyName
58 | const toPascalCase = (input) => {
59 | const res = input.replace(input[0], input[0].toUpperCase());
60 | return res.replace(/-(\w)/g, function (all, letter) {
61 | return letter.toUpperCase();
62 | });
63 | };
64 |
65 | console.log(toPascalCase('bi-utils'));
66 | console.log(generateUuid());
67 |
68 | // generateStyle({ backgroundImage: `url('sss')` });
69 | generateStyle({ '.a': { color: 'red' }, '#b': { color: 'blue' } });
70 | // console.log(getFileExt('aaa/dsaads.as.jpg'));
71 |
72 | // for (let i = 0; i < 100; i++) {
73 | // console.log(getRangeRandom(1, 100));
74 | // }
75 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "module": "ESNext", // ESNext,CommonJS
5 | "strict": true,
6 | "noImplicitAny": false,
7 | "lib": ["DOM", "ESNext"],
8 | "jsx": "preserve",
9 | "moduleResolution": "Node",
10 | "esModuleInterop": true, // ES 模块互操作,import React from 'react';react是module.exports导出的,因此需要设置该属性
11 | "forceConsistentCasingInFileNames": true, //在文件名中强制使用一致的大小写
12 | "skipLibCheck": true, // 跳过d.ts声明文件的类型检查。
13 | "baseUrl": "./",
14 | // "sourceMap": true, // 是否生成sourceMap
15 | // "noEmitOnError": false,
16 | // "declaration": true, // 生成.d.ts文件
17 | // "declarationDir": "./types", //生成.d.ts文件的目录
18 | "resolveJsonModule": true, // 解析json模块
19 | "paths": {
20 | "@/*": ["src/*"]
21 | }
22 | // "paths": {
23 | // "@/*": ["./src/*"] // 这样写的话,@/不会提示路径,得使用baseUrl:'./'+paths:{"@/*": ["src/*"]}这样才的话@/才会提示路径
24 | // }
25 | },
26 | // 命令行执行ts-node的时候的配置
27 | "ts-node": {
28 | "compilerOptions": {
29 | "module": "CommonJS"
30 | }
31 | },
32 | "exclude": ["doc/**/*", "node_modules/**/*"], // 排除include里的文件
33 | "include": [
34 | "./**/*.ts",
35 | "./**/*.js",
36 | ".eslintrc.js",
37 | "./rollup.config.ts" //https://rollupjs.org/guide/en/#--configplugin-plugin
38 | ] // 仅仅匹配这些文件
39 | }
40 |
--------------------------------------------------------------------------------
/typedoc.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "entryPoints": ["./src/index.ts"],
3 | "out": "./doc"
4 | }
5 |
--------------------------------------------------------------------------------