As imagens dinâmicas do Vue.js não funcionam

100

Eu tenho um caso onde em minha Vue.jscom webpackaplicativo web, eu preciso exibir imagens dinâmicas. Eu quero mostrar imgonde o nome do arquivo de imagens são armazenados em uma variável. Essa variável é uma computedpropriedade que está retornando uma Vuexvariável de armazenamento, que está sendo preenchida de forma assíncrona em beforeMount.

<div class="col-lg-2" v-for="pic in pics">
   <img v-bind:src="'../assets/' + pic + '.png'" v-bind:alt="pic">
</div>

No entanto, funciona perfeitamente quando eu apenas faço:

<img src="../assets/dog.png" alt="dog">

Meu caso é semelhante a este fiddle , mas aqui funciona com img URL, mas no meu com caminhos de arquivo reais, não funciona.

Qual deve ser a maneira correta de fazer isso?

Saurabh
fonte
1
resolveu `<v-img: src =" require ( @/assets/+ items.image) "height =" 200px "> </v-img>` este também resolveu o problema
Mohamed Raza

Respostas:

104

Eu fiz isso funcionar seguindo o código

  getImgUrl(pet) {
    var images = require.context('../assets/', false, /\.png$/)
    return images('./' + pet + ".png")
  }

e em HTML:

<div class="col-lg-2" v-for="pic in pics">
   <img :src="getImgUrl(pic)" v-bind:alt="pic">
</div>

Mas não sei por que minha abordagem anterior não funcionou.

Saurabh
fonte
7
De acordo com o apelido: "A expressão dentro de v-bind é executada em tempo de execução, aliases de webpack em tempo de compilação." github.com/vuejs/vue-loader/issues/896
antoine
@Grigio tem um bom aprimoramento para esta resposta: stackoverflow.com/a/47480286/5948587
samlandfried
1
O problema é que o caminho não existe. Todo o aplicativo vue é compilado pelo webpack em um único script (e as imagens também são renomeadas normalmente, usando um hash do conteúdo). Usando require.context, você força os arquivos a serem vistos pelo webpack e resolvem para a imagem resultante. Verifique o link de trabalho no navegador e você verá o que quero dizer. Ótima solução.
Estani
E se eu não quiser que minhas imagens estejam na pasta de ativos? E se eles só existirem na pasta pública do site porque foram carregados por usuários da área administrativa?
thinsoldier
Olá, eu tentei isso, mas falhei, então encontrei outra solução que usa literais de modelo. Pode funcionar e eu quero compartilhar isto: stackoverflow.com/questions/57349167/…
lin
87

Aqui está um atalho que o webpack usará para que você não precise usar require.context.

HTML:

<div class="col-lg-2" v-for="pic in pics">
    <img :src="getImgUrl(pic)" v-bind:alt="pic">
</div>

Método Vue:

getImgUrl(pic) {
    return require('../assets/'+pic)
}

E eu acho que os primeiros 2 parágrafos aqui explicam por que isso funciona? bem.

Observe que é uma boa ideia colocar as fotos do seu animal de estimação em um subdiretório, em vez de inseri-las com todos os outros ativos de imagem. Igual a:./assets/pets/

Artur Grigio
fonte
`<v-img: src =" require ( @/assets/+ items.image) "height =" 200px "> </v-img>` este também resolveu o problema
Mohamed Raza
Esta foi a única solução que funcionou depois de muitas tentativas ..
makkus
45

Você pode experimentar a requirefunção. como isso:

<img :src="require(`@/xxx/${name}.png`)" alt class="icon" />
feng zhang
fonte
Por que esse @símbolo é obrigatório?
dano em
1
@símbolo não é necessário, muitas vezes representa seu srcdiretório ao usar Resolve | webpack (vue-cli já está configurando isso.).
feng zhang
21

Há outra maneira de fazer isso adicionando seus arquivos de imagem à pasta pública em vez de ativos e acessá-los como imagens estáticas.

<img :src="'/img/' + pic + '.png'" v-bind:alt="pic" >

É aqui que você precisa colocar suas imagens estáticas:

insira a descrição da imagem aqui

Rukshan Dangalla
fonte
Isso funcionou para mim, exceto para a tag alt
Omiti
1
salvou-me de muita dor, recebi um erro estranho: Não é possível encontrar o módulo './undefined' usando require, obrigado
drakkar
1
Acho que esse é o caminho a percorrer, não a pasta de ativos.
jukenduit de
1
o requerimento dinâmico não funcionou para mim também no último Vue2
goodniceweb
8

