Vue 静态文件引入完全详解(附避坑指南,Vue 2/3 通用)

前言

在 Vue 项目开发中,我们经常需要引入各类静态文件 ------ 图片、字体、JS/CSS 插件、JSON 数据等。很多开发者(尤其是新手)会遇到「文件路径正确却加载失败」「打包后文件丢失」「动态引入不生效」等问题,核心原因是没搞懂 Vue 项目中两个静态文件目录的差异,以及不同场景下的正确引入方式

本文将从「静态文件目录核心区别」入手,详细讲解 Vue 中各类静态文件的引入方法,覆盖「模板中引入」「脚本中引入」「样式中引入」三大场景,同时梳理常见坑点与解决方案,Vue 2 和 Vue 3 项目通用,看完就能彻底解决静态文件引入的所有问题。

一、先搞懂:Vue 中的两个核心静态文件目录

Vue 项目(无论是 Vue CLI 搭建的 Vue 2 还是 Vue 3)默认提供两个存放静态文件的目录:publicsrc/assets,这两个目录的编译处理方式、访问规则、使用场景完全不同,这是解决静态文件引入问题的基础。

1. 目录 1:src/assets(源码内静态资源)

  • 核心特性 :被 webpack (Vue CLI 底层构建工具)编译处理、打包压缩、路径重写。

  • 具体行为

    1. 文件会被 webpack 解析,图片等资源会被压缩优化(如图片体积压缩、重命名为哈希值文件名,防止缓存问题)。
    2. 打包后,文件会被放入 dist/assets 目录下(文件名可能被修改,如 logo.pnglogo.2e8f9d.png)。
    3. 路径是「相对路径」,需要遵循 webpack 的模块解析规则,不能直接通过绝对路径访问。
  • 使用场景:项目内使用的「业务相关静态资源」,如组件内图片、全局样式、自定义字体、业务相关 JSON 数据等。

  • 示例目录结构

    复制代码
    src/
    ├── assets/
    │   ├── images/  # 组件内使用的图片
    │   │   ├── logo.png
    │   │   └── bg.jpg
    │   ├── styles/  # 全局样式文件
    │   │   └── global.css
    │   └── fonts/   # 自定义字体文件
    │       └── iconfont.ttf

2. 目录 2:public(纯静态资源)

  • 核心特性 :不被 webpack 编译处理,直接复制到打包后的 dist 根目录,保持原文件名和目录结构。

  • 具体行为

    1. 文件会被「原样复制」到 dist 根目录,不会被压缩、重命名,目录结构也会保留(如 public/js/third.jsdist/js/third.js)。
    2. 可以通过「绝对路径」直接访问,根路径对应 public 目录(即 / 等价于 public/)。
    3. 不支持 webpack 的模块解析,无法在 src 源码中通过 importrequire 引入。
  • 使用场景

    1. 第三方不可修改的静态资源(如第三方 JS 插件、CDN 备用文件)。
    2. 需要通过绝对路径访问的文件(如 favicon.icoindex.html 中引入的全局 JS/CSS)。
    3. 动态拼接路径访问的静态资源(如后端返回文件名称,前端拼接路径加载图片)。
  • 示例目录结构

    复制代码
    public/
    ├── favicon.ico  # 网站图标
    ├── js/           # 第三方 JS 插件
    │   └── echarts.min.js
    └── images/       # 需绝对路径访问的图片
        └── banner.png

3. 核心区别对比表

对比维度 src/assets public
webpack 处理 编译、压缩、重命名 不处理,原样复制
打包后目录 dist/assets/(哈希文件名) dist/ 根目录(原文件名)
访问方式 相对路径(import/require) 绝对路径(/ 开头)
缓存处理 哈希文件名,自动防缓存 需手动配置缓存(如添加版本号)
适用场景 项目内业务静态资源 第三方资源、绝对路径访问资源

二、实操:各类静态文件的正确引入方式

下面按「引入场景」和「文件类型」拆分,详细讲解每种静态文件的正确引入方法,涵盖 Vue 模板、脚本、样式三大核心场景。

