不同项目出现差异化
本地测试项目1通过link链接本地包vitepress-pure-theme-zyco
包中有如下逻辑
// vitepress-pure-theme-zyco的useValine中动态导入UMD模块valine
import("valine").then(({ default: Valine }) => {
......
}
在测试项目1中生产和开发环境皆正常。
本地测试项目2通过install安装依赖,生产环境正常,开发环境出现error
报错原因
首先来分析一下为什么会报错?
动态import引入UMD模块,在动态引入模块的上下文环境下,window是undefined,因此UMD模块挂载库代码到window上时出现error
两个项目一个报错,一个正常,出现差异的原因
一开始以为是依赖了不同版本的vite,导致开发环境的差异。分析vite的打包源码后,两个本地项目依赖的vite,无论开发模式还是生产模式下的打包,都会通过@rollup/plugin-commonjs将模块转化为ESM,因此并非vite版本差异导致问题
最后发现是link本地vitepress-pure-theme-zyco,导致vite在开发环境下,进行optimize deps(依赖优化)时,valine会被处理,而通过安装依赖vitepress-pure-theme-zyco,optimize deps会跳过valine
// vite v5.4.9
// 在vite插件import-analysis进行依赖分析后,通过插件vite:resolve解析包信息的时候,利用tryNodeResolve方法解析valine
function tryNodeResolve( ){
...
// 这里skipOptimization决定了是否对模块进行依赖优化,即转化为ES模块
const skipOptimization = !options.ssrOptimizeCheck && depsOptimizer?.options.noDiscovery
|| !isJsType
|| importer && isInNodeModules$1(importer)
|| exclude?.includes(pkgId)
|| exclude?.includes(id)
|| SPECIAL_QUERY_RE.test(resolved)
|| !options.ssrOptimizeCheck && !isBuild && ssr
|| ssr && isFilePathESM(resolved,options.packageCache)
&& !(include?.includes(pkgId) || include?.includes(id));
...
if (skipOptimization) {
// 跳过了依赖优化
} else {
// 这里的逻辑决定该包会被optimize deps
const optimizedInfo = depsOptimizer.registerMissingImport(id, resolved);
resolved = depsOptimizer.getOptimizedDepId(optimizedInfo);
}
}
上面代码中importer && isInNodeModules$1(importer)决定skipOptimizationtrue还是false至关重要
开发环境下,link本地vitepress-pure-theme-zyco,然后本地包的逻辑中动态引入了UMD模块valine,此时vite dev打包逻辑中importer是指向本地文件的一个绝对路径,isInNodeModules$1判断importer不是node_modules里的模块,所以importer && isInNodeModules$1(importer)是false,最终skipOptimization为false,于是会对valine进行依赖优化
这也是为什么在测试项目1的开发环境下没有报错。
开发环境下,安装依赖vitepress-pure-theme-zyco,包逻辑中动态引入了UMD模块valine,此时vite dev打包逻辑中importer是指向node_modules的一个绝对路径,isInNodeModules$1判断importer是node_modules里的模块文件,所以importer && isInNodeModules$1(importer)是true,最终skipOptimization为true,于是依赖优化会跳过valine
这也是为什么在测试项目2的开发环境下会出现Error,即依赖的依赖会被跳过。
解决方案
因为vite在创建开发服务器的前置逻辑中,创建了依赖优化器,可以利用配置optimizeDeps.include强行优化指定模块
function createServer(inlineConfig = {}) {
return _createServer(inlineConfig, { hotListen: true });
}
async function _createServer(inlineConfig = {}, options) {
...
await initServer();
}
const initServer = async () => {
if (serverInited) return;
if (initingServer) return initingServer;
initingServer = async function() {
await container.buildStart({});
if (isDepsOptimizerEnabled(config, false)) {
// 这里将会对vite配置中optimizeDeps.include指定的包进行依赖优化
await initDepsOptimizer(config, server);
}
...
}();
return initingServer;
}
修改vite配置
{
optimizeDeps: {
include: [`vitepress-pure-theme-zyco > valine`],
}
}
最后再提一句,如果仅仅是include: [valine]是无效的,vite在第一层依赖中找不到指定包(因为该包是“依赖的依赖“),需要明确依赖的层级关系才有效。