You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

pnpm+Vue+Vite Monorepo:工作区包内别名路径解析异常

Monorepo共享包内部别名路径解析失败问题解决

问题概述

使用pnpm、TypeScript、Vue和Vite构建包含共享组件的Monorepo时,主应用导入共享包组件正常,但共享包内部使用@/别名导入自身文件时失败——Vite会将共享包内的@/解析到主应用的src目录,导致文件找不到。

目录结构

src/
|
|- apps/
|   |- app1/
|   |   |- env/
|   |   |- node_modules/
|   |   |- src/
|   |   |   |- plugins/
|   |   |   |   |- some-logic.ts
|   |   |   |- styles/
|   |   |   |   |- app.scss
|   |   |   |- views/
|   |   |   |   |- HomeView.vue
|   |   |   ...
|   |   |   |- App.vue
|   |   |   |- main.ts
|   |   |
|   |   |- index.html
|   |   |- package.json
|   |   |- tsconfig.json
|   |   |- vite.config.ts
|
|- packages/
|   |- shared-ui/
|   |   |- node_modules/
|   |   |- src/
|   |   |   |- components/
|   |   |   |   |- Header.vue
|   |   |   |- plugins/
|   |   |   |   |- another-logic.ts
|   |   |   |- styles/
|   |   |   |   |- header.scss
|   |   |- package.json
|   |   |- tsconfig.json
|
|- node_modules/
...
|- package.json
|- pnpm-lock.yaml
|- pnpm-workspace.yaml
|- tsconfig.base.json

错误示例

共享包Header.vue内使用别名导入失败:

<script setup lang="ts">
import moreStuff from '@/plugins/another-logic.ts' // 失败
moreStuff();
</script>

<template>
    <div class="header">...</div>
</template>

<style lang="scss">
// @import '@/styles/header.scss'; // 失败
@import '../styles/header.scss'; // 正常工作
</style>

错误信息

import/no-unresolved    Unable to resolve path to module '@/plugins/another-logic.ts'
import/no-unresolved    [vite] Internal server error: [sass] ENOENT: no such file or directory, open '/namespace/apps/app1/src/styles/header.scss'

相关配置文件

根目录package.json

{
  "name": "namespace",
  "private": true,
  "type": "module",
  "packageManager": "pnpm@8.6.9",
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead",
    "not ie <= 11"
  ],
  "devDependencies": {
    "@types/node": "~20.3.3",
    "@typescript-eslint/eslint-plugin": "~5.61.0",
    "@typescript-eslint/parser": "~5.61.0",
    "eslint": "~8.44.0",
    "eslint-config-prettier": "~8.8.0",
    "eslint-import-resolver-typescript": "~3.5.5",
    "eslint-plugin-import": "~2.27.5",
    "eslint-plugin-prettier": "~4.2.1",
    "eslint-plugin-vue": "~9.15.1",
    "prettier": "~2.8.8",
    "ts-node": "~10.9.1",
    "typescript": "~5.1.6",
    "vite": "~4.3.9",
    "vite-plugin-eslint": "~1.8.1",
    "vue-eslint-parser": "~9.3.1"
  }
}

pnpm-workspace.yaml

packages:
  - 'apps/*'
  - 'packages/*'

tsconfig.base.json

{
  "compilerOptions": {
    "target": "es6",
    "module": "esnext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "isolatedModules": true,
    "strict": true,
    "jsx": "preserve",
    "experimentalDecorators": true,
    "noEmit": false,
    "skipLibCheck": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "useDefineForClassFields": true,
    "sourceMap": true,
    "lib": [
      "esnext",
      "dom",
      "dom.iterable",
      "scripthost"
    ]
  },
  "references": [
    {
      "path": "./packages/shared-ui"
    }
  ],
  "exclude": [
    "**/node_modules",
    "packages/**/dist"
  ]
}

apps/app1/package.json

