写在前面:这是我花了一周时间整理的教程,把自己踩过的坑都写出来了。如果你也在学 Vue3,希望能帮到你。后续会推出一系列的vue3相关的内容。

先说说为啥推荐用 Vue3 + Vite

我之前也是 Vue2 的死忠粉,Vue3 刚出来的时候觉得没必要升级。但真正用过之后,是真香,现在vue最新已经到了3.5的版本,很多写法都有更新,咱们也得跟上版本不是!
简单对比一下:

Vue3 比 Vue2 好在哪?

  • 性能确实提升了不少,我测试过,大概快了有 2 倍
  • 打包体积小了很多,从 30 多 KB 降到 10 KB 左右
  • TypeScript 支持好太多了,不用再纠结类型问题
  • Composition API 写起来是真舒服,特别是逻辑复用

Vite 比 Webpack 快在哪?

我给你看看我项目的真实数据(一个大概 50 个页面的管理后台):

对比项 用 Webpack 用 Vite 我的感受
启动速度 45 秒左右 1 秒多 简直了!
热更新 要等 4-5 秒 基本秒更新 改代码不用等了
打包速度 3 分钟+ 20 多秒 提测快多了

说实话,用过 Vite 之后,我是真回不去 Webpack 了🤣

这篇文章能帮你解决什么?

跟着我这篇文章做下来,你会得到:

1. 一套完整的项目模板
不用每次都从头开始配置了,直接复制就能用。我现在做新项目就是用这个模板,能省不少时间。

2. 代码规范配置
ESLint + Prettier + Husky 这一套,配好之后团队开发不会再因为代码风格吵架了(真事儿😂)

3. 一些常见问题的解决办法
我踩过的坑都写在这了,比如路径别名不生效、热更新有问题这些,都有解决方案。

4. 能直接用的代码
Axios 封装、环境变量配置这些,直接复制粘贴就行,不用再自己写了。

废话不多说,开始吧!


🛠️ 一、环境准备

1.1 检查 Node.js 版本

Vite 需要 Node.js 版本 >= 14.18+,推荐使用 16+18+ 版本。

# 检查 Node.js 版本
node -v

# 检查 npm 版本
npm -v

如果版本过低,请前往 Node.js官网 下载最新LTS版本。

我使用的node版本是v20。

我使用的是是v20
1.2 配置国内镜像源(可选但推荐)

如果你在国内,强烈建议配置淘宝镜像源,加速依赖安装:

# 设置淘宝镜像源
npm config set registry https://registry.npmmirror.com

# 验证是否设置成功
npm config get registry

或者使用 pnpm(推荐):

# 安装 pnpm
npm install -g pnpm

# pnpm 设置镜像源
pnpm config set registry https://registry.npmmirror.com

💡 为什么推荐 pnpm?

  • 更快的安装速度
  • 节省磁盘空间(硬链接机制)
  • 更严格的依赖管理

开始搭建项目

第一步:创建项目

打开终端,运行下面的命令:

# 用 pnpm 创建(我习惯用这个,快)
pnpm create vite my-vue3-project

# 如果你用 npm,就这样
npm create vite@latest my-vue3-project

运行之后会让你选择框架和语言:

? Select a framework: › Vue  # 选 Vue
? Select a variant: › TypeScript  # 建议选 TypeScript,后面会省很多事

💡 关于 TypeScript
我知道有些人觉得 TypeScript 麻烦,但真的建议用。项目大了之后,没有类型提示真的很痛苦。而且现在 Vue3 对 TS 支持很好,用起来不会有太多负担。

在这里插入图片描述

第二步:启动项目

# 进入项目
cd my-vue3-project

# 安装依赖(这步可能要等一会)
pnpm install

# 启动!
pnpm dev

浏览器打开 http://localhost:5173,能看到 Vue 的欢迎页面就成功了!
在这里插入图片描述

看看项目结构

创建好的项目长这样:

