text: /* @flow */
  
  const fnExpRE = /^([\w$_]+|\([^)]*?\))\s*=>|^function\s*\(/
  const simplePathRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/
  // a.b a['b'] a["b"] a[0] a[b]
  // KeyboardEvent.keyCode aliases
  const keyCodes: { [key: string]: number | Array<number> } = {
    esc: 27,
    tab: 9,
    enter: 13,
    space: 32,
    up: 38,
    left: 37,
    right: 39,
    down: 40,
    'delete': [8, 46]
  }
  
  // KeyboardEvent.key aliases
  const keyNames: { [key: string]: string | Array<string> } = {
    // #7880: IE11 and Edge use `Esc` for Escape key name.
    esc: ['Esc', 'Escape'],
    tab: 'Tab',
    enter: 'Enter',
    space: ' ',
    // #7806: IE11 uses key names without `Arrow` prefix for arrow keys.
    up: ['Up', 'ArrowUp'],
    left: ['Left', 'ArrowLeft'],
    right: ['Right', 'ArrowRight'],
    down: ['Down', 'ArrowDown'],
    'delete': ['Backspace', 'Delete']
  }
  
  // #4868: modifiers that prevent the execution of the listener
  // need to explicitly return null so that we can determine whether to remove
  // the listener for .once
  const genGuard = condition => `if(${condition})return null;` // 守卫，不满足直接return
  
  const modifierCode: { [key: string]: string } = {
    stop: '$event.stopPropagation();',
    prevent: '$event.preventDefault();',
    self: genGuard(`$event.target !== $event.currentTarget`),
    ctrl: genGuard(`!$event.ctrlKey`),
    shift: genGuard(`!$event.shiftKey`),
    alt: genGuard(`!$event.altKey`),
    meta: genGuard(`!$event.metaKey`),
    left: genGuard(`'button' in $event && $event.button !== 0`),
    middle: genGuard(`'button' in $event && $event.button !== 1`),
    right: genGuard(`'button' in $event && $event.button !== 2`)
  }
  
  export function genHandlers (
    events: ASTElementHandlers,
    isNative: boolean,
    warn: Function
  ): string {
    let res = isNative ? 'nativeOn:{' : 'on:{' // nativeOn 还是 on
    for (const name in events) { // 遍历每个event的事件名，可能是对象，也可能是数组
      res += `"${name}":${genHandler(name, events[name])},`
    }
    return res.slice(0, -1) + '}'
  }
  
  // Generate handler code with binding params on Weex
  /* istanbul ignore next */
  function genWeexHandler (params: Array<any>, handlerCode: string) {
    let innerHandlerCode = handlerCode
    const exps = params.filter(exp => simplePathRE.test(exp) && exp !== '$event')
    const bindings = exps.map(exp => ({ '@binding': exp }))
    const args = exps.map((exp, i) => {
      const key = `$_${i + 1}`
      innerHandlerCode = innerHandlerCode.replace(exp, key)
      return key
    })
    args.push('$event')
    return '{\n' +
      `handler:function(${args.join(',')}){${innerHandlerCode}},\n` +
      `params:${JSON.stringify(bindings)}\n` +
      '}'
  }
  
  function genHandler (
    name: string,
    handler: ASTElementHandler | Array<ASTElementHandler>
  ): string {
    if (!handler) {
      return 'function(){}'
    }
  
    if (Array.isArray(handler)) { // 如果是数组就遍历数组，递归调用handler对象，逗号链接然后返回
      return `[${handler.map(handler => genHandler(name, handler)).join(',')}]`
    }
  
    const isMethodPath = simplePathRE.test(handler.value)
    const isFunctionExpression = fnExpRE.test(handler.value)
  
    if (!handler.modifiers) { // 如果没有修饰符，并且满足了正则，就返回handler.value
      if (isMethodPath || isFunctionExpression) {
        return handler.value
      }
      /* istanbul ignore if */
      if (__WEEX__ && handler.params) {
        return genWeexHandler(handler.params, handler.value)
      }
      return `function($event){${handler.value}}` // inline statement 如果没有匹配到，比如handlerClick($event)，就会返回这种情况
    } else { // 根据不同的modifiers的key做不同的逻辑
      let code = ''
      let genModifierCode = ''
      const keys = []
      for (const key in handler.modifiers) {
        if (modifierCode[key]) { // 如果满足，就生成对应的代码,stop就会往事件处理函数里插入阻止事件冒泡
          genModifierCode += modifierCode[key]
          // left/right
          if (keyCodes[key]) {
            keys.push(key)
          }
        } else if (key === 'exact') {
          const modifiers: ASTModifiers = (handler.modifiers: any)
          genModifierCode += genGuard(
            ['ctrl', 'shift', 'alt', 'meta']
              .filter(keyModifier => !modifiers[keyModifier])
              .map(keyModifier => `$event.${keyModifier}Key`)
              .join('||')
          )
        } else {
          keys.push(key)
        }
      }
      if (keys.length) {
        code += genKeyFilter(keys)
      }
      // Make sure modifiers like prevent and stop get executed after key filtering
      if (genModifierCode) {
        code += genModifierCode
      }
      const handlerCode = isMethodPath
        ? `return ${handler.value}($event)`
        : isFunctionExpression // (()=>{xxx})()
          ? `return (${handler.value})($event)`
          : handler.value
      /* istanbul ignore if */
      if (__WEEX__ && handler.params) {
        return genWeexHandler(handler.params, code + handlerCode)
      }
      return `function($event){${code}${handlerCode}}`
    }
  }
  
  function genKeyFilter (keys: Array<string>): string {
    return `if(!('button' in $event)&&${keys.map(genFilterCode).join('&&')})return null;`
  }
  
  function genFilterCode (key: string): string {
    const keyVal = parseInt(key, 10)
    if (keyVal) {
      return `$event.keyCode!==${keyVal}`
    }
    const keyCode = keyCodes[key]
    const keyName = keyNames[key]
    return (
      `_k($event.keyCode,` +
      `${JSON.stringify(key)},` +
      `${JSON.stringify(keyCode)},` +
      `$event.key,` +
      `${JSON.stringify(keyName)}` +
      `)`
    )
  }
  