vite+vue3+ts前端搭建与配置(1)

基于vite+vue3的中台前端搭建

1.创建

1.1 vite

# 环境:
# Node 20.x
# Vite 6.x
# Typescript 5.x
pnpm create vite

# Project name: admin-template
# Select a framework: Vue
# Select a variant: TypeScript  

cd admin-template
pnpm install  # 安装依赖
pnpm dev  # 启动项目

1.2 其它必要依赖

项目初步创建后package.json中只有vue、vite、vue-tsc、typescript、@vitejs/plugin-vue这几个依赖,所以要安装以下依赖项:

# 1、必要依赖项 vue-router、axios、vue-i18n、pinia、@vitejs/plugin-vue-jsx、less或sass
# 2、组件:element-plus、@element-plus/icons-vue
# 3、辅助:nprogress、mitt、radashi、dayjs、@vueuse/core、@types/node、unplugin-auto-import、unplugin-vue-components
# 4、部分依赖需要另安装类型声明包:@types/nprogress、@types/mockjs

pnpm add vue-router axios vue-i18n pinia @vitejs/plugin-vue-jsx sass

pnpm add element-plus @element-plus/icons-vue 

pnpm add -D unplugin-auto-import unplugin-vue-components

pnpm add nprogress mitt radashi dayjs @vueuse/core

pnpm add -D @types/nprogress @types/mockjs @types/node

lodash 替换成 radashradashi:
radashiradash 是同一项目的不同分支,主要区别在于维护状态和目标定位:.

  • radash‌ 侧重替代 lodash 的通用工具函数需求,支持 TS 开发,适合需要完整工具函数库的开发者。
  • radashiradash 的活跃维护分支,持续更新并优化工具函数,强调轻量化、类型安全和无依赖特性。 ‌推荐给追求轻量化、无依赖且注重代码维护性的项目。(二者其中某些函数的使用方式存在差异,使用时请相应参考文档: RadashRadashi)

2 vite.config.ts基础配置

2.1 配置路径别名alias

// vite.config.ts
import { defineConfig } from 'vite';
import { resolve } from 'path';   // 需要安装 @types/node

export default defineConfig({
  resolve: {
    alias: {
      "~": resolve(process.cwd()),
      "@": resolve(__dirname, 'src'),
      '@ts': resolve(__dirname, 'src/types'),
      '@utils': resolve(__dirname, 'src/utils'),
      '@comps': resolve(__dirname, 'src/components')
    }
  }
})

然后需要在tsconfig.json中的compilerOptions配置baseUrl、paths;

{
  "compilerOptions": {
    /* alias */
    "baseUrl": "./",
    "paths": {
      "~/*": ["./*"],
      "@/*": ["src/*"],
      "@ts/*": ["src/types/*"],
      "@utils/*": ["src/utils/*"],
      "@comps/*": ["src/components/*"],
    }  
  }
}

2.2 提供全局 less/scss 变量

  • less: pnpm add -D less

    // vite.config.ts
    export default defineConfig({
      css: {
        preprocessorOptions:{
          less:{
            additionalData: '@import "@/styles/variables/index.less";',
            javascriptEnabled: true,
          }
        }
      },
    })
    
  • sass: pnpm add -D sass

    // vite.config.ts
    export default defineConfig({
      css: {
        preprocessorOptions:{
          scss:{
            additionalData: '@use "@/styles/variables/index.scss" as *;'
          }
        }
      },
    })
    

2.3 定义全局常量替换方式define

定义全局常量替换方式。其中每项在开发环境下会被定义在全局,而在构建时被静态替换。

// config/config/define.ts
import dayjs from 'dayjs';
import pkg from '../../package.json';

/** 项目构建时间 */
const APP_BUILD_TIME = JSON.stringify(dayjs().format('YYYY-MM-DD HH:mm:ss'));

/** 项目信息 */
const { dependencies, devDependencies, name, version } = pkg;
const APP_PKG_INFO = {
  dependencies, 
  devDependencies, 
  name, 
  version
};

export const viteDefine = {
  APP_BUILD_TIME,
  APP_PKG_INFO
}

