实战是检验一个前端的最佳实践,如同打怪涨经验升级,在这个漫长的过程中,我们遇到最多的业务层就是数据的处理 和 获取,最近一个项目刚近尾声,总结出了两个函数,一个是对枚举对象的转换处理,另一个是获取数据属性值,各有其用。
$$
$$
api 的作用是深层次获取对象 或是 数组里面的 某个属性值
参数
$$(target, defaultVal, cb)
参数解析
这个$$
函数接受三个参数:target
,defaultVal
和cb
。
target
:这是你想要创建代理的对象。如果target
是一个数组并且cb
是一个函数,那么它会找到数组中第一个满足cb
函数的元素。如果没有找到满足条件的元素,target
会被设置为一个空对象。defaultVal
:这是当你试图访问target
对象的一个不存在的属性时返回的默认值。cb
:这是一个可选的回调函数,它用于在target
是一个数组时,找到第一个满足该函数的元素。如果cb
不是一个函数或者没有提供,那么target
将不会被修改。
$$ 使用场景一:获取json数组某个索引下的json下面的深层及属性值
const jsonArr = [
{ id: 1, name: '张三', age: 18, info: { sex: '男', address: '北京' } },
{ id: 2, name: '李四', age: 19, info: { sex: '女', address: '北京' } },
{ id: 3, name: '王五', age: 20, info: { sex: '男', address: '北京' } }
]
/**
* 深层次访问 数组
*/
// 获取 jsonArr 数组下面 索引 为 0的值对象下面的 info 值
$$(jsonArr)['0.info'] // {sex: '男', address: '北京'}
// 获取 jsonArr 数组下面 索引 为 0的值对象下面的 info 值下面的 address 值
$$(jsonArr)['0.info.address'] // '北京'
// 获取 jsonArr 数组下面 索引 为 0的值对象下面的 info 值下面的 address 值
$$(jsonArr,'木有访问到值时候的默认值')['0.info.address'] // '北京'
// 获取 jsonArr 数组下面 索引 为 0的值对象下面的 info 值下面的 address 值,没有访问到返回默认值
$$(jsonArr,'木有访问到值时候的默认值')['0.info.phone'] // '木有访问到值时候的默认值'
// 获取 jsonArr 数组下面 索引 满足条件 id =2 的值对象下面的 info 值下面的 address 值
$$(jsonArr,'默认值',(item)=>item.id===2)['info.address'] // '北京'
// 获取 jsonArr 数组下面 索引 满足条件 id =22 的值对象下面的 info 值下面的 address 值,没有找到返回默认值
$$(jsonArr,'默认值',(item)=>item.id===22)['info.address'] // '默认值'
$$ 使用场景二:深层级获取对象下面的属性值
const jsonObj = {
id: 1,
name: '张三',
age: 18,
info: {
sex: '男',
address: '北京'
}
}
// 获取 jsonObj 对象下面 info 值
$$(jsonObj)['info'] // {sex: '男', address: '北京'}
// 获取 jsonObj 对象下面 info 值下面的 address 值
$$(jsonObj)['info.address'] // '北京'
// 获取 jsonObj 对象下面 info 值下面的 address 值
$$(jsonObj,'木有访问到值时候的默认值')['info.address'] // '北京'
// 获取 jsonObj 对象下面 info 值下面的 address 值,没有访问到返回默认值
$$(jsonObj,'木有访问到值时候的默认值')['info.phone'] // '木有访问到值时候的默认值'
// 获取 jsonObj 对象下面 info 值下面的 phone 值,没有给默认值的情况下
$$(jsonObj)['info.phone'] // undefined
在vue
项目中,我们知道在 vue
的 template
不能使用 高级语法 ?.
,但是你确实想用,因为可以节省你很多时间和让你少写很多代码,让你可以多出很多时间来愉快的摸鱼,$$
也是可以完成的
<template>
<div class="hello">
<!-- <p> {{ jsonObj?.info?.address || "默认值" }} </p> 这样子写链式是不支持的,用 $$ 解决即可-->
<!-- 获取 jsonObj 对象下面 info 值下面的 address 值 -->
<p> {{ $$(jsonObj)['info.address'] }} </p>
<!-- 获取 jsonArr 数组下面 索引 满足条件 id =2 的值对象下面的 info ,当然不建议在 模版内写太多判断-->
<p> {{ $$(jsonArr,'默认值',i => i.id===2)['info.address'] }} </p>
<!-- 获取 jsonArr 数组下面 索引 1 的值对象下面的 info 值下面的 address 值 -->
<p> {{ $$(jsonArr,'默认值')['1.info.address'] }} </p>
</div>
</template>
<script>
export default {
data(){
return {
jsonArr: [
{id: 1, name: '张三', age: 18, info: {sex: '男', address: '北京'}},
{id: 2, name: '李四', age: 19, info: {sex: '男', address: '北京'}},
{id: 3, name: '王五', age: 20, info: {sex: '男', address: '北京'}},
],
jsonObj: {
id: 1,
name: '张三',
age: 18,
info: {
sex: '男',
address: '北京'
}
}
}
},
methods: {
$$(target, defaultVal, cb){
if(Array.isArray(target) && typeof cb === 'function'){
target = target.find(i=>cb(i)) || {}
}
return new Proxy(target, {
get: (target, propKey) => {
const proKeyArr = propKey.split('.')
return proKeyArr.reduce((a, b) => a?.[b] || defaultVal, target)
}
})
}
}
}
</script>
源码
function $$(target, defaultVal, cb){
if(Array.isArray(target) && typeof cb === 'function'){
target = target.find(i=>cb(i)) || {}
}
return new Proxy(target, {
get: (target, propKey) => {
const proKeyArr = propKey.split('.')
return proKeyArr.reduce((a, b) => a?.[b] || defaultVal, target)
}
})
}
switchData
前端静态数据映射枚举对象 和 数组 互相转换,场景:后端接口返回的数据是枚举对象,前端需要转换成数组,或者前端定义的枚举对象,后端接口需要转换成枚举对象
switchData({
data = null,
sortAlias = 'index',
typeAlias = 'type',
switchType = 'object',
cb = null,
mapValueName = '',
types = []
})
data:
输入数据,可以是数组或对象(默认:null)。sortAlias:
用于对输出数组进行排序的属性名(默认:'index')。typeAlias:
用于识别输入数组中每个对象的类型的属性名(默认:'type')。switchType:
输入数据的类型('object'或'array'(默认为'object'))。cb:
一个回调函数,用于在将数据转换为数组之前处理数据(默认值:null)。mapValueName:
用于将输入数组中的值映射到不同属性名的属性名(默认值:")。types:
在转换数据时要过滤的类型数组(默认值:[])。
该函数首先根据switchType属性检查输入数据是数组还是对象。 如果输入数据是一个数组,它将根据types属性过滤该数组,然后使用回调函数cb(如果提供的话)或将mapValueName映射到不同的属性名,将其简化为一个对象。 如果输入数据是一个对象,则它根据types属性过滤对象键,然后使用回调函数cb(如果提供的话)或将mapValueName映射到不同的属性名,将其缩减为一个数组。 最后,该函数根据输入数据类型返回处理过的数组或对象。
switchData 使用场景一:枚举对象转换成指定顺序的数组
const billTypeEnum = {
9: { label: '销售出库单', path: '/dhpc/order/sales-delivery-detail', sort: 2 },
4: { label: '收款单', path: '/dhpc/bill/receipt-detail', sort: 0 },
5: { label: '付款单', path: '/dhpc/bill/pay-detail', sort: 1 },
}
switchData({ data: billTypeEnum, sortAlias: 'sort' })
/*
[
{ label: '收款单', path: '/dhpc/bill/receipt-detail', sort: 0, type: 4 },
{ label: '付款单', path: '/dhpc/bill/pay-detail', sort: 1, type: 5 },
{ label: '销售出库单', path: '/dhpc/order/sales-delivery-detail', sort: 2, type: 9 }
]
*/
如果你说你不想使用 typeAlias
的默认值 type
作为枚举键的key
, 如果不是默认值, typeAlias
值改成你业务中的 key
值即可, 示例如下:
onst billTypeEnum = {
9: { label: '销售出库单', path: '/dhpc/order/sales-delivery-detail', sort: 2 },
4: { label: '收款单', path: '/dhpc/bill/receipt-detail', sort: 0 },
5: { label: '付款单', path: '/dhpc/bill/pay-detail', sort: 1 },
}
switchData({ data: billTypeEnum, sortAlias: 'sort', typeAlias: 'value' })
/**
* [
* { label: '收款单', path: '/dhpc/bill/receipt-detail', sort: 0, value: 4 },
* { label: '付款单', path: '/dhpc/bill/pay-detail', sort: 1, value: 5 },
* { label: '销售出库单', path: '/dhpc/order/sales-delivery-detail', sort: 2, value: 9 }
* ]
*/
switchData 使用场景二:枚举对象转换成 指定 types 数组
在这个场景中,不不需要全部转换枚举的每个键值对,你只想通过
types
参数 进行过滤指定的而已, 示例如下:
const billTypeEnum = {
9: { label: '销售出库单', index: 14 },
4: { label: '收款单', index: 3 },
5: { label: '付款单', index: 2 },
6: { label: '销售退货单', index: 4 },
7: { label: '采购入库单', index: 0 },
8: { label: '采购退货单', index: 1 },
11: { label: '其他入库单', index: 5 },
12: { label: '其他出库单', index: 6 },
13: { label: '成本调价单', index: 7 },
19: { label: '应收调整单', index: 8 },
26: { label: '应付调整单', index: 9 },
28: { label: '组装拆装单', index: 10 },
29: { label: '其他收入单', index: 11 },
30: { label: '费用单', index: 12 },
33: { label: '现金银行转款单', index: 13 }
}
switchData({ data: billTypeEnum, types:[6,12,33] })
/**
* [
* { label: '销售退货单', index: 4, type: 6 },
* { label: '其他出库单', index: 6, type: 12 },
* { label: '现金银行转款单', index: 13, type: 33 }
* ]
*/
// 如果想自定义返回的数据类型时候,你可以使用 cb 参数 类似 JavaScript里面的 map api
switchData({ data: billTypeEnum, types:[6,12,33], cb: item=>item.label })
// ['销售退货单', '其他出库单', '现金银行转款单']
switchData 使用场景三:JSON 数组 转换成 枚举对象
现有json数组
const jsonArr = [
{ id: 1, name: '张三' },
{ id: 2, name: '李四' },
{ id: 3, name: '王五' }
]
使用 switchData 转换成 枚举对象
// 把json里面的 id 值作为枚举映射 key(typeAlias: 'id')
switchData({ data: jsonArr, switchType:'array', typeAlias: 'id'})
/**
{
1:{ id: 1, name: '张三' },
2:{ id: 2, name: '李四' },
3:{ id: 3, name: '王五' }
}
*/
如果你想要转换结果数据是这样的:
// {1: '张三', 2: '李四', 3: '王五'}
可以这样做
// 枚举映射 取 json对象 对应的 mapValueName 参数参数传入的 键值
switchData({ data: jsonArr, switchType:'array', typeAlias: 'id', mapValueName: 'name'})
// {1: '张三', 2: '李四', 3: '王五'}
源码
function switchData({data = null, sortAlias = 'index', typeAlias = 'type', switchType = 'object', cb = null, mapValueName = '', types = [] }){
if(!data) return
const filterCb = (t)=> !types.length || (types.length && types.includes(t*1))
if(Array.isArray(data) || switchType === 'array'){ // 数组 转 对象
return data.filter(i=>filterCb(i[typeAlias])).reduce((p,c)=>{
p[c[typeAlias]] = typeof cb ==="function" ? cb(c) : mapValueName ? c[mapValueName] : c
return p
},{})
} else { // 对象 转 数组
return Object.keys(data).filter(i=> filterCb(i)).reduce((p,c)=>{
const val = {...data[c],[typeAlias]:c*1}
p[data[c][sortAlias]] = typeof cb ==="function" ? cb(val) : val
return p
},[]).filter(Boolean)
}
}
结语
如果你有更好的想法,欢迎留言
文中若有不准确或错误的地方,欢迎指出