gpt4 book ai didi

javascript - 我可以修改 Vue.js VNodes 吗?

转载 作者:数据小太阳 更新时间:2023-10-29 04:48:43 24 4
gpt4 key购买 nike

我想为 child 分配一些属性和类 VNode通过 data 目的。那只是有效。但是在我的 Vue.js 调查中,我没有看到使用这种模式,这就是为什么我认为修改 children 不是一个好主意 VNode的。

但这种方法有时会派上用场——例如,我想为默认插槽中的所有按钮分配 aria-label属性。

请参见下面的示例,使用默认的有状态组件:

Vue.component('child', {
template: '<div>My role is {{ $attrs.role }}</div>',
})

Vue.component('parent', {
render(h) {
const {
default: defaultSlot
} = this.$slots

if (defaultSlot) {
defaultSlot.forEach((child, index) => {
if (!child.data) child.data = {}
if (!child.data.attrs) child.data.attrs = {}

const {
data
} = child

data.attrs.role = 'button'
data.class = 'bar'
data.style = `color: #` + index + index + index
})
}

return h(
'div', {
class: 'parent',
},
defaultSlot,
)
},
})

new Vue({
el: '#app',
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<parent>
<child></child>
<child></child>
<child></child>
<child></child>
<child></child>
</parent>
</div>


这是使用 的示例无国籍功能组件:

Vue.component('child', {
functional: true,
render(h, {
children
}) {
return h('div', {
class: 'bar'
}, children)
},
})

Vue.component('parent', {
functional: true,
render(h, {
scopedSlots
}) {
const defaultScopedSlot = scopedSlots.default({
foo: 'bar'
})

if (defaultScopedSlot) {
defaultScopedSlot.forEach((child, index) => {
child.data = {
style: `color: #` + index + index + index
}

child.data.attrs = {
role: 'whatever'
}
})
}
return h(
'div', {
class: 'parent',
},
defaultScopedSlot,
)
},
})

new Vue({
el: '#app',
})
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<parent>
<template v-slot:default="{ foo }">
<child>{{ foo }}</child>
<child>{{ foo }}</child>
<child>{{ foo }}</child>
</template>
</parent>
</div>


我正在等待以下答案:
  • 是的,您可以使用它,这种方法没有潜在的问题。
  • 是的,但这些问题可能会发生。
  • 不,有很多问题。

  • 更新 :

    我发现的另一种好方法是包裹 child VNode进入另一个创建 VNode使用适当的数据对象,如下所示:

    const wrappedChildren = children.map(child => {
    return h("div", { class: "foo" }, [child]);
    });

    使用这种方法,我不怕修改 child VNode的。

    先感谢您。

    最佳答案

    这样做有潜在的问题。非常谨慎地使用它可能是一种有用的技术,如果没有简单的替代方案可用,我个人很乐意使用它。但是,您处于未记录的领域,如果出现问题,您可能必须通过单步调试 Vue 内部来进行调试。它不适合胆小的人。

    首先,一些类似的东西被其他人使用的例子。

  • 补丁 key :
    https://medium.com/dailyjs/patching-the-vue-js-virtual-dom-the-need-the-explanation-and-the-solution-ba18e4ae385b
  • Vuetify 从 mixin 修补 VNode 的示例:
    https://github.com/vuetifyjs/vuetify/blob/5329514763e7fab11994c4303aa601346e17104c/packages/vuetify/src/components/VImg/VImg.ts#L219
  • Vuetify 从作用域插槽修补 VNode 的示例:https://github.com/vuetifyjs/vuetify/blob/7f7391d76dc44f7f7d64f30ad7e0e429c85597c8/packages/vuetify/src/components/VItemGroup/VItem.ts#L58

  • 我认为只有第三个例子才能真正与这个问题中的补丁相媲美。一个关键特性是它使用了作用域插槽而不是普通插槽,因此 VNode 是在同一个 render 中创建的。功能。

    使用普通插槽会变得更加复杂。问题是插槽的 VNode 是在父级的 render 中创建的。功能。如果 child 的 render函数多次运行,它只会不断为插槽传递相同的 VNode。修改这些 VNode 不一定会做您期望的事情,因为差异算法只会看到相同的 VNode 而不会执行任何 DOM 更新。

    这里有一个例子来说明:

    const MyRenderComponent = {
    data () {
    return {
    blueChildren: true
    }
    },

    render (h) {
    // Add a button before the slot children
    const children = [h('button', {
    on: {
    click: () => {
    this.blueChildren = !this.blueChildren
    }
    }
    }, 'Blue children: ' + this.blueChildren)]

    const slotContent = this.$slots.default

    for (const child of slotContent) {
    if (child.data && child.data.class) {
    // Add/remove the CSS class 'blue'
    child.data.class.blue = this.blueChildren

    // Log it out to confirm this really is happening
    console.log(child.data.class)
    }

    children.push(child)
    }

    return h('div', null, children)
    }
    }

    new Vue({
    el: '#app',

    components: {
    MyRenderComponent
    },

    data () {
    return {
    count: 0
    }
    }
    })
    .red {
    border: 1px solid red;
    margin: 10px;
    padding: 5px;
    }

    .blue {
    background: #009;
    color: white;
    }
    <script src="https://unpkg.com/vue@2.6.10/dist/vue.js"></script>
    <div id="app">
    <my-render-component>
    <div :class="{red: true}">This is a slot content</div>
    </my-render-component>
    <button @click="count++">
    Update outer: {{ count }}
    </button>
    </div>


    有两个按钮。第一个按钮切换 data名为 blueChildren 的属性.它用于决定是否向子项添加 CSS 类。更改 blueChildren 的值将成功触发子组件的重新渲染,并且 VNode 确实会更新,但 DOM 未更改。

    另一个按钮强制外部组件重新渲染。这会重新生成插槽中的 VNode。然后将这些传递给 child ,DOM 将得到更新。

    Vue 正在对什么可以和不能导致 VNode 进行相应的更改和优化做出一些假设。在 Vue 3 中,这只会变得更糟(我的意思是更好),因为有更多的这些优化正在进行中。有 a very interesting presentation Evan You gave关于 Vue 3 涵盖了即将到来的各种优化,它们都属于 Vue 的这一类,假设某些事情不能改变。

    有办法修复这个例子。当组件执行更新时,VNode 将包含对 DOM 节点的引用,因此可以直接更新。这不是很好,但可以做到。

    我自己的感觉是,只有当你正在做的修补是固定的,你才真正安全,这样更新就不是问题了。添加一些属性或 CSS 类应该可以工作,只要您以后不想更改它们。

    还有另一类问题需要克服。调整 VNode 可能非常繁琐。问题中的例子暗示了它。如果 data怎么办不见了?如果 attrs怎么办不见了?

    在问题中的作用域插槽示例中, child组件有 class="bar"在其 <div> .这在 parent 中被吹走了.也许这是故意的,也许不是,但是尝试将所有不同的对象合并在一起是非常棘手的。例如, class可以是字符串、对象或数组。 Vuetify 示例使用 _b , 是 Vue 内部 bindObjectProps 的别名, 以避免必须涵盖所有不同的情况本身。

    随着不同的格式是不同的节点类型。节点不一定代表组件或元素。还有文本节点和评论,其中评论节点是 v-if 的结果。而不是模板中的实际注释。

    正确处理所有不同的边缘情况非常困难。再说一次,对于您实际想到的用例,这些边缘情况可能都不会导致任何真正的问题。

    最后要注意的是,以上所有内容仅适用于修改 VNode。从插槽包装 VNode 或在它们之间插入其他子节点 render功能完全正常。

    关于javascript - 我可以修改 Vue.js VNodes 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57698768/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com