【前端工程化】package.json

3,740 阅读8分钟

前言

大家好,我是 simple ,想做一个用科技方法解决生活遇到的问题的人。

package.json作为项目的清单文件,记录了项目所依赖的各种包以及自定义的脚本、版本等信息,是现代前端项目中最为重要的文件之一。对一个项目快速上手,从package.json出发绝对是一个不错的选择。并且理解package.json各字段的作用和配置方式,对于我们提高项目构建、依赖管理及发布的技能至关重要。

生成package.json,使用npm init,然后依次输入选项或者一直按回车,就可以自动化生成一个package.json。如果觉得麻烦,运行npm init -y就可以默认生成一个package.json。

{
  "name": "project", // 项目名称
  "version": "1.0.0", // 项目版本
  "description": "", // 项目描述
  "main": "index.js", // 项目入口文件
  "scripts": { // 指定运行脚本命令的 npm 命令行缩写
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",// 作者
  "license": "ISC" // 许可证
}

下面我将从项目的角度来划分package.json的不同字段,如有不对的地方请指出。

项目概况

nameversion

如果你想要在npm发布一个包,"name"和"version"是必填的,并且将会是作为你的包的唯一id。

descriptionkeywords

"description"是一个项目描述,接受一个字符串。
"keywords"是一个数组,数组里面可以放多个项目关键词。
两者都是为了方便让npm搜索,也方便开发者更容易了解该项目的意义。

homepage, repositorybugs

"homepage" 是这个项目的首页路径,方便开发者访问这个包的主页获取更多信息。

"homepage": "https://github.com/owner/project#readme"

"repository" 是这个项目的代码仓库,方便贡献者找到代码仓库。

"repository": "git+https://github.com/username/repo-name.git"
"repository": {
  "type": "git",
  "url": "git+https://github.com/username/repo-name.git"
}

"bugs" 是为了方便开发者对项目的bug提出建议或意见。

"bugs": { "url" : "https://github.com/owner/project/issues", "email" : "project@hostname.com" }

unpkgjsdelivr

CDN方式下,引入当前npm包的链接。

"unpkg": "lib/index.iife.js",
"jsdelivr": "lib/index.iife.js",

项目环境

engines

package.json中的engines字段用于指定npm包运行所需的环境。它包含了node, npm, yarn等环境的版本要求。engines的格式是:

json
"engines": {
  "node": ">=10.13.0",
  "npm": ">=6.4.1",
  "yarn": "^1.9.4"
}

engines字段主要是确保npm包在安装后可以正确运行所需的环境。如果环境版本不匹配,npm install时会显示警告信息。让其他开发者在使用该包之前可以确保拥有匹配的环境,避免因版本不兼容导致的错误或警告。

browserslist

browserslist字段是用来指定项目的目标浏览器范围的。它曾经是package.json的标准字段之一,但是从npm v5.5.0开始,browserslist被移除了。

以 Babel 为例,它是怎么判断项目是否需要做降级处理的呢,答案就是通过 browserslist 查询出需要支持的浏览器列表,然后根据这个列表来做判断。

browserslist 配置方式
这是因为browserslist现在可以通过以下几种方式指定:

  1. package.json中使用"browserslist" key:
"browserslist": ["> 1%", "last 2 versions"] // 查询全球市场占有率大于 1% 的浏览器最后两个版本
  1. 项目根目录下的.browserslistrc文件:
> 1%
last 2 versions

os

指定操作系统

{ "os": [ "darwin", "linux" ] } // 适用
{ "os": [ "!linux" ] } // 禁止

cpu

{ "cpu" : [ "x64", "ia32" ] } # 适用
{ "cpu" : [ "!arm", "!mips" ] } # 禁止

项目分析

main

"main" 定义了项目的入口文件,默认值是根目录的"index.js"。

module

性质等同于main字段。module用于ES6规范的模块,只要支持ES6,会优先使用module入口。
这样,代码也可以启用tree shaking机制。

"module": "es/index.mjs",

bin

很多包都有一个可执行命令,并且希望被安装到环境变量中。npm 安装依赖的时候, npm会通过node_modules/.bin 将 bin 里面的文件暴露出来,当你通过 npx myapp的时候可以直接运行该命令。

{
    "bin": {
        "myapp": "./cli.js" 
    } 
}

dependenciesdevDependencies

两者的作用和区别

两者都是为了记录需要的依赖以及版本。

devDependencies用于开发环境下依赖的模块,生产环境不需要被打入包内。运行命令行npm i xxx --save-dev,会被安装到 "devDependencies"

dependencies依赖的包不仅开发环境能使用,生产环境也能使用。运行命令行npm i xxx,会被安装到 "devDependencies"

总结: 通常情况下, devDependencies一般是放置一些代码规范工具、打包工具、编译器等,而dependencies一般是放置组件库、框架等与页面相关的插件。其实依赖不管放在哪里对项目影响都不大,但是保持良好的习惯就会让人觉得项目很整洁。

指定不同版本

{ 
    "dependencies": {
        "foo": "1.0.0 - 2.9999.9999", // 1.0.1-2.9999.9999版本(和下面的表达差不多意思)
        "bar": ">=1.0.2 <2.1.2", // 大于或等于1.0.2版本小于2.1.2版本
        "baz": ">1.0.2 <=2.3.4", // 大于1.0.2版本小于等于2.3.4版本
        "boo": "2.0.1", // 指定版本
        "qux": "<1.0.0 || >=2.3.1 <2.4.5", // 小于1.0.0或者大于2.3.1且小于2.4.5
        "til": "~1.2.3", // ~ 匹配1.2.x的所有小版本,但不会匹配到1.3.x
        "elf": "^1.2.3", // 匹配1.x.x的所有大版本,但不会匹配2.x.x
        "two": "2.x", // 匹配所有2.x开头版本
        "thr": "3.3.x", // 匹配3.3.x版本
        "lat": "latest", // 最新版本
        "dyl": "file:../dyl" 
    } 
}

指定url的包

开发过程中,有可能你需要直接使用本地的包或者git仓库的包进行调试,又不希望频繁上传到npm包管理器这么麻烦。

<protocol>://[<user>[:<password>]@]<hostname>[:<port>][:][/]<path>[#<commit-ish> | #semver:<semver>]

可以根据version或者commit-ish来进行精确下载。

git示例

{ 
    "dependencies": {
        "bar": "git+ssh://git@github.com:npm/cli.git#v1.0.27",
        "foo": "git+ssh://git@github.com:npm/cli#semver:^5.0",
        "baz": "git+https://isaacs@github.com/npm/cli.git",
        "boo": "git://github.com/npm/cli.git#v1.0.27"
    } 
}

本地文件示例

{ 
    "dependencies": {
        "bar": "file:../foo/bar" 
    } 
}

项目运行

scripts

scripts字段的作用

scripts 属性是一个字典,对象的属性名为可以通过 npm run 运行命令,属性值可执行命令。{"xxx": "vite"},在这里使用终端执行npm run xxx

如果你没有全局安装vite,直接去执行vite命令行,终端是会报错的。但这里通过npm run xxx执行命令并没有报错,是因为我们在安装依赖的时候,是通过npm i xxx 来执行的,例如 npm i vite,npm 在 安装这个依赖的时候,就会node_modules/.bin/ 目录中创建好vite 为名的几个可执行文件了(上面的bin也提到了)。

image.png

.bin 目录,这个目录不是任何一个 npm 包。而是一个脚本。所以我们执行scripts里面的命令时,虽然在全局找不到对应的可运行命令,但也可以通过.bin文件夹下找到对应的脚本。

自定义脚本的写法

可以通过自定义脚本直接运行一些命令行提高自己的开发效率,或者运行脚本来打包配置项目。

{
  "scripts": {
      "clean": "rm -rf dist",
      "update": "sh ./update.sh"
  }
}

项目规范

huskylint-staged

husky的作用

husky是一个可以给git hooks添加执行自定义脚本的工具。它可以在git事件触发时(commit, push, rebase等)自动执行我们所配置的脚本,从而实现lint校验,测试运行,构建打包等工作流程。

配合git事件触发脚本

husky的安装和配置也很简单,主要有以下几个步骤:

  1. 安装husky包:npm install husky --save-dev
  2. 在package.json中添加git hooks脚本,如:"pre-commit": "npm run lint"
  3. 添加husky配置到package.json,如:"husky": { "hooks": { "pre-commit": "npm run lint"} }
  4. git commit触发pre-commit钩子,从而自动执行npm run lint进行lint校验。

所以,husky是一个比较方便的git hooks工具,可以让我们的开发工作流变得更加自动化和标准化。理解husky的作用和配置方式,可以大大提高我们的前端工程化实践水平。

lint-staged的作用

lint-staged是一个在git暂存文件(staged files)上运行代码格式规范的工具。它的主要作用是:

  1. 只校验暂存文件的改动(diff),而不是整个项目。这样可以提高lint效率,并且避免提交未修改的文件产生的lint错误。
  2. 支持在git pre-commit钩子中使用,结合husky可以实现commit前自动lint校验。
  3. 支持多种文件类型(JavaScript, CSS, Markdown 等)和多个linters。如ESLint, Stylelint, Prettier等。4. 提供了简单的配置方式,可以轻松地在package.json中完成配置。

lint-staged配合husky

  1. 安装lint-staged和相关的代码规范工具:npm install lint-staged eslint prettier -D
  2. 在package.json中添加lint-staged配置:"lint-staged": {"*.js": "eslint --fix",}
  3. 安装husky并配置pre-commit hook:"husky": { "hooks": { "pre-commit": "npm run lint"} }
  4. git add 添加文件并commit,pre-commit钩子就会自动执行lint-staged lint校验。

所以,lint-staged是一个非常实用的工具,让我们的代码质量管理可以更加自动化。理解lint-staged的作用和配置方式,可以很好地提高我们的前端工程化水平。

types

项目如果是用TypeScript写的,则需要types字段,对外暴露相关的类型定义。

"types": "lib/index.d.ts",

项目打包配置

private

"private" 配置一个布尔值,当为true的时候,npm将会拒绝发布它。这个是防止私人储存库意外发布的情况。

sideEffects

"sideEffects" 用于告知打包工具(webpack),当前项目无副作用,可以使用tree shaking优化。

副作用 是指,该函数的调用过程中,是否对主函数(调用者)产生了附加影响,例如修改了函数外的变量或参数,我们就认为该函数是 有副作用 的函数。

"sideEffects": [
    "a.js",
    "b.js"
],

exports

"exports" 允许在使用模块请求时声明应该使用哪个模块。当指定exports字段时,只有这些模块请求可用,请求其他模块将报错。

{
  "exports": {
    ".": "./main.js",
    "./sub/path": "./secondary.js",
    "./prefix/": "./directory/",
    "./prefix/deep/": "./other-directory/",
    "./other-prefix/*": "./yet-another/*/*.js"
  }
}
// 示例:
import _ from 'my-project' // 将会指向./main.js
import path from 'my-project/sub/path' // 将会指向./secondary.js

files

"files" 用于保留项目在npm发布时的文件或文件夹,在node_modules/下将会只留下files声明的文件或文件夹。你也可以提供一个.npmignore文件,让npm发布的时候规定哪些文件被忽略,就像.gitignore一样。

"files": [
    "lib",
    "bin",
    "scripts"
],

package.jsonREADMELICENSE / LICENCE 无法被忽略。