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

2023-11-26

我想在不一定是预定义的 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是经过净化的 HTML。<child>是一些有用的子组件,我想将其包裹起来data.text这是事先不知道的。 (input这里只是为了演示。这个 MCVE 并不真正类似于我正在构建的代码,它只是一个示例,显示了我所遇到的情况。)

那么:我将如何改变output函数或父组件的模板,这样 HTML 就可以来自input和插入的<child>模板是否正确呈现?

我尝试过的

  • 在 Vue 1 中,这个问题的答案很简单$compile。我正在使用 vuejs2 删除了$compile(出于合理的担忧,它使得很容易天真地引入 XSS 漏洞。)

  • v-html 会清理您提供的内容,从而将子组件剥离出来。显然这是不是这样做的方法。 (该页面建议使用部分,但我不确定如何将其应用于这种情况;无论如何,部分也已从 vue2 中删除。)

  • 我尝试过传递结果output()到另一个组件中,然后该组件将使用它作为模板。这似乎是一种很有前途的方法,但我不知道如何更改辅助组件的模板。template只接受字符串,而不是像许多其他组件属性那样的函数,因此我无法将模板 html 传递到 prop 中。类似于重写的东西this.template inside beforeMount() or bind()本来很好,但也没有什么快乐。在安装组件之前是否有其他方法可以替换组件的模板字符串?

  • Unlike template, I can将数据传递给组件render()函数...但是我仍然必须将 html 字符串解析为嵌套的createElement功能。到底是什么Vue 首先是在内部做的;除了我自己重新发明之外,有什么方法可以解决这个问题吗?

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}}。回想起来,这应该是显而易见的,但值得一试。

  • 也许构建一个异步组件答案是在飞行中?这显然可以生成具有任意模板的组件,但我如何合理地从父组件调用它,并提供output到构造函数? (它需要可通过不同的输入重用,多个实例可能同时可见;没有全局变量或单例。)

  • 我什至考虑过一些荒谬的事情,比如output()在要插入的点将输入拆分为数组<child>,然后在主模板中执行类似的操作:

  ...
  <template v-for="chunk in output">
      <span v-html="chunk"></span>
      <child>...</child>
  </template>
  ....

这是可行的,如果费力的话——我也必须将子插槽中的内容拆分到一个单独的数组中,并在 v-for 期间通过索引获取它,但这可以做到......if input是纯文本而不是 HTML。在分割 HTML 时,我经常会遇到每个标签不平衡的情况chunk,这可能会弄乱格式v-html为我重新平衡它。无论如何,这整个策略感觉像是一个糟糕的黑客;一定会有更好的办法。

  • 也许我只是将整个输入放入v-html然后(以某种方式)通过事后 DOM 操作将子组件插入到正确的位置?我还没有太深入地探索这个选项,因为它也感觉像是一种黑客攻击,与数据驱动策略相反,但如果其他一切都失败了,也许这是一种可行的方法?

一些先发制人的免责声明

  • 我非常清楚涉及的 XSS 风险$compile类似的操作。请放心,我所做的一切都不会以任何方式涉及未经处理的用户输入;用户不是插入任意组件代码,而是组件需要在用户定义的位置插入子组件。
  • 我有理由相信这不是 XY 问题,我确实需要动态插入组件。 (我希望从我所经历的失败尝试和死胡同的数量中可以明显看出,我在这方面投入了更多的思考!)也就是说,如果有一种不同的方法会导致类似的结果,我'我洗耳恭听。重点是我知道which我需要添加组件,但我无法提前知道where添加它;该决定发生在运行时。
  • 如果相关的话,在现实生活中我使用的是 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 进行构建,我相信该构建的默认值是 Vuewithout编译器,因此您需要修改它以使用不同的版本。

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

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

需要注意的是:如果搜索项与以下内容中的任何 HTML 标记的全部或部分匹配,则此操作将会失败text。例如,如果您输入“i”、“di”或“p”,结果将不是您所期望的,并且某些组合将在编译时引发错误。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 vuejs2 数据中动态插入子组件(无需 $compile 或滥用 v-html) 的相关文章