my-vue3-project/
├── public/              # 放静态资源的,比如图标啥的
├── src/               
│   ├── assets/          # 图片、样式这些
│   ├── components/      # 组件放这
│   ├── App.vue          # 主组件
│   ├── main.ts          # 入口文件(最重要)
│   └── style.css      
├── index.html           # HTML 模板
├── package.json       
├── vite.config.ts       # Vite 的配置文件
└── ...

简单看几个关键文件:

src/main.ts - 入口文件

这个文件很简单,就是创建 Vue 实例然后挂载:

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'

// 创建应用
const app = createApp(App)

// 挂载到页面上的 #app 节点
app.mount('#app')

vite.config.ts - Vite 配置

默认配置很简单,就一个 Vue 插件:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
})

后面我们会给它加点料,别急😄


开始配置项目

现在项目是能跑了,但还不够好用。我们来做一些优化。

路径别名配置(超级实用!)

你有没有遇到过这种情况:

import Button from '../../../components/Button.vue'

这种 ../../../ 看着就头疼,而且一不小心就写错。所以我们要配置路径别名。

修改 vite.config.ts

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'  // 记得导入 path

export default defineConfig({
  plugins: [vue()],
  
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),        // @ 就代表 src 目录
      '@components': resolve(__dirname, 'src/components'),
      '@assets': resolve(__dirname, 'src/assets'),
      '@utils': resolve(__dirname, 'src/utils'),
      // 根据你的项目需要,想加多少加多少
    }
  },
  
  server: {
    port: 3000,        // 我习惯用 3000 端口,你随意
    open: true,        // 启动自动打开浏览器,省得手动打开
    cors: true,           // 允许跨域
  
    // 代理配置(解决开发环境跨域问题)
    proxy: {
      '/api': {
        target: 'http://localhost:8080',  // 后端服务地址
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, '')
      }
    }
  },
  
  // 构建配置
  build: {
    outDir: 'dist',                    // 输出目录
    sourcemap: false,                  // 不生成 sourcemap
    minify: 'terser',                  // 使用 terser 压缩
    chunkSizeWarningLimit: 1500,       // chunk 大小警告的限制
  
    rollupOptions: {
      output: {
        // 分包策略
        manualChunks: {
          'vue-vendor': ['vue', 'vue-router', 'pinia'],
          'element-plus': ['element-plus'],
        }
      }
    }
  }
})

还要改一下 tsconfig.json,不然 TypeScript 会报错(最新的vite的配置文件是分开管理的,我们浏览器端就使用 tsconfig.app.json 文件就好了):
在这里插入图片描述

{
  "compilerOptions": {
    // ... 其他配置保持不变
  
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"],
      "@assets/*": ["src/assets/*"],
      // 和 vite.config.ts 里保持一致
    },
  
    // ... 其他配置
  }
}

在这里插入图片描述

配置完之后,导入就方便多了:

// 以前这样写,看着难受
import Button from '../../../components/Button.vue'

// 现在可以这样
import Button from '@components/Button.vue'

我们修改组件引入的方式:
在这里插入图片描述
重新启动,页面依旧可以成功渲染。

清爽多了吧!😊

配置自动导入(vite.config.ts):

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    vue(),
  
    // 自动导入 Vue API
    AutoImport({
      imports: ['vue', 'vue-router', 'pinia'],
      resolvers: [ElementPlusResolver()],
      dts: 'src/auto-imports.d.ts',  // 生成类型声明文件
    }),
  
    // 自动导入组件
    Components({
      resolvers: [ElementPlusResolver()],
      dts: 'src/components.d.ts',    // 生成类型声明文件
    }),
  ],
})

这样配好之后,refcomputed 这些就不用每次都手动导入了,写起来爽多了!