场景 1:在 Vue 模板(template)中引入静态文件

模板中主要引入图片、视频等可视化资源,分 assets 目录和 public 目录两种情况。

1. 引入 src/assets 下的文件

模板中引入 assets 下的文件,有两种常用方法,核心是「通过相对路径绑定」。

方法 1:直接使用相对路径(推荐,简单场景)

语法:src="./assets/xxx/xxx"(路径相对于当前组件文件)。

注意:./ 表示当前目录,../ 表示上级目录,需根据组件与 assets 的相对位置调整路径。

示例(组件 src/components/HelloWorld.vue):

复制代码
<template>
  <div class="hello">
    <!-- 引入 src/assets/images/logo.png -->
    <img src="../assets/images/logo.png" alt="Vue 图标" class="logo">
  </div>
</template>

<style scoped>
.logo {
  width: 100px;
  height: 100px;
}
</style>
方法 2:通过 require() 绑定(推荐,动态场景)

语法::src="require('@/assets/xxx/xxx')"(使用 v-bind 动态绑定,@ 是 Vue CLI 配置的 src 目录别名)。

优势:支持动态拼接部分路径(有限制),更灵活;@ 等价于 src/,无需关心相对目录层级,避免路径写错。

复制代码
<template>
  <div class="hello">
    <!-- 引入 src/assets/images/bg.jpg,@ 等价于 src/ -->
    <img :src="require('@/assets/images/bg.jpg')" alt="背景图" class="bg">
    
    <!-- 简单动态引入:根据变量切换图片(文件名动态,目录固定) -->
    <img :src="require(`@/assets/images/${imgName}.png`)" alt="动态图片">
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      imgName: 'logo' // 动态切换图片文件名
    }
  }
}
</script>

注意:require()目录部分必须是固定的 ,不能全部动态拼接(如 require(@/assets/images/${path}) 中,@/assets/images/ 必须固定,${path} 是文件名),否则 webpack 无法在编译时解析文件。

2. 引入 public 下的文件

模板中引入 public 下的文件,直接使用「绝对路径」(/ 开头,对应 public 目录),无需关心相对路径。

语法:src="/xxx/xxx"/ 等价于 public/,后续路径对应 public 内的目录结构)。

复制代码
<template>
  <div class="hello">
    <!-- 引入 public/images/banner.png -->
    <img src="/images/banner.png" alt="横幅图" class="banner">
    
    <!-- 引入 public/favicon.ico -->
    <link rel="icon" href="/favicon.ico">
  </div>
</template>

注意:不要在模板中使用 ./../ 访问 public 下的文件,必须以 / 开头,否则打包后会路径失效。

场景 2:在 Vue 脚本(script)中引入静态文件

脚本中主要引入 JSON 数据、图片路径(用于动态绑定)、JS 插件等,同样分 assetspublic 目录。

1. 引入 src/assets 下的文件
(1)引入 JSON 数据文件

直接使用 import 引入,webpack 会自动解析 JSON 文件,返回解析后的对象。

示例(src/assets/data/user.json 存在 JSON 文件):

json

复制代码
// src/assets/data/user.json
{
  "name": "张三",
  "age": 28,
  "gender": "男"
}

vue

复制代码
<script>
// 引入 src/assets/data/user.json
import userData from '@/assets/data/user.json';

export default {
  name: 'HelloWorld',
  mounted() {
    console.log('用户数据:', userData); // 输出:{ name: "张三", age: 28, gender: "男" }
  }
}
</script>
(2)引入图片 / 其他资源(用于动态绑定)

使用 require() 引入,返回文件的「打包后路径」,可赋值给变量用于模板动态绑定。

复制代码
<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      // 引入 src/assets/images/logo.png,返回打包后的路径
      logoUrl: require('@/assets/images/logo.png'),
      // 动态切换图片(目录固定,文件名动态)
      imgList: [
        require('@/assets/images/img1.png'),
        require('@/assets/images/img2.png')
      ]
    }
  }
}
</script>

<template>
  <div>
    <img :src="logoUrl" alt="图标">
    <img :src="imgList[0]" alt="图片1">
  </div>
