vue.js - v-html 替代品

A. *_*Lau 4 javascript vue.js vuejs2

我来自 JSX 的反应背景,所以使用类似

var test = <div>i am a div</div>

设置一些东西的 html 是很常见的。我知道您可以使用 v-html 实现相同的目的,但想知道根据我的以下代码,这是否是最好/最安全的方法:

组件

<template src="./templates/General.html"></template>

<script>
  export default {
      name: 'guide-general',
      data: function() {
          return {
              guides: [
                  {
                      title: "first",
                      description: "First description"
                  },
                  {
                      title: "second",
                      description: "second description"
                  },
                  {
                      title: "third",
                      description: `<ul>
                        <li>
                            test
                        </li>
                      </ul>`
                  }
              ]
          }
      },
      methods: {

      }
  }
</script>

<style scoped>
</style>
Run Code Online (Sandbox Code Playgroud)

html模板

<article>
    <div v-for="guide in guides">
        <h4>{{ guide.title }}</h4>
        <div v-html="guide.description">
        </div>
    </div>
</article>
Run Code Online (Sandbox Code Playgroud)

B. *_*ing 5

如果您想包含从 vue 属性呈现 HTML 的功能,您将需要使用v-html. 如果您允许用户修改 HTML 可能来自的属性,那么您将遇到 XSS 漏洞的安全问题,在这种情况下,您需要在将数据发送回时清理您的输入服务器(我强烈建议您为此使用信誉良好的外部库)。如果用户不会修改您的数据,那么使用完全没有安全问题v-html

每当您允许用户修改任何类型的数据时,您就会面临潜在的安全问题。您只需要预测这些问题是什么并解决它们。


2021 年 6 月更新

澄清一点,如果您不需要动态 HTML 渲染,而只需要处理渲染某些预定 HTML 模板的条件情况,那么您可以简单地在 Vue 模板中包含条件渲染。一个示例可能如下所示:

<script>
    export default {
      name: 'guide-general',
      data: function() {
          return {
              guides: [
                  {
                      title: "first",
                      description: "First description",
                      as_list: false
                  },
                  {
                      title: "second",
                      description: "second description",
                      as_list: false
                  },
                  {
                      title: "third",
                      description: "test",
                      as_list: true
                  }
              ]
          }
      },
      methods: {

      }
  }
</script>

<article>
    <div v-for="guide in guides">
        <h4>{{ guide.title }}</h4>
        <div v-if="guide.as_list">
            <ul>
                <li>{{guide.description}}</li>
            </ul>
        </div>
        <div v-else>{{guide.description}}</div>
    </div>
</article>
Run Code Online (Sandbox Code Playgroud)

简而言之,如果您不需要动态HTML 呈现而只需要条件HTML 呈现,则v-if在适用的情况下使用并构建您的数据以允许条件呈现正确运行。否则使用v-html,甚至可能构造 JSON 数据来递归生成预定义的组件<component :is="component.name">(这相当复杂,所以我不会在这个答案中详细介绍)。


Rai*_*kel 2

对于任何寻求渲染仅限于一组标签的 hmtl 的方法的人来说,解决方案是解析 html 字符串并使用渲染函数以编程方式构建模板。通过这种方法,您可以创建自己的安全 html 渲染器并避免使用v-html外部库。以下是此类 html 视图组件的一个小示例:

HtmlView.vue

<script>

import { h } from "vue";

export default {
  props: {
    html: {
      type: String,
      required: true,
    },
    tags: {
      /** @type {import("vue").PropType<String[]>} */
      type: Array,
      default: () => ["b"]
    },
  },
  setup(props) {
    const html = parseHTML(props.html);

    // return the render function
    return () => {
      const output = [];
      html.childNodes.forEach(childNode => {
        output.push(renderNode(childNode, props.tags));
      });
      if (output.length == 1) {
        return output[0];
      } else if (output.length == 0) {
        return "";
      }
      return output;
    };
  }
};

const parseHTML = (html) => {
  var template = document.createElement("template");
  template.innerHTML = html;
  return template.content;
};

/**
 *
 * @param {Node} node
 * @param {String[]} tags
 */
const renderNode = (node, tags) => {
  const nodeType = node.nodeType;
  if (nodeType == 1) {
    const nodeName = node.nodeName.toLocaleLowerCase();
    if (tags.includes(nodeName)) {
      const children = [];
      node.childNodes.forEach(childNode => {
        children.push(renderNode(childNode, tags));
      });
      return h(nodeName, children);
    }
  }
  return node.textContent;
};

</script>
Run Code Online (Sandbox Code Playgroud)

组件的属性tags限制了支持的标签集。在此示例中,默认情况下仅<b>支持标签。

请注意,上面的示例将仅渲染类型 1 ( ) 的DOM 节点ELEMENT_NODE,并且还将省略节点的任何属性,但它可以轻松扩展以支持所需的属性。

我在这里创建了一个工作示例。