NOTE:想要获得类型检查以及代码提示: 确保在全局声明文件(比如:global.d.ts)中添加类型声明。

// src/types/dts/global.d.ts
/** 构建时间 */
declare const APP_BUILD_TIME: string;

/** 平台的名称、版本、依赖 */
declare const PKGVersionInfo: {
  name: string;
  version: string;
}
declare const APP_PKG_INFO: {
  name: string;
  version: string;
  dependencies: PkgVersionInfo[];
  devDependencies: PkgVersionInfo[];
}

/**
 * .vue 文件中使用
  <script lang="ts" setup>
  const BUILDTIME = ref(APP_BUILD_TIME);
  const APPINFO = ref(APP_PKG_INFO)
  </script>
 */

2.4 配置代理 Proxy

Vite代理配置选项:server-proxy

export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
  const viteEnv = loadEnv(mode, process.cwd()) as unknown as ImportMetaEnv;
  return {
    server: {
      port: viteEnv.VITE_PORT,
      open: false,
      proxy: {
        /** 代理标识符,可根据实际情况自行修改 */
        '/api': {
          /** 要代理的后端 api 地址  !可以将改地址配置到环境文件中,然后通过viteEnv引用 */
          target: 'http://255.255.1.1:8081/',
          /** 是否改写 origin。设为 true,则请求 header 中 origin 将会与 target 配置项的域名一致。 */
          changeOrigin: true,
          /** 重写转发的请求链接, 如果需要 */
          rewrite: (url) => url.replace(/^\/api/, ''), 
        },
      },
    },
  }
})

3 plugins

3.1 按需自动导入 vue

vue、vue-router、pinia都可以通过unplugin-auto-import实现自动导入(只建议设置vue自动导入)。
安装:pnpm add -D unplugin-auto-import

import AutoImport from 'unplugin-auto-import/vite';

export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
  return {
    plugins: [
      vue({
        script: {
          defineModel: true,  // Vite 插件会自动为所有 Vue 组件创建对应的 TypeScript 接口,以增强类型检查的效果
        }
      }),
      AutoImport({
        imports: ['vue'],
        dts: 'src/types/auto-import.d.ts',  // 指定文件生成位置
      }),
    ]
  }
})

3.2 使用TSX/JSX

  • 依赖:pnpm add -D @vitejs/plugin-vue-jsx

  • 使用:

    import vueJsx from '@vitejs/plugin-vue-jsx';
    
    export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
      return {
        plugins: [ vueJsx() ]
      }
    })
    
  • 确保tsconfig.json中包含以下配置

    {
      "compilerOptions": {
        "jsx": "preserve",  // 主要是这个,使.tsx 中支持 JSX
        "jsxFactory": "h",
        "jsxFragmentFactory": "Fragment"
      }
    }
    
  • 基础示例:

    export default defineComponent({
      name: "TsxTest",
      render() {
        const _envTitle = ref<string>(import.meta.env.VITE_APP_NAME)
    
        console.log("_envTitle:", _envTitle.value);
        const count = ref<number>(0)
    
        return (
          <>
            <h1>Tsx_Test: {_envTitle.value}</h1>
            <p>测试{count.value}</p>
          </>
        )
      }
    })
    
    /**
     * 引入.vue文件一样: import TsxTest from '@comps/TsxTest';
     * <template>
     *  <TsxTest />
     * </template>
     */
    

3.3 可选:按需引入UI框架

  • ElementPlus: https://element-plus.org/zh-CN/guide/design

  • naive-ui: https://www.naiveui.com/zh-CN/os-theme/docs/installation

  • AntDesignVue: https://www.antdv.com/docs/vue/introduce-cn/

3.4 综合

创建与src同级的文件夹config,里面所有的文件,都属于在vite.config.ts文件中引用。