</template>
(3)引入 JS/CSS 样式文件

使用 import 直接引入,webpack 会自动将 CSS 注入到页面,JS 执行并合并打包。

复制代码
<script>
// 引入 src/assets/styles/global.css
import '@/assets/styles/global.css';

// 引入 src/assets/js/utils.js(自定义工具类)
import { formatDate } from '@/assets/js/utils.js';

export default {
  name: 'HelloWorld',
  mounted() {
    console.log(formatDate(new Date())); // 调用工具类方法
  }
}
</script>
2. 引入 public 下的文件

public 下的文件无法在脚本中通过 importrequire 引入(不被 webpack 解析),若需在脚本中访问,只能通过「绝对路径字符串」拼接,适用于动态加载场景。

示例(动态加载 public/images/ 下的图片,文件名由后端返回):

复制代码
<script>
export default {
  name: 'HelloWorld',
  data() {
    return {
      // 后端返回的文件名
      imgFileName: 'banner.png',
      // 拼接绝对路径(/ 对应 public 目录)
      imgUrl: ''
    }
  },
  mounted() {
    this.imgUrl = `/images/${this.imgFileName}`; // 最终路径:/images/banner.png
  }
}
</script>

<template>
  <img :src="imgUrl" alt="动态加载图片">
</template>

补充:若需引入 public 下的 JS 插件(如 public/js/echarts.min.js),推荐在 public/index.html 中直接通过 <script> 标签引入,全局可用。

场景 3:在 CSS 样式中引入静态文件

CSS 中主要引入背景图、自定义字体等资源,分 assetspublic 目录,路径写法有差异。

1. 引入 src/assets 下的文件

使用「相对路径」或「~@ 路径」(~ 表示 webpack 模块根目录,@ 等价于 src/),推荐使用 ~@,无需关心 CSS 文件与 assets 的相对层级。

复制代码
/* 方法 1:使用 ~@ 路径(推荐,无需关心相对层级) */
.bg {
  width: 100%;
  height: 300px;
  /* 引入 src/assets/images/bg.jpg */
  background: url(~@/assets/images/bg.jpg) no-repeat center center;
  background-size: cover;
}

/* 方法 2:使用相对路径(需根据 CSS 文件与 assets 的相对位置调整) */
/* 当前 CSS 文件路径:src/assets/styles/global.css */
/* 目标图片路径:src/assets/images/bg.jpg */
/* 相对路径:../images/bg.jpg(../ 回到 assets 目录,再进入 images) */
.bg2 {
  width: 100%;
  height: 300px;
  background: url(../images/bg.jpg) no-repeat center center;
  background-size: cover;
}

/* 引入自定义字体(src/assets/fonts/iconfont.ttf) */
@font-face {
  font-family: 'iconfont';
  src: url(~@/assets/fonts/iconfont.ttf) format('truetype');
}

.icon {
  font-family: 'iconfont';
}
2. 引入 public 下的文件

CSS 中引入 public 下的文件,使用「绝对路径」(/ 开头,对应 public 目录),注意:CSS 中的绝对路径是「相对于页面根目录」,与模板中的绝对路径规则一致。

复制代码
/* 引入 public/images/banner.png */
.banner {
  width: 100%;
  height: 400px;
  background: url(/images/banner.png) no-repeat center center;
  background-size: cover;
}

注意:CSS 中引入 public 下的文件,不要使用 http://https:// 硬编码绝对路径,否则打包后部署到不同域名会失效,使用 / 开头的相对根路径即可。

三、常见坑点与避坑指南

坑点 1:assets 下的文件路径写错,导致加载失败

  • 问题表现:本地开发时提示「Module not found」,或打包后图片显示 404。
  • 原因 :相对路径层级错误,或未使用 @/require() 引入。
  • 解决方案
    1. 优先使用 @ 表示 src/ 目录,避免相对路径层级错误(如 @/assets/images/logo.png)。
    2. 动态引入时必须使用 require(),不能直接拼接字符串(如 :src="@/assets/images/${imgName}.png" 错误,应使用 :src="require(@/assets/images/${imgName}.png)")。

