【问题标题】:How to load an External SVG into Vue as an Object如何将外部 SVG 作为对象加载到 Vue
【发布时间】:2021-08-28 19:14:49
【问题描述】:

我需要将 SVG 文件加载到 Vue 模板中。它必须以这样一种方式加载,以便我可以使用 js 和 css 访问内部类,所以大概我正在寻找一个 <object> 标签而不是 <img> 标签。

SVG 位于外部服务器上,不是我项目的一部分。如果我将 svg 作为项目的一部分,Vue-Svg-Loader 就可以正常工作,但是当 SVG 直到运行时才可用时,它似乎不起作用。

我尝试了以下方法

<template>
    <div ref="floorplan" id="floorplan-frame">
        <object
            type="image/svg+xml"
            id="floorplan"
            :data="svgPath"
        ></object>
    </div>
</template>

<script>
export default {
    data() {
        return {
            svgPath: 'http://test-mc4/floorplan.svg',
        };
    },
};
</script>

不幸的是,它不起作用。如果我用&lt;img :src="svgPath" /&gt; 替换&lt;object&gt; 标记,它确实显示了SVG,但作为内部css 类不可用的静态图像。它确实向我表明我的路径是正确的并且文件实际上是可用的,但它并没有解释为什么我使用它时对象标签只是空的。

我进行了广泛的搜索,如果它是内部的,我可以弄清楚如何将其加载为对象,或者只要它是图像,如何将其加载为外部。我似乎无法弄清楚如何做到这两点。

【问题讨论】:

标签: vue.js svg


【解决方案1】:

为了访问&lt;object&gt; 中的元素,您需要等到它被加载(load 事件),然后通过其contentDocument property 访问子 SVG 文档。

很遗憾,这不适用于您的情况,因为 SVG 文件来自不同的来源。 same-origin policy 将阻止对contentDocument 的访问。这是一个示例,它也失败了(记录 null),因为 data: URL 是不同的来源:

const svgPath = 'data:image/svg+xml;charset=utf-8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDIwIDIwIj4NCiAgICA8Y2lyY2xlIGN4PSIxMCIgY3k9IjEwIiByPSIxMCIgZmlsbD0icmVkIi8+DQo8L3N2Zz4=';

const app = new Vue({
  el: '#app',
  data: {
    svgPath,
  },
  methods: {
    svgLoaded() {
      setTimeout(() => {
        console.log(this.$refs.object.contentDocument);
      }, 1000);
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <object
    ref="object"
    :data="svgPath"
    width="100"
    height="100"
    v-on:load="svgLoaded"
  ></object>
</div>

从不同来源加载 SVG 然后使用 JS 和 CSS 访问其内部结构的唯一方法是 fetch 它,然后将其作为 v-html 加载到您的组件中。 请注意,这会打开严重的 XSS 漏洞;您应该警惕此选项,并且仅将其与您信任的外部服务器一起使用。无论如何,这是一个工作示例:

const svgPath = 'data:image/svg+xml;charset=utf-8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDIwIDIwIj4NCiAgICA8Y2lyY2xlIGN4PSIxMCIgY3k9IjEwIiByPSIxMCIgZmlsbD0icmVkIi8+DQo8L3N2Zz4=';

const app = new Vue({
  el: '#app',
  data: {
    svgData: '',
  },
  async mounted() {
    const svgResponse = await fetch(svgPath);
    this.svgData = await svgResponse.text();
    await Vue.nextTick();

    // SVG is present in the DOM at this point
    const svg = this.$refs.drawing.firstElementChild;
    console.log(svg.outerHTML);

    // DOM manipulations can be performed
    const circle = svg.querySelector('circle');
    circle.setAttribute('fill', 'blue');
    circle.setAttribute('r', '6');
  }
});
.drawing {
  width: 100px;
  height: 100px;
}

/* SVG can be styled as part of the main document */
.drawing circle {
  stroke: cyan;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <div
    class="drawing"
    ref="drawing"
    v-html="svgData"
  ></div>
</div>

【讨论】:

  • 这太好了,非常感谢。它让我离我想去的地方很近。 SVG 加载,我可以将其视为我页面的一部分。我还有两个问题。 1) 我如何判断 SVG 何时插入 DOM,以便我可以为我正在寻找的类执行 querySelectorAll?如果我在您插入代码之后立即执行此操作,它只会返回 null。如果我执行 SetTimeout 并在一秒钟后再次检查它会起作用,但这非常不雅。
  • 2) 图像被插入,但我的 CSS 类样式被图像本身的内部样式覆盖。当我使用 vue-svg-loader 插入图像时,我可以在图像中添加一个填充:#333333 css 样式。现在我这样做了,但是如果我查看开发工具,我可以看到 SVG 中的样式是浏览器最后关注的样式。我猜是因为 SVG 绝对是最后加载的,所以它是先例?我只需要对所有内容都使用 !important 吗?
  • 我忘了说我实际上完全控制了两台服务器(在某些情况下它实际上是同一台服务器),所以我不是超级 担心你提到的安全问题。这是一个内部项目,该项目将加载到每个设施的服务器上。除了平面图外,所有地方都一样,所以我想要一个编译的程序,它可以在任何地方加载,只导入本地平面图。
  • @JeffMcAleer (1) 在我的示例中,SVG 出现在 DOM 中的点是在 await 调用 Vue.nextTick 处理程序中的 Vue.nextTick 之后。您可以从那里触发任何 DOM 操作代码。 (2) 由于 SVG 被直接注入到主文档的树中,因此样式优先级没有什么特别之处。如果它的内部风格优先,那只是出于通常的原因。如果您无法围绕这些进行调整,您只需要!important。我已经编辑了示例,以显示 SVG 加载后的 DOM 操作和外部 CSS 样式。
  • 有趣。在Vue.nextTick 之后,我看到outHTML 控制台日志很好,但我的querySelectorAll 仍然返回null。如果我将相同的 querySelectorAll 放在 1000 的 setTimeout 中,它会返回我要查找的内容。无论如何,我认为您已经解锁了我需要弄清楚我的具体实施的关键。我不知道 v-html,这看起来很棒。我可能会尝试在这个组件安装之前将 svg 加载到我的 Vuex 存储中,这可能会解决我的一些时间问题。非常感谢,这很有帮助。
猜你喜欢
  • 1970-01-01
  • 2020-03-29
  • 2017-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-06
  • 2020-01-01
  • 2020-05-18
相关资源
最近更新 更多