gpt4 book ai didi

vuejs2 - 在 vuejs2 数据中动态插入子组件(不使用 $compile 或滥用 v-html)

转载 作者:行者123 更新时间:2023-12-04 11:19:58 27 4
gpt4 key购买 nike

我想在一个不需要预定义的 HTML 块中的任意点上动态插入新的 vuejs 组件。
这是一个稍微做作的示例,它演示了我正在尝试做的事情:

Vue.component('child', {
// pretend I do something useful
template: '<span>--&gt;<slot></slot>&lt;--</span>'
})

Vue.component('parent', {
data() {
return {
input: 'lorem',
text: '<p>Lorem ipsum dolor sit amet.</p><p><i>Lorem ipsum!</i></p>'
}
},
template: `<div>
Search: <input type='text' v-model="input"><br>
<hr>
This inserts the child component but doesn't render it
or the HTML:
<div>{{output}}</div>
<hr>
This renders the HTML but of course strips out the child component:
<div v-html="output"></div>
<hr>
(This is the child component, just to show that it's usable here:
<child>hello</child>)
<hr>
This is the goal: it renders both the input html
and the inserted child components:
TODO ¯\_(ツ)_/¯
</div>`,
computed: {
output() {
/* This is the wrong approach; what do I replace it with? */
var out = this.text;
if (this.input) {
this.input = this.input.replace(/[^a-zA-Z\s]/g,'');
var regex = new RegExp(this.input, "gi");
out = out.replace(regex, '<child><b>' + this.input + '</b></child>');
}
return out;
}
}
});

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

