Vue.js 组件的 vm.$el 是常量还是可以重新分配?

ber*_*nie 5 javascript vue.js vuejs2

Vue.js 组件实例有一个vm.$em属性,可以访问组件的根 DOM 元素。此属性的在组件的生命周期中是否会发生变化,还是在组件安装后保持不变?换句话说,一旦创建了一个组件的根 DOM 元素,它是否可以在某些情况下被 Vue 替换?

我知道 DOM 元素的内容当然可以改变。

根据下面的组件生命周期图,当数据发生变化时,会出现“虚拟 DOM 重新渲染和补丁”。我阅读了文档并试图在 Vue 的源代码中找到答案,但到目前为止我还没有找到任何决定性的答案。

我最接近源代码的是src/core/instance/lifecycle.js

Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {
  const vm: Component = this
  const prevEl = vm.$el
  const prevVnode = vm._vnode
  const restoreActiveInstance = setActiveInstance(vm)
  vm._vnode = vnode
  // Vue.prototype.__patch__ is injected in entry points
  // based on the rendering backend used.
  if (!prevVnode) {
    // initial render
    vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)
  } else {
    // updates
    vm.$el = vm.__patch__(prevVnode, vnode)
  }
  restoreActiveInstance()
  // update __vue__ reference
  if (prevEl) {
    prevEl.__vue__ = null
  }
  if (vm.$el) {
    vm.$el.__vue__ = vm
  }
  // etc.
}
Run Code Online (Sandbox Code Playgroud)

这表明vm.$el可以在每个组件渲染上重新分配,但__patch__()到目前为止我还没有破译它的工作原理以及_update()确切的调用时间。

何苦?

我有一些设置任务要直接在根 DOM 元素上完成(例如设置一个 jQuery 插件)。仅在mounted钩子中执行此操作是否足够(如果vm.$el是常量,则是),还是必须在检测到updated更改时在钩子中执行此操作vm.$el

Vue.js 生命周期图

Eua*_*ith 1

如果您完全控制组件内容,那么您可能会认为它vm.$el不会改变。如果你正在制作(例如)一个 mixin 或指令,那么你就不能 - 完全有可能制作一个可以通过使用 is 来更改顶级元素的组件来更改顶级元素的组件。

\n

例如,这定义了一个可以更改其顶级元素类型的组件:

\n
Vue.component(\'comp\',{\n  props:{type:String},\n  template:`\n    <component :is="type">\n       I am a {{type}}\n    </component>\n  `\n})\n
Run Code Online (Sandbox Code Playgroud)\n

这是一个演示它的小提琴: https ://jsfiddle.net/tazfLqbh/1/

\n

作为一个好的实践,我建议假设它可以改变,因为该行为似乎没有在文档中正式定义,所以行为可以随着未来的版本而改变。

\n

小提琴分析

\n

问题仍然是,当切换类型时,fiddle 中的动态组件是否会触发新 Vue 组件的创建。在开发控制台中:

\n
// "Inspect" the span\n> a = $0.__vue__\nVueComponent\xc2\xa0{_uid: 1, _isVue: true, $options: {\xe2\x80\xa6}, _renderProxy: Proxy, _self: VueComponent,\xc2\xa0\xe2\x80\xa6}\n// Toggle the type to div and "inspect" the div\n> b = $0.__vue__\nVueComponent\xc2\xa0{_uid: 1, _isVue: true, $options: {\xe2\x80\xa6}, _renderProxy: Proxy, _self: VueComponent,\xc2\xa0\xe2\x80\xa6}\n> a === b\ntrue\n
Run Code Online (Sandbox Code Playgroud)\n

因此,在切换根元素类型时不会创建新的 Vue 组件实例。

\n

正如预期的那样,这个小提琴显示动态组件类型确实会触发新的 Vue 组件实例的创建。在这种情况下,看来mounted就足够了。像这样的东西:

\n
<div id="app">\n  <button @click="toggle">\n    Toggle div or span element\n  </button>\n  <component :is="span?\'comp-span\':\'comp-div\'"></component>\n</div>\n\nVue.component(\'comp-span\',{\n  template:`\n    <span>\n       I am a span\n    </span>\n  `\n})\n\nVue.component(\'comp-div\',{\n  template:`\n    <div>\n       I am a div\n    </div>\n  `\n})\n
Run Code Online (Sandbox Code Playgroud)\n