compile.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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._blockStart = el.firstChild
  49. this.$el = el.childNodes[1]
  50. this._blockEnd = el.lastChild
  51. this._blockFragment = el
  52. } else {
  53. this.$el = el
  54. }
  55. this.$el.__vue__ = this
  56. this._callHook('beforeCompile')
  57. }
  58. /**
  59. * Create and bind a directive to an element.
  60. *
  61. * @param {String} name - directive name
  62. * @param {Node} node - target node
  63. * @param {Object} desc - parsed directive descriptor
  64. * @param {Object} def - directive definition object
  65. * @param {Vue|undefined} host - transclusion host component
  66. */
  67. exports._bindDir = function (name, node, desc, def, host) {
  68. this._directives.push(
  69. new Directive(name, node, this, desc, def, host)
  70. )
  71. }
  72. /**
  73. * Teardown an instance, unobserves the data, unbind all the
  74. * directives, turn off all the event listeners, etc.
  75. *
  76. * @param {Boolean} remove - whether to remove the DOM node.
  77. * @param {Boolean} deferCleanup - if true, defer cleanup to
  78. * be called later
  79. */
  80. exports._destroy = function (remove, deferCleanup) {
  81. if (this._isBeingDestroyed) {
  82. return
  83. }
  84. this._callHook('beforeDestroy')
  85. this._isBeingDestroyed = true
  86. var i
  87. // remove self from parent. only necessary
  88. // if parent is not being destroyed as well.
  89. var parent = this.$parent
  90. if (parent && !parent._isBeingDestroyed) {
  91. i = parent._children.indexOf(this)
  92. parent._children.splice(i, 1)
  93. }
  94. // same for transclusion host.
  95. var host = this._host
  96. if (host && !host._isBeingDestroyed) {
  97. i = host._transCpnts.indexOf(this)
  98. host._transCpnts.splice(i, 1)
  99. }
  100. // destroy all children.
  101. i = this._children.length
  102. while (i--) {
  103. this._children[i].$destroy()
  104. }
  105. // teardown all directives. this also tearsdown all
  106. // directive-owned watchers. intentionally check for
  107. // directives array length on every loop since directives
  108. // that manages partial compilation can splice ones out
  109. for (i = 0; i < this._directives.length; i++) {
  110. this._directives[i]._teardown()
  111. }
  112. // teardown all user watchers.
  113. var watcher
  114. for (i in this._userWatchers) {
  115. watcher = this._userWatchers[i]
  116. if (watcher) {
  117. watcher.teardown()
  118. }
  119. }
  120. // remove reference to self on $el
  121. if (this.$el) {
  122. this.$el.__vue__ = null
  123. }
  124. // remove DOM element
  125. var self = this
  126. if (remove && this.$el) {
  127. this.$remove(function () {
  128. self._cleanup()
  129. })
  130. } else if (!deferCleanup) {
  131. this._cleanup()
  132. }
  133. }
  134. /**
  135. * Clean up to ensure garbage collection.
  136. * This is called after the leave transition if there
  137. * is any.
  138. */
  139. exports._cleanup = function () {
  140. // remove reference from data ob
  141. this._data.__ob__.removeVm(this)
  142. this._data =
  143. this._watchers =
  144. this._userWatchers =
  145. this._watcherList =
  146. this.$el =
  147. this.$parent =
  148. this.$root =
  149. this._children =
  150. this._transCpnts =
  151. this._directives = null
  152. // call the last hook...
  153. this._isDestroyed = true
  154. this._callHook('destroyed')
  155. // turn off all instance listeners.
  156. this.$off()
  157. }