0%

实用小技巧

记录一些很实用的小方法,方便在以后的查询,持续更新中。

js

使用JSON格式化输出

1
2
3
4
5
6
7
8
9
/*
JSON.stringify(obj, replacer, space)
参数:
obj:需要格式化的对象
replacer:筛选最终要转化的字符串,参数为数组,通过 key来筛选,参数为函数,取决于返回值
space:文本添加缩进、空格和换行符,最大值为10

*/
JSON.stringify(obj, null, 4);

一个数的奇偶性判断

1
2
3
4
5
6
7
8
9
10
/*
使用位运算符 效率更高
& 只有对应的数为1时,结果才为1,其他都为0
0001
& 0011
--------
0001
*/
if (num & 1) console.log('奇数');
else console.log('偶数');

求 2 的 n 次方

1
1 << n;

两个变量互换

1
[var1, var2] = [var2, var1];

浅拷贝

1
Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));

深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
function deepCopy(obj) {
if (typeof obj !== 'object') {
return;
}
const newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}

计算滚动条的宽度

1
content.offsetHeight - content.scrollHeight;

监听 App 自带返回键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const hiddenProperty =
'hidden' in document
? 'hidden'
: 'webkitHidden' in document
? 'webkitHidden'
: 'mozHidden' in document
? 'mozHidden'
: null;

const visibilityChangeEvent = hiddenProperty.replace(/hidden/i, 'visibilitychange');

const onVisibilityChange = function () {
if (!document[hiddenProperty]) {
console.log('页面非激活');
} else {
console.log('页面激活');
}
};

document.addEventListener(visibilityChangeEvent, onVisibilityChange);

帧率计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
let lastTime = performance.now();
let frame = 0;
let lastFameTime = performance.now();
const loop = () => {
const now = performance.now();
const fs = now - lastFameTime;
lastFameTime = now;
let fps = Math.round(1000 / fs);
frame += 1;
if (now > 1000 + lastTime) {
fps = Math.round((frame * 1000) / (now - lastTime));

frame = 0;
lastTime = now;
}
console.log(fps);
window.requestAnimationFrame(loop);
};
window.requestAnimationFrame(loop);

函数扁平化 compose

1
2
3
4
5
6
7
8
9
10
11
function compose(...funcs) {
return function anonymous(...args) {
if (funcs.length === 0) return args;
if (funcs.length === 1) return funcs[0](...args);
return funcs.reduce((n, func) => {
return Array.isArray(n) ? func(...n) : func(n);
}, args);
};
}

// compose(fn1, fn2, fn3, fn4)(5)

函数柯理化 currying

1
2
3
4
5
6
7
8
9
10
function currying(fn, ...args) {
if (fn.length === args.length) {
return fn(...args);
} else {
return function anonymous(...newArgs) {
let allArgs = [...args, ...newArgs];
return currying(fn, ...allArgs);
};
}
}

将字符串渲染为 DOM 节点

主要是用 innerHTML 来实现,让浏览器帮我们进行转换,返回的是一个节点数组:

1
2
3
4
5
function parseDom(arg) {
const objE = document.createElement('div');
objE.innerHTML = arg;
return objE.childNodes; // []
}

缓动小算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Math.easeout = function (A, B, rate, callback) {
if (A == B || typeof A !== 'number') {
return;
}

B = B || 0;
rate = rate || 2;

const step = function () {
A = A + (B - A) / rate;

if (Math.abs(A - B) < 1) {
callback(B, true);
return;
}
callback(A, false);
requestAnimationFrame(step);
};
step();
};

快速实现折线图

在图标上定位好每个点的位置后,获取这些点,然后调用下列函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const fnLineChart = function (eleDots) {
[].slice.call(eleDots).forEach(function (ele, index) {
const eleNext = eleDots[index + 1];
if (!eleNext) return;
const eleLine = ele.querySelector('i');
if (!eleLine) {
eleLine = document.createElement('i');
eleLine.setAttribute('line', '');
ele.appendChild(eleLine);
}
// 记录坐标
const boundThis = ele.getBoundingClientRect();
// 下一个点的坐标
const boundNext = eleNext.getBoundingClientRect();
// 计算长度和旋转角度
const x1 = boundThis.left;
const y1 = boundThis.top;
const x2 = boundNext.left;
const y2 = boundNext.top;
// 长度
const distance = Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
// 角度
const radius = Math.atan((y2 - y1) / (x2 - x1));
// 设置线条样式
eleLine.style.width = distance + 'px';
eleLine.style.msTransform = 'rotate(' + radius + 'rad)';
eleLine.style.transform = 'rotate(' + radius + 'rad)';
});
};

折线的样式需要自定义:

1
2
3
4
5
i[line] {
position: absolute;
transform-origin: left center;
...;
}

转载自 不借助 Echarts 等图形框架原生 JS 快速实现折线图效果

JSON 树查找子节点的路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 查询节点深度
public findNodePath = (id: number, nodes: any[], path?: any[]): any => {
let item: any;
path = path || [];
for (item of nodes) {
const tmpPath: any[] = path.slice();
tmpPath.push(item.id);
if (id === item.id) {
return tmpPath;
}
if (item.children) {
const findResult = this.findNodePath(id, item.children, tmpPath);
if (findResult) {
return findResult;
}
}
}
};

或者