在上面的代码片段中,假设 data.text是经过 sanitizer 的 HTML。 <child>是一些做一些有用的事情的子组件,我想把它包裹起来 data.text那些事先不知道的。 ( input 仅用于此处的演示。此 MCVE 与我正在构建的代码并不真正相似,它只是一个示例,显示了我遇到的情况。)
所以:我将如何更改 output函数或父组件的模板,使得来自 input 的 HTML和插入的 <child>模板是否正确呈现?
我试过的
  • 在 Vue 1 中,这个问题的答案很简单 $compile .我正在使用 vuejs2,它删除了 $compile (出于合理的担忧,它使天真地引入 XSS 漏洞变得太容易了。)
  • v-html 会清理您提供给它的内容,从而去除子组件。显然这是not the way to do this . (该页面建议改用部分,但我不确定如何将其应用于这种情况;无论如何,部分也已从 vue2 中删除。)
  • 我试过传递 output() 的结果到另一个组件中,然后将其用作模板。这似乎是一种很有前途的方法,但我不知道如何更改该辅助组件的模板。 template只接受一个字符串,而不是像许多其他组件属性那样的函数,所以我不能将模板 html 传递给一个 prop。有点像重写 this.templatebeforeMount()bind()本来很好,但那里也没有快乐。是否有其他方法可以在安装之前替换组件的模板字符串?
  • 不像 template ,我可以将数据传递给组件的 render()函数...但是我仍然不得不将该 html 字符串解析为嵌套的 createElement职能。这究竟是什么Vue is doing internally in the first place ;除了自己重新发明之外,有没有什么方法可以解决这个问题?

  • Vue.component('foo', {
    props: ['myInput'],
    render(createElement) {
    console.log(this.myInput); // this works...
    // ...but how to parse the html in this.myInput into a usable render function?
    // return createElement('div', this.myInput);
    },
    })
  • 我也无法使用内联模板欺骗我:<foo inline-template>{{$parent.output}}</foo>与普通的旧设备完全相同 {{output}} .回想起来,这应该是显而易见的,但值得一试。
  • 也许构建一个 async component即时是答案吗?这可以清楚地生成一个带有任意模板的组件,但是我如何合理地从父组件调用它,并提供 output到构造函数? (它需要可重复使用不同的输入,多个实例可能同时可见;没有全局变量或单例。)
  • 我什至考虑过像 output() 这样荒谬的事情在将要插入的点处将输入拆分为一个数组 <child> ,然后在主模板中做这样的事情:

  •   ...
    <template v-for="chunk in output">
    <span v-html="chunk"></span>
    <child>...</child>
    </template>
    ....
    这将是可行的,如果费力的话——我也必须将 child 插槽中的内容拆分成一个单独的数组,并在 v-for 期间通过索引获取它,但可以做到... if input是纯文本而不是 HTML。在拆分 HTML 时,我经常会在每个 chunk 中得到不平衡的标签。 ,这会在 v-html 时弄乱格式为我重新平衡它。无论如何,整个策略感觉就像一个糟糕的黑客;一定会有更好的办法。
  • 也许我只是将整个输入放入 v-html然后(以某种方式)通过事后 DOM 操作在适当的位置插入子组件?我还没有深入探讨这个选项,因为它也感觉像是一个黑客,和数据驱动策略的相反,但如果所有其他方法都失败了,也许这是一种方法?

  • 一些先发制人的免责声明
  • 我非常清楚 $compile 中涉及的 XSS 风险- 类似的操作。请放心,我所做的一切都不会以任何方式涉及未经 sanitizer 的用户输入;用户不是插入任意组件代码,而是组件需要在用户定义的位置插入子组件。
  • 我有理由相信这不是 XY 问题,我确实需要即时插入组件。 (我希望从失败的尝试次数和我跑过的死胡同中可以明显看出,我在这个问题上投入了更多的想法!)也就是说,如果有一种不同的方法会导致类似的结果,我'我的耳朵。重点是我知道我需要添加哪个组件,但我无法提前知道在哪里添加它;该决定发生在运行时。
  • 如果相关,在现实生活中我使用的是 vue-cli webpack 模板中的单文件组件结构,而不是 Vue.component()如上面的示例。不会偏离该结构太远的答案是首选,尽管任何有效的方法都会起作用。

  • 进步!
    @BertEvans 在评论中指出 Vue.compile()是一种存在的东西,这是一种我无法相信我错过的东西,如果曾经有过的话。
    但是如果不使用该文档中的全局变量,我仍然无法使用它。这会呈现,但在全局中硬编码模板:
    var precompiled = Vue.compile('<span><child>test</child></span>');
    Vue.component('test', {
    render: precompiled.render,
    staticRenderFns: precompiled.staticRenderFns
    });
    但是各种尝试将其重新调整为可以接受输入属性的东西都没有成功(例如,以下内容抛出“渲染函数中的错误:ReferenceError:_c 未定义”,我认为是因为 staticRenderFns 还没有准备好 render 什么时候需要它们?
    Vue.component('test', {
    props: ['input'],
    render() { return Vue.compile(this.input).render()},
    staticRenderFns() {return Vue.compile(this.input).staticRenderFns()}
    });
    (这不是因为有两个单独的 compile() s——在 beforeMount() 内部进行预编译,然后返回其渲染,而 staticRenderFns 会引发相同的错误。)
    这真的感觉它在正确的轨道上,但我只是陷入了愚蠢的语法错误或类似的问题......

    最佳答案

    正如我在上面的评论中提到的,$compile已被删除,但 Vue.compile在某些版本中可用。除非在几种情况下,否则我相信您打算使用下面的方法。

    Vue.component('child', {
    // pretend I do something useful
    template: '<span>--&gt;<slot></slot>&lt;--</span>'
    })

    Vue.component('parent', {
    data() {
    return {
    input: 'lorem',
    text: '<div><p>Lorem ipsum dolor sit amet.</p><p><i>Lorem ipsum!</i></p></div>'
    }
    },
    template: `<div>
    Search: <input type='text' v-model="input"><br>
    <hr>
    <div><component :is="output"></component></div>
    </div>`,
    computed: {
    output() {
    if (!this.input)
    return Vue.compile(this.text)
    /* This is the wrong approach; what do I replace it with? */
    var out = this.text;
    if (this.input) {
    this.input = this.input.replace(/[^a-zA-Z\s]/g,'');
    var regex = new RegExp(this.input, "gi");
    out = out.replace(regex, '<child><b>' + this.input + '</b></child>');
    out = Vue.compile(out)
    }
    return out;
    }
    }
    });

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


    您提到您正在使用 webpack 构建,我相信该构建的默认值是没有编译器的 Vue,因此您需要修改它以使用不同的构建。

    我加了动态 component接受编译输出的结果。

    sample text不是有效模板,因为它有多个根。我添加了一个包装 div使其成为有效的模板。

    注意:如果搜索词与 text 中的任何 HTML 标签的全部或部分匹配,这将失败。 .例如,如果您输入“i”、“di”或“p”,结果将不是您所期望的,并且某些组合会在编译时引发错误。

    关于vuejs2 - 在 vuejs2 数据中动态插入子组件(不使用 $compile 或滥用 v-html),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44369839/

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