cainiaoblog 23.2.171

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (401) hide show
  1. package/CNAME +1 -0
  2. package/MP_verify_o6dHCK5mIkxNpzvZ.txt +1 -0
  3. package/README.md +8 -0
  4. package/about/index.html +54 -0
  5. package/ads.txt +1 -0
  6. package/app/manual.html +104 -0
  7. package/appConfigs/config +1 -0
  8. package/archives/2018/index.html +48 -0
  9. package/archives/2019/index.html +48 -0
  10. package/archives/2019/page/2/index.html +48 -0
  11. package/archives/2020/index.html +48 -0
  12. package/archives/2021/index.html +48 -0
  13. package/archives/2022/index.html +48 -0
  14. package/archives/2023/index.html +48 -0
  15. package/archives/index.html +48 -0
  16. package/archives/page/2/index.html +48 -0
  17. package/archives/page/3/index.html +48 -0
  18. package/article/10047.html +148 -0
  19. package/article/10164.html +64 -0
  20. package/article/10624.html +436 -0
  21. package/article/10745.html +586 -0
  22. package/article/11674.html +71 -0
  23. package/article/11939.html +72 -0
  24. package/article/12238.html +86 -0
  25. package/article/1230.html +533 -0
  26. package/article/13265.html +69 -0
  27. package/article/13823.html +78 -0
  28. package/article/14125.html +72 -0
  29. package/article/14192.html +561 -0
  30. package/article/14375.html +85 -0
  31. package/article/14744.html +83 -0
  32. package/article/14936.html +451 -0
  33. package/article/1518.html +250 -0
  34. package/article/15428.html +71 -0
  35. package/article/15518.html +288 -0
  36. package/article/15569.html +418 -0
  37. package/article/1566.html +122 -0
  38. package/article/16017.html +156 -0
  39. package/article/16947.html +440 -0
  40. package/article/17096.html +82 -0
  41. package/article/17246.html +89 -0
  42. package/article/17575.html +350 -0
  43. package/article/179.html +757 -0
  44. package/article/18039.html +233 -0
  45. package/article/18071.html +375 -0
  46. package/article/18142.html +71 -0
  47. package/article/19909.html +160 -0
  48. package/article/20014.html +89 -0
  49. package/article/20021.html +389 -0
  50. package/article/20037.html +280 -0
  51. package/article/20056.html +95 -0
  52. package/article/20093.html +404 -0
  53. package/article/2021.html +607 -0
  54. package/article/20459.html +115 -0
  55. package/article/20613.html +263 -0
  56. package/article/20635.html +71 -0
  57. package/article/21317.html +71 -0
  58. package/article/21320.html +90 -0
  59. package/article/21782.html +548 -0
  60. package/article/22230.html +435 -0
  61. package/article/22237.html +347 -0
  62. package/article/22398.html +109 -0
  63. package/article/22422.html +270 -0
  64. package/article/22565.html +300 -0
  65. package/article/23046.html +250 -0
  66. package/article/23083.html +215 -0
  67. package/article/23195.html +297 -0
  68. package/article/23301.html +835 -0
  69. package/article/23589.html +285 -0
  70. package/article/23655.html +338 -0
  71. package/article/23864.html +76 -0
  72. package/article/23914.html +96 -0
  73. package/article/24169.html +135 -0
  74. package/article/24581.html +349 -0
  75. package/article/2475.html +500 -0
  76. package/article/24826.html +77 -0
  77. package/article/24924.html +247 -0
  78. package/article/25164.html +1495 -0
  79. package/article/25318.html +394 -0
  80. package/article/25710.html +1129 -0
  81. package/article/25734.html +83 -0
  82. package/article/26083.html +174 -0
  83. package/article/26147.html +283 -0
  84. package/article/26291.html +110 -0
  85. package/article/26354.html +71 -0
  86. package/article/26624.html +145 -0
  87. package/article/26639.html +298 -0
  88. package/article/26850.html +138 -0
  89. package/article/26940.html +258 -0
  90. package/article/27796.html +190 -0
  91. package/article/27800.html +161 -0
  92. package/article/28963.html +159 -0
  93. package/article/28976.html +247 -0
  94. package/article/29042.html +72 -0
  95. package/article/29596.html +81 -0
  96. package/article/29619.html +183 -0
  97. package/article/29623.html +329 -0
  98. package/article/29829.html +205 -0
  99. package/article/29abbd1c.html +112 -0
  100. package/article/30215.html +178 -0
  101. package/article/30552.html +78 -0
  102. package/article/30818.html +201 -0
  103. package/article/31330.html +364 -0
  104. package/article/3154.html +577 -0
  105. package/article/31695.html +348 -0
  106. package/article/31826.html +71 -0
  107. package/article/31843.html +132 -0
  108. package/article/32280.html +212 -0
  109. package/article/32431.html +74 -0
  110. package/article/32443.html +72 -0
  111. package/article/32732.html +122 -0
  112. package/article/3286.html +251 -0
  113. package/article/32960.html +198 -0
  114. package/article/33713.html +72 -0
  115. package/article/33899.html +615 -0
  116. package/article/34351.html +74 -0
  117. package/article/34372.html +89 -0
  118. package/article/34410.html +74 -0
  119. package/article/34431.html +118 -0
  120. package/article/3447.html +75 -0
  121. package/article/34474.html +142 -0
  122. package/article/34871.html +147 -0
  123. package/article/35397.html +175 -0
  124. package/article/3544.html +199 -0
  125. package/article/35503.html +115 -0
  126. package/article/35624.html +84 -0
  127. package/article/35697.html +384 -0
  128. package/article/35863.html +73 -0
  129. package/article/36037.html +253 -0
  130. package/article/36118.html +71 -0
  131. package/article/3621.html +119 -0
  132. package/article/36251.html +296 -0
  133. package/article/36642.html +352 -0
  134. package/article/37102.html +763 -0
  135. package/article/37336.html +351 -0
  136. package/article/37351.html +89 -0
  137. package/article/3760.html +294 -0
  138. package/article/37749.html +75 -0
  139. package/article/37789.html +83 -0
  140. package/article/38005.html +765 -0
  141. package/article/38009.html +284 -0
  142. package/article/38269.html +194 -0
  143. package/article/38344.html +88 -0
  144. package/article/38359.html +334 -0
  145. package/article/38423.html +379 -0
  146. package/article/38530.html +213 -0
  147. package/article/38743.html +268 -0
  148. package/article/38753.html +204 -0
  149. package/article/38881.html +71 -0
  150. package/article/39087.html +414 -0
  151. package/article/39309.html +84 -0
  152. package/article/39486.html +74 -0
  153. package/article/39637.html +306 -0
  154. package/article/39839.html +72 -0
  155. package/article/39937.html +152 -0
  156. package/article/39970.html +71 -0
  157. package/article/402.html +559 -0
  158. package/article/40698.html +71 -0
  159. package/article/40811.html +71 -0
  160. package/article/40964.html +462 -0
  161. package/article/41011.html +322 -0
  162. package/article/41152.html +78 -0
  163. package/article/41696.html +71 -0
  164. package/article/41717.html +281 -0
  165. package/article/41777.html +149 -0
  166. package/article/419.html +985 -0
  167. package/article/4233.html +136 -0
  168. package/article/4292.html +86 -0
  169. package/article/43302.html +96 -0
  170. package/article/43397.html +106 -0
  171. package/article/43724.html +281 -0
  172. package/article/43999.html +224 -0
  173. package/article/44426.html +77 -0
  174. package/article/44964.html +137 -0
  175. package/article/45004.html +123 -0
  176. package/article/4510.html +83 -0
  177. package/article/45156.html +320 -0
  178. package/article/45157.html +52 -0
  179. package/article/45267.html +87 -0
  180. package/article/45641.html +207 -0
  181. package/article/45960.html +75 -0
  182. package/article/46100.html +1262 -0
  183. package/article/46366.html +74 -0
  184. package/article/46771.html +96 -0
  185. package/article/46911.html +517 -0
  186. package/article/46948.html +137 -0
  187. package/article/46ee4850.html +349 -0
  188. package/article/47019.html +84 -0
  189. package/article/47532.html +94 -0
  190. package/article/4783.html +429 -0
  191. package/article/47972.html +72 -0
  192. package/article/48216.html +127 -0
  193. package/article/48252.html +185 -0
  194. package/article/48520.html +74 -0
  195. package/article/48722.html +75 -0
  196. package/article/48807.html +99 -0
  197. package/article/49126.html +323 -0
  198. package/article/50001.html +88 -0
  199. package/article/50058.html +151 -0
  200. package/article/50343.html +410 -0
  201. package/article/50510.html +82 -0
  202. package/article/51082.html +98 -0
  203. package/article/51677.html +111 -0
  204. package/article/5204.html +1085 -0
  205. package/article/52500.html +538 -0
  206. package/article/52689.html +259 -0
  207. package/article/52877.html +569 -0
  208. package/article/53125.html +191 -0
  209. package/article/53428.html +251 -0
  210. package/article/53524.html +74 -0
  211. package/article/53698.html +322 -0
  212. package/article/53803e6.html +93 -0
  213. package/article/53924.html +291 -0
  214. package/article/53962.html +253 -0
  215. package/article/54006.html +71 -0
  216. package/article/54018.html +88 -0
  217. package/article/54161.html +83 -0
  218. package/article/54178.html +69 -0
  219. package/article/54258.html +327 -0
  220. package/article/54295.html +214 -0
  221. package/article/54313.html +1976 -0
  222. package/article/54351.html +90 -0
  223. package/article/54604.html +80 -0
  224. package/article/54632.html +71 -0
  225. package/article/54912.html +756 -0
  226. package/article/54918.html +129 -0
  227. package/article/55119.html +136 -0
  228. package/article/55756.html +118 -0
  229. package/article/55769.html +136 -0
  230. package/article/55870.html +161 -0
  231. package/article/56139.html +92 -0
  232. package/article/56557.html +69 -0
  233. package/article/56604.html +69 -0
  234. package/article/56816.html +266 -0
  235. package/article/56926.html +153 -0
  236. package/article/57747.html +409 -0
  237. package/article/57965.html +475 -0
  238. package/article/58816.html +78 -0
  239. package/article/59059.html +435 -0
  240. package/article/59095.html +350 -0
  241. package/article/59349.html +244 -0
  242. package/article/59402.html +216 -0
  243. package/article/59448.html +55 -0
  244. package/article/59870.html +94 -0
  245. package/article/59877.html +208 -0
  246. package/article/5dbb6f41.html +86 -0
  247. package/article/6008.html +75 -0
  248. package/article/60327.html +126 -0
  249. package/article/6035.html +76 -0
  250. package/article/60428.html +71 -0
  251. package/article/60544.html +128 -0
  252. package/article/61319.html +95 -0
  253. package/article/61367.html +93 -0
  254. package/article/624d1918.html +292 -0
  255. package/article/62543.html +71 -0
  256. package/article/62887.html +169 -0
  257. package/article/63074.html +120 -0
  258. package/article/63266.html +311 -0
  259. package/article/63351.html +74 -0
  260. package/article/63542.html +104 -0
  261. package/article/63824.html +275 -0
  262. package/article/64051.html +653 -0
  263. package/article/64189b69.html +72 -0
  264. package/article/64354.html +75 -0
  265. package/article/64869.html +79 -0
  266. package/article/64923.html +596 -0
  267. package/article/65128.html +95 -0
  268. package/article/65295.html +79 -0
  269. package/article/6888.html +75 -0
  270. package/article/7065.html +84 -0
  271. package/article/7455.html +85 -0
  272. package/article/7a3ef8f.html +315 -0
  273. package/article/8140.html +188 -0
  274. package/article/8190.html +85 -0
  275. package/article/8225.html +78 -0
  276. package/article/84c9ccd6.html +159 -0
  277. package/article/8f9a9ae3.html +82 -0
  278. package/article/9174.html +73 -0
  279. package/article/9738.html +294 -0
  280. package/article/a4145266.html +71 -0
  281. package/article/bedea419.html +72 -0
  282. package/article/bf7e7421.html +78 -0
  283. package/article/c1867fbf.html +103 -0
  284. package/article/c5576490.html +70 -0
  285. package/article/dcb0db28.html +76 -0
  286. package/article/fc3b727a.html +79 -0
  287. package/atom.xml +445 -0
  288. package/baidusitemap.xml +1079 -0
  289. package/blocks/dns.txt +264 -0
  290. package/blocks/filter.txt +1070 -0
  291. package/blocks/script.js +42 -0
  292. package/categories/CDN/index.html +48 -0
  293. package/categories/IDE/index.html +48 -0
  294. package/categories/git/index.html +48 -0
  295. package/categories/hexo/index.html +48 -0
  296. package/categories//345/205/254/345/205/261/350/212/202/346/227/245/index.html +48 -0
  297. package/categories//345/210/206/344/272/253/index.html +48 -0
  298. package/categories//345/211/215/347/253/257/index.html +48 -0
  299. package/categories//345/211/215/347/253/257/page/2/index.html +48 -0
  300. package/categories//345/211/215/347/253/257//346/265/217/350/247/210/345/231/250/index.html +48 -0
  301. package/categories//345/211/215/347/253/257//346/265/217/350/247/210/345/231/250//347/275/221/347/273/234/345/215/217/350/256/256/index.html +48 -0
  302. package/categories//345/215/232/345/256/242/index.html +48 -0
  303. package/categories//345/267/245/345/205/267/index.html +48 -0
  304. package/categories//345/271/277/345/221/212/350/277/207/346/273/244/index.html +48 -0
  305. package/categories//345/271/277/345/221/212/350/277/207/346/273/244//350/247/206/351/242/221/347/224/265/350/247/206/345/260/217/350/257/264/346/274/253/347/224/273/350/265/204/350/256/257/351/237/263/344/271/220/index.html +48 -0
  306. package/categories//346/255/243/345/210/231/index.html +48 -0
  307. package/categories//346/265/217/350/247/210/345/231/250/index.html +48 -0
  308. package/categories//347/256/227/346/263/225/index.html +48 -0
  309. package/categories//350/265/204/350/256/257/index.html +48 -0
  310. package/categories//350/275/273/346/235/276/344/270/200/345/210/273/index.html +48 -0
  311. package/cdn/css/font-awesome.min.css +4 -0
  312. package/cdn/css/grids-responsive-min.css +7 -0
  313. package/cdn/css/normalize.css +349 -0
  314. package/cdn/css/pure-min.css +11 -0
  315. package/cdn/fonts/fontawesome-webfont.eot +0 -0
  316. package/cdn/fonts/fontawesome-webfont.svg +2671 -0
  317. package/cdn/fonts/fontawesome-webfont.ttf +0 -0
  318. package/cdn/fonts/fontawesome-webfont.woff +0 -0
  319. package/cdn/fonts/fontawesome-webfont.woff2 +0 -0
  320. package/cdn/js/Valine.min.js +17 -0
  321. package/cdn/js/canvas-nest.js +1 -0
  322. package/cdn/js/clipboard.min.js +7 -0
  323. package/cdn/js/crypto-js.js +25 -0
  324. package/cdn/js/jquery.min.js +2 -0
  325. package/css/default.css +1144 -0
  326. package/css/donate.css +338 -0
  327. package/css/hbe.style.css +749 -0
  328. package/css/style.css +2095 -0
  329. package/donate/index.html +39 -0
  330. package/google917dcf72f6e5c7f5.html +1 -0
  331. package/guestbook/index.html +68 -0
  332. package/hosts/ads.txt +149 -0
  333. package/hosts/flash.txt +9 -0
  334. package/hosts/github.txt +6 -0
  335. package/hosts/google.txt +16936 -0
  336. package/hosts/winrar.txt +2 -0
  337. package/img/alipay.svg +46 -0
  338. package/img/bitcoin.svg +135 -0
  339. package/img/blog.ico +0 -0
  340. package/img/blog.png +0 -0
  341. package/img/gaba.png +0 -0
  342. package/img/github.svg +1 -0
  343. package/img/like.svg +1 -0
  344. package/img/mp-mini.jpg +0 -0
  345. package/img/mp.png +0 -0
  346. package/img/paypal.svg +63 -0
  347. package/img/upy_logo.svg +59 -0
  348. package/img/wechat.svg +49 -0
  349. package/index.html +48 -0
  350. package/jd_root.txt +1 -0
  351. package/js/codeblock-resizer.js +51 -0
  352. package/js/dark.js +12 -0
  353. package/js/donate.js +87 -0
  354. package/js/fixedPage.js +106 -0
  355. package/js/gitment.browser.js +3751 -0
  356. package/js/search.js +86 -0
  357. package/js/share.js +60 -0
  358. package/js/smartresize.js +32 -0
  359. package/js/totop.js +12 -0
  360. package/kankan/index.html +77 -0
  361. package/lib/hbe.js +293 -0
  362. package/package.json +25 -0
  363. package/page/10/index.html +49 -0
  364. package/page/11/index.html +59 -0
  365. package/page/12/index.html +67 -0
  366. package/page/13/index.html +63 -0
  367. package/page/14/index.html +66 -0
  368. package/page/15/index.html +60 -0
  369. package/page/16/index.html +60 -0
  370. package/page/17/index.html +62 -0
  371. package/page/18/index.html +61 -0
  372. package/page/19/index.html +58 -0
  373. package/page/2/index.html +48 -0
  374. package/page/20/index.html +57 -0
  375. package/page/21/index.html +61 -0
  376. package/page/22/index.html +55 -0
  377. package/page/23/index.html +64 -0
  378. package/page/24/index.html +60 -0
  379. package/page/25/index.html +58 -0
  380. package/page/26/index.html +48 -0
  381. package/page/27/index.html +58 -0
  382. package/page/3/index.html +48 -0
  383. package/page/4/index.html +48 -0
  384. package/page/5/index.html +51 -0
  385. package/page/6/index.html +51 -0
  386. package/page/7/index.html +51 -0
  387. package/page/8/index.html +53 -0
  388. package/page/9/index.html +48 -0
  389. package/randomcall/README.html +1 -0
  390. package/randomcall/lucker.html +290 -0
  391. package/randomcall/lucky.png +0 -0
  392. package/robots.txt +22 -0
  393. package/search.xml +6740 -0
  394. package/sitemap.xml +2669 -0
  395. package/tags//345/216/237/345/210/233/index.html +48 -0
  396. package/tags//345/250/261/344/271/220/index.html +48 -0
  397. package/tags//346/211/213/345/206/231/index.html +48 -0
  398. package/tags//347/226/253/346/203/205/index.html +48 -0
  399. package/tags//350/275/254/350/275/275/index.html +48 -0
  400. package/tags//350/275/254/350/275/275/page/2/index.html +48 -0
  401. package/v.html +90 -0
