# vue源码 事件机制
# 模版编译
- 在编译阶段对事件指令做收集
processAttrs
/src/compiler/parser/index.js
export const dirRE = process.env.VBIND_PROP_SHORTHAND
? /^v-|^@|^:|^\.|^#/
: /^v-|^@|^:|^#/
function processAttrs (el) {
const list = el.attrsList
...
for (i = 0, l = list.length; i < l; i++) {
name = rawName = list[i].name
value = list[i].value
if (dirRE.test(name)) { // 匹配
... // modifiers
modifiers = parseModifiers(name.replace(dirRE, ''))
...
if (bindRE.test(name)) { // v-bind
name = name.replace(bindRE, '')
...
} else if (onRE.test(name)) { // v-on
name = name.replace(onRE, '')
isDynamic = dynamicArgRE.test(name)// 动态绑定
if (isDynamic) {
name = name.slice(1, -1)
}
addHandler(el, name, value, modifiers, false, warn, list[i], isDynamic)
} else { // normal directives
...
} else {
// literal attribute
}
}
}
addHandler
为ast树添加事件相关属性 /compiler/helpers.jsgenerate
最终转换成ast树后生成render
渲染函数 /compiler/codegen/index.js
export function generate (
ast: ASTElement | void,
options: CompilerOptions
): CodegenResult {
const state = new CodegenState(options)
// fix #11483, Root level <script> tags should not be rendered.
const code = ast ? (ast.tag === 'script' ? 'null' : genElement(ast, state)) : '_c("div")' //here
return {
render: `with(this){return ${code}}`,
staticRenderFns: state.staticRenderFns
}
}
genElement
对普通模版分支会进入genData
...
// component or element
let code
if (el.component) {
code = genComponent(el.component, el, state)
} else {
let data
if (!el.plain || (el.pre && state.maybeComponent(el))) {
data = genData(el, state)
}
...
genData()
export function genData (el: ASTElement, state: CodegenState): string {
...
// event handlers
if (el.events) {
data += `${genHandlers(el.events, false)},`
}
if (el.nativeEvents) {
data += `${genHandlers(el.nativeEvents, true)},`
}
...
genHandlers()
遍历解析好的ast树,并拿到event对象,根据属性凭借字符串
export function genHandlers (
events: ASTElementHandlers,
isNative: boolean
): string {
const prefix = isNative ? 'nativeOn:' : 'on:'
let staticHandlers = ``
let dynamicHandlers = ``
for (const name in events) {
const handlerCode = genHandler(events[name]) //here
if (events[name] && events[name].dynamic) {
dynamicHandlers += `${name},${handlerCode},`
} else {
staticHandlers += `"${name}":${handlerCode},`
}
}
...
genHandler()
function genHandler (handler: ASTElementHandler | Array<ASTElementHandler>): string {
if (!handler) {
return 'function(){}'
}
if (Array.isArray(handler)) {
return `[${handler.map(handler => genHandler(handler)).join(',')}]`
}
//value : dothis
const isMethodPath = simplePathRE.test(handler.value) //@click = dothis
const isFunctionExpression = fnExpRE.test(handler.value) // @click = ()=>{}
const isFunctionInvocation = simplePathRE.test(handler.value.replace(fnInvokeRE, '')) // @click = dothis(event)
...
}
# 事件绑定
- createElm /vdom/patch.js
...
if (!appendAsTree) {
if (isDef(data)) {
invokeCreateHooks(vnode, insertedVnodeQueue)
}
insert(parentElm, vnode.elm, refElm)
}
...
- invokeCreateHooks
function invokeCreateHooks (vnode, insertedVnodeQueue) {
for (let i = 0; i < cbs.create.length; ++i) {
cbs.create[i](emptyNode, vnode)
}
i = vnode.data.hook // Reuse variable
if (isDef(i)) {
if (isDef(i.create)) i.create(emptyNode, vnode)
if (isDef(i.insert)) insertedVnodeQueue.push(vnode)
}
}
updateDOMListeners
function updateDOMListeners (oldVnode: VNodeWithData, vnode: VNodeWithData) {
if (!oldVnode.data.on && !vnode.data.on) {
return
}
const on = vnode.data.on || {}
const oldOn = oldVnode.data.on || {}
target = vnode.elm
updateListeners(on, oldOn, add, remove, createOnceHandler, vnode.context)
target = undefined
}
export default {
create: updateDOMListeners,
update: updateDOMListeners
}
# 关于组件通信
- 本质都是在当前组件实例里进行
- 能有通信的效果时因为事件监听的回调写在了父组件中