首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

重新认识 package.json

前言

在每个项目的根目录下面,一般都会有一个package.json文件,其定义了运行项目所需要的各种依赖和项目的配置信息(如名称、版本、许可证等元数据)。

大多数人对package.json文件的了解,仅停留在:

项目名称、项目构建版本、许可证的定义;

依赖定义(包括dependencies字段,devDependencies字段);

使用scripts字段指定运行脚本命令的npm命令行缩写。

其实,package.json的作用远不止于此,我们可以通过新增配置项实现更强大的功能,下面将带你重新认识package.json。

由简入繁,丰富项目的 package.json

简单版的 package.json

当我们新建一个名称为my-test的项目时,使用yarn init -y或npm init -y命令后,在项目目录下会新增一个package.json文件,内容如下:

{

"name": "my-test", # 项目名称

"version": "1.0.0", # 项目版本(格式:大版本.次要版本.小版本)

"description": "", # 项目描述

"main": "index.js", # 入口文件

"scripts": { # 指定运行脚本命令的 npm 命令行缩写

"test": "echo \"Error: no test specified\" && exit 1"

},

"keywords": [], # 关键词

"author": "", # 作者

"license": "ISC" # 许可证

}

复制代码

可以看到,package.json文件的内容是一个JSON对象,对象的每一个成员就是当前项目的一项配置。

安装项目依赖(dependencies & devDependencies)

dependencies字段指定了项目运行所依赖的模块(生产环境使用),如antd、react、moment等插件库:

它们是我们生产环境所需要的依赖项,在把项目作为一个npm包的时候,用户安装npm包时只会安装dependencies里面的依赖。

devDependencies字段指定了项目开发所需要的模块(开发环境使用),如webpack、typescript、babel等:

在代码打包提交线上时,我们并不需要这些工具,所以我们将它放入devDependencies中。

如果一个模块不在package.json文件之中,我们可以单独安装这个模块,并使用相应的参数,将其写入dependencies字段/devDependencies字段中:

# 使用 npm

npm install --save # 写入 dependencies 属性

npm install --save-dev # 写入 devDependencies 属性

# 使用 yarn

yarn add # 写入 dependencies 属性

yarn add --dev # 写入 devDependencies 属性

复制代码

有了package.json文件,开发直接使用npm install/yarn install命令,就会在当前目录中自动安装所需要的模块,安装完成项目所需的运行和开发环境就配置好了。

简化终端命令(scripts)

scripts字段是package.json中的一种元数据功能,它接受一个对象,对象的属性为可以通过npm run运行的脚本,值为实际运行的命令(通常是终端命令),如:

"scripts": {

"start": "node index.js"

},

复制代码

将终端命令放入scripts字段,既可以记录它们又可以实现轻松重用。

定义项目入口(main)

main字段是package.json中的另一种元数据功能,它可以用来指定加载的入口文件。假如你的项目是一个npm包,当用户安装你的包后,require('my-module')返回的是main字段中所列出文件的module.exports属性。

当不指定main字段时,默认值是模块根目录下面的index.js文件。

指定项目 node 版本(engines)

有时候,新拉一个项目的时候,由于和其他开发使用的node版本不同,导致会出现很多奇奇怪怪的问题(如某些依赖安装报错、依赖安装完项目跑步起来等)。

为了实现项目开箱即用的伟大理想,这时候可以使用package.json的engines字段来指定项目 node 版本:

"engines": {

"node": ">= 8.16.0"

},

复制代码

该字段也可以指定适用的npm版本:

"engines": {

"npm": ">= 6.9.0"

},

复制代码

需要注意的是,engines属性仅起到一个说明的作用,当用户版本不符合指定值时也不影响依赖的安装。

自定义命令(bin)

用过vue-cli,create-react-app等脚手架的朋友们,不知道你们有没有好奇过,为什么安装这些脚手架后,就可以使用类似vue create/create-react-app之类的命令,其实这和package.json中的bin字段有关。

bin字段用来指定各个内部命令对应的可执行文件的位置。当package.json提供了bin字段后,即相当于做了一个命令名和本地文件名的映射。

当用户安装带有bin字段的包时,

如果是全局安装,npm将会使用符号链接把这些文件链接到/usr/local/node_modules/.bin/;

如果是本地安装,会链接到./node_modules/.bin/。

举个 ,如果要使用my-app-cli作为命令时,可以配置以下bin字段:

"bin": {

"my-app-cli": "./bin/cli.js"

}

复制代码

上面代码指定,my-app-cli命令对应的可执行文件为bin子目录下的cli.js,因此在安装了my-app-cli包的项目中,就可以很方便地利用npm执行脚本:

"scripts": {

start: 'node node_modules/.bin/my-app-cli'

}

复制代码

咦,怎么看起来和vue create/create-react-app之类的命令不太像?原因:

当需要node环境时就需要加上node前缀

如果加上node前缀,就需要指定my-app-cli的路径 ->node_modules/.bin,否则node my-app-cli会去查找当前路径下的my-app-cli.js,这样肯定是不对。

若要实现像vue create/create-react-app之类的命令一样简便的方式,则可以在上文提到的bin子目录下可执行文件cli.js中的第一行写入以下命令:

#!/usr/bin/env node

复制代码

这行命令的作用是告诉系统用node解析,这样命令就可以简写成my-app-cli了。

React 项目相关

设置应用根路径(homepage)

