vue access to images - vue.js

I'm trying to view an image in one of my pages. I'm on a latest node-express-vue-nuxt-babel setup. My final goal was:
<img :src="'#/assets/images/projects/' + project.image" width="50px" />
I started with the above, but I got a 404. So I tried with a specific image:
<img src="#/assets/images/projects/5a2db62e346c1.jpg" width="50px" />
and it worked, but going back to dynamic source didn't and:
to <img :src="'#/assets/images/projects/5a2db62e346c1.jpg'" width="50px" />
was enough to produce the 404 again. The 404 was in the console, the page was loading fine with no errors but alas no signs of the image(s). Inspecting the elements made me notice that the dynamic :src became:
<img src="#/assets/images/projects/5a2db62e346c1.jpg" width="50px" />
and the 'static' src became:
<img src="/_nuxt/assets/images/projects/5a2db62e346c1.jpg" width="50px" />
So to make it work I had to renounce to the initial "#" and substitute it with "_nuxt":
<img :src="'_nuxt/assets/images/projects/' + project.image" width="50px" />
Ok, nice, but.. why?

Do this: <img :src="require('#/assets/images/projects/' + project.image)"/>

VueJs
There are several asset URL transforms rules in vuejs. Here is one of them:
If the URL starts with #, it's also interpreted as a module request. This is useful if your webpack config has an alias for #, which by default points to /src in any project created by vue-cli
For more information : Asset URL

You are using webpack for bundling the application.
At a very high level webpack looks for require and takes care of bundling your scripts. Exactly what that means depends on your specific configuration, but it usually gathers all the js files in one file, usually minifies and removes unused code.
Webpack is not limited to js files. By the use of plugins it can handle extracting images from html files, load resources from urls and much more.
The way it ends up loading images is still by the use of require, the plugins just plug everything in so webpack can handle them. That being said, require is a compile time feature and if the path can't be determined at compile time webpack will not work. Webpack will usually translate the image path to a path that's available at runtime (usually they are different and depend on your webpack config).
When you bind src like this:
:src="'#/assets/images/projects/' + project.image"
The path can't be determined at compile time and as such vue will resolve it at run time, but webpack already finished and it will not translate your path.
There are a couple of ways to handle this:
As you found out: using a static runtime path, that is not handled by webpack, will work. The downfall is that if you change the way you build your project you'll need to update all references (in your case _nuxt). Note: if using vue.cli, you usually get a folder called static that is used exactly for this.
Use a binding, but bind to the run time path. This has the same downside as above and also the following: webpack has cache-busting technics that mangle file names, so knowing the final name of an asset that is handled this way by webpack is virtually impossible.

It worked also with backticks:
<img :src="require(`#/assets/images/projects/` + project.image)" width="100px" />
Thanks guys!

Related

(Vue/Vite) script type="module" not running when loaded in iframe

