记一次Vue项目的相对路径问题

Tch
Tch
Published on 2025-02-17 / 16 Visits
0
0

1. 效果图

如图是我想要的效果,这是两个菜单页面,在页面标题前会有一个小图标。

EB307829-2B3D-45EC-AF15-DBAB184AAC5B.png

1. 先看代码

这是路由配置文件,是这两个页面的一些配置,其中meta.icon是图标的相对路径。

/src/router/index.ts

import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/taskList',
      name: 'TaskList',
      component: () => import('../views/task/TaskList.vue'),
      meta: {title: '任务列表', menu: true, show: true, icon: './assets/list.png'}
    },
    {
      path: '/addTask',
      name: 'AddTask',
      component: () => import('../views/task/AddTask.vue'),
      meta: {title: '添加任务', menu: true, show: true, icon: './assets/add.png'}
    }
  ]
})

export default router

在项目路径下,这两个图片是在/src/assets/确实存在的。

App.vue中,侧边栏容器显示菜单图标,图片的路径route.meta.icon来自路由配置文件,通过getIcon()方法返回图标的绝对路径。

<template>
  <div class="app">
        <!--header 顶栏容器-->
        <el-container class="middle">
          <!--aside 侧边栏容器-->
          <el-aside class="aside">
            <el-menu router default-active="1">
              <el-menu-item v-for="route in routes" :key="route.path" :index="route.path">
                <div class="menu-item">
                  <img :src="getIcon(route.meta.icon)" :alt="route.meta.title" class="menu-icon">
                  {{ route.meta.title }}
                </div>
              </el-menu-item>
            </el-menu>
          </el-aside>
          <!--main 主要区域容器-->
        </el-container>
        <!--footer 底栏容器-->
      </el-container>
    </div>
  </div>
</template>

<script>
    const getIcon = (icon) => {
      return new URL(icon, import.meta.url).href
    }
</script>

2. 发现问题

在开发环境中这样获取图标的绝对路径是没问题的,图标的路径如下。

但是在生产环境中却加载不到图标,路径是错的。

图标绝对路径如下,执行npm run build之后构建的/dist目录下也不存在这个路径。


3. 分析问题

以下是来自DeepSeek的回答。

App.vue中的getIcon函数使用new URL(icon, import.meta.url).href。这里的import.meta.url是当前模块(App.vue)的URL。在开发环境中,App.vue可能位于项目的根目录,所以./assets相对于App.vue是正确的。但是在生产环境中,当资源被打包后,App.vue的位置可能发生变化,导致相对路径解析错误,从而生成错误的URL。

生产环境下,打包后的结构是dist目录,App.vue可能被编译到assets目录下的某个JS文件中,这时候new URL('./assets/list.png', import.meta.url)会解析到assets目录下的assets/list.png,导致路径重复。

开发环境:Vite服务器直接提供源码,相对路径./assets/list/png能正确映射到src/assets目录。

生产环境:Vite打包时会对静态资源进行哈希处理并重新组织目录结构,直接实录字符串路径无法动态适配这些变化。

通过import引入图片,vite会在构建时将其识别为静态依赖,自动处理路径并替换为生产环境下的正确路径,确保引用无误。

4. 解决问题

所以,问题还是出在路径的解析上。

在路由配置中,使用import语句引入图片,在meta.icon中使用引入后的路径。

/src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import listIcon from '../assets/list.png'
import addIcon from '../assets/add.png'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/taskList',
      name: 'TaskList',
      component: () => import('../views/task/TaskList.vue'),
      meta: {title: '任务列表', menu: true, show: true, icon: listIcon}
    },
    {
      path: '/addTask',
      name: 'AddTask',
      component: () => import('../views/task/AddTask.vue'),
      meta: {title: '添加任务', menu: true, show: true, icon: addIcon}
    }
  ]
})

export default router

App.vue中,直接使用route.meta.icon作为图标的src

<img :src="route.meta.icon" :alt="route.meta.title" class="menu-icon">

5. 测试一下

5.1 开发环境

5.2 生产环境

以上。


Comment