npm é uma ferramenta de gerenciamento de pacotes amplamente utilizada por desenvolvedores front-end. No projeto, package.json é usado para gerenciar a configuração dos pacotes npm dos quais o projeto depende. package.json é um arquivo json, além de descrever as dependências dos pacotes do projeto, também nos permite usar "regras de versionamento semântico" para indicar as versões dos pacotes dependentes do seu projeto, permitindo que suas compilações sejam melhor compartilhadas com outros desenvolvedores para facilitar. reutilizar. Este artigo parte principalmente da prática recente, combinada com as versões mais recentes do npm e do nó, para apresentar algumas configurações comuns em package.json e como escrever um package.json padronizado.
Em um projeto nodejs, package.json é um arquivo de configuração que gerencia suas dependências. Geralmente quando inicializamos um projeto nodejs, passaremos:
npm init
e então 3 será gerado em. seu diretório/arquivos, node_modules, package.json e package.lock.json. O conteúdo de package.json é:
{ "name": "Nome do seu projeto", "versão": "1.0.0", "description": "Descrição do seu projeto", "principal": "app.js", "roteiros": { "test": "echo "Erro: nenhum teste especificado" && exit 1", }, "autor": "Nome do autor", "licença": "ISC", "dependências": { "dependência1": "^1.4.0", "dependência2": "^1.5.2" } }
Como pode ser visto acima, package.json contém os metadados do próprio projeto, bem como as informações de subdependência do projeto (como dependências, etc.).
Descobrimos que durante o npm init, não apenas o arquivo package.json foi gerado, mas também o arquivo package-lock.json foi gerado. Então, por que ainda precisamos gerar o arquivo package-lock.json ao limpar package.json? Essencialmente, o arquivo package-lock.json serve para bloquear a versão. O pacote sub-npm especificado em package.json é como: react: "^16.0.0". reagir, package.json atende aos requisitos. Isso significa que, de acordo com o mesmo arquivo package.json, não é possível garantir que as versões de subdependência instaladas duas vezes sejam consistentes.
O arquivo package-lock é mostrado abaixo, e a subdependência dependency1 especifica sua versão em detalhes. Desempenha o papel da versão de bloqueio.
{ "name": "Nome do seu projeto", "versão": "1.0.0", "lockfileVersion": 1, "requer": verdadeiro, "dependências": { "dependência1": { "versão": "1.4.0", "resolvido": "https://registry.npmjs.org/dependency1/-/dependency1-1.4.0.tgz", "integridade": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" }, "dependência2": { "versão": "1.5.2", "resolvido": "https://registry.npmjs.org/dependency2/-/dependency2-1.5.2.tgz", "integridade": "sha512-WOn21V8AhyE1QqVfPIVxe3tupJacq1xGkPTB4iagT6o+P2cAgEOOwIxMftr4+ZCTI6d551ij9j61DFr0nsP2uQ==" } } }
Este capítulo falará sobre os atributos de configuração comumente usados em package.json. Atributos como nome, versão e assim por diante são muito simples e não serão introduzidos um por um. Este capítulo apresenta principalmente as propriedades de script, bin e espaços de trabalho.
usa a tag script no npm para definir um script Sempre que npm run for especificado, um script de shell será criado automaticamente. O que precisa ser observado aqui é que o novo shell criado por npm run armazenará node_modules/.bin no. diretório local. Adicione subdiretórios à variável PATH.
Isso significa que todos os scripts no subdiretório node_modules/.bin do diretório atual podem ser chamados diretamente com o nome do script sem adicionar um caminho. Por exemplo, se as dependências do projeto atual incluírem esbuild, basta escrever esbuild xxx diretamente.
{ // ... "roteiros": { "construir": "esbuild index.js", } }
{ // ... "roteiros": { "construir": "./node_modules/.bin/esbuild index.js" } }
As duas formas de escrita acima são equivalentes.
O atributo bin é usado para carregar arquivos executáveis no ambiente global. Uma vez especificado o pacote npm com o campo bin, ele será carregado no ambiente global e o arquivo poderá ser executado através de um alias.
Por exemplo, o pacote npm de @bytepack/cli:
"bin": { "bytepack": "./bin/index.js" },
depois que @bytepack/cli for instalado globalmente, você pode executar diretamente o comando correspondente por meio do bytepack, como
bytepack -v //Mostrar 1.11.0.
Se não estiver instalado globalmente, ele será automaticamente conectado ao diretório node_module/.bin do projeto. Consistente com o que foi dito na tag script introduzida anteriormente, ela pode ser usada diretamente com aliases.
Quando o projeto é muito grande, o monorepo tornou-se cada vez mais popular recentemente. Quando se trata de monorepo, não olhamos para os espaços de trabalho. No início, usaríamos os espaços de trabalho do Yarn. Agora, o npm suporta oficialmente os espaços de trabalho e resolve o problema de como gerenciar vários subpacotes em um pacote raiz de nível superior. no sistema de arquivos local No diretório de declaração dos espaços de trabalho O pacote será vinculado ao node_modules do pacote raiz de nível superior.
Vamos ilustrar diretamente com um exemplo do site oficial:
{ "nome": "meu-projeto", "espaços de trabalho": [ "pacotes/a" ] }
Em um pacote npm chamado my-project, existe um diretório configurado pelos espaços de trabalho.
. +-- pacote.json +-- index.js `-- pacotes +-- um | `-- package.json
e o pacote raiz de nível superior chamado my-project possui um pacote/subpacote. Neste momento, se instalarmos o npm, o pacote npm a instalado em node_modules no pacote raiz aponta para o pacote local/a
. +-- node_modules | `-- pacotes/a -> ../pacotes/a +-- package-lock.json +-- pacote.json `-- pacotes +-- um | `-- package.json
acima--
packages/a -> ../packages/a
refere-se ao soft link de a em node_modules para o pacote npm local
Ambientes comuns com atributos relacionados ao ambiente package.json, basicamente O acima está dividido em duas categorias: navegador e ambiente de nó. A seguir, vamos dar uma olhada nas propriedades de configuração relacionadas ao ambiente em package.json. A definição de ambiente pode ser entendida simplesmente da seguinte forma:
js inclui os módulos commonjs, CMD, UMD, AMD e ES. No entanto, a partir do node13.2.0, o node suporta oficialmente a especificação do módulo ES. O campo pode ser usado em package.json para declarar a especificação modular que o pacote npm segue.
//pacote.json { nome: "algum pacote", tipo: "módulo"||"commonjs" }
Deve-se observar que
quando o tipo não é especificado, o valor padrão do tipo é commonjs. No entanto, é recomendado que todos os pacotes npm especifiquem o tipo.
Quando o campo de tipo especifica o valor como módulo, a especificação ESModule é usada
. campo type for especificado, todos os .js no diretório serão usados. Os arquivos que terminam com o sufixo seguem a especificação de modularização especificada por tipo.
Além do tipo, que pode especificar a especificação de modularização, a especificação de modularização seguida pelo arquivo é especificada. o sufixo do arquivo. Os arquivos que terminam com .mjs são as especificações ESModule usadas. A terminação .cjs segue a especificação commonjs
3.2 main
para definir o arquivo de entrada do pacote npm.
dar uma olhada nos cenários de uso desses três campos e nas prioridades quando esses três campos existem ao mesmo tempo. Vamos supor que exista um pacote npm chamado demo1,
----- dist |-- index.browser.js |-- index.browser.mjs |-- index.js |--
package.json de index.mjs especifica os três campos main, module e browser ao mesmo tempo,
"main": "dist/index.js", // main "módulo": "dist/index.mjs", // módulo // o navegador pode ser definido como um objeto de mapeamento correspondente ao campo principal/módulo ou pode ser definido diretamente como a string "navegador": { "./dist/index.js": "./dist/index.browser.js", // navegador + cjs "./dist/index.mjs": "./dist/index.browser.mjs" // navegador + mjs }, // "browser": "./dist/index.browser.js" // O navegador
é construído e usado por padrão. Por exemplo, se referenciarmos este pacote npm no projeto:
import demo from 'demo'
Depois de construir o acima. código através da ferramenta de construção, o módulo A sequência de carregamento é:
navegador+mjs > módulo > navegador+cjs > principal
Esta sequência de carregamento é a sequência de carregamento padrão da maioria das ferramentas de construção, como webapck, esbuild, etc. Esta ordem de carregamento pode ser modificada através da configuração correspondente, mas na maioria dos cenários, ainda seguiremos a ordem de carregamento padrão.
Se o campo de exportações estiver definido em package.json, então o conteúdo definido neste campo é a exportação real e completa do pacote npm, e a prioridade será maior que os campos principal e de arquivo.
Por exemplo:
{ "nome": "pacote", "exportações": { ".": "./main.mjs", "./foo": "./foo.js" } }
import { algo } from "pkg"; // de "pkg/main.mjs"
const { algo } = require("pkg/foo"); // require("pkg/foo.js")
do exemplo acima Parece que as exportações podem definir exportações com caminhos diferentes. Se existirem exportações, o diretório de arquivos válido anteriormente será inválido em todos os lugares, como require('pkg/package.json'), porque não está especificado nas exportações, e um erro será relatado.
Outro recurso importante das exportações é a referência condicional. Por exemplo, podemos especificar pacotes npm para referenciar diferentes arquivos de entrada com base em diferentes métodos de referência ou tipos modulares.
//pacote.json { "nome":"pacote", "principal": "./main-require.cjs", "exportações": { "importar": "./main-module.js", "require": "./main-require.cjs" }, "tipo": "módulo" }
No exemplo acima, se nos
referirmos a "./main-require.cjs"
através deconst p = require('pkg').
Se você passar:
import p from 'pkg',
a referência será "./main-module.js"
. A última coisa a observar é: se o atributo exports existir, o atributo exports não terá apenas uma prioridade maior que main, mas também maior que os campos do módulo e do navegador.
As propriedades de configuração relacionadas à dependência em package.json incluem dependências, devDependencies, peerDependencies, peerDependenciesMeta, etc.
Dependências são dependências de projeto e devDependencies são módulos necessários para o desenvolvimento, para que possamos instalá-los conforme necessário durante o processo de desenvolvimento para melhorar nossa eficiência de desenvolvimento. O que precisa ser observado aqui é tentar usá-los da forma mais padronizada possível em seus próprios projetos. Por exemplo, webpack, babel, etc. são dependências de desenvolvimento, não dependências do projeto em si.
dependências Além de dependências e devDependencies, este artigo se concentra em peerDependencies e peerDependenciesMeta.
peerDependencies são dependências em package.json, que podem resolver o problema de download da biblioteca principal várias vezes e unificar a versão da biblioteca principal.
//pacote/pacote -----node_modules |-- npm-a -> Depende de reagir, reagir-dom |-- npm-b -> Depende de reagir, reagir-dom |-- index.js
Por exemplo, no exemplo acima, se os pacotes sub-npm aeb vêm de react e react-dom, então se declararmos PeerDependicies no package.json dos pacotes sub-npm a e b, as Dependências correspondentes não serão reinstaladas.
Há dois pontos a serem observados:
"peerDependencies": {. "reagir": "^16.8.3 || ^17 || ^18" }, "peerDependenciesMeta": { "reagir-dom": { "opcional": verdadeiro }, "reação nativa": { "opcional": verdadeiro } }
"react-dom" e "react-native" são especificados aqui em peerDependenciesMeta e são opcionais, portanto, se "react-dom" e "react-native" não estiverem instalados no projeto, nenhum erro será relatado.
É importante notar que realmente eliminamos a restrição por meio de peerDependenciesMeta, mas muitas vezes há cenários em que é A ou B. Por exemplo, no exemplo acima, o que precisamos é de "react-dom" e "react-native" e precisamos instalar um, mas na verdade, não podemos conseguir esse prompt por meio da declaração acima.
Também existem muitos atributos de três partes em package.json, como tipos usados em tsc, sideEffects usados em ferramentas de construção, husky usado em git e eslintIgnore usado em eslint. extensões são para ferramentas de desenvolvimento específicas são significativas e não darei exemplos aqui.