逃脱长篇大论,7张脑图解读package.json配置信息!

1,082 阅读9分钟

我正在参加「掘金·启航计划」

1 基本配置

1.1 描述信息

image.png

  • npm官网上的js-md5包详细信息页:包含Name包名称、version版本、license许可证、Homepage项目主页、Repository代码地址。 image.png

  • npm官网上的js-md5搜索条主页:包含Name包名称、keysword关键字、description描述信息。

    image.png

  • package.json配置:

    {
      "name": "vue-project-name",
      "version": "0.1.0",
      "author":"author",
      "description":"项目描述",
      "keywords":["xxx","xxx"],
      "contributors":[{"name":"","email":""}],
      "bugs":{
        "url": "https://github.com/vuejs/vue/issues"
      },
      "homepage":"XXX.com",
      "repository":{
        "type":"git",
        "url":"git+https://..."
      },
    }
    

1.2 文件&目录

image.png

  • package.json配置:

    {
      "main":"dist/index.js",// 默认项目根目录下的index.js
      "browser":"./browser/index.js",// 只想在web使用,不允许在server端使用,可以通过browser字段
      "module":"dist/vue.runtime.esm.js",// 指定这个包被install时候有哪些文件
      "files":[
        "es",
        "lib"
      ],
      "man":,
      "directories":{
        "lib":"./lib",// 存放 js 代码的目录
        "bin":"./bin",// 若指定了bin字段,那么这个就无效了,存放可执行二进制文件的目录
        "doc":"./doc",// 存放文档的目录
        "example":"",
        ...
      },
      "bin":{
        "npm":"./cli.js",
      },
      "workspaces":[
        "packages/*"
      ],
      "man":[
         "./man/npm-access.1",
         "./man/npm-audit.1"
      ],
      "exports":{
          ".": "./main.mjs",    
          "./foo": "./foo.js",
          ...
      }
    }
    
  • exports:最大的特点就是条件引用,比如我们可以根据不同的引用方式或者模块化类型,来指定npm包引用不同的入口文件。如果我们通过const p = require("pkg")引用的是"./main-require.cjs",如果我们通过import p from "pkg"引用的是"./main-module.cjs"。如果存在exports属性,exports属性不仅优先级高于main,同时也高于module和browser字段

  • 创建工作空间(workspaces):创建工作空间需要private为true时workspace才会被启用,workspaces属性的值为一个字符串数组,每一项指代一个workspace路径,支持全局匹配,这里的路径指的是package.json所在文件夹名

      |--node_modules
      |--packages
        |--workspace-home  # 工作空间workspace-home
           |--src 
           |--package.json #独有的依赖,我们就可以单独安装在这个里面
        |--workspace-admin
               |--src   #我们这里可以引用share模块的, 由于定义的名字叫 @project/share, 我们引入的时候就像这样 import { xxxx } from '@project/share'
           |--package.json 
        |--share
           |--package.json #在package.json里面要定义name属性,比如这个叫 @project/share
      |--package.json  #公用的依赖,我们就可以安装在这个里面
      
    

    注意地,工作空间中,package.json我们需要添加name属性,这是工作空间名字,不是文件名。创建工作空间我们就可以让移动端与pc端项目共存在一个文件夹中了

  • vue项目搭建工作空间:新建一个packages的目录,这个目录新建三个文件夹admin,mobile,share,

    /vue-project
      |--packages
         |--admin pc端
            |--public
            |--src
            |--babel.config.js
            |--package.json
         |--mobile 移动端
            |--public
            |--src
            |--babel.config.js
            |--package.json
         |--share 放公用的代码
            |--index.js
            |--package.json
      |--package.json
    
  • 在对应的admin、mobile、share的package文件中定义相关脚本启动信息,以admin为例:

    // admin/package.json
    {
      "name": "@project/admin",
      "version": "0.1.0",
      "scripts": {
        "serve": "vue-cli-service serve",
        "dev": "vue-cli-service serve",
        "build": "vue-cli-service build",
        "lint": "vue-cli-service lint"
      },
      "devDependencies": {
        "@vue/cli-service": "~4.5.0"
      }
    }
    
    // 根目录下package.json
    {
     "private": true,
      "workspaces": [
        "packages/*"
      ],
      "scripts": {
        "lint": "yarn workspaces run lint",
        "build": "yarn workspaces run build",
        "dev:admin": "yarn --cwd packages/admin dev",
        "build:admin": "yarn --cwd packages/admin build",
        "dev:mobile": "yarn --cwd packages/mobile dev",
        "build:mobile": "yarn --cwd packages/mobile build"
      },
    }
    
  • 工作空间的相关命令:

    #能够共享的包就安装到根
    #工作空间独立的就单独安装到工作区
    #添加到根
    yarn add cross-env -D -W
    #删除根
    yarn remove cross-env  -W
    
    #如果想单独添加或者移除某个子项目的依赖,可以使用如下命令:
    yarn workspace <workspace_name > add <pkg_name> --dev
    yarn workspace <workspace_name > remove <pkg_name>
    
    #注意: workspace_name workspace_name 包名,package.json 中设置的 name,不是文件夹名
    
    #比如
    yarn workspace @project/home add  swiper
    yarn workspace @project/admin add  swiper
    
    yarn workspace @project/home add react-custom-scrollbars
    

