Vue3 + TypeScript 项目框架搭建指南

Vue3 + TypeScript 项目框架搭建指南

搭建一个vue3+Ts 项目基本框架以及配置, 要求配置好server中proxy代理、有sit,development,production环境,同时 配置好axios,mock 、less、代码压缩

1. 项目初始化

perl 复制代码
# 使用 Vite 创建项目
npm create vue@latest my-vue3-project  

# 选择配置
# ✔ Project name: ... my-vue3-project
# ✔ Add TypeScript? ... Yes
# ✔ Add JSX Support? ... No
# ✔ Add Vue Router for Single Page Application development? ... Yes
# ✔ Add Pinia for state management? ... Yes
# ✔ Add Vitest for Unit testing? ... No
# ✔ Add an End-to-End Testing Solution? › No
# ✔ Add ESLint for code quality? ... Yes
# ✔ Add Prettier for code formatting? ... Yes

cd my-vue3-project
npm install

2. 安装必要依赖

bash 复制代码
# 安装 axios、mockjs、less 等
npm install axios mockjs
npm install -D less @types/node

3. 项目目录结构

bash 复制代码
src/
├── api/           # API 接口
├── assets/        # 静态资源
├── components/    # 组件
├── mock/          # Mock 数据
├── router/        # 路由
├── stores/        # 状态管理
├── types/         # TypeScript 类型定义
├── utils/         # 工具函数
├── views/         # 页面组件
├── App.vue
└── main.ts

4. 环境配置

环境变量文件

.env.development

env

ini 复制代码
VITE_APP_TITLE=Development
VITE_APP_BASE_API=/api
VITE_APP_MOCK=true

.env.sit

env

ini 复制代码
VITE_APP_TITLE=SIT
VITE_APP_BASE_API=/api
VITE_APP_MOCK=false

.env.production

env

ini 复制代码
VITE_APP_TITLE=Production
VITE_APP_BASE_API=/api
VITE_APP_MOCK=false

5. Vite 配置

vite.config.ts

javascript 复制代码
import { fileURLToPath, URL } from 'node:url'
import { defineConfig ,loadEnv} from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
// https://vite.dev/config/
export default defineConfig(({mode})=>{
  // 加载环境变量
  const env = loadEnv(mode, process.cwd(), '')
  console.log("env",env)

  return {
    plugins: [
      vue(),
      vueDevTools(),
    ],
    resolve: {
      alias: {
        '@': fileURLToPath(new URL('./src', import.meta.url))
      },
    },
    css: {
      preprocessorOptions: {
        less: {
          javascriptEnabled: true, // 启用 Less 中的 JavaScript 表达式
          additionalData: `@import "@/styles/variables.less";`// 全局注入 Less 变量文件
        }
      }
    },
    server: {
      // port: 3000,        // 端口号
      // open: true,        // 自动打开浏览器  [在package.json  dev --open也可以]
      // cors: true,        // 允许跨域
      // host: '0.0.0.0',   // 允许外部访问(可选)
      proxy: {
        // 代理接口如以"/api  开头
        [env.VITE_APP_BASE_API]: {
          //获取数据的服务器地址设置
          target: 'http://10.241.154.118:90',// 代理人的地址
          //需要代理跨域
          changeOrigin: true,
          //路径重写
          rewrite: (path) => path.replace(/^\/api/, '')
        }
        
      },
      // 热更新配置
      hmr: {
        overlay: true
      }
    },
    build: {
      target: 'es2015',// 构建目标
      minify: 'terser',// 压缩工具
      terserOptions: {
        compress: {
          drop_console: mode === 'production', // 生产环境移除console
          drop_debugger: mode === 'production'  // 生产环境移除debugger
        }
      },
      rollupOptions: {  // Rollup打包配置
        output: {
          chunkFileNames: 'js/[name]-[hash].js',  // 代码分割文件命名
          entryFileNames: 'js/[name]-[hash].js',  // 入口文件命名  
          assetFileNames: '[ext]/[name]-[hash].[ext]',// 静态资源命名
          manualChunks: { // 手动代码分割
            vue: ['vue', 'vue-router', 'pinia'] // Vue生态库单独打包
          }
        }
      }
    }
  }
})

安装 terser 压缩工具

复制代码
npm install -D terser

