vue3等效于vnode.computedInstance

发布于 2025-02-01 12:23:28 字数 2384 浏览 0 评论 0 原文

我正在将我的应用程序从vue2转换为vue3,但在我的形式的一个方面停滞不前。

我正在使用SFC用于Form Element组件(FormInput,FormTextArea,FormCheckBox等),并使用插槽将它们传递到形式的容器组件(FormGroup,FormTab等)中,例如:

<ssi-form>
  <ssi-form-tabs>
    <ssi-form-tab title="tab1">
      <ssi-form-input title="name" ... />
      <ssi-form-checkbox title="active" ... />
    </ssi-form-tab>
  </ssi-form-tabs>
</ssi-form>

这些父容器需要查看一些计算的属性孩子形成元素,将错误消息拉到顶部。

在VUE2中,我使用已安装的生命周期挂钩(带有选项API)读取插槽并访问计算的属性,例如:

  mounted: function() {
    const vm = this;

    this.$slots.default.forEach((vNode) => {
      const vueComponent = vNode.componentInstance;
      vueComponent.$on("invalid", vm.onInvalid);

      if (vueComponent.errorCount === undefined) return;
      this.childFormElements.push(vueComponent);
    });
  },

使用此设置,我可以从每个孩子中获取 errorcount 从每个孩子中的计算属性插槽,因此我可以将错误汇总到表单的最高级别。

现在,我要切换到VUE3,似乎 componentInstance 不存在。我尝试使用 OnMounted 指令设置类似的逻辑,但是当我访问插槽元素时,我找不到任何方法来查看其 errorcount 计算属性

onMounted(() => { 
  slots.default().forEach((vNode) => {
    console.log(vNode);
  });
});

:不包含计算属性。我认为当我阅读 Define Expose 时,我发现了一些有用的东西,但是即使公开了 errorcount 属性,也没有出现。

这是我试图使用的文本输入的&lt; script&gt;

<script setup lang="ts">
import { ref, defineProps, defineEmits, computed } from "vue";

let props = defineProps<{
  label: string, 
  id: string, 
  modelValue: string|number, 
  type?: string, 
  description?: string, 
  min?: string|number, 
  max?: string|number, 
  pattern?: string, 
  message?: string
}>();
let emit = defineEmits(["input", "update:modelValue", "invalid"]);

let state = ref(null);
let error = ref("");

const input = ref(null);

function onInput(event: Event) {
  validate();
  emit('update:modelValue', event.target.value)
}

// methods
function validate() {
  let inputText = input.value;
  if (inputText === null) return;

  inputText.checkValidity();
  state.value = inputText.validity.valid;
  error.value = inputText.validationMessage;
}

const errorCount = computed(() => {
  return state.value === false ? 1 : 0;
});

defineExpose({errorCount})

</script>

因此,问题是 - 父组件如何读取 errorcycount 从放置在插槽中的组件中的属性?

I'm in the process of converting my app from Vue2 to Vue3, but have gotten stalled out on one aspect of my forms.

I'm using SFCs for form element components (FormInput, FormTextArea, FormCheckbox, etc.), and passing them into form container components (FormGroup, FormTab, etc) using slots, like so:

<ssi-form>
  <ssi-form-tabs>
    <ssi-form-tab title="tab1">
      <ssi-form-input title="name" ... />
      <ssi-form-checkbox title="active" ... />
    </ssi-form-tab>
  </ssi-form-tabs>
</ssi-form>

Those parent containers need to view some computed properties of the child form elements to pull error messages to the top.

In Vue2, I used the mounted lifecycle hook (with the options API) to read the slots and access the computed properties, like this:

  mounted: function() {
    const vm = this;

    this.$slots.default.forEach((vNode) => {
      const vueComponent = vNode.componentInstance;
      vueComponent.$on("invalid", vm.onInvalid);

      if (vueComponent.errorCount === undefined) return;
      this.childFormElements.push(vueComponent);
    });
  },

Using this setup, I could grab the errorCount computed property from each child in the slot, so I could aggregate errors going up to the top level of the form.

