Skip to content

Vite ​

默认配置 ​

ts
// vite
import { defineConfig } from 'vite';
// 导入vue
import vue from '@vitejs/plugin-vue';
// vue调试工具
import vueDevTools from 'vite-plugin-vue-devtools';
// element按需引入
import AutoImport from 'unplugin-auto-import/vite';
// 自动注册全局组件
import Components from 'unplugin-vue-components/vite';
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers';

// 路径模块(1)
// import { resolve } from "path";

// 路径导入以及url(2)
import { fileURLToPath, URL } from 'node:url';

export default defineConfig(({ mode })=>({
  base: "./",
  resolve: {
    alias: {
      // "@": resolve(__dirname, "./src"),
      // comp: resolve(__dirname, "./src/components"),

      '@': fileURLToPath(new URL('./src', import.meta.url)),
      'comp': fileURLToPath(new URL('./src/components', import.meta.url)),
    },
  },
  plugins: [
    vue(),
    // vue调试工具 【只开发环境使用】
    mode === 'development' ? vueDevTools() : undefined,
    // element按需引入
    AutoImport({
      resolvers: [ElementPlusResolver()],
      // 生成的类型文件路径(会自动创建,TS会识别)
      // dts: 'src/auto-imports.d.ts',
    }),
    Components({
      resolvers: [ElementPlusResolver()],
      // 生成的组件类型文件路径
      // dts: 'src/components.d.ts',

      // 方法1:明确指定只扫描哪些目录
      dirs: ["src/components/base"], // 只自动注册 `src/components/base`下的组件
      // 方法2:使用排除规则(正则表达式)
      // exclude: [
      //   /TopNavigation\.vue/, // 排除 TopNavigation 组件
      //   /loading\index\.vue/,       // 排除 Loading 组件
      //   /Progress\.vue/,       // 排除 progress 组件
      // ],
    }),
  ],
  
  // 生产构建设置
  build: {
    rollupOptions: {
      // 打包输出配置
      output: {
        /********** 使用 contenthash,确保只有内容变化的文件指纹才变 **********/
        // 1. 入口文件:使用 contenthash,8位长度通常足够且兼顾文件名长度
        entryFileNames: 'assets/[name]-[hash:16].js',

        // 2. 动态导入的 chunk(即你的各个页面):独立计算 contenthash
        chunkFileNames: 'assets/[name]-[hash:16].js',

        // 3. 静态资源(CSS、图片等):同样使用 contenthash
        assetFileNames: 'assets/[name]-[hash:16][extname]',

        // 分包
        manualChunks: (id)=> {
          // 把第三方依赖进行分包处理
          if (id.includes('node_modules') && (id.endsWith('.js') || id.endsWith('.ts'))) {
            return 'vendor';
          }
        }
      }
    }
  },
  
  server: {
    // 自动打开浏览器进行预览
    open: false,
    // 网络端口号
    port: 9999,
    host: "0.0.0.0",
    // 配置反向代理
    proxy: {
      "/api": {
        target: "http://localhost:3000",
        // target就是你要访问的目标地址,可以是基础地址,这样方便在这个网站的其他api口调用数据
        ws: true, //是否代理 websockets
        secure: false, //是否https接口
        changeOrigin: true, //是否跨域
        rewrite: (path) => path.replace(/^\/api/, ""),
      },
      "/apiOne": {
        target: "http://luke.svvs.top",
        // target就是你要访问的目标地址,可以是基础地址,这样方便在这个网站的其他api口调用数据
        ws: true, //是否代理 websockets
        secure: false, //是否https接口
        changeOrigin: true, //是否跨域
        rewrite: (path) => path.replace(/^\/apiOne/, ""),
      },
    },
  },
}));

版本检测 ​

1. 自定义vite插件 ​

该插件的作用?

在 Vite 构建完成后,生成一个 version.json 文件,里面包含当前的时间戳作为版本号。

实现功能:线上对version.json进行轮询检测版本号,有新版上线提示用户进行页面更新。

在src同级目录下创建plugins/versionPlugin.ts文件,复制保存一下代码块即可。

ts
/****************** 把当前时间戳写入version.json中,并保存到 outDir 目录中 ******************/

/** 该插件的作用:在 Vite 构建完成后,生成一个 version.json 文件,里面包含当前的时间戳作为版本号。 */

