vue源码解析--过滤器函数(filter-parse)的解析

70 阅读1分钟

vuefilter允许用在两个地方,一个是双括号插值,一个是v-bind表达式后面,如果解析到这两种情况,执行parseFilters解析filter 具体文件目录在src\compiler\parser\filter-parser.js

解析思路是:

  1. 将接收到的表达式字符串循环解析处理,如果是字母或者数字,不做处理,直接跳过
  2. 如果解析到" ,' ,/ 或者模板字符串,则将对应的标记字段赋值为true,直到解析到下一个对应的闭合符合前,中间的任何字符都不进行处理,直接跳过。
  3. 如果解析到{}[] 字符串,则将对应计数字段加一或者减一
  4. 如果首次解析到 | ,并且第三点的计数字段都为0,符合要求。在该字符前面的是基础表达式,后面的则是过滤器函数,将lastFilterIndex 字段设为 i+1 ,表示过滤器函数从 i + 1开始截取。同时将基础表达式赋值给expression
  5. 如果再次解析到|,并且符合要求,截取slice.(lastFilterIndex ,i),将过滤器函数存入 filters 字段中,再将lastFilterIndex 字段设为 i+1
  6. 解析完表达式以后,将最后的过滤器函数再推入filters数组中,最后调用wrapFilter生成最终的表达式 eg:arr | get | classNameGet => _f("classNameGet")(_f("get")(arr))

该函数只是对表达式进行解析,如果有语义错误的表达式,会将错误的表达式抛出,在其他地方进行报错提示 比如 " 未闭合 在这里插入图片描述

/* @flow */

const validDivisionCharRE = /[\w).+\-_$\]]/
// filter允许用在两个地方,一个是双括号插值,一个是v-bind表达式后面,如果解析到这两种情况,执行parseFilters解析filter
export function parseFilters(exp: string): string {
  let inSingle = false
  let inDouble = false
  let inTemplateString = false
  let inRegex = false
  let curly = 0
  let square = 0
  let paren = 0
  let lastFilterIndex = 0
  let c, prev, i, expression, filters
  // for循环传入的value字符串
  // 0x27 == ' ; 0x5C == \ ; 0x22 == " ; 0x60 == ` ; 0x2f == / ; 0x7c == |
  // 
  for (i = 0; i < exp.length; i++) {
    prev = c
    c = exp.charCodeAt(i)
    if (inSingle) {
      if (c === 0x27 && prev !== 0x5C) inSingle = false
    } else if (inDouble) {
      if (c === 0x22 && prev !== 0x5C) inDouble = false
    } else if (inTemplateString) {
      if (c === 0x60 && prev !== 0x5C) inTemplateString = false
    } else if (inRegex) {
      if (c === 0x2f && prev !== 0x5C) inRegex = false
    } else if (
      c === 0x7C && // pipe
      exp.charCodeAt(i + 1) !== 0x7C &&
      exp.charCodeAt(i - 1) !== 0x7C &&
      !curly && !square && !paren
    ) {
      // 如果是 | ,则有可能是fitler, 要求前后的字符都不能为 | , 并且不能在 { }, 【】,() 包裹中
      if (expression === undefined) {
        // first filter, end of expression
        lastFilterIndex = i + 1
        expression = exp.slice(0, i).trim()
      } else {
        pushFilter()
      }
    } else {

      switch (c) {
        // 解析为 “ 时,标记为双引号
        case 0x22: inDouble = true; break         // "
        // 解析为 ’ 时,标记为单引号
        case 0x27: inSingle = true; break         // '
        // 解析为 ` 时,标记为模板字符串
        case 0x60: inTemplateString = true; break // `
        // 解析为( 时,paren 计数加一, 通过 paren 是否为0判断 () 是否闭合
        case 0x28: paren++; break                 // (
        // 解析为 )时,paren 计数减一
        case 0x29: paren--; break                 // )
        // 解析为 [ 时, square 计数加一 ,通过 square 是否为0判断 [] 是否闭合
        case 0x5B: square++; break                // [
        // 解析为 ] 时, square 计数减一
        case 0x5D: square--; break                // ]
        // 解析为 { 时, curly 计数加一, 通过 curly 是否为0判断 {} 是否闭合
        case 0x7B: curly++; break                 // {
        // 解析为 } 时, curly 计数减一,
        case 0x7D: curly--; break                 // }
      }
      // 如果是 / ,向前找第一个非空格的字符
      if (c === 0x2f) { // /
        let j = i - 1
        let p
        // find first non-whitespace prev char
        for (; j >= 0; j--) {
          p = exp.charAt(j)
          if (p !== ' ') break
        }
        // 如果 p 不存在或者正则校验不通过,则说明是正则
        if (!p || !validDivisionCharRE.test(p)) {
          inRegex = true
        }
      }
    }
  }
  // 如果expression 为空,则说明没有没有filter函数,或者是写法出了问题。某些符号没闭合
  if (expression === undefined) {
    expression = exp.slice(0, i).trim()
  } else if (lastFilterIndex !== 0) {
    pushFilter()
  }
  // 初始化filters变量。将过滤器函数推入filters数组中
  function pushFilter() {
    (filters || (filters = [])).push(exp.slice(lastFilterIndex, i).trim())
    lastFilterIndex = i + 1
  }
  // 如果有过滤器函数,则结合expression生成最终的表达式
  if (filters) {
    for (i = 0; i < filters.length; i++) {
      expression = wrapFilter(expression, filters[i])
    }
  }

  return expression
}
// filter函数结合expression生成最终的表达式
function wrapFilter(exp: string, filter: string): string {
  const i = filter.indexOf('(')
  // filter是一个函数名称,如 fn
  if (i < 0) {
    // _f: resolveFilter
    return `_f("${filter}")(${exp})`
  } else {
    // filter 是一个函数调用,如 fn(arg)或 fn()
    const name = filter.slice(0, i)
    const args = filter.slice(i + 1)
    return `_f("${name}")(${exp}${args !== ')' ? ',' + args : args}`
  }
}