# filter 的本质

# filter 的本质

在函数式编程中有一个概念叫 compose(或 pipe),可以将多个函数组合起来,得到一个新的函数,其本质就是上一个函数的返回值作为下一个函数的参数,这和 fitler 很类似。

实际上 Angular 也有类似 filter 的功能叫做 pipeline,我个人觉得这个名字更贴切。

Vue 中,我们可以在文本以及属性中使用 filter,并且 filter 还可以串联,那么 filter 是如何被解析的呢?

# 解析 filter

以在文本中使用为例:

<p>hello {{ name | capitalize }}</p>

filter 的处理可以理解为一个文本解析器:

  • 先匹配模板标记中的内容,如上例的 name | capitalize,通过正则很容易办到
  • 依次遍历每一位,判断是否是 |
  • 保存 filter 继续解析
  • 转换为代码字符串
function parseFilters(exp) {
  const filters = []
  let experssion
  let lastFilterIndex
  let i

  const pushFilter = () => {
    filters.push(exp.slice(lastFilterIndex, i).trim())
  }

  for (i = 0; i < exp.length; i++) {
    // 确保不是 ||
    if (exp[i] === '|' && exp[i - 1] !== '|' && exp[i + 1] !== '|') {
      // 变量,如上例的 name
      if (experssion === undefined) {
        experssion = exp.slice(0, i).trim()
        // 下一次开始截取的位置
        lastFilterIndex = i + 1
      } else {
        pushFilter()
      }
    }
  }

  // 处理最后一项
  // 如 name | capitalize | addColon
  // addColon 后面没有 | 符号,所以没办法触发循环中的 pushFilter 函数
  if (expression === undefined) {
    experssion = exp.slice(0, i).trim()
  } else {
    pushFilter()
  }
}

# 处理 filter 的参数

最后,需要将 filter 转换为对应的代码字符串:

const wrapFilter(arg, filter) {
  const i = filter.indexOf('(')

  if (i < 0) {
    return `_f(${filter})(${arg})`
  } else {
    const filterName = filter.slice(0, i)
    let args = filter.slice(i + 1)
    args = args === ')' ? args : ',' + args
    return `_f(${filter})(${arg + args}`
  }

}

if (filters.length) {
  for (let i = 0; i < filters.length; i++>) {
    // experssion 实际就是下一个 filter 的参数
    expression = wrapFilter(experssion, filters[i])
  }
}