随笔记录:vue3+vite项目dist文件手动打包

vite对项目进行手动分包

记录一个在开发过程遇到的需求,对自己的项目build打包进行手动分包

项目概述:在一个vue项目中可能有多个不关联的模块内容,希望在build打包发布的时候对项目代码进行手动分包(按模块分)

要求:

1.希望项目打包之后按模块手动分包

项目大致结构

src -- 项目主要文件
└── view -- 页面文件
     ├── common -- 系统通用部分
          ├── page1 -- 业务代码1
          ├── page2 --  业务代码2
          ├── page3  --  业务代码3
    ├── module1 -- 模块1
    	  ├── page1 -- 业务代码1
          ├── page2 --  业务代码2
          ├── page3  --  业务代码3
	├── module2  -- 模块2
	├── module3  -- 模块3
└── api -- 接口请求文档
└── components -- 自定义组件文件夹
└── theme -- 样式文件

2.按模块打包之后,若某个模块下的代码修改之后重新打包后,生成的dist文件中的文件指纹进行变更,其他未修改的文件保持不变

3. 在进行build打包的时候可以指定模块进行增量打包,而不是全量打包

实现过程

实现代码的手动分包+文件hash文件指纹的缓存

import { defineConfig } from 'vite';
import { createHash } from 'crypto';
import { readFileSync, existsSync } from 'fs';
import { basename, extname, resolve } from 'path';

// 安全的文件内容读取函数
const getFileContent = (filePath) => {
  try {
    return existsSync(filePath) ? readFileSync(filePath, 'utf-8') : '';
  } catch (e) {
    console.warn(`Failed to read ${filePath}:`, e.message);
    return '';
  }
};

// 生成内容hash
const generateContentHash = (content, length = 8) => {
  return createHash('sha256')
    .update(content)
    .digest('hex')
    .substring(0, length);
};

plugins: [
    {
      name: 'file-content-hash',
      enforce: 'post',
      generateBundle(options, bundle) {
        const distDir = options.dir || 'dist';
        
        Object.entries(bundle).forEach(([fileName, chunk]) => {
          if (chunk.type === 'chunk' || chunk.type === 'asset') {
            const filePath = resolve(process.cwd(), distDir, fileName);
            const newContent = chunk.type === 'chunk' ? chunk.code : chunk.source;
            
            if (existsSync(filePath)) {
              const existingContent = readFileSync(filePath, 'utf-8');
              if (existingContent === newContent) {
                delete bundle[fileName]; // 保留已存在的相同文件
              }
            }
          }
        });
      }
    }
  ]
build: {
     outDir: 'dist', // 打包输出目录
     chunkSizeWarningLimit: 1500, // 代码分包阈值
     rollupOptions: {
          // 确保相同模块总是生成相同的 chunk
          hoistTransitiveImports: false,
          preserveEntrySignatures: 'strict',
          // 更稳定的模块 ID
          generatedCode: {
               preset: 'es2015'
          },
          output: {
               // compact: true,
               manualChunks(id) {
               		//所有第三方插件打包在一个文件内
               		if (id.includes('node_modules')) {
						 return 'vendor';
					}
					//1和2选一个使用
					// 1.modules1下所有的业务代码都放在modules1文件夹下
                    if (id.includes('src/views/modules1')) {
                         const match = id.match(/src\/views\/iotgw_modules\/([^\/]+)/)
							return match ? `modules1/${match[1]}` : undefined
                    }
                    // 2.按views下一级目录拆分打包
					if (id.includes('src/views/')) {
							// 按模块拆分
							const moduleMatch = id.match(/src\/views\/([^\/]+)/);
							if (moduleMatch) {
								return `views-${moduleMatch[1]}`;
							}
							return 'views-other';
					}
					return undefined
               },
               // JS文件命名规则(基于内容hash)
               chunkFileNames: (chunkInfo) => {
                    const content = chunkInfo.moduleIds
                         .map(id => getFileContent(id))
                         .join('');
                    const hash = generateContentHash(content);
                    return `assets/${chunkInfo.name}-${hash}.js`;
               },

               // 入口文件命名规则
               entryFileNames: (chunkInfo) => {
                    const content = chunkInfo.moduleIds
                         .map(id => getFileContent(id))
                         .join('');
                    const hash = generateContentHash(content);
                    return `assets/${chunkInfo.name}-${hash}.js`;
               },
               // 资源文件命名规则(CSS/图片等)
               assetFileNames: ({ name }) => {
                    const filePath = resolve(process.cwd(), name);
                    const content = getFileContent(filePath);
                    const hash = content ? generateContentHash(content) : '[hash]';
                    const ext = extname(name).slice(1);
                    const baseName = basename(name, `.${ext}`);
                    return `assets/${baseName}-${hash}.${ext}`;
               },
          },
     },
},

实现过程

对指定模块进行增量打包

例:针对modules1模块打包
1.新建一个vite.iot.config.ts文件

import { mergeConfig } from 'vite';
import baseConfig from './vite.config';

export default mergeConfig(baseConfig, {
  build: {
    rollupOptions: {
      output: {
        manualChunks(id: string) {
          if (id.includes('src/views/modules1')) return 'modules1';
          if (id.includes('node_modules')) return 'vendor';
          return undefined;
        }
      }
    }
  }
});

2.在package.json文件内配置

"scripts": {
		"dev": "vite",
		"build:modules1": "cross-env NODE_OPTIONS=--max-old-space-size=8096 vite build --config vite.iot.config.ts",
	},

3.运行打包命令

npm run build:modules1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值