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?


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.


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/


`<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" />

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


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/


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;
  }
}

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>

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
},

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.


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? 😊


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>

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">

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>
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.