Vite 中配置 Less 预处理器

这是 Vite 中配置 Less 预处理器 的选项,让我为你详细解释:

配置作用分析
yaml 复制代码
css: {
  preprocessorOptions: {
    less: {
      javascriptEnabled: true,                    // 启用 Less 中的 JavaScript 表达式
      additionalData: `@import "@/styles/variables.less";`  // 全局注入 Less 变量文件
    }
  }
}
详细功能说明
1. javascriptEnabled: true
  • 作用:允许在 Less 文件中使用 JavaScript 表达式
  • 示例
less 复制代码
// 可以在 Less 中使用 JS 计算
@color: `Math.random() > 0.5 ? 'red' : 'blue'`;
@width: `100 + 50`;
2. additionalData: '@import "@/styles/variables.less";'
  • 作用:在每个 Less 文件开头自动注入指定的变量文件
  • 好处:无需在每个文件中手动导入变量
实际使用效果

配置前:

less 复制代码
// 每个 .vue 文件或 .less 文件都需要手动导入
<style lang="less">
@import "@/styles/variables.less";

.container {
  color: @primary-color;
  background: @bg-color;
}
</style>

配置后:

less 复制代码
// 无需手动导入,变量自动可用
<style lang="less">
.container {
  color: @primary-color;      // 直接使用全局变量
  background: @bg-color;
}
</style>

典型项目结构

bash 复制代码
src/
├── styles/
│   ├── variables.less        # 全局变量定义
│   └── mixins.less          # 全局混合器
├── components/
│   └── Button.vue           # 组件,自动拥有变量访问权
└── App.vue
variables.less 示例内容:
less 复制代码
// 颜色变量
@primary-color: #1890ff;
@success-color: #52c41a;
@warning-color: #faad14;
@error-color: #f5222d;

// 尺寸变量
@header-height: 64px;
@sidebar-width: 200px;

// 字体变量
@font-size-base: 14px;
@font-family: 'Arial', sans-serif;

完整配置示例

php 复制代码
// vite.config.js
export default defineConfig({
  css: {
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
        additionalData: `
          @import "@/styles/variables.less";
          @import "@/styles/mixins.less";
        `,
        modifyVars: {
          // 修改变量值(可选)
          'primary-color': '#1DA57A'
        }
      }
    }
  },
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src')  // 确保 @ 别名正确配置
    }
  }
})

优势总结

  1. 代码复用:全局变量和混合器
  2. 维护方便:统一管理样式变量
  3. 开发效率:无需重复导入
  4. 主题切换:通过修改变量实现整体主题变更

6. TypeScript 配置

上面初始化项目时候选择ts 默认生成好的 tsconfig.json

json

json 复制代码
{
  "files": [],
  "references": [
    {
      "path": "./tsconfig.node.json"
    },
    {
      "path": "./tsconfig.app.json"
    }
  ]
}

7. Axios 配置

src/utils/request.ts

typescript 复制代码
import axios from 'axios'
import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'
import { ElMessage } from 'element-plus'

interface RequestOptions extends AxiosRequestConfig {
  loading?: boolean
}

class Request {
  private instance: AxiosInstance

  constructor(baseURL: string) {
    this.instance = axios.create({
      baseURL,
      timeout: 10000,
      headers: {
        'Content-Type': 'application/json'
      }
    })

    this.setupInterceptors()
  }

  private setupInterceptors() {
    // 请求拦截器
    this.instance.interceptors.request.use(
      (config) => {
        // 添加 token 等
        const token = localStorage.getItem('token')
        if (token) {
          config.headers.Authorization = `Bearer ${token}`
        }
        return config
      },
      (error) => {
        return Promise.reject(error)
      }
    )

    // 响应拦截器
    this.instance.interceptors.response.use(
      (response: AxiosResponse) => {
        const { data } = response
        // 根据后端接口规范调整
        if (data.code === 200) {
          return data
        } else {
          ElMessage.error(data.message || '请求失败')
          return Promise.reject(new Error(data.message || '请求失败'))
        }
      },
      (error) => {
        let message = '请求失败'
        if (error.response?.status) {
          switch (error.response.status) {
            case 401:
              message = '未授权'
              // 跳转到登录页
              break
            case 403:
              message = '拒绝访问'
              break
            case 404:
              message = '请求地址错误'
              break
            case 500:
              message = '服务器内部错误'
              break
            default:
              message = '网络连接错误'
          }
        }
        ElMessage.error(message)
        return Promise.reject(error)
      }
    )
  }

