我们大部分开发同学,90%时间都与业务在打交道,所以我整理了些我工作中那些减少工作量的函数和方法,分享给各位!(持续补充中...)
一、 处理金额的千分位
背景:由于去年的时候换了新的单位,开始进入金融领域,所以需求上,跟金额打交道也越来越多,需求:在处理数据的过程中,我们拿到的金额常常是以分为单位的一串数字,根据需求我们要换算成单位为‘元’的千分位数据,下面就几种场景分别出了不同的方案:
- 简版,保留两位小数,接收一个
number
,返回number
const handleThousands = (amount: any) => {
let num;
const value = parseFloat(amount).toFixed(2);
if (value.indexOf('.') === -1) {
num = value.replace(/\d{1,3}(?=(\d{3})+$)/g, s => `${s},`);
} else {
num = value.replace(/(\d)(?=(\d{3})+\.)/g, s => `${s},`);
}
return num;
};
/**
*
* @param amountValue 需要格式化的数值
* @param isCents 默认数据是以'分'为单位,如果数据是以'元'为单位,传第二个字段为'false'即可
*/
export const handleAmount = (amountValue = 0, isCents = true): number => {
if (amountValue) {
const normalAmountValue = isCents ? amountValue / 100 : amountValue;
return handleThousands(normalAmountValue);
}
return 0;
};
a. 以分
为单位(默认不需要传第二个参数):
console.log(handleAmount(788000)); // 7,880.00
b. 以元
为单位(第二个参数给 false
):
console.log(handleAmount(788000, false)); // 788,000.00
- 升级版,产品经理有特殊要求的,根据情况保留小数位,添加相应单位,接收
number
,返回string
- 具体需求:
- 1、【小于100万】使用单位(元),保留0位小数。示例:¥ 234,500元
- 2、【大于等于100万,小于1亿】使用单位(万),保留2位小数。示例:¥ 4,500.25万
- 3、【大于等于1亿】使用单位(亿),保留4位小数。示例:¥ 5.3541亿
const handleThousands = (amount, val) => {
let result, num;
let value = parseFloat(amount).toFixed(val);
if (value.indexOf('.') === -1) {
num = value.replace(/\d{1,3}(?=(\d{3})+$)/g, function(s) {
return s + ',';
});
} else {
num = value.replace(/(\d)(?=(\d{3})+\.)/g, function(s) {
return s + ',';
});
}
if (val === 2) {
result = `¥ ${num}万`;
} else if (val === 4) {
result = `¥ ${num}亿`;
} else {
result = `¥ ${num}元`;
}
return result;
};
/**
*
* @param {number} amountValue 需要格式化的数值,
* @return {string} 带货币单位和货币符号 例如:`¥ 100元`;
*/
export const handleAmount = (amountValue: number): string => {
if (amountValue) {
const oldAmountValue = amountValue;
const normalAmountValue = oldAmountValue / 100;
if (normalAmountValue > 100000000) {
return handleThousands(normalAmountValue / 100000000, 4);
} else if (normalAmountValue > 1000000) {
return handleThousands(normalAmountValue / 10000, 2);
} else {
return handleThousands(normalAmountValue, 0);
}
} else {
return '¥ 0';
}
};
a. 以元为单位
console.log(handleAmount(788000)); // ¥ 7,880元
b. 以万为单位
console.log(handleAmount(788000000)); // ¥ 788.00万
c. 以亿为单位
console.log(handleAmount(788000000000)); // ¥ 78.8000亿
二、小数转百分比数值
我根据使用场景给了两种模式:
- 严格模式:向下取舍,保留两位小数;
- 非严格自由模式:四舍五入,保留小数位数可自定义
/**
* 小数转百分比,默认使用精确计量(不进行四舍五入),并保留两位小数
* @param {number} value 传入的小数值 例如:0.223455
* @param {boolean} percent 是否添加单位 ‘%’,默认添加,如不需‘%’,给false 即可 例如:false
* @param {number} precision 四舍五入并可设置精确的小数位,默认不使用; 例如:2
*/
export const getDecimalplaces2percent = (
value = 0,
percent = true,
precision = 0,
): string | number => {
const count = precision ? (value * 100).toFixed(precision) : Math.floor(value * 100 * 100) / 100;
if (!percent) {
return count;
}
return `${count}%`;
};
调用:
a. 默认四舍五入,保留两位小数
console.log(getDecimalplaces2percent(0.234343434)); // 23.43%
b. 纯数字,不带百分号
console.log(getDecimalplaces2percent(0.234343434, false)); // 23.43
c. 设置精确的小数位
console.log(getDecimalplaces2percent(0.234343434, true, 4)); // 23.4343%
三、多环境的配置
我们的项目一般情况下会有多余
1
个或2
个环境,那对于不同环境的配置我们如何来优雅的取到不同的参数?
const { ENV } = window;
const settingsDict = {
local: {
adminUrl: 'https://admin-qa.xxxxxxxxx.com',
courseUrl: 'https://app-qa.xxxxxxxxx.com',
insuranceUrl: 'https://insurance-qa.xxxxxxxxx.com',
},
qa: {
adminUrl: 'https://admin-qa.xxxxxxxxx.com',
courseUrl: 'https://app-qa.xxxxxxxxx.com',
insuranceUrl: 'https://insurance-qa.xxxxxxxxx.com',
},
staging: {
adminUrl: 'https://admin-staging.xxxxxxxxx.com',
courseUrl: 'https://app-staging.xxxxxxxxx.com',
insuranceUrl: 'https://insurance-staging.xxxxxxxxx.com',
},
production: {
adminUrl: 'https://admin.xxxxxxxxx.com',
courseUrl: 'https://app.xxxxxxxxx.com',
insuranceUrl: 'https://i.xxxxxxxxx.com',
},
};
const config = settingsDict[ENV] || settingsDict.local;
const settings = {
...config,
getAdminUrl: () => config.adminUrl,
getInsuranceUrl: (pathname) =>`config.insuranceUrl${pathname}`,
};
export default settings;
把对应的配置写成一个配置文件,通过拿到当前环境,返回对应
url
,同样也可传需要的参数,拼接成想要的地址。
方法调用:
settings.getInsuranceUrl('detail') => https://insurance-qa.xxxxxxxxx.com/detail
四、多区间条件匹配的优化
多区间逻辑判断:把每个条件和返回的
message
信息用对象的方式整合,然后用循环的方式选取出来。
export const getGreetingMessage = name => {
const hour = new Date().getHours();
const messages = [
{
check: () => hour < 9 && hour >= 5,
format: () => `早安,${name},每天都是新的一天!`,
},
{
check: () => hour < 12 && hour >= 9,
format: () => `上午好,${name},开始一天新的工作吧!`,
},
{
check: () => hour < 14 && hour >= 12,
format: () => `中午好,${name},吃顿好的犒劳一下自己吧!`,
},
{
check: () => hour < 19 && hour >= 14,
format: () => `下午好,${name},集中精力向目标前进!`,
},
{
check: () => hour < 23 && hour >= 19,
format: () => `晚上好,${name},又向目标前进了一步哦!`,
},
{
check: () => hour >= 23,
format: () => '夜深喽,早些休息吧!',
},
];
let message = '你好';
messages.forEach(item => {
if (item.check()) {
message = item.format(name);
}
});
return message;
};
五、给img
拼接宽高
给
img
的url
地址拼接宽高
interface IImageResponse {
width: number;
height: number;
url: string;
}
/**
* 处理img,拼接宽、高、url
* @param {string[]} imgArr 接受一个包含图片链接的img数组
* @return {IImageResponse[]} 返回一个拼接宽、高 、url链接的对象数组
*/
export const handleImgInfos = (imgArr: string[]): IImageResponse[] => {
const images = [];
// 创建 img
imgArr.forEach(item => {
const image = new Image();
image.src = item;
image.onload = () => {
const { width, height } = image;
images.push({ width, height, url: item });
};
});
return images;
};
调用方式:
// 入参包含图片链接的数组
const imgStr='http://f3.v.veimg.cn/meadincms/1/2015/1123/20151123081509321.jpg'
console.log(
handleImgInfos(['http://f3.v.veimg.cn/meadincms/1/2015/1123/20151123081509321.jpg'])
);
// [{width: 500, height: 280, url: "http://f3.v.veimg.cn/meadincms/1/2015/1123/20151123081509321.jpg"}]
六、动态增减表单项的解决方案
我们正常的表单都是每个item绑定一个
key
,当我们有需求需要动态增减表单项,就需要单独处理,此处就是应对表单处理过程中的的场景,有两种方式推荐给大家:
- 外部隔离:通过创建多个子
form
表单,用form
表单来实现隔离 - 内部接力:通过绑定不同的
key
来实现每个item
绑定不同id
来区分
基础场景:
1. 外部隔离
外部隔离的方式,是通过创建多个子
form
,来实现子form
和 父form
隔离,在最后提交步骤,拿到子组件form
的formList
及父组件的form
上代码--------->
父组件:
import React, { useState, useCallback } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Button, Divider } from 'antd';
import { v4 as uuidv4 } from 'uuid';
import CardLayout from '@xb/layouts/CardLayout';
import Form1 from './widgets/Form1';
export const formLayout = {
labelCol: { span: 5 },
wrapperCol: { span: 15 },
};
const TestForm = () => {
const form = useForm();
const { getFieldDecorator, validateFields } = form;
const [formList, setFormList] = useState([]);
const [msgList, setMsgList] = useState([{ id: uuidv4() }]);
// 子组件添加formList中
const getForm = useCallback(
item => {
setFormList(r => [...r, item]);
},
[formList],
);
// 添加
const onAdd = useCallback(() => {
setMsgList([...msgList, { id: uuidv4() }]);
}, [msgList]);
//删除
const onDel = useCallback(
i => {
//页面 UI 删除
const tmp = JSON.parse(JSON.stringify(msgList));
tmp.splice(i, 1);
setMsgList([...tmp]);
// formList 数据删除
formList.splice(i, 1);
setFormList(formList);
},
[msgList, formList],
);
// 提交保存
const handleSubmit = () => {
Promise.all([...formList.map(item => item.validateFields()), validateFields()]).then(data => {
console.log(data, '默认返回参数');
const list = data.slice(0, formList.length);
console.log({ ...data[data.length - 1], list }, '拼接后参数');
});
};
return (
<CardLayout>
<Form {...formLayout}>
<Form.Item label="名称">
{getFieldDecorator('name', {
rules: [
{
required: true,
message: '请输入名称',
},
],
})(<Input />)}
</Form.Item>
<Form.Item label="类型">
{getFieldDecorator('type', {
rules: [
{
required: true,
message: '请输入类型',
},
],
})(<Input />)}
</Form.Item>
<Button onClick={onAdd}>添加</Button>
{msgList.map((item, index) => {
return (
<Form1
key={item.id}
msgIndex={index}
value={item}
onDel={() => {
onDel(index);
}}
getForm={getForm}
/>
);
})}
<Divider />
<Divider />
<Form.Item style={{ marginTop: 30, marginLeft: '30%' }}>
<Button type="primary" block onClick={handleSubmit}>
查询
</Button>
</Form.Item>
</Form>
</CardLayout>
);
};
export default TestForm;
子组件:
import React, { useEffect } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Row, Col, Icon } from 'antd';
interface IProps {
msgIndex: number;
value: any;
onDel: () => void;
getForm: (f) => void;
}
const formLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 17 },
};
const Form1: React.FC<IProps> = props => {
const { msgIndex, onDel } = props;
const form = useForm();
const { getFieldDecorator } = form;
useEffect(() => {
props.getForm(form);
}, []);
return (
<Form {...formLayout}>
<Row>
<Col span={23}>
<Form.Item label={`策略${msgIndex + 1}`}>
{getFieldDecorator('strategy', {
rules: [
{
required: true,
message: '请输入策略',
},
],
})(<Input />)}
</Form.Item>
</Col>
<Col span={1}>
{msgIndex !== 0 && (
<Icon
type="minus-circle"
style={{ fontSize: 16, marginTop: 10, marginLeft: -14 }}
onClick={onDel}
/>
)}
</Col>
</Row>
</Form>
);
};
export default Form1;
提交表单:
提交后的参数获取:
2. 内部接力
内部接力的方式,是通过绑定不同的
key
来区分不同的item
,不会创建多个form
,最终提交时,还是同一个form
,缺点就是还需要根据规则把每个item
拆分出来
上代码--------->
import React, { useCallback } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Button, Divider, Row, Col, Icon } from 'antd';
import { v4 as uuidv4 } from 'uuid';
import CardLayout from '@xb/layouts/CardLayout';
export const formLayout = {
labelCol: { span: 5 },
wrapperCol: { span: 15 },
};
const TestForm = () => {
const form = useForm();
const { getFieldDecorator, validateFields, getFieldValue, setFieldsValue } = form;
getFieldDecorator('strategy', { initialValue: [{ id: uuidv4() }] });
const strategy = getFieldValue('strategy');
// 添加
const onAddMsg = useCallback(() => {
const strategyList = getFieldValue('strategy');
const nextKeys = [...strategyList, { id: uuidv4() }];
setFieldsValue({
strategy: nextKeys,
});
}, [getFieldValue, setFieldsValue]);
// 删除
const onDel = useCallback(
i => {
const strategyList = getFieldValue('strategy');
strategyList.splice(i, 1);
setFieldsValue({
strategy: strategyList,
});
},
[getFieldValue, setFieldsValue],
);
// 表单提交
const handleSubmit = () => {
validateFields().then(data => {
console.log(data, '默认返回参数');
let list = [];
const { name, type, strategy, ...strategyList } = data;
strategy.forEach(item => {
list.push({ strategy: strategyList[`strategy[${item.id}]`] });
});
console.log({ name, type, list }, '拼接后参数');
});
};
return (
<CardLayout>
<Form {...formLayout}>
<Form.Item label="名称">
{getFieldDecorator('name', {
rules: [
{
required: true,
message: '请输入名称',
},
],
})(<Input />)}
</Form.Item>
<Form.Item label="类型">
{getFieldDecorator('type', {
rules: [
{
required: true,
message: '请输入类型',
},
],
})(<Input />)}
</Form.Item>
<Button onClick={onAddMsg}>添加</Button>
<Divider />
{strategy.map((k, index) => {
return (
<Row>
<Col span={23}>
<Form.Item label={`策略${index + 1}`}>
{getFieldDecorator(`strategy[${k.id}]`, {
rules: [
{
required: true,
message: '请输入策略',
},
],
})(<Input />)}
</Form.Item>
</Col>
<Col span={1}>
{index !== 0 && (
<Icon
type="minus-circle"
style={{ fontSize: 16, marginTop: 10, marginLeft: -14 }}
onClick={onDel}
/>
)}
</Col>
</Row>
);
})}
<Form.Item style={{ marginTop: 30, marginLeft: '30%' }}>
<Button type="primary" block onClick={handleSubmit}>
查询
</Button>
</Form.Item>
</Form>
</CardLayout>
);
};
export default TestForm;
提交表单:
提交后的参数获取:
升级场景:
在实现完成上面的场景后,产品提出了新的需求,在原来的动态表单上又新增了一层,实现群组的表单动态增减,如下图:
首先我们上面讲过了单层的动态增减表单,下面我们就重点看一下动态增减一个嵌套的form怎么实现:
最外层的组件 index.tsx
import React, { useState, useCallback } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Button, Divider } from 'antd';
import { v4 as uuidv4 } from 'uuid';
import CardLayout from '@xb/layouts/CardLayout';
import Form1 from './widgets/Form1';
import Form2 from './widgets/Form2';
export const formLayout = {
labelCol: { span: 5 },
wrapperCol: { span: 15 },
};
const TestForm = () => {
const form = useForm();
const { getFieldDecorator, validateFields } = form;
const [formList, setFormList] = useState([]);
const [groupFormList, setGroupFormList] = useState([]);
const [msgList, setMsgList] = useState([{ id: uuidv4() }]);
const [msgGroupList, setGroupMsgList] = useState([{ id: uuidv4() }]);
const [subFormList, setSubFormList] = useState([{ id: uuidv4(), form: [] }]);
// 添加formList
const getForm = useCallback(
item => {
setFormList(r => [...r, item]);
},
[formList],
);
// 添加groupFormList
const getGroupForm = useCallback(item => {
setGroupFormList(r => [...r, item]);
}, []);
// 添加单个
const onAdd = useCallback(() => {
setMsgList([...msgList, { id: uuidv4() }]);
}, [msgList]);
//添加群组
const onAddGroup = useCallback(() => {
const id = uuidv4();
setGroupMsgList([...msgGroupList, { id }]);
setSubFormList([...subFormList, { id, form: [] }]);
}, [msgGroupList, subFormList]);
//删除
const onDel = useCallback(
i => {
//页面 UI 删除
const tmp = JSON.parse(JSON.stringify(msgList));
tmp.splice(i, 1);
setMsgList([...tmp]);
// formList 数据删除
formList.splice(i, 1);
setFormList(formList);
},
[msgList, formList],
);
// 删除群组
const onDelGroup = (i: number) => {
//页面 UI 删除
const tmp = JSON.parse(JSON.stringify(msgGroupList));
tmp.splice(i, 1);
setGroupMsgList([...tmp]);
// 数据删除
groupFormList.splice(i, 1);
subFormList.splice(i, 1);
setGroupFormList(groupFormList);
setSubFormList(subFormList);
};
const handleSubmit = () => {
const list = subFormList.map(item => item.form);
Promise.all([
...formList.map(item => item.validateFields()),
...groupFormList.map(item => item.validateFields()),
...list.flat().map(item => item.validateFields()),
validateFields(),
]).then(data => {
console.log(data, '结果');
const singleData = data.slice(0, formList.length);
const groupData = [...data.slice(formList.length, groupFormList.length + formList.length)];
console.log(singleData, groupData);
groupFormList.map((item, index) => {
Promise.all([...list[index].map(i => i.validateFields())]).then(value => {
groupData[index]['list'] = value;
});
});
const params = {
...data[data.length - 1],
list: singleData,
groupData,
};
console.log(params, '参数');
});
};
return (
<CardLayout>
<Form {...formLayout}>
<Form.Item label="名称">
{getFieldDecorator('name', {
rules: [
{
required: true,
message: '请输入名称',
},
],
})(<Input />)}
</Form.Item>
<Form.Item label="类型">
{getFieldDecorator('type', {
rules: [
{
required: true,
message: '请输入类型',
},
],
})(<Input />)}
</Form.Item>
<Button onClick={onAdd}>添加</Button>
<Divider />
{/* 单层策略 */}
{msgList.map((item, index) => {
return (
<Form1
key={item.id}
msgIndex={index}
onDel={() => {
onDel(index);
}}
getForm={getForm}
/>
);
})}
<Divider />
<Button onClick={onAddGroup}>添加群组</Button>
{/* 双层策略 */}
{msgGroupList.map((item, index) => {
return (
<Form2
key={item.id}
msgIndex={index}
onDelGroup={() => {
onDelGroup(index);
}}
getGroupForm={getGroupForm}
subFormList={subFormList}
setSubFormList={setSubFormList}
/>
);
})}
<Divider />
<Form.Item style={{ marginTop: 30, marginLeft: '30%' }}>
<Button type="primary" block onClick={handleSubmit}>
查询
</Button>
</Form.Item>
</Form>
</CardLayout>
);
};
export default TestForm;
单层策略组件 Form1.tsx:
import React, { useEffect } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Row, Col, Icon } from 'antd';
import { FormMethods } from 'rc-form-hooks/dist/formHooks';
interface IProps {
msgIndex: number;
onDel: () => void;
getForm: (f: FormMethods<any>) => void;
}
const formLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 17 },
};
const Form1: React.FC<IProps> = props => {
const { msgIndex, onDel } = props;
const form = useForm();
const { getFieldDecorator } = form;
useEffect(() => {
props.getForm(form);
}, []);
return (
<Form {...formLayout}>
<Row>
<Col span={23}>
<Form.Item label={`策略${msgIndex + 1}`}>
{getFieldDecorator('strategy', {
rules: [
{
required: true,
message: '请输入策略',
},
],
})(<Input />)}
</Form.Item>
</Col>
<Col span={1}>
{msgIndex !== 0 && (
<Icon
type="minus-circle"
style={{ fontSize: 16, marginTop: 10, marginLeft: -14 }}
onClick={onDel}
/>
)}
</Col>
</Row>
</Form>
);
};
export default Form1;
双层策略组件 Form2->index.tsx
import React, { useEffect, useState, useCallback } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Button } from 'antd';
import { v4 as uuidv4 } from 'uuid';
import { FormMethods } from 'rc-form-hooks/dist/formHooks';
import SubPage from './SubPage';
interface IProps {
msgIndex: number;
onDelGroup: () => void;
getGroupForm: (f: FormMethods<any>) => void;
subFormList: any;
setSubFormList: any;
}
const formLayout = {
labelCol: { span: 5 },
wrapperCol: { span: 15 },
};
const Form2: React.FC<IProps> = props => {
const { msgIndex, onDelGroup, subFormList, setSubFormList } = props;
const form = useForm();
const { getFieldDecorator } = form;
const [msgList, setMsgList] = useState([]);
const [formList, setFormList] = useState([]);
// 添加formList
const getForm = useCallback(
(item, groupIndex) => {
const list = subFormList.slice();
setFormList([...formList, item]);
console.log(list, '添加群组的子策略');
// 把子组件的form存起来
list[groupIndex].form = [...list[groupIndex].form, item];
setSubFormList(list);
},
[formList, subFormList],
);
// 添加单个
const onAdd = useCallback(() => {
const id = uuidv4();
setMsgList([...msgList, { id }]);
}, [msgList]);
//删除
const onDel = useCallback(
i => {
//页面 UI 删除
const tmp = JSON.parse(JSON.stringify(msgList));
tmp.splice(i, 1);
setMsgList([...tmp]);
// 数据删除
formList.splice(i, 1);
setFormList(formList);
},
[msgList, formList],
);
useEffect(() => {
props.getGroupForm(form);
}, []);
return (
<Form {...formLayout}>
<div
style={{
height: '100%',
width: '100%',
border: '1px solid pink',
marginTop: 20,
paddingLeft: 10,
}}
>
群组{msgIndex + 1}
<Form.Item label="类型">
{getFieldDecorator('type', {
rules: [
{
required: true,
message: '请输入类型',
},
],
})(<Input />)}
</Form.Item>
<Button onClick={onAdd}>添加</Button>
{msgIndex !== 0 && <Button onClick={onDelGroup}>删除群组</Button>}
{msgList.map((item, index) => {
return (
<SubPage
key={item.id}
msgIndex={index}
groupIndex={msgIndex}
onDel={() => {
onDel(index);
}}
getForm={getForm}
/>
);
})}
</div>
</Form>
);
};
export default Form2;
双层嵌套的子组件 Form->Subpage.tsx
侧重点:
双层嵌套的内层的form
如何和绑定外层,返回一个form
列表
import React, { useEffect } from 'react';
import useForm from 'rc-form-hooks';
import { Form, Input, Row, Col, Icon } from 'antd';
import { FormMethods } from 'rc-form-hooks/dist/formHooks';
interface IProps {
msgIndex: number;
groupIndex: number;
onDel: (i: number) => void;
getForm: (f: FormMethods<any>, id: number) => void;
}
const formLayout = {
labelCol: { span: 5 },
wrapperCol: { span: 16 },
};
const SubPage: React.FC<IProps> = props => {
const { msgIndex, groupIndex, onDel } = props;
const form = useForm();
const { getFieldDecorator } = form;
useEffect(() => {
props.getForm(form, groupIndex);
}, [groupIndex]);
return (
<Form {...formLayout}>
<Row>
<Col span={23}>
<Form.Item label={`策略${msgIndex + 1}`}>
{getFieldDecorator('strategy', {
rules: [
{
required: true,
message: '请输入策略',
},
],
})(<Input />)}
</Form.Item>
</Col>
<Col span={1}>
{msgIndex !== 0 && (
<Icon
type="minus-circle"
style={{ fontSize: 16, marginTop: 10, marginLeft: -50 }}
onClick={() => {
onDel(msgIndex);
}}
/>
)}
</Col>
</Row>
</Form>
);
};
export default SubPage;
七、把字符串作为 URI 组件进行编码、解码
encodeURIComponent()
函数可把字符串作为 URI 组件进行编码。
encodeURIComponent("中文")
// "%E4%B8%AD%E6%96%87"
decodeURIComponent()
函数可把encodeURIComponent
编码完的数据进行编码
decodeURIComponent("%E4%B8%AD%E6%96%87")
// "中文"
八、uuid
生成随机字符串(比随机数高级的随机字符串)
平常我们用到随机数可能会想到的是如下:
Math.random() ⇨ 0.3920174387754096
今天就推荐一个高逼格的生成随机字符串的方法:UUID
UUID
是通用唯一识别码(Universally Unique Identifier
)的缩写,是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部,其目的是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问题。
UUID
是由一组32位数的16进制数字所构成,所以UUID
理论上的总数为16^32=2^128,约等于3.4 x 10^38。也就是说若每纳秒产生1兆个UUID
,要花100亿年才会将所有UUID用完。
UUID的标准型式包含32个16进制数字,以连字号分为五段,形式为8-4-4-4-12的32个字符。
示例:
550e8400-e29b-41d4-a716-446655440000
线上引入方式:
版本4:
<script src="http://wzrd.in/standalone/uuid%2Fv4@latest"></script>
<script>
uuidv4(); // ⇨ v4 UUID
</script>
版本5:
<script src="http://wzrd.in/standalone/uuid%2Fv5@latest"></script>
<script>
uuidv5('http://example.com/hello', uuidv5.URL); // ⇨ v5 UUID
</script>
npm 下载方式:
npm install uuid
根据版本号按需引入:
<!--推荐使用版本4-->
const uuidv4 = require('uuid/v4');
uuidv4(); // ⇨ '10ba038e-48da-487b-96e8-8d3b99b6d18a'
九、clipboard.js:复制文字功能
今天再推荐一个比较常使用的功能,就是复制、剪切功能,不依赖
flash
, 不依赖其他框架,gzip
压缩后只有3kb
大小,下面一起来试试吧!
1. 复制
复制:一个很常见的用例是从另一个元素复制内容。你可以给目标元素添加一个
data-clipboard-target
属性来实现这个功能。这个属性的值就是能匹配到另一个元素的选择器。
- 第三方
CDN
引入方式:github.com/zenorocha/c…
下面是在原生html
场景的使用方式:
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
</head>
<body>
<script>
new ClipboardJS('.btn');
</script>
<input id="foo" value="我是内容5555" style='width:300px'>
<button class="btn" data-clipboard-target="#foo">按钮按钮</button>
</body>
</html>
npm
下载方式:
npm install clipboard --save
下面是在react
组件的使用方式:
import React, { useEffect } from 'react';
import ClipboardJS from 'clipboard'
const App = () => {
useEffect(() => {
const clipboard = new ClipboardJS('.btn')
// 这里是在组件销毁的时候把 clipboard 销毁
return () => clipboard.destroy()
}, [])
return (
<>
<input id="copy-text" value={'内容'} />
<button className="btn" data-clipboard-target="#copy-text">复制</button>
</>
)
}
export default App;
2. 剪切
剪切:你可以定义一个
data-clipboard-action='cut'
属性来指明你想要复制还是剪切内容,使用方式如下:
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
</head>
<body>
<script>
new ClipboardJS('.btn');
</script>
<input id="foo" value="我是内容5555" style='width:300px'>
<button class="btn" data-clipboard-action="cut" data-clipboard-target="#foo">按钮按钮</button>
</body>
</html>
3. 从属性复制文本
从属性复制文本,简单说就是说不用必须从另一个元素来复制,你仅需要给目标元素设置一个
data-clipboard-text
属性即可
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
</head>
<body>
<script>
new ClipboardJS('.btn');
</script>
// 这里点击按钮,复制到粘贴板的内容就是data-clipboard-text上的值
<button class="btn" data-clipboard-text="这里是我要的内容">按钮按钮</button>
</body>
</html>
4. 复制 / 剪切 后的回调:success / error
有时我们复制 / 剪切后,需要给用户一些反馈,这里我们就用到事件的回调,如下
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
</head>
<body>
<script>
var clipboard = new ClipboardJS('.btn');
// 成功后的回调
clipboard.on('success', function(e) {
console.info('Action:', e.action);
console.info('Text:', e.text);
console.info('Trigger:', e.trigger);
e.clearSelection();
});
// 失败后的回调
clipboard.on('error', function(e) {
console.error('Action:', e.action);
console.error('Trigger:', e.trigger);
});
</script>
<button class="btn" data-clipboard-text="这里是我要的内容">按钮按钮</button>
</body>
</html>
如果亲感觉我的文章还不错的话,可以一下添加关注哦!
内容还在持续更新中,怕错过更新的点个关注再走呗!点个关注不迷路哦!😄
求内推
注:如果有小伙伴公司正在招聘,可联系我哦!技术栈:react,ts,antd,webpack...,可以参考我的文章。地点:北京,联系电话:15210667658