Skip to content

Vue源码——模版编译(八) #39

Open
@coderInwind

Description

@coderInwind

前言

在前篇文章我们了解到了在 vue 模板编译流程中解析到文本时调用 chars 钩子,在里面处理相关的内容;我们知道,Vue 可以使用插值符号在文本中随意的穿插变量,那么它是怎么解析插值语法的呢?这就用到了我们的 parseText 函数。

if (!inVPre && text !== " " && (res = parseText(text, delimiters))) {
  child = {
    type: 2,
    expression: res.expression,
    tokens: res.tokens,
    text,
  };
} else if (
  text !== " " ||
  !children.length ||
  children[children.length - 1].text !== " "
) {
  child = {
    type: 3,
    text,
  };
}

正文

自定义插值符号

在编译器 options 中有属性 delimiters 允许用户传入自定义的插值符号(默认的插值符号是{{ }}),此处 buildRegex 函数根据用户传入插值符号生成用于匹配的正则表达式

const buildRegex = cached(delimiters => {
  // 前面添加反斜杠在正则中用作转义
  const open = delimiters[0].replace(regexEscapeRE, '\\$&')
  const close = delimiters[1].replace(regexEscapeRE, '\\$&')
  // 生成正则表达式
  return new RegExp(open + '((?:.|\\n)+?)' + close, 'g')
})

函数接收两个参数,1、文本字符串;2、用户自定义的插值符号

// 默认情况下匹配文本中插值符号的正则
const defaultTagRE = /\{\{((?:.|\r?\n)+?)\}\}/g
// 匹配 delimiters 正则
const regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g
// flow 类型
type TextParseResult = {
  expression: string,
  tokens: Array<string | { '@binding': string }>
}

export function parseText (
  text: string,
  delimiters?: [string, string]
): TextParseResult | void {
  // 匹配插值符号的正则
  const tagRE = delimiters ? buildRegex(delimiters) : defaultTagRE
  
  if (!tagRE.test(text)) {
    return
  }

  const tokens = []
  const rawTokens = []
  // 下一次开始匹配的位置
  let lastIndex = tagRE.lastIndex = 0

  let match, index, tokenValue
  // 以首个插值匹配结果为条件
  while ((match = tagRE.exec(text))) {
    // 开头的位置
    index = match.index
    if (index > lastIndex) {
      // 插值符号前的文本
      rawTokens.push(tokenValue = text.slice(lastIndex, index))
      // 插值符号前的文本(json)
      tokens.push(JSON.stringify(tokenValue))
    }
    // 过滤非法符号
    const exp = parseFilters(match[1].trim())
    // 加工后的变量名
    tokens.push(`_s(${exp})`)
    // 加工后的变量名
    rawTokens.push({ '@binding': exp })

    lastIndex = index + match[0].length
  }
  if (lastIndex < text.length) {
    rawTokens.push(tokenValue = text.slice(lastIndex))
    tokens.push(JSON.stringify(tokenValue))
  }
  return {
    // 以 +号连接成字符串
    expression: tokens.join('+'),
    tokens: rawTokens
  }
}

模板文本为:<div>姓名:[name];年龄:[age]</div>
解析后的返回值形如:

{
  expression: "\"姓名:\"+_s(name)+\";年龄:\"+_s(age)",
  tokens:["姓名",{@binding: "name"},";年龄:",{@binding: "age"}]
}

总结

本篇文章介绍了 parseText 的作用,这里对我们 Vue 的插值语法文本进行加工,为后续 render 函数渲染 dom 做准备

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions