compile.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. var _ = require('../util')
  2. var Directive = require('../directive')
  3. var compile = require('../compiler/compile')
  4. var transclude = require('../compiler/transclude')
  5. /**
  6. * Transclude, compile and link element.
  7. *
  8. * If a pre-compiled linker is available, that means the
  9. * passed in element will be pre-transcluded and compiled
  10. * as well - all we need to do is to call the linker.
  11. *
  12. * Otherwise we need to call transclude/compile/link here.
  13. *
  14. * @param {Element} el
  15. * @return {Element}
  16. */
  17. exports._compile = function (el) {
  18. var options = this.$options
  19. if (options._linkFn) {
  20. // pre-transcluded with linker, just use it
  21. this._initElement(el)
  22. options._linkFn(this, el)
  23. } else {
  24. // transclude and init element
  25. // transclude can potentially replace original
  26. // so we need to keep reference
  27. var original = el
  28. el = transclude(el, options)
  29. this._initElement(el)
  30. // compile and link the rest
  31. compile(el, options)(this, el)
  32. // finally replace original
  33. if (options.replace) {
  34. _.replace(original, el)
  35. }
  36. }
  37. return el
  38. }
  39. /**
  40. * Initialize instance element. Called in the public
  41. * $mount() method.
  42. *
  43. * @param {Element} el
  44. */
  45. exports._initElement = function (el) {
  46. if (el instanceof DocumentFragment) {
  47. this._isBlock = true
  48. this.$el = this._blockStart = el.firstChild
  49. this._blockEnd = el.lastChild
  50. this._blockFragment = el
  51. } else {
  52. this.$el = el
  53. }
  54. this.$el.__vue__ = this
  55. this._callHook('beforeCompile')
  56. }
  57. /**
  58. * Create and bind a directive to an element.
  59. *
  60. * @param {String} name - directive name
  61. * @param {Node} node - target node
  62. * @param {Object} desc - parsed directive descriptor
  63. * @param {Object} def - directive definition object
  64. * @param {Vue|undefined} host - transclusion host component
  65. */
  66. exports._bindDir = function (name, node, desc, def, host) {
  67. this._directives.push(
  68. new Directive(name, node, this, desc, def, host)
  69. )
  70. }
  71. /**
  72. * Teardown an instance, unobserves the data, unbind all the
  73. * directives, turn off all the event listeners, etc.
  74. *
  75. * @param {Boolean} remove - whether to remove the DOM node.
  76. * @param {Boolean} deferCleanup - if true, defer cleanup to
  77. * be called later
  78. */
  79. exports._destroy = function (remove, deferCleanup) {
  80. if (this._isBeingDestroyed) {
  81. return
  82. }
  83. this._callHook('beforeDestroy')
  84. this._isBeingDestroyed = true
  85. var i
  86. // remove self from parent. only necessary
  87. // if parent is not being destroyed as well.
  88. var parent = this.$parent
  89. if (parent && !parent._isBeingDestroyed) {
  90. i = parent._children.indexOf(this)
  91. parent._children.splice(i, 1)
  92. }
  93. // same for transclusion host.
  94. var host = this._host
  95. if (host && !host._isBeingDestroyed) {
  96. i = host._transCpnts.indexOf(this)
  97. host._transCpnts.splice(i, 1)
  98. }
  99. // destroy all children.
  100. i = this._children.length
  101. while (i--) {
  102. this._children[i].$destroy()
  103. }
  104. // teardown all directives. this also tearsdown all
  105. // directive-owned watchers. intentionally check for
  106. // directives array length on every loop since directives
  107. // that manages partial compilation can splice ones out
  108. for (i = 0; i < this._directives.length; i++) {
  109. this._directives[i]._teardown()
  110. }
  111. // teardown all user watchers.
  112. var watcher
  113. for (i in this._userWatchers) {
  114. watcher = this._userWatchers[i]
  115. if (watcher) {
  116. watcher.teardown()
  117. }
  118. }
  119. // remove reference to self on $el
  120. if (this.$el) {
  121. this.$el.__vue__ = null
  122. }
  123. // remove DOM element
  124. var self = this
  125. if (remove && this.$el) {
  126. this.$remove(function () {
  127. self._cleanup()
  128. })
  129. } else if (!deferCleanup) {
  130. this._cleanup()
  131. }
  132. }
  133. /**
  134. * Clean up to ensure garbage collection.
  135. * This is called after the leave transition if there
  136. * is any.
  137. */
  138. exports._cleanup = function () {
  139. // remove reference from data ob
  140. this._data.__ob__.removeVm(this)
  141. this._data =
  142. this._watchers =
  143. this._userWatchers =
  144. this._watcherList =
  145. this.$el =
  146. this.$parent =
  147. this.$root =
  148. this._children =
  149. this._transCpnts =
  150. this._directives = null
  151. // call the last hook...
  152. this._isDestroyed = true
  153. this._callHook('destroyed')
  154. // turn off all instance listeners.
  155. this.$off()
  156. }