Skip to content

feat(runtime-core): directive install hook #4235

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions packages/runtime-core/__tests__/directives.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,4 +395,81 @@ describe('directives', () => {
expect(beforeUpdate).toHaveBeenCalledTimes(1)
expect(count.value).toBe(1)
})

it('directive install hooks', async () => {
const count = ref(0)
let d1_installed_created = jest.fn()
let d1_installed_mounted = jest.fn()
let d1_installed_beforeMounted = jest.fn()
let d1_installed_beforeupdate = jest.fn()
let d1_installed_updated = jest.fn()
let d1_installed_beforeUnmount = jest.fn()
let d1_installed_unmounted = jest.fn()

let mounted_overwritten = jest.fn()

const d1 = {
install() {
return {
created: d1_installed_created,
mounted: d1_installed_mounted,
beforeMount: d1_installed_beforeMounted,
beforeUpdate: d1_installed_beforeupdate,
updated: d1_installed_updated,
beforeUnmount: d1_installed_beforeUnmount,
unmounted: d1_installed_unmounted
}
},
mounted: mounted_overwritten
}

// test that install doesn't override hooks that it does not provide.
let d2_created = jest.fn()
let d2_installed_mounted = jest.fn()
let d2_install = jest
.fn()
.mockReturnValue({ mounted: d2_installed_mounted })
const d2 = {
install: d2_install,
created: d2_created
}

const Comp = {
render() {
return withDirectives(h('div', [count.value]), [[d1], [d2]])
}
}

const App = {
name: 'App',
render() {
return h('div', [h(Comp)])
}
}

const root = nodeOps.createElement('div')
render(h(App), root)

count.value++

await nextTick()

// all installed hooks called
expect(d1_installed_created).toHaveBeenCalled()
expect(d1_installed_mounted).toHaveBeenCalled()
expect(d1_installed_beforeMounted).toHaveBeenCalled()
expect(d1_installed_beforeupdate).toHaveBeenCalled()
expect(d1_installed_updated).toHaveBeenCalled()

expect(mounted_overwritten).not.toHaveBeenCalled()

expect(d2_created).toHaveBeenCalled()
expect(d2_install).toHaveBeenCalled()
expect(d2_installed_mounted).toHaveBeenCalled()

render(null, root)

expect(d1_installed_beforeUnmount).toHaveBeenCalledTimes(1)
expect(d1_installed_unmounted).toHaveBeenCalledTimes(1)
})
})
11 changes: 8 additions & 3 deletions packages/runtime-core/src/directives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ return withDirectives(h(comp), [
*/

import { VNode } from './vnode'
import { isFunction, EMPTY_OBJ, makeMap } from '@vue/shared'
import { isFunction, EMPTY_OBJ, makeMap, extend } from '@vue/shared'
import { warn } from './warning'
import { ComponentInternalInstance, Data } from './component'
import { currentRenderingInstance } from './componentRenderContext'
Expand Down Expand Up @@ -53,6 +53,7 @@ export interface ObjectDirective<T = any, V = any> {
unmounted?: DirectiveHook<T, null, V>
getSSRProps?: SSRDirectiveHook
deep?: boolean
install?: (binding: DirectiveBinding) => ObjectDirective
}

export type FunctionDirective<T = any, V = any> = DirectiveHook<T, any, V>
Expand Down Expand Up @@ -106,14 +107,18 @@ export function withDirectives<T extends VNode>(
if (dir.deep) {
traverse(value)
}
bindings.push({
let binding = {
dir,
instance,
value,
oldValue: void 0,
arg,
modifiers
})
}
if (dir.install) {
extend(binding.dir, dir.install(binding))
}
bindings.push(binding)
}
return vnode
}
Expand Down