管道符的外观就是一根竖直线,也就是|。这个熟悉Linux的同学肯定不陌生了。而在Vue中,也常常能看见它的身影。比如Vue的过滤器:
<div>我叫:{{name|nameFilter}}div>
过滤器的本质就是一种函数方法,类似java里面输入输出包装流,你传进去一个参数,然后根据这个参数,内部做一些变换映射,得到指定结果的过程。与普通方法不同的时,你只需要在你需要加工的参数后面跟上一根竖线然后再加上方法名字就可以了,写法比较简易。
这个是前提。
在普通js中,可以使用eval方法来实现:
- /**
- * 根据方法名字找到方法体
- * @param methodName
- * @returns {any}
- */
- function callMethodByName(methodName) {
- return eval(methodName)
- }
-
- /**
- * 定义一个测试方法
- * @param p1
- * @param p2
- * @returns {*} 返回两个参数的和
- */
- function testMethod(p1, p2) {
- return p1 + p2
- }
-
- const method = callMethodByName('testMethod') // 找到testMethod这个方法
-
- const result = method(2, 3) // 执行testMethod方法
-
- console.log(result) // 5
在vue中,承载方法的有method、computed和filter。其中computed带有缓存(虽然可以关闭),但是传参实现方式比较特殊,这里不做处理,感兴趣的自己去度娘看看。因此,我们就针对method和filter说一下操作方式。
vue的很多钩子都在它自己的$options里面。其中,所有的method会放在$options的method集合中,因此我们可以这样写:
- export default {
- data() {
- return {
- }
- },
- mounted() {
- const result = this.$options.methods['testMethod'](5, 5)
- console.log(result) // 10
- },
- methods: {
- testMethod(p1, p2) {
- return p1 + p2
- }
- }
- }
filter的实现和method类似,都是在$options里面。
- export default {
- data() {
- return {
- }
- },
- mounted() {
- const result = this.$options.filters['val']('')
- console.log(result) // --
- },
- filters: {
- val(v) {
- return v || '--'
- }
- }
- }
基础知识讲解完毕后,就可以进入正题了。
举个栗子。
- 我们从后端获取了一个列表,里面有id,姓名(name),性别(gender),年龄(age),部门(dept)字段
- 其中性别返回的值为0,1。0-男 1-女 (没有中性,手动滑稽)
- dept返回值为100,101,102。100-人力资源部 101-财务部 102-科技部
- 其余字段真实字符展示。
-
- 原始数据:
- [
- {
- "id": "user001",
- "name": "小王",
- "age": 30,
- "gender": 0,
- "dept": 100
- },
- {
- "id": "user003",
- "name": "老邓",
- "age": 40,
- "gender": 1,
- "dept": 101
- },
- {
- "id": "user003",
- "name": "小芸",
- "age": 27,
- "gender": 1,
- "dept": 102
- }
- ]
这个案例其实很常见,后端对于一些常量值不会返回真实文字,而是返回一个编码。前端开发拿到编码后需要做一些映射转换,将code转为文字。而这转换的过程就是写一个方法去实现。
现在有个需求,打印出这个列表里面的name、gender和dept字段,并且gender和dept要以汉字展示。
先定义gender和dept的转换方法:
- method: {
- gender(v) {
- return v === 1 ? '女' : '男'
- },
- dept(v) {
- let res = ''
- switch (v) {
- case 100:
- res = '人力资源部'
- break
- case 101:
- res = '财务部'
- break
- case 102:
- res = '科技部'
- break
- }
- return res
- }
- }
然后,我们使用List的map功能进行映射:
- //映射
- const list = data.map(({ name, gender, dept }) => {
- return {
- name,
- gender: this.gender(gender),
- dept: this.dept(dept)
- }
- })
-
-
- //自定义打印
- const print=(list)=> {
- list.forEach(({ name, gender, dept }) => {
- console.log(`姓名:${name} 性别:${gender} 部门:${dept}`)
- })
- }
-
- print(list)
但是有个问题,就是代码冗余度较高,你看,即使用展开运算符缩减了代码,gender关键字依旧出现了6次。而且,字段越多,我们要补全的就越多。并且一般这种映射方法都是采用filter的。采用filter的话可以在html中以管道符存在,如果是采用filter,那么单独在非html区域因为this指向原因就无法直接使用,就得再写一套method方法,冗余度更高。
改进办法就是采用自定义管道符实现。我们先将要打印的字段保存到一个数组:
- // 待打印字段
- const fields = [
- {
- prop: '姓名',
- field: 'name|name'
- },
- {
- prop: '性别',
- field: 'gender|gender'
- },
- {
- prop: '部门',
- field: 'dept|dept'
- }
- ]
然后遍历每一个字段,在读到filed的时候进行拆分,然后拿到方法名字,计算出映射后的值:
- function pipleLine(dataList) {
- return dataList.map(v => fields.map(({ field = '' }) => {
- // 仿照vue的系统级filter进行管道格式解析
- const vals = field.split('|')
- let val = v[field]
- if (vals.length > 1) {
- val = v[vals[0]]
- // 循环遍历所有的filter进行层层过滤
- for (let i = 1; i < vals.length; i++) {
- const filter = this.$options.filters[vals[i]] // 根据名字找到系统的filter
- if (typeof filter === 'function') {
- val = filter(val)
- }
- }
- }
- return val
- }))
- }
-
- pipleLine([...])
-
- //返回
- [
- {
- "id": "user001",
- "name": "小王",
- "age": 30,
- "gender": "男",
- "dept": "人力资源部"
- },
- {
- "id": "user003",
- "name": "老邓",
- "age": 40,
- "gender": "女",
- "dept": "财务部"
- },
- {
- "id": "user003",
- "name": "小芸",
- "age": 27,
- "gender": "女",
- "dept": "科技部"
- }
- ]
因为管道符可以无限连接,类似:
<div>我叫:{{name|nameFilter|f2|f3....}}div>
所以我们是遍历了竖线后的每个方法对竖线最左边的原始参数进行处理的。
然后根据需要,自行打印这个列表。
vue的过滤器或者方法都是支持带参数的,如果要解析的话,需要匹配两个括号位置,截取中间字符作为参数。这个后面有空再写吧。
另外,上面的方式只针对vue的写法。普通js可以将:
- this.$options.filters[vals[i]]
-
- 转换为:
-
- eval(vals[i])