compile.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. var _ = require('../util')
  2. var Directive = require('../directive')
  3. var compiler = require('../compiler')
  4. /**
  5. * Transclude, compile and link element.
  6. *
  7. * If a pre-compiled linker is available, that means the
  8. * passed in element will be pre-transcluded and compiled
  9. * as well - all we need to do is to call the linker.
  10. *
  11. * Otherwise we need to call transclude/compile/link here.
  12. *
  13. * @param {Element} el
  14. * @return {Element}
  15. */
  16. exports._compile = function (el) {
  17. var options = this.$options
  18. var host = this._host
  19. if (options._linkFn) {
  20. // pre-transcluded with linker, just use it
  21. this._initElement(el)
  22. this._unlinkFn = options._linkFn(this, el, host)
  23. } else {
  24. // transclude and init element
  25. // transclude can potentially replace original
  26. // so we need to keep reference; this step also injects
  27. // the template and caches the original attributes
  28. // on the container node and replacer node.
  29. var original = el
  30. el = compiler.transclude(el, options)
  31. this._initElement(el)
  32. // root is always compiled per-instance, because
  33. // container attrs and props can be different every time.
  34. var rootLinker = compiler.compileRoot(el, options)
  35. // compile and link the rest
  36. var contentLinkFn
  37. var ctor = this.constructor
  38. // component compilation can be cached
  39. // as long as it's not using inline-template
  40. if (options._linkerCachable) {
  41. contentLinkFn = ctor.linker
  42. if (!contentLinkFn) {
  43. contentLinkFn = ctor.linker = compiler.compile(el, options)
  44. }
  45. }
  46. // link phase
  47. var rootUnlinkFn = rootLinker(this, el)
  48. var contentUnlinkFn = contentLinkFn
  49. ? contentLinkFn(this, el)
  50. : compiler.compile(el, options)(this, el, host)
  51. // register composite unlink function
  52. // to be called during instance destruction
  53. this._unlinkFn = function () {
  54. rootUnlinkFn()
  55. // passing destroying: true to avoid searching and
  56. // splicing the directives
  57. contentUnlinkFn(true)
  58. }
  59. // finally replace original
  60. if (options.replace) {
  61. _.replace(original, el)
  62. }
  63. }
  64. return el
  65. }
  66. /**
  67. * Initialize instance element. Called in the public
  68. * $mount() method.
  69. *
  70. * @param {Element} el
  71. */
  72. exports._initElement = function (el) {
  73. if (el instanceof DocumentFragment) {
  74. this._isFragment = true
  75. this.$el = this._fragmentStart = el.firstChild
  76. this._fragmentEnd = el.lastChild
  77. // set persisted text anchors to empty
  78. if (this._fragmentStart.nodeType === 3) {
  79. this._fragmentStart.data = this._fragmentEnd.data = ''
  80. }
  81. this._blockFragment = el
  82. } else {
  83. this.$el = el
  84. }
  85. this.$el.__vue__ = this
  86. this._callHook('beforeCompile')
  87. }
  88. /**
  89. * Create and bind a directive to an element.
  90. *
  91. * @param {String} name - directive name
  92. * @param {Node} node - target node
  93. * @param {Object} desc - parsed directive descriptor
  94. * @param {Object} def - directive definition object
  95. * @param {Vue|undefined} host - transclusion host component
  96. */
  97. exports._bindDir = function (name, node, desc, def, host, scope) {
  98. this._directives.push(
  99. new Directive(name, node, this, desc, def, host, scope)
  100. )
  101. }
  102. /**
  103. * Teardown an instance, unobserves the data, unbind all the
  104. * directives, turn off all the event listeners, etc.
  105. *
  106. * @param {Boolean} remove - whether to remove the DOM node.
  107. * @param {Boolean} deferCleanup - if true, defer cleanup to
  108. * be called later
  109. */
  110. exports._destroy = function (remove, deferCleanup) {
  111. if (this._isBeingDestroyed) {
  112. return
  113. }
  114. this._callHook('beforeDestroy')
  115. this._isBeingDestroyed = true
  116. var i
  117. // remove self from parent. only necessary
  118. // if parent is not being destroyed as well.
  119. var parent = this.$parent
  120. if (parent && !parent._isBeingDestroyed) {
  121. parent.$children.$remove(this)
  122. }
  123. // destroy all children.
  124. i = this.$children.length
  125. while (i--) {
  126. this.$children[i].$destroy()
  127. }
  128. // teardown props
  129. if (this._propsUnlinkFn) {
  130. this._propsUnlinkFn()
  131. }
  132. // teardown all directives. this also tearsdown all
  133. // directive-owned watchers.
  134. if (this._unlinkFn) {
  135. this._unlinkFn()
  136. }
  137. i = this._watchers.length
  138. while (i--) {
  139. this._watchers[i].teardown()
  140. }
  141. // remove reference to self on $el
  142. if (this.$el) {
  143. this.$el.__vue__ = null
  144. }
  145. // remove DOM element
  146. var self = this
  147. if (remove && this.$el) {
  148. this.$remove(function () {
  149. self._cleanup()
  150. })
  151. } else if (!deferCleanup) {
  152. this._cleanup()
  153. }
  154. }
  155. /**
  156. * Clean up to ensure garbage collection.
  157. * This is called after the leave transition if there
  158. * is any.
  159. */
  160. exports._cleanup = function () {
  161. // remove reference from data ob
  162. // frozen object may not have observer.
  163. if (this._data.__ob__) {
  164. this._data.__ob__.removeVm(this)
  165. }
  166. // Clean up references to private properties and other
  167. // instances. preserve reference to _data so that proxy
  168. // accessors still work. The only potential side effect
  169. // here is that mutating the instance after it's destroyed
  170. // may affect the state of other components that are still
  171. // observing the same object, but that seems to be a
  172. // reasonable responsibility for the user rather than
  173. // always throwing an error on them.
  174. this.$el =
  175. this.$parent =
  176. this.$root =
  177. this.$children =
  178. this._watchers =
  179. this._directives = null
  180. // call the last hook...
  181. this._isDestroyed = true
  182. this._callHook('destroyed')
  183. // turn off all instance listeners.
  184. this.$off()
  185. }