{
  "name": "@namespace/app1",
  "private": true,
  "type": "module",
  "packageManager": "pnpm@8.6.9",
  "scripts": {
    "serve": "vite"
  },
  "dependencies": {
    "@namespace/shared-ui": "workspace:*",
    "@vee-validate/zod": "~4.10.8",
    "axios": "~1.4.0",
    "pinia": "~2.1.4",
    "vee-validate": "~4.10.8",
    "vite-plugin-vuetify": "~1.0.2",
    "vue": "~3.3.4",
    "vue-router": "~4.2.4",
    "vuetify": "~3.3.6",
    "zod": "~3.21.4"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "~4.2.3",
    "sass": "~1.64.1",
    "vite-tsconfig-paths": "~4.2.0"
  }
}

apps/app1/tsconfig.json

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/",
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "typeRoots": [
      "./node_modules/@types",
      "./src/types"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.d.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "vite.config.ts"
  ]
}

apps/app1/vite.config.ts

import { type UserConfigExport, defineConfig } from 'vite';
import eslint from 'vite-plugin-eslint';
import vuetify from 'vite-plugin-vuetify';
import tsconfigPaths from 'vite-tsconfig-paths';

import vue from '@vitejs/plugin-vue';

export default defineConfig(({ mode }) => {
    const isDevelopment = mode === 'development';
    const config: UserConfigExport = {
        root: `${process.cwd()}/`,
        envDir: `${process.cwd()}/env/`,
        plugins: [tsconfigPaths(), eslint(), vue(), vuetify()],
        resolve: {
            alias: {
                '@/': `${process.cwd()}/src/`,
                vue: 'vue/dist/vue.esm-bundler.js'
            }
        }
    };

    if (isDevelopment) {
        config.server = {
            host: true,
            port: Number(process.env.PORT)
        };
    }

    return config;
});

packages/shared-ui/package.json

{
  "name": "@namespace/shared-ui",
  "private": true,
  "type": "module",
  "packageManager": "pnpm@8.6.9",
  "dependencies": {
    "axios": "~1.4.0"
  },
  "devDependencies": {
    "@vee-validate/zod": "~4.10.8",
    "vee-validate": "~4.10.8",
    "vue": "~3.3.4",
    "vuetify": "~3.3.6",
    "zod": "~3.21.4"
  }
}

packages/shared-ui/tsconfig.json

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "baseUrl": "./",
    "outDir": "./dist/",
    "paths": {
      "@/*": [
        "src/*"
      ]
    },
    "typeRoots": [
      "./node_modules/@types",
      "./src/types"
    ]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.d.ts",
    "src/**/*.tsx",
    "src/**/*.vue"
  ]
}

解决方案

1. 为共享包添加Vite配置

在packages/shared-ui/下创建vite.config.ts,配置自身的@别名:

import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
  plugins: [tsconfigPaths()],
  resolve: {
    alias: {
      '@/': new URL('./src/', import.meta.url).pathname,
    },
  },
});

2. 调整主应用Vite的路径解析配置

修改apps/app1/vite.config.ts中的tsconfigPaths插件,让它包含所有工作区的TS配置文件:

tsconfigPaths({
  projects: [
    '../../tsconfig.base.json',
    './tsconfig.json',
    '../../packages/shared-ui/tsconfig.json',
  ],
}),

3. 更新ESLint配置(根目录)

如果根目录没有.eslintrc.js则创建,添加工作区TS配置的解析支持:

module.exports = {
  // ...其他原有配置
  settings: {
    'import/resolver': {
      typescript: {
        project: [
          './tsconfig.base.json',
          './apps/*/tsconfig.json',
          './packages/*/tsconfig.json',
        ],
      },
    },
  },
};

4. 验证并重启服务

执行pnpm install确保依赖链接正确,然后重启主应用的开发服务,此时共享包内的@/别名应该能正确解析到自身的src目录。


内容的提问来源于stack exchange,提问作者RonZ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 01:05:52