  public request<T = any>(config: RequestOptions): Promise<T> {
    return this.instance.request(config)
  }

  public get<T = any>(url: string, config?: RequestOptions): Promise<T> {
    return this.request({ ...config, method: 'GET', url })
  }

  public post<T = any>(url: string, data?: any, config?: RequestOptions): Promise<T> {
    return this.request({ ...config, method: 'POST', url, data })
  }

  public put<T = any>(url: string, data?: any, config?: RequestOptions): Promise<T> {
    return this.request({ ...config, method: 'PUT', url, data })
  }

  public delete<T = any>(url: string, config?: RequestOptions): Promise<T> {
    return this.request({ ...config, method: 'DELETE', url })
  }
}

// 创建请求实例
const baseURL = import.meta.env.VITE_APP_BASE_API as string
export const http = new Request(baseURL)

8. Mock 配置

src/mock/index.ts

typescript

javascript 复制代码
import Mock from 'mockjs'
import user from './user'

// 判断是否开启 mock
const isMock = import.meta.env.VITE_APP_MOCK === 'true'

if (isMock) {
  Mock.setup({
    timeout: '200-600'
  })

  // 用户相关接口
  Mock.mock(//api/user/login/, 'post', user.login)
  Mock.mock(//api/user/info/, 'get', user.getUserInfo)
  Mock.mock(//api/user/list/, 'get', user.getUserList)
}

src/mock/user.ts

typescript

css 复制代码
import { MockMethod } from 'vite-plugin-mock'

export default {
  login: () => {
    return {
      code: 200,
      message: 'success',
      data: {
        token: Mock.Random.string(32),
        userInfo: {
          id: 1,
          username: 'admin',
          nickname: '管理员',
          avatar: Mock.Random.image('100x100')
        }
      }
    }
  },

  getUserInfo: () => {
    return {
      code: 200,
      message: 'success',
      data: {
        id: 1,
        username: 'admin',
        nickname: '管理员',
        avatar: Mock.Random.image('100x100'),
        roles: ['admin']
      }
    }
  },

  getUserList: () => {
    return {
      code: 200,
      message: 'success',
      data: {
        total: 100,
        list: Array.from({ length: 10 }, (_, index) => ({
          id: index + 1,
          username: Mock.Random.string(5, 10),
          nickname: Mock.Random.cname(),
          email: Mock.Random.email(),
          phone: Mock.Random.string('number', 11),
          createTime: Mock.Random.datetime()
        }))
      }
    }
  }
} as MockMethod

9. Less 样式配置

src/styles/variables.less

less

less 复制代码
// 颜色变量
@primary-color: #1890ff;
@success-color: #52c41a;
@warning-color: #faad14;
@error-color: #f5222d;

// 字体
@font-size-base: 14px;
@font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;

// 布局
@layout-header-height: 64px;
@layout-sider-width: 200px;

// 边框
@border-radius-base: 4px;
@border-color-base: #d9d9d9;

src/styles/global.less

less

less 复制代码
* {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  font-family: @font-family;
  font-size: @font-size-base;
}

#app {
  height: 100%;
}

// 通用样式
.text-center {
  text-align: center;
}

.mt-20 {
  margin-top: 20px;
}

.mb-20 {
  margin-bottom: 20px;
}

10. API 接口管理

src/api/user.ts

typescript

typescript 复制代码
import { http } from '@/utils/request'

export interface LoginParams {
  username: string
  password: string
}

export interface UserInfo {
  id: number
  username: string
  nickname: string
  avatar: string
  roles: string[]
}

export interface UserListParams {
  page: number
  size: number
  keyword?: string
}

export const userApi = {
  // 登录
  login: (data: LoginParams) => http.post<{ token: string; userInfo: UserInfo }>('/user/login', data),
  
  // 获取用户信息
  getUserInfo: () => http.get<UserInfo>('/user/info'),
  
  // 获取用户列表
  getUserList: (params: UserListParams) => http.get<{ total: number; list: UserInfo[] }>('/user/list', { params })
}

11. 类型定义

src/types/api.ts

typescript

typescript 复制代码
export interface ApiResponse<T = any> {
  code: number
  message: string
  data: T
}

export interface PageParams {
  page: number
  size: number
}

export interface PageResponse<T = any> {
  total: number
  list: T[]
}

12. 主入口文件

src/main.ts

typescript

javascript 复制代码
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
import router from './router'

// 引入 mock
import './mock'

// 引入全局样式
import './styles/global.less'

const app = createApp(App)

app.use(createPinia())
app.use(router)
app.use(ElementPlus)

app.mount('#app')

13. 示例页面组件

src/views/Home.vue

vue

xml 复制代码
<template>
  <div class="home-container">
    <h1 class="text-center">{{ title }}</h1>
    <el-button type="primary" @click="handleLogin">模拟登录</el-button>
    <el-button @click="getUserList">获取用户列表</el-button>
    
    <el-table :data="userList" class="mt-20">
      <el-table-column prop="id" label="ID" width="80" />
      <el-table-column prop="username" label="用户名" />
      <el-table-column prop="nickname" label="昵称" />
      <el-table-column prop="email" label="邮箱" />
    </el-table>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { userApi } from '@/api/user'
import type { UserInfo } from '@/api/user'

const title = ref('Vue3 + TypeScript 项目')
const userList = ref<UserInfo[]>([])

const handleLogin = async () => {
  try {
    const result = await userApi.login({
      username: 'admin',
      password: '123456'
    })
    console.log('登录成功:', result)
  } catch (error) {
    console.error('登录失败:', error)
  }
}

const getUserList = async () => {
  try {
    const result = await userApi.getUserList({
      page: 1,
      size: 10
    })
    userList.value = result.list
  } catch (error) {
    console.error('获取用户列表失败:', error)
  }
}

onMounted(() => {
  getUserList()
})
</script>

<style lang="less" scoped>
.home-container {
  padding: 20px;
  
  h1 {
    color: @primary-color;
    margin-bottom: 20px;
  }
}
</style>

14. Package.json 脚本配置

package.json

json

json 复制代码
{
  "scripts": {
    "dev": "vite",
    "dev:sit": "vite --mode sit",
    "build": "vue-tsc && vite build",
    "build:sit": "vue-tsc && vite build --mode sit",
    "build:prod": "vue-tsc && vite build --mode production",
    "preview": "vite preview"
  }
}

15. 使用说明

  1. 开发环境运行

    bash

    arduino 复制代码
    npm run dev
  2. SIT环境构建

    bash

    arduino 复制代码
    npm run build:sit
  3. 生产环境构建

    bash

    arduino 复制代码
    npm run build:prod

主要特性

  • ✅ Vue3 + TypeScript
  • ✅ 环境配置(development/sit/production)
  • ✅ Proxy 代理配置
  • ✅ Axios 封装和拦截器
  • ✅ Mock 数据模拟
  • ✅ Less 预处理器
  • ✅ 代码压缩优化
  • ✅ 路由和状态管理
  • ✅ Element Plus UI 组件库
  • ✅ 类型安全

这个框架提供了完整的开发环境配置,您可以根据实际需求进一步调整和扩展。

相关推荐
lapiii3582 小时前
[前端-React] Hook
前端·javascript·react.js
白龙马云行技术团队2 小时前
前端自适应动态架构图演进
前端
一枚前端小能手2 小时前
🎬 使用 Web 动画 API - 关键帧与交互控制实战指南
前端·javascript·api
西西学代码2 小时前
Flutter---异步编程
开发语言·前端·javascript
拉不动的猪2 小时前
CSS 像素≠物理像素:0.5px 效果的核心密码是什么?
前端·css·面试
前端市界2 小时前
Copilot新模型GPT-5.1太强了!自动生成完美Axios封装,同事都看傻了
前端·前端框架·github
米欧2 小时前
取消当前正在进行的所有接口请求
前端·javascript·axios
浪里行舟2 小时前
告别“拼接”,迈入“原生”:文心5.0如何用「原生全模态」重塑AI天花板?
前端·javascript·后端
OpenTiny社区2 小时前
救命!这个低代码工具太香了 ——TinyEngine 物料自动导入上手
前端·低代码·github