pnpm替换lerna+yarn的踩坑记录

2,223 阅读3分钟

如果有使用monorepo的需求,lerna+yarn会是很多开发者的选择,然而在实际开发中,lerna的很多功能我们并不需要,同时它也存在着一定的上手学习成本,而且 yarn也会存在一些问题比如多个项目会重复安装依赖、幽灵依赖等,这时候不妨考虑用更加轻便高效的pnpm

pnpm

pnpmyarn/npm一样是一款包管理工具,不同于yarn/npm的扁平化的依赖管理机制,pnpm采用软硬链接的机制实现依赖的管理和引用,不仅安装速度极快,高效利用磁盘空间,也解决了幽灵依赖的问题,而且pnpm workplace 提供了monorepo的支持,我们完全可以用pnpm替换lerna+yarn

如何替换

  1. 全局安装pnpm
 npm install -g pnpm    
  1. 先配置pnpm workspace,在项目根目录新建一个pnpm-workspaceyaml 输入如下代码
packages:

 \- 'packages/*'   
  1. 删除项目中已有的lerna.json, 用pnpm的 workspace 来替代lerna实现monorepo的包管理方式
  2. 执行pnpm install安装项目需要的依赖(安装过程中可能出现卡住的情况,可能是因为在内网环境下源不稳定重新安装即可) 安装成功后如果是Windows环境会在你当前项目所在硬盘的根目录下新增了一个.pnpm-store文件夹,刚刚装的依赖文件都在里面
  3. 把项目中的package-lock.jsonyarn.lock删掉,所有锁都由 pnpm-lock.yaml来提供,至此pnpm的替换工作结束
  4. 替换掉脚本命令,与yarn相关的命令替换为: pnpm 或者 pnpm run
  5. 调整 pipeline、以及Dockfile或者其他CI/CD配置文件里面的依赖安装命令

遇到的坑和解决方式

1、vite打包失败
Error:[vite]:Rollup failed to resolve import "vue-property-decorator" from "../shared/xxx/index.ts".This is most likely unintended because it can break your application at runtime.If you do want to externalize this module explicitly add it tobuild.rollupoptions.external'

执行打包指令后会遇到一些导入依赖失败的问题,这是因为pnpm的依赖引用是根据package.json中是否有正确声明依赖来实现的,比如你要在packages/shared里声明vue-property-decorator,那必须要在packages/shared/package.json中有声明这个依赖,而现在shared中并没有package.json文件,所以无法引入。 所以建议去除每个子项目的共同依赖,比如vue,lodash等,然后统一放入顶层的package.json

解决办法
  1. 在各子模块的package.json搜索导入失败的依赖XXX,确认它们版本号是否统一,如果是的话,直接执行pnpm add XXX -w在根目录添加这个依赖再pnpm rm XXX -F {package}, 如果版本不一致就不要rm这一步
  2. 重复第一步,直到所有有问题的依赖重新安装好
2、vue页面空白

初步判断是由于import { Vue } from 'vue-property-decorator'; 引入异常导致的 在vite.config中设置了vue的别名, 在引入vue的时候没有按pnpm规则走软硬链接的方式,而是去直接读取了根目录下node_modules/vue/dist/vue.js,所以没有正确取到真正的存在store下的vue依赖包

resolve: {
    alias: [
        ...
       { find: 'vue', replacement: lernaRootPath('node_modules/vue/dist/vue.js') },
   ],
},
解决办法:
  • 修改vue指向, 把vue指向vue/dist/vue.esm.js
resolve: {
    alias: [
        ...
        { find: 'vue', replacement: 'vue/dist/vue.esm.js' },
   ],
},
3、部分依赖需要预编译

因为ViteDevServer是基于浏览器的Natvie ES Module实现的,所以对于使用的依赖如果是CommonJSAMD的模块,则须要进行模块类型的转化,比如依赖中使用到了echarts4.6.0,它属于CommonJS的模块,需要进行预编译对它进行模块类型的转化

// vite.config.ts
const { merge } = require('webpack-merge');

export default defineConfig(({ mode }) => {
    return merge(common({ mode }), {
        optimizeDeps: {
            entries: ['./vite/entry.ts'],
            include: ['需要预编译的依赖'],
        },
        ....
    });
});