npm thực chất là gì
npm là ba thứ gói chung dưới một cái tên. Nó là một registry: một cơ sở dữ liệu công khai khổng lồ chứa các package JavaScript tại npmjs.com. Nó là một công cụ dòng lệnh đi kèm Node.js để cài đặt và quản lý các package đó. Và nó là một đặc tả (định dạng package.json) mô tả những gì một dự án cần.
Khi bạn chạy npm install express, công cụ dòng lệnh kết nối với registry, tải express cùng mọi thứ nó phụ thuộc vào, đặt các tệp vào một thư mục tên là node_modules, và ghi package cùng phiên bản của nó vào package.json. Toàn bộ quy trình chỉ có vậy.
Nếu đã cài Node.js, bạn đã có sẵn npm. Kiểm tra thử:
node --version
npm --version
Nếu cả hai lệnh đều in ra phiên bản, bạn đã sẵn sàng.
Bắt đầu một dự án: npm init
Mọi dự án npm đều cần một tệp package.json. Đó là tệp kê khai: nó liệt kê tên dự án, phiên bản, các script và thư viện phụ thuộc. Cách nhanh nhất để tạo tệp này là npm init -y, chấp nhận mọi giá trị mặc định:
mkdir my-app
cd my-app
npm init -y
Lệnh đó ghi ra nội dung giống như sau:
{
"name": "my-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Bỏ -y đi thì npm sẽ hỏi bạn lần lượt từng trường. Dù cách nào, kết quả vẫn là một tệp package.json, tệp mà mọi thứ khác gắn vào. Chúng ta sẽ tìm hiểu chi tiết các trường của nó ở trang tiếp theo.
Cài một package
Khi đã có package.json, hãy cài package bằng npm install (hoặc viết tắt npm i):
npm install lodash
Có ba việc xảy ra:
- npm tải
lodashvà các thư viện nó phụ thuộc vào thư mụcnode_modules/. - npm thêm
"lodash": "^4.17.21"(hoặc phiên bản mới nhất lúc đó) vào phầndependenciescủapackage.json. - npm ghi ra tệp
package-lock.jsonlưu phiên bản chính xác của mọi package trong cây thư viện.
Giờ bạn có thể dùng nó:
Lời gọi require (hoặc import trong dự án ESM) tìm package bằng cách tìm bên trong node_modules. Bạn không cần viết đường dẫn: bộ phân giải module của Node lo việc đó.
dependencies và devDependencies
Không phải package nào cũng cần khi ứng dụng chạy trên production. Framework test, linter và bundler chỉ có ý nghĩa trong lúc phát triển. Hãy cài chúng bằng --save-dev (hoặc -D):
npm install --save-dev jest
npm install -D eslint prettier
Các package này được ghi vào devDependencies thay vì dependencies:
{
"dependencies": {
"lodash": "^4.17.21"
},
"devDependencies": {
"jest": "^29.7.0",
"eslint": "^8.57.0",
"prettier": "^3.2.5"
}
}
Trên máy chủ production, npm install --omit=dev bỏ qua hoàn toàn phần dev, giúp việc cài đặt nhẹ và nhanh hơn. Phân chia đúng chỗ này quan trọng hơn bạn nghĩ: một webpack đặt nhầm vào dependencies sẽ làm phình mọi lần triển khai lên production.
Cài mọi thứ cùng lúc
Khi clone một repository đã có sẵn package.json, bạn không cần liệt kê từng package. Chỉ cần chạy:
npm install
Không có đối số, npm đọc package.json (và tuân theo đúng phiên bản trong package-lock.json) rồi cài toàn bộ cây thư viện vào node_modules. Đây là lệnh đầu tiên bạn chạy mỗi khi lấy code mới về.
Đó cũng là lý do node_modules nên nằm trong .gitignore. Nó tạo lại được từ lockfile, rất nặng, và thay đổi mỗi khi có ai chạy npm install. Hãy commit package.json và package-lock.json; để mọi người tự tạo lại node_modules.
Cập nhật package
npm outdated cho thấy những package đã cũ:
npm outdated
Bạn sẽ thấy một bảng với các cột Current, Wanted và Latest. Wanted là phiên bản mới nhất được phép theo khoảng phiên bản trong package.json (với ^4.17.21 là mọi phiên bản dưới 5.0.0). Latest là phiên bản mới nhất đã được xuất bản, có thể là một bản major mà bạn chưa chọn nâng lên.
Để cập nhật trong khoảng được phép:
npm update
Để nhảy lên phiên bản mới nhất thật sự, kể cả khi tăng phiên bản major, hãy cài lại package với @latest:
npm install lodash@latest
Tăng phiên bản major có thể làm hỏng code của bạn: đó chính là điều con số phiên bản muốn báo hiệu. Hãy đọc changelog trước khi nâng.
Gỡ cài đặt
Gỡ một package cũng đơn giản tương tự:
npm uninstall lodash
Lệnh này xóa package khỏi node_modules và xóa mục tương ứng trong package.json. Thêm -D nếu đó là dev dependency (npm vẫn tự nhận ra, nhưng viết rõ ràng giúp tránh bất ngờ trong script).
Toàn cục và cục bộ
Gần như mọi lần cài đặt nên là cục bộ: cố định trong một dự án, nằm trong node_modules của dự án đó. Ngoại lệ là các công cụ dòng lệnh bạn muốn dùng ở mọi nơi:
npm install -g typescript
npm install -g http-server
Cài toàn cục đặt công cụ vào một vị trí dùng chung cho cả hệ thống và thêm mục bin của nó vào PATH, nên bạn có thể chạy tsc hay http-server từ bất kỳ thư mục nào. Nhưng các package cài toàn cục không được theo dõi theo từng dự án và có thể lệch phiên bản giữa các máy.
Một giải pháp trung gian tốt hơn cho các lệnh dùng một lần là npx, đi kèm với npm:
npx create-react-app my-app
npx prettier --write .
npx chạy một package mà không cần cài toàn cục: nó tải package khi cần, chạy, và xong. Với những công cụ chỉ dùng một lần, cách này gọn gàng hơn cài toàn cục vĩnh viễn.
Bảng lệnh tối giản
Những lệnh bạn sẽ thực sự dùng hằng ngày:
npm init -y # create package.json
npm install # install everything in package.json
npm install <pkg> # add a runtime dependency
npm install -D <pkg> # add a dev dependency
npm install -g <pkg> # install a CLI tool globally
npm uninstall <pkg> # remove a dependency
npm outdated # see what's out of date
npm update # update within allowed ranges
npm install <pkg>@latest # jump to the newest version
npm run <script> # run a script from package.json
npx <pkg> # run a package without installing it
Đó là phần lớn của npm. Phần còn lại như xuất bản package, workspace, scoped package, bạn có thể tìm hiểu khi cần.
Bên trong node_modules thực sự có gì
Một cách hiểu cuối cùng. node_modules là một thư mục gần như phẳng chứa mọi package dự án của bạn phụ thuộc vào, cộng với mọi thứ mà những package đó phụ thuộc vào, theo kiểu bắc cầu. Cài một package có thể kéo theo cả trăm package khác, điều đó là bình thường. npm loại bỏ trùng lặp khi có thể, nên hai package cùng phụ thuộc vào một phiên bản lodash sẽ dùng chung một bản.
Tệp lockfile (package-lock.json) ghi lại phiên bản chính xác đã được phân giải của từng package đó. Đây là điều giúp các bản build tái tạo được: hai lập trình viên chạy npm install từ cùng một lockfile sẽ có cây thư viện giống nhau đến từng byte, dù cách nhau nhiều tháng.
Hãy coi node_modules là đầu ra được tạo tự động. Không bao giờ sửa tệp bên trong nó: thay đổi của bạn sẽ biến mất ở lần cài đặt tiếp theo của bất kỳ ai.
Tiếp theo: package.json
package.json là tệp npm liên tục đọc và ghi lại phía sau. Hiểu các trường của nó như scripts, main, type, khoảng phiên bản, engines là điều biến npm từ một hộp đen thành thứ bạn kiểm soát được. Đó là nội dung tiếp theo.
Câu hỏi thường gặp
npm là gì?
npm là trình quản lý package mặc định của Node.js. Nó đi kèm với Node, lưu trữ một kho package JavaScript công khai khổng lồ, và cung cấp công cụ dòng lệnh để cài đặt, cập nhật và xuất bản các package đó. Khi bạn chạy npm install lodash, npm tải lodash từ kho về thư mục node_modules và ghi nó vào package.json.
dependencies và devDependencies khác nhau thế nào?
dependencies là các package ứng dụng cần để chạy trên môi trường production, như express hay react. devDependencies chỉ cần trong lúc phát triển hoặc build: trình chạy test, bundler, linter. Bạn cài loại sau bằng npm install --save-dev <pkg> (hoặc -D). Trên production, npm install --omit=dev sẽ bỏ qua devDependencies.
Có nên commit node_modules lên git không?
Không. node_modules có thể dễ dàng nặng hàng trăm megabyte và hoàn toàn tạo lại được từ package.json + package-lock.json. Hãy thêm nó vào .gitignore và commit tệp lockfile thay thế. Bất kỳ ai clone repository của bạn chỉ cần chạy npm install là có đúng cây thư viện y hệt.
Cài npm toàn cục và cục bộ khác nhau thế nào?
Cài cục bộ (npm install <pkg>) đặt package vào node_modules của dự án và ghi nó vào package.json. Cài toàn cục (npm install -g <pkg>) cài package cho cả hệ thống, thường dùng cho các công cụ dòng lệnh bạn muốn dùng ở mọi nơi. Hãy ưu tiên cài cục bộ cho thư viện của dự án để phiên bản luôn cố định theo từng dự án.