1.3 发布配置

image.png

  • package.json配置:

    {
      "private":true,
      "preferGlobal":true,// 用户不把该模块安装为全局模块时,如果设置weitrue就会显示警告。
      "publishConfig":{
        "tag":"1.1.0"
        "registry":"http://registry.mddd.js.com",
        "access":"public",// 包的访问权限
      },
      "os":["darwin","linux"],
      "cpu":["x64","!mips"],// 适用cpu与禁用cpu
      "license":"MIT",
    }
    

1.4 第三方配置

image.png

  • package.json配置:

    {
     "gitHooks": {
        "pre-commit": "lint-staged"
      },
      "lint-staged": {
        "*.{js,jsx,vue}": [
          "vue-cli-service lint",
          "git add"
        ]
      }
      "browserlist":{
        "production":[
          ">0.2%",
          "not dead",
          "not op_mini all",
        ],
        "development":[
          "last 1 chrome version",
          "last 1 firefox version",
          "last 1 safari version"
        ]
      },
      "typings":"types/index.d.ts",
      "type":"module||common.js",
      "unpkg":"dist/vue.js",
      "babel": {
         "presets": ["@babel/preset-env"],
         "plugins": [...]
        },
      "eslintConfig":{
          "root": true,
          "env": {
            "node": true
          },
          "extends": [
            "plugin:vue/essential",
            "eslint:recommended"
          ],
          "rules": {},
          "parserOptions": {
            "parser": "babel-eslint"
         },
      },
      "jsdelivr": "dist/vue.global.js",// 与unpkg类似
      "sideEffects": [
         "a.js",
         "b.js"
       ]
    }
    
  • sideEffects:格式可以为boolean或string[],为fasle,告知打包工具,当前项目无副作用,可以适用tree shaking优化。为字符串数组,告知哪些文件无副作用,可以使用tree shaking优化。

  • engines:项目运行环境的要求声明

    "engines": {
        "node": ">=0.10.3 <15"
    }
    

2 脚本命令

image.png

2.1 script字段相关的配置字段

  • config:用来配置 scripts 运行时的配置参数。
    "config": { 
    "port": 3000 
    }
    

2.2 npm run xxx发生了什么?

其实npm run 实际上是npm run-script命令得简写,当我们运行npm run xxx时,基本步骤如下(以npm run serve为例):

"serve": "vue-cli-service serve",
  • 在项目的根路径找到“package.json”文件,然后在里面的“scripts”找到“xxx”命令,并执行。

  • 以传给npm run 的第一个参数作为键,也就是xxx为serve,执行npm run serve就是执行vue-cli-service serve命令。

    为什么不直接执行vue-cli-service serve?

    因为系统并没有vue-cli-service这个命令,只有npm相关命令,而在我们下载安装依赖的时候,会在node_modules/.bin目录下创建好名为vue-cli-service的可执行文件。

  • 当执行vue-cli-service serve命令时,npm会到./node_modules/.bin中找到可执行文件作为脚本来执行,npm 还会自动把node_modules/.bin加入$PATH 变量内,相当于执行了./node_modules/.bin/vue-cli-service serve

    依赖包里的.bin目录不是任何的npm包,其目录下文件的可执行文件都是一个个软链接;其余node_modules目录下的文件夹都是一个个下载下来的依赖模块软连接相当于是一种映射,在执行npm run xxx的时候,就会到.bin目录找到对应的映射文件,然后再找到相对应的js文件。一般针对一个依赖模块,在npm下会有三个可执行文件,而在yarn下只有两个(没有以.ps1为后缀名的)

    image.png

  • 通过.bin目录下的可执行文件映射到vue-cli-service.js并执行。

    软链接指向哪里?指向的是对应依赖模块的js文件,如vue-cli-serve指向node_modules/@vue/cli-service/bin/vue-cli-service.js。怎么体现?在npm安装的时候就将bin/vue-cli-service.js作为bin声明了。

    image.png

