Vue.js动态图片在使用webpack时无法正常工作。

173
我有一个案例,在我的Vue.js和webpack的web应用程序中,我需要显示动态图片。我想要展示img标签,其中图片的文件名存储在一个变量中。这个变量是一个computed属性,它返回一个Vuex存储变量,该变量在beforeMount生命周期钩子函数中异步地被填充。
<div class="col-lg-2" v-for="pic in pics">
   <img v-bind:src="'../assets/' + pic + '.png'" v-bind:alt="pic">
</div>

然而,当我只是这样做时,它完美地运行。
<img src="../assets/dog.png" alt="dog">

我的情况与这个fiddle类似,但是这里使用的是图片URL,而我用实际文件路径时无法工作。

正确的方法应该是什么?


6
已解决。这个也解决了问题: <v-img :src="require(@/assets/ + items.image)" height="200px"></v-img> - Mohamed Raza
23个回答

156

我按照以下代码使它工作起来了

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

在 HTML 中:

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

但是我不确定为什么我的早期方法没有起作用。


10
根据nickmessing的说法:“v-bind内的表达式在运行时执行,而webpack别名在编译时执行。” https://github.com/vuejs/vue-loader/issues/896 - antoine
@Grigio对这个答案有一个不错的增强功能:https://dev59.com/Q1kS5IYBdhLWcg3wAiR8#47480286 - samlandfried
2
问题在于路径不存在。整个Vue应用程序由Webpack编译成单个脚本(图像通常也会被重命名,使用内容的哈希值)。使用require.context,您可以强制文件被Webpack看到并解析为结果图像。在浏览器中检查工作链接,您就会明白我的意思。这是一个很好的解决方案。 - estani
1
如果我不想让我的图片在资产文件夹中怎么办?如果它们只存在于网站的公共文件夹中,因为它们是管理员区域用户上传的呢? - thinsoldier
1
你好,我尝试了这个但是失败了,然后我找到了另一个解决方案,使用模板字面量。它可以工作,我想分享一下: https://dev59.com/71MH5IYBdhLWcg3w_WZ_ - lin
显示剩余3条评论

146

这里有一个 Webpack 将使用的简写,因此您无需使用 require.context

HTML:

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

Vue 方法:

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

我发现这里的前两段很好地解释了为什么会有效?

请注意,最好将宠物图片放在一个子目录中,而不是和其他所有图像资源混在一起。像这样:./assets/pets/