Now that I'm switching to Vue3, it seems like componentInstance doesn't exist. I tried setting up similar logic using the onMounted directive, but when I access the slot elements, I can't find any way to see their errorCount computed property:

onMounted(() => { 
  slots.default().forEach((vNode) => {
    console.log(vNode);
  });
});

The logged object does not contain the computed property. I thought I found something useful when I read about defineExpose, but even after exposing the errorCount property, nothing comes up.

Here is the <script setup> from the SFC for the text input that I'm trying to work with:

<script setup lang="ts">
import { ref, defineProps, defineEmits, computed } from "vue";

let props = defineProps<{
  label: string, 
  id: string, 
  modelValue: string|number, 
  type?: string, 
  description?: string, 
  min?: string|number, 
  max?: string|number, 
  pattern?: string, 
  message?: string
}>();
let emit = defineEmits(["input", "update:modelValue", "invalid"]);

let state = ref(null);
let error = ref("");

const input = ref(null);

function onInput(event: Event) {
  validate();
  emit('update:modelValue', event.target.value)
}

// methods
function validate() {
  let inputText = input.value;
  if (inputText === null) return;

  inputText.checkValidity();
  state.value = inputText.validity.valid;
  error.value = inputText.validationMessage;
}

const errorCount = computed(() => {
  return state.value === false ? 1 : 0;
});

defineExpose({errorCount})

</script>

So the question is - how can a parent component read the errorCount property from a component placed into a slot?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

奢欲 2025-02-08 12:23:28

没有充分的理由不应使用内部属性,尤其是因为它们不属于公共API,并且可以改变其行为或无通知而被删除。一个最好的解决方案是可以通过公共API实现目标的解决方案。

这里的解决方案是 process插槽在容器的呈现器组件中。 vNode对象是渲染元素的模板,并且可以在这一点上进行转换,例如,可以添加来自容器组件范围的REF。

如果需要访问儿童组件的实例以添加事件侦听器,则它们此时:

const slots = useSlots();
const inputRefs = reactive([]);

return () => {
  const children = slots.default?.() || [];
  inputRefs.length = children
    .filter(vnode => vnode.type === InputComponent)
    .length;

  return children.map((vnode, index) => {
    if (vnode.type === InputComponent) {
      return cloneVNode(vnode, {
        ref: el => inputRefs[index] = el,
        onInvalid: invalidHandler
      });
    } else {
      return vnode;
    }
  );
}

其中 ref is ref函数维护了一系列儿童参考,尽管不需要添加事件侦听器的唯一目的。

Internal properties shouldn't be used without a good reason, especially because they don't belong to public API and can change their behaviour or be removed without notice. A preferable solution is the one that can achieve the goal by means of public API.

Here the solution is to process slots in container component's render function. Vnode objects are the templates for rendered elements and they can be transformed at this point, e.g. a ref from the scope of container component can be added.

If the instances of child components need to be accessed to add event listeners, they can be added to a vnode at this point:

const slots = useSlots();
const inputRefs = reactive([]);

return () => {
  const children = slots.default?.() || [];
  inputRefs.length = children
    .filter(vnode => vnode.type === InputComponent)
    .length;

  return children.map((vnode, index) => {
    if (vnode.type === InputComponent) {
      return cloneVNode(vnode, {
        ref: el => inputRefs[index] = el,
        onInvalid: invalidHandler
      });
    } else {
      return vnode;
    }
  );
}

Where ref is ref function that maintains a collection of children refs, although it's not needed for the sole purpose of adding event listeners.

如日中天 2025-02-08 12:23:28

我相信这是不可行的,因为它已经被认为是不良练习 以来,“ Nofollow Noreferrer”>。

DefineExpose 在这种情况下确实有帮助,但是由于您说

我可以从插槽中的每个孩子中获取 errorcount 计算的属性

提供 /注射可能是更好的方法?

I believe this is not doable, since it has already been considered a bad practice since Vue 2.

defineExpose does help in such a situation, but since you said

I could grab the errorCount computed property from each child in the slot

maybe Provide / Inject could be a better approach?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文