全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 10 分钟 ✍️ juanwangdev

插值表达式与事件绑定

Vue编译阶段将插值表达式 {{}} 转为 _s() 调用,事件绑定 @click 转为 on 配置对象。

插值表达式处理

文本节点解析

JavaScript
// 模板: <p>{{ msg }}</p>
// AST文本节点
{
  type: 2,
  text: '{{ msg }}',
  expression: '_s(msg)'
}

// 解析过程
function parseText(text, delimiters) {
  const tagRE = new RegExp(
    escapeRegex(delimiters[0]) + '([\\s\\S]+?)' + escapeRegex(delimiters[1]),
    'g'
  )
  
  if (!tagRE.test(text)) return null
  
  const tokens = []
  let lastIndex = tagRE.lastIndex = 0
  let match, token
  
  while ((match = tagRE.exec(text))) {
    // 静态文本
    if (match.index > lastIndex) {
      tokens.push(JSON.stringify(text.slice(lastIndex, match.index)))
    }
    // 插值表达式
    token = `_s(${match[1].trim()})`
    tokens.push(token)
    lastIndex = match.index + match[0].length
  }
  
  // 剩余文本
  if (lastIndex < text.length) {
    tokens.push(JSON.stringify(text.slice(lastIndex)))
  }
  
  return tokens.join('+')
}

{{ msg }} hello {{ name }} 转为 _s(msg)+" hello "+_s(name)

插值编译结果

JavaScript
// 模板
<p>{{ message }} - {{ count + 1 }}</p>

// 渲染函数
_c('p', [_v(_s(message) + " - " + _s(count + 1))])

插值通过 _s() 转为字符串,静态文本转为字符串字面量,用 + 连接。

事件绑定处理

v-on 解析

JavaScript
function processAttrs(el) {
  const list = el.attrsList
  for (let i = 0; i < list.length; i++) {
    const { name, value } = list[i]
    const dirMatch = name.match(dirRE)
    
    if (dirMatch && name.match(onRE)) {
      // v-on:click="handler"
      name = name.replace(onRE, '')  // 提取事件名
      addHandler(el, name, value, modifiers, true)
    }
  }
}

提取 v-on 后的事件名,调用 addHandler 添加到 el.events

addHandler 实现

JavaScript
function addHandler(el, name, value, modifiers) {
  modifiers = modifiers || emptyModifiers
  
  // 处理修饰符
  if (modifiers.once) {
    name = '!' + name  // 标记once事件
  }
  if (modifiers.capture) {
    name = '~' + name  // 标记capture事件
  }
  if (modifiers.passive) {
    name = '&' + name  // 标记passive事件
  }
  
  // 存储到el.events
  el.events = el.events || {}
  el.events[name] = { value, modifiers }
}

通过前缀字符标记修饰符:! once、~ capture、& passive。

事件代码生成

JavaScript
function genHandlers(events, isNative) {
  let res = isNative ? 'nativeOn:' : 'on:'
  let str = ''
  for (const name in events) {
    str += `"${name}":${genHandler(name, events[name])},`
  }
  return res + `{${str}}`
}

function genHandler(name, handler) {
  if (!handler) return 'function(){}'
  
  if (Array.isArray(handler)) {
    return `[${handler.map(h => genHandler(name, h)).join(',')}]`
  }
  
  const isPath = pathRE.test(handler.value)
  const maybeMethod = fnExpRE.test(handler.value)
  
  if (typeof handler.value === 'string' && !maybeMethod) {
    return `function($event){${handler.value}}`
  }
  
  return handler.value
}

简单表达式直接返回函数引用,字符串表达式包装为 function($event){...}

事件修饰符编译

JavaScript
// v-on:click.stop="onClick"
// 编译为
on: {
  "click": function($event) {
    $event.stopPropagation()
    return onClick($event)
  }
}

// v-on:submit.prevent="onSubmit"
// 编译为
on: {
  "submit": function($event) {
    $event.preventDefault()
    return onSubmit($event)
  }
}

修饰符在codegen阶段包装为事件对象方法调用。

综合示例

JavaScript
// 模板
<button @click="handleClick" :disabled="isDisabled">
  {{ text }}
</button>

// AST
{
  tag: 'button',
  events: { click: { value: 'handleClick' } },
  attrsMap: { ':disabled': 'isDisabled' },
  children: [{ type: 2, text: '{{ text }}', expression: '_s(text)' }]
}

// 渲染函数
_c('button', {
  on: { "click": handleClick },
  attrs: { "disabled": isDisabled }
}, [_v(_s(text))])

事件、属性、插值分别编译到 onattrs、子节点数组。

要点总结

  • 插值表达式通过正则匹配提取,转为 _s(expr) 调用
  • 混合文本中插值与静态文本用 + 连接
  • v-on 解析事件名,通过前缀字符标记修饰符
  • 事件存储到 el.events,codegen生成 on 配置对象
  • 字符串表达式包装为 function($event){...}
  • 修饰符如 .stop/.prevent 编译为事件对象方法调用

📝 发现内容有误?点击此处直接编辑

← 上一篇 插槽实现原理
下一篇 → 模板解析与AST生成
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库