配置自动导入(vite.config.ts):**

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  plugins: [
    vue(),
  
    // 自动导入 Vue API
    AutoImport({
      imports: ['vue', 'vue-router', 'pinia'],
      resolvers: [ElementPlusResolver()],
      dts: 'src/auto-imports.d.ts',  // 生成类型声明文件
    }),
  
    // 自动导入组件
    Components({
      resolvers: [ElementPlusResolver()],
      dts: 'src/components.d.ts',    // 生成类型声明文件
    }),
  ],
})

这样配好之后,refcomputed 这些就不用每次都手动导入了,写起来爽多了!

来看看自动导入的效果

没配置自动导入之前:

<script setup lang="ts">
// 每次都要手动导入,烦死了
import { ref, computed, watch, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { ElMessage, ElButton } from 'element-plus'

const count = ref(0)
const doubleCount = computed(() => count.value * 2)
</script>

<template>
  <el-button @click="count++">点击</el-button>
  <div>{{ doubleCount }}</div>
</template>

配置自动导入之后:

<script setup lang="ts">
// 啥都不用导入,直接用!
const count = ref(0)  // ref 自动导入
const doubleCount = computed(() => count.value * 2)  // computed 自动导入
const router = useRouter()  // useRouter 自动导入

onMounted(() => {  // onMounted 自动导入
  console.log('mounted')
})

watch(count, (newVal) => {  // watch 自动导入
  console.log('count changed:', newVal)
})
</script>

<template>
  <!-- ElButton 组件自动导入,不用 import -->
  <el-button @click="count++">点击</el-button>
  <div>{{ doubleCount }}</div>
</template>

看到没?代码清爽了很多!不用写一堆 import,TypeScript 类型提示也都有,爽歪歪😎


配置代码规范(重要!)

这一步真的别跳过,我吃过亏😭

以前有个项目,一开始没配规范,后来团队来了新人,代码风格五花八门:

  • 有人用单引号,有人用双引号
  • 有人加分号,有人不加
  • 缩进有2格的、4格的、甚至还有用tab的…

最后合并代码的时候那叫一个乱,git diff 全是这种无意义的改动。所以,一开始就把规范配好!

安装 ESLint 和 Prettier

# 一次性装完这些
pnpm add -D eslint eslint-plugin-vue @typescript-eslint/parser @typescript-eslint/eslint-plugin prettier eslint-config-prettier eslint-plugin-prettier

创建 ESLint 配置文件

新建 .eslintrc.cjs 文件:

module.exports = {
  root: true,
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended',
    'prettier',
  ],
  parser: 'vue-eslint-parser',
  parserOptions: {
    ecmaVersion: 'latest',
    parser: '@typescript-eslint/parser',
    sourceType: 'module',
  },
  plugins: ['vue', '@typescript-eslint', 'prettier'],
  rules: {
    'prettier/prettier': 'error',
    'vue/multi-word-component-names': 'off',  // 允许单词组件名
    '@typescript-eslint/no-explicit-any': 'warn',  // any 类型警告
    '@typescript-eslint/no-unused-vars': 'error',  // 未使用变量报错
  },
}

Prettier 配置

新建 .prettierrc.json

{
  "semi": false,              // 不加分号(我个人习惯,你可以改成 true)
  "singleQuote": true,        // 用单引号
  "printWidth": 100,          // 一行最多 100 个字符
  "trailingComma": "es5",     // 对象最后一个属性后面加逗号
  "tabWidth": 2               // 缩进 2 格
}

这些配置看你个人喜好,关键是团队统一。

添加 npm 命令

package.json 里加上这些:

{
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc && vite build",
    "preview": "vite preview",
    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix",
    "format": "prettier --write src/**/*.{js,ts,vue,json,css,scss}"
  }
}

配好之后,可以运行试试:

# 自动修复代码格式
pnpm lint

# 格式化所有文件
pnpm format

看看实际效果

比如你写了这样一段代码(格式很乱):

// 格式化之前(各种不规范)
const   user={name:"张三",age:  18,   email:"test@example.com",  }

function getUserInfo(  ){
return user
}

const list=[1,2,3,4,5]
const result=list.map(item=>  {
    return item*2
  })

