前言
- 常网IT源码上线啦!
- 本篇录入技术选型专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
- 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
- 接下来想分享一些自己在项目中遇到的技术选型以及问题场景。
生怕体检像X光,把我们平常偷偷熬的夜、应酬喝的酒、解压吃进去的夜宵,都照的一清二楚。
一、前言
最讲封装,还是用组件例子来说吧,不然干巴巴说有点干巴巴。
我们就来一个最简单的表格组件来讲。
像这样子的表格效果,在很多详情页的时候都会看到。
可随意配置,独占一行,或者一行两列。
于是,我们来思考一下怎么封装较好。
二、组件封装
我们先给组件一个命名:PaleTable,灰白表格。
那应该怎么开发。
我们看样式,其实他就是一个表格,一个好的组件,往往用最朴实无华的元素开发。
td。
一行几列,可以用colspan属性实现。
代码如下:
一个最简单的小组件就完成了。
<table class="static-table">
<tr v-for="(list, key) in data" :key="key">
<template v-for="(item, itemKey) in list">
<td class="labels" :key="item.labels">{{ item.labels }}</td>
<td class="value" :colspan="item.colspan" :width="item.width" :key="itemKey">
<span >{{ item.value }}</span>
</td>
</template>
</tr>
</table>
props: {
data: {
type: Array,
default: () => [],
},
},
父组件传入:
data: [
// 这是一行
[
{
labels: '标题',
value: '',
width: '80%',
colspan: 3,
},
],
// 这是一行两列
[
{
labels: '类型',
value: '',
width: '40%',
},
{
labels: '时间',
value: '',
width: '40%',
},
],
]
三、需求提升
很显然,这只适用于最简单的展示。
而需求往往是不会这么简单就放过我们的,比如,添加附件下载,图片查看,按钮穿插进去。
于是,我们的PaleTable组件得到更强大的提升。
<template>
<table class="static-table">
<tr v-for="(list, key) in data" :key="key">
<template v-for="(item, itemKey) in list">
<td class="labels" :key="item.labels">{{ item.labels }}</td>
<td class="value" :colspan="item.colspan" :width="item.width" :key="itemKey">
// 数组
<div>
<div v-for="(file, fileIndex) in item.value" :key="fileIndex"></div>
</div>
// 附件
<div class="file-content" v-if="item.isFile">
<div class="file-content-block" v-for="(file, fileIndex) in item.value" :key="fileIndex">
<span class="file-content-block__text" title="点击可下载" @click="clickName(file)">{{ file.originalName }}</span>
<el-image
v-if="['png', 'jpg', , 'jpeg', 'gif'].includes(file.extension)"
style="width: 50px; height: 50px"
:src="file.src"
fit="fill"
:preview-src-list="[file.src]"
@click="handleClickItem"
>
</el-image>
<div v-else style="height: 50px"></div>
</div>
</div>
// 按钮
<div v-else-if="item.btn && item.btn.isShow" :style="item.btn.style">
<span>{{ item.value }}</span>
<div>
<i v-if="item.btn.icon" :class="item.btn.icon.class" :style="item.btn.icon.style"></i>
<el-button :type="item.btn.type || 'text'" size="small" plain @click="$emit('btnClick', item)">{{ item.btn.name }}</el-button>
</div>
</div>
// 最简单的纯文字
<span v-else>{{ item.value }}</span>
</td>
</template>
</tr>
</table>
</template>
<script>
export default {
props: {
data: {
type: Array,
default: () => [],
},
},
methods: {
// 点击文件名字,进行下载
clickName(file) {
this.$emit('click', file)
},
// 关闭el-image遮罩层
handleClickItem() {
this.$nextTick(() => {
// 获取遮罩层dom
let domImageMask = document.querySelectorAll('.el-image-viewer__mask')
if (!domImageMask.length) {
return
}
domImageMask.forEach((d) => {
d.addEventListener('click', () => {
// 点击遮罩层时调用关闭按钮的 click 事件
d.nextElementSibling.click()
})
})
})
},
},
}
</script>
<style scoped lang='scss'>
.static-table {
width: 100%;
border-left: 1px solid #e5e5e5;
border-collapse: collapse;
color: #333;
margin-bottom: 20px;
}
.static-table tr td {
border-bottom: 1px solid #e5e5e5;
border-right: 1px solid #e5e5e5;
border-top: 1px solid #e5e5e5;
padding: 15px 12px;
background: white;
}
.static-table .labels {
width: 160px;
vertical-align: middle;
font-weight: 400;
background-color: #f8f8f8;
}
.static-table .el-radio__input.is-disabled.is-checked .el-radio__inner::after {
background-color: #a3b8dd;
width: 10px;
height: 10px;
}
.static-table .el-input.is-disabled .el-input__icon {
display: none;
}
.el-static-table thead tr th {
color: #333;
background-color: #f8f8f8;
font-weight: normal;
}
.static-table .el-checkbox__input.is-disabled.is-checked .el-checkbox__inner:after {
border-color: #a3b8dd;
}
.file-content {
display: flex;
align-items: center;
&-block {
padding: 10px;
border: 1px solid #f3f3f3;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
&__text {
margin-right: 10px;
cursor: pointer;
}
}
}
</style>
这么一个强大的组件就封装好了。
我们来处理一下数据。
四、数据处理
细心的你,想必也发现了,我们的value是直接写死在数组里面的。
那,我们需要动态的设置value属性。
一般来说,我们拿到后端的数据,都是对象结构。
而我们上面定义的是没有name(后端返回的key)
怎么确定一个二维数组的位置?
那就是行和列。
假设我们已经有了,setArrItem函数,先不用管这个函数是怎么实现的。
let map = {
feedbackTitle: [0, 0],
type: [1, 0],
time: [1, 1],
}
setArrItem(map, 'value', this.testData, this.form)
解释一下:setArrItem函数接收第一个参数,是映射关系,map对象里面的key,对应着后端返回的对象的key,后面的数组是我们传入PaleTable的数组的行和列
第二个参数:修改this.testData的value属性。
第三个参数:传入PaleTable的数组
第四个参数:后端返回的对象
这样子根据现有的数据结构就可以动态设置value渲染。
接下来,我们看看setArrItem函数怎么实现
/**
* 设置数组的值
*
* @param {Object} map
* @param {String} value
* @param {Array} arr
* @param {Object} obj
* @example
* let map = { metaDataSubjectName: [0, 0] };
* let arr = [ [ { value: 1 } ] ]
* let obj = { metaDataSubjectName: 1 }
* setArrItem(map, 'value', arr, obj)
* 遍历map拿到 [0, 0] 的值,设置arr的属性,arr[0[0]]赋值为obj['metaDataSubjectName']的值
*/
const setArrItem = (map, value, arr, obj) => {
for (let key in map) {
if (Object.hasOwnProperty.call(map, key)) {
const item = getArrItem(arr, map[key])
item[value] = obj[key] // 设置value
}
}
}
map是一个对象,forin遍历取值。
18行的getArrItem函数,主要是从arr数组中,拿到我们想要更新value的对象。
如:
{
labels: '受理人',
value: '',
width: '40%',
}
看看getArrItem的实现。
/**
* 获取数组中的值
*
* @param {*} data
* @param {*} valueArr
* @return {*}
* @example
* getArrItem([{a: 1}], [0, 'a']) ==> 1
*/
const getArrItem = (data, valueArr) => {
if (valueArr.length == 0) {
return data
}
data = data[valueArr.shift()]
return getArrItem(data, valueArr)
}
第一个参数data:是我们的arr
第二个参数是数组:行和列
valueArr.shift():取出行,data就可以精确到第几行,接着递归调用自己。
第二次遍历,valueArr还有一个列,不走if。
继续valueArr.shift():取出列,data就可以精确到第几列,接着递归调用自己。
此时valueArr为空,返回data,此时获取到arr的第几行第几列的对象了。
完美实现~
五、优化
思考一下,这样子处理,会不会觉得麻烦?
是否有更好的实现?
如果testData数组里面的字段顺序换了一下,那动态设置value的map里面的数组[0,1]也要跟着换,有点麻烦。
那怎么优化好?
其实可以在testData的每个对象里面添加key,即后端返回的key,这样子就对应上了,我们只需要循环的时候去查找就好。
那我一开始为什么不这样子做?
目前做法:相当于已经帮我们关系映射好了,就少了一层查找,也就是少了一层for循环。
而如果把key加在testData数组里面,会增加循环的次数。
那是否一开始数组就不要设置二维数组?
可以,设置一维数组,全部平铺化,就根xlsx表格一样,记录行和列去映射,但对直观性就不是很好。
无法一眼就看出来。这也是为什么一开始要设计成二维数组。(有二维数组,处理值的时候可以打平化)
因为我们的PaleTable需要到。
其实还有很多可能比较好的处理方式,看个人的抉择。
至此撒花~
后记
我们在实际项目中或多或少遇到一些奇奇怪怪的问题。
自己也会对一些写法的思考,为什么不行🤔,又为什么行了?
最后,祝君能拿下满意的offer。
我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车
👍 如果对您有帮助,您的点赞是我前进的润滑剂。
以往推荐
前端哪有什么设计模式(12k+)
为什么没人用mixin(7k+)