2.3 script字段里的命令

  • 脚本命令写法:一种是通过软链接映射,另一种是通过全路径指向在npm依赖包里的目标js文件。

    "script":{
        "serve": "vue-cli-service serve",
        "dev": "node node_modules/@vue/cli-service/bin/vue-cli-service serve",
    }
    
  • 命令上的灵活配置:多个命令串行执行,多个命令并行执行,定义命令传参

    多个命令串行执行使用&&:前一条命令执行完才去执行后一条命令,下面例子就是在前端程序执行终端执行后端启动文件。

    "script":{
        "server": "cd server && nodemon app.js",
    }
    

    多个命令并行执行使用&:同时执行多项命令,加上& wait的原因是保证每条命令都能执行完,因为每条命令执行时间都不一样

    "script":{
        "lint": "npm run lint:js & npm run lint:css & npm run lint:json & npm run lint:markdown & mocha tests/ & wait",
    }
    

    其实上面这种方法都不太好,有一个更好的方法就是使用npm-run-all,需要安装npm-run-all插件,在后面加上参数--parallel

    "script":{
       "dev": "npm-run-all --parallel mock serve",
       "mock": "cd mock && nodemon app.js",
       "serve": "vue-cli-service serve",
    }
    

    或者还有一种方法是安装插件concurrently进行使用。

    "script":{
        "dev:mock": "concurrently \"yarn:mock\" \"vue-cli-service serve\"",
        "mock": "cd mock && nodemon app.js",
    }
    

    刚才我们看到 --parallel就是一个命令传参,传参一种是带值,一种是不带值(本身就代表一种值或一种模式)

    --prod //生产模式
    --source-map // 是否允许调试时看到源代码
    
    // 带值
    --max_old_space_size=12288 // 允许增加节点的最大堆大小
    
  • 常见脚本命令:

    // 删除目录
    "clean": "rm -rf dist && mkdir dist",
    "example": "export DEBUG=table* && babel-node ./example/example.js",
    "test": "export DEBUG=table && babel-node ./test/test.js",
    "build": "./node_modules/.bin/babel src --out-dir dist",
    
    // 可以简写成下面的形式
    "build": "babel src --out-dir dist",
    "build:pkg": "babel bin --out-dir dist/bin --copy-files",
    "prepublishOnly": "npm run clean && npm run build",
    
    // 配置环境变量
    "start": "export DEBUG=table* && node scripts/start.js",
    "build": "node ./scripts/build.js",
    "test": "node scripts/test.js --env=jsdom",
    "pub:es": "npm run clean && export BABEL_ENV=production && babel src --out-dir es --copy-files",
    "pub:lib": "npm run clean && export BABEL_ENV=node && babel src --out-dir lib --copy-files",
    "pub:um": "npm run clean:dist && export RABEL_ENV=production && webpack --config ./config/webpack.config.pub.js",
    "pub:optimized": "rm es/setting.js es/i18n.js es/index.local.js && rm lib.settings.js lib/i18n.js lib/index.local.js",
    "prepublishOnly": "npm run pub:lib && npm run pub:es && npm run pub:umd && npm run pub:optimized"
    
    // 本地搭建一个 HTTP 服务
    "serve": "http-server -p 9090 dist/",
    
    // 打开浏览器
    "open:dev": "opener http://localhost:9090",
    
    // 实时刷新
     "livereload": "live-reload --port 9091 dist/",
    
    // 构建 HTML 文件
    "build:html": "jade index.jade > dist/index.html",
    
    // 只要 CSS 文件有变动,就重新执行构建
    "watch:css": "watch 'npm run build:css' assets/styles/",
    
    // 只要 HTML 文件有变动,就重新执行构建
    "watch:html": "watch 'npm run build:html' assets/html",
    
    // 部署到 Amazon S3
    "deploy:prod": "s3-cli sync ./dist/ s3://example-com/prod-site/",
    
    // 构建 favicon
    "build:favicon": "node scripts/favicon.js",
    