Sua melhor aposta é apenas usar um método simples para construir a string correta para a imagem no índice fornecido:

methods: {
  getPic(index) {
    return '../assets/' + this.pics[index] + '.png';
  }
}

em seguida, faça o seguinte dentro do seu v-for:

<div class="col-lg-2" v-for="(pic, index) in pics">
   <img :src="getPic(index)" v-bind:alt="pic">
</div>

Aqui está o JSFiddle (obviamente as imagens não aparecem, então coloquei a imagem srcao lado da imagem):

https://jsfiddle.net/q2rzssxr/

craig_h
fonte
Mesmo? Aqui está um exemplo com imagens reais que parecem funcionar bem: jsfiddle.net/q2rzssxr/1
craig_h
Não tenho certeza do porquê, fiz funcionar pelo código que escrevi em outra resposta. Seu exemplo até funciona sem esta função, veja aqui: jsfiddle.net/9a6Lg2vd/1
Saurabh
No meu caso, as fotos estão sendo preenchidas de forma assíncrona usando a loja Vuex, pode ser que tenha algo a ver com isso, tentei simular, mas não funcionou: jsfiddle.net/9a6Lg2vd/2
Saurabh
Meu caso é mais parecido com este: jsfiddle.net/9a6Lg2vd/4 , mas no meu animal de estimação local os dados são preenchidos a partir de uma chamada de ajax, mas as imagens não são renderizadas.
Saurabh
Isso também funciona: jsfiddle.net/9a6Lg2vd/5 , não sei por que não funciona com caminhos de arquivo.
Saurabh
6

Eu também encontrei este problema e parece que as duas respostas mais votadas funcionam, mas há um pequeno problema, webpack lança um erro no console do navegador (Erro: Não é possível encontrar o módulo './undefined' em webpackContextResolve) o que não é muito bom.

Então, resolvi um pouco diferente. Todo o problema com a variável dentro da instrução require é que a instrução require é executada durante o empacotamento e a variável dentro dessa instrução aparece apenas durante a execução do aplicativo no navegador. Portanto, o webpack vê a imagem necessária como indefinida de qualquer maneira, já que durante a compilação essa variável não existe.

O que eu fiz foi colocar a imagem aleatória no comando require e esconder essa imagem no css, para que ninguém a visse.

// template
<img class="user-image-svg" :class="[this.hidden? 'hidden' : '']" :src="userAvatar" alt />

//js
data() {
  return {
    userAvatar: require('@/assets/avatar1.svg'),
    hidden: true
  }
}

//css
.hidden {display: none}

A imagem vem como parte das informações do banco de dados via Vuex e é mapeada para o componente como um computador

computed: {
  user() {
    return this.$store.state.auth.user;
  }
}

Assim que esta informação estiver disponível, troco a imagem inicial para a real

watch: {
  user(userData) {
    this.userAvatar = require(`@/assets/${userData.avatar}`);
    this.hidden = false;
  }
}
Alonad
fonte
4

Aqui está uma resposta muito simples. : D

<div class="col-lg-2" v-for="pic in pics">
   <img :src="`../assets/${pic}.png`" :alt="pic">
</div>
Diamante
fonte
3

Você pode usar o bloco try catch para ajudar com imagens não encontradas

getProductImage(id) {
          var images = require.context('@/assets/', false, /\.jpg$/)
          let productImage = ''
          try {
            productImage = images(`./product${id}.jpg`)
          } catch (error) {
            productImage = images(`./no_image.jpg`)
          }
          return productImage
},
Gabriel Fernandez
fonte
2
<img src="../assets/graph_selected.svg"/>

O caminho estático é resolvido pelo Webpack como uma dependência do módulo por meio do carregador. Mas para o caminho dinâmico, você precisa usar require para resolver o caminho. Você pode então alternar entre as imagens usando uma variável booleana e uma expressão ternária.

<img :src="this.graph ? require( `../assets/graph_selected.svg`) 
: require( `../assets/graph_unselected.svg`) " alt="">

E, claro, alternar o valor do booleano por meio de algum manipulador de eventos.