1
2
3
4
5
6
7
8
9
10
11
12
function findTopParents(menuJson, childId, result) {
result = result || [];
let menuStr = typeof menuJson === 'string' ? menuJson : JSON.stringify(menuJson);
let reg = new RegExp('id":"([^"]+)"[^\\}\\]\\[\\{]+\\[\\{[^\\}\\]\\[\\{]+id":"' + childId);

if (reg.test(menuStr)) {
result.push(menuStr.match(reg)[1]);
return findTopParents(menuStr, menuStr.match(reg)[1], result);
} else {
return result;
}
}

正则

对数字添加千分位符号

1
2
3
4
5
6
7
String(Number).replace(/(\d)(?=(\d{3})+$)/g, '$1,');
// 例如:
String(123456789).replace(/(\d)(?=(\d{3})+$)/g, '$1,'); // "123,456,789"

Number.toLocaleString('en-US');
// 例如:
(123456789).toLocaleString('en-US'); // "123,456,789"

手机号替换

1
2
3
const phone = '15112345678';
phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
// 151****5678

常用的一些匹配

1
2
匹配中文字符
/[\u4e00-\u9fa5]/
1
2
3
匹配双字节字符(包括汉字在内)
/[^\x00-\xff]/
/[^-~]/g
1
2
匹配首尾空格
/(^\s*)|(\s*$)/
1
2
匹配全空格
/^(?!(\s+$))/
1
2
匹配空行
/\n[\s| ]*\r/
1
2
匹配邮箱
/\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*/

css

滚动嵌套时让父滚动不触发

1
overscroll-behavior: contain;

IOS 滚动不平滑

1
-webkit-overflow-scrolling: touch;

镂空字体(让文字显示背景色或图片)

1
2
webkit-background-clip: text;
-webkit-text-fill-color: transparent;

配合文本描边 -webkit-text-stroke 会有更不错的表现

清除 Chrome 浏览器自动填充输入框的背景色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
-webkit-animation: autofill 0s forwards;
animation: autofill 0s forwards;
}

@keyframes autofill {
100% {
background: transparent;
color: inherit;
}
}

@-webkit-keyframes autofill {
100% {
background: transparent;
color: inherit;
}
}

深色模式匹配

css 使用媒体查询 @media(prefers-color-scheme: dark) 适配:

1
2
3
4
5
6
7
8
9
10
body {
background: #f2f2f2;
color: #333;
}
@media (prefers-color-scheme: dark) {
body {
background: #222;
color: #eee;
}
}

js 使用 matchMedia 适配:

1
2
3
4
5
6
7
8
9
10
11
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
// dark mode
}

window.matchMedia('(prefers-color-scheme: dark)').addListener(e => {
if (e.matches) {
console.log('dark mode is enabled');
} else {
console.log('dark mode is disabled');
}
});

一行 css 实现简单的深色模式:

1
filter: invert(1) hue-rotate(180deg);

清除 antd button 点击时的 css 动画

1
2
3
4
5
button[ant-click-animating-without-extra-node]:after {
border: 0 none;
opacity: 0;
animation: none 0 ease 0 1 normal;
}

图片加载失败兜底处理

在图片加载失败的时候,改变默认的展示。在img标签上添加加载失败处理:

1
<img src="xxx.png" alt="xxx" onerror="this.classList.add('error');">

添加全局css样式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
img.error {
display: inline-block;
transform: scale(1);
content: '';
color: transparent;
}
img.error::before {
content: '';
position: absolute;
left: 0; top: 0;
width: 100%; height: 100%;
background: #f5f5f5 url(break.svg) no-repeat center / 50% 50%;
}
img.error::after {
content: attr(alt);
position: absolute;
left: 0; bottom: 0;
width: 100%;
padding: 0 8px 0;
box-sizing: border-box;
line-height: 2;
background-color: rgba(0,0,0,.5);
color: white;
font-size: 12px;
text-align: center;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

node

判断模块的运行方式

检测一个node模块(一个文件),是直接运行,还是被引入(require):

1
2
3
if (module === require.main) {
console.log('直接运行');
}

node 中快速加载  当前文件夹下所有文件

node 中经常会实现按类型对文件进行分类,再通过 index.js 来引入统一导出。可以通过以下方法来实现:

1
2
3
4
5
6
7
8
// index.js
const fs = require('fs');

const handler = fs
.readdirSync(__dirname)
.filter(value => value !== 'index.js')
.map(value => require('./' + value));
// [{...},{...}]

会生成一个数组,每一项是一个文件是导出的内容,如果我们想合并为一个对象:

1
2
3
module.exports = pageHandles.reduce((a, b) => {
return { ...a, ...b };
}, {});

设置 iOS 中 H5 字体跟随系统改变

ios 系统

  • 默认中文字体是 Heiti SC
  • 默认英文字体是 Helvetica
  • 默认数字字体是 HelveticaNeue
  • 无微软雅黑字体

android 系统

  • 默认中文字体是 Droidsansfallback
  • 默认英文和数字字体是 Droid Sans
  • 无微软雅黑字体
1
font-family: -apple-system, Helvetica, 'Heiti SC';

webpack

在 webpack 中使用 dart-sass 替换 node-sass

1
2
3
4
5
# 卸载node-sass
$ yarn remove node-sass

# 安装dart-sass
$ yarn add node-sass@npm:sass

package.json 文件中增加:

1
2
3
"resolution": {
"node-sass": "npm:sass"
},

其他

禁止 Chrome 自动翻译网页

head 里加上

1
<meta name="google" content="notranslate" />