├── config
│   ├── build         # 构建配置
│   ├── plugins       # 插件配置
│   └── utils         # 工具函数
├── src
...
  • config/plugins/index.ts: 插件配置文件,用于配置插件,并返回一个vite插件数组

    import type { PluginOption } from "vite";
    import vue from '@vitejs/plugin-vue';
    import vueJsx from '@vitejs/plugin-vue-jsx';
    import AutoImport from 'unplugin-auto-import/vite';
    import Components from 'unplugin-vue-components/vite';
    import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';
    
    /**
     * vite插件
     * @returns PluginOption[]
     */
    export const setupVitePlugins = (): PluginOption[] => {  
      const plugins = [
        vue({
          script: {
            defineModel: true,  /** Vite 插件会自动为所有 Vue 组件创建对应的 TypeScript 接口,以增强类型检查的效果 */
          }
        }),
        vueJsx(), /** tsx jsx 语法支持 */
        /** 动导入常用的 API,减少手动导入的繁琐操作 */
        AutoImport({
          resolvers: [
            ElementPlusResolver()
          ],
          imports: ['vue'],
          dts: 'src/types/auto-import.d.ts',  /** 指定文件生成位置 */
        }),
        /** 自动注册组件,使得你可以直接在模板中使用全局或局部组件,而无需显式地通过 import 或 components 选项来注册。 */
        Components({
          resolvers: [
            ElementPlusResolver()
          ],
          dts: 'src/types/components.d.ts'
        }),
      ]
    
      return plugins
    }
    
  • config/build/index.ts: vite.config.ts的其它配置选项,如defineresolvecss

    • index.ts:统一导出文件

      export * from './define';
      
    • define.ts:定义环境变量,用于在vite.config.ts中引用

      import dayjs from 'dayjs';
      import pkg from '../../package.json';
      
      /** 项目构建时间 */
      const APP_BUILD_TIME = JSON.stringify(dayjs().format('YYYY-MM-DD HH:mm:ss'));
      
      interface Package {
        name: string;
        version: string;
        dependencies: Record<string, string>;
        devDependencies: Record<string, string>;
        [key: string]: any;
      }
      interface PkgVersionInfo {
        name: string;
        version: string;
      }
      
      /** 项目信息 */
      const { dependencies, devDependencies, name, version } = pkg as Package;
      function transformVersionData(tuple: [string, string]): PkgVersionInfo {
        const [name, version] = tuple;
        return {
          name,
          version
        };
      }
      const APP_PKG_INFO = {
        name, 
        version,
        dependencies: Object.entries(dependencies).map(item => transformVersionData(item)), 
        devDependencies: Object.entries(devDependencies).map(item => transformVersionData(item))
      };
      
      export const viteDefine = {
        APP_BUILD_TIME,
        APP_PKG_INFO
      }
      
  • config/utils/index.ts: 工具函数,用于封装一些通用的工具函数,如:getLocalIPAddress

    import os from 'os';
    
    export const getLocalIPAddress = ():string => {
      const interfaces = os.networkInterfaces();
      for (const interfaceName in interfaces) {
        const addresses = interfaces[interfaceName] as os.NetworkInterfaceInfo[]
        for (const address of addresses) {
          if (address.family === 'IPv4' && !address.internal) {
            return address.address
          }
        }
      }
      return 'localhost'
    }
    
    export function isDevelopment(): boolean {
      return process.env.NODE_ENV === 'development';
    }
    
  • vite.config.ts

    import type { ConfigEnv, UserConfig } from 'vite';
    import { defineConfig, loadEnv } from 'vite';
    import { resolve } from "path";
    
    // ./config
    import { viteDefine } from "./config/build";
    import { setupVitePlugins } from "./config/plugins";
    import { getLocalIPAddress } from "./config/utils";
    
    // https://vitejs.dev/config/
    export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
      const viteEnv = loadEnv(mode, process.cwd()) as unknown as ImportMetaEnv;
      return {
        server: {
          host: getLocalIPAddress(),
          port: viteEnv.VITE_PORT,
          open: false
        },
        define: viteDefine,
        plugins: setupVitePlugins(),
        resolve: {
          alias: {
            "~": resolve(process.cwd()),
            "@": resolve(__dirname, 'src'),
            '@ts': resolve(__dirname, 'src/types'),
            '@utils': resolve(__dirname, 'src/utils'),
            '@comps': resolve(__dirname, 'src/components')
          }
        },
        css: {
          preprocessorOptions: {
            scss: {
              additionalData: '@use "@/styles/scss/variables.scss";',
              javascriptEnabled: true,
            }
          }
        },
        // 打包配置
        build: { }
      }
    })
    
    