Ron16
fonte
Obrigado, isso funciona :src="require(../assets/category_avatar/baby_kids.jpeg)"
Mohamed Raza
2

Vue.js usa vue-loader, um carregador para WebPack que é configurado para reescrever / converter caminhos em tempo de compilação, a fim de permitir que você não se preocupe com caminhos estáticos que seriam diferentes entre as implantações (local, dev, uma plataforma de hospedagem ou outra) , permitindo que você use caminhos relativos do sistema de arquivos local . Ele também adiciona outros benefícios, como cache de ativos e controle de versão (você provavelmente pode ver isso verificando osrc URL está sendo gerado).

Portanto, ter um src que normalmente seria tratado por vue-loader / WebPack definido como uma expressão dinâmica, avaliada em tempo de execução, contornará esse mecanismo e o URL dinâmico gerado será inválido no contexto da implantação real (a menos que seja totalmente qualificado, isso é uma exceção )

Se, em vez disso, você usar uma requirechamada de função na expressão dinâmica, vue-loader/ WebPack a verá e aplicará a mágica usual.

Por exemplo, isso não funcionaria:

<img alt="Logo" :src="logo" />
computed: {
    logo() {
        return this.colorMode === 'dark'
               ? './assets/logo-dark.png'
               : './assets/logo-white.png';
    }
}

Embora isso funcione:

<img alt="Logo" :src="logo" />
computed: {
    logo() {
        return this.colorMode === 'dark'
               ? require('./assets/logo-dark.png')
               : require('./assets/logo-white.png');
    }
}

Eu só descobri isso sozinho. Demorou uma hora mas ... você vive, você aprende, certo? 😊

Paul-Sebastian Manole
fonte
0

bem, a melhor e mais fácil maneira que funcionou para mim foi buscar dados de uma API.

methods: {
       getPic(index) {
    return this.data_response.user_Image_path + index;
  }
 }

o método getPic pega um parâmetro que é o nome do arquivo e retorna o caminho absoluto do arquivo talvez do seu servidor com o nome do arquivo simples ...

aqui está um exemplo de um componente onde usei isto:

<template>
    <div class="view-post">
        <div class="container">
     <div class="form-group">
             <label for=""></label>
             <input type="text" class="form-control" name="" id="" aria-describedby="helpId" placeholder="search here">
             <small id="helpId" class="form-text user-search text-muted">search for a user here</small>
           </div>
           <table class="table table-striped ">
               <thead>
                   <tr>
                       <th>name</th>
                       <th>email</th>
                       <th>age</th>
                       <th>photo</th>
                   </tr>
                   </thead>
                   <tbody>
                       <tr v-bind:key="user_data_get.id"  v-for="user_data_get in data_response.data">
                           <td scope="row">{{ user_data_get.username }}</td>
                           <td>{{ user_data_get.email }}</td>
                           <td>{{ user_data_get.userage }}</td>
                           <td><img :src="getPic(user_data_get.image)"  clas="img_resize" style="height:50px;width:50px;"/></td>
                       </tr>

                   </tbody>
           </table>
        </div>

    </div>
</template>

<script>
import axios from 'axios';
export default {
     name: 'view',
  components: {

  },
  props:["url"],
 data() {
     return {
         data_response:"",
         image_path:"",
     }
 },
 methods: {
       getPic(index) {
    return this.data_response.user_Image_path + index;
  }
 },
 created() {
     const res_data = axios({
    method: 'post',
    url: this.url.link+"/view",
   headers:{
     'Authorization': this.url.headers.Authorization,
     'content-type':this.url.headers.type,
   }
    })
    .then((response)=> {
        //handle success
      this.data_response = response.data;
      this.image_path = this.data_response.user_Image_path;
       console.log(this.data_response.data)
    })
    .catch(function (response) {
        //handle error
        console.log(response);
    });
 },
}
</script>


<style scoped>

</style>
user8453321
fonte
0

Eu encontrei o mesmo problema. Isso funcionou para mim, alterando '../assets/' para './assets/'.

 <img v-bind:src="'./assets/' + pic + '.png'" v-bind:alt="pic">
Bex
fonte
0

Tentei todas as respostas aqui, mas o que funcionou para mim no Vue2 é assim.

<div class="col-lg-2" v-for="pic in pics">
   <img :src="require(`../assets/${pic.imagePath}.png`)" :alt="pic.picName">
</div>
Leipzy
fonte