dd2f1a5ed7c125aef212f8599dfe2a38b4ff592ad1996004f6ea6dae2036a52bbfadb8eac762ff4da490e01b561f5542a364069e0d9fcd8e9deba53a0fca96 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /* @flow */
  2. import { emptyNode } from 'core/vdom/patch'
  3. import { resolveAsset, handleError } from 'core/util/index'
  4. import { mergeVNodeHook } from 'core/vdom/helpers/index'
  5. export default {
  6. create: updateDirectives,
  7. update: updateDirectives,
  8. destroy: function unbindDirectives (vnode: VNodeWithData) {
  9. updateDirectives(vnode, emptyNode)
  10. }
  11. }
  12. function updateDirectives (oldVnode: VNodeWithData, vnode: VNodeWithData) {
  13. if (oldVnode.data.directives || vnode.data.directives) {
  14. _update(oldVnode, vnode)
  15. }
  16. }
  17. function _update (oldVnode, vnode) {
  18. const isCreate = oldVnode === emptyNode
  19. const isDestroy = vnode === emptyNode
  20. const oldDirs = normalizeDirectives(oldVnode.data.directives, oldVnode.context)
  21. const newDirs = normalizeDirectives(vnode.data.directives, vnode.context)
  22. const dirsWithInsert = []
  23. const dirsWithPostpatch = []
  24. let key, oldDir, dir
  25. for (key in newDirs) {
  26. oldDir = oldDirs[key]
  27. dir = newDirs[key]
  28. if (!oldDir) {
  29. // new directive, bind
  30. callHook(dir, 'bind', vnode, oldVnode)
  31. if (dir.def && dir.def.inserted) {
  32. dirsWithInsert.push(dir)
  33. }
  34. } else {
  35. // existing directive, update
  36. dir.oldValue = oldDir.value
  37. dir.oldArg = oldDir.arg
  38. callHook(dir, 'update', vnode, oldVnode)
  39. if (dir.def && dir.def.componentUpdated) {
  40. dirsWithPostpatch.push(dir)
  41. }
  42. }
  43. }
  44. if (dirsWithInsert.length) {
  45. const callInsert = () => {
  46. for (let i = 0; i < dirsWithInsert.length; i++) {
  47. callHook(dirsWithInsert[i], 'inserted', vnode, oldVnode)
  48. }
  49. }
  50. if (isCreate) {
  51. mergeVNodeHook(vnode, 'insert', callInsert)
  52. } else {
  53. callInsert()
  54. }
  55. }
  56. if (dirsWithPostpatch.length) {
  57. mergeVNodeHook(vnode, 'postpatch', () => {
  58. for (let i = 0; i < dirsWithPostpatch.length; i++) {
  59. callHook(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode)
  60. }
  61. })
  62. }
  63. if (!isCreate) {
  64. for (key in oldDirs) {
  65. if (!newDirs[key]) {
  66. // no longer present, unbind
  67. callHook(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy)
  68. }
  69. }
  70. }
  71. }
  72. const emptyModifiers = Object.create(null)
  73. function normalizeDirectives (
  74. dirs: ?Array<VNodeDirective>,
  75. vm: Component
  76. ): { [key: string]: VNodeDirective } {
  77. const res = Object.create(null)
  78. if (!dirs) {
  79. // $flow-disable-line
  80. return res
  81. }
  82. let i, dir
  83. for (i = 0; i < dirs.length; i++) {
  84. dir = dirs[i]
  85. if (!dir.modifiers) {
  86. // $flow-disable-line
  87. dir.modifiers = emptyModifiers
  88. }
  89. res[getRawDirName(dir)] = dir
  90. dir.def = resolveAsset(vm.$options, 'directives', dir.name, true)
  91. }
  92. // $flow-disable-line
  93. return res
  94. }
  95. function getRawDirName (dir: VNodeDirective): string {
  96. return dir.rawName || `${dir.name}.${Object.keys(dir.modifiers || {}).join('.')}`
  97. }
  98. function callHook (dir, hook, vnode, oldVnode, isDestroy) {
  99. const fn = dir.def && dir.def[hook]
  100. if (fn) {
  101. try {
  102. fn(vnode.elm, dir, vnode, oldVnode, isDestroy)
  103. } catch (e) {
  104. handleError(e, vnode.context, `directive ${dir.name} ${hook} hook`)
  105. }
  106. }
  107. }