package-lock.json
là gì? Tại sao lại có file package-lock.json
này trong khi chúng ta đã có package.json
?
Nhiều bạn (bao gồm cả mình trước đây) thường hay bối rối về 2 file này, vì chúng có nhiều điểm tương đồng. Nhưng đừng lo lắng, sau khi đọc bài này bạn sẽ hiểu rõ hơn về chúng..
Đối với công dụng
yarn.lock
cũng tương tự nhưpackage-lock.json
nhé, chỉ là format nó khác thôi
Khi chúng ta dùng nodejs hay npm để quản lý các thư viện javascript, chúng ta sẽ có file package.json để lưu version các package mà chúng ta cần cho dự án.
Ví dụ, nếu chúng ta muốn tạo một RESTful API bằng Express, chúng ta sẽ chạy lệnh npm install express
Sau đó, file package.json của chúng ta sẽ có thêm express với version là 4.0.0 trong phần dependencies.
{ "name": "my-app", "version": "1.0.0", "main": "index.js", "dependency": { "express": "4.0.0" }}
Khi chúng ta cài đặt thư viện Express, cũng có nghĩa là chúng ta cài những thư viện mà Express dùng đến. Ví dụ Express dùng thư viện send để thực hiện chức năng static file server.
Thêm nữa, package send
lại cần một package khác để hoạt động, đó là mime-types. Vì thế npm sẽ cài mime-types
.
Cuối cùng khi cài đặt mime-types
, npm nhận ra rằng mime-types
cần mime-db
, và nó cũng tải luôn mime-db
Nó giống như một quá trình đệ quy vậy, nó sẽ tải xuống các dependency của bạn và cả dependency của dependency. Đó là lý do folder node_modules
của bạn rất nặng.
Ngay cả việc bạn chỉ cài một dependency express
thôi, nếu bạn kiểm tra thư mục node_modules
– thư mục mà npm lưu trữ những dependency của bạn – bạn sẽ thấy nhiều package tại đây.
txt
▾ node_modules/ ▸ accepts/ ▸ array-flatten/ ▸ body-parser/ ▸ bytes/ ▸ content-disposition/ ▸ content-type/ ▸ cookie-signature/ ▸ cookie/ ▸ debug/ ▸ depd/ ▸ destroy/ ▸ ee-first/ ▸ encodeurl/ ▸ escape-html/ ▸ etag/ ▸ express/ ▸ finalhandler/ ▸ forwarded/ ▸ fresh/ ▸ http-errors/ ▸ iconv-lite/ ▸ inherits/ ▸ ipaddr.js/ ▸ media-typer/ ▸ merge-descriptors/ ▸ methods/ ▸ mime-db/ ▸ mime-types/ ▸ mime/ ▸ ms/ ▸ negotiator/ ▸ on-finished/ ▸ parseurl/ ▸ path-to-regexp/ ▸ proxy-addr/ ▸ qs/ ▸ range-parser/ ▸ raw-body/ ▸ safe-buffer/ ▸ safer-buffer/ ▸ send/ ▸ serve-static/ ▸ setprototypeof/ ▸ statuses/ ▸ toidentifier/ ▸ type-is/ ▸ unpipe/ ▸ utils-merge/ ▸ vary/ package-lock.json package.json
Giả sử nếu không có file package-lock.json
và bạn chạy npm install
vào buổi chiều, bạn có thể nhận được những dependency khác so với buổi sáng, dù cho bạn không thay đổi verson express
trong package.json
Câu lệnh
npm install
có thể cài những package khác version quy định trong filepackage.json
nếu không có filepackage-lock.json
🥇Tại sao cùng một package.json
nhưng khi npm install
lại tải về những dependency khác nhau?
Trong file package.json, bạn sẽ thấy các dependency có version có dấu ^ hoặc ~ đứng trước. Những dấu này có nghĩa là NPM không cần phải cài đặt đúng version đã chỉ định. NPM có thể cài đặt version “thích hợp” với dependency trong package.json
Ví dụ chúng ta không có file package-lock.json
, trong package.json
chúng ta có "express": "^4.0.0"
.
Trong trường hợp này khi chạy npm install
thì chúng ta có thể nhận lại express version là 4.0.1
hoặc 4.1.0
(tất nhiên với điều kiện là express đã xuất bản version 4.0.1
hoặc 4.1.0
)
Để lý giải cho điều này thì chúng ta cần hiểu về semantic version, mình có thể tóm gọn như thế này
Cấu trúc version có dạng MAJOR.MINOR.PATCH
- Số version MAJOR đại diện cho những thay đổi lớn, không tương thích với phiên bản trước
- Số version MINOR đại diện cho những chức năng mới được thêm vào, vẫn tương thích với phiên bản trước
- Số version PATCH đại diện cho những bản vá lỗi, vẫn tương thích ngược với phiên bản trước
Ngoài ra còn một số prefix (tiền tố) khi những version này được ghi lại trong file package.json
đó là
^
: Có thể cài version MINOR và PATCH. Ví dụ^0.13.0
thì có thể cài0.13.1
,0.14.0
~
: Có thể cài version PATCH. Ví dụ~0.13.0
thì có thể cài0.13.1
nhưng không thể cài0.14.0
Oke. Đến đây bạn đã có câu trả lời cho câu hỏi “Tại sao cùng một package.json
nhưng khi npm install
lại tải về những dependency khác nhau?” rồi đúng không. Đó là do mấy cái tiền tố trước version.
Tiếp tục nào!!!!
Nếu không dùng đến file package-lock.json
, sử dụng ^
và ~
có thể không xảy ra vấn đề gì. Vì theo semantic version mà mình đã nói ở trên, các version update sau minor và patch có khả năng tương thích ngược – nghĩa là không có bất kỳ “breaking change” nào với code bạn cả.
Tuy nhiên, là con người ai mà chả có lỗi lầm. Ví dụ, người tạo ra package chỉ sửa một lỗi nhỏ thôi, họ note rằng đây chỉ là một bản PATCH, trong khi đó có thể nó gây ra một sự thay đổi lớn trong code của bạn.
Ví dụ Chai từng đánh số version bị sai dẫn đến hàng triệu bản build trên khắp thế giới bị lỗi.
Vậy nghĩa là không sử dụng ^
hoặc ~
thì npm install
sẽ luôn cài đúng phiên bản package đúng không?
Không, không hề 🙃
Ngay cả khi bạn không dùng
^
và~
trong filepackage.json
của bạn thì những package của bạn nó cũng dùng^
hoặc~
để setting version các dependency mà nó phụ thuộc. Vậy nên nếu chạynpm install
vẫn có thể tải về những version khác nhau.
🥇Cách package-lock.json
giải quyết vấn đề version không ổn định
Khi bạn cài bất cứ package nào bằng câu lệnh npm install <packagename>
, chúng ta sẽ có thêm file package-lock.json
.
File package-lock.json
sẽ liệt kê hết tất cả những package trong ứng dụng của bạn, bao gồm package của package,… Đó là lý do tại sao file package-lock.json
nó lại dài hơn package.json
.
- Nếu bạn chạy chạy câu lệnh
npm install
mà có cả 2 filepackage.json
vàpackage-lock.json
thì bạn yên tâm rằng bạn luôn tải đúng version như đã ghi trong filepackage-lock.json
. Filepackage-lock.json
cũng sẽ không bao giờ bị thay đổi - Nếu bạn thay đổi bằng tay version hoặc package được ghi trong file
package.json
và chạynpm install
thì lúc nàypackage-lock.json
sẽ được cập nhật theo để tương thích với filepackage.json
Vậy nên nếu có file package-lock.json
, bạn sẽ luôn nhận được chính xác version các package của bạn, cho dù đó là hàng ngàn năm sau.
Ngoài npm install
, chúng ta còn có một câu lệnh khá là tương đồng là npm ci
npm ci
thường được dùng trong các script tự động hóa CI CD.
Sự khác biệt giữa npm install
và npm ci
là:
- Nếu những dependency trong
package-lock.json
không khớp vớipackage.json
thìnpm ci
sẽ ngừng lại và quăng ra lỗi npm ci
chỉ có thể cài tất cả các dependency trong project cùng một lúc, không thể cài từng cái nhưnpm install
được- Nếu
node_modules
tồn tại, nó sẽ bị xóa trước khi càinpm ci
npm ci
sẽ không bao giờ viết lên filepackage.json
hoặcpackage-lock.json
Ví dụ dưới đây sẽ cho bạn thấy công dụng thực tế của npm ci
Nếu có bạn thay đổi bằng tay file package.json
ở dưới local, nhưng quên chạy lại câu lệnh npm install
, điều này dẫn đến package-lock.json
không tương thích với package.json
. Bây giờ nếu trên server của bạn có flow là
Nếu với flow này thì sau khi npm install
, server bạn sẽ cập nhật file package-lock.json
trên server, lúc này package-lock.json
ở server khác với trên Github, lần sau bạn sẽ không thể thực hiện theo flow như trên được nữa vì git pull
sẽ bị chặn do sự khác nhau giữa package-lock.json
ở server và Github
Bạn thấy đấy, dùng npm install
trong trường hợp này không hay cho lắm, nếu chúng ta dùng npm ci
thì chúng ta sẽ không bao giờ sợ rằng file package-lock.json
bị thay đổi, và luôn đảm bảo rằng package-lock.json
luôn được đồng bộ với package.json
🥇Tóm lại
- File
package-lock.json
sẽ giúp bạn cố định version khi bạn chạynpm install
. - Bạn nên đưa
package-lock.json
vào git, đừng ignore nó để tránh những trường hợp lỗi code do update version. - Nếu ở local, khi cài package mới hay tải tất cả package trong project thì dùng
npm install
. - Nếu trên server thì nên dùng
npm ci
để không change bất cứ thứ gì.
Oke, bài viết đến đây là hết rồi đó, mong rằng bài viết có thể giúp bạn hiểu hơn về công dụng của package-lock.json
cũng như là package.json
One thought on “Tại sao package-lock.json tồn tại và cách nó hoạt động”