3 项目依赖

image.png

  • peerDependencies:消除重复下载依赖的问题。举个栗子:加油helloWorld项目中的package.json的dependencies中声明了packageA,有两个插件plugin1和plugin2也依赖packageA,如果在插件中使用dependencies来声明packageA,那么npm i安装完依赖后的包结构是这样的:

    ├── helloWorld
    │   └── node_modules
    │       ├── packageA
    │       ├── plugin1
    │       │   └── nodule_modules
    │       │       └── packageA
    │       └── plugin2
    │       │   └── nodule_modules
    │       │       └── packageA
    

    从上面我们可以看到packageA被安装了三次就冗余了。所以我们需要在两个插件的package中配置peerDependencies:

    //plugin1/package.json
    {
      "peerDependencies": {
        "packageA": "1.0.1"
      }
    }
    
    //plugin2/package.json
    {
      "peerDependencies": {
        "packageA": "1.0.1"
      }
    }
    
    //项目的package.json
    {
    "dependencies": {
    "packageA": "1.0.1"
    }
    

    配置好上面的依赖package后,最后npm i 生成的文件结构如下:

    ├── helloWorld
    │   └── node_modules
    │       ├── packageA
    │       ├── plugin1
    │       └── plugin2
    
  • bundleDependencies:用户安装了package-a后,将package-a所声明的依赖包汇总到package-a自身的node_modules下,便于用户管理,如果package-a中没有配置bundleDependencies,在安装了 package-a 的项目下 node_modules 就会长这样:

    // package-a中没有配置bundleDependencies
    ├── node_modules
        ├── package-a
        ├── react
        ├── core-js
        └── loadsh
    
    // package-a中配置了bundleDependencies
    ├── node_modules
        ├── package-a
        │   └── react
        │   └── core-js
        └── loadsh
    
    // package-a包
    {
      "name": "package-a",
      "dependencies": {
        "react": "^15.0.0",
        "core-js": "^2.0.0",
        "lodash": "^4.0.0"
      },
      "bundleDependencies": [
        "react",
        "core-js"
      ]
    }
    
  • optionalDependencies:可选依赖,如果有一些依赖包即使安装失败,项目仍然能够运行或者希望npm继续运行,就可以使用optionalDependencies。另外optionalDependencies会覆盖dependencies中的同名依赖包,所以不要在两个地方都写。

  • npm、yarn、pnpm三种安装依赖命令区别:

    npmyarnpnpm
    Install allnpm installyarnpnpm install
    Installnpm install [package]yarn add [package]pnpm add [package]
    npm install [package] -Dyarn add [package] -Dpnpm add -D [package]
    npm install [package] -gyarn global add [package]pnpm add -g [package]
    Uninstallnpm uninstall [package]yarn remove [package]pnpm remove [package]
    Updatenpm update [package]yarn upgrade [package]pnpm update [package]

4 依赖版本

image.png

image.png

  • 先行版本:被标注在修订版之后,先加上一个连接号再加上一连串以句点分隔的标识符来修饰。标识符必须由ASCII字母数字和连接号组成([0-9a-zA-Z-]),如1.0.0-alpha、1.0.0-alpha.1
  • 版本编译信息:标注在修订版或先行版本号之后,加上一个加号再加上一连串以句点分割的标识符来修饰。标识符必须由ASCII字母数字和连接号组成([0-9a-zA-Z-]),如1.0.0+190292、1.0.0-alpha+001、1.0.0-alpha+exp.001

5 参考资料

# package.json 配置完全解读 | # js-md5 | # 语义版本控制规范 | # 关于前端大管家package.json,你知道多少 |# vue项目中使用workspaces |# 一文搞懂peerDependencies |# package.json scripts 脚本使用指南 | 用 npm script 打造超溜的前端工作流 | # 当你运行npm run命令时,会发生什么?