@@ -0,0 +1,763 @@
1
+ <!DOCTYPE html><html lang="zh-CN" data-dark><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport"><meta content="yes" name="apple-mobile-web-app-capable"><meta content="black-translucent" name="apple-mobile-web-app-status-bar-style"><meta content="telephone=no" name="format-detection"><meta name="description" content="前端壹菜鸟,记录工作难题,关注前端知识,收集精彩博文,做技术的搬运工"><meta name="msvalidate.01" content="0FE4D8B3381D3D87088996B886E1E2BD"><title>前端工程化 - 剖析npm的包管理机制 | 前端壹菜鸟</title><link rel="stylesheet" type="text/css" href="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/css/style.min.css"><link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/normalize.css@latest/normalize.min.css"><link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/purecss@latest/build/pure-min.min.css"><link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/purecss@latest/build/grids-responsive-min.css"><link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css"><link rel="icon" mask="" sizes="any" href="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/img/blog.ico"><link rel="Shortcut Icon" type="image/x-icon" href="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/img/blog.ico"><link rel="apple-touch-icon" href="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/img/blog.png"><link rel="apple-touch-icon-precomposed" href="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/img/blog.png"><link rel="alternate" type="application/atom+xml" href="/atom.xml"><script type="text/javascript" src="//cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js"></script><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-8385136408348258"></script><script src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" async></script><script>(function(){
2
+ var bp = document.createElement('script');
3
+ var curProtocol = window.location.protocol.split(':')[0];
4
+ if (curProtocol === 'https'){
5
+ bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
6
+ }
7
+ else{
8
+ bp.src = 'http://push.zhanzhang.baidu.com/push.js';
9
+ }
10
+ var s = document.getElementsByTagName("script")[0];
11
+ s.parentNode.insertBefore(bp, s);
12
+ })();
13
+ </script><script>(function(){
14
+ var src = 'https://jspassport.ssl.qhimg.com/11.0.1.js?d182b3f28525f2db83acfaaf6e696dba';
15
+ document.write('<script src="' + src + '" id="sozz"><\/script>');
16
+ })();
17
+ </script><script>var _hmt=_hmt||[];(function(){var hm=document.createElement("script");hm.src='https://hm.baidu.com/hm.js?011f0b44e8452bfa57fbfa23c5fe7683';var s=document.getElementsByTagName("script")[0];s.parentNode.insertBefore(hm, s);})();
18
+ </script><script async src="https://www.googletagmanager.com/gtag/js?id=UA-150860401-1"></script><script>window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);};gtag('js',new Date());gtag('config','UA-150860401-1');
19
+ </script><script type="text/javascript" src="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/js/fixedPage.min.js"></script><meta name="generator" content="Hexo 6.3.0"></head><body><div class="body_container"><div id="header"><div class="site-name"><a id="logo" href="/.">前端壹菜鸟</a><p class="description">关注前端知识,收集精彩博文,做技术的搬运工</p></div><div id="nav-menu"><a class="current" href="/." target="_self"><i class="fa fa-home"> 首页</i></a><a href="/archives/" target="_self"><i class="fa fa-archive"> 归档</i></a><a href="/guestbook/" target="_self"><i class="fa fa-comments"> 留言</i></a><a href="/about/" target="_self"><i class="fa fa-user"> 关于</i></a><a href="https://laonongmin.online/" target="_target"><i class="fa fa-group"> 看看</i></a><a href="https://love.xuehuayu.cn/" target="_target"><i class="fa fa-heart"> LOVE</i></a></div></div><div class="pure-g" id="layout"><div class="pure-u-1 pure-u-md-3-4"><div class="content_container"><div class="post"><h1 class="post-title c-post">前端工程化 - 剖析npm的包管理机制</h1><div class="post-meta"><span class="date">2019-12-18</span><span> | </span><span class="tag"><a href="/tags/%E8%BD%AC%E8%BD%BD/">转载 </a></span><span> | </span><span class="category"><a href="/categories/%E5%89%8D%E7%AB%AF/">前端 </a></span><span id="busuanzi_container_page_pv"> | <span id="busuanzi_value_page_pv"><i class="fa fa-spinner"></i></span><span> 阅读</span></span><span class="post-time"><span class="post-meta-item-text"> | </span><span class="post-meta-item-icon"><i class="fa fa-keyboard-o"></i><span class="post-count"> 11.2k</span><span class="post-meta-item-text"> 字</span></span></span><span class="post-time"> | <span class="post-meta-item-icon"><i class="fa fa-hourglass-half"></i><span class="post-count"> 44</span><span class="post-meta-item-text"> 分钟</span></span></span></div><a class="disqus-comment-count" href="/article/37102.html#vcomment"><span class="waline-comment-count" data-path="/article/37102.html"></span><span> 条评论</span></a><div class="post-content"><p><code>原文地址:https://juejin.im/post/5df789066fb9a0161f30580c</code></p>
20
+ <p>现如今,前端开发的同学已经离不开 <code>npm</code> 这个包管理工具,其优秀的包版本管理机制承载了整个繁荣发展的<code>NodeJS</code>社区,理解其内部机制非常有利于加深我们对模块开发的理解、各项前端工程化的配置以加快我们排查问题(相信不少同学收到过各种依赖问题的困扰)的速度。</p>
21
+ <p>本文从三个角度:<code>package.json</code>、版本管理、依赖安装结合具体实例对 <code>npm</code> 的包管理机制进行了详细分析。</p>
22
+ <span id="more"></span>
23
+
24
+ <h2 id="一、剖析-package-json"><a href="#一、剖析-package-json" class="headerlink" title="一、剖析 package.json"></a>一、剖析 package.json</h2><p><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef28758e8c0?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>在 <code>Node.js</code> 中,模块是一个库或框架,也是一个 <code>Node.js</code> 项目。<code>Node.js</code> 项目遵循模块化的架构,当我们创建了一个 <code>Node.js</code> 项目,意味着创建了一个模块,这个模块必须有一个描述文件,即 <code>package.json</code>。它是我们最常见的配置文件,但是它里面的配置你真的有详细了解过吗?配置一个合理的 <code>package.json</code> 文件直接决定着我们项目的质量,所以首先带大家分析下 <code>package.json</code> 的各项详细配置。</p>
25
+ <h3 id="1-1-必备属性"><a href="#1-1-必备属性" class="headerlink" title="1.1 必备属性"></a>1.1 必备属性</h3><p><code>package.json</code> 中有非常多的属性,其中必须填写的只有两个:<code>name</code> 和 <code>version</code> ,这两个属性组成一个 <code>npm</code> 模块的唯一标识。</p>
26
+ <h4 id="npm包命名规则"><a href="#npm包命名规则" class="headerlink" title="npm包命名规则"></a>npm包命名规则</h4><p><code>name</code> 即模块名称,其命名时需要遵循官方的一些规范和建议:</p>
27
+ <ul>
28
+ <li><p>包名会成为模块<code>url</code>、命令行中的一个参数或者一个文件夹名称,任何非<code>url</code>安全的字符在包名中都不能使用,可以使用 <code>validate-npm-package-name</code> 包来检测包名是否合法。</p>
29
+ </li>
30
+ <li><p>语义化包名,可以帮助开发者更快的找到需要的包,并且避免意外获取错误的包。</p>
31
+ </li>
32
+ <li><p>若包名称中存在一些符号,将符号去除后不得与现有包名重复</p>
33
+ </li>
34
+ </ul>
35
+ <p>例如:由于<code>react-native</code>已经存在,<code>react.native</code>、<code>reactnative</code>都不可以再创建。</p>
36
+ <ul>
37
+ <li>如果你的包名与现有的包名太相近导致你不能发布这个包,那么推荐将这个包发布到你的作用域下。</li>
38
+ </ul>
39
+ <p>例如:用户名 <code>conard</code>,那么作用域为 <code>@conard</code>,发布的包可以是<code>@conard/react</code>。</p>
40
+ <h4 id="查看包是否被占用"><a href="#查看包是否被占用" class="headerlink" title="查看包是否被占用"></a>查看包是否被占用</h4><p><code>name</code> 是一个包的唯一标识,不得和其他包名重复,我们可以执行 <code>npm view packageName</code> 查看包是否被占用,并可以查看它的一些基本信息:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef28368aba9?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>若包名称从未被使用过,则会抛出 <code>404</code> 错误:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef28544db20?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>另外,你还可以去 <code>https://www.npmjs.com/</code> 查询更多更详细的包信息。</p>
41
+ <h3 id="1-2描述信息"><a href="#1-2描述信息" class="headerlink" title="1.2描述信息"></a>1.2描述信息</h3><h4 id="基本描述"><a href="#基本描述" class="headerlink" title="基本描述"></a>基本描述</h4><pre><code>&#123;
42
+ &quot;description&quot;: &quot;An enterprise-class UI design language and React components implementation&quot;,
43
+ &quot;keywords&quot;: [
44
+ &quot;ant&quot;,
45
+ &quot;component&quot;,
46
+ &quot;components&quot;,
47
+ &quot;design&quot;,
48
+ &quot;framework&quot;,
49
+ &quot;frontend&quot;,
50
+ &quot;react&quot;,
51
+ &quot;react-component&quot;,
52
+ &quot;ui&quot;
53
+ ]
54
+ &#125;
55
+ 复制代码
56
+ </code></pre>
57
+ <p><code>description</code>用于添加模块的的描述信息,方便别人了解你的模块。</p>
58
+ <p><code>keywords</code>用于给你的模块添加关键字。</p>
59
+ <p>当然,他们的还有一个非常重要的作用,就是利于模块检索。当你使用 <code>npm search</code> 检索模块时,会到<code>description</code> 和 <code>keywords</code> 中进行匹配。写好 <code>description</code> 和 <code>keywords</code> 有利于你的模块获得更多更精准的曝光:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef2871aa774?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
60
+ <h4 id="开发人员"><a href="#开发人员" class="headerlink" title="开发人员"></a>开发人员</h4><p>描述开发人员的字段有两个:<code>author</code> 和 <code>contributors</code>, <code>author</code> 指包的主要作者,一个 <code>author</code> 对应一个人。 <code>contributors</code> 指贡献者信息,一个 <code>contributors</code> 对应多个贡献者,值为数组,对人的描述可以是一个字符串,也可以是下面的结构:</p>
61
+ <pre><code>&#123;
62
+ &quot;name&quot; : &quot;ConardLi&quot;,
63
+ &quot;email&quot; : &quot;lisqPersion@163.com&quot;,
64
+ &quot;url&quot; : &quot;https://github.com/ConardLi&quot;
65
+ &#125;
66
+ 复制代码
67
+ </code></pre>
68
+ <h4 id="地址"><a href="#地址" class="headerlink" title="地址"></a>地址</h4><pre><code>&#123;
69
+ &quot;homepage&quot;: &quot;http://ant.design/&quot;,
70
+ &quot;bugs&quot;: &#123;
71
+ &quot;url&quot;: &quot;https://github.com/ant-design/ant-design/issues&quot;
72
+ &#125;,
73
+ &quot;repository&quot;: &#123;
74
+ &quot;type&quot;: &quot;git&quot;,
75
+ &quot;url&quot;: &quot;https://github.com/ant-design/ant-design&quot;
76
+ &#125;,
77
+ &#125;
78
+ 复制代码
79
+ </code></pre>
80
+ <p><code>homepage</code> 用于指定该模块的主页。</p>
81
+ <p><code>repository</code> 用于指定模块的代码仓库。<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef285e805c7?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br><code>bugs</code> 指定一个地址或者一个邮箱,对你的模块存在疑问的人可以到这里提出问题。</p>
82
+ <h3 id="1-3-依赖配置"><a href="#1-3-依赖配置" class="headerlink" title="1.3 依赖配置"></a>1.3 依赖配置</h3><p>我们的项目可能依赖一个或多个外部依赖包,根据依赖包的不同用途,我们将他们配置在下面几个属性下:<code>dependencies、devDependencies、peerDependencies、bundledDependencies、optionalDependencies</code>。</p>
83
+ <h4 id="配置规则"><a href="#配置规则" class="headerlink" title="配置规则"></a>配置规则</h4><p>在介绍几种依赖配置之前,首先我们来看一下依赖的配置规则,你看到的依赖包配置可能是下面这样的:</p>
84
+ <pre><code> &quot;dependencies&quot;: &#123;
85
+ &quot;antd&quot;: &quot;ant-design/ant-design#4.0.0-alpha.8&quot;,
86
+ &quot;axios&quot;: &quot;^1.2.0&quot;,
87
+ &quot;test-js&quot;: &quot;file:../test&quot;,
88
+ &quot;test2-js&quot;: &quot;http://cdn.com/test2-js.tar.gz&quot;,
89
+ &quot;core-js&quot;: &quot;^1.1.5&quot;,
90
+ &#125;
91
+ 复制代码
92
+ </code></pre>
93
+ <p>依赖配置遵循下面几种配置规则:</p>
94
+ <ul>
95
+ <li><p><code>依赖包名称:VERSION</code></p>
96
+ </li>
97
+ <li><p><code>VERSION</code>是一个遵循<code>SemVer</code>规范的版本号配置,<code>npm install</code> 时将到npm服务器下载符合指定版本范围的包。</p>
98
+ </li>
99
+ <li><p><code>依赖包名称:DWONLOAD_URL</code></p>
100
+ </li>
101
+ <li><p><code>DWONLOAD_URL</code> 是一个可下载的<code>tarball</code>压缩包地址,模块安装时会将这个<code>.tar</code>下载并安装到本地。</p>
102
+ </li>
103
+ <li><p><code>依赖包名称:LOCAL_PATH</code></p>
104
+ </li>
105
+ <li><p><code>LOCAL_PATH</code> 是一个本地的依赖包路径,例如 <code>file:../pacakges/pkgName</code>。适用于你在本地测试一个<code>npm</code>包,不应该将这种方法应用于线上。</p>
106
+ </li>
107
+ <li><p><code>依赖包名称:GITHUB_URL</code></p>
108
+ </li>
109
+ <li><p><code>GITHUB_URL</code> 即 <code>github</code> 的 <code>username/modulename</code> 的写法,例如:<code>ant-design/ant-design</code>,你还可以在后面指定 <code>tag</code> 和 <code>commit id</code>。</p>
110
+ </li>
111
+ <li><p><code>依赖包名称:GIT_URL</code></p>
112
+ </li>
113
+ <li><p><code>GIT_URL</code> 即我们平时clone代码库的 <code>git url</code>,其遵循以下形式:</p>
114
+ <p> <protocol>:&#x2F;&#x2F;[<user>[:<password>]@]<hostname>[:<port>][:][&#x2F;]<path>[#<commit-ish> | #semver:<semver>]复制代码</p>
115
+ </li>
116
+ </ul>
117
+ <p>其中 <code>protocal</code> 可以是以下几种形式:</p>
118
+ <ul>
119
+ <li><code>git://github.com/user/project.git#commit-ish</code></li>
120
+ <li><code>git+ssh://user@hostname:project.git#commit-ish</code></li>
121
+ <li><code>git+ssh://user@hostname/project.git#commit-ish</code></li>
122
+ <li><code>git+http://user@hostname/project/blah.git#commit-ish</code></li>
123
+ <li><code>git+https://user@hostname/project/blah.git#commit-ish</code></li>
124
+ </ul>
125
+ <h4 id="dependencies"><a href="#dependencies" class="headerlink" title="dependencies"></a>dependencies</h4><p><code>dependencies</code> 指定了项目运行所依赖的模块,开发环境和生产环境的依赖模块都可以配置到这里,例如</p>
126
+ <pre><code> &quot;dependencies&quot;: &#123;
127
+ &quot;lodash&quot;: &quot;^4.17.13&quot;,
128
+ &quot;moment&quot;: &quot;^2.24.0&quot;,
129
+ &#125;
130
+ 复制代码
131
+ </code></pre>
132
+ <h4 id="devDependencies"><a href="#devDependencies" class="headerlink" title="devDependencies"></a>devDependencies</h4><p>有一些包有可能你只是在开发环境中用到,例如你用于检测代码规范的 <code>eslint</code> ,用于进行测试的 <code>jest</code> ,用户使用你的包时即使不安装这些依赖也可以正常运行,反而安装他们会耗费更多的时间和资源,所以你可以把这些依赖添加到 <code>devDependencies</code> 中,这些依赖照样会在你本地进行 <code>npm install</code> 时被安装和管理,但是不会被安装到生产环境:</p>
133
+ <pre><code> &quot;devDependencies&quot;: &#123;
134
+ &quot;jest&quot;: &quot;^24.3.1&quot;,
135
+ &quot;eslint&quot;: &quot;^6.1.0&quot;,
136
+ &#125;
137
+ 复制代码
138
+ </code></pre>
139
+ <h4 id="peerDependencies"><a href="#peerDependencies" class="headerlink" title="peerDependencies"></a>peerDependencies</h4><p><code>peerDependencies</code> 用于指定你正在开发的模块所依赖的版本以及用户安装的依赖包版本的兼容性。</p>
140
+ <p>上面的说法可能有点太抽象,我们直接拿 <code>ant-design</code> 来举个例子,<code>ant-design</code> 的 <code>package.json</code> 中有如下配置:</p>
141
+ <pre><code> &quot;peerDependencies&quot;: &#123;
142
+ &quot;react&quot;: &quot;&gt;=16.0.0&quot;,
143
+ &quot;react-dom&quot;: &quot;&gt;=16.0.0&quot;
144
+ &#125;
145
+ 复制代码
146
+ </code></pre>
147
+ <p>当你正在开发一个系统,使用了 <code>ant-design</code> ,所以也肯定需要依赖 <code>React</code>。同时, <code>ant-design</code> 也是需要依赖 <code>React</code> 的,它要保持稳定运行所需要的 <code>React</code> 版本是<code>16.0.0</code>,而你开发时依赖的 <code>React</code> 版本是 <code>15.x</code>:</p>
148
+ <p>这时,<code>ant-design</code> 要使用 <code>React</code>,并将其引入:</p>
149
+ <pre><code>import * as React from&#39;react&#39;;
150
+ import * as ReactDOM from&#39;react-dom&#39;;
151
+ 复制代码
152
+ </code></pre>
153
+ <p>这时取到的是宿主环境也就是你的环境中的 <code>React</code> 版本,这就可能造成一些问题。在 <code>npm2</code> 的时候,指定上面的 <code>peerDependencies</code> 将意味着强制宿主环境安装 <code>react@&gt;=16.0.0和react-dom@&gt;=16.0.0</code> 的版本。</p>
154
+ <p><code>npm3</code> 以后不会再要求 <code>peerDependencies</code> 所指定的依赖包被强制安装,相反 <code>npm3</code> 会在安装结束后检查本次安装是否正确,如果不正确会给用户打印警告提示。</p>
155
+ <pre><code> &quot;dependencies&quot;: &#123;
156
+ &quot;react&quot;: &quot;15.6.0&quot;,
157
+ &quot;antd&quot;: &quot;^3.22.0&quot;
158
+ &#125;
159
+ 复制代码
160
+ </code></pre>
161
+ <p>例如,我在项目中依赖了 <code>antd</code> 的最新版本,然后依赖了 <code>react</code> 的 <code>15.6.0</code>版本,在进行依赖安装时将给出以下警告:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef2b2a3e5c4?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
162
+ <h4 id="optionalDependencies"><a href="#optionalDependencies" class="headerlink" title="optionalDependencies"></a>optionalDependencies</h4><p>某些场景下,依赖包可能不是强依赖的,这个依赖包的功能可有可无,当这个依赖包无法被获取到时,你希望 <code>npm install</code> 继续运行,而不会导致失败,你可以将这个依赖放到 <code>optionalDependencies</code> 中,注意 <code>optionalDependencies</code> 中的配置将会覆盖掉 <code>dependencies</code> 所以只需在一个地方进行配置。</p>
163
+ <p>当然,引用 <code>optionalDependencies</code> 中安装的依赖时,一定要做好异常处理,否则在模块获取不到时会导致报错。</p>
164
+ <h4 id="bundledDependencies"><a href="#bundledDependencies" class="headerlink" title="bundledDependencies"></a>bundledDependencies</h4><p>和以上几个不同,<code>bundledDependencies</code> 的值是一个数组,数组里可以指定一些模块,这些模块将在这个包发布时被一起打包。</p>
165
+ <pre><code> &quot;bundledDependencies&quot;: [&quot;package1&quot; , &quot;package2&quot;]
166
+ 复制代码
167
+ </code></pre>
168
+ <h3 id="1-4-协议"><a href="#1-4-协议" class="headerlink" title="1.4 协议"></a>1.4 协议</h3><pre><code>&#123;
169
+ &quot;license&quot;: &quot;MIT&quot;
170
+ &#125;
171
+ 复制代码
172
+ </code></pre>
173
+ <p><code>license</code> 字段用于指定软件的开源协议,开源协议里面详尽表述了其他人获得你代码后拥有的权利,可以对你的的代码进行何种操作,何种操作又是被禁止的。同一款协议有很多变种,协议太宽松会导致作者丧失对作品的很多权利,太严格又不便于使用者使用及作品的传播,所以开源作者要考虑自己对作品想保留哪些权利,放开哪些限制。</p>
174
+ <blockquote>
175
+ <p>软件协议可分为开源和商业两类,对于商业协议,或者叫法律声明、许可协议,每个软件会有自己的一套行文,由软件作者或专门律师撰写,对于大多数人来说不必自己花时间和精力去写繁长的许可协议,选择一份广为流传的开源协议就是个不错的选择。</p>
176
+ </blockquote>
177
+ <p>以下就是几种主流的开源协议:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef2b7fb2674?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
178
+ <ul>
179
+ <li><code>MIT</code>:只要用户在项目副本中包含了版权声明和许可声明,他们就可以拿你的代码做任何想做的事情,你也无需承担任何责任。</li>
180
+ <li><code>Apache</code>:类似于 <code>MIT</code>,同时还包含了贡献者向用户提供专利授权相关的条款。</li>
181
+ <li><code>GPL</code>:修改项目代码的用户再次分发源码或二进制代码时,必须公布他的相关修改。</li>
182
+ </ul>
183
+ <p>如果你对开源协议有更详细的要求,可以到 <a target="_blank" rel="noopener" href="https://choosealicense.com/">choosealicense.com&#x2F;</a> 获取更详细的开源协议说明。<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef2b2f744d8?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
184
+ <h3 id="1-5-目录、文件相关"><a href="#1-5-目录、文件相关" class="headerlink" title="1.5 目录、文件相关"></a>1.5 目录、文件相关</h3><h4 id="程序入口"><a href="#程序入口" class="headerlink" title="程序入口"></a>程序入口</h4><pre><code>&#123;
185
+ &quot;main&quot;: &quot;lib/index.js&quot;,
186
+ &#125;
187
+ 复制代码
188
+ </code></pre>
189
+ <p><code>main</code> 属性可以指定程序的主入口文件,例如,上面 <code>antd</code> 指定的模块入口 <code>lib/index.js</code> ,当我们在代码用引入 <code>antd</code> 时:<code>import &#123; notification &#125; from &#39;antd&#39;;</code> 实际上引入的就是 <code>lib/index.js</code> 中暴露出去的模块。<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef2bd8639b2?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
190
+ <h4 id="命令行工具入口"><a href="#命令行工具入口" class="headerlink" title="命令行工具入口"></a>命令行工具入口</h4><p>当你的模块是一个命令行工具时,你需要为命令行工具指定一个入口,即指定你的命令名称和本地可指定文件的对应关系。如果是全局安装,npm 将会使用符号链接把可执行文件链接到 <code>/usr/local/bin</code>,如果是本地安装,会链接到 <code>./node_modules/.bin/</code>。</p>
191
+ <pre><code>&#123;
192
+ &quot;bin&quot;: &#123;
193
+ &quot;conard&quot;: &quot;./bin/index.js&quot;
194
+ &#125;
195
+ &#125;
196
+ 复制代码
197
+ </code></pre>
198
+ <p>例如上面的配置:当你的包安装到全局时:<code>npm</code> 会在 <code>/usr/local/bin</code>下创建一个以 <code>conard</code> 为名字的软链接,指向全局安装下来的 <code>conard</code> 包下面的 <code>&quot;./bin/index.js&quot;</code>。这时你在命令行执行 <code>conard</code> 则会调用链接到的这个js文件。</p>
199
+ <blockquote>
200
+ <p>这里不再过多展开,更多内容在我后续的命令行工具文章中会进行详细讲解。</p>
201
+ </blockquote>
202
+ <h4 id="发布文件配置"><a href="#发布文件配置" class="headerlink" title="发布文件配置"></a>发布文件配置</h4><pre><code>&#123;
203
+ &quot;files&quot;: [
204
+ &quot;dist&quot;,
205
+ &quot;lib&quot;,
206
+ &quot;es&quot;
207
+ ]
208
+ &#125;
209
+ 复制代码
210
+ </code></pre>
211
+ <p><code>files</code> 属性用于描述你 <code>npm publish</code> 后推送到 <code>npm</code> 服务器的文件列表,如果指定文件夹,则文件夹内的所有内容都会包含进来。我们可以看到下载后的包是下面的目录结构:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef2d5915089?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
212
+ <blockquote>
213
+ <p>另外,你还可以通过配置一个 <code>.npmignore</code> 文件来排除一些文件, 防止大量的垃圾文件推送到 <code>npm</code>, 规则上和你用的 <code>.gitignore</code> 是一样的。<code>.gitignore</code> 文件也可以充当<code>.npmignore</code> 文件。</p>
214
+ </blockquote>
215
+ <h4 id="man"><a href="#man" class="headerlink" title="man"></a>man</h4><p><code>man</code> 命令是 <code>Linux</code> 下的帮助指令,通过 <code>man</code> 指令可以查看 <code>Linux</code> 中的指令帮助、配置文件帮助和编程帮助等信息。</p>
216
+ <p>如果你的 <code>node.js</code> 模块是一个全局的命令行工具,在 <code>package.json</code> 通过 <code>man</code> 属性可以指定 <code>man</code> 命令查找的文档地址。</p>
217
+ <p><code>man</code> 文件必须以数字结尾,或者如果被压缩了,以 <code>.gz</code> 结尾。数字表示文件将被安装到 <code>man</code> 的哪个部分。如果 <code>man</code> 文件名称不是以模块名称开头的,安装的时候会给加上模块名称前缀。</p>
218
+ <p>例如下面这段配置:</p>
219
+ <pre><code>&#123;
220
+ &quot;man&quot; : [
221
+ &quot;/Users/isaacs/dev/npm/cli/man/man1/npm-access.1&quot;,
222
+ &quot;/Users/isaacs/dev/npm/cli/man/man1/npm-audit.1&quot;
223
+ ]
224
+ &#125;
225
+ 复制代码
226
+ </code></pre>
227
+ <p>在命令行输入 <code>man npm-audit</code> :<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef2c221c0cd?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
228
+ <h4 id="规范项目目录"><a href="#规范项目目录" class="headerlink" title="规范项目目录"></a>规范项目目录</h4><p>一个 <code>node.js</code> 模块是基于 <code>CommonJS</code> 模块化规范实现的,严格按照 <code>CommonJS</code> 规范,模块目录下除了必须包含包描述文件 <code>package.json</code> 以外,还需要包含以下目录:</p>
229
+ <ul>
230
+ <li><code>bin</code>:存放可执行二进制文件的目录</li>
231
+ <li><code>lib</code>:存放js代码的目录</li>
232
+ <li><code>doc</code>:存放文档的目录</li>
233
+ <li><code>test</code>:存放单元测试用例代码的目录</li>
234
+ <li>…</li>
235
+ </ul>
236
+ <p>在模块目录中你可能没有严格按照以上结构组织或命名,你可以通过在 <code>package.json</code> 指定 <code>directories</code> 属性来指定你的目录结构和上述的规范结构的对应情况。除此之外 <code>directories</code> 属性暂时没有其他应用。</p>
237
+ <pre><code>&#123;
238
+ &quot;directories&quot;: &#123;
239
+ &quot;lib&quot;: &quot;src/lib/&quot;,
240
+ &quot;bin&quot;: &quot;src/bin/&quot;,
241
+ &quot;man&quot;: &quot;src/man/&quot;,
242
+ &quot;doc&quot;: &quot;src/doc/&quot;,
243
+ &quot;example&quot;: &quot;src/example/&quot;
244
+ &#125;
245
+ &#125;
246
+ 复制代码
247
+ </code></pre>
248
+ <blockquote>
249
+ <p>不过官方文档表示,虽然目前这个属性没有什么重要作用,未来可能会整出一些花样出来,例如:doc 中存放的 markdown 文件、example 中存放的示例文件,可能会友好的展示出来。</p>
250
+ </blockquote>
251
+ <h3 id="1-6-脚本配置"><a href="#1-6-脚本配置" class="headerlink" title="1.6 脚本配置"></a>1.6 脚本配置</h3><h4 id="script"><a href="#script" class="headerlink" title="script"></a>script</h4><pre><code>&#123;
252
+ &quot;scripts&quot;: &#123;
253
+ &quot;test&quot;: &quot;jest --config .jest.js --no-cache&quot;,
254
+ &quot;dist&quot;: &quot;antd-tools run dist&quot;,
255
+ &quot;compile&quot;: &quot;antd-tools run compile&quot;,
256
+ &quot;build&quot;: &quot;npm run compile &amp;&amp; npm run dist&quot;
257
+ &#125;
258
+ &#125;
259
+ 复制代码
260
+ </code></pre>
261
+ <p><code>scripts</code> 用于配置一些脚本命令的缩写,各个脚本可以互相组合使用,这些脚本可以覆盖整个项目的生命周期,配置后可使用 <code>npm run command</code> 进行调用。如果是 <code>npm</code> 关键字,则可以直接调用。例如,上面的配置制定了以下几个命令:<code>npm run test</code>、<code>npm run dist</code>、<code>npm run compile</code>、<code>npm run build</code>。</p>
262
+ <h4 id="config"><a href="#config" class="headerlink" title="config"></a>config</h4><p><code>config</code> 字段用于配置脚本中使用的环境变量,例如下面的配置,可以在脚本中使用<code>process.env.npm_package_config_port</code>进行获取。</p>
263
+ <pre><code>&#123;
264
+ &quot;config&quot; : &#123; &quot;port&quot; : &quot;8080&quot; &#125;
265
+ &#125;
266
+ 复制代码
267
+ </code></pre>
268
+ <h3 id="1-7-发布配置"><a href="#1-7-发布配置" class="headerlink" title="1.7 发布配置"></a>1.7 发布配置</h3><h4 id="preferGlobal"><a href="#preferGlobal" class="headerlink" title="preferGlobal"></a>preferGlobal</h4><p>如果你的 <code>node.js</code> 模块主要用于安装到全局的命令行工具,那么该值设置为 <code>true</code> ,当用户将该模块安装到本地时,将得到一个警告。这个配置并不会阻止用户安装,而是会提示用户防止错误使用而引发一些问题。</p>
269
+ <h4 id="private"><a href="#private" class="headerlink" title="private"></a>private</h4><p>如果将 <code>private</code> 属性设置为 <code>true</code>,npm将拒绝发布它,这是为了防止一个私有模块被无意间发布出去。<br>![xuehuayu.cn](data:image&#x2F;svg+xml;utf8,<?xml version=&quot;1.0&quot;?><svg xmlns=&quot;http://www.w3.org/2000/svg&quot; version=&quot;1.1&quot; width=&quot;1088&quot; height=&quot;318&quot;></svg>)</p>
270
+ <h4 id="publishConfig"><a href="#publishConfig" class="headerlink" title="publishConfig"></a>publishConfig</h4><pre><code> &quot;publishConfig&quot;: &#123;
271
+ &quot;registry&quot;: &quot;https://registry.npmjs.org/&quot;
272
+ &#125;,
273
+ 复制代码
274
+ </code></pre>
275
+ <p>发布模块时更详细的配置,例如你可以配置只发布某个 <code>tag</code>、配置发布到的私有 <code>npm</code> 源。更详细的配置可以参考 <a target="_blank" rel="noopener" href="http://caibaojian.com/npm/misc/config.html">npm-config</a></p>
276
+ <h4 id="os"><a href="#os" class="headerlink" title="os"></a>os</h4><p>假如你开发了一个模块,只能跑在 <code>darwin</code> 系统下,你需要保证 <code>windows</code> 用户不会安装到你的模块,从而避免发生不必要的错误。</p>
277
+ <p>使用 <code>os</code> 属性可以帮助你完成以上的需求,你可以指定你的模块只能被安装在某些系统下,或者指定一个不能安装的系统黑名单:</p>
278
+ <pre><code>&quot;os&quot; : [ &quot;darwin&quot;, &quot;linux&quot; ]
279
+ &quot;os&quot; : [ &quot;!win32&quot; ]
280
+ 复制代码
281
+ </code></pre>
282
+ <p>例如,我把一个测试模块指定一个系统黑名单:<code>&quot;os&quot; : [ &quot;!darwin&quot; ]</code>,当我在此系统下安装它时会爆出如下错误:<br>![xuehuayu.cn](data:image&#x2F;svg+xml;utf8,<?xml version=&quot;1.0&quot;?><svg xmlns=&quot;http://www.w3.org/2000/svg&quot; version=&quot;1.1&quot; width=&quot;969&quot; height=&quot;137&quot;></svg>)</p>
283
+ <blockquote>
284
+ <p>在node环境下可以使用 process.platform 来判断操作系统。</p>
285
+ </blockquote>
286
+ <h4 id="cpu"><a href="#cpu" class="headerlink" title="cpu"></a>cpu</h4><p>和上面的 <code>os</code> 类似,我们可以用 <code>cpu</code> 属性更精准的限制用户安装环境:</p>
287
+ <pre><code>&quot;cpu&quot; : [ &quot;x64&quot;, &quot;ia32&quot; ]
288
+ &quot;cpu&quot; : [ &quot;!arm&quot;, &quot;!mips&quot; ]
289
+ 复制代码
290
+ </code></pre>
291
+ <blockquote>
292
+ <p>在node环境下可以使用 process.arch 来判断 cpu 架构。</p>
293
+ </blockquote>
294
+ <h2 id="二、剖析包版本管理机制"><a href="#二、剖析包版本管理机制" class="headerlink" title="二、剖析包版本管理机制"></a>二、剖析包版本管理机制</h2><p><code>Nodejs</code>成功离不开 <code>npm</code> 优秀的依赖管理系统。在介绍整个依赖系统之前,必须要了解 <code>npm</code>如何管理依赖包的版本,本章将介绍 <code>npm包</code> 的版本发布规范、如何管理各种依赖包的版本以及一些关于包版本的最佳实践。<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef301559a8e?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
295
+ <h3 id="2-1-查看npm包版本"><a href="#2-1-查看npm包版本" class="headerlink" title="2.1 查看npm包版本"></a>2.1 查看npm包版本</h3><p>你可以执行 <code>npm view package version</code> 查看某个 <code>package</code> 的最新版本。</p>
296
+ <p>执行 <code>npm view conard versions</code> 查看某个 <code>package</code> 在npm服务器上所有发布过的版本。<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3016562c8?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>执行 <code>npm ls</code> 可查看当前仓库依赖树上所有包的版本信息。<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef302c760fe?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
297
+ <h3 id="2-2-SemVer规范"><a href="#2-2-SemVer规范" class="headerlink" title="2.2 SemVer规范"></a>2.2 SemVer规范</h3><p><code>npm包</code> 中的模块版本都需要遵循 <code>SemVer</code>规范——由 <code>Github</code> 起草的一个具有指导意义的,统一的版本号表示规则。实际上就是 <code>Semantic Version</code>(语义化版本)的缩写。</p>
298
+ <blockquote>
299
+ <p>SemVer规范官网: <a target="_blank" rel="noopener" href="https://semver.org/">semver.org&#x2F;</a></p>
300
+ </blockquote>
301
+ <h4 id="标准版本"><a href="#标准版本" class="headerlink" title="标准版本"></a>标准版本</h4><p><code>SemVer</code>规范的标准版本号采用 <code>X.Y.Z</code> 的格式,其中 X、Y 和 Z 为非负的整数,且禁止在数字前方补零。X 是主版本号、Y 是次版本号、而 Z 为修订号。每个元素必须以数值来递增。</p>
302
+ <ul>
303
+ <li>主版本号(<code>major</code>):当你做了不兼容的API 修改</li>
304
+ <li>次版本号(<code>minor</code>):当你做了向下兼容的功能性新增</li>
305
+ <li>修订号(<code>patch</code>):当你做了向下兼容的问题修正。</li>
306
+ </ul>
307
+ <p>例如:<code>1.9.1 -&gt; 1.10.0 -&gt; 1.11.0</code></p>
308
+ <h4 id="先行版本"><a href="#先行版本" class="headerlink" title="先行版本"></a>先行版本</h4><p>当某个版本改动比较大、并非稳定而且可能无法满足预期的兼容性需求时,你可能要先发布一个先行版本。</p>
309
+ <p>先行版本号可以加到“主版本号.次版本号.修订号”的后面,先加上一个连接号再加上一连串以句点分隔的标识符和版本编译信息。</p>
310
+ <ul>
311
+ <li>内部版本(<code>alpha</code>):</li>
312
+ <li>公测版本(<code>beta</code>):</li>
313
+ <li>正式版本的候选版本<code>rc</code>: 即 <code>Release candiate</code></li>
314
+ </ul>
315
+ <h4 id="React的版本"><a href="#React的版本" class="headerlink" title="React的版本"></a>React的版本</h4><p>下面我们来看看 <code>React</code> 的历史版本:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3224e9335?imageslim" alt="xuehuayu.cn"><br>可见是严格按照 <code>SemVer</code> 规范来发版的:</p>
316
+ <ul>
317
+ <li>版本号严格按照 <code>主版本号.次版本号.修订号</code> 格式命名</li>
318
+ <li>版本是严格递增的,:<code>16.8.0 -&gt; 16.8.1 -&gt; 16.8.2</code></li>
319
+ <li>发布重大版本或版本改动较大时,先发布<code>alpha</code>、<code>beta</code>、<code>rc</code>等先行版本</li>
320
+ </ul>
321
+ <h4 id="发布版本"><a href="#发布版本" class="headerlink" title="发布版本"></a>发布版本</h4><p>在修改 <code>npm</code> 包某些功能后通常需要发布一个新的版本,我们通常的做法是直接去修改 <code>package.json</code> 到指定版本。如果操作失误,很容易造成版本号混乱,我们可以借助符合 <code>Semver</code> 规范的命令来完成这一操作:</p>
322
+ <ul>
323
+ <li><code>npm version patch</code> : 升级修订版本号</li>
324
+ <li><code>npm version minor</code> : 升级次版本号</li>
325
+ <li><code>npm version major</code> : 升级主版本号</li>
326
+ </ul>
327
+ <h3 id="2-3-版本工具使用"><a href="#2-3-版本工具使用" class="headerlink" title="2.3 版本工具使用"></a>2.3 版本工具使用</h3><p>在开发中肯定少不了对一些版本号的操作,如果这些版本号符合 <code>SemVer</code>规范 ,我们可以借助用于操作版本的npm包<code>semver</code>来帮助我们进行比较版本大小、提取版本信息等操作。</p>
328
+ <blockquote>
329
+ <p>Npm 也使用了该工具来处理版本相关的工作。</p>
330
+ </blockquote>
331
+ <pre><code>npm install semver
332
+ 复制代码
333
+ </code></pre>
334
+ <ul>
335
+ <li><p>比较版本号大小</p>
336
+ <p> semver.gt(‘1.2.3’, ‘9.8.7’) &#x2F;&#x2F; false<br> semver.lt(‘1.2.3’, ‘9.8.7’) &#x2F;&#x2F; true复制代码</p>
337
+ </li>
338
+ <li><p>判断版本号是否符合规范,返回解析后符合规范的版本号。</p>
339
+ <p> semver.valid(‘1.2.3’) &#x2F;&#x2F; ‘1.2.3’<br> semver.valid(‘a.b.c’) &#x2F;&#x2F; null复制代码</p>
340
+ </li>
341
+ <li><p>将其他版本号强制转换成semver版本号</p>
342
+ <p> semver.valid(semver.coerce(‘v2’)) &#x2F;&#x2F; ‘2.0.0’<br> semver.valid(semver.coerce(‘42.6.7.9.3-alpha’)) &#x2F;&#x2F; ‘42.6.7’复制代码</p>
343
+ </li>
344
+ <li><p>一些其他用法</p>
345
+ <p> semver.clean(‘ &#x3D;v1.2.3 ‘) &#x2F;&#x2F; ‘1.2.3’<br> semver.satisfies(‘1.2.3’, ‘1.x || &gt;&#x3D;2.5.0 || 5.0.0 - 7.2.3’) &#x2F;&#x2F; true<br> semver.minVersion(‘&gt;&#x3D;1.0.0’) &#x2F;&#x2F; ‘1.0.0’复制代码</p>
346
+ </li>
347
+ </ul>
348
+ <p>以上都是semver最常见的用法,更多详细内容可以查看 semver文档:<a target="_blank" rel="noopener" href="https://github.com/npm/node-semver">github.com&#x2F;npm&#x2F;node-se…</a></p>
349
+ <h3 id="2-4-依赖版本管理"><a href="#2-4-依赖版本管理" class="headerlink" title="2.4 依赖版本管理"></a>2.4 依赖版本管理</h3><p>我们经常看到,在 <code>package.json</code> 中各种依赖的不同写法:</p>
350
+ <pre><code> &quot;dependencies&quot;: &#123;
351
+ &quot;signale&quot;: &quot;1.4.0&quot;,
352
+ &quot;figlet&quot;: &quot;*&quot;,
353
+ &quot;react&quot;: &quot;16.x&quot;,
354
+ &quot;table&quot;: &quot;~5.4.6&quot;,
355
+ &quot;yargs&quot;: &quot;^14.0.0&quot;
356
+ &#125;
357
+ 复制代码
358
+ </code></pre>
359
+ <p>前面三个很容易理解:</p>
360
+ <ul>
361
+ <li><code>&quot;signale&quot;: &quot;1.4.0&quot;</code>: 固定版本号</li>
362
+ <li><code>&quot;figlet&quot;: &quot;*&quot;</code>: 任意版本(<code>&gt;=0.0.0</code>)</li>
363
+ <li><code>&quot;react&quot;: &quot;16.x&quot;</code>: 匹配主要版本(<code>&gt;=16.0.0 &lt;17.0.0</code>)</li>
364
+ <li><code>&quot;react&quot;: &quot;16.3.x&quot;</code>: 匹配主要版本和次要版本(<code>&gt;=16.3.0 &lt;16.4.0</code>)</li>
365
+ </ul>
366
+ <p>再来看看后面两个,版本号中引用了 <code>~</code> 和 <code>^</code> 符号:</p>
367
+ <ul>
368
+ <li><code>~</code>: 当安装依赖时获取到有新版本时,安装到 <code>x.y.z</code> 中 <code>z</code> 的最新的版本。即保持主版本号、次版本号不变的情况下,保持修订号的最新版本。</li>
369
+ <li><code>^</code>: 当安装依赖时获取到有新版本时,安装到 <code>x.y.z</code> 中 <code>y</code> 和 <code>z</code> 都为最新版本。 即保持主版本号不变的情况下,保持次版本号、修订版本号为最新版本。</li>
370
+ </ul>
371
+ <p>在 <code>package.json</code> 文件中最常见的应该是 <code>&quot;yargs&quot;: &quot;^14.0.0&quot;</code> 这种格式的 依赖, 因为我们在使用 <code>npm install package</code> 安装包时,<code>npm</code> 默认安装当前最新版本,然后在所安装的版本号前加 <code>^</code> 号。</p>
372
+ <p>注意,当主版本号为 <code>0</code> 的情况,会被认为是一个不稳定版本,情况与上面不同:</p>
373
+ <ul>
374
+ <li>主版本号和次版本号都为 <code>0</code>: <code>^0.0.z</code>、<code>~0.0.z</code> 都被当作固定版本,安装依赖时均不会发生变化。</li>
375
+ <li>主版本号为 <code>0</code>: <code>^0.y.z</code> 表现和 <code>~0.y.z</code> 相同,只保持修订号为最新版本。</li>
376
+ </ul>
377
+ <blockquote>
378
+ <p>1.0.0 的版本号用于界定公共 API。当你的软件发布到了正式环境,或者有稳定的API时,就可以发布1.0.0版本了。所以,当你决定对外部发布一个正式版本的npm包时,把它的版本标为1.0.0。</p>
379
+ </blockquote>
380
+ <h3 id="2-5-锁定依赖版本"><a href="#2-5-锁定依赖版本" class="headerlink" title="2.5 锁定依赖版本"></a>2.5 锁定依赖版本</h3><h4 id="lock文件"><a href="#lock文件" class="headerlink" title="lock文件"></a>lock文件</h4><p>实际开发中,经常会因为各种依赖不一致而产生奇怪的问题,或者在某些场景下,我们不希望依赖被更新,建议在开发中使用 <code>package-lock.json</code>。</p>
381
+ <p>锁定依赖版本意味着在我们不手动执行更新的情况下,每次安装依赖都会安装固定版本。保证整个团队使用版本号一致的依赖。</p>
382
+ <p>每次安装固定版本,无需计算依赖版本范围,大部分场景下能大大加速依赖安装时间。</p>
383
+ <blockquote>
384
+ <p>使用 package-lock.json 要确保npm的版本在5.6以上,因为在5.0 - 5.6中间,对 package-lock.json的处理逻辑进行过几次更新,5.6版本后处理逻辑逐渐稳定。</p>
385
+ </blockquote>
386
+ <p>关于 <code>package-lock.json</code> 详细的结构,我们会在后面的章节进行解析。</p>
387
+ <h4 id="定期更新依赖"><a href="#定期更新依赖" class="headerlink" title="定期更新依赖"></a>定期更新依赖</h4><p>我们的目的是保证团队中使用的依赖一致或者稳定,而不是永远不去更新这些依赖。实际开发场景下,我们虽然不需要每次都去安装新的版本,仍然需要定时去升级依赖版本,来让我们享受依赖包升级带来的问题修复、性能提升、新特性更新。<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef31ae6c1eb?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>使用 <code>npm outdated</code> 可以帮助我们列出有哪些还没有升级到最新版本的依赖:</p>
388
+ <ul>
389
+ <li>黄色表示不符合我们指定的语意化版本范围 - 不需要升级</li>
390
+ <li>红色表示符合指定的语意化版本范围 - 需要升级</li>
391
+ </ul>
392
+ <p>执行 <code>npm update</code> 会升级所有的红色依赖。</p>
393
+ <h3 id="2-6-依赖版本选择的最佳实践"><a href="#2-6-依赖版本选择的最佳实践" class="headerlink" title="2.6 依赖版本选择的最佳实践"></a>2.6 依赖版本选择的最佳实践</h3><h4 id="版本发布"><a href="#版本发布" class="headerlink" title="版本发布"></a>版本发布</h4><ul>
394
+ <li>对外部发布一个正式版本的npm包时,把它的版本标为<code>1.0.0</code>。</li>
395
+ <li>某个包版本发行后,任何修改都必须以新版本发行。</li>
396
+ <li>版本号严格按照 <code>主版本号.次版本号.修订号</code> 格式命名</li>
397
+ <li>版本号发布必须是严格递增的</li>
398
+ <li>发布重大版本或版本改动较大时,先发布<code>alpha、beta、rc</code>等先行版本</li>
399
+ </ul>
400
+ <h4 id="依赖范围选择"><a href="#依赖范围选择" class="headerlink" title="依赖范围选择"></a>依赖范围选择</h4><ul>
401
+ <li>主工程依赖了很多子模块,都是团队成员开发的<code>npm</code>包,此时建议把版本前缀改为<code>~</code>,如果锁定的话每次子依赖更新都要对主工程的依赖进行升级,非常繁琐,如果对子依赖完全信任,直接开启<code>^</code>每次升级到最新版本。</li>
402
+ <li>主工程跑在<code>docker</code>线上,本地还在进行子依赖开发和升级,在<code>docker</code>版本发布前要锁定所有依赖版本,确保本地子依赖发布后线上不会出问题。</li>
403
+ </ul>
404
+ <h4 id="保持依赖一致"><a href="#保持依赖一致" class="headerlink" title="保持依赖一致"></a>保持依赖一致</h4><ul>
405
+ <li>确保<code>npm</code>的版本在<code>5.6</code>以上,确保默认开启 <code>package-lock.json</code> 文件。</li>
406
+ <li>由初始化成员执行 <code>npm inatall</code> 后,将 <code>package-lock.json</code> 提交到远程仓库。不要直接提交 <code>node_modules</code>到远程仓库。</li>
407
+ <li>定期执行 <code>npm update</code> 升级依赖,并提交 <code>lock</code> 文件确保其他成员同步更新依赖,不要手动更改 <code>lock</code> 文件。</li>
408
+ </ul>
409
+ <h4 id="依赖变更"><a href="#依赖变更" class="headerlink" title="依赖变更"></a>依赖变更</h4><ul>
410
+ <li>升级依赖: 修改 <code>package.json</code>文件的依赖版本,执行 <code>npm install</code></li>
411
+ <li>降级依赖: 直接执行 <code>npm install package@version</code>(改动<code>package.json</code>不会对依赖进行降级)</li>
412
+ <li>注意改动依赖后提交<code>lock</code>文件</li>
413
+ </ul>
414
+ <h2 id="三、剖析-npm-install-原理"><a href="#三、剖析-npm-install-原理" class="headerlink" title="三、剖析 npm install 原理"></a>三、剖析 npm install 原理</h2><p><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef327ccaba5?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br><code>npm install</code> 大概会经过上面的几个流程,这一章就来讲一讲各个流程的实现细节、发展以及为何要这样实现。</p>
415
+ <h3 id="3-1-嵌套结构"><a href="#3-1-嵌套结构" class="headerlink" title="3.1 嵌套结构"></a>3.1 嵌套结构</h3><p>我们都知道,执行 <code>npm install</code> 后,依赖包被安装到了 <code>node_modules</code> ,下面我们来具体了解下,<code>npm</code> 将依赖包安装到 <code>node_modules</code> 的具体机制是什么。</p>
416
+ <p>在 <code>npm</code> 的早期版本, <code>npm</code> 处理依赖的方式简单粗暴,以递归的形式,严格按照 <code>package.json</code> 结构以及子依赖包的 <code>package.json</code> 结构将依赖安装到他们各自的 <code>node_modules</code> 中。直到有子依赖包不在依赖其他模块。</p>
417
+ <p>举个例子,我们的模块 <code>my-app</code> 现在依赖了两个模块:<code>buffer</code>、<code>ignore</code>:</p>
418
+ <pre><code>&#123;
419
+ &quot;name&quot;: &quot;my-app&quot;,
420
+ &quot;dependencies&quot;: &#123;
421
+ &quot;buffer&quot;: &quot;^5.4.3&quot;,
422
+ &quot;ignore&quot;: &quot;^5.1.4&quot;,
423
+ &#125;
424
+ &#125;
425
+ 复制代码
426
+ </code></pre>
427
+ <p><code>ignore</code>是一个纯 <code>JS</code> 模块,不依赖任何其他模块,而 <code>buffer</code> 又依赖了下面两个模块:<code>base64-js</code> 、 <code>ieee754</code>。</p>
428
+ <pre><code>&#123;
429
+ &quot;name&quot;: &quot;buffer&quot;,
430
+ &quot;dependencies&quot;: &#123;
431
+ &quot;base64-js&quot;: &quot;^1.0.2&quot;,
432
+ &quot;ieee754&quot;: &quot;^1.1.4&quot;
433
+ &#125;
434
+ &#125;
435
+ 复制代码
436
+ </code></pre>
437
+ <p>那么,执行 <code>npm install</code> 后,得到的 <code>node_modules</code> 中模块目录结构就是下面这样的:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef33997d7f2?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>这样的方式优点很明显, <code>node_modules</code> 的结构和 <code>package.json</code> 结构一一对应,层级结构明显,并且保证了每次安装目录结构都是相同的。</p>
438
+ <p>但是,试想一下,如果你依赖的模块非常之多,你的 <code>node_modules</code> 将非常庞大,嵌套层级非常之深:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef33d822969?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
439
+ <ul>
440
+ <li>在不同层级的依赖中,可能引用了同一个模块,导致大量冗余。</li>
441
+ <li>在 <code>Windows</code> 系统中,文件路径最大长度为260个字符,嵌套层级过深可能导致不可预知的问题。</li>
442
+ </ul>
443
+ <h3 id="3-2-扁平结构"><a href="#3-2-扁平结构" class="headerlink" title="3.2 扁平结构"></a>3.2 扁平结构</h3><p>为了解决以上问题,<code>NPM</code> 在 <code>3.x</code> 版本做了一次较大更新。其将早期的嵌套结构改为扁平结构:</p>
444
+ <ul>
445
+ <li>安装模块时,不管其是直接依赖还是子依赖的依赖,优先将其安装在 <code>node_modules</code> 根目录。</li>
446
+ </ul>
447
+ <p>还是上面的依赖结构,我们在执行 <code>npm install</code> 后将得到下面的目录结构:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3518941f2?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3519475d1?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>此时我们若在模块中又依赖了 <code>base64-js@1.0.1</code> 版本:</p>
448
+ <pre><code>&#123;
449
+ &quot;name&quot;: &quot;my-app&quot;,
450
+ &quot;dependencies&quot;: &#123;
451
+ &quot;buffer&quot;: &quot;^5.4.3&quot;,
452
+ &quot;ignore&quot;: &quot;^5.1.4&quot;,
453
+ &quot;base64-js&quot;: &quot;1.0.1&quot;,
454
+ &#125;
455
+ &#125;
456
+ 复制代码
457
+ </code></pre>
458
+ <ul>
459
+ <li>当安装到相同模块时,判断已安装的模块版本是否符合新模块的版本范围,如果符合则跳过,不符合则在当前模块的 <code>node_modules</code> 下安装该模块。</li>
460
+ </ul>
461
+ <p>此时,我们在执行 <code>npm install</code> 后将得到下面的目录结构:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef355ae3b37?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef35ae17872?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>对应的,如果我们在项目代码中引用了一个模块,模块查找流程如下:</p>
462
+ <ul>
463
+ <li>在当前模块路径下搜索</li>
464
+ <li>在当前模块 <code>node_modules</code> 路径下搜素</li>
465
+ <li>在上级模块的 <code>node_modules</code> 路径下搜索</li>
466
+ <li>…</li>
467
+ <li>直到搜索到全局路径中的 <code>node_modules</code></li>
468
+ </ul>
469
+ <p>假设我们又依赖了一个包 <code>buffer2@^5.4.3</code>,而它依赖了包 <code>base64-js@1.0.3</code>,则此时的安装结构是下面这样的:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef377260d67?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>所以 <code>npm 3.x</code> 版本并未完全解决老版本的模块冗余问题,甚至还会带来新的问题。</p>
470
+ <p>试想一下,你的APP假设没有依赖 <code>base64-js@1.0.1</code> 版本,而你同时依赖了依赖不同 <code>base64-js</code> 版本的 <code>buffer</code> 和 <code>buffer2</code>。由于在执行 <code>npm install</code> 的时候,按照 <code>package.json</code> 里依赖的顺序依次解析,则 <code>buffer</code> 和 <code>buffer2</code> 在 <code>package.json</code> 的放置顺序则决定了 <code>node_modules</code> 的依赖结构:</p>
471
+ <p>先依赖<code>buffer2</code>:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3824eba10?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>先依赖<code>buffer</code>:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef38a55f11e?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>另外,为了让开发者在安全的前提下使用最新的依赖包,我们在 <code>package.json</code> 通常只会锁定大版本,这意味着在某些依赖包小版本更新后,同样可能造成依赖结构的改动,依赖结构的不确定性可能会给程序带来不可预知的问题。</p>
472
+ <h3 id="3-3-Lock文件"><a href="#3-3-Lock文件" class="headerlink" title="3.3 Lock文件"></a>3.3 Lock文件</h3><p>为了解决 <code>npm install</code> 的不确定性问题,在 <code>npm 5.x</code> 版本新增了 <code>package-lock.json</code> 文件,而安装方式还沿用了 <code>npm 3.x</code> 的扁平化的方式。</p>
473
+ <p><code>package-lock.json</code> 的作用是锁定依赖结构,即只要你目录下有 <code>package-lock.json</code> 文件,那么你每次执行 <code>npm install</code> 后生成的 <code>node_modules</code> 目录结构一定是完全相同的。</p>
474
+ <p>例如,我们有如下的依赖结构:</p>
475
+ <pre><code>&#123;
476
+ &quot;name&quot;: &quot;my-app&quot;,
477
+ &quot;dependencies&quot;: &#123;
478
+ &quot;buffer&quot;: &quot;^5.4.3&quot;,
479
+ &quot;ignore&quot;: &quot;^5.1.4&quot;,
480
+ &quot;base64-js&quot;: &quot;1.0.1&quot;,
481
+ &#125;
482
+ &#125;
483
+ 复制代码
484
+ </code></pre>
485
+ <p>在执行 <code>npm install</code> 后生成的 <code>package-lock.json</code> 如下:</p>
486
+ <pre><code>&#123;
487
+ &quot;name&quot;: &quot;my-app&quot;,
488
+ &quot;version&quot;: &quot;1.0.0&quot;,
489
+ &quot;dependencies&quot;: &#123;
490
+ &quot;base64-js&quot;: &#123;
491
+ &quot;version&quot;: &quot;1.0.1&quot;,
492
+ &quot;resolved&quot;: &quot;https://registry.npmjs.org/base64-js/-/base64-js-1.0.1.tgz&quot;,
493
+ &quot;integrity&quot;: &quot;sha1-aSbRsZT7xze47tUTdW3i/Np+pAg=&quot;
494
+ &#125;,
495
+ &quot;buffer&quot;: &#123;
496
+ &quot;version&quot;: &quot;5.4.3&quot;,
497
+ &quot;resolved&quot;: &quot;https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz&quot;,
498
+ &quot;integrity&quot;: &quot;sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==&quot;,
499
+ &quot;requires&quot;: &#123;
500
+ &quot;base64-js&quot;: &quot;^1.0.2&quot;,
501
+ &quot;ieee754&quot;: &quot;^1.1.4&quot;
502
+ &#125;,
503
+ &quot;dependencies&quot;: &#123;
504
+ &quot;base64-js&quot;: &#123;
505
+ &quot;version&quot;: &quot;1.3.1&quot;,
506
+ &quot;resolved&quot;: &quot;https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz&quot;,
507
+ &quot;integrity&quot;: &quot;sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==&quot;
508
+ &#125;
509
+ &#125;
510
+ &#125;,
511
+ &quot;ieee754&quot;: &#123;
512
+ &quot;version&quot;: &quot;1.1.13&quot;,
513
+ &quot;resolved&quot;: &quot;https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz&quot;,
514
+ &quot;integrity&quot;: &quot;sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==&quot;
515
+ &#125;,
516
+ &quot;ignore&quot;: &#123;
517
+ &quot;version&quot;: &quot;5.1.4&quot;,
518
+ &quot;resolved&quot;: &quot;https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz&quot;,
519
+ &quot;integrity&quot;: &quot;sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==&quot;
520
+ &#125;
521
+ &#125;
522
+ &#125;
523
+ 复制代码
524
+ </code></pre>
525
+ <p>我们来具体看看上面的结构:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3a81eb51f?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>最外面的两个属性 <code>name</code> 、<code>version</code> 同 <code>package.json</code> 中的 <code>name</code> 和 <code>version</code> ,用于描述当前包名称和版本。</p>
526
+ <p><code>dependencies</code> 是一个对象,对象和 <code>node_modules</code> 中的包结构一一对应,对象的 <code>key</code> 为包名称,值为包的一些描述信息:</p>
527
+ <ul>
528
+ <li><code>version</code>:包版本 —— 这个包当前安装在 <code>node_modules</code> 中的版本</li>
529
+ <li><code>resolved</code>:包具体的安装来源</li>
530
+ <li><code>integrity</code>:包 <code>hash</code> 值,基于 <code>Subresource Integrity</code> 来验证已安装的软件包是否被改动过、是否已失效</li>
531
+ <li><code>requires</code>:对应子依赖的依赖,与子依赖的 <code>package.json</code> 中 <code>dependencies</code>的依赖项相同。</li>
532
+ <li><code>dependencies</code>:结构和外层的 <code>dependencies</code> 结构相同,存储安装在子依赖 <code>node_modules</code> 中的依赖包。</li>
533
+ </ul>
534
+ <p>这里注意,并不是所有的子依赖都有 <code>dependencies</code> 属性,只有子依赖的依赖和当前已安装在根目录的 <code>node_modules</code> 中的依赖冲突之后,才会有这个属性。</p>
535
+ <p>例如,回顾下上面的依赖关系:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef35ae17872?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>我们在 <code>my-app</code> 中依赖的 <code>base64-js@1.0.1</code> 版本与 <code>buffer</code> 中依赖的 <code>base64-js@^1.0.2</code> 发生冲突,所以 <code>base64-js@1.0.1</code> 需要安装在 <code>buffer</code> 包的 <code>node_modules</code> 中,对应了 <code>package-lock.json</code> 中 <code>buffer</code> 的 <code>dependencies</code> 属性。这也对应了 <code>npm</code> 对依赖的扁平化处理方式。</p>
536
+ <p>所以,根据上面的分析, <code>package-lock.json</code> 文件 和 <code>node_modules</code> 目录结构是一一对应的,即项目目录下存在 <code>package-lock.json</code> 可以让每次安装生成的依赖目录结构保持相同。</p>
537
+ <p>另外,项目中使用了 <code>package-lock.json</code> 可以显著加速依赖安装时间。</p>
538
+ <p>我们使用 <code>npm i --timing=true --loglevel=verbose</code> 命令可以看到 <code>npm install</code> 的完整过程,下面我们来对比下使用 <code>lock</code> 文件和不使用 <code>lock</code> 文件的差别。在对比前先清理下<code>npm</code> 缓存。</p>
539
+ <p>不使用 <code>lock</code> 文件:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef39713273a?imageslim" alt="xuehuayu.cn"><br>使用 <code>lock</code> 文件:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3b5e532e0?imageslim" alt="xuehuayu.cn"><br>可见, <code>package-lock.json</code> 中已经缓存了每个包的具体版本和下载链接,不需要再去远程仓库进行查询,然后直接进入文件完整性校验环节,减少了大量网络请求。</p>
540
+ <h4 id="使用建议"><a href="#使用建议" class="headerlink" title="使用建议"></a>使用建议</h4><p>开发系统应用时,建议把 <code>package-lock.json</code> 文件提交到代码版本仓库,从而保证所有团队开发者以及 <code>CI</code> 环节可以在执行 <code>npm install</code> 时安装的依赖版本都是一致的。</p>
541
+ <p>在开发一个 <code>npm</code>包 时,你的 <code>npm</code>包 是需要被其他仓库依赖的,由于上面我们讲到的扁平安装机制,如果你锁定了依赖包版本,你的依赖包就不能和其他依赖包共享同一 <code>semver</code> 范围内的依赖包,这样会造成不必要的冗余。所以我们不应该把<code>package-lock.json</code> 文件发布出去( <code>npm</code> 默认也不会把 <code>package-lock.json</code> 文件发布出去)。</p>
542
+ <h3 id="3-4-缓存"><a href="#3-4-缓存" class="headerlink" title="3.4 缓存"></a>3.4 缓存</h3><p>在执行 <code>npm install</code> 或 <code>npm update</code>命令下载依赖后,除了将依赖包安装在<code>node_modules</code> 目录下外,还会在本地的缓存目录缓存一份。</p>
543
+ <p>通过 <code>npm config get cache</code> 命令可以查询到:在 <code>Linux</code> 或 <code>Mac</code> 默认是用户主目录下的 <code>.npm/_cacache</code> 目录。</p>
544
+ <p>在这个目录下又存在两个目录:<code>content-v2</code>、<code>index-v5</code>,<code>content-v2</code> 目录用于存储 <code>tar</code>包的缓存,而<code>index-v5</code>目录用于存储<code>tar</code>包的 <code>hash</code>。</p>
545
+ <p>npm 在执行安装时,可以根据 <code>package-lock.json</code> 中存储的 <code>integrity、version、name</code> 生成一个唯一的 <code>key</code> 对应到 <code>index-v5</code> 目录下的缓存记录,从而找到 <code>tar</code>包的 <code>hash</code>,然后根据 <code>hash</code> 再去找缓存的 <code>tar</code>包直接使用。</p>
546
+ <p>我们可以找一个包在缓存目录下搜索测试一下,在 <code>index-v5</code> 搜索一下包路径:</p>
547
+ <pre><code>grep &quot;https://registry.npmjs.org/base64-js/-/base64-js-1.0.1.tgz&quot; -r index-v5
548
+ 复制代码
549
+ </code></pre>
550
+ <p><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3b8fb68f5?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>然后我们将json格式化:</p>
551
+ <pre><code>&#123;
552
+ &quot;key&quot;: &quot;pacote:version-manifest:https://registry.npmjs.org/base64-js/-/base64-js-1.0.1.tgz:sha1-aSbRsZT7xze47tUTdW3i/Np+pAg=&quot;,
553
+ &quot;integrity&quot;: &quot;sha512-C2EkHXwXvLsbrucJTRS3xFHv7Mf/y9klmKDxPTE8yevCoH5h8Ae69Y+/lP+ahpW91crnzgO78elOk2E6APJfIQ==&quot;,
554
+ &quot;time&quot;: 1575554308857,
555
+ &quot;size&quot;: 1,
556
+ &quot;metadata&quot;: &#123;
557
+ &quot;id&quot;: &quot;base64-js@1.0.1&quot;,
558
+ &quot;manifest&quot;: &#123;
559
+ &quot;name&quot;: &quot;base64-js&quot;,
560
+ &quot;version&quot;: &quot;1.0.1&quot;,
561
+ &quot;engines&quot;: &#123;
562
+ &quot;node&quot;: &quot;&gt;= 0.4&quot;
563
+ &#125;,
564
+ &quot;dependencies&quot;: &#123;&#125;,
565
+ &quot;optionalDependencies&quot;: &#123;&#125;,
566
+ &quot;devDependencies&quot;: &#123;
567
+ &quot;standard&quot;: &quot;^5.2.2&quot;,
568
+ &quot;tape&quot;: &quot;4.x&quot;
569
+ &#125;,
570
+ &quot;bundleDependencies&quot;: false,
571
+ &quot;peerDependencies&quot;: &#123;&#125;,
572
+ &quot;deprecated&quot;: false,
573
+ &quot;_resolved&quot;: &quot;https://registry.npmjs.org/base64-js/-/base64-js-1.0.1.tgz&quot;,
574
+ &quot;_integrity&quot;: &quot;sha1-aSbRsZT7xze47tUTdW3i/Np+pAg=&quot;,
575
+ &quot;_shasum&quot;: &quot;6926d1b194fbc737b8eed513756de2fcda7ea408&quot;,
576
+ &quot;_shrinkwrap&quot;: null,
577
+ &quot;bin&quot;: null,
578
+ &quot;_id&quot;: &quot;base64-js@1.0.1&quot;
579
+ &#125;,
580
+ &quot;type&quot;: &quot;finalized-manifest&quot;
581
+ &#125;
582
+ &#125;
583
+ 复制代码
584
+ </code></pre>
585
+ <p>上面的 <code>_shasum</code> 属性 <code>6926d1b194fbc737b8eed513756de2fcda7ea408</code> 即为 <code>tar</code> 包的 <code>hash</code>, <code>hash</code>的前几位 <code>6926</code> 即为缓存的前两层目录,我们进去这个目录果然找到的压缩后的依赖包:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3bc635b03?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
586
+ <blockquote>
587
+ <p>以上的缓存策略是从 npm v5 版本开始的,在 npm v5 版本之前,每个缓存的模块在 ~&#x2F;.npm 文件夹中以模块名的形式直接存储,储存结构是{cache}&#x2F;{name}&#x2F;{version}。</p>
588
+ </blockquote>
589
+ <p><code>npm</code> 提供了几个命令来管理缓存数据:</p>
590
+ <ul>
591
+ <li><code>npm cache add</code>:官方解释说这个命令主要是 <code>npm</code> 内部使用,但是也可以用来手动给一个指定的 package 添加缓存。</li>
592
+ <li><code>npm cache clean</code>:删除缓存目录下的所有数据,为了保证缓存数据的完整性,需要加上 <code>--force</code> 参数。</li>
593
+ <li><code>npm cache verify</code>:验证缓存数据的有效性和完整性,清理垃圾数据。</li>
594
+ </ul>
595
+ <p>基于缓存数据,npm 提供了离线安装模式,分别有以下几种:</p>
596
+ <ul>
597
+ <li><code>--prefer-offline</code>: 优先使用缓存数据,如果没有匹配的缓存数据,则从远程仓库下载。</li>
598
+ <li><code>--prefer-online</code>: 优先使用网络数据,如果网络数据请求失败,再去请求缓存数据,这种模式可以及时获取最新的模块。</li>
599
+ <li><code>--offline</code>: 不请求网络,直接使用缓存数据,一旦缓存数据不存在,则安装失败。</li>
600
+ </ul>
601
+ <h3 id="3-5-文件完整性"><a href="#3-5-文件完整性" class="headerlink" title="3.5 文件完整性"></a>3.5 文件完整性</h3><p>上面我们多次提到了文件完整性,那么什么是文件完整性校验呢?</p>
602
+ <p>在下载依赖包之前,我们一般就能拿到 <code>npm</code> 对该依赖包计算的 <code>hash</code> 值,例如我们执行 <code>npm info</code> 命令,紧跟 <code>tarball</code>(下载链接) 的就是 <code>shasum</code>(<code>hash</code>) :<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3c2a2dac0?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>用户下载依赖包到本地后,需要确定在下载过程中没有出现错误,所以在下载完成之后需要在本地在计算一次文件的 <code>hash</code> 值,如果两个 <code>hash</code> 值是相同的,则确保下载的依赖是完整的,如果不同,则进行重新下载。</p>
603
+ <h3 id="3-6-整体流程"><a href="#3-6-整体流程" class="headerlink" title="3.6 整体流程"></a>3.6 整体流程</h3><p>好了,我们再来整体总结下上面的流程:</p>
604
+ <ul>
605
+ <li><p>检查 <code>.npmrc</code> 文件:优先级为:项目级的 <code>.npmrc</code> 文件 &gt; 用户级的 <code>.npmrc</code> 文件&gt; 全局级的 <code>.npmrc</code> 文件 &gt; npm 内置的 <code>.npmrc</code> 文件</p>
606
+ </li>
607
+ <li><p>检查项目中有无 <code>lock</code> 文件。</p>
608
+ </li>
609
+ <li><p>无 <code>lock</code> 文件:</p>
610
+ </li>
611
+ <li><p>从 <code>npm</code> 远程仓库获取包信息</p>
612
+ </li>
613
+ <li><p>根据 <code>package.json</code> 构建依赖树,构建过程:</p>
614
+ </li>
615
+ <li><p>构建依赖树时,不管其是直接依赖还是子依赖的依赖,优先将其放置在 <code>node_modules</code> 根目录。</p>
616
+ </li>
617
+ <li><p>当遇到相同模块时,判断已放置在依赖树的模块版本是否符合新模块的版本范围,如果符合则跳过,不符合则在当前模块的 <code>node_modules</code> 下放置该模块。</p>
618
+ </li>
619
+ <li><p>注意这一步只是确定逻辑上的依赖树,并非真正的安装,后面会根据这个依赖结构去下载或拿到缓存中的依赖包</p>
620
+ </li>
621
+ <li><p>在缓存中依次查找依赖树中的每个包</p>
622
+ </li>
623
+ <li><p>不存在缓存:</p>
624
+ </li>
625
+ <li><p>从 <code>npm</code> 远程仓库下载包</p>
626
+ </li>
627
+ <li><p>校验包的完整性</p>
628
+ </li>
629
+ <li><p>校验不通过:</p>
630
+ </li>
631
+ <li><p>重新下载</p>
632
+ </li>
633
+ <li><p>校验通过:</p>
634
+ </li>
635
+ <li><p>将下载的包复制到 <code>npm</code> 缓存目录</p>
636
+ </li>
637
+ <li><p>将下载的包按照依赖结构解压到 <code>node_modules</code></p>
638
+ </li>
639
+ <li><p>存在缓存:将缓存按照依赖结构解压到 <code>node_modules</code></p>
640
+ </li>
641
+ <li><p>将包解压到 <code>node_modules</code></p>
642
+ </li>
643
+ <li><p>生成 <code>lock</code> 文件</p>
644
+ </li>
645
+ <li><p>有 <code>lock</code> 文件:</p>
646
+ </li>
647
+ <li><p>检查 <code>package.json</code> 中的依赖版本是否和 <code>package-lock.json</code> 中的依赖有冲突。</p>
648
+ </li>
649
+ <li><p>如果没有冲突,直接跳过获取包信息、构建依赖树过程,开始在缓存中查找包信息,后续过程相同</p>
650
+ </li>
651
+ </ul>
652
+ <p><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef327ccaba5?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>上面的过程简要描述了 <code>npm install</code> 的大概过程,这个过程还包含了一些其他的操作,例如执行你定义的一些生命周期函数,你可以执行 <code>npm install package --timing=true --loglevel=verbose</code> 来查看某个包具体的安装流程和细节。</p>
653
+ <h3 id="3-7-yarn"><a href="#3-7-yarn" class="headerlink" title="3.7 yarn"></a>3.7 yarn</h3><p><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3dde095ab?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br><code>yarn</code> 是在 <code>2016</code> 年发布的,那时 <code>npm</code> 还处于 <code>V3</code> 时期,那时候还没有 <code>package-lock.json</code> 文件,就像上面我们提到的:不稳定性、安装速度慢等缺点经常会受到广大开发者吐槽。此时,<code>yarn</code> 诞生:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3e0ee2fbd?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>上面是官网提到的 <code>yarn</code> 的优点,在那个时候还是非常吸引人的。当然,后来 <code>npm</code> 也意识到了自己的问题,进行了很多次优化,在后面的优化(<code>lock</code>文件、缓存、默认-s…)中,我们多多少少能看到 <code>yarn</code> 的影子,可见 <code>yarn</code> 的设计还是非常优秀的。</p>
654
+ <p><code>yarn</code> 也是采用的是 <code>npm v3</code> 的扁平结构来管理依赖,安装依赖后默认会生成一个 <code>yarn.lock</code> 文件,还是上面的依赖关系,我们看看 <code>yarn.lock</code> 的结构:</p>
655
+ <pre><code># THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.# yarn lockfile v1
656
+
657
+
658
+ base64-js@1.0.1:
659
+ version &quot;1.0.1&quot;
660
+ resolved &quot;https://registry.yarnpkg.com/base64-js/-/base64-js-1.0.1.tgz#6926d1b194fbc737b8eed513756de2fcda7ea408&quot;
661
+ integrity sha1-aSbRsZT7xze47tUTdW3i/Np+pAg=
662
+
663
+ base64-js@^1.0.2:
664
+ version &quot;1.3.1&quot;
665
+ resolved &quot;https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1&quot;
666
+ integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
667
+
668
+ buffer@^5.4.3:
669
+ version &quot;5.4.3&quot;
670
+ resolved &quot;https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115&quot;
671
+ integrity sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==
672
+ dependencies:
673
+ base64-js &quot;^1.0.2&quot;
674
+ ieee754 &quot;^1.1.4&quot;
675
+
676
+ ieee754@^1.1.4:
677
+ version &quot;1.1.13&quot;
678
+ resolved &quot;https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84&quot;
679
+ integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
680
+
681
+ ignore@^5.1.4:
682
+ version &quot;5.1.4&quot;
683
+ resolved &quot;https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf&quot;
684
+ integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==
685
+
686
+ 复制代码
687
+ </code></pre>
688
+ <p>可见其和 <code>package-lock.json</code> 文件还是比较类似的,还有一些区别就是:</p>
689
+ <ul>
690
+ <li><code>package-lock.json</code> 使用的是 <code>json</code> 格式,<code>yarn.lock</code> 使用的是一种自定义格式</li>
691
+ <li><code>yarn.lock</code> 中子依赖的版本号不是固定的,意味着单独又一个 <code>yarn.lock</code> 确定不了 <code>node_modules</code> 目录结构,还需要和 <code>package.json</code> 文件进行配合。而 <code>package-lock.json</code> 只需要一个文件即可确定。</li>
692
+ </ul>
693
+ <p><code>yarn</code> 的缓策略看起来和 <code>npm v5</code> 之前的很像,每个缓存的模块被存放在独立的文件夹,文件夹名称包含了模块名称、版本号等信息。使用命令 <code>yarn cache dir</code> 可以查看缓存数据的目录:<br><img src="https://user-gold-cdn.xitu.io/2019/12/16/16f0eef3eb66af39?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
694
+ <blockquote>
695
+ <p><code>yarn</code> 默认使用 <code>prefer-online</code> 模式,即优先使用网络数据,如果网络数据请求失败,再去请求缓存数据。</p>
696
+ </blockquote>
697
+ <h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul>
698
+ <li><a target="_blank" rel="noopener" href="https://juejin.im/post/5a6008c2f265da3e5033cd93">juejin.im&#x2F;post&#x2F;5a6008…</a></li>
699
+ <li><a target="_blank" rel="noopener" href="https://www.zhihu.com/question/305539244/answer/551386426">www.zhihu.com/question/30…</a></li>
700
+ <li><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/37285173">zhuanlan.zhihu.com&#x2F;p&#x2F;37285173</a></li>
701
+ <li><a target="_blank" rel="noopener" href="https://semver.org/lang/zh-CN/">semver.org&#x2F;lang&#x2F;zh-CN&#x2F;</a></li>
702
+ <li><a target="_blank" rel="noopener" href="http://deadhorse.me/nodejs/2014/04/27/semver-in-nodejs.html">deadhorse.me&#x2F;nodejs&#x2F;2014…</a></li>
703
+ <li><a target="_blank" rel="noopener" href="http://caibaojian.com/npm/files/package.json.html">caibaojian.com&#x2F;npm&#x2F;files&#x2F;p…</a></li>
704
+ </ul>
705
+ <h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>希望阅读完本篇文章能对你有如下帮助:</p>
706
+ <ul>
707
+ <li>了解 <code>pacakge.json</code> 中的各项详细配置从而对项目工程化配置有更进一步的见解</li>
708
+ <li>掌握 <code>npm</code> 的版本管理机制,能合理配置依赖版本</li>
709
+ <li>理解 <code>npm install</code> 安装原理,能合理运用 <code>npm</code>缓存、<code>package-lock.json</code></li>
710
+ </ul>
711
+ <p>文中如有错误,欢迎在评论区指正,如果这篇文章帮助到了你,欢迎点赞和关注。</p>
712
+ <p>想阅读更多优质文章、可关注我的<a target="_blank" rel="noopener" href="https://github.com/ConardLi/ConardLi.github.io">github博客</a>,你的star✨、点赞和关注是我持续创作的动力!</p>
713
+ <p>推荐关注我的微信公众号【code秘密花园】,每天推送高质量文章,我们一起交流成长。<br><img src="https://user-gold-cdn.xitu.io/2019/9/1/16ced1f6bae445fa?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
714
+ </div><iframe src="/donate/?AliPayQR=https://store.xuehuayu.cn/store/public/alipay_qr.jpg&amp;WeChatQR=https://store.xuehuayu.cn/store/public/wechat_qr.png&amp;UnionPayQR=null&amp;GitHub=null&amp;BTCQR=null&amp;BTCKEY=null&amp;PayPal=https://www.paypal.com/paypalme/OrdinaryPerson" style="overflow-x:hidden; overflow-y:hidden; border:0xp none #fff; min-height:240px; width:100%;" frameborder="0" scrolling="no"></iframe><div><ul class="post-copyright"><li class="post-copyright-author"><strong>本文作者:</strong>雪花雨</li><li class="post-copyright-link"><strong>本文链接:</strong><a href="/article/37102.html">https://xuehuayu.cn/article/37102.html</a></li><li class="post-copyright-license"><strong>版权声明:</strong>① 标为原创的文章为博主原创,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接。② 转载文章来自网络,已标明出处,<a href="mailto:cainiaoblog@126.com">侵删</a>。</li></ul></div><br><div class="tags"><a href="/tags/%E8%BD%AC%E8%BD%BD/">转载</a></div><div class="post-nav"><a class="pre" href="/article/31695.html">VS Code 成主宰、Vue 备受热捧!2019 前端开发趋势必读</a><a class="next" href="/article/54295.html">前端实现弹幕效果的方法总结(包含css3和canvas的实现方式)</a></div><div id="vcomment"></div><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@waline/client@v2/dist/waline.min.css"><script src="//cdn.jsdelivr.net/npm/@waline/client@v2/dist/waline.js"></script><script>const origin = window.location.origin
715
+ const serverURL = origin.includes('cainiaoblog') ? 'https://cmts.cainiaoblog.cn' : 'https://cmts.xuehuayu.cn'
716
+ const locale = {
717
+ placeholder: '请正确填写昵称和邮箱,方便接收回复通知~',
718
+ sofa: '沙发空缺中,还不快抢~',
719
+ };
720
+ Waline.init({
721
+ el: '#vcomment',
722
+ serverURL: serverURL,
723
+ pageSize: '20',
724
+ visitor: true, // 阅读量统计
725
+ requiredMeta: ['nick', 'mail'],
726
+ emoji: [
727
+ 'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/weibo',
728
+ 'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/qq',
729
+ 'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/tw-emoji',
730
+ ],
731
+ locale,
732
+ pageview: true
733
+ })
734
+ </script></div></div></div><div class="pure-u-1 pure-u-md-1-4 fixed-search hidden_mid_and_down"><div id="sidebar"><script type="text/javascript" src="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/js/search.min.js"></script><div class="widget"><div class="widget-search"><input class="search" type="radio" name="search" value="baidu" id="baidu" checked="checked"/><label class="label" for="baidu" title="百度全站搜索">百度</label><input class="search" type="radio" name="search" value="google" id="google"/><label class="label" for="google" title="谷歌全站搜索">谷歌</label><input class="search" type="radio" name="search" value="self" id="self"/><label class="label" for="self" title="使用站内搜索">站内</label></div><div class="widget" id="search"><form class="search-form" action="//www.baidu.com/baidu" method="get" accept-charset="utf-8" target="_blank"><input type="search" name="word" maxlength="30" placeholder="百度全站搜索"/><input type="hidden" name="si" value="xuehuayu.cn"/><input type="hidden" name="cl" value="3"/><input type="hidden" name="ct" value="2097152"/><input type="hidden" name="s" value="on"/><input class="search-submit" type="submit" value=""/></form></div></div><script>$('input[type=radio][name=search]').change(function() {
735
+ var val = $(this).val()
736
+ var self = '<div class="search-form"><input id="local-search-input" placeholder="站内搜索,首次慢" type="search" name="q" results="0"><input class="search-submit" type="submit" value=""/><div id="local-search-result"></div></div>'
737
+ var google = '<form class="search-form" action="//www.google.com/search" method="get" accept-charset="utf-8" target="_blank"><input type="search" name="q" maxlength="30" placeholder="谷歌全站搜索"><input type="hidden" name="sitesearch" value="xuehuayu.cn"><input class="search-submit" type="submit" value=""/></form>'
738
+ var baidu = '<form class="search-form" action="//www.baidu.com/baidu" method="get" accept-charset="utf-8" target="_blank"><input type="search" name="word" maxlength="30" placeholder="百度全站搜索"><input type="hidden" name="si" value="xuehuayu.cn"><input type="hidden" name="cl" value="3"><input type="hidden" name="ct" value="2097152"><input type="hidden" name="s" value="on"><input class="search-submit" type="submit" value=""/></form>'
739
+
740
+ if (val === 'self') {
741
+ $('#search').html(self)
742
+ var search_path = 'search.xml';
743
+ if (search_path.length == 0) {
744
+ search_path = 'search.xml';
745
+ }
746
+ var path = 'https://cdn.staticaly.com/gh/npljy/npljy.github.io/main/' + search_path;
747
+ searchFunc(path, 'local-search-input', 'local-search-result');
748
+ } else if (val === 'baidu') {
749
+ $('#search').html(baidu)
750
+ } else if (val === 'google') {
751
+ $('#search').html(google)
752
+ }
753
+ })</script><div class="widget widget-wxmp"><img alt="微信公众号" width="100%" src="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/img/mp-mini.jpg"/></div><div class="widget widget-categories"><div class="widget-title"><i class="fa fa-folder-o"> 分类</i></div><ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/categories/CDN/">CDN</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/IDE/">IDE</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/git/">git</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/hexo/">hexo</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/%E5%85%AC%E5%85%B1%E8%8A%82%E6%97%A5/">公共节日</a><span class="category-list-count">1</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/%E5%88%86%E4%BA%AB/">分享</a><span class="category-list-count">29</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/%E5%89%8D%E7%AB%AF/">前端</a><span class="category-list-count">191</span><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/%E5%89%8D%E7%AB%AF/%E6%B5%8F%E8%A7%88%E5%99%A8/">浏览器</a><span class="category-list-count">1</span><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/%E5%89%8D%E7%AB%AF/%E6%B5%8F%E8%A7%88%E5%99%A8/%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE/">网络协议</a><span class="category-list-count">1</span></li></ul></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/%E5%8D%9A%E5%AE%A2/">博客</a><span class="category-list-count">3</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/%E5%B7%A5%E5%85%B7/">工具</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/%E5%B9%BF%E5%91%8A%E8%BF%87%E6%BB%A4/">广告过滤</a><span class="category-list-count">8</span><ul class="category-list-child"><li class="category-list-item"><a class="category-list-link" href="/categories/%E5%B9%BF%E5%91%8A%E8%BF%87%E6%BB%A4/%E8%A7%86%E9%A2%91%E7%94%B5%E8%A7%86%E5%B0%8F%E8%AF%B4%E6%BC%AB%E7%94%BB%E8%B5%84%E8%AE%AF%E9%9F%B3%E4%B9%90/">视频电视小说漫画资讯音乐</a><span class="category-list-count">2</span></li></ul></li><li class="category-list-item"><a class="category-list-link" href="/categories/%E6%AD%A3%E5%88%99/">正则</a><span class="category-list-count">5</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/%E6%B5%8F%E8%A7%88%E5%99%A8/">浏览器</a><span class="category-list-count">5</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/%E7%AE%97%E6%B3%95/">算法</a><span class="category-list-count">2</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/%E8%B5%84%E8%AE%AF/">资讯</a><span class="category-list-count">16</span></li><li class="category-list-item"><a class="category-list-link" href="/categories/%E8%BD%BB%E6%9D%BE%E4%B8%80%E5%88%BB/">轻松一刻</a><span class="category-list-count">1</span></li></ul></div><div class="widget widget-tags"><div class="widget-title"><i class="fa fa-star-o"> 标签</i></div><div class="tagcloud"><a href="/tags/%E5%8E%9F%E5%88%9B/" style="font-size: 15px;">原创</a> <a href="/tags/%E8%BD%AC%E8%BD%BD/" style="font-size: 15px;">转载</a> <a href="/tags/%E6%89%8B%E5%86%99/" style="font-size: 15px;">手写</a> <a href="/tags/%E7%96%AB%E6%83%85/" style="font-size: 15px;">疫情</a> <a href="/tags/%E5%A8%B1%E4%B9%90/" style="font-size: 15px;">娱乐</a></div></div><div class="widget widget-recent-posts"><div class="widget-title"><i class="fa fa-file-o"> 最近文章</i></div><ul class="post-list"><li class="post-list-item"><a class="post-list-link" href="/article/c5576490.html">面试准备</a></li><li class="post-list-item"><a class="post-list-link" href="/article/84c9ccd6.html">精读《SolidJS》</a></li><li class="post-list-item"><a class="post-list-link" href="/article/53803e6.html">git提交注释规范</a></li><li class="post-list-item"><a class="post-list-link" href="/article/a4145266.html">前端数组拍平flat一行代码</a></li><li class="post-list-item"><a class="post-list-link" href="/article/bedea419.html">一道前端this面试题</a></li><li class="post-list-item"><a class="post-list-link" href="/article/bf7e7421.html">git多账号配置</a></li></ul></div><div class="widget widget-links"><div class="widget-title"><i class="fa fa-external-link"> 友情链接</i></div><ul></ul><a href="https://github.com/npljy?utm_source=xuehuayu.cn" title="GitHub" target="_blank">GitHub</a><ul></ul><a href="https://laonongmin.online?utm_source=xuehuayu.cn" title="看看" target="_blank">看看</a><ul></ul><a href="https://cainiaoblog.cn?utm_source=xuehuayu.cn" title="菜鸟博客" target="_blank">菜鸟博客</a><ul></ul><a href="mailto:cainiaoblog@126.com" title="友链联系" target="_blank">友链联系</a></div></div></div><div class="pure-u-1 pure-u-md-3-4"><div id="footer"><div class="flex-block justify-center align-center flex-wrap"><a class="gxba-link" id="gxba" rel="nofollow" target="_blank" href="http://beian.miit.gov.cn/">京ICP备20007647号-2</a><a class="gaba-link" id="gaba-link" rel="nofollow" target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010802031264"><img class="nofancybox" src="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/img/gaba.png" alt=""/><span id="gaba">京公网安备 11010802031264号</span><span style="padding-right: 10px;"></span></a><span>Copyright © 2023 </span><a href="/." rel="nofollow">前端壹菜鸟. </a><script>(function(){
754
+ var cnb =window.location.origin.indexOf('cainiaoblog') !==-1
755
+ if (cnb) {
756
+ var gxba =document.getElementById('gxba')
757
+ var gaba =document.getElementById('gaba')
758
+ var gabaLink =document.getElementById('gaba-link')
759
+ gxba.innerText ='京ICP备20007647号-1'
760
+ gaba.innerText ='京公网安备 11010802031254号'
761
+ gabaLink.setAttribute('href','http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010802031254')
762
+ }
763
+ })()</script></div><div class="flex-block justify-center align-center flex-wrap"><a rel="nofollow" target="_blank" href="https://v6.51.la/s/Guz3lwHCsVme9Cu"><div class="busuanzi_container" id="busuanzi_container"><span class="busuanzi_container_site_pv" id="busuanzi_container_site_pv">访问量:<span id="busuanzi_value_site_pv"><i class="fa fa-spinner"></i></span></span><span class="busuanzi_container_site_uv" id="busuanzi_container_site_uv">访客数:<span id="busuanzi_value_site_uv"><i class="fa fa-spinner"></i></span></span><span style="padding-right: 10px;"></span></div></a><a rel="nofollow" target="_blank" href="https://www.upyun.com/?utm_source=lianmeng&amp;utm_medium=referral"><span style="font-weight:bold;letter-spacing: 1px;">本网站由 </span><img class="nofancybox" height="32" style="vertical-align: middle" src="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/img/upy_logo.min.svg" alt=""/><span style="font-weight:bold;letter-spacing: 1px;">提供CDN加速/云存储服务</span></a><span style="padding-right: 10px;"></span><a rel="nofollow" style="font-size:18px;font-weight:bold;vertical-align: middle" target="_blank" title="注册就送代金券可直接使用" href="https://console.upyun.com/register/?invite=HyDsjZHIL">注册</a></div></div></div></div><a class="show" id="rocket" href="#top"></a><div class="darkmode-toggle">🌓</div><script type="text/javascript" src="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/js/totop.min.js" async></script><script type="text/javascript" src="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/js/dark.min.js" async></script><script type="text/javascript" src="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/js/codeblock-resizer.min.js"></script><script type="text/javascript" src="//cdn.staticaly.com/gh/npljy/npljy.github.io/main/js/smartresize.min.js"></script></div></body></html>