npm ci;peerDependencies的作用;lock文件提交与否;xxxDependencies;

疑问

  • jenkins上直接用npm ci是否比yarn install的打包时间更快?
  • 还是那个g6装包问题,g6如果需求某个版本的tslib,是否是g6自身的问题?

    • 作为库开发者,有义务保证依赖包之间的强制最低版本要求!肯定是g6没写好peerDependencies或者踩了什么坑。

      摘录&心得

  • 所谓CI环境,应该是指类似jenkins上的包管理问题

  • 如果 package-lock.json 和 npm-shrinkwrap.json 同时存在于项目根目录,package-lock.json 将会被忽略。
  • 作为库开发者,有义务保证依赖包之间的强制最低版本要求。

    npm ci

  • npm ci 就是专门为 CI 环境准备的安装命令

  • npm ci相比npm install的不同之处

    • 项目中必须存在 package-lock.json 或 npm-shrinkwrap.json(早期lock文件);
    • 完全根据lock文件安装
      • 与package.json冲突时直接报错
      • 不需要计算依赖书,因此更快
      • 永远不会变更package.json和lock文件
    • 会删除项目中现有的node_modules

      lock文件

  • 为什么单一的package.json文件不能确定唯一的依赖树

    • 不同版本的npm的装包策略不同
    • package.json指定的是范围,某些依赖可能更新了更新的版本
  • 是否要提交lock文件

    • 共同开发一个应用:建议提交,因为要维系团队内包版本一致
    • 开发给外部使用的库:谨慎考虑,库项目一般是被依赖对象。
      • 不提交lock文件可以复用主项目包,减少重复依赖。
      • 建议定义peerDependencies(由此可见peerDependencies一般在开发依赖库时使用,用于减少重复依赖
      • 建议提交lock文件,但在发布时应该被忽略

        xxxDependencies

        依赖是否被打包,完全取决于项目中是否引用,xxxDependencies更多的是一种规范作用。
  • dependencies项目依赖

    • 这些依赖都会成为线上生产环境中的代码组成部分。
    • 会被下载!
  • devDependencies 开发依赖
    • 不会被自动下载
    • 一般只在开发阶段起作用或只是在开发环境中需要用到
    • 为辅助开发的工具包
      • 比如 Webpack,预处理器 babel-loader、scss-loader,测试工具……
  • peerDependencies 同版本依赖
    • 如果你安装我,那么你最好也安装我对应的依赖。
    • 使用场景
      • 我是插件,我不能独立运行
      • 我运行需要的核心库在peerDependencies里
      • 我不希望核心库被重复下载
  • bundledDependencies 捆绑依赖
    • 和 npm pack 打包命令有关
    • 指定的依赖包,必须先在 dependencies 和 devDependencies 声明过
  • optionalDependencies 可选依赖

    • 不建议使用,会增加项目的复杂度和不确定性
    • 你可以安装我,也可以不安装,佛系。

      依赖管理实例

      如何保证依赖包之间的强制最低版本要求?
      create-react-app会对项目中的核心依赖进行检索,不符合会直接退出。 ```javascript // 下面的代码是node.js代码,管中窥豹,可以看出node.js在前端担任的角色。 // 值得细读,对依赖包的版本管理的复杂性、重要性会有更深的理解。 function verifyPackageTree() { const depsToCheck = [ ‘babel-eslint’, ‘babel-jest’, ‘babel-loader’, ‘eslint’, ‘jest’, ‘webpack’, ‘webpack-dev-server’, ];

    const getSemverRegex = () => /\bv?(?:0|[1-9]\d).(?:0|[1-9]\d).(?:0|[1-9]\d)(?:-[\da-z-]+(?:.[\da-z-]+))?(?:+[\da-z-]+(?:.[\da-z-]+)*)?\b/gi; const ownPackageJson = require(‘../../package.json’); const expectedVersionsByDep = {}; depsToCheck.forEach(dep => { const expectedVersion = ownPackageJson.dependencies[dep]; if (!expectedVersion) {

    1. throw new Error('This dependency list is outdated, fix it.');

    } if (!getSemverRegex().test(expectedVersion)) {

    1. throw new Error(
    2. `The ${dep} package should be pinned, instead got version ${expectedVersion}.`
    3. );

    } expectedVersionsByDep[dep] = expectedVersion; });

    let currentDir = __dirname;

    while (true) { const previousDir = currentDir; currentDir = path.resolve(currentDir, ‘..’); if (currentDir === previousDir) {

    1. // We've reached the root.
    2. break;

    }

    const maybeNodeModules = path.resolve(currentDir, ‘node_modules’); if (!fs.existsSync(maybeNodeModules)) {

    1. continue;

    }

    depsToCheck.forEach(dep => {

    1. const maybeDep = path.resolve(maybeNodeModules, dep);
    2. if (!fs.existsSync(maybeDep)) {
    3. return;
    4. }
    5. const maybeDepPackageJson = path.resolve(maybeDep, 'package.json');
    6. if (!fs.existsSync(maybeDepPackageJson)) {
    7. return;
    8. }
    9. const depPackageJson = JSON.parse(
    10. fs.readFileSync(maybeDepPackageJson, 'utf8')
    11. );
    12. const expectedVersion = expectedVersionsByDep[dep];
    13. if (!semver.satisfies(depPackageJson.version, expectedVersion)) {
    14. console.error(//...);
    15. process.exit(1);
    16. }

    }); } } ```

    最佳实践

  • 优先使用 npm v5.4.2 以上的 npm 版本

  • 提交 package.json、lock文件
  • 版本升级
    • 依靠 npm update 命令升级到新的小版本
    • 依靠 npm install @ 升级大版本
  • 任何团队成员有更包操作,其他成员在拉取代码后,需执行install更新依赖
  • 任何时候都不要手动修改lock文件