坑点 2:public 下的文件使用相对路径引入,打包后失效

  • 问题表现:本地开发时能正常加载,打包后部署到服务器显示 404。
  • 原因public 下的文件未使用 / 开头的绝对路径,而是使用 ./../ 相对路径。
  • 解决方案 :所有 public 下的文件,引入时均以 / 开头(如 /images/banner.png),对应打包后的 dist 根目录。

坑点 3:动态加载 assets 下的文件,全部路径动态拼接

  • 问题表现:webpack 编译报错「Critical dependency: the request of a dependency is an expression」。

  • 原因require() 中全部路径都是动态的,webpack 无法在编译时解析文件(webpack 需要固定目录,才能提前打包)。

  • 解决方案require()目录部分必须固定 ,只有文件名可以动态拼接,示例:

    复制代码
    // 正确:目录固定(@/assets/images/),文件名动态(${imgName})
    require(`@/assets/images/${imgName}.png`);
    
    // 错误:全部路径动态
    require(`${imgPath}.png`);

坑点 4:打包后 assets 下的图片文件名变成哈希值,无法通过固定名称访问

  • 问题表现 :打包后图片文件名变成 logo.2e8f9d.png,后端返回固定文件名 logo.png,无法匹配。
  • 原因assets 下的文件被 webpack 重命名优化,用于防缓存。
  • 解决方案 :将这类需要「固定文件名访问」的图片移到 public 目录,通过绝对路径访问,避免 webpack 重命名。

坑点 5:自定义字体文件引入后,打包后显示乱码 / 不生效

  • 问题表现:本地开发时字体正常,打包后字体不生效,浏览器控制台提示字体文件 404。
  • 原因:webpack 对字体文件的解析配置缺失,或字体文件路径写错。
  • 解决方案
    1. 确保字体文件放在 src/assets/fonts/ 下,使用 ~@ 路径引入。
    2. Vue CLI 已默认配置字体文件解析,无需额外配置,若手动修改 vue.config.js,需确保包含字体文件后缀(ttfwoffwoff2 等)。

四、总结与核心要点回顾

  1. 两个核心目录src/assets(webpack 编译处理,相对路径引入)、public(原样复制,绝对路径引入),根据文件使用场景选择对应目录。
  2. 引入核心原则
    • src/assets:模板中用相对路径 /require(),脚本中用 import/require(),CSS 中用 ~@/ 相对路径。
    • public:所有场景均用 / 开头的绝对路径,不支持 import/require()
  3. 避坑核心 :动态引入 assets 目录文件时,目录必须固定;public 目录文件必须用绝对路径;避免打包后路径失效。
  4. 最佳实践 :业务相关静态资源优先放入 src/assets,享受 webpack 优化;第三方资源、需要固定文件名访问的资源放入 public
相关推荐
mCell11 小时前
如何零成本搭建个人站点
前端·程序员·github
mCell12 小时前
为什么 Memo Code 先做 CLI:以及终端输入框到底有多难搞
前端·设计模式·agent
恋猫de小郭12 小时前
AI 在提高你工作效率的同时,也一直在增加你的疲惫和焦虑
前端·人工智能·ai编程
少云清12 小时前
【安全测试】2_客户端脚本安全测试 _XSS和CSRF
前端·xss·csrf
萧曵 丶12 小时前
Vue 中父子组件之间最常用的业务交互场景
javascript·vue.js·交互
银烛木12 小时前
黑马程序员前端h5+css3
前端·css·css3
m0_6070766012 小时前
CSS3 转换,快手前端面试经验,隔壁都馋哭了
前端·面试·css3
听海边涛声12 小时前
CSS3 图片模糊处理
前端·css·css3
IT、木易12 小时前
css3 backdrop-filter 在移动端 Safari 上导致渲染性能急剧下降的优化方案有哪些?
前端·css3·safari
0思必得013 小时前
[Web自动化] Selenium无头模式
前端·爬虫·selenium·自动化·web自动化