Browse Source

improve event update algorithm (thanks to suggestions from @defcc)

dev
Evan You 8 years ago
parent
commit
c660917b29
  1. 96
      src/core/vdom/helpers/update-listeners.js
  2. 12
      test/unit/modules/vdom/modules/events.spec.js

96
src/core/vdom/helpers/update-listeners.js

@ -1,7 +1,44 @@
/* @flow */ /* @flow */
import { cached } from 'shared/util'
import { warn } from 'core/util/index' import { warn } from 'core/util/index'
const normalizeEvent = cached((name: string): {
name: string,
once: boolean,
capture: boolean
} => {
const once = name.charAt(0) === '~' // Prefixed last, checked first
name = once ? name.slice(1) : name
const capture = name.charAt(0) === '!'
name = capture ? name.slice(1) : name
return {
name,
once,
capture
}
})
function createEventHandle (fn: Function | Array<Function>): {
fn: Function | Array<Function>;
invoker: Function;
} {
const handle = {
fn,
invoker: function () {
const fn = handle.fn
if (Array.isArray(fn)) {
for (let i = 0; i < fn.length; i++) {
fn[i].apply(null, arguments)
}
} else {
fn.apply(null, arguments)
}
}
}
return handle
}
export function updateListeners ( export function updateListeners (
on: Object, on: Object,
oldOn: Object, oldOn: Object,
@ -9,69 +46,30 @@ export function updateListeners (
remove: Function, remove: Function,
vm: Component vm: Component
) { ) {
let name, cur, old, curIsArray, oldIsArray, fn, event, capture, once let name, cur, old, event
for (name in on) { for (name in on) {
cur = on[name] cur = on[name]
old = oldOn[name] old = oldOn[name]
curIsArray = Array.isArray(cur) event = normalizeEvent(name)
oldIsArray = Array.isArray(old)
if (!cur) { if (!cur) {
process.env.NODE_ENV !== 'production' && warn( process.env.NODE_ENV !== 'production' && warn(
`Invalid handler for event "${name}": got ` + String(cur), `Invalid handler for event "${event.name}": got ` + String(cur),
vm vm
) )
} else if (!old) { } else if (!old) {
once = name.charAt(0) === '~' // Prefixed last, checked first if (!cur.invoker) {
event = once ? name.slice(1) : name cur = on[name] = createEventHandle(cur)
capture = event.charAt(0) === '!'
event = capture ? event.slice(1) : event
if (curIsArray) {
add(event, (cur.invoker = arrInvoker(cur)), once, capture)
} else {
if (!cur.invoker) {
fn = cur
cur = on[name] = {}
cur.fn = fn
cur.invoker = fnInvoker(cur)
}
add(event, cur.invoker, once, capture)
} }
add(event.name, cur.invoker, event.once, event.capture)
} else if (cur !== old) { } else if (cur !== old) {
if (curIsArray || oldIsArray) { old.fn = cur
if (!curIsArray) cur = [cur] on[name] = old
if (!oldIsArray) old = [old]
old.length = cur.length
for (let i = 0; i < old.length; i++) old[i] = cur[i]
on[name] = old
} else {
old.fn = cur
on[name] = old
}
} }
} }
for (name in oldOn) { for (name in oldOn) {
if (!on[name]) { if (!on[name]) {
once = name.charAt(0) === '~' // Prefixed last, checked first event = normalizeEvent(name)
event = once ? name.slice(1) : name remove(event.name, oldOn[name].invoker, event.capture)
capture = event.charAt(0) === '!'
event = capture ? event.slice(1) : event
remove(event, oldOn[name].invoker, capture)
}
}
}
function arrInvoker (arr: Array<Function>): Function {
return function (ev) {
const single = arguments.length === 1
for (let i = 0; i < arr.length; i++) {
single ? arr[i](ev) : arr[i].apply(null, arguments)
} }
} }
} }
function fnInvoker (o: { fn: Function }): Function {
return function (ev) {
const single = arguments.length === 1
single ? o.fn(ev) : o.fn.apply(null, arguments)
}
}

12
test/unit/modules/vdom/modules/events.spec.js

@ -105,24 +105,30 @@ describe('vdom events module', () => {
const click = jasmine.createSpy() const click = jasmine.createSpy()
const click2 = jasmine.createSpy() const click2 = jasmine.createSpy()
const click3 = jasmine.createSpy() const click3 = jasmine.createSpy()
const vnode0 = new VNode('a', { on: { click: click }})
const vnode1 = new VNode('a', { on: { click: [click, click2] }}) const vnode1 = new VNode('a', { on: { click: [click, click2] }})
const vnode2 = new VNode('a', { on: { click: click }}) const vnode2 = new VNode('a', { on: { click: click }})
const vnode3 = new VNode('a', { on: { click: [click2, click3] }}) const vnode3 = new VNode('a', { on: { click: [click2, click3] }})
const elm = patch(null, vnode1) const elm = patch(null, vnode0)
document.body.appendChild(elm) document.body.appendChild(elm)
triggerEvent(elm, 'click') triggerEvent(elm, 'click')
expect(click.calls.count()).toBe(1) expect(click.calls.count()).toBe(1)
expect(click2.calls.count()).toBe(0)
patch(vnode0, vnode1)
triggerEvent(elm, 'click')
expect(click.calls.count()).toBe(2)
expect(click2.calls.count()).toBe(1) expect(click2.calls.count()).toBe(1)
patch(vnode1, vnode2) patch(vnode1, vnode2)
triggerEvent(elm, 'click') triggerEvent(elm, 'click')
expect(click.calls.count()).toBe(2) expect(click.calls.count()).toBe(3)
expect(click2.calls.count()).toBe(1) expect(click2.calls.count()).toBe(1)
patch(vnode2, vnode3) patch(vnode2, vnode3)
triggerEvent(elm, 'click') triggerEvent(elm, 'click')
expect(click.calls.count()).toBe(2) expect(click.calls.count()).toBe(3)
expect(click2.calls.count()).toBe(2) expect(click2.calls.count()).toBe(2)
expect(click3.calls.count()).toBe(1) expect(click3.calls.count()).toBe(1)
}) })

Loading…
Cancel
Save