当我们使用create-react-app脚手架搭建的React项目,默认是使用内置的webpack配置,当package.json中不配置homepage属性时,build 打包之后的文件资源应用路径默认是/,如下图:

一般来说,我们打包的静态资源会部署在CDN上,为了让我们的应用知道去哪里加载资源,则需要我们设置一个根路径,这时可以通过package.json中的homepage字段设置应用的根路径。

当我们设置了homepage属性后:

{

"homepage": "https://xxxx.cdn/my-project",

}

复制代码

打包后的资源路径就会加上homepage的地址:

开发环境解决跨域问题(proxy)

在做前后端分离的项目的时候,调用接口时则会遇到跨域的问题,当在开发环境中时,可以通过配置package.json中的proxy来解决跨域问题,配置如下:

{

"proxy": "http://localhost:4000" // 配置你要请求的服务器地址

}

复制代码

注意,当create-react-app的版本高于 2.0 版本的时候在package.json中只能配置string类型,这意味着如果要使用package.json来解决跨域问题,则只能代理一个服务器地址。

如果要代理多个服务器地址时,则需要安装http-proxy-middleware,在src目录下新建setupProxy.js:

const proxy = require("http-proxy-middleware");

module.exports = function(app) {

app.use(

proxy("/base", {

target: "http://localhost:4000",

changeOrigin: true

})

);

app.use(

proxy("/fans", {

target: "http://localhost:5000",

changeOrigin: true

})

);

};

复制代码根据开发环境采用不同的全局变量值(自定义字段)

假设有这么一个组件,当组件被点击时,在开发环境时是跳转测试环境的sentry地址,在正式环境时则跳转正式环境的sentry地址。

首先,通过配置前面提到的scripts字段,实现环境变量(NODE_ENV)的设置:

"scripts": {

"start": "NODE_ENV=development node scripts/start.js",

"build": "NODE_ENV=production node scripts/build.js",

},

复制代码

项目启动起来后,在代码中我们可以通过process.env.NODE_ENV访问到NODE_ENV的值。

方案一

我们可以在组件中写类似以下的判断代码,根据不同环境给sentryUrl设置不同的值:

let sentryUrl;

if (process.env.NODE_ENV === 'development') {

sentryUrl = 'test-sentry.xxx.com';

} else {

sentryUrl = 'sentry.xxx.com';

}

复制代码

这么做好像没毛病,但是深入一想,如果有多个组件,要根据不同的环境使用不同的服务(多种服务)地址,如果按照上面的写法,项目中将存在许多重复的判断代码,且当服务地址发生变化时,包含这些服务地址的组件都需要相应的做改动,这样明显是不合理的。

方案二

解决方案:相关服务的地址配置在package.json中,同时修改项目的webpack配置。

注:修改项目的webpack配置需要 eject 项目的webpack配置(更多细节可阅读 :react + typescript 项目的定制化过程)。

在项目根目录下使用yarn eject成功 eject 出配置后,可以发现项目目录的变化如下:

如果需要定制化项目,一般就是在config目录下对默认的webpack配置进行修改,在这里我们需要关注config/path.js和config/env.js两个文件:

env.js的主要目的在于读取env配置文件并将env的配置信息给到全局变量process.env;

path.js的主要目的在于为项目提供各种路径,包括构建路径、public路径等。

由于本文的重点不是学习webpack配置,这里仅介绍如何实现【根据开发环境采用不同的全局变量值】的功能。

首先,需要在package.json中配置以下内容:

"scripts": {

"start": "NODE_ENV=development node scripts/start.js",

"build": "NODE_ENV=production node scripts/build.js",

},

"sentryPath": {

"dev": "https://test-sentry.xxx.com",

"prod": "https://sentry.xxx.com"

}

复制代码

然后,修改path.js文件,内容如下:

// 重写 getPublicUrl 方法

const getPublicUrl = (appPackageJson, pathName) => {

let path;

switch (process.env.DEPLOY_ENV) {

case 'development':

path = require(appPackageJson)[pathName].dev;

break;

case 'production':

path = require(appPackageJson)[pathName].prod;

break;

default:

path = envPublicUrl || require(appPackageJson).homepage;

}

return path;

}

// 新增 getSentryPath 方法

const getSentryPath = (appPackageJson) => {

return getPublicUrl(appPackageJson, 'sentryPath');

}

// config after eject: we're in ./config/

module.exports = {

...,

sentryUrl: getSentryPath(resolveApp('package.json')), // 新增

};

复制代码

最后,修改env.js文件,内容如下:

// 修改 getClientEnvironment 方法

function getClientEnvironment(publicUrl) {

const raw = Object.keys(process.env)

.filter(key => REACT_APP.test(key))

.reduce(

(env, key) => {

...

},

{

NODE_ENV: process.env.NODE_ENV || 'development',

PUBLIC_URL: publicUrl,

SENTRY_URL: paths.sentryUrl // 新增

}

);

const stringified = {

...

};

return { raw, stringified };

}

复制代码

通过上面的配置,我们就可以在组件中通过process.env.SENTRY_URL获取到sentry服务的地址了,虽然看起来比方案一繁琐,但是这种收益是长期的,如要新增一个sonarqube服务,同理实现即可,通过使用package.json也可以清楚的看到当前服务在不同环境下使用的地址。

总结

本文介绍了package.json的多种常见的配置字段及作用,并通过例子加深大家对package.json这些字段的理解。

除了一些常用字段,还介绍了在React项目中package.json文件能实现的一些功能进行介绍。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200516A0NFS800?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券