[{"data":1,"prerenderedAt":3660},["ShallowReactive",2],{"page-\u002Fvue\u002Fvue事件绑定原理":3},{"id":4,"title":5,"body":6,"description":3653,"extension":3654,"meta":3655,"navigation":234,"path":3656,"seo":3657,"stem":3658,"__hash__":3659},"content\u002FVue\u002FVue事件绑定原理.md","Vue事件绑定原理",{"type":7,"value":8,"toc":3639},"minimark",[9,13,52,56,81,85,178,181,595,598,685,688,700,703,740,991,1001,1488,1491,1535,1827,1842,2742,2745,2770,2935,2948,3311,3330,3615,3618,3626,3629,3635],[10,11,5],"h1",{"id":12},"vue事件绑定原理",[14,15,16,20,21,24,25,28,29,32,33,36,37,40,41,43,44,47,48,51],"p",{},[17,18,19],"code",{},"Vue","中通过",[17,22,23],{},"v-on","或其语法糖",[17,26,27],{},"@","指令来给元素绑定事件并且提供了事件修饰符，基本流程是进行模板编译生成",[17,30,31],{},"AST","，生成",[17,34,35],{},"render","函数后并执行得到",[17,38,39],{},"VNode","，",[17,42,39],{},"生成真实",[17,45,46],{},"DOM","节点或者组件时候使用",[17,49,50],{},"addEventListener","方法进行事件绑定。",[53,54,55],"h2",{"id":55},"描述",[14,57,58,60,61,63,64,66,67,69,70,73,74,77,78,80],{},[17,59,23],{},"与",[17,62,27],{},"用于绑定事件监听器，事件类型由参数指定，表达式可以是一个方法的名字或一个内联语句，如果没有修饰符也可以省略，用在普通元素上时，只能监听原生",[17,65,46],{},"事件，用在自定义元素组件上时，也可以监听子组件触发的自定义事件，在监听原生",[17,68,46],{},"事件时，方法以事件为唯一的参数，如果使用内联语句，语句可以访问一个",[17,71,72],{},"$event property：v-on:click=\"handle('param', $event)\"","，自",[17,75,76],{},"2.4.0","开始",[17,79,23],{},"同样支持不带参数绑定一个事件或监听器键值对的对象，注意当使用对象语法时，是不支持任何修饰器的。",[82,83,84],"h3",{"id":84},"修饰符",[86,87,88,99,108,118,124,130,136,142,148,154,160],"ul",{},[89,90,91,94,95,98],"li",{},[17,92,93],{},".stop",": 调用",[17,96,97],{},"event.stopPropagation()","，即阻止事件冒泡。",[89,100,101,94,104,107],{},[17,102,103],{},".prevent",[17,105,106],{},"event.preventDefault()","，即阻止默认事件。",[89,109,110,113,114,117],{},[17,111,112],{},".capture",": 添加事件侦听器时使用",[17,115,116],{},"capture","模式，即使用事件捕获模式处理事件。",[89,119,120,123],{},[17,121,122],{},".self",": 只当事件是从侦听器绑定的元素本身触发时才触发回调。",[89,125,126,129],{},[17,127,128],{},".{keyCode | keyAlias}",": 只当事件是从特定键触发时才触发回调。",[89,131,132,135],{},[17,133,134],{},".native",": 监听组件根元素的原生事件，即注册组件根元素的原生事件而不是组件自定义事件的。",[89,137,138,141],{},[17,139,140],{},".once",": 只触发一次回调。",[89,143,144,147],{},[17,145,146],{},".left(2.2.0)",": 只当点击鼠标左键时触发。",[89,149,150,153],{},[17,151,152],{},".right(2.2.0)",": 只当点击鼠标右键时触发。",[89,155,156,159],{},[17,157,158],{},".middle(2.2.0)",": 只当点击鼠标中键时触发。",[89,161,162,165,166,169,170,173,174,177],{},[17,163,164],{},".passive(2.3.0)",": 以",[17,167,168],{},"{ passive: true }","模式添加侦听器，表示",[17,171,172],{},"listener","永远不会调用",[17,175,176],{},"preventDefault()","。",[82,179,180],{"id":180},"普通元素",[182,183,188],"pre",{"className":184,"code":185,"language":186,"meta":187,"style":187},"language-html shiki shiki-themes github-light github-dark","\u003C!-- 方法处理器 -->\n\u003Cbutton v-on:click=\"doThis\">\u003C\u002Fbutton>\n\n\u003C!-- 动态事件 (2.6.0+) -->\n\u003Cbutton v-on:[event]=\"doThis\">\u003C\u002Fbutton>\n\n\u003C!-- 内联语句 -->\n\u003Cbutton v-on:click=\"doThat('param', $event)\">\u003C\u002Fbutton>\n\n\u003C!-- 缩写 -->\n\u003Cbutton @click=\"doThis\">\u003C\u002Fbutton>\n\n\u003C!-- 动态事件缩写 (2.6.0+) -->\n\u003Cbutton @[event]=\"doThis\">\u003C\u002Fbutton>\n\n\u003C!-- 停止冒泡 -->\n\u003Cbutton @click.stop=\"doThis\">\u003C\u002Fbutton>\n\n\u003C!-- 阻止默认行为 -->\n\u003Cbutton @click.prevent=\"doThis\">\u003C\u002Fbutton>\n\n\u003C!-- 阻止默认行为，没有表达式 -->\n\u003Cform @submit.prevent>\u003C\u002Fform>\n\n\u003C!--  串联修饰符 -->\n\u003Cbutton @click.stop.prevent=\"doThis\">\u003C\u002Fbutton>\n\n\u003C!-- 键修饰符，键别名 -->\n\u003Cinput @keyup.enter=\"onEnter\">\n\n\u003C!-- 键修饰符，键代码 -->\n\u003Cinput @keyup.13=\"onEnter\">\n\n\u003C!-- 点击回调只会触发一次 -->\n\u003Cbutton v-on:click.once=\"doThis\">\u003C\u002Fbutton>\n\n\u003C!-- 对象语法 (2.4.0+) -->\n\u003Cbutton v-on=\"{ mousedown: doThis, mouseup: doThat }\">\u003C\u002Fbutton>\n","html","",[17,189,190,199,229,236,242,262,267,273,293,298,304,324,329,335,355,360,366,386,391,397,417,422,428,445,450,456,476,481,487,505,510,516,532,537,543,563,568,574],{"__ignoreMap":187},[191,192,195],"span",{"class":193,"line":194},"line",1,[191,196,198],{"class":197},"sJ8bj","\u003C!-- 方法处理器 -->\n",[191,200,202,206,210,214,217,221,224,226],{"class":193,"line":201},2,[191,203,205],{"class":204},"sVt8B","\u003C",[191,207,209],{"class":208},"s9eBZ","button",[191,211,213],{"class":212},"sScJk"," v-on:click",[191,215,216],{"class":204},"=",[191,218,220],{"class":219},"sZZnC","\"doThis\"",[191,222,223],{"class":204},">\u003C\u002F",[191,225,209],{"class":208},[191,227,228],{"class":204},">\n",[191,230,232],{"class":193,"line":231},3,[191,233,235],{"emptyLinePlaceholder":234},true,"\n",[191,237,239],{"class":193,"line":238},4,[191,240,241],{"class":197},"\u003C!-- 动态事件 (2.6.0+) -->\n",[191,243,245,247,249,252,254,256,258,260],{"class":193,"line":244},5,[191,246,205],{"class":204},[191,248,209],{"class":208},[191,250,251],{"class":212}," v-on:[event]",[191,253,216],{"class":204},[191,255,220],{"class":219},[191,257,223],{"class":204},[191,259,209],{"class":208},[191,261,228],{"class":204},[191,263,265],{"class":193,"line":264},6,[191,266,235],{"emptyLinePlaceholder":234},[191,268,270],{"class":193,"line":269},7,[191,271,272],{"class":197},"\u003C!-- 内联语句 -->\n",[191,274,276,278,280,282,284,287,289,291],{"class":193,"line":275},8,[191,277,205],{"class":204},[191,279,209],{"class":208},[191,281,213],{"class":212},[191,283,216],{"class":204},[191,285,286],{"class":219},"\"doThat('param', $event)\"",[191,288,223],{"class":204},[191,290,209],{"class":208},[191,292,228],{"class":204},[191,294,296],{"class":193,"line":295},9,[191,297,235],{"emptyLinePlaceholder":234},[191,299,301],{"class":193,"line":300},10,[191,302,303],{"class":197},"\u003C!-- 缩写 -->\n",[191,305,307,309,311,314,316,318,320,322],{"class":193,"line":306},11,[191,308,205],{"class":204},[191,310,209],{"class":208},[191,312,313],{"class":212}," @click",[191,315,216],{"class":204},[191,317,220],{"class":219},[191,319,223],{"class":204},[191,321,209],{"class":208},[191,323,228],{"class":204},[191,325,327],{"class":193,"line":326},12,[191,328,235],{"emptyLinePlaceholder":234},[191,330,332],{"class":193,"line":331},13,[191,333,334],{"class":197},"\u003C!-- 动态事件缩写 (2.6.0+) -->\n",[191,336,338,340,342,345,347,349,351,353],{"class":193,"line":337},14,[191,339,205],{"class":204},[191,341,209],{"class":208},[191,343,344],{"class":212}," @[event]",[191,346,216],{"class":204},[191,348,220],{"class":219},[191,350,223],{"class":204},[191,352,209],{"class":208},[191,354,228],{"class":204},[191,356,358],{"class":193,"line":357},15,[191,359,235],{"emptyLinePlaceholder":234},[191,361,363],{"class":193,"line":362},16,[191,364,365],{"class":197},"\u003C!-- 停止冒泡 -->\n",[191,367,369,371,373,376,378,380,382,384],{"class":193,"line":368},17,[191,370,205],{"class":204},[191,372,209],{"class":208},[191,374,375],{"class":212}," @click.stop",[191,377,216],{"class":204},[191,379,220],{"class":219},[191,381,223],{"class":204},[191,383,209],{"class":208},[191,385,228],{"class":204},[191,387,389],{"class":193,"line":388},18,[191,390,235],{"emptyLinePlaceholder":234},[191,392,394],{"class":193,"line":393},19,[191,395,396],{"class":197},"\u003C!-- 阻止默认行为 -->\n",[191,398,400,402,404,407,409,411,413,415],{"class":193,"line":399},20,[191,401,205],{"class":204},[191,403,209],{"class":208},[191,405,406],{"class":212}," @click.prevent",[191,408,216],{"class":204},[191,410,220],{"class":219},[191,412,223],{"class":204},[191,414,209],{"class":208},[191,416,228],{"class":204},[191,418,420],{"class":193,"line":419},21,[191,421,235],{"emptyLinePlaceholder":234},[191,423,425],{"class":193,"line":424},22,[191,426,427],{"class":197},"\u003C!-- 阻止默认行为，没有表达式 -->\n",[191,429,431,433,436,439,441,443],{"class":193,"line":430},23,[191,432,205],{"class":204},[191,434,435],{"class":208},"form",[191,437,438],{"class":212}," @submit.prevent",[191,440,223],{"class":204},[191,442,435],{"class":208},[191,444,228],{"class":204},[191,446,448],{"class":193,"line":447},24,[191,449,235],{"emptyLinePlaceholder":234},[191,451,453],{"class":193,"line":452},25,[191,454,455],{"class":197},"\u003C!--  串联修饰符 -->\n",[191,457,459,461,463,466,468,470,472,474],{"class":193,"line":458},26,[191,460,205],{"class":204},[191,462,209],{"class":208},[191,464,465],{"class":212}," @click.stop.prevent",[191,467,216],{"class":204},[191,469,220],{"class":219},[191,471,223],{"class":204},[191,473,209],{"class":208},[191,475,228],{"class":204},[191,477,479],{"class":193,"line":478},27,[191,480,235],{"emptyLinePlaceholder":234},[191,482,484],{"class":193,"line":483},28,[191,485,486],{"class":197},"\u003C!-- 键修饰符，键别名 -->\n",[191,488,490,492,495,498,500,503],{"class":193,"line":489},29,[191,491,205],{"class":204},[191,493,494],{"class":208},"input",[191,496,497],{"class":212}," @keyup.enter",[191,499,216],{"class":204},[191,501,502],{"class":219},"\"onEnter\"",[191,504,228],{"class":204},[191,506,508],{"class":193,"line":507},30,[191,509,235],{"emptyLinePlaceholder":234},[191,511,513],{"class":193,"line":512},31,[191,514,515],{"class":197},"\u003C!-- 键修饰符，键代码 -->\n",[191,517,519,521,523,526,528,530],{"class":193,"line":518},32,[191,520,205],{"class":204},[191,522,494],{"class":208},[191,524,525],{"class":212}," @keyup.13",[191,527,216],{"class":204},[191,529,502],{"class":219},[191,531,228],{"class":204},[191,533,535],{"class":193,"line":534},33,[191,536,235],{"emptyLinePlaceholder":234},[191,538,540],{"class":193,"line":539},34,[191,541,542],{"class":197},"\u003C!-- 点击回调只会触发一次 -->\n",[191,544,546,548,550,553,555,557,559,561],{"class":193,"line":545},35,[191,547,205],{"class":204},[191,549,209],{"class":208},[191,551,552],{"class":212}," v-on:click.once",[191,554,216],{"class":204},[191,556,220],{"class":219},[191,558,223],{"class":204},[191,560,209],{"class":208},[191,562,228],{"class":204},[191,564,566],{"class":193,"line":565},36,[191,567,235],{"emptyLinePlaceholder":234},[191,569,571],{"class":193,"line":570},37,[191,572,573],{"class":197},"\u003C!-- 对象语法 (2.4.0+) -->\n",[191,575,577,579,581,584,586,589,591,593],{"class":193,"line":576},38,[191,578,205],{"class":204},[191,580,209],{"class":208},[191,582,583],{"class":212}," v-on",[191,585,216],{"class":204},[191,587,588],{"class":219},"\"{ mousedown: doThis, mouseup: doThat }\"",[191,590,223],{"class":204},[191,592,209],{"class":208},[191,594,228],{"class":204},[82,596,597],{"id":597},"组件元素",[182,599,601],{"className":184,"code":600,"language":186,"meta":187,"style":187},"\u003C!-- 自定义事件 -->\n\u003Cmy-component @my-event=\"handleThis\">\u003C\u002Fmy-component>\n\n\u003C!-- 内联语句 -->\n\u003Cmy-component @my-event=\"handleThis('param', $event)\">\u003C\u002Fmy-component>\n\n\u003C!-- 组件中的原生事件 -->\n\u003Cmy-component @click.native=\"onClick\">\u003C\u002Fmy-component>\n",[17,602,603,608,629,633,637,656,660,665],{"__ignoreMap":187},[191,604,605],{"class":193,"line":194},[191,606,607],{"class":197},"\u003C!-- 自定义事件 -->\n",[191,609,610,612,615,618,620,623,625,627],{"class":193,"line":201},[191,611,205],{"class":204},[191,613,614],{"class":208},"my-component",[191,616,617],{"class":212}," @my-event",[191,619,216],{"class":204},[191,621,622],{"class":219},"\"handleThis\"",[191,624,223],{"class":204},[191,626,614],{"class":208},[191,628,228],{"class":204},[191,630,631],{"class":193,"line":231},[191,632,235],{"emptyLinePlaceholder":234},[191,634,635],{"class":193,"line":238},[191,636,272],{"class":197},[191,638,639,641,643,645,647,650,652,654],{"class":193,"line":244},[191,640,205],{"class":204},[191,642,614],{"class":208},[191,644,617],{"class":212},[191,646,216],{"class":204},[191,648,649],{"class":219},"\"handleThis('param', $event)\"",[191,651,223],{"class":204},[191,653,614],{"class":208},[191,655,228],{"class":204},[191,657,658],{"class":193,"line":264},[191,659,235],{"emptyLinePlaceholder":234},[191,661,662],{"class":193,"line":269},[191,663,664],{"class":197},"\u003C!-- 组件中的原生事件 -->\n",[191,666,667,669,671,674,676,679,681,683],{"class":193,"line":275},[191,668,205],{"class":204},[191,670,614],{"class":208},[191,672,673],{"class":212}," @click.native",[191,675,216],{"class":204},[191,677,678],{"class":219},"\"onClick\"",[191,680,223],{"class":204},[191,682,614],{"class":208},[191,684,228],{"class":204},[53,686,687],{"id":687},"分析",[14,689,690,692,693,696,697,177],{},[17,691,19],{},"源码的实现比较复杂，会处理各种兼容问题与异常以及各种条件分支，文章分析比较核心的代码部分，精简过后的版本，重要部分做出注释，",[17,694,695],{},"commit id","为",[17,698,699],{},"ef56410",[82,701,702],{"id":702},"编译阶段",[14,704,705,707,708,711,712,714,715,717,718,721,722,724,725,728,729,732,733,736,737,739],{},[17,706,19],{},"在挂载实例前，有相当多的工作是进行模板的编译，将",[17,709,710],{},"template","模板进行编译，解析成",[17,713,31],{},"树，再转换成",[17,716,35],{},"函数，而在编译阶段，就是对事件的指令做收集处理。",[719,720],"br",{},"\n在",[17,723,710],{},"模板中，定义事件的部分是属于",[17,726,727],{},"XML","的",[17,730,731],{},"Attribute","，所以收集指令时需要匹配",[17,734,735],{},"Attributes","以确定哪个",[17,738,731],{},"是属于事件。",[182,741,745],{"className":742,"code":743,"language":744,"meta":187,"style":187},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fparser\u002Findex.js line 23\nexport const onRE = \u002F^@|^v-on:\u002F\nexport const dirRE = process.env.VBIND_PROP_SHORTHAND\n  ? \u002F^v-|^@|^:|^\\.|^#\u002F\n  : \u002F^v-|^@|^:|^#\u002F\n\u002F\u002F ...\nconst dynamicArgRE = \u002F^\\[.*\\]$\u002F\n\u002F\u002F ...\nexport const bindRE = \u002F^:|^\\.|^v-bind:\u002F\n  \n\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fparser\u002Findex.js line 757\nfunction processAttrs (el) {\n  const list = el.attrsList\n  let i, l, name, rawName, value, modifiers, syncGen, isDynamic\n  for (i = 0, l = list.length; i \u003C l; i++) {\n    name = rawName = list[i].name\n    value = list[i].value\n    if (dirRE.test(name)) { \u002F\u002F 匹配指令属性\n      \u002F\u002F mark element as dynamic\n      el.hasBindings = true\n      \u002F\u002F modifiers\n      modifiers = parseModifiers(name.replace(dirRE, '')) \u002F\u002F 将修饰符解析\n      \u002F\u002F support .foo shorthand syntax for the .prop modifier\n      if (process.env.VBIND_PROP_SHORTHAND && propBindRE.test(name)) {\n        (modifiers || (modifiers = {})).prop = true\n        name = `.` + name.slice(1).replace(modifierRE, '')\n      } else if (modifiers) {\n        name = name.replace(modifierRE, '')\n      }\n      if (bindRE.test(name)) { \u002F\u002F v-bind \u002F\u002F 处理v-bind的情况\n        \u002F\u002F ...\n      } else if (onRE.test(name)) { \u002F\u002F v-on \u002F\u002F 处理事件绑定\n        name = name.replace(onRE, '') \u002F\u002F 将事件名匹配\n        isDynamic = dynamicArgRE.test(name) \u002F\u002F 动态事件绑定\n        if (isDynamic) { \u002F\u002F 如果是动态事件\n          name = name.slice(1, -1) \u002F\u002F 去掉两端的 []\n        }\n        addHandler(el, name, value, modifiers, false, warn, list[i], isDynamic) \u002F\u002F 处理事件收集\n      } else { \u002F\u002F normal directives \u002F\u002F 处理其他指令\n        \u002F\u002F ...\n      }\n    } else {\n      \u002F\u002F literal attribute \u002F\u002F 处理文字属性\n      \u002F\u002F ...\n    }\n  }\n}\n","javascript",[17,746,747,752,757,762,767,772,777,782,786,791,796,801,806,811,816,821,826,831,836,841,846,851,856,861,866,871,876,881,886,891,896,901,906,911,916,921,926,931,936,942,947,952,958,967,973,979,985],{"__ignoreMap":187},[191,748,749],{"class":193,"line":194},[191,750,751],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fparser\u002Findex.js line 23\n",[191,753,754],{"class":193,"line":201},[191,755,756],{},"export const onRE = \u002F^@|^v-on:\u002F\n",[191,758,759],{"class":193,"line":231},[191,760,761],{},"export const dirRE = process.env.VBIND_PROP_SHORTHAND\n",[191,763,764],{"class":193,"line":238},[191,765,766],{},"  ? \u002F^v-|^@|^:|^\\.|^#\u002F\n",[191,768,769],{"class":193,"line":244},[191,770,771],{},"  : \u002F^v-|^@|^:|^#\u002F\n",[191,773,774],{"class":193,"line":264},[191,775,776],{},"\u002F\u002F ...\n",[191,778,779],{"class":193,"line":269},[191,780,781],{},"const dynamicArgRE = \u002F^\\[.*\\]$\u002F\n",[191,783,784],{"class":193,"line":275},[191,785,776],{},[191,787,788],{"class":193,"line":295},[191,789,790],{},"export const bindRE = \u002F^:|^\\.|^v-bind:\u002F\n",[191,792,793],{"class":193,"line":300},[191,794,795],{},"  \n",[191,797,798],{"class":193,"line":306},[191,799,800],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fparser\u002Findex.js line 757\n",[191,802,803],{"class":193,"line":326},[191,804,805],{},"function processAttrs (el) {\n",[191,807,808],{"class":193,"line":331},[191,809,810],{},"  const list = el.attrsList\n",[191,812,813],{"class":193,"line":337},[191,814,815],{},"  let i, l, name, rawName, value, modifiers, syncGen, isDynamic\n",[191,817,818],{"class":193,"line":357},[191,819,820],{},"  for (i = 0, l = list.length; i \u003C l; i++) {\n",[191,822,823],{"class":193,"line":362},[191,824,825],{},"    name = rawName = list[i].name\n",[191,827,828],{"class":193,"line":368},[191,829,830],{},"    value = list[i].value\n",[191,832,833],{"class":193,"line":388},[191,834,835],{},"    if (dirRE.test(name)) { \u002F\u002F 匹配指令属性\n",[191,837,838],{"class":193,"line":393},[191,839,840],{},"      \u002F\u002F mark element as dynamic\n",[191,842,843],{"class":193,"line":399},[191,844,845],{},"      el.hasBindings = true\n",[191,847,848],{"class":193,"line":419},[191,849,850],{},"      \u002F\u002F modifiers\n",[191,852,853],{"class":193,"line":424},[191,854,855],{},"      modifiers = parseModifiers(name.replace(dirRE, '')) \u002F\u002F 将修饰符解析\n",[191,857,858],{"class":193,"line":430},[191,859,860],{},"      \u002F\u002F support .foo shorthand syntax for the .prop modifier\n",[191,862,863],{"class":193,"line":447},[191,864,865],{},"      if (process.env.VBIND_PROP_SHORTHAND && propBindRE.test(name)) {\n",[191,867,868],{"class":193,"line":452},[191,869,870],{},"        (modifiers || (modifiers = {})).prop = true\n",[191,872,873],{"class":193,"line":458},[191,874,875],{},"        name = `.` + name.slice(1).replace(modifierRE, '')\n",[191,877,878],{"class":193,"line":478},[191,879,880],{},"      } else if (modifiers) {\n",[191,882,883],{"class":193,"line":483},[191,884,885],{},"        name = name.replace(modifierRE, '')\n",[191,887,888],{"class":193,"line":489},[191,889,890],{},"      }\n",[191,892,893],{"class":193,"line":507},[191,894,895],{},"      if (bindRE.test(name)) { \u002F\u002F v-bind \u002F\u002F 处理v-bind的情况\n",[191,897,898],{"class":193,"line":512},[191,899,900],{},"        \u002F\u002F ...\n",[191,902,903],{"class":193,"line":518},[191,904,905],{},"      } else if (onRE.test(name)) { \u002F\u002F v-on \u002F\u002F 处理事件绑定\n",[191,907,908],{"class":193,"line":534},[191,909,910],{},"        name = name.replace(onRE, '') \u002F\u002F 将事件名匹配\n",[191,912,913],{"class":193,"line":539},[191,914,915],{},"        isDynamic = dynamicArgRE.test(name) \u002F\u002F 动态事件绑定\n",[191,917,918],{"class":193,"line":545},[191,919,920],{},"        if (isDynamic) { \u002F\u002F 如果是动态事件\n",[191,922,923],{"class":193,"line":565},[191,924,925],{},"          name = name.slice(1, -1) \u002F\u002F 去掉两端的 []\n",[191,927,928],{"class":193,"line":570},[191,929,930],{},"        }\n",[191,932,933],{"class":193,"line":576},[191,934,935],{},"        addHandler(el, name, value, modifiers, false, warn, list[i], isDynamic) \u002F\u002F 处理事件收集\n",[191,937,939],{"class":193,"line":938},39,[191,940,941],{},"      } else { \u002F\u002F normal directives \u002F\u002F 处理其他指令\n",[191,943,945],{"class":193,"line":944},40,[191,946,900],{},[191,948,950],{"class":193,"line":949},41,[191,951,890],{},[191,953,955],{"class":193,"line":954},42,[191,956,957],{},"    } else {\n",[191,959,961,964],{"class":193,"line":960},43,[191,962,963],{},"      \u002F\u002F literal attribute",[191,965,966],{}," \u002F\u002F 处理文字属性\n",[191,968,970],{"class":193,"line":969},44,[191,971,972],{},"      \u002F\u002F ...\n",[191,974,976],{"class":193,"line":975},45,[191,977,978],{},"    }\n",[191,980,982],{"class":193,"line":981},46,[191,983,984],{},"  }\n",[191,986,988],{"class":193,"line":987},47,[191,989,990],{},"}\n",[14,992,993,994,997,998,1000],{},"通过",[17,995,996],{},"addHandler","方法，为",[17,999,31],{},"树添加事件相关的属性以及对事件修饰符进行处理。",[182,1002,1004],{"className":742,"code":1003,"language":744,"meta":187,"style":187},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fhelpers.js line 69\nexport function addHandler (\n  el: ASTElement,\n  name: string,\n  value: string,\n  modifiers: ?ASTModifiers,\n  important?: boolean,\n  warn?: ?Function,\n  range?: Range,\n  dynamic?: boolean\n) {\n  modifiers = modifiers || emptyObject\n  \u002F\u002F passive 和 prevent 不能同时使用，具体是由passive模式的性质决定的\n  \u002F\u002F 详细可以参阅 https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-CN\u002Fdocs\u002FWeb\u002FAPI\u002FEventTarget\u002FaddEventListener\n  \u002F\u002F warn prevent and passive modifier\n  \u002F* istanbul ignore if *\u002F\n  if (\n    process.env.NODE_ENV !== 'production' && warn &&\n    modifiers.prevent && modifiers.passive\n  ) {\n    warn(\n      'passive and prevent can\\'t be used together. ' +\n      'Passive handler can\\'t prevent default event.',\n      range\n    )\n  }\n  \u002F\u002F 标准化click.right和click.middle，因为它们实际上不会触发。\n  \u002F\u002F 从技术上讲，这是特定于浏览器的，但是至少目前来说，浏览器是唯一具有右键\u002F中间点击的目标环境。\n  \u002F\u002F normalize click.right and click.middle since they don't actually fire\n  \u002F\u002F this is technically browser-specific, but at least for now browsers are\n  \u002F\u002F the only target envs that have right\u002Fmiddle clicks.\n  if (modifiers.right) { \u002F\u002F 将鼠标右键点击标准化 右键点击默认的是 contextmenu 事件\n    if (dynamic) { \u002F\u002F 如果是动态事件\n      name = `(${name})==='click'?'contextmenu':(${name})` \u002F\u002F 动态确定事件名\n    } else if (name === 'click') { \u002F\u002F 如果不是动态事件且是鼠标右击\n      name = 'contextmenu' \u002F\u002F 则直接替换为contextmenu事件\n      delete modifiers.right \u002F\u002F 删除modifiers的right属性\n    }\n  } else if (modifiers.middle) { \u002F\u002F 同样标准化处理鼠标中键点击的事件\n    if (dynamic) { \u002F\u002F 如果是动态事件\n      name = `(${name})==='click'?'mouseup':(${name})` \u002F\u002F 动态确定事件名\n    } else if (name === 'click') { \u002F\u002F 如果不是动态事件且是鼠标中键点击\n      name = 'mouseup' \u002F\u002F 处理为mouseup事件\n    }\n  }\n  \u002F\u002F 下面是对捕获、一次触发、passive模式的modifiers处理，主要是为事件添加 !、~、& 标记\n  \u002F\u002F 这一部分标记可以在Vue官方文档中查阅 \n  \u002F\u002F https:\u002F\u002Fcn.vuejs.org\u002Fv2\u002Fguide\u002Frender-function.html#%E4%BA%8B%E4%BB%B6-amp-%E6%8C%89%E9%94%AE%E4%BF%AE%E9%A5%B0%E7%AC%A6\n  \u002F\u002F check capture modifier\n  if (modifiers.capture) {\n    delete modifiers.capture\n    name = prependModifierMarker('!', name, dynamic)\n  }\n  if (modifiers.once) {\n    delete modifiers.once\n    name = prependModifierMarker('~', name, dynamic)\n  }\n  \u002F* istanbul ignore if *\u002F\n  if (modifiers.passive) {\n    delete modifiers.passive\n    name = prependModifierMarker('&', name, dynamic)\n  }\n  \n  \u002F\u002F events 用来记录绑定的事件\n  let events\n  if (modifiers.native) { \u002F\u002F 如果是要触发根元素原生事件则直接取得nativeEvents\n    delete modifiers.native\n    events = el.nativeEvents || (el.nativeEvents = {})\n  } else { \u002F\u002F 否则取得events\n    events = el.events || (el.events = {})\n  }\n    \n  \u002F\u002F 将事件处理函数作为handler\n  const newHandler: any = rangeSetItem({ value: value.trim(), dynamic }, range)\n  if (modifiers !== emptyObject) {\n    newHandler.modifiers = modifiers\n  }\n\n \u002F\u002F 绑定的事件可以多个，回调也可以多个，最终会合并到数组中\n  const handlers = events[name]\n  \u002F* istanbul ignore if *\u002F\n  if (Array.isArray(handlers)) {\n    important ? handlers.unshift(newHandler) : handlers.push(newHandler)\n  } else if (handlers) {\n    events[name] = important ? [newHandler, handlers] : [handlers, newHandler]\n  } else {\n    events[name] = newHandler\n  }\n\n  el.plain = false\n}\n",[17,1005,1006,1011,1016,1021,1026,1031,1036,1041,1046,1051,1056,1061,1066,1071,1076,1081,1086,1091,1096,1101,1106,1111,1116,1121,1126,1131,1135,1140,1145,1150,1155,1160,1165,1170,1175,1180,1185,1190,1194,1199,1203,1208,1213,1218,1222,1226,1231,1236,1242,1248,1254,1260,1266,1271,1277,1283,1289,1294,1299,1305,1311,1317,1322,1327,1333,1339,1345,1351,1357,1363,1369,1374,1380,1386,1392,1398,1404,1409,1414,1420,1426,1431,1437,1443,1449,1455,1461,1467,1472,1477,1483],{"__ignoreMap":187},[191,1007,1008],{"class":193,"line":194},[191,1009,1010],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fhelpers.js line 69\n",[191,1012,1013],{"class":193,"line":201},[191,1014,1015],{},"export function addHandler (\n",[191,1017,1018],{"class":193,"line":231},[191,1019,1020],{},"  el: ASTElement,\n",[191,1022,1023],{"class":193,"line":238},[191,1024,1025],{},"  name: string,\n",[191,1027,1028],{"class":193,"line":244},[191,1029,1030],{},"  value: string,\n",[191,1032,1033],{"class":193,"line":264},[191,1034,1035],{},"  modifiers: ?ASTModifiers,\n",[191,1037,1038],{"class":193,"line":269},[191,1039,1040],{},"  important?: boolean,\n",[191,1042,1043],{"class":193,"line":275},[191,1044,1045],{},"  warn?: ?Function,\n",[191,1047,1048],{"class":193,"line":295},[191,1049,1050],{},"  range?: Range,\n",[191,1052,1053],{"class":193,"line":300},[191,1054,1055],{},"  dynamic?: boolean\n",[191,1057,1058],{"class":193,"line":306},[191,1059,1060],{},") {\n",[191,1062,1063],{"class":193,"line":326},[191,1064,1065],{},"  modifiers = modifiers || emptyObject\n",[191,1067,1068],{"class":193,"line":331},[191,1069,1070],{},"  \u002F\u002F passive 和 prevent 不能同时使用，具体是由passive模式的性质决定的\n",[191,1072,1073],{"class":193,"line":337},[191,1074,1075],{},"  \u002F\u002F 详细可以参阅 https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-CN\u002Fdocs\u002FWeb\u002FAPI\u002FEventTarget\u002FaddEventListener\n",[191,1077,1078],{"class":193,"line":357},[191,1079,1080],{},"  \u002F\u002F warn prevent and passive modifier\n",[191,1082,1083],{"class":193,"line":362},[191,1084,1085],{},"  \u002F* istanbul ignore if *\u002F\n",[191,1087,1088],{"class":193,"line":368},[191,1089,1090],{},"  if (\n",[191,1092,1093],{"class":193,"line":388},[191,1094,1095],{},"    process.env.NODE_ENV !== 'production' && warn &&\n",[191,1097,1098],{"class":193,"line":393},[191,1099,1100],{},"    modifiers.prevent && modifiers.passive\n",[191,1102,1103],{"class":193,"line":399},[191,1104,1105],{},"  ) {\n",[191,1107,1108],{"class":193,"line":419},[191,1109,1110],{},"    warn(\n",[191,1112,1113],{"class":193,"line":424},[191,1114,1115],{},"      'passive and prevent can\\'t be used together. ' +\n",[191,1117,1118],{"class":193,"line":430},[191,1119,1120],{},"      'Passive handler can\\'t prevent default event.',\n",[191,1122,1123],{"class":193,"line":447},[191,1124,1125],{},"      range\n",[191,1127,1128],{"class":193,"line":452},[191,1129,1130],{},"    )\n",[191,1132,1133],{"class":193,"line":458},[191,1134,984],{},[191,1136,1137],{"class":193,"line":478},[191,1138,1139],{},"  \u002F\u002F 标准化click.right和click.middle，因为它们实际上不会触发。\n",[191,1141,1142],{"class":193,"line":483},[191,1143,1144],{},"  \u002F\u002F 从技术上讲，这是特定于浏览器的，但是至少目前来说，浏览器是唯一具有右键\u002F中间点击的目标环境。\n",[191,1146,1147],{"class":193,"line":489},[191,1148,1149],{},"  \u002F\u002F normalize click.right and click.middle since they don't actually fire\n",[191,1151,1152],{"class":193,"line":507},[191,1153,1154],{},"  \u002F\u002F this is technically browser-specific, but at least for now browsers are\n",[191,1156,1157],{"class":193,"line":512},[191,1158,1159],{},"  \u002F\u002F the only target envs that have right\u002Fmiddle clicks.\n",[191,1161,1162],{"class":193,"line":518},[191,1163,1164],{},"  if (modifiers.right) { \u002F\u002F 将鼠标右键点击标准化 右键点击默认的是 contextmenu 事件\n",[191,1166,1167],{"class":193,"line":534},[191,1168,1169],{},"    if (dynamic) { \u002F\u002F 如果是动态事件\n",[191,1171,1172],{"class":193,"line":539},[191,1173,1174],{},"      name = `(${name})==='click'?'contextmenu':(${name})` \u002F\u002F 动态确定事件名\n",[191,1176,1177],{"class":193,"line":545},[191,1178,1179],{},"    } else if (name === 'click') { \u002F\u002F 如果不是动态事件且是鼠标右击\n",[191,1181,1182],{"class":193,"line":565},[191,1183,1184],{},"      name = 'contextmenu' \u002F\u002F 则直接替换为contextmenu事件\n",[191,1186,1187],{"class":193,"line":570},[191,1188,1189],{},"      delete modifiers.right \u002F\u002F 删除modifiers的right属性\n",[191,1191,1192],{"class":193,"line":576},[191,1193,978],{},[191,1195,1196],{"class":193,"line":938},[191,1197,1198],{},"  } else if (modifiers.middle) { \u002F\u002F 同样标准化处理鼠标中键点击的事件\n",[191,1200,1201],{"class":193,"line":944},[191,1202,1169],{},[191,1204,1205],{"class":193,"line":949},[191,1206,1207],{},"      name = `(${name})==='click'?'mouseup':(${name})` \u002F\u002F 动态确定事件名\n",[191,1209,1210],{"class":193,"line":954},[191,1211,1212],{},"    } else if (name === 'click') { \u002F\u002F 如果不是动态事件且是鼠标中键点击\n",[191,1214,1215],{"class":193,"line":960},[191,1216,1217],{},"      name = 'mouseup' \u002F\u002F 处理为mouseup事件\n",[191,1219,1220],{"class":193,"line":969},[191,1221,978],{},[191,1223,1224],{"class":193,"line":975},[191,1225,984],{},[191,1227,1228],{"class":193,"line":981},[191,1229,1230],{},"  \u002F\u002F 下面是对捕获、一次触发、passive模式的modifiers处理，主要是为事件添加 !、~、& 标记\n",[191,1232,1233],{"class":193,"line":987},[191,1234,1235],{},"  \u002F\u002F 这一部分标记可以在Vue官方文档中查阅 \n",[191,1237,1239],{"class":193,"line":1238},48,[191,1240,1241],{},"  \u002F\u002F https:\u002F\u002Fcn.vuejs.org\u002Fv2\u002Fguide\u002Frender-function.html#%E4%BA%8B%E4%BB%B6-amp-%E6%8C%89%E9%94%AE%E4%BF%AE%E9%A5%B0%E7%AC%A6\n",[191,1243,1245],{"class":193,"line":1244},49,[191,1246,1247],{},"  \u002F\u002F check capture modifier\n",[191,1249,1251],{"class":193,"line":1250},50,[191,1252,1253],{},"  if (modifiers.capture) {\n",[191,1255,1257],{"class":193,"line":1256},51,[191,1258,1259],{},"    delete modifiers.capture\n",[191,1261,1263],{"class":193,"line":1262},52,[191,1264,1265],{},"    name = prependModifierMarker('!', name, dynamic)\n",[191,1267,1269],{"class":193,"line":1268},53,[191,1270,984],{},[191,1272,1274],{"class":193,"line":1273},54,[191,1275,1276],{},"  if (modifiers.once) {\n",[191,1278,1280],{"class":193,"line":1279},55,[191,1281,1282],{},"    delete modifiers.once\n",[191,1284,1286],{"class":193,"line":1285},56,[191,1287,1288],{},"    name = prependModifierMarker('~', name, dynamic)\n",[191,1290,1292],{"class":193,"line":1291},57,[191,1293,984],{},[191,1295,1297],{"class":193,"line":1296},58,[191,1298,1085],{},[191,1300,1302],{"class":193,"line":1301},59,[191,1303,1304],{},"  if (modifiers.passive) {\n",[191,1306,1308],{"class":193,"line":1307},60,[191,1309,1310],{},"    delete modifiers.passive\n",[191,1312,1314],{"class":193,"line":1313},61,[191,1315,1316],{},"    name = prependModifierMarker('&', name, dynamic)\n",[191,1318,1320],{"class":193,"line":1319},62,[191,1321,984],{},[191,1323,1325],{"class":193,"line":1324},63,[191,1326,795],{},[191,1328,1330],{"class":193,"line":1329},64,[191,1331,1332],{},"  \u002F\u002F events 用来记录绑定的事件\n",[191,1334,1336],{"class":193,"line":1335},65,[191,1337,1338],{},"  let events\n",[191,1340,1342],{"class":193,"line":1341},66,[191,1343,1344],{},"  if (modifiers.native) { \u002F\u002F 如果是要触发根元素原生事件则直接取得nativeEvents\n",[191,1346,1348],{"class":193,"line":1347},67,[191,1349,1350],{},"    delete modifiers.native\n",[191,1352,1354],{"class":193,"line":1353},68,[191,1355,1356],{},"    events = el.nativeEvents || (el.nativeEvents = {})\n",[191,1358,1360],{"class":193,"line":1359},69,[191,1361,1362],{},"  } else { \u002F\u002F 否则取得events\n",[191,1364,1366],{"class":193,"line":1365},70,[191,1367,1368],{},"    events = el.events || (el.events = {})\n",[191,1370,1372],{"class":193,"line":1371},71,[191,1373,984],{},[191,1375,1377],{"class":193,"line":1376},72,[191,1378,1379],{},"    \n",[191,1381,1383],{"class":193,"line":1382},73,[191,1384,1385],{},"  \u002F\u002F 将事件处理函数作为handler\n",[191,1387,1389],{"class":193,"line":1388},74,[191,1390,1391],{},"  const newHandler: any = rangeSetItem({ value: value.trim(), dynamic }, range)\n",[191,1393,1395],{"class":193,"line":1394},75,[191,1396,1397],{},"  if (modifiers !== emptyObject) {\n",[191,1399,1401],{"class":193,"line":1400},76,[191,1402,1403],{},"    newHandler.modifiers = modifiers\n",[191,1405,1407],{"class":193,"line":1406},77,[191,1408,984],{},[191,1410,1412],{"class":193,"line":1411},78,[191,1413,235],{"emptyLinePlaceholder":234},[191,1415,1417],{"class":193,"line":1416},79,[191,1418,1419],{}," \u002F\u002F 绑定的事件可以多个，回调也可以多个，最终会合并到数组中\n",[191,1421,1423],{"class":193,"line":1422},80,[191,1424,1425],{},"  const handlers = events[name]\n",[191,1427,1429],{"class":193,"line":1428},81,[191,1430,1085],{},[191,1432,1434],{"class":193,"line":1433},82,[191,1435,1436],{},"  if (Array.isArray(handlers)) {\n",[191,1438,1440],{"class":193,"line":1439},83,[191,1441,1442],{},"    important ? handlers.unshift(newHandler) : handlers.push(newHandler)\n",[191,1444,1446],{"class":193,"line":1445},84,[191,1447,1448],{},"  } else if (handlers) {\n",[191,1450,1452],{"class":193,"line":1451},85,[191,1453,1454],{},"    events[name] = important ? [newHandler, handlers] : [handlers, newHandler]\n",[191,1456,1458],{"class":193,"line":1457},86,[191,1459,1460],{},"  } else {\n",[191,1462,1464],{"class":193,"line":1463},87,[191,1465,1466],{},"    events[name] = newHandler\n",[191,1468,1470],{"class":193,"line":1469},88,[191,1471,984],{},[191,1473,1475],{"class":193,"line":1474},89,[191,1476,235],{"emptyLinePlaceholder":234},[191,1478,1480],{"class":193,"line":1479},90,[191,1481,1482],{},"  el.plain = false\n",[191,1484,1486],{"class":193,"line":1485},91,[191,1487,990],{},[82,1489,1490],{"id":1490},"代码生成",[14,1492,1493,1494,1496,1497,1499,1500,1503,1504,1506,1507,1509,1510,1513,1514,1516,1517,1520,1521,1523,1524,1527,1528,1530,1531,1534],{},"接下来需要将",[17,1495,31],{},"语法树转",[17,1498,35],{},"函数，在这个过程中会加入对事件的处理，首先模块导出了",[17,1501,1502],{},"generate","函数，",[17,1505,1502],{},"函数即会返回",[17,1508,35],{},"字符串，在这之前会调用",[17,1511,1512],{},"genElement","函数，而在上述",[17,1515,996],{},"方法处理的最后执行了",[17,1518,1519],{},"el.plain = false","，这样在",[17,1522,1512],{},"函数中会调用",[17,1525,1526],{},"genData","函数，而在",[17,1529,1526],{},"函数中即会调用",[17,1532,1533],{},"genHandlers","函数。",[182,1536,1538],{"className":742,"code":1537,"language":744,"meta":187,"style":187},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Findex.js line 42\nexport function generate (\n  ast: ASTElement | void,\n  options: CompilerOptions\n): CodegenResult {\n  const state = new CodegenState(options)\n  const code = ast ? genElement(ast, state) : '_c(\"div\")'\n  return {\n    render: `with(this){return ${code}}`, \u002F\u002F 即render字符串\n    staticRenderFns: state.staticRenderFns\n  }\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Findex.js line 55\nexport function genElement (el: ASTElement, state: CodegenState): string {\n    \u002F\u002F ...\n    let code\n    if (el.component) {\n      code = genComponent(el.component, el, state)\n    } else {\n      let data\n      if (!el.plain || (el.pre && state.maybeComponent(el))) {\n        data = genData(el, state)\n      }\n\n      const children = el.inlineTemplate ? null : genChildren(el, state, true)\n      code = `_c('${el.tag}'${\n        data ? `,${data}` : '' \u002F\u002F data\n      }${\n        children ? `,${children}` : '' \u002F\u002F children\n      })`\n    }\n    \u002F\u002F ...\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Findex.js line 219\nexport function genData (el: ASTElement, state: CodegenState): string {\n  let data = '{'\n  \u002F\u002F ...\n  \u002F\u002F event handlers\n  if (el.events) {\n    data += `${genHandlers(el.events, false)},`\n  }\n  if (el.nativeEvents) {\n    data += `${genHandlers(el.nativeEvents, true)},`\n  }\n  \u002F\u002F ...\n  data = data.replace(\u002F,$\u002F, '') + '}'\n  \u002F\u002F ...\n  return data\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fto-function.js line 12 \nfunction createFunction (code, errors) {\n  try {\n    return new Function(code) \u002F\u002F 将render字符串转为render函数\n  } catch (err) {\n    errors.push({ err, code })\n    return noop\n  }\n}\n",[17,1539,1540,1545,1550,1555,1560,1565,1570,1575,1580,1585,1590,1594,1598,1602,1607,1612,1617,1622,1627,1632,1636,1641,1646,1651,1655,1659,1664,1669,1674,1679,1684,1689,1693,1697,1701,1705,1710,1715,1720,1725,1730,1735,1740,1744,1749,1754,1758,1762,1767,1771,1776,1780,1784,1789,1794,1799,1804,1809,1814,1819,1823],{"__ignoreMap":187},[191,1541,1542],{"class":193,"line":194},[191,1543,1544],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Findex.js line 42\n",[191,1546,1547],{"class":193,"line":201},[191,1548,1549],{},"export function generate (\n",[191,1551,1552],{"class":193,"line":231},[191,1553,1554],{},"  ast: ASTElement | void,\n",[191,1556,1557],{"class":193,"line":238},[191,1558,1559],{},"  options: CompilerOptions\n",[191,1561,1562],{"class":193,"line":244},[191,1563,1564],{},"): CodegenResult {\n",[191,1566,1567],{"class":193,"line":264},[191,1568,1569],{},"  const state = new CodegenState(options)\n",[191,1571,1572],{"class":193,"line":269},[191,1573,1574],{},"  const code = ast ? genElement(ast, state) : '_c(\"div\")'\n",[191,1576,1577],{"class":193,"line":275},[191,1578,1579],{},"  return {\n",[191,1581,1582],{"class":193,"line":295},[191,1583,1584],{},"    render: `with(this){return ${code}}`, \u002F\u002F 即render字符串\n",[191,1586,1587],{"class":193,"line":300},[191,1588,1589],{},"    staticRenderFns: state.staticRenderFns\n",[191,1591,1592],{"class":193,"line":306},[191,1593,984],{},[191,1595,1596],{"class":193,"line":326},[191,1597,990],{},[191,1599,1600],{"class":193,"line":331},[191,1601,235],{"emptyLinePlaceholder":234},[191,1603,1604],{"class":193,"line":337},[191,1605,1606],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Findex.js line 55\n",[191,1608,1609],{"class":193,"line":357},[191,1610,1611],{},"export function genElement (el: ASTElement, state: CodegenState): string {\n",[191,1613,1614],{"class":193,"line":362},[191,1615,1616],{},"    \u002F\u002F ...\n",[191,1618,1619],{"class":193,"line":368},[191,1620,1621],{},"    let code\n",[191,1623,1624],{"class":193,"line":388},[191,1625,1626],{},"    if (el.component) {\n",[191,1628,1629],{"class":193,"line":393},[191,1630,1631],{},"      code = genComponent(el.component, el, state)\n",[191,1633,1634],{"class":193,"line":399},[191,1635,957],{},[191,1637,1638],{"class":193,"line":419},[191,1639,1640],{},"      let data\n",[191,1642,1643],{"class":193,"line":424},[191,1644,1645],{},"      if (!el.plain || (el.pre && state.maybeComponent(el))) {\n",[191,1647,1648],{"class":193,"line":430},[191,1649,1650],{},"        data = genData(el, state)\n",[191,1652,1653],{"class":193,"line":447},[191,1654,890],{},[191,1656,1657],{"class":193,"line":452},[191,1658,235],{"emptyLinePlaceholder":234},[191,1660,1661],{"class":193,"line":458},[191,1662,1663],{},"      const children = el.inlineTemplate ? null : genChildren(el, state, true)\n",[191,1665,1666],{"class":193,"line":478},[191,1667,1668],{},"      code = `_c('${el.tag}'${\n",[191,1670,1671],{"class":193,"line":483},[191,1672,1673],{},"        data ? `,${data}` : '' \u002F\u002F data\n",[191,1675,1676],{"class":193,"line":489},[191,1677,1678],{},"      }${\n",[191,1680,1681],{"class":193,"line":507},[191,1682,1683],{},"        children ? `,${children}` : '' \u002F\u002F children\n",[191,1685,1686],{"class":193,"line":512},[191,1687,1688],{},"      })`\n",[191,1690,1691],{"class":193,"line":518},[191,1692,978],{},[191,1694,1695],{"class":193,"line":534},[191,1696,1616],{},[191,1698,1699],{"class":193,"line":539},[191,1700,990],{},[191,1702,1703],{"class":193,"line":545},[191,1704,235],{"emptyLinePlaceholder":234},[191,1706,1707],{"class":193,"line":565},[191,1708,1709],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Findex.js line 219\n",[191,1711,1712],{"class":193,"line":570},[191,1713,1714],{},"export function genData (el: ASTElement, state: CodegenState): string {\n",[191,1716,1717],{"class":193,"line":576},[191,1718,1719],{},"  let data = '{'\n",[191,1721,1722],{"class":193,"line":938},[191,1723,1724],{},"  \u002F\u002F ...\n",[191,1726,1727],{"class":193,"line":944},[191,1728,1729],{},"  \u002F\u002F event handlers\n",[191,1731,1732],{"class":193,"line":949},[191,1733,1734],{},"  if (el.events) {\n",[191,1736,1737],{"class":193,"line":954},[191,1738,1739],{},"    data += `${genHandlers(el.events, false)},`\n",[191,1741,1742],{"class":193,"line":960},[191,1743,984],{},[191,1745,1746],{"class":193,"line":969},[191,1747,1748],{},"  if (el.nativeEvents) {\n",[191,1750,1751],{"class":193,"line":975},[191,1752,1753],{},"    data += `${genHandlers(el.nativeEvents, true)},`\n",[191,1755,1756],{"class":193,"line":981},[191,1757,984],{},[191,1759,1760],{"class":193,"line":987},[191,1761,1724],{},[191,1763,1764],{"class":193,"line":1238},[191,1765,1766],{},"  data = data.replace(\u002F,$\u002F, '') + '}'\n",[191,1768,1769],{"class":193,"line":1244},[191,1770,1724],{},[191,1772,1773],{"class":193,"line":1250},[191,1774,1775],{},"  return data\n",[191,1777,1778],{"class":193,"line":1256},[191,1779,990],{},[191,1781,1782],{"class":193,"line":1262},[191,1783,235],{"emptyLinePlaceholder":234},[191,1785,1786],{"class":193,"line":1268},[191,1787,1788],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fto-function.js line 12 \n",[191,1790,1791],{"class":193,"line":1273},[191,1792,1793],{},"function createFunction (code, errors) {\n",[191,1795,1796],{"class":193,"line":1279},[191,1797,1798],{},"  try {\n",[191,1800,1801],{"class":193,"line":1285},[191,1802,1803],{},"    return new Function(code) \u002F\u002F 将render字符串转为render函数\n",[191,1805,1806],{"class":193,"line":1291},[191,1807,1808],{},"  } catch (err) {\n",[191,1810,1811],{"class":193,"line":1296},[191,1812,1813],{},"    errors.push({ err, code })\n",[191,1815,1816],{"class":193,"line":1301},[191,1817,1818],{},"    return noop\n",[191,1820,1821],{"class":193,"line":1307},[191,1822,984],{},[191,1824,1825],{"class":193,"line":1313},[191,1826,990],{},[14,1828,1829,1830,1503,1832,1834,1835,1837,1838,1841],{},"可以看到无论是处理普通元素事件还是组件根元素原生事件都会调用",[17,1831,1533],{},[17,1833,1533],{},"函数即会遍历解析好的",[17,1836,31],{},"树中事件属性，拿到",[17,1839,1840],{},"event","对象属性，并根据属性上的事件对象拼接成字符串。",[182,1843,1845],{"className":742,"code":1844,"language":744,"meta":187,"style":187},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Fevents.js line 3\nconst fnExpRE = \u002F^([\\w$_]+|\\([^)]*?\\))\\s*=>|^function(?:\\s+[\\w$]+)?\\s*\\(\u002F\nconst fnInvokeRE = \u002F\\([^)]*?\\);*$\u002F\nconst simplePathRE = \u002F^[A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*|\\['[^']*?']|\\[\"[^\"]*?\"]|\\[\\d+]|\\[[A-Za-z_$][\\w$]*])*$\u002F\n\n\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Fevents.js line 7\n\u002F\u002F KeyboardEvent.keyCode aliases\nconst keyCodes: { [key: string]: number | Array\u003Cnumber> } = {\n  esc: 27,\n  tab: 9,\n  enter: 13,\n  space: 32,\n  up: 38,\n  left: 37,\n  right: 39,\n  down: 40,\n  'delete': [8, 46]\n}\n\u002F\u002F KeyboardEvent.key aliases\nconst keyNames: { [key: string]: string | Array\u003Cstring> } = {\n  \u002F\u002F #7880: IE11 and Edge use `Esc` for Escape key name.\n  esc: ['Esc', 'Escape'],\n  tab: 'Tab',\n  enter: 'Enter',\n  \u002F\u002F #9112: IE11 uses `Spacebar` for Space key name.\n  space: [' ', 'Spacebar'],\n  \u002F\u002F #7806: IE11 uses key names without `Arrow` prefix for arrow keys.\n  up: ['Up', 'ArrowUp'],\n  left: ['Left', 'ArrowLeft'],\n  right: ['Right', 'ArrowRight'],\n  down: ['Down', 'ArrowDown'],\n  \u002F\u002F #9112: IE11 uses `Del` for Delete key name.\n  'delete': ['Backspace', 'Delete', 'Del']\n}\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Fevents.js line 37\n\u002F\u002F #4868: modifiers that prevent the execution of the listener\n\u002F\u002F need to explicitly return null so that we can determine whether to remove\n\u002F\u002F the listener for .once\nconst genGuard = condition => `if(${condition})return null;`\nconst modifierCode: { [key: string]: string } = {\n  stop: '$event.stopPropagation();',\n  prevent: '$event.preventDefault();',\n  self: genGuard(`$event.target !== $event.currentTarget`),\n  ctrl: genGuard(`!$event.ctrlKey`),\n  shift: genGuard(`!$event.shiftKey`),\n  alt: genGuard(`!$event.altKey`),\n  meta: genGuard(`!$event.metaKey`),\n  left: genGuard(`'button' in $event && $event.button !== 0`),\n  middle: genGuard(`'button' in $event && $event.button !== 1`),\n  right: genGuard(`'button' in $event && $event.button !== 2`)\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Fevents.js line 55\nexport function genHandlers (\n  events: ASTElementHandlers,\n  isNative: boolean\n): string {\n  const prefix = isNative ? 'nativeOn:' : 'on:'\n  let staticHandlers = ``\n  let dynamicHandlers = ``\n  for (const name in events) { \u002F\u002F 遍历AST解析后的事件属性\n    const handlerCode = genHandler(events[name]) \u002F\u002F 将事件对象转换成可拼接的字符串\n    if (events[name] && events[name].dynamic) {\n      dynamicHandlers += `${name},${handlerCode},`\n    } else {\n      staticHandlers += `\"${name}\":${handlerCode},`\n    }\n  }\n  staticHandlers = `{${staticHandlers.slice(0, -1)}}`\n  if (dynamicHandlers) {\n    return prefix + `_d(${staticHandlers},[${dynamicHandlers.slice(0, -1)}])`\n  } else {\n    return prefix + staticHandlers\n  }\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Fevents.js line 96\nfunction genHandler (handler: ASTElementHandler | Array\u003CASTElementHandler>): string {\n  if (!handler) {\n    return 'function(){}'\n  }\n\n  \u002F\u002F 事件绑定可以多个，多个在解析AST树时会以数组的形式存在，如果有多个则会递归调用getHandler方法返回数组。\n  if (Array.isArray(handler)) {\n    return `[${handler.map(handler => genHandler(handler)).join(',')}]`\n  }\n\n  const isMethodPath = simplePathRE.test(handler.value) \u002F\u002F 调用方法为 doThis 型\n  const isFunctionExpression = fnExpRE.test(handler.value) \u002F\u002F 调用方法为 () => {} or function() {} 型\n  const isFunctionInvocation = simplePathRE.test(handler.value.replace(fnInvokeRE, '')) \u002F\u002F 调用方法为 doThis($event) 型\n\n  if (!handler.modifiers) { \u002F\u002F 没有修饰符\n    if (isMethodPath || isFunctionExpression) { \u002F\u002F 符合这两个条件则直接返回\n      return handler.value\n    }\n    \u002F* istanbul ignore if *\u002F\n    if (__WEEX__ && handler.params) {\n      return genWeexHandler(handler.params, handler.value)\n    }\n    return `function($event){${ \u002F\u002F 返回拼接的匿名函数的字符串\n      isFunctionInvocation ? `return ${handler.value}` : handler.value\n    }}` \u002F\u002F inline statement\n  } else { \u002F\u002F 处理具有修饰符的情况\n    let code = ''\n    let genModifierCode = ''\n    const keys = []\n    for (const key in handler.modifiers) {  \u002F\u002F 遍历modifiers上记录的修饰符\n      if (modifierCode[key]) {\n        genModifierCode += modifierCode[key]  \u002F\u002F 根据修饰符添加对应js的代码\n        \u002F\u002F left\u002Fright\n        if (keyCodes[key]) {\n          keys.push(key)\n        }\n      } else if (key === 'exact') { \u002F\u002F 针对exact的处理\n        const modifiers: ASTModifiers = (handler.modifiers: any)\n        genModifierCode += genGuard(\n          ['ctrl', 'shift', 'alt', 'meta']\n            .filter(keyModifier => !modifiers[keyModifier])\n            .map(keyModifier => `$event.${keyModifier}Key`)\n            .join('||')\n        )\n      } else {\n        keys.push(key) \u002F\u002F 如果修饰符不是以上修饰符，则会添加到keys数组中\n      }\n    }\n    if (keys.length) {\n      code += genKeyFilter(keys) \u002F\u002F 处理其他修饰符 即keyCodes中定义的修饰符\n    }\n    \u002F\u002F Make sure modifiers like prevent and stop get executed after key filtering\n    if (genModifierCode) {\n      code += genModifierCode\n    }\n    \u002F\u002F 根据三种不同的书写模板返回不同的字符串\n    const handlerCode = isMethodPath\n      ? `return ${handler.value}($event)`\n      : isFunctionExpression\n        ? `return (${handler.value})($event)`\n        : isFunctionInvocation\n          ? `return ${handler.value}`\n          : handler.value\n    \u002F* istanbul ignore if *\u002F\n    if (__WEEX__ && handler.params) {\n      return genWeexHandler(handler.params, code + handlerCode)\n    }\n    return `function($event){${code}${handlerCode}}`\n  }\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Fevents.js line 175\nfunction genFilterCode (key: string): string {\n  const keyVal = parseInt(key, 10)\n  if (keyVal) { \u002F\u002F 如果key是数字，则直接返回$event.keyCode!==${keyVal}\n    return `$event.keyCode!==${keyVal}`\n  }\n  const keyCode = keyCodes[key]\n  const keyName = keyNames[key]\n  \u002F\u002F 返回_k函数，它的第一个参数是$event.keyCode，\n  \u002F\u002F 第二个参数是key的值，\n  \u002F\u002F 第三个参数就是key在keyCodes中对应的数字。\n  return (\n    `_k($event.keyCode,` +\n    `${JSON.stringify(key)},` +\n    `${JSON.stringify(keyCode)},` +\n    `$event.key,` +\n    `${JSON.stringify(keyName)}` +\n    `)`\n  )\n}\n",[17,1846,1847,1852,1857,1862,1867,1871,1876,1881,1886,1891,1896,1901,1906,1911,1916,1921,1926,1931,1935,1940,1945,1950,1955,1960,1965,1970,1975,1980,1985,1990,1995,2000,2005,2010,2014,2018,2022,2027,2032,2037,2042,2047,2052,2057,2062,2067,2072,2077,2082,2087,2092,2097,2102,2106,2110,2115,2120,2125,2130,2135,2140,2145,2150,2155,2160,2165,2170,2174,2179,2183,2187,2192,2197,2202,2206,2211,2215,2219,2223,2228,2233,2238,2243,2247,2251,2256,2261,2266,2270,2274,2279,2284,2290,2295,2301,2307,2313,2318,2324,2330,2336,2341,2347,2353,2359,2365,2371,2377,2383,2389,2395,2401,2407,2413,2419,2424,2430,2436,2442,2448,2454,2460,2466,2472,2478,2484,2489,2494,2500,2506,2511,2517,2523,2529,2534,2540,2546,2552,2558,2564,2570,2576,2582,2587,2592,2598,2603,2609,2614,2619,2624,2630,2636,2642,2648,2654,2659,2665,2671,2677,2683,2689,2695,2701,2707,2713,2719,2725,2731,2737],{"__ignoreMap":187},[191,1848,1849],{"class":193,"line":194},[191,1850,1851],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Fevents.js line 3\n",[191,1853,1854],{"class":193,"line":201},[191,1855,1856],{},"const fnExpRE = \u002F^([\\w$_]+|\\([^)]*?\\))\\s*=>|^function(?:\\s+[\\w$]+)?\\s*\\(\u002F\n",[191,1858,1859],{"class":193,"line":231},[191,1860,1861],{},"const fnInvokeRE = \u002F\\([^)]*?\\);*$\u002F\n",[191,1863,1864],{"class":193,"line":238},[191,1865,1866],{},"const simplePathRE = \u002F^[A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*|\\['[^']*?']|\\[\"[^\"]*?\"]|\\[\\d+]|\\[[A-Za-z_$][\\w$]*])*$\u002F\n",[191,1868,1869],{"class":193,"line":244},[191,1870,235],{"emptyLinePlaceholder":234},[191,1872,1873],{"class":193,"line":264},[191,1874,1875],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Fevents.js line 7\n",[191,1877,1878],{"class":193,"line":269},[191,1879,1880],{},"\u002F\u002F KeyboardEvent.keyCode aliases\n",[191,1882,1883],{"class":193,"line":275},[191,1884,1885],{},"const keyCodes: { [key: string]: number | Array\u003Cnumber> } = {\n",[191,1887,1888],{"class":193,"line":295},[191,1889,1890],{},"  esc: 27,\n",[191,1892,1893],{"class":193,"line":300},[191,1894,1895],{},"  tab: 9,\n",[191,1897,1898],{"class":193,"line":306},[191,1899,1900],{},"  enter: 13,\n",[191,1902,1903],{"class":193,"line":326},[191,1904,1905],{},"  space: 32,\n",[191,1907,1908],{"class":193,"line":331},[191,1909,1910],{},"  up: 38,\n",[191,1912,1913],{"class":193,"line":337},[191,1914,1915],{},"  left: 37,\n",[191,1917,1918],{"class":193,"line":357},[191,1919,1920],{},"  right: 39,\n",[191,1922,1923],{"class":193,"line":362},[191,1924,1925],{},"  down: 40,\n",[191,1927,1928],{"class":193,"line":368},[191,1929,1930],{},"  'delete': [8, 46]\n",[191,1932,1933],{"class":193,"line":388},[191,1934,990],{},[191,1936,1937],{"class":193,"line":393},[191,1938,1939],{},"\u002F\u002F KeyboardEvent.key aliases\n",[191,1941,1942],{"class":193,"line":399},[191,1943,1944],{},"const keyNames: { [key: string]: string | Array\u003Cstring> } = {\n",[191,1946,1947],{"class":193,"line":419},[191,1948,1949],{},"  \u002F\u002F #7880: IE11 and Edge use `Esc` for Escape key name.\n",[191,1951,1952],{"class":193,"line":424},[191,1953,1954],{},"  esc: ['Esc', 'Escape'],\n",[191,1956,1957],{"class":193,"line":430},[191,1958,1959],{},"  tab: 'Tab',\n",[191,1961,1962],{"class":193,"line":447},[191,1963,1964],{},"  enter: 'Enter',\n",[191,1966,1967],{"class":193,"line":452},[191,1968,1969],{},"  \u002F\u002F #9112: IE11 uses `Spacebar` for Space key name.\n",[191,1971,1972],{"class":193,"line":458},[191,1973,1974],{},"  space: [' ', 'Spacebar'],\n",[191,1976,1977],{"class":193,"line":478},[191,1978,1979],{},"  \u002F\u002F #7806: IE11 uses key names without `Arrow` prefix for arrow keys.\n",[191,1981,1982],{"class":193,"line":483},[191,1983,1984],{},"  up: ['Up', 'ArrowUp'],\n",[191,1986,1987],{"class":193,"line":489},[191,1988,1989],{},"  left: ['Left', 'ArrowLeft'],\n",[191,1991,1992],{"class":193,"line":507},[191,1993,1994],{},"  right: ['Right', 'ArrowRight'],\n",[191,1996,1997],{"class":193,"line":512},[191,1998,1999],{},"  down: ['Down', 'ArrowDown'],\n",[191,2001,2002],{"class":193,"line":518},[191,2003,2004],{},"  \u002F\u002F #9112: IE11 uses `Del` for Delete key name.\n",[191,2006,2007],{"class":193,"line":534},[191,2008,2009],{},"  'delete': ['Backspace', 'Delete', 'Del']\n",[191,2011,2012],{"class":193,"line":539},[191,2013,990],{},[191,2015,2016],{"class":193,"line":545},[191,2017,990],{},[191,2019,2020],{"class":193,"line":565},[191,2021,235],{"emptyLinePlaceholder":234},[191,2023,2024],{"class":193,"line":570},[191,2025,2026],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Fevents.js line 37\n",[191,2028,2029],{"class":193,"line":576},[191,2030,2031],{},"\u002F\u002F #4868: modifiers that prevent the execution of the listener\n",[191,2033,2034],{"class":193,"line":938},[191,2035,2036],{},"\u002F\u002F need to explicitly return null so that we can determine whether to remove\n",[191,2038,2039],{"class":193,"line":944},[191,2040,2041],{},"\u002F\u002F the listener for .once\n",[191,2043,2044],{"class":193,"line":949},[191,2045,2046],{},"const genGuard = condition => `if(${condition})return null;`\n",[191,2048,2049],{"class":193,"line":954},[191,2050,2051],{},"const modifierCode: { [key: string]: string } = {\n",[191,2053,2054],{"class":193,"line":960},[191,2055,2056],{},"  stop: '$event.stopPropagation();',\n",[191,2058,2059],{"class":193,"line":969},[191,2060,2061],{},"  prevent: '$event.preventDefault();',\n",[191,2063,2064],{"class":193,"line":975},[191,2065,2066],{},"  self: genGuard(`$event.target !== $event.currentTarget`),\n",[191,2068,2069],{"class":193,"line":981},[191,2070,2071],{},"  ctrl: genGuard(`!$event.ctrlKey`),\n",[191,2073,2074],{"class":193,"line":987},[191,2075,2076],{},"  shift: genGuard(`!$event.shiftKey`),\n",[191,2078,2079],{"class":193,"line":1238},[191,2080,2081],{},"  alt: genGuard(`!$event.altKey`),\n",[191,2083,2084],{"class":193,"line":1244},[191,2085,2086],{},"  meta: genGuard(`!$event.metaKey`),\n",[191,2088,2089],{"class":193,"line":1250},[191,2090,2091],{},"  left: genGuard(`'button' in $event && $event.button !== 0`),\n",[191,2093,2094],{"class":193,"line":1256},[191,2095,2096],{},"  middle: genGuard(`'button' in $event && $event.button !== 1`),\n",[191,2098,2099],{"class":193,"line":1262},[191,2100,2101],{},"  right: genGuard(`'button' in $event && $event.button !== 2`)\n",[191,2103,2104],{"class":193,"line":1268},[191,2105,990],{},[191,2107,2108],{"class":193,"line":1273},[191,2109,235],{"emptyLinePlaceholder":234},[191,2111,2112],{"class":193,"line":1279},[191,2113,2114],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Fevents.js line 55\n",[191,2116,2117],{"class":193,"line":1285},[191,2118,2119],{},"export function genHandlers (\n",[191,2121,2122],{"class":193,"line":1291},[191,2123,2124],{},"  events: ASTElementHandlers,\n",[191,2126,2127],{"class":193,"line":1296},[191,2128,2129],{},"  isNative: boolean\n",[191,2131,2132],{"class":193,"line":1301},[191,2133,2134],{},"): string {\n",[191,2136,2137],{"class":193,"line":1307},[191,2138,2139],{},"  const prefix = isNative ? 'nativeOn:' : 'on:'\n",[191,2141,2142],{"class":193,"line":1313},[191,2143,2144],{},"  let staticHandlers = ``\n",[191,2146,2147],{"class":193,"line":1319},[191,2148,2149],{},"  let dynamicHandlers = ``\n",[191,2151,2152],{"class":193,"line":1324},[191,2153,2154],{},"  for (const name in events) { \u002F\u002F 遍历AST解析后的事件属性\n",[191,2156,2157],{"class":193,"line":1329},[191,2158,2159],{},"    const handlerCode = genHandler(events[name]) \u002F\u002F 将事件对象转换成可拼接的字符串\n",[191,2161,2162],{"class":193,"line":1335},[191,2163,2164],{},"    if (events[name] && events[name].dynamic) {\n",[191,2166,2167],{"class":193,"line":1341},[191,2168,2169],{},"      dynamicHandlers += `${name},${handlerCode},`\n",[191,2171,2172],{"class":193,"line":1347},[191,2173,957],{},[191,2175,2176],{"class":193,"line":1353},[191,2177,2178],{},"      staticHandlers += `\"${name}\":${handlerCode},`\n",[191,2180,2181],{"class":193,"line":1359},[191,2182,978],{},[191,2184,2185],{"class":193,"line":1365},[191,2186,984],{},[191,2188,2189],{"class":193,"line":1371},[191,2190,2191],{},"  staticHandlers = `{${staticHandlers.slice(0, -1)}}`\n",[191,2193,2194],{"class":193,"line":1376},[191,2195,2196],{},"  if (dynamicHandlers) {\n",[191,2198,2199],{"class":193,"line":1382},[191,2200,2201],{},"    return prefix + `_d(${staticHandlers},[${dynamicHandlers.slice(0, -1)}])`\n",[191,2203,2204],{"class":193,"line":1388},[191,2205,1460],{},[191,2207,2208],{"class":193,"line":1394},[191,2209,2210],{},"    return prefix + staticHandlers\n",[191,2212,2213],{"class":193,"line":1400},[191,2214,984],{},[191,2216,2217],{"class":193,"line":1406},[191,2218,990],{},[191,2220,2221],{"class":193,"line":1411},[191,2222,235],{"emptyLinePlaceholder":234},[191,2224,2225],{"class":193,"line":1416},[191,2226,2227],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Fevents.js line 96\n",[191,2229,2230],{"class":193,"line":1422},[191,2231,2232],{},"function genHandler (handler: ASTElementHandler | Array\u003CASTElementHandler>): string {\n",[191,2234,2235],{"class":193,"line":1428},[191,2236,2237],{},"  if (!handler) {\n",[191,2239,2240],{"class":193,"line":1433},[191,2241,2242],{},"    return 'function(){}'\n",[191,2244,2245],{"class":193,"line":1439},[191,2246,984],{},[191,2248,2249],{"class":193,"line":1445},[191,2250,235],{"emptyLinePlaceholder":234},[191,2252,2253],{"class":193,"line":1451},[191,2254,2255],{},"  \u002F\u002F 事件绑定可以多个，多个在解析AST树时会以数组的形式存在，如果有多个则会递归调用getHandler方法返回数组。\n",[191,2257,2258],{"class":193,"line":1457},[191,2259,2260],{},"  if (Array.isArray(handler)) {\n",[191,2262,2263],{"class":193,"line":1463},[191,2264,2265],{},"    return `[${handler.map(handler => genHandler(handler)).join(',')}]`\n",[191,2267,2268],{"class":193,"line":1469},[191,2269,984],{},[191,2271,2272],{"class":193,"line":1474},[191,2273,235],{"emptyLinePlaceholder":234},[191,2275,2276],{"class":193,"line":1479},[191,2277,2278],{},"  const isMethodPath = simplePathRE.test(handler.value) \u002F\u002F 调用方法为 doThis 型\n",[191,2280,2281],{"class":193,"line":1485},[191,2282,2283],{},"  const isFunctionExpression = fnExpRE.test(handler.value) \u002F\u002F 调用方法为 () => {} or function() {} 型\n",[191,2285,2287],{"class":193,"line":2286},92,[191,2288,2289],{},"  const isFunctionInvocation = simplePathRE.test(handler.value.replace(fnInvokeRE, '')) \u002F\u002F 调用方法为 doThis($event) 型\n",[191,2291,2293],{"class":193,"line":2292},93,[191,2294,235],{"emptyLinePlaceholder":234},[191,2296,2298],{"class":193,"line":2297},94,[191,2299,2300],{},"  if (!handler.modifiers) { \u002F\u002F 没有修饰符\n",[191,2302,2304],{"class":193,"line":2303},95,[191,2305,2306],{},"    if (isMethodPath || isFunctionExpression) { \u002F\u002F 符合这两个条件则直接返回\n",[191,2308,2310],{"class":193,"line":2309},96,[191,2311,2312],{},"      return handler.value\n",[191,2314,2316],{"class":193,"line":2315},97,[191,2317,978],{},[191,2319,2321],{"class":193,"line":2320},98,[191,2322,2323],{},"    \u002F* istanbul ignore if *\u002F\n",[191,2325,2327],{"class":193,"line":2326},99,[191,2328,2329],{},"    if (__WEEX__ && handler.params) {\n",[191,2331,2333],{"class":193,"line":2332},100,[191,2334,2335],{},"      return genWeexHandler(handler.params, handler.value)\n",[191,2337,2339],{"class":193,"line":2338},101,[191,2340,978],{},[191,2342,2344],{"class":193,"line":2343},102,[191,2345,2346],{},"    return `function($event){${ \u002F\u002F 返回拼接的匿名函数的字符串\n",[191,2348,2350],{"class":193,"line":2349},103,[191,2351,2352],{},"      isFunctionInvocation ? `return ${handler.value}` : handler.value\n",[191,2354,2356],{"class":193,"line":2355},104,[191,2357,2358],{},"    }}` \u002F\u002F inline statement\n",[191,2360,2362],{"class":193,"line":2361},105,[191,2363,2364],{},"  } else { \u002F\u002F 处理具有修饰符的情况\n",[191,2366,2368],{"class":193,"line":2367},106,[191,2369,2370],{},"    let code = ''\n",[191,2372,2374],{"class":193,"line":2373},107,[191,2375,2376],{},"    let genModifierCode = ''\n",[191,2378,2380],{"class":193,"line":2379},108,[191,2381,2382],{},"    const keys = []\n",[191,2384,2386],{"class":193,"line":2385},109,[191,2387,2388],{},"    for (const key in handler.modifiers) {  \u002F\u002F 遍历modifiers上记录的修饰符\n",[191,2390,2392],{"class":193,"line":2391},110,[191,2393,2394],{},"      if (modifierCode[key]) {\n",[191,2396,2398],{"class":193,"line":2397},111,[191,2399,2400],{},"        genModifierCode += modifierCode[key]  \u002F\u002F 根据修饰符添加对应js的代码\n",[191,2402,2404],{"class":193,"line":2403},112,[191,2405,2406],{},"        \u002F\u002F left\u002Fright\n",[191,2408,2410],{"class":193,"line":2409},113,[191,2411,2412],{},"        if (keyCodes[key]) {\n",[191,2414,2416],{"class":193,"line":2415},114,[191,2417,2418],{},"          keys.push(key)\n",[191,2420,2422],{"class":193,"line":2421},115,[191,2423,930],{},[191,2425,2427],{"class":193,"line":2426},116,[191,2428,2429],{},"      } else if (key === 'exact') { \u002F\u002F 针对exact的处理\n",[191,2431,2433],{"class":193,"line":2432},117,[191,2434,2435],{},"        const modifiers: ASTModifiers = (handler.modifiers: any)\n",[191,2437,2439],{"class":193,"line":2438},118,[191,2440,2441],{},"        genModifierCode += genGuard(\n",[191,2443,2445],{"class":193,"line":2444},119,[191,2446,2447],{},"          ['ctrl', 'shift', 'alt', 'meta']\n",[191,2449,2451],{"class":193,"line":2450},120,[191,2452,2453],{},"            .filter(keyModifier => !modifiers[keyModifier])\n",[191,2455,2457],{"class":193,"line":2456},121,[191,2458,2459],{},"            .map(keyModifier => `$event.${keyModifier}Key`)\n",[191,2461,2463],{"class":193,"line":2462},122,[191,2464,2465],{},"            .join('||')\n",[191,2467,2469],{"class":193,"line":2468},123,[191,2470,2471],{},"        )\n",[191,2473,2475],{"class":193,"line":2474},124,[191,2476,2477],{},"      } else {\n",[191,2479,2481],{"class":193,"line":2480},125,[191,2482,2483],{},"        keys.push(key) \u002F\u002F 如果修饰符不是以上修饰符，则会添加到keys数组中\n",[191,2485,2487],{"class":193,"line":2486},126,[191,2488,890],{},[191,2490,2492],{"class":193,"line":2491},127,[191,2493,978],{},[191,2495,2497],{"class":193,"line":2496},128,[191,2498,2499],{},"    if (keys.length) {\n",[191,2501,2503],{"class":193,"line":2502},129,[191,2504,2505],{},"      code += genKeyFilter(keys) \u002F\u002F 处理其他修饰符 即keyCodes中定义的修饰符\n",[191,2507,2509],{"class":193,"line":2508},130,[191,2510,978],{},[191,2512,2514],{"class":193,"line":2513},131,[191,2515,2516],{},"    \u002F\u002F Make sure modifiers like prevent and stop get executed after key filtering\n",[191,2518,2520],{"class":193,"line":2519},132,[191,2521,2522],{},"    if (genModifierCode) {\n",[191,2524,2526],{"class":193,"line":2525},133,[191,2527,2528],{},"      code += genModifierCode\n",[191,2530,2532],{"class":193,"line":2531},134,[191,2533,978],{},[191,2535,2537],{"class":193,"line":2536},135,[191,2538,2539],{},"    \u002F\u002F 根据三种不同的书写模板返回不同的字符串\n",[191,2541,2543],{"class":193,"line":2542},136,[191,2544,2545],{},"    const handlerCode = isMethodPath\n",[191,2547,2549],{"class":193,"line":2548},137,[191,2550,2551],{},"      ? `return ${handler.value}($event)`\n",[191,2553,2555],{"class":193,"line":2554},138,[191,2556,2557],{},"      : isFunctionExpression\n",[191,2559,2561],{"class":193,"line":2560},139,[191,2562,2563],{},"        ? `return (${handler.value})($event)`\n",[191,2565,2567],{"class":193,"line":2566},140,[191,2568,2569],{},"        : isFunctionInvocation\n",[191,2571,2573],{"class":193,"line":2572},141,[191,2574,2575],{},"          ? `return ${handler.value}`\n",[191,2577,2579],{"class":193,"line":2578},142,[191,2580,2581],{},"          : handler.value\n",[191,2583,2585],{"class":193,"line":2584},143,[191,2586,2323],{},[191,2588,2590],{"class":193,"line":2589},144,[191,2591,2329],{},[191,2593,2595],{"class":193,"line":2594},145,[191,2596,2597],{},"      return genWeexHandler(handler.params, code + handlerCode)\n",[191,2599,2601],{"class":193,"line":2600},146,[191,2602,978],{},[191,2604,2606],{"class":193,"line":2605},147,[191,2607,2608],{},"    return `function($event){${code}${handlerCode}}`\n",[191,2610,2612],{"class":193,"line":2611},148,[191,2613,984],{},[191,2615,2617],{"class":193,"line":2616},149,[191,2618,990],{},[191,2620,2622],{"class":193,"line":2621},150,[191,2623,235],{"emptyLinePlaceholder":234},[191,2625,2627],{"class":193,"line":2626},151,[191,2628,2629],{},"\u002F\u002F dev\u002Fsrc\u002Fcompiler\u002Fcodegen\u002Fevents.js line 175\n",[191,2631,2633],{"class":193,"line":2632},152,[191,2634,2635],{},"function genFilterCode (key: string): string {\n",[191,2637,2639],{"class":193,"line":2638},153,[191,2640,2641],{},"  const keyVal = parseInt(key, 10)\n",[191,2643,2645],{"class":193,"line":2644},154,[191,2646,2647],{},"  if (keyVal) { \u002F\u002F 如果key是数字，则直接返回$event.keyCode!==${keyVal}\n",[191,2649,2651],{"class":193,"line":2650},155,[191,2652,2653],{},"    return `$event.keyCode!==${keyVal}`\n",[191,2655,2657],{"class":193,"line":2656},156,[191,2658,984],{},[191,2660,2662],{"class":193,"line":2661},157,[191,2663,2664],{},"  const keyCode = keyCodes[key]\n",[191,2666,2668],{"class":193,"line":2667},158,[191,2669,2670],{},"  const keyName = keyNames[key]\n",[191,2672,2674],{"class":193,"line":2673},159,[191,2675,2676],{},"  \u002F\u002F 返回_k函数，它的第一个参数是$event.keyCode，\n",[191,2678,2680],{"class":193,"line":2679},160,[191,2681,2682],{},"  \u002F\u002F 第二个参数是key的值，\n",[191,2684,2686],{"class":193,"line":2685},161,[191,2687,2688],{},"  \u002F\u002F 第三个参数就是key在keyCodes中对应的数字。\n",[191,2690,2692],{"class":193,"line":2691},162,[191,2693,2694],{},"  return (\n",[191,2696,2698],{"class":193,"line":2697},163,[191,2699,2700],{},"    `_k($event.keyCode,` +\n",[191,2702,2704],{"class":193,"line":2703},164,[191,2705,2706],{},"    `${JSON.stringify(key)},` +\n",[191,2708,2710],{"class":193,"line":2709},165,[191,2711,2712],{},"    `${JSON.stringify(keyCode)},` +\n",[191,2714,2716],{"class":193,"line":2715},166,[191,2717,2718],{},"    `$event.key,` +\n",[191,2720,2722],{"class":193,"line":2721},167,[191,2723,2724],{},"    `${JSON.stringify(keyName)}` +\n",[191,2726,2728],{"class":193,"line":2727},168,[191,2729,2730],{},"    `)`\n",[191,2732,2734],{"class":193,"line":2733},169,[191,2735,2736],{},"  )\n",[191,2738,2740],{"class":193,"line":2739},170,[191,2741,990],{},[82,2743,2744],{"id":2744},"事件绑定",[14,2746,2747,2748,2750,2751,2753,2754,2756,2757,2760,2761,2763,2764,2766,2767,2769],{},"前面介绍了如何编译模板提取事件收集指令以及生成",[17,2749,35],{},"字符串和",[17,2752,35],{},"函数，但是事件真正的绑定到",[17,2755,46],{},"上还是离不开事件注册，此阶段就发生在",[17,2758,2759],{},"patchVnode","过程中，在生成完成",[17,2762,39],{},"后，进行",[17,2765,2759],{},"过程中创建真实",[17,2768,46],{},"时会进行事件注册的相关钩子处理。",[182,2771,2773],{"className":742,"code":2772,"language":744,"meta":187,"style":187},"\u002F\u002F dev\u002Fsrc\u002Fcore\u002Fvdom\u002Fpatch.js line 33\nconst hooks = ['create', 'activate', 'update', 'remove', 'destroy']\n\n\u002F\u002F dev\u002Fsrc\u002Fcore\u002Fvdom\u002Fpatch.js line 125\nfunction createElm (\n    vnode,\n    insertedVnodeQueue,\n    parentElm,\n    refElm,\n    nested,\n    ownerArray,\n    index\n  ) {\n  \u002F\u002F ...\n  if (isDef(data)) {\n    invokeCreateHooks(vnode, insertedVnodeQueue)\n  }\n  \u002F\u002F ...\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fcore\u002Fvdom\u002Fpatch.js line 303\n\u002F\u002F 在之前cbs经过处理 \n\u002F\u002F 这里cbs.create包含如下几个回调：\n\u002F\u002F updateAttrs、updateClass、updateDOMListeners、updateDOMProps、updateStyle、update、updateDirectives\nfunction invokeCreateHooks (vnode, insertedVnodeQueue) {\n    for (let i = 0; i \u003C cbs.create.length; ++i) {\n      cbs.create[i](emptyNode, vnode)\n    }\n    i = vnode.data.hook \u002F\u002F Reuse variable\n    if (isDef(i)) {\n      if (isDef(i.create)) i.create(emptyNode, vnode)\n      if (isDef(i.insert)) insertedVnodeQueue.push(vnode)\n    }\n}\n",[17,2774,2775,2780,2785,2789,2794,2799,2804,2809,2814,2819,2824,2829,2834,2838,2842,2847,2852,2856,2860,2864,2868,2873,2878,2883,2888,2893,2898,2903,2907,2912,2917,2922,2927,2931],{"__ignoreMap":187},[191,2776,2777],{"class":193,"line":194},[191,2778,2779],{},"\u002F\u002F dev\u002Fsrc\u002Fcore\u002Fvdom\u002Fpatch.js line 33\n",[191,2781,2782],{"class":193,"line":201},[191,2783,2784],{},"const hooks = ['create', 'activate', 'update', 'remove', 'destroy']\n",[191,2786,2787],{"class":193,"line":231},[191,2788,235],{"emptyLinePlaceholder":234},[191,2790,2791],{"class":193,"line":238},[191,2792,2793],{},"\u002F\u002F dev\u002Fsrc\u002Fcore\u002Fvdom\u002Fpatch.js line 125\n",[191,2795,2796],{"class":193,"line":244},[191,2797,2798],{},"function createElm (\n",[191,2800,2801],{"class":193,"line":264},[191,2802,2803],{},"    vnode,\n",[191,2805,2806],{"class":193,"line":269},[191,2807,2808],{},"    insertedVnodeQueue,\n",[191,2810,2811],{"class":193,"line":275},[191,2812,2813],{},"    parentElm,\n",[191,2815,2816],{"class":193,"line":295},[191,2817,2818],{},"    refElm,\n",[191,2820,2821],{"class":193,"line":300},[191,2822,2823],{},"    nested,\n",[191,2825,2826],{"class":193,"line":306},[191,2827,2828],{},"    ownerArray,\n",[191,2830,2831],{"class":193,"line":326},[191,2832,2833],{},"    index\n",[191,2835,2836],{"class":193,"line":331},[191,2837,1105],{},[191,2839,2840],{"class":193,"line":337},[191,2841,1724],{},[191,2843,2844],{"class":193,"line":357},[191,2845,2846],{},"  if (isDef(data)) {\n",[191,2848,2849],{"class":193,"line":362},[191,2850,2851],{},"    invokeCreateHooks(vnode, insertedVnodeQueue)\n",[191,2853,2854],{"class":193,"line":368},[191,2855,984],{},[191,2857,2858],{"class":193,"line":388},[191,2859,1724],{},[191,2861,2862],{"class":193,"line":393},[191,2863,990],{},[191,2865,2866],{"class":193,"line":399},[191,2867,235],{"emptyLinePlaceholder":234},[191,2869,2870],{"class":193,"line":419},[191,2871,2872],{},"\u002F\u002F dev\u002Fsrc\u002Fcore\u002Fvdom\u002Fpatch.js line 303\n",[191,2874,2875],{"class":193,"line":424},[191,2876,2877],{},"\u002F\u002F 在之前cbs经过处理 \n",[191,2879,2880],{"class":193,"line":430},[191,2881,2882],{},"\u002F\u002F 这里cbs.create包含如下几个回调：\n",[191,2884,2885],{"class":193,"line":447},[191,2886,2887],{},"\u002F\u002F updateAttrs、updateClass、updateDOMListeners、updateDOMProps、updateStyle、update、updateDirectives\n",[191,2889,2890],{"class":193,"line":452},[191,2891,2892],{},"function invokeCreateHooks (vnode, insertedVnodeQueue) {\n",[191,2894,2895],{"class":193,"line":458},[191,2896,2897],{},"    for (let i = 0; i \u003C cbs.create.length; ++i) {\n",[191,2899,2900],{"class":193,"line":478},[191,2901,2902],{},"      cbs.create[i](emptyNode, vnode)\n",[191,2904,2905],{"class":193,"line":483},[191,2906,978],{},[191,2908,2909],{"class":193,"line":489},[191,2910,2911],{},"    i = vnode.data.hook \u002F\u002F Reuse variable\n",[191,2913,2914],{"class":193,"line":507},[191,2915,2916],{},"    if (isDef(i)) {\n",[191,2918,2919],{"class":193,"line":512},[191,2920,2921],{},"      if (isDef(i.create)) i.create(emptyNode, vnode)\n",[191,2923,2924],{"class":193,"line":518},[191,2925,2926],{},"      if (isDef(i.insert)) insertedVnodeQueue.push(vnode)\n",[191,2928,2929],{"class":193,"line":534},[191,2930,978],{},[191,2932,2933],{"class":193,"line":539},[191,2934,990],{},[14,2936,2937,2940,2941,2944,2945,2947],{},[17,2938,2939],{},"invokeCreateHooks","就是一个模板指令处理的任务，他分别针对不同的指令为真实阶段创建不同的任务，针对事件，这里会调",[17,2942,2943],{},"updateDOMListeners","对真实的",[17,2946,46],{},"节点注册事件任务。",[182,2949,2951],{"className":742,"code":2950,"language":744,"meta":187,"style":187},"\u002F\u002F dev\u002Fsrc\u002Fplatforms\u002Fweb\u002Fruntime\u002Fmodules\u002Fevents.js line 105\nfunction updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) {\n  if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) {  \u002F\u002F on是事件指令的标志\n    return\n  }\n  \u002F\u002F 新旧节点不同的事件绑定解绑\n  const on = vnode.data.on || {}\n  const oldOn = oldVnode.data.on || {}\n  \u002F\u002F 拿到需要添加事件的真实DOM节点\n  target = vnode.elm\n  \u002F\u002F normalizeEvents是对事件兼容性的处理\n  normalizeEvents(on)\n  \u002F\u002F 调用updateListeners方法，并将on作为参数传进去\n  updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context)\n  target = undefined\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fcore\u002Fvdom\u002Fhelpers\u002Fupdate-listeners.js line line 53\nexport function updateListeners (\n  on: Object,\n  oldOn: Object,\n  add: Function,\n  remove: Function,\n  createOnceHandler: Function,\n  vm: Component\n) {\n  let name, def, cur, old, event\n  for (name in on) { \u002F\u002F 遍历事件\n    def = cur = on[name]\n    old = oldOn[name]\n    event = normalizeEvent(name)\n    \u002F* istanbul ignore if *\u002F\n    if (__WEEX__ && isPlainObject(def)) {\n      cur = def.handler\n      event.params = def.params\n    }\n    if (isUndef(cur)) { \u002F\u002F 事件名非法的报错处理\n      process.env.NODE_ENV !== 'production' && warn(\n        `Invalid handler for event \"${event.name}\": got ` + String(cur),\n        vm\n      )\n    } else if (isUndef(old)) { \u002F\u002F 旧节点不存在\n      if (isUndef(cur.fns)) { \u002F\u002F createFunInvoker返回事件最终执行的回调函数\n        cur = on[name] = createFnInvoker(cur, vm)\n      }\n      if (isTrue(event.once)) {  \u002F\u002F 只触发一次的事件\n        cur = on[name] = createOnceHandler(event.name, cur, event.capture)\n      }\n      \u002F\u002F 执行真正注册事件的执行函数\n      add(event.name, cur, event.capture, event.passive, event.params)\n    } else if (cur !== old) {\n      old.fns = cur\n      on[name] = old\n    }\n  }\n  for (name in oldOn) { \u002F\u002F 旧节点存在，解除旧节点上的绑定事件\n    if (isUndef(on[name])) {\n      event = normalizeEvent(name)\n      \u002F\u002F 移除事件监听\n      remove(event.name, oldOn[name], event.capture)\n    }\n  }\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fplatforms\u002Fweb\u002Fruntime\u002Fmodules\u002Fevents.js line 32\n\u002F\u002F 在执行完回调之后，移除事件绑定\nfunction createOnceHandler (event, handler, capture) {\n  const _target = target \u002F\u002F save current target element in closure\n  return function onceHandler () {\n    const res = handler.apply(null, arguments)\n    if (res !== null) {\n      remove(event, onceHandler, capture, _target)\n    }\n  }\n}\n",[17,2952,2953,2958,2963,2968,2973,2977,2982,2987,2992,2997,3002,3007,3012,3017,3022,3027,3031,3035,3040,3045,3050,3055,3060,3065,3070,3075,3079,3084,3089,3094,3099,3104,3108,3113,3118,3123,3127,3132,3137,3142,3147,3152,3157,3162,3167,3171,3176,3181,3185,3190,3195,3200,3205,3210,3214,3218,3223,3228,3233,3238,3243,3247,3251,3255,3259,3264,3269,3274,3279,3284,3289,3294,3299,3303,3307],{"__ignoreMap":187},[191,2954,2955],{"class":193,"line":194},[191,2956,2957],{},"\u002F\u002F dev\u002Fsrc\u002Fplatforms\u002Fweb\u002Fruntime\u002Fmodules\u002Fevents.js line 105\n",[191,2959,2960],{"class":193,"line":201},[191,2961,2962],{},"function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) {\n",[191,2964,2965],{"class":193,"line":231},[191,2966,2967],{},"  if (isUndef(oldVnode.data.on) && isUndef(vnode.data.on)) {  \u002F\u002F on是事件指令的标志\n",[191,2969,2970],{"class":193,"line":238},[191,2971,2972],{},"    return\n",[191,2974,2975],{"class":193,"line":244},[191,2976,984],{},[191,2978,2979],{"class":193,"line":264},[191,2980,2981],{},"  \u002F\u002F 新旧节点不同的事件绑定解绑\n",[191,2983,2984],{"class":193,"line":269},[191,2985,2986],{},"  const on = vnode.data.on || {}\n",[191,2988,2989],{"class":193,"line":275},[191,2990,2991],{},"  const oldOn = oldVnode.data.on || {}\n",[191,2993,2994],{"class":193,"line":295},[191,2995,2996],{},"  \u002F\u002F 拿到需要添加事件的真实DOM节点\n",[191,2998,2999],{"class":193,"line":300},[191,3000,3001],{},"  target = vnode.elm\n",[191,3003,3004],{"class":193,"line":306},[191,3005,3006],{},"  \u002F\u002F normalizeEvents是对事件兼容性的处理\n",[191,3008,3009],{"class":193,"line":326},[191,3010,3011],{},"  normalizeEvents(on)\n",[191,3013,3014],{"class":193,"line":331},[191,3015,3016],{},"  \u002F\u002F 调用updateListeners方法，并将on作为参数传进去\n",[191,3018,3019],{"class":193,"line":337},[191,3020,3021],{},"  updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context)\n",[191,3023,3024],{"class":193,"line":357},[191,3025,3026],{},"  target = undefined\n",[191,3028,3029],{"class":193,"line":362},[191,3030,990],{},[191,3032,3033],{"class":193,"line":368},[191,3034,235],{"emptyLinePlaceholder":234},[191,3036,3037],{"class":193,"line":388},[191,3038,3039],{},"\u002F\u002F dev\u002Fsrc\u002Fcore\u002Fvdom\u002Fhelpers\u002Fupdate-listeners.js line line 53\n",[191,3041,3042],{"class":193,"line":393},[191,3043,3044],{},"export function updateListeners (\n",[191,3046,3047],{"class":193,"line":399},[191,3048,3049],{},"  on: Object,\n",[191,3051,3052],{"class":193,"line":419},[191,3053,3054],{},"  oldOn: Object,\n",[191,3056,3057],{"class":193,"line":424},[191,3058,3059],{},"  add: Function,\n",[191,3061,3062],{"class":193,"line":430},[191,3063,3064],{},"  remove: Function,\n",[191,3066,3067],{"class":193,"line":447},[191,3068,3069],{},"  createOnceHandler: Function,\n",[191,3071,3072],{"class":193,"line":452},[191,3073,3074],{},"  vm: Component\n",[191,3076,3077],{"class":193,"line":458},[191,3078,1060],{},[191,3080,3081],{"class":193,"line":478},[191,3082,3083],{},"  let name, def, cur, old, event\n",[191,3085,3086],{"class":193,"line":483},[191,3087,3088],{},"  for (name in on) { \u002F\u002F 遍历事件\n",[191,3090,3091],{"class":193,"line":489},[191,3092,3093],{},"    def = cur = on[name]\n",[191,3095,3096],{"class":193,"line":507},[191,3097,3098],{},"    old = oldOn[name]\n",[191,3100,3101],{"class":193,"line":512},[191,3102,3103],{},"    event = normalizeEvent(name)\n",[191,3105,3106],{"class":193,"line":518},[191,3107,2323],{},[191,3109,3110],{"class":193,"line":534},[191,3111,3112],{},"    if (__WEEX__ && isPlainObject(def)) {\n",[191,3114,3115],{"class":193,"line":539},[191,3116,3117],{},"      cur = def.handler\n",[191,3119,3120],{"class":193,"line":545},[191,3121,3122],{},"      event.params = def.params\n",[191,3124,3125],{"class":193,"line":565},[191,3126,978],{},[191,3128,3129],{"class":193,"line":570},[191,3130,3131],{},"    if (isUndef(cur)) { \u002F\u002F 事件名非法的报错处理\n",[191,3133,3134],{"class":193,"line":576},[191,3135,3136],{},"      process.env.NODE_ENV !== 'production' && warn(\n",[191,3138,3139],{"class":193,"line":938},[191,3140,3141],{},"        `Invalid handler for event \"${event.name}\": got ` + String(cur),\n",[191,3143,3144],{"class":193,"line":944},[191,3145,3146],{},"        vm\n",[191,3148,3149],{"class":193,"line":949},[191,3150,3151],{},"      )\n",[191,3153,3154],{"class":193,"line":954},[191,3155,3156],{},"    } else if (isUndef(old)) { \u002F\u002F 旧节点不存在\n",[191,3158,3159],{"class":193,"line":960},[191,3160,3161],{},"      if (isUndef(cur.fns)) { \u002F\u002F createFunInvoker返回事件最终执行的回调函数\n",[191,3163,3164],{"class":193,"line":969},[191,3165,3166],{},"        cur = on[name] = createFnInvoker(cur, vm)\n",[191,3168,3169],{"class":193,"line":975},[191,3170,890],{},[191,3172,3173],{"class":193,"line":981},[191,3174,3175],{},"      if (isTrue(event.once)) {  \u002F\u002F 只触发一次的事件\n",[191,3177,3178],{"class":193,"line":987},[191,3179,3180],{},"        cur = on[name] = createOnceHandler(event.name, cur, event.capture)\n",[191,3182,3183],{"class":193,"line":1238},[191,3184,890],{},[191,3186,3187],{"class":193,"line":1244},[191,3188,3189],{},"      \u002F\u002F 执行真正注册事件的执行函数\n",[191,3191,3192],{"class":193,"line":1250},[191,3193,3194],{},"      add(event.name, cur, event.capture, event.passive, event.params)\n",[191,3196,3197],{"class":193,"line":1256},[191,3198,3199],{},"    } else if (cur !== old) {\n",[191,3201,3202],{"class":193,"line":1262},[191,3203,3204],{},"      old.fns = cur\n",[191,3206,3207],{"class":193,"line":1268},[191,3208,3209],{},"      on[name] = old\n",[191,3211,3212],{"class":193,"line":1273},[191,3213,978],{},[191,3215,3216],{"class":193,"line":1279},[191,3217,984],{},[191,3219,3220],{"class":193,"line":1285},[191,3221,3222],{},"  for (name in oldOn) { \u002F\u002F 旧节点存在，解除旧节点上的绑定事件\n",[191,3224,3225],{"class":193,"line":1291},[191,3226,3227],{},"    if (isUndef(on[name])) {\n",[191,3229,3230],{"class":193,"line":1296},[191,3231,3232],{},"      event = normalizeEvent(name)\n",[191,3234,3235],{"class":193,"line":1301},[191,3236,3237],{},"      \u002F\u002F 移除事件监听\n",[191,3239,3240],{"class":193,"line":1307},[191,3241,3242],{},"      remove(event.name, oldOn[name], event.capture)\n",[191,3244,3245],{"class":193,"line":1313},[191,3246,978],{},[191,3248,3249],{"class":193,"line":1319},[191,3250,984],{},[191,3252,3253],{"class":193,"line":1324},[191,3254,990],{},[191,3256,3257],{"class":193,"line":1329},[191,3258,235],{"emptyLinePlaceholder":234},[191,3260,3261],{"class":193,"line":1335},[191,3262,3263],{},"\u002F\u002F dev\u002Fsrc\u002Fplatforms\u002Fweb\u002Fruntime\u002Fmodules\u002Fevents.js line 32\n",[191,3265,3266],{"class":193,"line":1341},[191,3267,3268],{},"\u002F\u002F 在执行完回调之后，移除事件绑定\n",[191,3270,3271],{"class":193,"line":1347},[191,3272,3273],{},"function createOnceHandler (event, handler, capture) {\n",[191,3275,3276],{"class":193,"line":1353},[191,3277,3278],{},"  const _target = target \u002F\u002F save current target element in closure\n",[191,3280,3281],{"class":193,"line":1359},[191,3282,3283],{},"  return function onceHandler () {\n",[191,3285,3286],{"class":193,"line":1365},[191,3287,3288],{},"    const res = handler.apply(null, arguments)\n",[191,3290,3291],{"class":193,"line":1371},[191,3292,3293],{},"    if (res !== null) {\n",[191,3295,3296],{"class":193,"line":1376},[191,3297,3298],{},"      remove(event, onceHandler, capture, _target)\n",[191,3300,3301],{"class":193,"line":1382},[191,3302,978],{},[191,3304,3305],{"class":193,"line":1388},[191,3306,984],{},[191,3308,3309],{"class":193,"line":1394},[191,3310,990],{},[14,3312,3313,3314,60,3317,3320,3321,728,3323,3325,3326,3329],{},"最终添加与移除事件都是调用的",[17,3315,3316],{},"add",[17,3318,3319],{},"remove","方法，最终调用的方法即",[17,3322,46],{},[17,3324,50],{},"方法与",[17,3327,3328],{},"removeEventListener","方法。",[182,3331,3333],{"className":742,"code":3332,"language":744,"meta":187,"style":187},"\u002F\u002F dev\u002Fsrc\u002Fplatforms\u002Fweb\u002Fruntime\u002Fmodules\u002Fevents.js line 46\nfunction add (\n  name: string,\n  handler: Function,\n  capture: boolean,\n  passive: boolean\n) {\n  \u002F\u002F async edge case #6566: inner click event triggers patch, event handler\n  \u002F\u002F attached to outer element during patch, and triggered again. This\n  \u002F\u002F happens because browsers fire microtask ticks between event propagation.\n  \u002F\u002F the solution is simple: we save the timestamp when a handler is attached,\n  \u002F\u002F and the handler would only fire if the event passed to it was fired\n  \u002F\u002F AFTER it was attached.\n  if (useMicrotaskFix) {\n    const attachedTimestamp = currentFlushTimestamp\n    const original = handler\n    handler = original._wrapper = function (e) {\n      if (\n        \u002F\u002F no bubbling, should always fire.\n        \u002F\u002F this is just a safety net in case event.timeStamp is unreliable in\n        \u002F\u002F certain weird environments...\n        e.target === e.currentTarget ||\n        \u002F\u002F event is fired after handler attachment\n        e.timeStamp >= attachedTimestamp ||\n        \u002F\u002F bail for environments that have buggy event.timeStamp implementations\n        \u002F\u002F #9462 iOS 9 bug: event.timeStamp is 0 after history.pushState\n        \u002F\u002F #9681 QtWebEngine event.timeStamp is negative value\n        e.timeStamp \u003C= 0 ||\n        \u002F\u002F #9448 bail if event is fired in another document in a multi-page\n        \u002F\u002F electron\u002Fnw.js app, since event.timeStamp will be using a different\n        \u002F\u002F starting reference\n        e.target.ownerDocument !== document\n      ) {\n        return original.apply(this, arguments)\n      }\n    }\n  }\n  target.addEventListener(\n    name,\n    handler,\n    supportsPassive\n      ? { capture, passive }\n      : capture\n  )\n}\n\n\u002F\u002F dev\u002Fsrc\u002Fplatforms\u002Fweb\u002Fruntime\u002Fmodules\u002Fevents.js line 92\nfunction remove (\n  name: string,\n  handler: Function,\n  capture: boolean,\n  _target?: HTMLElement\n) {\n  (_target || target).removeEventListener(\n    name,\n    handler._wrapper || handler,\n    capture\n  )\n}\n",[17,3334,3335,3340,3345,3349,3354,3359,3364,3368,3373,3378,3383,3388,3393,3398,3403,3408,3413,3418,3423,3428,3433,3438,3443,3448,3453,3458,3463,3468,3473,3478,3483,3488,3493,3498,3503,3507,3511,3515,3520,3525,3530,3535,3540,3545,3549,3553,3557,3562,3567,3571,3575,3579,3584,3588,3593,3597,3602,3607,3611],{"__ignoreMap":187},[191,3336,3337],{"class":193,"line":194},[191,3338,3339],{},"\u002F\u002F dev\u002Fsrc\u002Fplatforms\u002Fweb\u002Fruntime\u002Fmodules\u002Fevents.js line 46\n",[191,3341,3342],{"class":193,"line":201},[191,3343,3344],{},"function add (\n",[191,3346,3347],{"class":193,"line":231},[191,3348,1025],{},[191,3350,3351],{"class":193,"line":238},[191,3352,3353],{},"  handler: Function,\n",[191,3355,3356],{"class":193,"line":244},[191,3357,3358],{},"  capture: boolean,\n",[191,3360,3361],{"class":193,"line":264},[191,3362,3363],{},"  passive: boolean\n",[191,3365,3366],{"class":193,"line":269},[191,3367,1060],{},[191,3369,3370],{"class":193,"line":275},[191,3371,3372],{},"  \u002F\u002F async edge case #6566: inner click event triggers patch, event handler\n",[191,3374,3375],{"class":193,"line":295},[191,3376,3377],{},"  \u002F\u002F attached to outer element during patch, and triggered again. This\n",[191,3379,3380],{"class":193,"line":300},[191,3381,3382],{},"  \u002F\u002F happens because browsers fire microtask ticks between event propagation.\n",[191,3384,3385],{"class":193,"line":306},[191,3386,3387],{},"  \u002F\u002F the solution is simple: we save the timestamp when a handler is attached,\n",[191,3389,3390],{"class":193,"line":326},[191,3391,3392],{},"  \u002F\u002F and the handler would only fire if the event passed to it was fired\n",[191,3394,3395],{"class":193,"line":331},[191,3396,3397],{},"  \u002F\u002F AFTER it was attached.\n",[191,3399,3400],{"class":193,"line":337},[191,3401,3402],{},"  if (useMicrotaskFix) {\n",[191,3404,3405],{"class":193,"line":357},[191,3406,3407],{},"    const attachedTimestamp = currentFlushTimestamp\n",[191,3409,3410],{"class":193,"line":362},[191,3411,3412],{},"    const original = handler\n",[191,3414,3415],{"class":193,"line":368},[191,3416,3417],{},"    handler = original._wrapper = function (e) {\n",[191,3419,3420],{"class":193,"line":388},[191,3421,3422],{},"      if (\n",[191,3424,3425],{"class":193,"line":393},[191,3426,3427],{},"        \u002F\u002F no bubbling, should always fire.\n",[191,3429,3430],{"class":193,"line":399},[191,3431,3432],{},"        \u002F\u002F this is just a safety net in case event.timeStamp is unreliable in\n",[191,3434,3435],{"class":193,"line":419},[191,3436,3437],{},"        \u002F\u002F certain weird environments...\n",[191,3439,3440],{"class":193,"line":424},[191,3441,3442],{},"        e.target === e.currentTarget ||\n",[191,3444,3445],{"class":193,"line":430},[191,3446,3447],{},"        \u002F\u002F event is fired after handler attachment\n",[191,3449,3450],{"class":193,"line":447},[191,3451,3452],{},"        e.timeStamp >= attachedTimestamp ||\n",[191,3454,3455],{"class":193,"line":452},[191,3456,3457],{},"        \u002F\u002F bail for environments that have buggy event.timeStamp implementations\n",[191,3459,3460],{"class":193,"line":458},[191,3461,3462],{},"        \u002F\u002F #9462 iOS 9 bug: event.timeStamp is 0 after history.pushState\n",[191,3464,3465],{"class":193,"line":478},[191,3466,3467],{},"        \u002F\u002F #9681 QtWebEngine event.timeStamp is negative value\n",[191,3469,3470],{"class":193,"line":483},[191,3471,3472],{},"        e.timeStamp \u003C= 0 ||\n",[191,3474,3475],{"class":193,"line":489},[191,3476,3477],{},"        \u002F\u002F #9448 bail if event is fired in another document in a multi-page\n",[191,3479,3480],{"class":193,"line":507},[191,3481,3482],{},"        \u002F\u002F electron\u002Fnw.js app, since event.timeStamp will be using a different\n",[191,3484,3485],{"class":193,"line":512},[191,3486,3487],{},"        \u002F\u002F starting reference\n",[191,3489,3490],{"class":193,"line":518},[191,3491,3492],{},"        e.target.ownerDocument !== document\n",[191,3494,3495],{"class":193,"line":534},[191,3496,3497],{},"      ) {\n",[191,3499,3500],{"class":193,"line":539},[191,3501,3502],{},"        return original.apply(this, arguments)\n",[191,3504,3505],{"class":193,"line":545},[191,3506,890],{},[191,3508,3509],{"class":193,"line":565},[191,3510,978],{},[191,3512,3513],{"class":193,"line":570},[191,3514,984],{},[191,3516,3517],{"class":193,"line":576},[191,3518,3519],{},"  target.addEventListener(\n",[191,3521,3522],{"class":193,"line":938},[191,3523,3524],{},"    name,\n",[191,3526,3527],{"class":193,"line":944},[191,3528,3529],{},"    handler,\n",[191,3531,3532],{"class":193,"line":949},[191,3533,3534],{},"    supportsPassive\n",[191,3536,3537],{"class":193,"line":954},[191,3538,3539],{},"      ? { capture, passive }\n",[191,3541,3542],{"class":193,"line":960},[191,3543,3544],{},"      : capture\n",[191,3546,3547],{"class":193,"line":969},[191,3548,2736],{},[191,3550,3551],{"class":193,"line":975},[191,3552,990],{},[191,3554,3555],{"class":193,"line":981},[191,3556,235],{"emptyLinePlaceholder":234},[191,3558,3559],{"class":193,"line":987},[191,3560,3561],{},"\u002F\u002F dev\u002Fsrc\u002Fplatforms\u002Fweb\u002Fruntime\u002Fmodules\u002Fevents.js line 92\n",[191,3563,3564],{"class":193,"line":1238},[191,3565,3566],{},"function remove (\n",[191,3568,3569],{"class":193,"line":1244},[191,3570,1025],{},[191,3572,3573],{"class":193,"line":1250},[191,3574,3353],{},[191,3576,3577],{"class":193,"line":1256},[191,3578,3358],{},[191,3580,3581],{"class":193,"line":1262},[191,3582,3583],{},"  _target?: HTMLElement\n",[191,3585,3586],{"class":193,"line":1268},[191,3587,1060],{},[191,3589,3590],{"class":193,"line":1273},[191,3591,3592],{},"  (_target || target).removeEventListener(\n",[191,3594,3595],{"class":193,"line":1279},[191,3596,3524],{},[191,3598,3599],{"class":193,"line":1285},[191,3600,3601],{},"    handler._wrapper || handler,\n",[191,3603,3604],{"class":193,"line":1291},[191,3605,3606],{},"    capture\n",[191,3608,3609],{"class":193,"line":1296},[191,3610,2736],{},[191,3612,3613],{"class":193,"line":1301},[191,3614,990],{},[53,3616,3617],{"id":3617},"每日一题",[182,3619,3624],{"className":3620,"code":3622,"language":3623},[3621],"language-text","https:\u002F\u002Fgithub.com\u002FWindrunnerMax\u002FEveryDay\n","text",[17,3625,3622],{"__ignoreMap":187},[53,3627,3628],{"id":3628},"参考",[182,3630,3633],{"className":3631,"code":3632,"language":3623},[3621],"https:\u002F\u002Fcn.vuejs.org\u002Fv2\u002Fapi\u002F#v-on\nhttps:\u002F\u002Fjuejin.im\u002Fpost\u002F6844903919290679304\nhttps:\u002F\u002Fjuejin.im\u002Fpost\u002F6844904061897015310\nhttps:\u002F\u002Fjuejin.im\u002Fpost\u002F6844904126250221576\nhttps:\u002F\u002Fsegmentfault.com\u002Fa\u002F1190000009750348\nhttps:\u002F\u002Fblog.csdn.net\u002Fweixin_41275295\u002Farticle\u002Fdetails\u002F100549145\nhttps:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-CN\u002Fdocs\u002FWeb\u002FAPI\u002FEventTarget\u002FaddEventListener\nhttps:\u002F\u002Fgithub.com\u002Fliutao\u002Fvue2.0-source\u002Fblob\u002Fmaster\u002F%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86.md\n",[17,3634,3632],{"__ignoreMap":187},[3636,3637,3638],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":187,"searchDepth":201,"depth":201,"links":3640},[3641,3646,3651,3652],{"id":55,"depth":201,"text":55,"children":3642},[3643,3644,3645],{"id":84,"depth":231,"text":84},{"id":180,"depth":231,"text":180},{"id":597,"depth":231,"text":597},{"id":687,"depth":201,"text":687,"children":3647},[3648,3649,3650],{"id":702,"depth":231,"text":702},{"id":1490,"depth":231,"text":1490},{"id":2744,"depth":231,"text":2744},{"id":3617,"depth":201,"text":3617},{"id":3628,"depth":201,"text":3628},"Vue中通过v-on或其语法糖@指令来给元素绑定事件并且提供了事件修饰符，基本流程是进行模板编译生成AST，生成render函数后并执行得到VNode，VNode生成真实DOM节点或者组件时候使用addEventListener方法进行事件绑定。","md",{},"\u002Fvue\u002Fvue事件绑定原理",{"title":5,"description":3653},"Vue\u002FVue事件绑定原理","s-UNpONAbutvX9SwIrOWJURgosOAlP992M4Xljkl6Pk",1791042470621]