工程化第一步这个package.json要真的搞明白才行

工程化最开始就是package.json开始的,很多人学了很多年也没搞清楚这个为什么这么神奇,其实有些字段是在特定场景才有效的,那每个属性的适用场景和作用是什么,又牵扯很多知识点,今天先解读一些常见的属性,关注我,后期在遇到特定场景也会再逐步的补充这些属性,只有真正清楚知道每个自动的属性和场景你才能真正使用它得心应手,也才能真正掌握并帮助你解决你的问题。

创建

一个package.json 你可以使用npm init 按指令创建,也可以通过npm init -y来快速创建,当然也可以手动来创建,那现在我们创建一个。 package.json

swift 复制代码
{
  "name": "package-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

这个是原装的属性,当然还有一些额外的场景才有的,接下来我们来逐步分析:

常用属性

这些属性没有没有太复杂,但还是要仔细看看每个都可以做什么,适用于什么场景,如何去使用和配置

name

package.json 中最重要字段之一就是名称name 字段,有人的说为什么我没有配置也没有影响我项目的正常运行呢?这个是因为你没有发布npm 包,这个name就是你发布到npm的包名,如果你不打算发布你的包到npm上这个是可以省略的。

当然这个名称也不是随便乱起的,它也有一定的规则,否则有警告的如下:

ini 复制代码
字符串与 "^(?:(?:@(?:[a-z0-9-*~][a-z0-9-*._~]*)?/[a-z0-9-._~])|[a-z0-9-~])[a-z0-9-._~]*$" 的模式不匹配。

name的规则如下:

  • 名称必须少于或等于 214 个字符。 这包括范围包的范围。
  • 范围包的名称可以以点或下划线开头。 没有范围是不允许的。
  • 新包的名称中不得包含大写字母。
  • 该名称最终成为 URL、命令行参数和文件夹名称的一部分。 因此,名称不能包含任何非 URL 安全字符

version

这个是版本,跟name 字段差不多,基本是一起的,因为它们共同构成一个假定完全唯一的标识符,如果你不发布包也是这个省略这个字段的,当然这个字段值也不是随意写的,它完全基于符合语义化版本(Semantic Versioning)来管理依赖的版本,所以版本号的更新需要符合语义化版本规范,简单总结:可以被 semver 包可解析的,当然你也可以使用这个semver 进行版本的操作,具体可以看我的另一篇版本号文章版本号

keywords

这个也是发布到npm 上才有用的,它的作用也是别人可以通过搜索来查找你的包,或是通过npm search xxx开查找你的包 如下,我有两个运行时的包处理react preact 的className的,可以看看查出了什么:

vbnet 复制代码
PS D:\lucky-file\package-demo> npm search 'react runtime clsx'        
NAME                      | DESCRIPTION          | AUTHOR          | DATE       | VERSION  | KEYWORDS
react-auto-clsx           |                      | =crazy-code     | 2023-10-10 | 1.1.0    | react vite webpack className clsx runtime
react-runtime-clsx        |                      | =zsh-yzy        | 2023-11-30 | 0.0.1    | react vite webpack className clsx runtime
react-auto-classnames     | JSX runtime for...     | =meowtec        | 2022-09-30 | 1.0.2    | react classnames clsx jsx-runtime
PS D:\lucky-file\package-demo> 

是不是几个包的信息,所以这个是搜索查找用的,如果你不发布这个也可以不写的

homepage

项目主页的 url。随便找个npm 包,可以看一下,该包的右侧有个地址就是这个字段进行配置的

bugs

这个就是要需要作者给个地址,比如你写的包有问题,别人怎么给你提问题,联系到你 通常我们设置的issue地址,那如何打开呢,可以通过如下方式:

xml 复制代码
npm bugs [<pkgname> [<pkgname> ...]]

license

指定一个许可证,以便人们知道他们如何被允许使用它,以及你对其施加的任何限制,常见的开源协议是 MIT 和 BSD,如果我们不确定我们应该如何写,可以看一下网上这个图说的比较清晰借用一下:

author

它表示项目的作者信息,它既可以是一个字符串,又可以是一个对象

perl 复制代码
"author":{
  "name": "lvz",
  "email": "xxxx@xx.com",
  "url": "https://xxxxx"
}

或者使用字符串的形式也可以

perl 复制代码
"author": "lvz <xxxxx@xx.com> (https://xxxxx)"

contributors

contributors 它表示的是贡献者,是一个数组,当然这个也有两种写法

perl 复制代码
"contributors": [
  "lvz1 <xxxxx@xx.com> (https://xxxxx)"
 ]

或者对象的写法

perl 复制代码
"contributors":[
    {
      "name": "lvz1",
      "email": "xxxx@xx.com",
      "url": "https://xxxxx"
    }
]

funding

如果你不开源,对资金没有要求或有其它渠道可以不使用,这个是在npm 6.13.0添加了funding命令,针对开源者,让维护 npm 的开发人员,为有意愿的捐赠者指明捐赠平台。在 package.json 文件中添加了一个funding字段,指向在线捐赠服务的 url,如 Patreon、Open 或者其他支付网站,并可以通过npm fund命令列出这些捐赠平台及其 url。

json 复制代码
"funding": [
    {
      "type": "patreon",
      "url": "http://XXXXXX"
    },
    "http://XXXXX",
    {
          "type": "open",
      "url": "https://XXXXXX"
    }
  ]

files

项目在进行 npm 发布时,可以通过 files 指定需要跟随一起发布的内容来控制 npm 包的大小,避免安装时间太长,比如我们包体有很多文件,但我们只想把dist放进去,其他src,test文件并不想放在包体中,可以使用这个字段

但无论设置如何,总是会包含某些文件:

  • package.json
  • README
  • CHANGES / CHANGELOG / HISTORY
  • LICENSE / LICENCE
  • NOTICE
  • "main"字段中的文件 相反,某些文件总是被忽略:
  • .git
  • CVS
  • .svn
  • .hg
  • .lock-wscript
  • .wafpickle-N
  • .*.swp
  • .DS_Store
  • ._*
  • npm-debug.log
  • .npmrc
  • node_modules
  • config.gypi
  • *.orig
  • package-lock.json

当然除了files 也有其它方式可以来忽略文件进入包体,我们先看一下我们什么也不处理的文件格式

  1. 添加.gitignore文件
css 复制代码
src/*
example/*

我们发布之后在看一下文件结构,只有index.js 也就是main字段指向的index.js还有package.json两个文件

  1. 添加.npmignore 文件
css 复制代码
src/*
example

同样我们发布之后看一下文件结构,如下图所示:

.npmignore在程序包的根目录下,它不会覆盖"files"字段,但在子目录中,它将覆盖。该.npmignore文件的工作方式与一样 .gitignore。如果有.gitignore文件但.npmignore缺少文件,.gitignore则将使用的内容

repository

简单点说就是配置仓库地址,这个配置后,可以在npm 包首页看到仓库地址入口,可以进行点击查看

json 复制代码
 "repository": {
    "type": "git",
    "url": "https://github.com/xxxxx",
    "directory": "xxxx/xxx"
  }

当然你也可以直接写字符串

json 复制代码
"repository":"https://github.com/xxxxx/xxxx"

config

配置对象可用于设置在升级后持续存在的包脚本中使用的配置参数

json 复制代码
  "config": {
    "type": "chrome"
  },

我们在index.js中进行打印一下

arduino 复制代码
console.log(process.env.npm_package_config_type)

此时如果你直接node index.js 你会发现打印的是个undefined,我们在将其放在scripts中:

json 复制代码
 "scripts": {
    "start": "node index.js"
  },

再执行就可以打印出值了,这样我们就可以在脚本中使用了这个环境变量了。

依赖配置项

这个是我们这次的重点之一,其实有些朋友经常分不清,这些到底怎么回事,我们先整体分析一下,我们安装一个包有两种,一种是当前项目package.json 已有的,一种是安装包中package.json的依赖,那针对这个我们做个实验再总结一下看看它们都是什么妖魔鬼怪。

我们在介绍version这个字段的时候,讲解了如何安装一个指定范围的npm包,如果有疑问可以进去查看哈,这里就不再赘述了

背景 有两个项目,一个是project-demo,一个是package-demo,包名是lucky-package-demo,project-demo会依赖lucky-package-demo这个包。 如下project-demo 中的package.json

json 复制代码
"dependencies": {
    "lucky-package-demo": "*"
}

devDependencies

简单理解就是这个包是开发环境用到了,生产环境并不需要它,比如我们的webpack,vite,或是eslint等

我们在package-demo 中安装 vite

js 复制代码
"devDependencies": {
    "vite": "4.3.0"
}

我们在project-demo更新lucky-package-demo后,可以看到project-demo的node_modules中并不存在vite这个包,也就是在依赖包中的devDependencies不会被安装

dependencies

表示项目运行所需要的依赖,比如我们的react,react-dmo,vue,组件库等

我们在package-demo中安装 react 和 react-demo

js 复制代码
"dependencies": {
    "react": "~16.5.0",
    "react-dom": ">16.3.0 <16.6.0"
 }

然后我们在project-demo更新lucky-package-demo后,可以看到project-demo的node_modules中发现react 和 react-demo 已经被安装了,虽然暂时我们并没有去使用这两个包,也就是说dependencies中的包将会被安装

peerDependencies

也叫同等依赖,它主要用于确保多个模块在同一个主模块的上下文中使用,并共享依赖的版本,简单来说就是project-demo项目中已经安装react了,package-demo中我没必要在dependencies依赖这个react了。从而避免项目中和依赖包中出现重复安装包所导致的包版本不相容、打包了多份不同版本的库等问题,我们举几个常遇到的问题来解释一下这个属性。

  1. 我有一个组件库,依赖了react,项目中我也要使用react并且已经安装好了,组件库和项目可以共享react
  2. 但有个问题,我项目更新了,react的版本和组件库react的版本不一致了,还是共享,如何避免冲突

安装方式:

我们可以配合一些属性使用,在依赖包中,因为我们不会在项目包中安装,可以放在devDependencies中,我们又想告诉项目包我们需要什么范围内的版本,就需要设置peerDependencies,如下:

package-demo中如下配置

json 复制代码
"devDependencies": {
    "react": "17.0.2",
    "vite": "4.3.0"
},
"peerDependencies": {
    "react":">16.8.0 <18.0.0"
}

如果我们在project-demo中安装的是 react:"17.0.1",此时是满足我们依赖包中peerDependencies中对react的版本范围要求的,安装很顺利,项目和依赖包会共享17.0.1的react的包,也不会存在任何警告。

diff 复制代码
dependencies:
- lucky-package-demo
+ lucky-package-demo 0.0.10
- react
+ react 17.0.1 (18.2.0 is available)

但如果此时我们将project-demo中react缓存16.8.0此时会有如下提示,因为此时16.8.0 是不满足peerDependencies中react版本范围要求的,此时你手动处理一下,要么安装正确的版本,要么通知作者更新一下peerDependencies

kotlin 复制代码
dependencies:
- lucky-package-demo
+ lucky-package-demo 0.0.10
- react
+ react 16.8.0 (18.2.0 is available)

 WARN  Issues with peer dependencies found
.
└─┬ lucky-package-demo 0.0.10
  └── ✕ unmet peer react@">16.8.0 <18.0.0": found 16.8.0

peerDependenciesMeta

如果 peerDependencies 中指定的包尚未安装,npm 将发出警告,如下配置将对等依赖标记为可选,如果用户没有安装对等依赖,npm不会发出警告

json 复制代码
 "peerDependenciesMeta":{
    "react":{
      "optional":true
    }
  }

在project-demo中安装lucky-package-demo,可以发现node_modules中并不存在react的包,控制台也没有任何的警告出现

css 复制代码
npm i -S lucky-package-demo

如果设置成optional:false中,在project-demo中安装lucky-package-demo后将会自动安装符合范围的react的包

bundleDependencies

npm的命令 -B, --save-bundle

语法:npm i -B [package-name]

这个属性使用的较少,通过在 bundleDependencies 数组中指定包名称,在发布包时,包名的数组会被打包进去,有的说我试过但发布包什么也没有呢?因为单单配置bundleDependencies这个是没有效果的,需要再在依赖包中安装devDependencies或dependencies中才能将其打包到node_modules中。举例说明:

在package-demo 中进行如下安装和配置,并在project-demo中npm i -S lucky-package-demo安装,你会发现project-demo中的node_modules中lucky-package-demo的node_modules中是bundleDependencies中配置的react-runtime-clsx包

json 复制代码
"dependencies": {
    "react-runtime-clsx": "0.0.1"
 },
"bundleDependencies": [
    "react-runtime-clsx"
]

当然你可以在package-demo跟目录执行npm pack,和安装lucky-package-demo一样,可以看看目录结构:

scss 复制代码
project-demo
----node_modules
-------lucky-package-demo
-----------node_modules
---------------react-runtime-clsx //这个是bundleDependencies配置的
---------------clsx  这个是react-runtime-clsx的依赖

optionalDependencies

通过npm 命令 -O, --save-optional

npm i -O [package-name]

optionalDependencies 用于定义可选依赖项,和 dependencies 非常类似,主要的差别在于:在 optionalDependencies 中的依赖包安装报错甚至找不到时不会影响到包管理器的安装行为

lua 复制代码
npm i -O react-runtime-clsx
// package-demo package.json 如下:
"optionalDependencies": {
    "react-runtime-clsx": "0.0.1"
}

optionalDependencies 中的条目将覆盖 dependencies 中的同名条目,因此通常最好只放在一个位置

overrides

如果需要对依赖项的依赖项进行特定更改,例如将依赖项的版本替换为已知的安全问题,将现有依赖项替换为分支,或者确保在任何地方都使用相同版本的包,则可以添加覆盖。

package-demo中package.json 配置

json 复制代码
"optionalDependencies": {
    "react-runtime-clsx": "0.0.1"
  }

在project-demo中配置,clsx@2.0.0是react-runtime-clsx依赖项,通过overrides添加"clsx":"1.0.0"将其进行版本替换

json 复制代码
"dependencies": {
    "lucky-package-demo": "0.0.15"
 },
"overrides":{
    "clsx":"1.0.0"
}

具体的大家可以看一下文档overrides文档)

项目中的依赖

项目中我们也会有依赖,有些是辅助开发的工具,有些是项目引用的代码包,因为项目我们是不会发布它作为npm包来使用的,它作为的是一个工程,所以在依赖上是有区分的,你是不是也有下面的困惑呢?

我随便装的并不影响到开发和构建,似乎这两者没有区别,安装哪里都一样呢?

感觉是对的,其实项目中这两者并没有明显的划分,只是我们通常是将开发中使用的安装到devDependencies,运行使用的放在dependencies,其实这两个都是安装了的,无论你放哪都可以使用,所以也有部分刚接触时很困惑,因为这个针对的是npm发布包有用的,在项目中还是按规范了养成习惯,清楚每个的含义,对我们做npm包的发布是有用的哈

后续更新

关注我,后续也会逐步的分析剩下跟构建和发布相关的属性,单独来说不方便理解,比如说main,简单来说就是入口文件,那针对浏览器,针对node或是其它场景该如何构建,如何处理呢?让我们逐渐来挖掘吧

相关推荐
豐儀麟阁贵几秒前
8.5在方法中抛出异常
java·开发语言·前端·算法
zengyuhan50330 分钟前
Windows BLE 开发指南(Rust windows-rs)
前端·rust
醉方休33 分钟前
Webpack loader 的执行机制
前端·webpack·rust
前端老宋Running42 分钟前
一次从“卡顿地狱”到“丝般顺滑”的 React 搜索优化实战
前端·react.js·掘金日报
隔壁的大叔42 分钟前
如何自己构建一个Markdown增量渲染器
前端·javascript
用户44455436542644 分钟前
Android的自定义View
前端
WILLF1 小时前
HTML iframe 标签
前端·javascript
枫,为落叶1 小时前
Axios使用教程(一)
前端
小章鱼学前端1 小时前
2025 年最新 Fabric.js 实战:一个完整可上线的图片选区标注组件(含全部源码).
前端·vue.js
ohyeah1 小时前
JavaScript 词法作用域、作用域链与闭包:从代码看机制
前端·javascript