oliverfindl / vue-svg-inline-plugin

Vue plugin for inline replacement of SVG images with actual content of SVG files.
MIT License
33 stars 3 forks source link

"Argument is not valid" for items without v-svg-inline directive #18

Open tomastan opened 2 years ago

tomastan commented 2 years ago

We noticed some random JS exceptions in Sentry logs which made us a bit confused.

For example: TypeError: [vue-svg-inline-plugin] Argument is not valid! [path="/image/i.702/w.400:h.400:f.0/image.png"]

The confusion is because this element does not contain SVG and should not be affected at all:

<img v-lazy="getImageUrl(item.image_id)">

The same Vue component contains other elements using vue-svg-inline-plugin, for example:

<img v-svg-inline src="/customer/images/challenge-success.svg" alt="">
<img v-svg-inline src="/customer/images/challenge-failed.svg" alt="">

Any ideas?

oliverfindl commented 2 years ago

Hello,

this error means, that plugin expects SVG file, but got something else (in your case PNG).

https://github.com/oliverfindl/vue-svg-inline-plugin/blob/97be1e3e224b355c9a629cc3677152775215e83e/src/index.js#L75

https://github.com/oliverfindl/vue-svg-inline-plugin/blob/97be1e3e224b355c9a629cc3677152775215e83e/src/index.js#L460-L461

Are you 100% sure, its coming from this part of code?

<img v-lazy="getImageUrl(item.image_id)">

Because this plugin registers its own Vue directive and should not run outside of it. If you are able to reproduce this issue, please try to add console.log({ node, directives }); after this line (edit this file directly in node_modules):

https://github.com/oliverfindl/vue-svg-inline-plugin/blob/97be1e3e224b355c9a629cc3677152775215e83e/src/index.js#L775-L776

To see if there is actually processed image node without chosen directive.

Thanks.

tomastan commented 2 years ago

@oliverfindl what I'm sure is that we set directive on svg files only (see screen below). That why my own confusion comes from :). It's very hard to reproduce as it occures randomly on a production server with more usage. But I'll try to look if I can catch it on my dev environment to give more debugging information.

image

tomastan commented 2 years ago

I managed to explore the issue a bit more and get it replicated. Seems there is some conflict with vue-lazyload package in my case.

The simplified block that reproduces the issue is this:

<template>
    <div>
        <div v-if="showBlock=='default'">
            <img v-lazy="getImageUrl(item.image_id)">
        </div>

        <div v-if="showBlock=='failed'">
            <img v-svg-inline src="/customer/images/challenge-failed.svg" alt="">
        </div>
    </div>
</template>

On showBlock=="default" a PNG image is loaded externally with a vue-lazyload. And after showBlock="failed" it should show a static SVG, which is works, but the beforementioned error is thrown.

If instead of <img v-lazy="getImageUrl(item.image_id)"> a simple <img :src="getImageUrl(item.image_id)"> is used, the problem does not happen. So somehow these two elements collide with each other.

As you proposed in console.log() into the beforeMount() method so the output if here: image

oliverfindl commented 2 years ago

Hello,

this looks really strange. Can you also expand img node in console? And please, send me exact version of vue-lazyload, so I can also replicate this issue locally.

Thanks.

Ilya-Chernyshev commented 2 years ago

I also ran into this problem. I used standard settings and only svg files

image image
Ilya-Chernyshev commented 2 years ago

The solution to the problem was to set the data-src attribute. And the installation of version 2.2.2. But there was another error in the console.

On version 2.2.3. does not work.

image
tomastan commented 2 years ago

@oliverfindl the object is pretty large so does not fit to the screen, so cant copy it all here image

But something useful is here maybe. Yes - dataset contains wrong value. image

We use vue-lazyload 1.3.4 (the latest one for vue 2)

tomastan commented 2 years ago

FYI, the resulting element in DOM looks like this:

<img data-src="/image/i.167/w.400:h.400:f.0/image.png"
src="https://aimera.local/customer/images/challenge-failed.svg" lazy="error" alt="">
oliverfindl commented 2 years ago

Hello @tomastan,

I created a dummy document and unfortunately I can't replicate your issue. I had to use version 1.3.3, because 1.3.4 was throwing error: [Vue warn]: Failed to resolve directive: lazy vue.js.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>img, svg { max-height: 200px; }</style>
    </head>
    <body>
        <div id="app">
            <img v-svg-inline :src="svg" />
            <img v-lazy="png" />
        </div>
        <script src="https://unpkg.com/vue-svg-inline-plugin@2.2.3/dist/vue-svg-inline-plugin.min.js"></script>
        <script src="https://unpkg.com/vue-lazyload@1.3.3/vue-lazyload.js"></script>
        <script src="https://unpkg.com/vue@2.7.14/dist/vue.js"></script>
        <script>
            Vue.use(VueSvgInlinePlugin);
            Vue.use(VueLazyload);
            new Vue({
                data: {
                    svg: "https://raw.githubusercontent.com/vuejs/art/master/logo.svg",
                    png: "https://raw.githubusercontent.com/vuejs/art/master/logo.png"
                }
            }).$mount("#app");
        </script>
    </body>
</html>

Please provide minimal example, so I can replicate this issue.

Thanks.

oliverfindl commented 2 years ago

Hello @Ilya-Chernyshev,

this is different issue. There was already reported something similar, which was related to Vite due to using esbuild for dev server and Rollup for production build. Most probably your issue is related to your bundler. Please as first step, try to investigate your rendered webpage and try to find what is actually set into src attribute of img tag. You can also look at network tab for version 2.2.2, find requested URL for your SVG and modify your bundler accordingly. Furthermore, if you can't debug it yourself, please create a new issue as this not related to this one and provide minimal reproduction repository with your bundler, so I can examine its config and replicate this issue.

Thanks.

tomastan commented 2 years ago

@oliverfindl I made a jsfiddle (https://jsfiddle.net/kpjzhotf/1/) for you. The key factor for this to happen is switching between two parent elements containing lazyloaded png in one, and inline-svg in the another. I have no idea, how these elements are related, but they do somehow. Maybe some Vue optimizers in force?

      <div v-if="!showSvg">
        <img v-lazy="png" />
      </div>

      <div v-if="showSvg">
        <img v-svg-inline :src="svg" />
      </div>
oliverfindl commented 2 years ago

Hello,

thanks for clarification. I found out, that you don't need to use v-lazy directive to trigger this behaviour, just replace v-lazy with :src and its still happening.

I presume, its due to how Vue is referencing elements. Our problem here is, that this plugin creates new svg node and replaces original img node with it, while Vue doesn't have any clue whats happening. Later it tries to change only attribute, that has changed and not whole node. And most probably, Vue still keeps reference for original img node, that is now detached from DOM.

I also tested your example with Vue 3 and its working there correctly. Unfortunately, I don't have any solution for Vue 2 based projects.

If you have more questions, feel free to ask here.

Thanks.

tomastan commented 2 years ago

I somehow cannot replicate the issue with :src, for me no errors thrown. Would be interesting to see.

However, I also think there is something is with Vue internals. But this bring me to a solution. If key property is added to v-if blocks the optimization is avoided and issue is does not happen. So this may be useful for other:

<div v-if="!showSvg" key="svg">
    <img v-lazy="png" />
</div>

<div v-if="showSvg" key="png">
    <img v-svg-inline :src="svg" />
</div>

Working example: https://jsfiddle.net/x0trzuaj/1/