vue.js - 在以编程方式创建的孩子中观看道具更新
问题描述
我使用以下方法创建了孩子:
const ComponentClass = Vue.extend(someComponent);
const instance = new ComponentClass({
propsData: { prop: this.value }
})
instance.$mount();
this.$refs.container.appendChild(instance.$el);
当this.value
在父级中更新时,其值在子级中不会改变。我试过看它,但没有用。
解决方案
更新:
有一种更简单的方法可以实现这一点:
- 创建一个
<div>
- 将其附加到您的
$refs.container
.$mount()
在 div 中创建一个新的 Vue 实例- 将 div 实例的数据设置为您想要动态绑定的任何内容,从父级获取值
- 通过 render 函数从 div 的数据中为挂载的组件提供 props
methods: {
addComponent() {
const div = document.createElement("div");
this.$refs.container.appendChild(div);
new Vue({
components: { Test },
data: this.$data,
render: h => h("test", {
props: {
message: this.msg
}
})
}).$mount(div);
}
}
重要说明: this
inthis.$data
指的是父级(具有该addComponent
方法的组件),而this
insiderender
指new Vue()
的是实例。所以,反应链是:parent.$data
> new Vue().$data
> new Vue().render
=> Test.props
。我曾多次尝试绕过该new Vue()
步骤并Test
直接传递一个组件,但还没有找到方法。不过,我很确定这是可能的,尽管上面的解决方案在实践中实现了它,因为<div>
其中的new Vue()
渲染被它的模板取代,它是Test
组件。所以,在实践中,Test
是 的直系祖先$refs.container
。但实际上,它通过了一个额外的 Vue 实例,用于绑定。
显然,如果您不想在每次调用该方法时都向容器中添加一个新的子组件,您可以div
简单地放弃占位符和.$mount(this.$refs.container)
,但是这样做您将在以后每次调用该方法时替换现有的子组件。
看到它在这里工作:https ://codesandbox.io/s/nifty-dhawan-9ed2l?file=/src/components/HelloWorld.vue
但是,与下面的方法不同,您不能使用父级的值动态覆盖子级的数据道具。但是,如果你想一想,这就是data
应该工作的方式,所以只要使用props
你想要绑定的任何东西。
初步答案:
这是我在多个项目中使用过的一个函数,主要用于为地图框弹出窗口和标记创建编程组件,但对于创建组件而不实际将它们添加到 DOM 也很有用,用于各种目的。
import Vue from "vue";
// import store from "./store";
export function addProgrammaticComponent(parent, component, dataFn, componentOptions) {
const ComponentClass = Vue.extend(component);
const initData = dataFn() || {};
const data = {};
const propsData = {};
const propKeys = Object.keys(ComponentClass.options.props || {});
Object.keys(initData).forEach(key => {
if (propKeys.includes(key)) {
propsData[key] = initData[key];
} else {
data[key] = initData[key];
}
});
const instance = new ComponentClass({
// store,
data,
propsData,
...componentOptions
});
instance.$mount(document.createElement("div"));
const dataSetter = data => {
Object.keys(data).forEach(key => {
instance[key] = data[key];
});
};
const unwatch = parent.$watch(dataFn || {}, dataSetter);
return {
instance,
update: () => dataSetter(dataFn ? dataFn() : {}),
dispose: () => {
unwatch();
instance.$destroy();
}
};
}
componentOptions
是为新实例提供任何自定义(一次性)功能(即:mounted()
,观察者,计算,存储,你命名它......)。
我在这里设置了一个演示:https ://codesandbox.io/s/gifted-mestorf-297xx?file=/src/components/HelloWorld.vue
请注意,我不是appendChild
故意在函数中执行的,因为在某些情况下,我想在instance
不将其添加到 DOM 的情况下使用。常规用法是:
const component = addProgrammaticComponent(this, SomeComponent, dataFn);
this.$el.appendChild(component.instance.$el);
根据您的动态组件的作用,您可能希望.dispose()
在父级的beforeDestroy()
. 如果你不这样做,beforeDestroy()
on child 永远不会被调用。
可能最酷的部分是您实际上不需要将孩子附加到父母的 DOM(它可以放置在 DOM 中的任何位置,并且孩子仍然会响应父母的任何更改,就像它是实际的后代)。他们的“链接”是程序化的,通过dataFn
.
显然,这为一系列潜在问题打开了大门,尤其是在摧毁父母而不摧毁孩子方面。因此,您需要非常小心和彻底地进行此类清理。您可以将每个动态组件注册到父级的属性中并将.dispose()
它们全部注册到父级的属性中,beforeDestroy()
或者给它们一个特定的选择器并在销毁父级之前清除整个 DOM。
另一个有趣的注意事项是,在 Vue 3 中,以上所有内容都不再是必需的,因为大多数核心 Vue 功能(反应性、计算、钩子、侦听器)现在都可以按原样公开和重用,所以你$mount
不必组件以访问其“魔法”。
推荐阅读
- android - 仅支持 armeabi 和 x86 的 android 库的问题
- java - 自定义对象作为 Map 键
- c# - 单击按钮时调用控制器方法时需要将文本框值作为参数传递
- java - 卡桑德拉没有开始
- google-sheets - 如何将输入添加到运行列表
- python - Django serializer.data 包含不存在的值
- php - 从 MySQL 数据库中批量获取大数据(就像 PhpStorm 那样)
- python - Python中是否有任何用于聚合秒级毫秒数据的函数?
- optimization - 在 PuLP 的计划优化中添加 7 天约束
- c++ - 从守护进程启动的控制台应用程序捕获输出