I'm experimenting with Vite, VueJS 3 and vite-plugin-singlefile for an app which is bundled to a single HTML file, and then served inside a sandboxed iframe through a parent site I don't have much control to change.
Specifically, the iframe runs sandboxed with <iframe src="someotherorigin/page.html" sandbox="allow-scripts allow-same-origin allow-forms">. My built HTML page references some external scripts via CDN (e.g. Bootstrap, etc), but the actual app code itself is inlined.
The app works fine with Vite's dev server and build+serve option. It's also fine when I preview in other tools... But in the target environment it seems like the main entrypoint script simply doesn't run. Nothing renders but also no error messages in console. I do get a couple of warnings about malformed CSP, but that's all:
Content Security Policy: Interpreting none as a hostname, not a keyword. If you intended this to be a keyword, use 'none' (wrapped in single quotes).
Content Security Policy: Interpreting https://none as a hostname, not a keyword. If you intended this to be a keyword, use 'none' (wrapped in single quotes).
This question got me curious so I tried manually editing the built index.html to change the inlined <script type="module">...</script> to <script>...</script> - And it works fine!
...But:
I can't make that change in the source index.html (Vite just ignores & refuses to bundle the TypeScript /src/main.ts source unless "module" is set)
I don't think there's an easy way to automate changing it in the build pipeline either (seems like it'd be messing around with custom Vite plugins)
I don't really understand what's wrong in the first place - why would type="module" cause the iframe not to run this Vue-generated script? Is there some other more proper fix?

Vue.js - Why should I put images on /assets instead of putting them directly in /public

When I use the #vue/cli to create a vuejs project, I see that there is a folder /assets that contains images and whatever I want. Than they can be referenced in the html such as <img src="#/assets/images/home.png" /> or import it on the js part.
My question is, why can't I just put the assets in /public/assets and put directly <img src="/assets/images/home.png" /> in my code? Where is the advantage of these assets?
It allows Webpack to handle assets, which means it can merge/minify files (useful for JS and CSS), optimize images, and more importantly version them so that cache handling is improved.

VueJS & v-html: How do I prepend the baseURL to asset links in imported HTML?

Apologies if this explanation isn't super clear. I am new to VueJS and I will do my best to explain my predicament.
I am building an application that imports html from external files into a component using v-html. I achieved that without any problems. However, the html has a bunch of asset urls that start with a /. What I want to do is to ensure that every asset url that starts with a / has a baseURL placed in front of it, converting it from say '/some-folder/some-asset.jpg' to '../../static/some-folder/some-asset.jpg' automatically without me having to programmatically modify the url.
I have tried modifying settings in the configuration file index.js, namely by trying different urls in assetsSubDirectory and assetsPublicPath but without success.
This is an example of a node in the imported html:
<picture>
<source media="(min-width: 768px)"srcset="/media/3974/cover_tablet.jpg">
<img id="img_20921" src="/media/3973/cover-mobile.jpg" class="img-fluid">
</picture>
I am trying to change the src value of:
/media/3974/cover_tablet.jpg
to:
../../static/media/3974/cover_tablet.jpg
by setting a base URL in the vue configuration but it won't prepend ../../static to the url and therefore the app cannot find the relevant asset.
The only way I managed to get this to work is by using JQuery to look for "src:" strings in the imported html and then prepend the baseURL into the link. This isn't at all ideal though and what I really want to achieve is for any link that starts with a "/" to have the apps baseURL prepended to it automatically.
I am starting to wonder if it is even possible for urls in imported html to pick up the apps baseURL value?
I found a solution. I was using the webpack template for my project which proved to make this particular issue difficult to work out so I reset my project using the Vue CLI template instead, put all my assets in the public folder and all the links in my imported html were picked up immediately with no fuss and no messing around with webpack configuration.

Compile a ".vue" component in browser, with JS only?

I'd like to compile ".vue" components (with contains html/js/css) into JS, but in browser side, without browserify/vuify/webpack or others ...
In a better world, i'd like to include my ".vue" component into my html app, like that, withoud need of compile things, server side:
<script type="vuejs/component" src="myComp.vue"></script>
It should be possible ?! no ?
(And I can't imagine that no one got this idea, or have done it already)
In fact, it's possible with http-vue-loader :
https://github.com/FranckFreiburger/http-vue-loader
It doesn't make sense to compile in the browser when it's so much more efficient to just pre-compile your component locally instead of relying on a visitor's client to do it.
In fact, the answer above regarding vue-http-loader says it's only for use in development and links to this article: https://vuejs.org/2015/10/28/why-no-template-url/
With that said, I created a vue-cli template that lets you pre-compile .vue files into a single .js files you can use in the browser. The single JS file contains the template, script, and styles. It uses webpack, but it's super easy to run and watches your files as you edit them.
$ vue init RonnieSan/vue-browser-sfc my-project
Repo at: https://github.com/RonnieSan/vue-browser-sfc
Instructions are in the README.

What files do webpack loaders work through?

When you set the regex after the test key in a loader object, does that look through all files in your project and load them using the loader you've designated, even if those files weren't required by the file in your entry point? Does this then get placed in the bundle.js file?
No it will only include what is required by your script.
<img src={ require('../some/img.png') } /> is a way to tell Webpack that your source code needs this image to run.
In a production Webpack build, this will get compiled into something like <img src="http://yoursite/whatever/89de0f2.png" />. The require() statement is never executed, it's replaced with valid Javascript code. This replaced code is what's put in bundle.js.
The image is then put into whatever output folder you specify (like a local dist/ folder), and it's renamed to something unique, which is usually some hash of the file contents, resulting in 89de0f2.png. (I made up this name for the example, but it usually looks something like that).
Now when you upload that file, 89de0f2.png, your source code will reference 89de0f2.png exactly, so that version of the image is guaranteed to exist. This is how Webpack gives you production guaranteed asset loading.
Wepback will only put img.png in your dist/ folder as 89de0f2.png if you specifically require it. Any other images will not be put in that folder.
You may also be asking about base64 encoding images and putting them directly into your bundle.js file. In this case, no image is put into dist/, but all the other rules reply. The require() call is still replaced with valid Javascript.
There is one case where Webpack will require multiple assets. You can require patterns, like <img src={ require.context( './images', true, /\.png/ ) } /> and Webpack will build all png files in that directory into the dist/ folder. See this Stackoverflow question for more context.