因为我文笔不是很好,所以我只能贴下代码,其实里面很多代码都是在uni的源码里面复制过来的,主要的地方就是manualChunks这个方法,思路是参考的uni源码的vue3分支uni-app\packages\uni-mp-vite\src\plugin\build.ts文件的代码
新建一个文件,把下面代码复制进去
import fs from 'node:fs';
import path from 'node:path';
import type { Plugin } from 'vite';
import {
EXTNAME_JS_RE,
normalizePath,
hasJsonFile,
removeExt,
isCSSRequest,
DEFAULT_ASSETS_RE
} from '@dcloudio/uni-cli-shared';
const chunkFileNameBlackList = ['main', 'pages.json', 'manifest.json'];
function isVueJs(id: string) {
return id.includes('\0plugin-vue:export-helper');
}
function staticImportedByEntry(
id: string,
getModuleInfo: any,
cache: Map<string, boolean>,
importStack: string[] = []
): boolean {
if (cache.has(id)) {
return cache.get(id) as boolean;
}
if (importStack.includes(id)) {
// circular deps!
cache.set(id, false);
return false;
}
const mod = getModuleInfo(id);
if (!mod) {
cache.set(id, false);
return false;
}
if (mod.isEntry) {
cache.set(id, true);
return true;
}
const someImporterIs = mod.importers.some((importer) =>
staticImportedByEntry(importer, getModuleInfo, cache, importStack.concat(id))
);
cache.set(id, someImporterIs);
return someImporterIs;
}
export default function uniSubpackagePlugin(options?: {
entryFile?: string[];
exclude?: (string | RegExp)[];
}): Plugin {
const { entryFile, exclude } = options || {};
return {
name: 'uni-subpackage-plugin',
enforce: 'post',
apply: 'build',
config: function (config) {
try {
// 判断是否是小程序
if (
process.env.UNI_PLATFORM === 'mp-weixin' ||
process.env.UNI_PLATFORM === 'mp-alipay'
) {
const cache = new Map<string, boolean>();
// 获取项目路径
const inputDir = normalizePath(process.env.UNI_INPUT_DIR || '');
// 实例化pages.json
const pages: Record<string, any> = JSON.parse(
fs
.readFileSync(path.resolve(inputDir, 'pages.json'))
.toString()
.replace(/\s\/\/.*|\/\*[\s\S]*?\*\//g, '')
);
// 获取分包信息
const subPackages = (pages?.subPackages || []).map((item) => {
return {
name: item.root,
subPath: normalizePath(path.resolve(inputDir, item.root))
};
});
const getSubPackageInfo = (moduleInfo) => {
return subPackages.find(({ subPath }) => {
if (moduleInfo.importers) {
return moduleInfo.importers.every(
(item) => !!item.startsWith(subPath)
);
}
return moduleInfo.every((item) => !!item.startsWith(subPath));
});
};
if (!Array.isArray(config.build?.rollupOptions?.output)) {
if (!config.build) config.build = {};
if (!config.build.rollupOptions) config.build.rollupOptions = {};
if (!config.build.rollupOptions.output)
config.build.rollupOptions.output = {};
// 为了支持小程序的跨分包 JS 代码引用,避免文件名修改
config.build.rollupOptions.output['entryFileNames'] = function (chunk) {
if (chunk.name === 'main') {
return 'app.js';
}
if (entryFile?.includes(chunk.name)) {
const normalizedId = normalizePath(chunk.facadeModuleId);
const filename = removeExt(
normalizePath(
path.relative(inputDir, normalizedId.split('?')[0])
)
);
return filename + '.js';
}
return chunk.name + '.js';
};
// 为了支持小程序的 vendor.js 文件分包
config.build.rollupOptions.output['manualChunks'] = function (
id: string,
{ getModuleInfo }
) {
const normalizedId = normalizePath(id);
const filename = normalizedId.split('?')[0];
const isExclude = exclude?.some((item) => {
if (typeof item === 'string') {
return normalizedId.includes(item);
} else if (item instanceof RegExp) {
return item.test(normalizedId);
}
return false;
});
// 处理资源文件
if (DEFAULT_ASSETS_RE.test(filename)) {
const moduleInfo = getModuleInfo(id);
const subPackageInfo = getSubPackageInfo(moduleInfo);
if (!isExclude && subPackageInfo?.name) {
return subPackageInfo?.name + '/common/assets';
} else {
return 'common/assets';
}
}
// 处理项目内的js,ts文件
if (EXTNAME_JS_RE.test(filename)) {
const moduleInfo = getModuleInfo(id);
const subPackageInfo = getSubPackageInfo(moduleInfo);
const chunkFileName = removeExt(
normalizePath(path.relative(inputDir, filename))
);
if (
filename.startsWith(inputDir) &&
!filename.includes('node_modules')
) {
if (
!chunkFileNameBlackList.includes(chunkFileName) &&
!hasJsonFile(chunkFileName) // 无同名的page,component
) {
// 为了支持小程序的跨分包 JS 文件,这是控制输出的路径
if (
!isExclude &&
subPackageInfo?.name &&
!filename.startsWith(subPackageInfo?.subPath)
) {
return `${subPackageInfo?.name}/${chunkFileName}`;
}
return chunkFileName;
}
return;
} else if (filename.includes('node_modules')) {
if (
subPackageInfo?.name &&
moduleInfo.importers.every(
(item) => !item.includes('node_modules')
)
) {
// const directory =
// filename.match(/node_modules\/(.+)\.(.+)/);
// if (directory && directory[1]) {
// return (
// subPackageInfo?.name +
// '/node-modules/' +
// directory[1]
// );
// }
return subPackageInfo?.name + '/common/vendor';
} else if (
moduleInfo.importers.length > 1 &&
moduleInfo.importers.every((item) => {
const importerModuleInfo = getModuleInfo(item);
return importerModuleInfo.importers.every(
(item) => !item.includes('node_modules')
);
})
) {
return 'common/vendor';
}
}
if (isExclude) {
// 注释掉uni的分包规则,使用vite默认的分包规则,来支持vendor.js 文件分包,不然会分包不彻底
return 'common/vendor';
}
// vendor.js 文件分包
if (subPackageInfo?.name) {
return subPackageInfo?.name + '/common/vendor';
}
}
// 这里是uni的默认逻辑,不太清楚是干嘛的
if (
isVueJs(normalizedId) ||
(normalizedId.includes('node_modules') &&
!isCSSRequest(normalizedId) &&
// 使用原始路径,格式化的可能找不到模块信息 https://github.com/dcloudio/uni-app/issues/3425
staticImportedByEntry(id, getModuleInfo, cache))
) {
return 'common/vendor';
}
};
}
}
} catch (error) {
console.error('[uni-subpackage-plugin] error:', error);
}
return config;
}
};
}