随机推荐

  • 别名模板专业化

    别名模板 14 5 7 可以显式专门化 14 7 3 吗 我的标准 fu 失败了 我找不到编译器来测试 文本 当 template id 引用别名模板的专门化时 意味着yes 但这个例子似乎指的是其他东西 暗示no NB I m worki
  • 包含非自反元素的集合的比较

    在Python中 一个值x并不总是被限制为等于自身 也许最著名的例子是NaN gt gt gt x float NaN gt gt gt x x False 现在考虑仅包含一项的列表 我们可以考虑两个这样的列表equal当且仅当它们包含的物
  • 在 C# 中使用 Linq 或 lambda 从数据库表中获取 X 个随机元素

    我有一个包含 x 个用户的数据库 我想随机获取所有用户 然后在我的网站上写下 50 个用户 现在我只使用 take 50 并检索最新 50 个用户 我希望它从整个表中随机洗牌 50 个 有什么想法吗 这就是我的代码现在的样子 userLis
  • %r 是什么意思?

    是什么意思 r在下面的声明中 print r 1 我想我听说过 s d and f但从未听说过这个 背景 在Python中 有两个内置函数用于将对象转换为字符串 str vs repr str应该是一个友好的 人类可读的字符串 repr应该
  • 防止 JSPX 创建自关闭标签 (
    !=

    JSPX 有一个可爱的旋转副作用 div class magic div Into div class magic div 对于许多浏览器来说 即使它是有效的 XHTML 这也会导致布局混乱 因此 我求助于使用 groovy 脚本通过以下正
  • jQuery DataTables 渲染列数据

    我正在使用 jQuery DataTables 显示来自 JSON 编码的 PHP 响应的信息 JSON 响应包含对象 名称 姓名 包含 全名 姓氏 ID 我一直在使用columns以我想要的方式显示数据 但是我遇到了一个我无法弄清楚的问题
  • 如何使用 wallet:accounts:read 请求访问所有用户帐户?

    当我的应用程序请求时wallet accounts read permission使用 Coinbase API v2 用户会看到一个包含所有帐户的下拉列表 他只能在三个可能的钱包中选择一个 如果我的应用程序想要与所有三个钱包 例如 BTC
  • Python:为什么(“hello”是“hello”)评估为True? [复制]

    这个问题在这里已经有答案了 为什么 hello is hello 生产True在Python中 我读了以下内容here 如果两个字符串文字相等 则它们已被置于相同的位置 内存位置 字符串是一个不可变的实体 没有伤害可以 做完了 那么每个 P
  • 添加列之前如何检查列是否存在

    我有一个数据库 如果它不存在 我想向其中添加一列 如何使用 sqlite swift API 做到这一点 通常 如果要向现有表添加新列 您将需要一个迁移路径 您可以使用userVersion管理数据库架构版本的属性 if db userVe
  • 一个用于验证美国和加拿大邮政编码的正则表达式

    我正在开发一个文具程序 客户可以选择美国或加拿大地区 当他们输入地址时 必须输入邮政编码 我正在尝试验证字段 但我无法对美国或加拿大使用 reg exp 我需要一个可验证两个国家 地区邮政编码的正则表达式 不知道您使用的是什么语言 我不会使
  • 垂直对齐属性如何工作?

    我不明白什么时候vertical align会和不会工作 每次我遇到一个用例vertical align至于它是否真的有效 这似乎是一个抛硬币的问题 我知道它必须应用于内联元素 我读到我必须指定一个line height对于通常没有的元素
  • Echoprint iOS 缺少框架

    有人下载过 EchoPrint 的 iOS 演示版本吗 它是一个音频指纹开源软件 我刚刚下载了它 但它似乎缺少框架 有什么地方可以获得功能版本吗 按照步骤使其框架运行 下载最新版本boost 下载link最新 1 51 0 的 boost
  • 无法启动 GlassFish 4.0 (Windows) - 端口 1527 - 地址已在使用中

    我是 Java EE 7 的新手 我有 Netbeans 7 4 GlassFish 4 0 和 Java EE 7 在 64 位 Windows 8 1 Pro 计算机中 我想要启动 GlassFish 4 0 Server 因此单击 N
  • 如何使用 Ramda 实现使用无点递归来删除对象中的空值?

    我正在学习 pointfree 函数 并尝试以这种风格实现这个递归空值删除器 有效 但是是not无点 function removeNulls obj return R ifElse R either R is Array R is Obj
  • 如何使用mongoose在mongodb中存储图像?

    任何人都有一个使用 mongoose 和 Nodejs Express 在 mongodb 中插入图像的快速示例 我读了一些例子 但我不明白如何做到这一点 我想以表格形式上传图片 app post videos new function r
  • Emacsclient 钩子上的kill

    我试图在 Emacs 中找到一个钩子 它应该在 emacs 服务器正常关闭之前触发 我尝试使用 elisp 来执行kill emacs query functions kill emacs hook server done hook 如下所
  • 如何在打乱的连续整数数组中查找重复元素?

    我最近在某处遇到一个问题 假设您有一个包含 1001 个整数的数组 整数按随机顺序排列 但您知道每个整数都在 1 到 1000 含 之间 此外 除了一个数字出现两次之外 每个数字在数组中只出现一次 假设您只能访问数组的每个元素一次 描述一种
  • JavaScript内部方法实现源码

    有没有办法查看 JavaScript 方法背后的代码 不是网站 html 或 js 文件中的 javascript 方法 而是 JavaScript 的内部方法 例如 我怎样才能看到JavaScript如何计算offsetTop一个元素的
  • MySQL DATETIME DIFF 查询

    我有一个 MySQL 查询 每 30 分钟通过 cron 运行一次以删除旧的属性列表 查询是 DELETE FROM wpdb gt posts WHERE post type rentals AND DATEDIFF NOW post d
  • 在 vuejs2 数据中动态插入子组件(无需 $compile 或滥用 v-html)

    我想在不一定是预定义的 HTML 块中的任意点动态插入新的 vuejs 组件 这是一个稍微做作的示例 演示了我正在尝试做的事情 Vue component child pretend I do something useful templa