如何在 Vue.js 中使用组件动态包装子字符串?

Em *_*dhu 2 javascript vue.js vue-component vuejs2

我想做什么

给定来自用户输入的字符串,我试图用包裹在组件中的特定子字符串来呈现这个字符串。在我的特殊情况下,匹配的子字符串是一个匹配正则表达式模式的日期,并且应该包装它的组件是来自Vuetify芯片

我做了什么

预期视图

以上是我到目前为止所取得的成绩的截图。的输入在其textarea下方呈现,某些子字符串包裹在 Vuetify 的芯片组件中。上面是通过用 HTML 替换正则表达式模式匹配的子字符串来呈现组件并将此字符串提供给v-html用于呈现的指令来实现的。下面是一些代码,显示了这是如何完成的。

<div style="line-height:40px;" v-html="messageOutput"></div>
Run Code Online (Sandbox Code Playgroud)
let finalStr = ''
let str = 'Your a/c no. XXXXXXXXXX85 is credited on 15-11-17.'

let dateReg = /((?=\d{4})\d{4}|(?=[a-zA-Z]{3})[a-zA-Z]{3}|\d{2})((?=\/)\/|-)((?=[0-9]{2})[0-9]{2}|(?=[0-9]{1,2})[0-9]{1,2}|[a-zA-Z]{3})((?=\/)\/|-)((?=[0-9]{4})[0-9]{4}|(?=[0-9]{2})[0-9]{2}|[a-zA-Z]{3})/

const date = dateReg.exec(str)
finalStr = str.replace(date[0], `
 <div class="md-chip md-chip-clickable">
  <div class="md-chip-icon primary"><i class="material-icons">date_range</i></div>
   ${date[0]}
 </div>
`)
Run Code Online (Sandbox Code Playgroud)

什么不起作用

问题是使用自定义组件而不是纯 HTML 不会提供预期的输出。样式不会呈现,组件不会对事件做出反应。

如何在 Vue.js 中使用组件动态包装子字符串?

Win*_*ing 5

问题

自定义组件无法按预期工作的问题源于将它们包含在v-html指令中的尝试。由于v-html指令的值被插入为纯 HTML,通过设置元素的innerHTML,数据和事件不会被动绑定。

请注意,您不能用于v-html组合模板部分,因为 Vue 不是基于字符串的模板引擎。相反,组件更适合作为 UI 重用和组合的基本单元。

插入原始 HTML 的 Vue 指南

[v-html更新] 元素的innerHTML. 请注意,内容是作为纯 HTML 插入的 - 它们不会被编译为 Vue 模板。如果您发现自己尝试使用 组合模板v-html,请尝试改用组件来重新考虑解决方案。

关于v-html指令的Vue API 文档

解决方案

组件是 UI 重用和组合的基本单元。我们现在必须构建一个组件,该组件能够识别特定的子字符串并在它们周围包裹一个组件。Vue 的组件/模板和指令本身无法处理这个任务——这是不可能的。然而,Vue 确实提供了一种通过渲染函数在较低级别构建组件的方法。

使用 render 函数,我们可以接受一个字符串作为 prop,对它进行标记并构建一个视图,其中包含包裹在组件中的匹配子字符串。以下是这种解决方案的简单实现:

const Chip = {
  template: `
    <div class="chip">
      <slot></slot>
    </div>
  `,
};

const SmartRenderer = {
  props: [
    'string',
  ],

  render(createElement) {
    const TOKEN_DELIMITER_REGEX = /(\s+)/;
    const tokens = this.string.split(TOKEN_DELIMITER_REGEX);
    const children = tokens.reduce((acc, token) => {
      if (token === 'foo') return [...acc, createElement(Chip, token)];
      return [...acc, token];
    }, []);

    return createElement('div', children);
  },
};

const SmartInput = {
  components: {
    SmartRenderer
  },

  data: () => ({
    value: '',
  }),

  template: `
    <div class="smart-input">
      <textarea
        class="input"
        v-model="value"
      >
      </textarea>
      <SmartRenderer :string="value" />
    </div>
  `,
};

new Vue({
  el: '#root',

  components: {
    SmartInput,
  },

  template: `
    <SmartInput />
  `,

  data: () => ({}),
});
Run Code Online (Sandbox Code Playgroud)
.chip {
  display: inline-block;
  font-weight: bold;
}

.smart-input .input {
  font: inherit;
  resize: vertical;
}

.smart-input .output {
  overflow-wrap: break-word;
  word-break: break-all;
}
Run Code Online (Sandbox Code Playgroud)
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <link rel="stylesheet" href="https://unpkg.com/milligram@1.3.0/dist/milligram.css">
</head>
<body>
  <p>Start typing below. At some point include <strong>foo</strong>, separated from other words with at least one whitespace character.</p>
  <div id="root"></div>
  <script src="https://unpkg.com/vue@2.5.8/dist/vue.min.js"></script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

有一个SmartRenderer组件string通过 prop接受 a 。在渲染函数中,我们:

  1. 通过用空格字符分割字符串来标记字符串。
  2. 通过迭代每个标记来构建要呈现的元素数组,然后
    1. 检查令牌是否与规则匹配(在我们的原始实现中查看字符串是否匹配foo)并将其包装在一个组件中(在我们的原始实现中,组件是 a Chip,它只是使foo 粗体)否则保持令牌原样。
    2. 将每次迭代的结果累加到一个数组中。
  3. Step 3然后将 的数组createElement作为div要创建的元素的子元素传递给。
render(createElement) {
  const TOKEN_DELIMITER_REGEX = /(\s+)/;
  const tokens = this.string.split(TOKEN_DELIMITER_REGEX);
  const children = tokens.reduce((acc, token) => {
    if (token === 'foo') return [...acc, createElement(Chip, token)];
    return [...acc, token];
  }, []);

  return createElement('div', children);
},
Run Code Online (Sandbox Code Playgroud)

createElement将 HTML 标记名称、组件选项(或函数)作为其第一个参数,在我们的示例中,第二个参数需要一个或多个子项来呈现。您可以createElement文档中阅读更多信息。

发布的解决方案有一些未解决的问题,例如:

  • 处理各种空白字符,例如换行符 ( \n)。
  • 处理多次出现的空白字符,例如 ( \s\s\s)。

它在检查令牌是否需要包装以及如何包装它的方式上也很幼稚——它只是一个if带有硬编码包装组件的语句。您可以实现一个名为的 prop rules,它是一个指定规则的对象数组测试和一个组件来包装令牌,如果测试通过。但是,此解决方案应该足以让您入门。

进一步阅读