// fs模块用于文件操作,path模块用于处理文件路径
import fs from 'node:fs';
// path模块用于处理文件路径
import path from 'node:path';
// Plugin和ResolvedConfig类型用于定义Vite插件的类型。
import type { Plugin, ResolvedConfig } from 'vite';

// versionPlugin函数返回一个符合Vite插件规范的对象
export function versionPlugin(): Plugin {
  // 声明闭包变量
  let config: ResolvedConfig;

  // 返回插件对象
  return {
    /**
     * 插件名称
     * 当构建出错或打印警告时,Vite 会显示 [version-plugin] xxx error,让你快速定位是哪个插件的问题。
     * Vite 内部通过 name 来识别插件,避免同一个插件被重复加载。
     */
    name: 'version-plugin',
    // 限制该插件仅在构建模式下生效
    apply: 'build', // 只在 build 时生效

    /* configResolved:
    执行时机: 在 Vite 解析完所有配置文件和插件配置之后、构建正式开始之前触发。
    参数: 接收一个 resolvedConfig 对象,这是 Vite 经过合并、校验、补全默认值后的最终只读配置。
    核心作用: 当你需要在其他钩子中使用 Vite 的最终配置(如 outDir、base、环境变量等)时,就必须通过这个钩子把它存下来。 */
    configResolved(resolvedConfig) {
      // 保存最终配置
      config = resolvedConfig;
    },

    // closeBundle:在整个打包流程结束后执行
    closeBundle() {
      const version = Date.now().toString();
      // 直接写到 dist 目录(outDir),不污染源码的 public 目录
      const outDir = config.build.outDir; // 使用最终配置config
      const versionFile = path.resolve(outDir, 'version.json');
      
      // 确保 dist 目录存在
      if (!fs.existsSync(outDir)) {
        fs.mkdirSync(outDir, { recursive: true });
      }
      
      // 将版本号的 JSON 对象同步写入到指定文件中
      fs.writeFileSync(versionFile, JSON.stringify({ version }));
      console.log(`\n[version-plugin] 已生成 version.json: ${version}`);
    },
  }
}

2. 在vite中使用插件 ​

vite.config.ts文件中配置即可。

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

// 导入自动生成版本号更新插件
import { versionPlugin } from './plugins/versionPlugin';

export default defineConfig(({ mode })=>{
  return {
    ...,
    plugins: [
      vue(),
      // 使用自动生成版本号更新插件
      versionPlugin(),
    ],
    ...,
  }
});

3. 轮询 version.json ​

关键提示

在任意的js或ts文件中,写入以下代码进行轮询查询,必须保证以下代码只执行一次即可。

ts
const CHECK_INTERVAL = 5 * 60 * 1000 // 5分钟检测一次
// 保存至本地版本号时间戳的Key
const VERSION_KEY = 'versionTimestamp';
// 保存计时器,便于清除
let versionTimer: ReturnType<typeof setInterval> | null = null;
// 开始先删除本地版本号时间戳的Key,防止刷新后还是会触发弹窗
sessionStorage.removeItem(VERSION_KEY);
// 执行请求
function startVersionCheck() {
  // 首次加载立即检测
  checkVersion();
  // 之后定时检测
  versionTimer = setInterval(()=> checkVersion(), CHECK_INTERVAL);
}

// 立即执行
startVersionCheck();

async function checkVersion() {
  try {
    // 发送请求获取数据
    const sendVersion = await fetch('/version.json?t=' + Date.now());
    // 转换json数据
    const result = await sendVersion.json();
    // 获取版本号时间戳
    const serverVersion = result.version;

    // 获取本地存储的版本时间戳
    const sessionVersion = sessionStorage.getItem(VERSION_KEY);

    // 判断版本是否更新
    if (sessionVersion && sessionVersion !== serverVersion) {
      // 清除计时器
      versionTimer && clearInterval(versionTimer);

      // 发现新版本,提示用户
      const confirmed = confirm('页面有更新,刷新一下体验更好哦~');
      // 刷新页面
      confirmed && location.reload();

      // 重新开启计时器
      versionTimer = setInterval(()=> checkVersion(), CHECK_INTERVAL)
    }

    // 保存当前版本
    sessionStorage.setItem(VERSION_KEY, serverVersion);
  } catch (e) {
    // console.warn('版本检测失败', e)
  }
}

Copyright © 2026 Luke