整理一些js写法
# 整理一些js写法
在工作中遇到了许多重复需要的js方法,每次都要查阅之前写的。 为了方便特意写一篇记录这些方法。 并且这也是自己学习js一点点进步的方法。
# 从元素为对象的数组中提取出对象的某属性为x的快速写法,摘自vuex教程
说到底是Array的find()、filter()方法
find返回的是数组里第一个符合条件的选项,
filter返回的是数组里符合条件的选项集合,是一个数组
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# filter 其他用法
# 去掉数组中值为index的项目
this.arr = this.arr.filter((item) => item !== index);
1
# 判断数组中有没有值为index的项目
this.arr.filter((item) => item === index).length !== 0
1
# 获取数组中某一属性的值组成数组
const res = this.list.map(item => item.id);
1
# 数组中是否包含某一值
Array.prototype.includes()方法用来判断一个数组是否包含一个指定的值,根据情况,如果包含则返回 true,否则返回false。
const array = [1, 2, 3];
const isinclude = array1.includes(2)
1
2
2
# 反转对象的key和value的值
如果直接将对象传进去,将会改变原有对象,建议传{...obj}进去
const reverseKV = (obj) => {
Object.keys(obj).forEach(k => {
let value = obj[k];
obj[value] = k;
delete obj[k];
});
return obj;
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 查找数组中,对象某个属性值为val的索引(index)
优雅一点,可以用lodash的findIndex()
const findElem = (arrayToSearch, attr, val) => {
for (let i = 0; i < arrayToSearch.length; i++) {
if (arrayToSearch[i][attr] === val) {
return i;
}
}
return -1;
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 判断页面上的字符是否溢出
const isOverflow = (element) => {
return element ? element[0].offsetWidth >= element[0].scrollWidth : false;
}
1
2
3
2
3
集成为一个vue组件
# 时间格式化
const timeFormat = (value) => {
return value === 0 ? '-' : moment(value).format('YYYY-MM-DD HH:mm:ss');
}
1
2
3
2
3
# 复制到剪贴板
const copyToClipboard = (content) => {
let tempEl = document.createElement('input');
tempEl.setAttribute('value', content);
document.body.appendChild(tempEl);
tempEl.select();
if (document.execCommand('copy')) {
this.$message.success('复制成功');
} else {
this.$message.error('复制失败');
}
document.body.removeChild(tempEl);
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
30 seconds of code (opens new window)
const copyToClipboard = str => {
const el = document.createElement('textarea');
el.value = str;
el.setAttribute('readonly', '');
el.style.position = 'absolute';
el.style.left = '-9999px';
document.body.appendChild(el);
const selected =
document.getSelection().rangeCount > 0
? document.getSelection().getRangeAt(0)
: false;
el.select();
document.execCommand('copy');
document.body.removeChild(el);
if (selected) {
document.getSelection().removeAllRanges();
document.getSelection().addRange(selected);
}
};
// 例子
copyToClipboard('Lorem ipsum'); // 'Lorem ipsum' copied to clipboard.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 事件中的offsetX,offsetY,只有chrome能算准,火狐firefox会时常为0,这时候就需要换一种算法
//firefox event.offsetX 时常为0
// let ox = event.offsetX; //
// let oy = event.offsetY;
let srcObj = event.target || event.srcElement;
let ox = event.offsetX || (event.clientX - srcObj.getBoundingClientRect().left);
let oy = event.offsetY || (event.clientY - srcObj.getBoundingClientRect().top);
1
2
3
4
5
6
2
3
4
5
6
# div支持拖动并限制四周不超过浏览器页面
需要拖动的元素:@mousedown.prevent="move"
元素中拖动无法点击的元素:@mousedown.stop
const move = (e) => {
let draggableElement = this.$refs['popper'] || e.target; //获取目标元素
// 算出鼠标相对元素的位置
let disX = e.clientX - draggableElement.offsetLeft;
let disY = e.clientY - draggableElement.offsetTop;
document.onmousemove = (e) => { //鼠标按下并移动的事件
// 用鼠标的位置减去鼠标相对元素的位置,得到元素的位置
let left = e.clientX - disX;
let top = e.clientY - disY;
let bottom = window.innerHeight - draggableElement.offsetHeight;
let right = window.innerWidth - draggableElement.offsetWidth;
// 限制拖出页面
top <= 0 && (top = 0);
left <= 0 && (left = 0);
left >= right && (left = right);
top >= bottom && (top = bottom);
// 移动当前元素
draggableElement.style.left = left + 'px';
draggableElement.style.top = top + 'px';
// 这两句如果拖动元素没有设置bottom和right可以去掉
draggableElement.style.bottom = 'unset';
draggableElement.style.right = 'unset';
};
document.onmouseup = () => {
document.onmousemove = null;
document.onmouseup = null;
};
}
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
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
# 手动排队等待请求,一次仅允许发送n个请求
如果真的请求特别多,建议去使用更好的(官方一些的)方法
const handleRequestQueue = (paramsArr, maxLength, callback, failCallback) => {
const paramsLength = paramsArr.length;
const requestsQueue = [];
const results = [];
let i = 0;
const handleRequest = (param) => {
const req = api.getSomething(param).then(res => {
const len = results.push(res);
typeof callback === 'function' && callback(res);
if (len < paramsLength && i + 1 < paramsLength) {
requestsQueue.shift();
handleRequest(paramsArr[++i]);
}
// 所有请求发送完毕
}).catch(e => {
results.push(e);
typeof failCallback === 'function' && failCallback(e, data);
});
if (requestsQueue.push(req) < maxLength) {
handleRequest(paramsArr[++i]);
}
};
handleRequest(paramsArr[i]);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 30 seconds of code (opens new window)
# javascript-switch-object (opens new window) 代替switch语法、switch的优雅写法
const switchFn = (lookupObject, defaultCase = '_default') =>
expression => (lookupObject[expression] || lookupObject[defaultCase])();
const knownFruit = () => console.log('Known fruit');
const unknownFruit = () => console.log('Unknown fruit');
const logFruit = {
'apples': knownFruit,
'oranges': knownFruit,
'default': unknownFruit
};
const fruitSwitch = switchFn(logFruit, 'default');
fruitSwitch('apples'); // Logs: 'Known fruit'
fruitSwitch('pineapples'); // Logs: 'Unknown fruit'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# getURLParameters (opens new window) 在Vue中你可以使用 route 来获取参数
# JavaScript
const getURLParameters = url =>
(url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce(
(a, v) => (
(a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a
),
{}
);
1
2
3
4
5
6
7
2
3
4
5
6
7
# Examples
getURLParameters('google.com'); // {}
getURLParameters('http://url.com/page?name=Adam&surname=Smith');
// {name: 'Adam', surname: 'Smith'}
1
2
3
2
3
# createElement (opens new window)
# JavaScript
const createElement = str => {
const el = document.createElement('div');
el.innerHTML = str;
return el.firstElementChild;
};
1
2
3
4
5
2
3
4
5
# Examples
const el = createElement(
`<div class="container">
<p>Hello!</p>
</div>`
);
console.log(el.className); // 'container'
1
2
3
4
5
6
2
3
4
5
6
# deep clone (opens new window)
const deepClone = obj => {
if (obj === null) return null;
let clone = Object.assign({}, obj);
Object.keys(clone).forEach(
key =>
(clone[key] =
typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key])
);
if (Array.isArray(obj)) {
clone.length = obj.length;
return Array.from(clone);
}
return clone;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# async 中异步调用函数
使用Promise.all()
async function f() {
let f1 = func1('prama');
let f2 = func2();
let values = await Promise.all([f1, f2]);
}
1
2
3
4
5
6
2
3
4
5
6
# el-upload 自定义上传文件
之前没有对接过上传接口,这次对接后发现上传接口的数据格式一般来说不是普通的Json,而是FormData,所以我们需要这样上传:
const formData = new FormData();
formData.append('file', file);
formData.append('name', file.name);
formData.append('type', file.type);
let ret = await api.upload(this.action.id, formData);
1
2
3
4
5
2
3
4
5
这里也可以参考postman,Post的body不再是上传Raw,而是formData
# 下载文件
// 传入下载的参数
const getFile = async (url, method = 'get', params = {}, data = {}, errorFunc) => {
const instance = axios.create({
headers: {
'jwt-token': Auth.getJwtToken()
},
responseType: 'blob'
});
startLoading(); // 页面开始加载
const res = await instance.request({url, method, params, data}).finally(endLoading); // 不管怎么样结束加载
const filename = decodeURIComponent(
res.headers['content-disposition'] && res.headers['content-disposition'].split('filename=')[1]
) || '文件';
const contentType = (res.headers['content-type'] && res.headers['content-type'].split(';')[0]) || '';
if (res.data instanceof Blob) {
if (res.data.type === 'application/json') { // 处理后端报错的情况
const rawText = await res.data.text();
const raw = JSON.parse(rawText);
if (raw.code && raw.code !== 0) {
console.log(raw.message);
errorFunc && typeof errorFunc === 'function' && errorFunc(raw.message);
}
} else {
downloadFile(res.data, filename);
}
} else {
const blob = new Blob([res.data], {
type: contentType
});
downloadFile(blob, filename);
}
};
// 下载blob
const downloadFile = (blob, filename) => {
if (window.navigator && window.navigator.msSaveOrOpenBlob) { // for IE
window.navigator.msSaveOrOpenBlob(blob, filename);
} else {
const downloadElement = document.createElement('a');
const href = window.URL.createObjectURL(blob); //创建下载的链接
downloadElement.href = href;
downloadElement.download = filename; //下载后文件名
document.body.appendChild(downloadElement);
// downloadElement.click(); //点击下载
// 兼容火狐
if (document.all) {
downloadElement.click();
} else {
let evt = document.createEvent('MouseEvents');
evt.initEvent('click', true, true);
downloadElement.dispatchEvent(evt);
}
document.body.removeChild(downloadElement); //下载完成移除元素
window.URL.revokeObjectURL(href); //释放掉blob对象
}
};
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# 扁平化树状结构数据
const treeList = [{children: []}];
const flatChildren = (rawList, resList) => {
rawList.forEach(item => {
item.hasOwnProperty('children') && Array.isArray(item.children) && flatChildren(item.children);
delete item.children;
resList.push(item);
});
};
const resList = [];
flatChildren(treeList, resList);
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# iframe通信
// iframe
window.parent.postMessage({action: 'xxx', message: 'this is a msg'}, '*');
// 外部
window.addEventListener('message', (evt) => {
if (evt.data && evt.data.action === 'xxx') {
console.log(evt.data.message);
}
});
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# 根据时间排序
arr.sort((a, b) => moment(a.time).diff(moment(b.time)));
1
# 生成UUID(lodash)
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'.split('')
const uuid = _.times(4, () => _.sample(chars)).join('')
1
2
2
发现一个不错的方法:
const uuid = () => {
const temp_url = URL.createObjectURL(new Blob())
const uuid = temp_url.toString()
URL.revokeObjectURL(temp_url)
return uuid.substring(uuid.lastIndexOf('/') + 1)
}
1
2
3
4
5
6
2
3
4
5
6
# 字节转为单位
function bytesToSize(bytes) {
if (bytes === 0) {
return {num: 0, unit: 'B'};
}
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return {
num: (bytes / Math.pow(k, i)).toFixed(2),
unit: sizes[i]
};
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 前端分页(lodash)
_.chunk(arr, size)
1
# 数组转为树
const arr2tree = (arr, idKey = 'id', parentIdKey = 'parentId', childrenKey = 'children') => {
const map = {};
const tree = [];
arr.map((item) => {
const res = {...item, [childrenKey]: []};
map[res[idKey]] = res;
return res;
}).forEach((item) => {
const parent = map[item[parentIdKey]];
item[parentKey] = parent;
parent ? parent[childrenKey].push(item) : tree.push(item);
});
return tree;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# 给树的每个节点添加rootId
const addRootId = (arr, rootIdKey = 'rootId', idKey = 'id', childrenKey = 'children') => {
const traverse = (node, rootId) => {
node[rootIdKey] = rootId;
node[childrenKey] && node[childrenKey].forEach(child => traverse(child, rootId));
};
arr.forEach(root => {
traverse(root, root[idKey]);
});
};
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
# 给树的每个节点添加层级
const setLevel = (val, level = 1) => {
val.forEach(item => {
item.kcTableLevel = level;
if (item.children) {
setLevel(item.children, level + 1);
}
});
};
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 首字母大写
const firstUpperCase = str => str.replace(/^\S/, l => l.toUpperCase())
1
# 转换对象key/value
const reverseKeyValue = (obj: Record<string, string>) => Object.fromEntries(Object.entries(obj).map(([key, value]) => [value, key]));
1
# ElementPlus 表格常用格式化
import dayjs from 'dayjs';
import type { TableColumnCtx } from 'element-plus/es/components/table/src/table-column/defaults';
export const tableDecimalFormatter = <T extends unknown>(row: T, column: TableColumnCtx<T>, cellValue: unknown, index: number) => {
const number = Number(cellValue);
return (Number.isFinite(number) ? Math.round(number * 100) / 100 : cellValue); // Number(number.toFixed(2))
};
export const tableDateFormatter = <T extends unknown>(row: T, column: TableColumnCtx<T>, cellValue: unknown, index: number) => {
const day = dayjs(cellValue as string | number | Date);
return day.isValid() ? day.format('YYYY-MM-DD HH:mm:ss') : cellValue;
};
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# ElementPlus 常用校验
export const validateAccount = (rule: any, value: string, callback: any) => {
if (!value) {
return callback(new Error('请输入账户名'));
} if (!/^[a-zA-Z0-9_]{1,32}$/.test(value)) {
return callback(new Error('账户名只能包含字母、数字、下划线,最长32位'));
}
return callback();
};
export const validatePhoneNumber = (rule: any, value: string, callback: any) => {
if (value && !/^(?:(?:\+|00)86)?1\d{10}$/.test(value)) {
return callback(new Error('手机号格式不正确'));
}
return callback();
};
export const validateEmail = (rule: any, value: string, callback: any) => {
if (value && !/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(value)) {
return callback(new Error('邮箱格式不正确'));
}
return callback();
};
export const useValidateFile = (fileList: Ref<UploadUserFile[]>, message = '请上传文件') => (rule: any, value: string, callback: any) => {
if (!fileList.value.length) {
return callback(new Error(message));
}
return callback();
};
// onChange时使用
export const checkFile = (uploadFile: UploadFile, uploadFiles: UploadFiles) => {
if (!['jpg', 'mp4', 'png', 'jpeg', 'doc', 'docx', 'xls', 'xlsx', 'pdf', 'txt'].includes(uploadFile.name.split('.').pop()!)) {
uploadFiles.splice(uploadFiles.indexOf(uploadFile), 1);
ElMessage.error(`文件格式不正确: ${uploadFile.name}`);
}
};
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
30
31
32
33
34
35
36
37
38
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
30
31
32
33
34
35
36
37
38
# timeoutPromise
const usePromiseTimeout = (ms?: number) => new Promise((resolve) => setTimeout(resolve, ms))
1
或者
import { promiseTimeout } from '@vueuse/core'
1
# 给Promise加上全局loading
const withElLoading = async <T>(
fn: Promise<T>,
options?: Parameters<typeof ElLoading.service>[0],
) => {
const loadingInstance = ElLoading.service({
lock: true,
fullscreen: true,
...options,
});
return fn.finally(loadingInstance.close)
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# object to URLSearchParams
const obj2params = (obj: {}) => {
const params = new URLSearchParams();
Object.entries(obj).forEach(([key, value]) => {
params.append(key, value as string);
});
return params;
};
1
2
3
4
5
6
7
2
3
4
5
6
7
# 展示n位小数
export function displayNumber(number: number, fixed = 1) {
if (typeof number !== 'number')
return ''
const n = 10 ** fixed
return String(Math.round(number * n) / n)
}
1
2
3
4
5
6
2
3
4
5
6
# 获取数组重复项
export function getDuplicates(arr: any[]) {
const duplicates = arr.filter((item, index) => arr.indexOf(item) !== index)
return [...new Set(duplicates)]
}
1
2
3
4
2
3
4
上次更新: 2023/06/01, 12:40:50