运行 pnpm format 之后:

// 格式化之后(整整齐齐)
const user = {
  name: '张三',
  age: 18,
  email: 'test@example.com',
}

function getUserInfo() {
  return user
}

const list = [1, 2, 3, 4, 5]
const result = list.map((item) => {
  return item * 2
})

是不是舒服多了?以后不用纠结格式问题,pnpm format 一键搞定😎


📁 六、推荐的项目目录结构

my-vue3-project/
├── public/
│   └── favicon.ico
├── src/
│   ├── api/                  # API 接口
│   │   ├── user.ts
│   │   └── product.ts
│   ├── assets/               # 静态资源
│   │   ├── images/
│   │   └── styles/
│   │       └── global.scss
│   ├── components/           # 公共组件
│   │   ├── common/           # 通用组件
│   │   │   ├── Button.vue
│   │   │   └── Modal.vue
│   │   └── business/         # 业务组件
│   │       └── UserCard.vue
│   ├── composables/          # 组合式函数
│   │   ├── useUser.ts
│   │   └── useAuth.ts
│   ├── directives/           # 自定义指令
│   │   └── loading.ts
│   ├── layouts/              # 布局组件
│   │   ├── DefaultLayout.vue
│   │   └── AdminLayout.vue
│   ├── router/               # 路由配置
│   │   └── index.ts
│   ├── stores/               # Pinia 状态管理
│   │   ├── user.ts
│   │   └── cart.ts
│   ├── types/                # TypeScript 类型定义
│   │   ├── user.ts
│   │   └── api.ts
│   ├── utils/                # 工具函数
│   │   ├── request.ts        # axios 封装
│   │   ├── storage.ts        # localStorage 封装
│   │   └── validate.ts       # 表单验证
│   ├── views/                # 页面组件
│   │   ├── home/
│   │   │   └── index.vue
│   │   ├── user/
│   │   │   ├── login.vue
│   │   │   └── register.vue
│   │   └── product/
│   │       └── detail.vue
│   ├── App.vue
│   ├── main.ts
│   └── env.d.ts
├── .eslintrc.cjs
├── .prettierrc.json
├── .gitignore
├── index.html
├── package.json
├── tsconfig.json
└── vite.config.ts

🎯 七、创建基础功能模块

7.1 封装 Axios 请求

创建 src/utils/request.ts

import axios, { type AxiosInstance, type AxiosResponse } from 'axios'
import { ElMessage } from 'element-plus'

// 创建 axios 实例
const service: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
  timeout: 15000,
  headers: {
    'Content-Type': 'application/json;charset=utf-8',
  },
})

