npm es una herramienta de administración de paquetes ampliamente utilizada por los desarrolladores front-end. En el proyecto, package.json se usa para administrar la configuración de los paquetes npm de los que depende el proyecto. package.json es un archivo json Además de describir las dependencias de los paquetes del proyecto, también nos permite usar "reglas de control de versiones semánticas" para indicar las versiones de los paquetes dependientes de su proyecto, lo que permite que sus compilaciones se compartan mejor con otros desarrolladores para facilitar su uso. reutilizar. Este artículo comienza principalmente con la práctica reciente, combinada con las últimas versiones de npm y nodos, para presentar algunas configuraciones comunes en package.json y cómo escribir un package.json estandarizado.
En un proyecto nodejs, package.json es un archivo de configuración que administra sus dependencias. Generalmente cuando inicializamos un proyecto nodejs, pasaremos:
npm init
y luego se generará 3. su directorio/archivos, node_modules, package.json y package.lock.json. El contenido de package.json es:
{ "name": "El nombre de tu proyecto", "versión": "1.0.0", "description": "Descripción de tu proyecto", "principal": "aplicación.js", "guiones": { "test": "echo "Error: no se ha especificado ninguna prueba" && salida 1", }, "autor": "Nombre del autor", "licencia": "ISC", "dependencias": { "dependencia1": "^1.4.0", "dependencia2": "^1.5.2" } }
Como se puede ver en lo anterior, package.json contiene los metadatos del proyecto en sí, así como la información de subdependencia del proyecto (como dependencias, etc.).
Descubrimos que durante el inicio de npm, no solo se generó el archivo package.json, sino que también se generó el archivo package-lock.json. Entonces, ¿por qué todavía necesitamos generar el archivo package-lock.json al borrar package.json? Básicamente, el archivo package-lock.json sirve para bloquear la versión. El paquete sub-npm especificado en package.json es como: reaccionar: "^16.0.0" en la instalación real, siempre que la versión sea superior a. reacciona, package.json cumple con los requisitos. Esto significa que, según el mismo archivo package.json, no se puede garantizar que las versiones de subdependencia instaladas dos veces sean coherentes.
El archivo de bloqueo del paquete se muestra a continuación y la subdependencia dependency1 especifica su versión en detalle. Desempeña el papel de la versión de bloqueo.
{ "name": "El nombre de tu proyecto", "versión": "1.0.0", "versión de archivo de bloqueo": 1, "requiere": verdadero, "dependencias": { "dependencia1": { "versión": "1.4.0", "resuelto": "https://registry.npmjs.org/dependency1/-/dependency1-1.4.0.tgz", "integridad": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" }, "dependencia2": { "versión": "1.5.2", "resuelto": "https://registry.npmjs.org/dependency2/-/dependency2-1.5.2.tgz", "integridad": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==" } } }
Este capítulo hablará sobre los atributos de configuración de uso común en package.json. Los atributos como el nombre, la versión, etc. son demasiado simples y no se presentarán uno por uno. Este capítulo presenta principalmente las propiedades de script, bin y espacios de trabajo.
usa la etiqueta script en npm para definir un script. Siempre que se especifique npm run, se creará automáticamente un script de shell. Lo que hay que tener en cuenta aquí es que el nuevo shell creado por npm run almacenará node_modules/.bin. directorio local. Agregue subdirectorios a la variable PATH.
Esto significa que todos los scripts en el subdirectorio node_modules/.bin del directorio actual se pueden llamar directamente con el nombre del script sin agregar una ruta. Por ejemplo, si las dependencias del proyecto actual incluyen esbuild, simplemente escriba esbuild xxx directamente.
{ //... "guiones": { "build": "esbuild index.js", } }
{ //... "guiones": { "build": "./node_modules/.bin/esbuild index.js" } }
Las dos formas de escritura anteriores son equivalentes.
El atributo bin se utiliza para cargar archivos ejecutables en el entorno global. Una vez que se especifica el paquete npm con el campo bin, se cargará en el entorno global y el archivo se podrá ejecutar a través de un alias.
Por ejemplo, el paquete npm de @bytepack/cli:
"bin": { "bytepack": "./bin/index.js" },
una vez que @bytepack/cli esté instalado globalmente, puede ejecutar directamente el comando correspondiente a través de bytepack, como
bytepack -v //Mostrar 1.11.0
Si no está instalado globalmente, se conectará automáticamente al directorio node_module/.bin del proyecto. De acuerdo con lo que se dijo en la etiqueta de secuencia de comandos presentada anteriormente, se puede usar directamente con alias.
Cuando el proyecto es demasiado grande, monorepo se ha vuelto cada vez más popular recientemente. Cuando se trata de monorepo, no nos fijamos en los espacios de trabajo. Al principio, usábamos espacios de trabajo de hilo. Ahora, npm admite espacios de trabajo para resolver el problema de cómo administrar múltiples subpaquetes en un paquete raíz de nivel superior. en el sistema de archivos local En el directorio de declaración de espacios de trabajo El paquete estará vinculado de forma suave a los node_modules del paquete raíz de nivel superior.
Ilustremos directamente con un ejemplo del sitio web oficial:
{ "nombre": "mi-proyecto", "espacios de trabajo": [ "paquetes/a" ] }
En un paquete npm llamado mi-proyecto, hay un directorio configurado por espacios de trabajo.
. +-- paquete.json +-- índice.js `-- paquetes +-- un | `-- paquete.json
y el paquete raíz de nivel superior llamado mi-proyecto tiene un paquete/un subpaquete. En este momento, si instalamos npm, entonces el paquete npm instalado en node_modules en el paquete raíz apunta al paquete local/a
. +-- módulos_nodo | `-- paquetes/a -> ../paquetes/a +-- paquete-lock.json +-- paquete.json `-- paquetes +-- un | `-- paquete.json
arriba--
paquetes/a -> ../packages/a
se refiere al enlace suave desde a en node_modules al paquete npm local
Entornos comunes con atributos relacionados con el entorno package.json, básicamente Lo anterior se divide en dos categorías: navegador y entorno de nodo. A continuación, echemos un vistazo a las propiedades de configuración relacionadas con el entorno en package.json. La definición de entorno se puede entender simplemente de la siguiente manera:
js incluye el módulo commonjs, CMD, UMD, AMD y ES. Al principio, solo el campo commonjs era compatible con el nodo 13.2.0, el nodo admite oficialmente la especificación del módulo ES. El campo se puede utilizar en package.json para declarar la especificación modular que sigue el paquete npm.
//paquete.json { nombre: "algún paquete", tipo: "módulo"||"commonjs" }
Cabe señalar que
cuando no se especifica el tipo, el valor predeterminado de tipo es commonjs. Sin embargo, se recomienda que todos los paquetes npm especifiquen el tipo.
Cuando el campo tipo especifica el valor como módulo, se utiliza la especificación ESModule
. Cuando se especifica el campo tipo, se utilizarán todos los .js del directorio. Los archivos que terminan con el sufijo siguen la especificación de modularización especificada por tipo.
Además del tipo, que puede especificar la especificación de modularización, la especificación de modularización seguida por el archivo se especifica mediante. el sufijo del archivo Los archivos que terminan en .mjs son las especificaciones de ESModule utilizadas. La terminación .cjs sigue la especificación commonjs
Además del tipo, package.json también tiene tres campos: principal, módulo y navegador. para definir el archivo de entrada del paquete npm.
un vistazo a los escenarios de uso de estos tres campos y las prioridades cuando estos tres campos existen al mismo tiempo. Supongamos que hay un paquete npm llamado demo1,
----- dist |-- index.browser.js |-- index.browser.mjs |-- index.js |--
el paquete.json de index.mjs especifica los tres campos principal, módulo y navegador al mismo tiempo,
"main": "dist/index.js", // main "módulo": "dist/index.mjs", // módulo // el navegador se puede definir como un objeto de mapeo correspondiente al campo principal/módulo, o se puede definir directamente como la cadena "navegador": { "./dist/index.js": "./dist/index.browser.js", // navegador+cjs "./dist/index.mjs": "./dist/index.browser.mjs" // navegador+mjs }, // "browser": "./dist/index.browser.js" // El navegador
se crea y se usa de forma predeterminada. Por ejemplo, si hacemos referencia a este paquete npm en el proyecto:
importar demostración desde 'demo'
Después de compilar lo anterior. codifique a través de la herramienta de compilación, el módulo La secuencia de carga es:
navegador + mjs > módulo > navegador + cjs > principal
Esta secuencia de carga es la secuencia de carga predeterminada de la mayoría de las herramientas de compilación, como webapck, esbuild, etc. Este orden de carga se puede modificar mediante la configuración correspondiente, pero en la mayoría de los escenarios, seguiremos el orden de carga predeterminado.
Si el campo de exportaciones está definido en package.json, entonces el contenido definido en este campo es la exportación real y completa del paquete npm, y la prioridad será mayor que los campos principal y de archivo.
Por ejemplo:
{ "nombre": "paquete", "exportaciones": { ".": "./main.mjs", "./foo": "./foo.js" } }
importar {algo} de "pkg"; // de "pkg/main.mjs"
const {algo} = require("pkg/foo"); // require("pkg/foo.js")
del ejemplo anterior Parece que las exportaciones pueden definir exportaciones con diferentes caminos. Si existen exportaciones, el directorio de archivos previamente válido no será válido en todas partes, como require('pkg/package.json'), porque no está especificado en las exportaciones, y se informará un error.
Otra característica importante de las exportaciones es la referencia condicional. Por ejemplo, podemos especificar paquetes npm para hacer referencia a diferentes archivos de entrada según diferentes métodos de referencia o tipos modulares.
// paquete.json { "nombre": "paquete", "principal": "./main-require.cjs", "exportaciones": { "importar": "./main-module.js", "requerir": "./main-require.cjs" }, "tipo": "módulo" }
En el ejemplo anterior, si nos
referimos a "./main-require.cjs"
medianteconst p = require('pkg').
Si pasa:
import p from 'pkg',
la referencia es "./main-module.js"
Lo último que debe tener en cuenta es: si el atributo exports existe, el atributo exports no solo tendrá una prioridad más alta que main, pero también más alto que los campos del módulo y del navegador.
Las propiedades de configuración relacionadas con las dependencias en package.json incluyen dependencias, devDependencies, peerDependencies, peerDependenciesMeta, etc.
Las dependencias son dependencias del proyecto y las devDependencies son módulos necesarios para el desarrollo, por lo que podemos instalarlos según sea necesario durante el proceso de desarrollo para mejorar nuestra eficiencia de desarrollo. Lo que hay que tener en cuenta aquí es intentar utilizarlos de la forma más estándar posible en sus propios proyectos. Por ejemplo, webpack, babel, etc. son dependencias de desarrollo, no dependencias del proyecto en sí.
dependencias Además de las dependencias y devDependencies, este artículo se centra en peerDependencies y peerDependenciesMeta.
Las peerDependencies son dependencias en package.json, que pueden resolver el problema de que la biblioteca principal se descargue varias veces y unifique la versión de la biblioteca principal.
//paquete/paquete -----node_modules |-- npm-a -> Depende de reaccionar, reaccionar-dom |-- npm-b -> Depende de reaccionar, reaccionar-dom |-- index.js
Por ejemplo, en el ejemplo anterior, si los paquetes sub-npm a y b provienen de reaccionar y reaccionar-dom, entonces si declaramos PeerDependicies en el package.json de los paquetes sub-npm a y b, las Dependencias correspondientes no se reinstalarán.
Hay dos puntos a tener en cuenta:
"peerDependencies": {. "reaccionar": "^16.8.3 || ^17 || ^18" }, "peerDependenciesMeta": { "reaccionar-dom": { "opcional": verdadero }, "reaccionar-nativo": { "opcional": verdadero } }
"react-dom" y "react-native" se especifican aquí en peerDependenciesMeta y son opcionales, por lo que si "react-dom" y "react-native" no están instalados en el proyecto, no se informará ningún error.
Vale la pena señalar que, de hecho, hemos eliminado la restricción a través de peerDependenciesMeta, pero a menudo hay escenarios en los que es A o B. Por ejemplo, en el ejemplo anterior, lo que necesitamos es "react-dom" y "react-native". y necesitamos instalar uno, pero de hecho, no podemos lograr este mensaje a través de la declaración anterior.
También hay muchos atributos de tres partes en package.json, como los tipos utilizados en tsc, los efectos secundarios utilizados en las herramientas de compilación, los husky utilizados en git y la configuración de eslintIgnore utilizada en eslint. Las extensiones son para herramientas de desarrollo específicas y no daré ejemplos aquí.