4 定义环境变量

.env         # 存放不同环境共用的环境变量,定义的变量将被各环境共享
.env.dev     # 开发环境  => .env.development
.env.prod    # 正式环境  => .env.production
.env.stage   # 测试环境  => .env.staging

配置示例:

  • .env

    # 项目名称
    VITE_APP_NAME = "vue-admin-main"
    
  • .env.development

    NODE_ENV = "development"
    # 端口号
    VITE_PORT = 3375
    # 接口地址
    VITE_APP_BASE_URL = "http://255.255.255.1:8080"
    
  • .env.production

    NODE_ENV = "production"
    VITE_PORT = 3071
    VITE_APP_BASE_URL = "http://127.0.0.1:3030"
    

**NOTE1:**在vite中默认以"VITE_"开头的变量,将被挂载在 import.meta.env 对象上。

NOTE:实现环境变量代码自动提示: 添加全局类型声明(注意tsconfig.json中的include配置是否正确)

// src/types/env.d.ts
interface ImportMetaEnv {
  VITE_PORT: number
  VITE_APP_BASE_URL: string
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}

// 使用示例:console.log(import.meta.env.VITE_APP_BASE_URL)
// 当写到 import.meta.env. 时会有后面的代码提示

vite.config.ts中使用示例

// vite.config.ts
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
  const viteEnv = loadEnv(mode, process.cwd()) as unknown as ImportMetaEnv;
  return {
    server: {
      port: viteEnv.VITE_PORT,
      open: false
    },
  }
})

5 tsconfig.json的个别配置

项目创建时会自动生成tsconfig.json文件,但部分配置需要自己补充

{
  "compilerOptions":{
    "strict": true, //启用所有严格类型检查选项
    "jsx": "preserve", //指定生成的 JSX 代码样式, preserve模式,在preserve模式下生成代码中会保留JSX以供后续的转换操作使用
    "jsxImportSource": "vue",

    "noUnusedLocals": false, /*true 时,在编译时检测未使用的局部变量,并生成相应的警告或错误*/
    "noUnusedParameters": false, /*true 时,TypeScript 编译器会检测未使用的函数参数,并生成相应的警告*/
    "allowJs": true,  /* true => 允许编译器编译JS,JSX文件 */
    "noImplicitAny": false, /* true => 对具有隐含的 ‘any’ 类型的表达式和声明启用错误报告 */
    "skipLibCheck": true, //忽略所有的声明文件( *.d.ts)的类型检查
    "forceConsistentCasingInFileNames": true, //禁止对同一个文件的不一致的引用
    "composite": true,  // 启用项目编译的增量构建和依赖管理,通常与 "references" 配合使用。
    "noEmit": true, // 禁止 TypeScript 编译器生成输出文件(如 JavaScript 文件),仅进行类型检查。

    "baseUrl": "./",  /** 解析非相对模块的基地址,默认是当前目录 */
    "paths": {        /** 模块名到基于 baseUrl的路径映射的列表 */
      "@/*": [ "src/*" ],
      "@ts/*": [ "src/types/*" ],
      "@utils/*": [ "src/utils/*" ],
      "@comps/*": [ "src/components/*" ]
    },
    /** 指定要包含但在源文件中不引用的类型包名称 */
    "types": [
      "node",
      "vite/client",  // 引入 Vite 客户端类型声明,但这只是告诉 TypeScript 编译器项目所依赖的类型声明模块
    ]
    
  },
  /*  指定要包含在编译中的文件或文件夹的模式 */
  "include": [
    "mock/*.ts",
    "src",
    "vite.config.ts",
    "config/**/*.ts",
    "*.config.ts",
    "*.config.mts",
  ],
  /* 指定不需要被 TypeScript 编译器编译的文件或文件夹 */
  "exclude": ["dist", "node_modules"],
  /* 指定项目的依赖关系,引用的其他 TypeScript 配置文件 */
  "references": [{ "path": "./tsconfig.node.json" }]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值