Taro 4 项目从 npm 迁移到 pnpm 踩坑实录

36 阅读6分钟

Taro 4 项目从 npm 迁移到 pnpm 踩坑实录

最近将一个基于 Taro 4 + React + TDesign 的微信小程序项目从 npm 迁移到 pnpm,原本只是想利用 pnpm 的依赖缓存机制提升安装效率,减少磁盘占用。

没想到迁移过程中连续遇到了几个问题:先是开发环境无法启动,修复后又出现微信小程序上传缺少依赖,最后上传阶段不断在不同的 JS 文件上报 undefined 错误。

项目主要依赖版本如下:

依赖版本
Taro4.3.0
pnpm12.18.0
React18.3.1
TDesign Miniprogram1.16.0
@tarojs/plugin-mini-ci4.3.1
miniprogram-ci1.9.17
Webpack5.91.0

迁移后,首先使用 pnpm 安装项目依赖:

pnpm install

依赖安装过程没有明显异常,但运行微信小程序开发命令时:

pnpm dev:weapp

却发现项目无法正常启动。

为了排查问题,将终端日志重定向到文件:

pnpm dev:weapp *> pnpm-error.log

打开 pnpm-error.log 后发现,Taro 生成的 Webpack 配置中,@tarojs/runtime 和 @tarojs/shared 的解析结果都是 null,导致 Webpack 无法正常启动。

这是迁移中遇到的第一个问题。

解决开发环境启动问题后,继续执行微信小程序上传命令:

pnpm run build:weapp:upload

这次又出现了新的错误:

请安装依赖:miniprogram-ci

补齐依赖后,构建可以正常完成,但上传阶段又陆续出现以下错误:

done: app.js
processing: miniprogram_npm/tdesign-miniprogram/button/index.js

错误 上传失败
app.js: undefined

[ELIFECYCLE] Command failed with exit code 1.

再次执行,报错文件可能变成:

错误 上传失败
miniprogram_npm/tdesign-miniprogram/badge/badge.js: undefined

甚至是:

错误 上传失败
common.js: undefined

这些错误并不是同一个文件固定报错,容易让人误以为是 TDesign 组件、Webpack 编译产物或者微信小程序上传工具本身出现了问题。

经过逐步排查,最终的处理方式是:显式补充 Taro 依赖、安装小程序 CI 上传依赖,以及调整 pnpm 的依赖布局模式。

解决方案

1. 解决 Webpack 无法解析 Taro 依赖的问题

迁移后,首先遇到的问题是:

pnpm dev:weapp

无法正常启动。

通过 PowerShell 将日志保存到文件:

pnpm dev:weapp *> pnpm-error.log

在日志中发现,Taro 生成的 Webpack 配置里,两个模块的解析结果为 null:

@tarojs/runtime: null
@tarojs/shared: null

pnpm 默认采用严格的依赖隔离机制。与 npm 相对扁平的 node_modules 不同,pnpm 不会默认让所有间接依赖都能被项目直接访问。

如果项目或构建工具需要直接解析某些依赖,但项目没有显式声明,就可能出现模块解析失败。

针对这次遇到的问题,直接安装与当前 Taro 版本一致的依赖:

pnpm add @tarojs/runtime@4.3.0 @tarojs/shared@4.3.0

安装完成后,重新启动:

pnpm dev:weapp

此时开发环境恢复正常。

需要注意的是,@tarojs/runtime 和 @tarojs/shared 应尽量与项目中的 @tarojs/taro 保持版本一致,避免引入额外的版本冲突。

2. 解决微信小程序上传缺少 miniprogram-ci 的问题

开发环境启动成功后,继续执行上传:

pnpm run build:weapp:upload

项目对应的脚本配置为:

{
  "scripts": {
    "dev:weapp": "taro build --type weapp --watch",
    "build:weapp": "taro build --type weapp",
    "build:weapp:upload": "taro build --type weapp --upload"
  }
}

但执行时提示:

请安装依赖:miniprogram-ci

这是因为 Taro 的小程序 CI 上传流程需要使用 miniprogram-ci。

在 npm 环境中,某些依赖可能通过间接依赖的方式被访问到;迁移到 pnpm 后,这类未显式声明的依赖更容易暴露出来。

解决方式是将 miniprogram-ci 显式安装为开发依赖:

pnpm add -D miniprogram-ci@1.9.17

这里使用的是本次项目的版本。其他项目应根据当前 Taro CI 插件的兼容要求选择对应版本。

安装完成后,再次执行:

pnpm run build:weapp:upload

这次不再提示缺少 miniprogram-ci,但又出现了新的上传错误。

3. 解决上传阶段 JS 文件报 undefined 的问题

安装 miniprogram-ci 后,项目已经能够正常编译,但在上传阶段出现:

错误 上传失败
app.js: undefined

随后多次尝试,错误文件又变成:

miniprogram_npm/tdesign-miniprogram/badge/badge.js: undefined

或者:

common.js: undefined

这里有两个比较明显的特点:

  • Taro 构建可以正常完成,问题集中在上传流程。
  • 报错涉及不同的 JS 文件,并不是某个固定组件始终编译失败。

这时需要考虑 pnpm 与 npm 在依赖布局上的差异。

pnpm 默认使用 isolated 模式,通过虚拟存储目录及链接管理依赖,安装后通常可以看到:

node_modules/
├── .pnpm/
│   ├── @tarojs+cli@4.3.0/
│   ├── miniprogram-ci@1.9.17/
│   └── ...
├── @tarojs/
└── tdesign-miniprogram/

这种机制可以严格管理依赖关系,但部分原本依赖传统扁平化 node_modules 结构的工具链,可能存在兼容性问题。

对于从 npm 迁移而来的老项目,可以尝试将依赖布局切换为 hoisted 模式。

本次使用的是 pnpm 12,需要在项目根目录使用 pnpm-workspace.yaml 管理对应配置。

首先执行:

pnpm config set --location=project nodeLinker hoisted

然后检查:

pnpm config get nodeLinker

期望得到:

hoisted

也可以直接打开项目根目录的 pnpm-workspace.yaml,添加:

nodeLinker: hoisted

如果文件已经存在,只需要添加对应字段,不要覆盖原有的 workspace 配置。

hoisted 模式会让 pnpm 使用更接近 npm 的扁平化依赖布局,从而减少部分工具链在依赖路径解析方面的兼容问题。

修改配置后,需要重新安装依赖才能应用新的布局。

在 Windows PowerShell 中执行:

Remove-Item -Recurse -Force node_modules
pnpm install

这里建议保留 pnpm-lock.yaml,避免因为重新解析依赖版本而引入其他变量。

完成后重新构建:

pnpm run build:weapp

再执行上传:

pnpm run build:weapp:upload

本次迁移中的上传问题最终通过调整 nodeLinker 为 hoisted 得到解决。

需要说明的是,这种处理方式说明依赖布局与问题有关,但不能仅凭 undefined 错误认定 miniprogram-ci 内部的具体故障原因。

4. 将 pnpm 配置保存到项目

为了避免其他开发者重新安装依赖后再次遇到相同问题,建议将项目级配置一并提交到 Git。

项目根目录保留:

# pnpm-workspace.yaml
nodeLinker: hoisted

同时可以在 package.json 中声明包管理器版本:

{
  "packageManager": "pnpm@12.18.0"
}

这样团队成员和 CI 环境可以尽量保持一致的 pnpm 版本及依赖安装行为。

需要注意,hoisted 并不是 pnpm 的默认推荐模式,也不是所有 Taro 项目的必需配置。对于新项目,仍然可以优先使用默认的依赖隔离模式。