// 请求拦截器
service.interceptors.request.use(
  (config) => {
    // 从本地存储获取 token
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config
  },
  (error) => {
    console.error('Request error:', error)
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  (response: AxiosResponse) => {
    const res = response.data

    // 根据后端返回的 code 判断
    if (res.code !== 200) {
      ElMessage.error(res.message || '请求失败')
  
      // 401: 未授权,跳转登录页
      if (res.code === 401) {
        localStorage.removeItem('token')
        window.location.href = '/login'
      }
  
      return Promise.reject(new Error(res.message || '请求失败'))
    }
  
    return res
  },
  (error) => {
    console.error('Response error:', error)
    ElMessage.error(error.message || '网络请求失败')
    return Promise.reject(error)
  }
)

export default service

7.2 配置环境变量

创建 .env.development(开发环境):

# 开发环境配置
VITE_APP_TITLE=My Vue3 App - Dev
VITE_API_BASE_URL=http://localhost:8080/api

创建 .env.production(生产环境):

# 生产环境配置
VITE_APP_TITLE=My Vue3 App
VITE_API_BASE_URL=https://api.yourdomain.com/api

怎么用这些环境变量?

举个实际例子,比如在标题栏显示不同环境的标识:

<script setup lang="ts">
// 获取环境变量
const appTitle = import.meta.env.VITE_APP_TITLE
const apiUrl = import.meta.env.VITE_API_BASE_URL
const isDev = import.meta.env.DEV  // 开发环境是 true,生产环境是 false

// 开发环境会看到:My Vue3 App - Dev
// 生产环境会看到:My Vue3 App
console.log('当前环境:', appTitle)
console.log('API地址:', apiUrl)
</script>

<template>
  <div>
    <h1>{{ appTitle }}</h1>
    <!-- 开发环境显示调试信息 -->
    <div v-if="isDev" class="debug-info">
      API: {{ apiUrl }}
    </div>
  </div>
</template>

这样切换环境时,不用改代码,Vite 会自动读取对应的 .env 文件👍

7.3 创建 API 接口模块

创建 src/api/user.ts

import request from '@utils/request'

// 用户登录
export function login(data: { username: string; password: string }) {
  return request({
    url: '/user/login',
    method: 'post',
    data,
  })
}

// 获取用户信息
export function getUserInfo() {
  return request({
    url: '/user/info',
    method: 'get',
  })
}

// 更新用户信息
export function updateUserInfo(data: any) {
  return request({
    url: '/user/update',
    method: 'put',
    data,
  })
}

在组件中怎么调用这些 API?

创建一个登录页面 src/views/login/index.vue

<script setup lang="ts">
import { login } from '@api/user'  // 看,用上 @ 别名了
import { ElMessage } from 'element-plus'

// 表单数据
const formData = reactive({
  username: '',
  password: ''
})

const loading = ref(false)

// 登录处理
const handleLogin = async () => {
  if (!formData.username || !formData.password) {
    ElMessage.warning('请输入用户名和密码')
    return
  }

  try {
    loading.value = true
  
    // 调用登录 API(就是刚才在 api/user.ts 里定义的)
    const res = await login({
      username: formData.username,
      password: formData.password
    })
  
    // 登录成功
    ElMessage.success('登录成功')
    localStorage.setItem('token', res.data.token)
  
    // 跳转首页
    router.push('/')
  
  } catch (error) {
    // 错误处理已经在 request.ts 的拦截器里统一处理了
    // 这里不用再写 ElMessage.error
  } finally {
    loading.value = false
  }
}
</script>

<template>
  <div class="login-page">
    <el-form>
      <el-form-item label="用户名">
        <el-input v-model="formData.username" placeholder="请输入用户名" />
      </el-form-item>
    
      <el-form-item label="密码">
        <el-input 
          v-model="formData.password" 
          type="password" 
          placeholder="请输入密码"
        />
      </el-form-item>
    
      <el-button 
        type="primary" 
        :loading="loading"
        @click="handleLogin"
      >
        登录
      </el-button>
    </el-form>
  </div>
</template>

看到没?API 调用就是这么简单:

  1. api/user.ts 里定义接口
  2. 在组件里导入函数
  3. 直接调用,等待结果
  4. 错误处理都在拦截器里统一搞定了

后面要加新接口,按这个模式来就行,很规范👌


🚨 八、常见问题解决

问题1:Vite 启动报错 EADDRINUSE

原因: 端口被占用

解决:

# 方法1:修改 vite.config.ts 中的 port
server: {
  port: 3001,  // 换一个端口
}

# 方法2:杀掉占用端口的进程(Windows)
netstat -ano | findstr :5173
taskkill /PID <进程ID> /F

# 方法2:杀掉占用端口的进程(Mac/Linux)
lsof -ti:5173 | xargs kill

问题2:路径别名 @ 不生效

原因: TypeScript 配置不正确

解决: 确保 tsconfig.json 中配置了 baseUrlpaths

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

问题3:Element Plus 样式丢失

原因: 没有导入样式文件

解决:main.ts 中导入:

import 'element-plus/dist/index.css'

或者使用 unplugin-vue-components 自动导入(推荐)。

问题4:热更新不生效

解决:

// vite.config.ts
export default defineConfig({
  server: {
    watch: {
      usePolling: true,  // 启用轮询模式
    },
  },
})

🎉 九、测试运行

启动开发服务器

pnpm dev

访问 http://localhost:3000,应该能看到页面正常运行。

[配图占位:项目运行成功截图]

构建生产版本

# 构建
pnpm build

# 本地预览构建结果
pnpm preview

构建完成后,会在项目根目录生成 dist 文件夹,这就是可以部署到服务器的静态文件。


写在最后

如果你一路跟下来了,那恭喜!你现在有了:

  • 一个配置完整的 Vue3 + Vite 项目
  • 代码规范配置(不会再被 leader 说代码乱了)
  • 一些实用的工具封装(Axios、环境变量这些)

说点实在的

很多人搭建项目就是 npm create vite 一把梭,然后啥规范都不配。等项目大了,各种问题就来了:

  • 有人用单引号,有人用双引号
  • 有人缩进 2 格,有人缩进 4 格
  • git 提交信息五花八门,想找个提交记录都费劲

所以别嫌麻烦,这些配置真的很有用。我之前维护过一个没配规范的项目,那叫一个痛苦😭

关于这个系列

这是我 Vue3 系列的第一篇,后面还会写:

  • Vue3 的组合式 API 怎么用
  • Pinia 状态管理
  • 路由配置和权限控制
  • 性能优化
  • 打包部署

每篇都是我实际项目中用到的东西,不会讲那些花里胡哨用不到的。

下一篇写啥?

下一篇准备讲 Vue3 的组合式 API,主要是这几个:

ref 和 reactive 到底怎么选?
说实话,我一开始也搞不清楚。现在算是总结出一套自己的用法了,下篇会详细说。

computed 和 watch 的一些技巧
这俩我用得比较多,有些小技巧挺实用的,到时候分享给你们。

自定义 Hooks(Composable)
这个真的很好用!把一些通用逻辑封装起来,到处都能用。我会分享几个我常用的,比如 useTable、useForm 这些。

从 Options API 迁移到 Composition API
如果你项目还是 Vue2 或者 Options API,我会教你怎么一步步迁移。

大概 3-5 天后发吧,我得好好整理下。


完整源码在哪?

这个项目的完整配置我放在网站上了(私信我也可以)。因为平台限制,我不能直接贴链接😅

你可以这样找到:

百度或 Google 搜索:代码小库

然后在网站首页搜「Vue3 Vite 项目搭建」就能找到了,里面有完整的源码和配置文件。


关于我

我是【代码小库】的作者,做前端 6 年多了。

这几年经历过:

  • 外包公司的加班地狱(每天改到凌晨那种)
  • 创业公司的从 0 到 1
  • 大厂的各种规范和流程

踩过的坑真的太多了,所以想把这些经验分享出来,帮大家少走点弯路。

为啥要写这个系列?

说实话,网上的 Vue3 教程很多,但质量参差不齐。有的太基础,照着做完还是不会;有的太高深,新手根本看不懂。

我想写的是那种:

  • 看得懂 - 用大白话讲,不搞那些高大上的术语
  • 跟得上 - 跟着做一定能跑起来
  • 用得到 - 都是实际项目中会用到的东西

想找我交流?

搜索「开发者代码仓」关注服务号就行,我基本每天都会看消息。

有问题可以留言,我会尽量回复(工作日可能会慢点,周末回复快)


最后说两句

如果这篇文章对你有帮助,麻烦帮我:

  • 点个赞👍(真的很重要!)
  • 收藏一下⭐(下次要用的时候好找)
  • 有问题评论区留言💬(我会认真回复)

写教程真的挺费时间的,这一篇我前前后后写了一周。但看到有人说有帮助,就觉得值了。

下一篇讲组合式 API,我先去整理了,争取这周内发出来。

咱们下篇见!👋


#Vue3 #Vite #前端开发 #项目搭建

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。