<v-img :src="require(@/assets/ + items.image)" height="200px"></v-img> 这个也解决了问题。 - Mohamed Raza
这是经过多次尝试后唯一可行的解决方案。 - makkus
这里每个答案的秘密都在于它们在require方法中有../assets,请查看https://dev59.com/71MH5IYBdhLWcg3w_WZ_获取更多详细信息。 - Bhaskar
这是唯一对我有效的解决方案。 - nth-child
2
在Vue3中,require未定义 :( - nowox

91

你可以尝试使用require函数,像这样:

<img :src="require(`@/xxx/${name}.png`)" alt class="icon" />

@ 符号指向 src 目录。

来源:Vue URL 转换规则


为什么需要那个 @ 符号? - harm
3
在使用 Resolve | webpack 时,@ 符号并不是必需的,通常它代表你的 src 目录(vue-cli 已经配置好了这个)。 - feng zhang

45
有另一种方法可以实现,即将您的图像文件添加到公共文件夹而不是资源文件夹,并将其作为静态图像进行访问。
<img :src="'/img/' + pic + '.png'" alt="pic" >

这是你需要放置静态图片的地方:

enter image description here


除了我省略了v-bind的alt标签外,这对我很有效。 - StefanBob
1
这节省了我很多麻烦,我得到了奇怪的错误:使用require不能找到模块'./undefined',谢谢。 - drakkar
1
我认为这是正确的方式,而不是使用资产文件夹。 - jukenduit
1
动态 require 在最新的 Vue2 中对我也没有起作用。 - goodniceweb
1
在Vue3中,require对我也不起作用,但这是一个惊人的解决方案。 - Ngọc Nguyễn

13

最好的方法是使用简单的方法来构建给定索引处的图像的正确字符串:

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

然后在你的v-for内部执行以下操作:

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

这是JSFiddle链接(显然图片不显示,所以我已经将图片的src放到了图片旁边):

https://jsfiddle.net/q2rzssxr/


真的吗?这里有一个使用实际图片的示例,似乎工作正常:https://jsfiddle.net/q2rzssxr/1/ - craig_h
不确定为什么,但我通过我在另一个答案中编写的代码使其工作。你的例子甚至可以在没有这个函数的情况下工作,看这里:https://jsfiddle.net/9a6Lg2vd/1/ - Saurabh
在我的情况下,pics 是使用 Vuex store 异步填充的,可能与此有关,我尝试模拟它,但没有成功:https://jsfiddle.net/9a6Lg2vd/2/ - Saurabh
我的情况更像是这个:https://jsfiddle.net/9a6Lg2vd/4/,但在我的本地,pets从一个ajax调用中获取数据,但图像不会被渲染。 - Saurabh
这个也可以运行:https://jsfiddle.net/9a6Lg2vd/5/,不确定为什么使用文件路径时无法运行。 - Saurabh

9
Vue.js使用vue-loader,这是一个为WebPack设计的加载器,它在编译时设置为重写/转换路径,以允许您不必担心静态路径在不同部署之间的差异(本地、开发、一种托管平台或另一种),从而使您能够使用相对本地文件系统路径。它还添加了其他好处,如资产缓存和版本控制(您可以通过检查实际生成的src URL来看到这一点)。
因此,将通常由vue-loader/WebPack处理的src设置为在运行时评估的动态表达式将绕过此机制,并且在实际部署环境中生成的动态URL将无效(除非它完全限定,这是一个例外)。
如果您使用动态表达式中的require函数调用,vue-loader/WebPack将看到它并应用通常的魔法。
例如,以下内容将不起作用:
<img alt="Logo" :src="logo" />

computed: {
    logo() {
        return this.colorMode === 'dark'
               ? './assets/logo-dark.png'
               : './assets/logo-white.png';
    }
}

虽然这样做可以运行:

<img alt="Logo" :src="logo" />

computed: {
    logo() {
        return this.colorMode === 'dark'
               ? require('./assets/logo-dark.png')
               : require('./assets/logo-white.png');
    }
}

我刚刚发现这个问题,花了一个小时才解决……不过人活着就是要学习,对吧?


8
我也遇到了这个问题,似乎最受欢迎的两个答案都可以解决,但是有一个小问题,Webpack会在浏览器控制台中抛出一个错误(Error: Cannot find module './undefined' at webpackContextResolve),这并不好看。
所以我采用了一种略微不同的解决方法。在require语句内部使用变量的整个问题在于,在打包期间执行require语句,并且该语句内的变量仅在应用程序在浏览器中执行时出现。因此,无论如何webpack都将所需的图像视为undefined,因为在编译期间该变量不存在。
我的做法是在require语句中放置随机图像,并在CSS中隐藏该图像,这样任何人都看不到它。
// 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}

图像作为信息的一部分通过Vuex从数据库中获取,并将其映射到组件作为计算属性。

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

一旦有了这些信息,我就会将初始图像替换为真实图像。

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

6
<div
    v-for="(data, i) in statistics"
    :key="i"
    class="d-flex align-items-center"
  >
    <img :src="require('@/assets/images/'+ data.title + '.svg')" />
    <div class="ml-2 flex-column d-flex">
      <h4 class="text-left mb-0">{{ data.count }}</h4>
      <small class="text-muted text-left mt-0">{{ data.title }}</small>
    </div>
  </div>

6
这里是非常简单的答案。 :D
<div class="col-lg-2" v-for="pic in pics">
   <img :src="`../assets/${pic}.png`" :alt="pic">
</div>

6
<img src="../assets/graph_selected.svg"/>

Webpack会通过loader将静态路径解析为模块依赖。但是对于动态路径,您需要使用require来解析路径。然后,您可以使用布尔变量和三元表达式在不同的图像之间进行切换。

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

当然,通过一些事件处理程序切换布尔值的值。


谢谢,这个有效::src="require(../assets/category_avatar/baby_kids.jpeg)" - Mohamed Raza

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接