无法访问 Vue 组件的$el,除非在$mounted上



尝试遍历 Vue 组件数组并访问每个组件的 DOM 元素。这似乎是一个相当基本的需求,但我已经搜索了很多,尽管在同一一般领域有很多问题,但我还没有想出解决这个问题的方法。

上下文:我的应用将访问 DOM 元素以根据应用逻辑设置文本样式。在这篇文章中,我创建了一个精简的最小应用程序来重现我的问题,因此它不做任何样式,而只是显示元素的 innerText。

JSFiddle 或请参阅下面的相同代码。您将看到此代码仅在 $mounted 事件处理程序中短暂地访问组件的$el。之后它是未定义的。我可以在事件处理程序中保存引用,但认为这是必要的似乎很疯狂......我错过了什么?

.HTML

<div id="app"
oncontextmenu="return false" 
v-on:mouseup="showchars()"
>
<h2>The components a, b and c will be here once mounted. Note in the initial dialogs that their $el is accessible when their mount event handler calls showchars. Then click this div to call showchars again, and note that $el is no longer defined.</h2>
<charcomponent 
v-for = "c in charcomponents"
v-bind:char="c.char"
></charcomponent>
</div>

.JS

let CharComponent = {
props: ['char'],
mounted: function() {
this.showinfo("it's like this on mount");
},
methods: {
showinfo: function(cmt) {
try {
alert(cmt + ': char=' + this.char +', el=' + this.$el + ', and...');
alert('...el.innerText=' + this.$el.innerText);
}
catch(error) {
alert(error);
}
}  
},
template: '<p>{{ char }}</p>'  
}
var vm = new Vue({
el: "#app",
components: {
charcomponent: CharComponent
},
data: {
charcomponents: []
},
methods: {
showchars() {
for (var c of this.charcomponents) {
c.showinfo("it's different later");      
}
}
}
})
var ccharcomp = Vue.extend(CharComponent);
for (var ch of ['a','b','c']) {
var ocharcomp = new ccharcomp({
propsData: {
char: ch
}
});
vm.charcomponents.push(ocharcomp);
}
//alert(vm.chars.length);

我可以尝试解释正在发生的事情,尽管我有点不清楚你为什么要做你正在做的事情。

让我们从这里开始:

var ocharcomp = new ccharcomp({
propsData: {
char: ch
}
});
vm.charcomponents.push(ocharcomp);

这是创建ccharcomp组件的新实例,这实际上只是CharComponent。然后将其中的每一个都添加到数组中。

这些 Vue 实例都没有被挂载。它们永远不会被渲染。它们只是被创建并推送到数组上。他们不会有$elmounted钩子永远不会被调用。

然后在您的模板中,您有以下内容:

<charcomponent 
v-for = "c in charcomponents"
v-bind:char="c.char"
></charcomponent>

这将循环访问同一数组,并为每个条目创建一个新的CharComponent实例。char的值将从数组中的每个组件复制到模板中创建的相应组件。

在模板中创建的组件将被渲染、挂载并具有$el。您从mounted钩子在日志记录中看到的就是这一点。

然后我们有这个:

showchars() {
for (var c of this.charcomponents) {
c.showinfo("it's different later");      
}
}

这是循环访问原始组件数组,即从未挂载的组件。这些没有$el,他们从来没有。模板创建的组件仍然存在,并且仍然具有$el但它们不在数组中。

我无法就如何解决您的问题提出具体建议,因为我真的不明白您为什么要以如此奇怪的方式创建子组件。更正常的模式是:

  1. 将数组['a','b','c']在相关父组件的data中。
  2. 在模板中循环遍历该数组以创建子数组。
  3. 使用模板中的ref和父组件中的$refs来访问子组件,然后使用$el获取每个子组件的元素。

但是,如果您只想应用样式,那么通常不鼓励像这样抓取元素。相反,你应该在模板中使用:class:style绑定,让 Vue 为你应用样式。您可能需要引入额外的data属性来保存基础状态,以便模板可以确定在何处应用哪些样式。

通过此方法实例化的组件(声明性):

<charcomponent 
v-for = "c in charcomponents"
v-bind:char="c.char"
></charcomponent>

与您在此处创建的那些不同(编程):

var ccharcomp = Vue.extend(CharComponent);
for (var ch of ['a','b','c']) {
var ocharcomp = new ccharcomp({
propsData: {
char: ch
}
});
vm.charcomponents.push(ocharcomp);
}

初始警报系列由挂载的钩子从第一组组件(声明性)触发。当然,$el对象之所以存在在这里,是因为标记已经是 DOM 的一部分,并且它还使用了您在 Vue 实例中的本地声明

components: {
charcomponent: CharComponent 
},

对于存储在名为 charcomponents 的数组中的第二组组件,没有任何挂载的元素。请注意,当你使用 Vue.extend 时,只创建了 Vue 实例的一个子类,因此必须通过 $mount 方法挂载来自实例化的新对象。

showchars() {
for (var c of this.charcomponents) {
c.showinfo("it's different later");      
}
}

实际上,访问第二批组件的$el元素尚未定义。

我在 JSFiddle 中创建一个简单的实现来展示不同的实现。

@skirtle和@Jose Mari Ponce的非常翔实的回答帮助我明白为什么我所拥有的不起作用。我打算再次查看模板中的类/样式绑定,作为完成此特定应用程序所需内容的更好方法,但我也想遵循这一点,因为它似乎还需要循环访问已知的组件列表并访问$el,因为应用程序逻辑需要确定模板绑定到的值。我也认为除了这个应用程序之外,它具有更广泛的适用性。

事实证明,这只是用JSappendChild替换我的HTMLv-for的问题,并将动态创建的组件挂载到新的子元素上:

.HTML

<div id="components">
</div>

.JS

var ccharcomp = Vue.extend(CharComponent);
var container = document.getElementById("components");
for (var ch of ['a','b','c']) {
var p = document.createElement("p");
container.appendChild(p);
var ocharcomp = new ccharcomp({
propsData: {
char: ch
}
});
vm.charcomponents.push(ocharcomp.$mount(p));
}

JSFiddle

最新更新