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,292 @@
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>React + TypeScript 从零开发Popup组件并发布到 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">React + TypeScript 从零开发Popup组件并发布到 npm</h1><div class="post-meta"><span class="date">2022-02-17</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"> 3.8k</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"> 16</span><span class="post-meta-item-text"> 分钟</span></span></span></div><a class="disqus-comment-count" href="/article/624d1918.html#vcomment"><span class="waline-comment-count" data-path="/article/624d1918.html"></span><span> 条评论</span></a><div class="post-content"><p>在组件库系列文章中介绍了如何从 0 到 1 搭建一个 React 组件库架子,但为了一两个组件去搭建组件库未免显得大材小用。<br>这次以移动端一个常用组件 <code>Popup</code> 为例,以最方便快捷的形式发布一个完整的 npm 包。</p>
20
+ <span id="more"></span>
21
+ <blockquote>
22
+ <p><a target="_blank" rel="noopener" href="https://github.com/worldzhao/react-easy-popup">✨ 仓库传送门</a><br><a target="_blank" rel="noopener" href="https://worldzhao.github.io/react-easy-popup/">🚀 在线预览</a></p>
23
+ </blockquote>
24
+ <p>本文包含以下内容:</p>
25
+ <ol>
26
+ <li><p><code>Popup</code>组件的开发;</p>
27
+ </li>
28
+ <li><p>一些工具的使用</p>
29
+ <ul>
30
+ <li><a target="_blank" rel="noopener" href="https://github.com/jaredpalmer/tsdx">tsdx</a> :项目初始化、开发以及打包大管家;</li>
31
+ <li><a target="_blank" rel="noopener" href="https://github.com/sindresorhus/np">np</a>:一键发布 npm 包;</li>
32
+ <li><a target="_blank" rel="noopener" href="https://www.npmjs.com/package/gh-pages">gh-pages</a>:部署示例 demo ;</li>
33
+ <li><a target="_blank" rel="noopener" href="https://github.com/kefranabg/readme-md-generator">readme-md-generator</a>:生成一份规范的<code>README.md</code>文件。</li>
34
+ </ul>
35
+ </li>
36
+ </ol>
37
+ <p>本文不会和组件库那篇文章一般死扣打包细节,因为单个组件和组件库的打包有本质上的区别:</p>
38
+ <ul>
39
+ <li>组件库需要提供按需引入的能力,所以对组件仅仅是进行了语法上的编译(以及比较绕的样式处理),故选择了 gulp 管理打包流程;</li>
40
+ <li>单组件一般不需要提供按需引入的能力,只需要打包出一个 js bundle 和 css bundle 即可,webpack 与 rollup 就更适用于此类场景。</li>
41
+ </ul>
42
+ <p><img src="https://raw.githubusercontent.com/worldzhao/blog/master/images/react-popup-1.gif" alt="react-popup-1"></p>
43
+ <h2 id="项目初始化"><a href="#项目初始化" class="headerlink" title="项目初始化"></a>项目初始化</h2><p><a target="_blank" rel="noopener" href="https://github.com/jaredpalmer/tsdx">tsdx</a> 内置三种项目模板:</p>
44
+ <ol>
45
+ <li>basic &#x3D;&gt; 工具包模板</li>
46
+ <li>react &#x3D;&gt; React 组件模板,使用 parcel 用作 example 调试</li>
47
+ <li>react-with-storybook &#x3D;&gt; 同上,使用 storybook 编写文档以及 example 调试</li>
48
+ </ol>
49
+ <p>模板还内置了<code>start</code>、<code>build</code>、<code>test</code>以及<code>lint</code>等 npm scripts,的确是零配置开箱即用(大误)。</p>
50
+ <p>为了方便讲解,此处选择<code>react</code>模板。</p>
51
+ <p><img src="https://raw.githubusercontent.com/worldzhao/blog/master/images/react-popup-2.webp" alt="react-popup-2"></p>
52
+ <p>执行<code>npx tsdx create react-easy-popup</code>,选择<code>react</code>完成项目创建后进入项目目录。</p>
53
+ <p><img src="https://raw.githubusercontent.com/worldzhao/blog/master/images/react-popup-3.webp" alt="react-popup-3"></p>
54
+ <h3 id="配置-tsdx"><a href="#配置-tsdx" class="headerlink" title="配置 tsdx"></a>配置 tsdx</h3><p>由于<code>tsdx</code>没有提供样式文件打包支持,使用<code>css in js</code>方案会带来额外的依赖以及运行时消耗,所以需要简单配置一下<code>tsdx</code>以支持 less 样式。</p>
55
+ <p>参照<a target="_blank" rel="noopener" href="https://github.com/jaredpalmer/tsdx#customization">customization-tsdx</a>这一小节进行配置。</p>
56
+ <p>安装相关依赖:</p>
57
+ <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add rollup-plugin-postcss autoprefixer cssnano less --dev</span><br></pre></td></tr></table></figure>
58
+
59
+ <p>新建 <code>tsdx.config.js</code>,写入以下内容:</p>
60
+ <p><strong>tsdx.config.js</strong></p>
61
+ <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> postcss = <span class="built_in">require</span>(<span class="string">&#x27;rollup-plugin-postcss&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> autoprefixer = <span class="built_in">require</span>(<span class="string">&#x27;autoprefixer&#x27;</span>);</span><br><span class="line"><span class="keyword">const</span> cssnano = <span class="built_in">require</span>(<span class="string">&#x27;cssnano&#x27;</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = &#123;</span><br><span class="line"> <span class="title function_">rollup</span>(<span class="params">config, options</span>) &#123;</span><br><span class="line"> config.<span class="property">plugins</span>.<span class="title function_">push</span>(</span><br><span class="line"> <span class="title function_">postcss</span>(&#123;</span><br><span class="line"> <span class="attr">plugins</span>: [</span><br><span class="line"> <span class="title function_">autoprefixer</span>(),</span><br><span class="line"> <span class="title function_">cssnano</span>(&#123;</span><br><span class="line"> <span class="attr">preset</span>: <span class="string">&#x27;default&#x27;</span>,</span><br><span class="line"> &#125;),</span><br><span class="line"> ],</span><br><span class="line"> <span class="attr">inject</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">extract</span>: <span class="string">&#x27;react-easy-popup.min.css&#x27;</span>,</span><br><span class="line"> &#125;)</span><br><span class="line"> );</span><br><span class="line"> <span class="keyword">return</span> config;</span><br><span class="line"> &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
62
+
63
+ <p>在 <code>package.json</code> 中配置<code>browserslist</code>字段。</p>
64
+ <p><strong>package.json</strong></p>
65
+ <figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">// ...</span><br><span class="line"><span class="addition">+ &quot;browserslist&quot;: [</span></span><br><span class="line"><span class="addition">+ &quot;last 2 versions&quot;,</span></span><br><span class="line"><span class="addition">+ &quot;Android &gt;= 4.4&quot;,</span></span><br><span class="line"><span class="addition">+ &quot;iOS &gt;= 9&quot;</span></span><br><span class="line"><span class="addition">+ ],</span></span><br><span class="line">// ...</span><br></pre></td></tr></table></figure>
66
+
67
+ <p>清空<code>src</code>目录,新建<code>index.tsx</code>、<code>index.less</code>。</p>
68
+ <p><strong>src&#x2F;index.tsx</strong></p>
69
+ <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">&#x27;./index.less&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">Popup</span> = (<span class="params"></span>) =&gt; (</span><br><span class="line"> <span class="language-xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&quot;react-easy-popup&quot;</span>&gt;</span>hello,react-easy-popup<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">Popup</span>;</span><br></pre></td></tr></table></figure>
70
+
71
+ <p><strong>src&#x2F;index.less</strong></p>
72
+ <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">.react-easy-popup &#123;</span><br><span class="line"> display: flex;</span><br><span class="line"> color: skyblue;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
73
+
74
+ <p><strong>example&#x2F;index.tsx</strong></p>
75
+ <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">&#x27;react-app-polyfill/ie11&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">ReactDOM</span> <span class="keyword">from</span> <span class="string">&#x27;react-dom&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Popup</span> <span class="keyword">from</span> <span class="string">&#x27;../.&#x27;</span>; <span class="comment">// 此处存在parcel alias 见下文</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">&#x27;../dist/react-easy-popup.min.css&#x27;</span>; <span class="comment">// 此处不存在parcel alias 写好相对路径</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">App</span> = (<span class="params"></span>) =&gt; &#123;</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <span class="language-xml"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span><br><span class="line"><span class="language-xml"> <span class="tag">&lt;<span class="name">Popup</span> /&gt;</span></span></span><br><span class="line"><span class="language-xml"> <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br><span class="line"> );</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="title class_">ReactDOM</span>.<span class="title function_">render</span>(<span class="language-xml"><span class="tag">&lt;<span class="name">App</span> /&gt;</span></span>, <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">&#x27;root&#x27;</span>));</span><br></pre></td></tr></table></figure>
76
+
77
+ <p>进入项目根目录,执行以下命令:</p>
78
+ <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn start</span><br></pre></td></tr></table></figure>
79
+
80
+ <p>现在 <code>src</code> 目录下的内容的变更会被实时监听,在根目录下生成的<code>dist</code>文件夹包含打包后的内容。</p>
81
+ <p>开发时调试的文件夹为<code>example</code>,另起一个终端。执行以下命令:</p>
82
+ <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">cd example</span><br><span class="line">yarn # 安装依赖</span><br><span class="line">yarn start # 启动example</span><br></pre></td></tr></table></figure>
83
+
84
+ <p>在<code>localhost:1234</code>可以发现项目启动啦,样式生效且有浏览器前缀。</p>
85
+ <p><img src="https://raw.githubusercontent.com/worldzhao/blog/master/images/react-popup-4.webp" alt="react-popup-4"></p>
86
+ <blockquote>
87
+ <p>若 example 启动后网页报错,删除 example 下的.cache 以及 dist 目录重新 start</p>
88
+ </blockquote>
89
+ <p>需要注意的是 <code>example</code> 的入口文件<code>index.tsx</code>引入的是我们打包后的文件,即<code>dist/index.js</code>。</p>
90
+ <p>但是引入路径却为<code>&#39;../.&#39;</code>,这是因为 <code>tsdx</code> 使用了 <code>parcel</code> 的 <a target="_blank" rel="noopener" href="https://github.com/palmerhq/tsdx/pull/88/files">aliasing</a>。</p>
91
+ <p><img src="https://raw.githubusercontent.com/worldzhao/blog/master/images/react-popup-5.webp" alt="react-popup-5"></p>
92
+ <p>同时,观察根目录下的<code>dist</code>文件夹:</p>
93
+ <p><strong>dist</strong></p>
94
+ <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">├── index.d.ts # 组件声明文件</span><br><span class="line">├── index.js # 组件入口</span><br><span class="line">├── react-easy-popup.cjs.development.js # 开发时引入的组件代码 Commonjs规范</span><br><span class="line">├── react-easy-popup.cjs.development.js.map # soucemap</span><br><span class="line">├── react-easy-popup.cjs.production.min.js # 压缩后的组件代码</span><br><span class="line">├── react-easy-popup.cjs.production.min.js.map # sourcemap</span><br><span class="line">├── react-easy-popup.esm.js # ES Module规范的组件组件代码</span><br><span class="line">├── react-easy-popup.esm.js.map # sourcemap</span><br><span class="line">└── react-easy-popup.min.css # 样式文件</span><br></pre></td></tr></table></figure>
95
+
96
+ <p>也可以很轻易地在<code>package.json</code>中找到<code>main</code>、<code>module</code>以及<code>typings</code>相关配置。</p>
97
+ <blockquote>
98
+ <p>基于 rollup 手动搭一个组件模板并不困难,但是社区已经提供了方便的轮子,就不要重复造轮子啦。既要有造轮子的能力,也要有不造轮子的觉悟。似乎我们正在造轮子?</p>
99
+ </blockquote>
100
+ <h2 id="实现-Portal"><a href="#实现-Portal" class="headerlink" title="实现 Portal"></a>实现 Portal</h2><p><code>Popup</code>在移动端场景下极其常见,其内部基于<code>Portal</code>实现,自身又可以作为<code>Toast</code>和<code>Modal</code>等组件的下层组件。</p>
101
+ <p>要实现<code>Popup</code>,就要先基于<a target="_blank" rel="noopener" href="https://zh-hans.reactjs.org/docs/portals.html">ReactDOM.createPortal</a>实现一个<code>Portal</code>。</p>
102
+ <p>此处结合官方文档做一个简单总结。</p>
103
+ <ol>
104
+ <li>什么是传送门?<code>Portal</code> 是一种将子节点渲染到存在于父组件以外的 <code>DOM</code> 节点的优秀的方案。</li>
105
+ <li>为什么需要传送门?父组件有 <code>overflow: hidden</code> 或 <code>z-index</code> 样式,我们又需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框。</li>
106
+ </ol>
107
+ <p>同时还有很重要的一点:<code>portal</code>与普通的 <code>React</code> 子节点行为一致,仍存在于<code>React</code>树,所以<code>Context</code>依旧可以触及。有一些弹层组件会提供<code>xxx.show()</code>的 API 形式进行弹出,这种调用形式较为方便,虽然底层也是基于<code>Portal</code>,但是内部重新执行了<code>ReactDOM.render</code>,脱离了当前主应用的<code>React</code>树,自然也无法获取到<code>Context</code>。</p>
108
+ <blockquote>
109
+ <p>推荐阅读:<a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/29880992">传送门:React Portal-程墨 Morgan</a></p>
110
+ </blockquote>
111
+ <p>清空 src 目录,新建以下文件:</p>
112
+ <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">├── index.less # 样式文件</span><br><span class="line">├── index.ts # 入口文件</span><br><span class="line">├── popup.tsx # popup 组件</span><br><span class="line">├── portal.tsx # portal 组件</span><br><span class="line">└── type.ts # 类型定义文件</span><br></pre></td></tr></table></figure>
113
+
114
+ <p>在编写代码之前,需要确定好<code>Portal</code>组件的 API。</p>
115
+ <p>与<code>ReactDOM.createPortal</code>方法接受的参数基本一致:指定的挂载节点以及内容。唯一的区别是:<code>Portal</code> 在未传入指定的挂载节点时,会创建一个节点以供使用。</p>
116
+ <p>属性 说明 类型 默认值<br>node 可选,自定义容器节点 HTMLElement -<br>children 需要传送的内容 ReactNode -<br>在<code>type.ts</code>中写入<code>Portal</code>的<code>Props</code>类型定义。</p>
117
+ <p><strong>src&#x2F;type.ts</strong></p>
118
+ <figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> <span class="title class_">PortalProps</span> = <span class="title class_">React</span>.<span class="property">PropsWithChildren</span>&lt;&#123;</span><br><span class="line"> node?: <span class="title class_">HTMLElement</span>;</span><br><span class="line">&#125;&gt;;</span><br></pre></td></tr></table></figure>
119
+
120
+ <p>现在开始编写代码:</p>
121
+ <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">ReactDOM</span> <span class="keyword">from</span> <span class="string">&#x27;react-dom&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">PortalProps</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./type&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">Portal</span> = (<span class="params">&#123; node, children &#125;: PortalProps</span>) =&gt; &#123;</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">ReactDOM</span>.<span class="title function_">createPortal</span>(children, node);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">Portal</span>;</span><br></pre></td></tr></table></figure>
122
+
123
+ <blockquote>
124
+ <p>注意:此处没有使用 React.FC 去进行声明 <a target="_blank" rel="noopener" href="https://github.com/typescript-cheatsheets/react-typescript-cheatsheet">react-typescript-cheatsheet</a>:Section 2: Getting Started &#x3D;&gt; Function Components &#x3D;&gt; What about <code>React.FC</code>&#x2F;<code>React.FunctionComponent</code>?</p>
125
+ </blockquote>
126
+ <p>代码实现比较简单,就是调用了一下<code>ReactDOM.createPortal</code>,没有考虑到使用者未传入<code>node</code>的情况:需要内部创建,组件销毁时销毁该<code>node</code>。</p>
127
+ <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&quot;react&quot;</span>;</span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">ReactDOM</span> <span class="keyword">from</span> <span class="string">&quot;react-dom&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">PortalProps</span> &#125; <span class="keyword">from</span> <span class="string">&quot;./type&quot;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 判断是否为浏览器环境</span></span><br><span class="line"><span class="keyword">const</span> canUseDOM = !!(</span><br><span class="line"> <span class="keyword">typeof</span> <span class="variable language_">window</span> !== <span class="string">&quot;undefined&quot;</span> &amp;&amp;</span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">document</span> &amp;&amp;</span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">document</span>.<span class="property">createElement</span></span><br><span class="line">);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">Portal</span> = (<span class="params">&#123; node, children &#125;: PortalProps</span>) =&gt; &#123;</span><br><span class="line"> <span class="comment">// 使用ref记录内部创建的节点 初始值为null</span></span><br><span class="line"> <span class="keyword">const</span> defaultNodeRef = <span class="title class_">React</span>.<span class="property">useRef</span>&lt;<span class="title class_">HTMLElement</span> | <span class="literal">null</span>&gt;(<span class="literal">null</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 组件卸载时 移除该节点</span></span><br><span class="line"> <span class="title class_">React</span>.<span class="title function_">useEffect</span>(</span><br><span class="line"> <span class="function">() =&gt;</span> <span class="function">() =&gt;</span> &#123;</span><br><span class="line"> <span class="keyword">if</span> (defaultNodeRef.<span class="property">current</span>) &#123;</span><br><span class="line"> <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">removeChild</span>(defaultNodeRef.<span class="property">current</span>);</span><br><span class="line"> &#125;</span><br><span class="line"> &#125;,</span><br><span class="line"> []</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果非浏览器环境 直接返回 null 服务端渲染需要</span></span><br><span class="line"> <span class="keyword">if</span> (!canUseDOM) <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 若用户未传入节点,Portal也未创建节点,则创建节点并添加至body</span></span><br><span class="line"> <span class="keyword">if</span> (!node &amp;&amp; !defaultNodeRef.<span class="property">current</span>) &#123;</span><br><span class="line"> <span class="keyword">const</span> defaultNode = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">&quot;div&quot;</span>);</span><br><span class="line"> defaultNode.<span class="property">className</span> = <span class="string">&quot;react-easy-popup__portal&quot;</span>;</span><br><span class="line"> defaultNodeRef.<span class="property">current</span> = defaultNode;</span><br><span class="line"> <span class="variable language_">document</span>.<span class="property">body</span>.<span class="title function_">appendChild</span>(defaultNode);</span><br><span class="line"> &#125;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">ReactDOM</span>.<span class="title function_">createPortal</span>(children, (node || defaultNodeRef.<span class="property">current</span>)!); <span class="comment">// 这里需要进行断言</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">Portal</span>;</span><br></pre></td></tr></table></figure>
128
+
129
+ <p>同时为了让非 ts 用户能够享受到良好的运行时错误提示,需要安装<code>prop-types</code>。</p>
130
+ <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add prop-types</span><br></pre></td></tr></table></figure>
131
+
132
+ <p><strong>src&#x2F;portal.tsx</strong></p>
133
+ <figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">// ...</span><br><span class="line"></span><br><span class="line"><span class="addition">+ Portal.propTypes = &#123;</span></span><br><span class="line"><span class="addition">+ node: canUseDOM ? PropTypes.instanceOf(HTMLElement) : PropTypes.any,</span></span><br><span class="line"><span class="addition">+ children: PropTypes.node,</span></span><br><span class="line"><span class="addition">+ &#125;;</span></span><br><span class="line"></span><br><span class="line">export default Portal;</span><br></pre></td></tr></table></figure>
134
+
135
+ <p>这样就完成了 <code>Portal</code> 组件的编写,在入口文件进行导出。</p>
136
+ <p><strong>src&#x2F;index.ts</strong></p>
137
+ <figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> &#123; <span class="keyword">default</span> <span class="keyword">as</span> <span class="title class_">Portal</span> &#125; <span class="keyword">from</span> <span class="string">&#x27;./portal&#x27;</span>;</span><br></pre></td></tr></table></figure>
138
+
139
+ <p><code>example/index.ts</code>中引入<code>Portal</code>,进行测试。</p>
140
+ <p><strong>example&#x2F;index.tsx</strong></p>
141
+ <figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line">import &quot;react-app-polyfill/ie11&quot;;</span><br><span class="line">import * as React from &quot;react&quot;;</span><br><span class="line">import * as ReactDOM from &quot;react-dom&quot;;</span><br><span class="line"><span class="deletion">- import Popup from &quot;../.&quot;; // 此处存在parcel alias 见下文</span></span><br><span class="line"><span class="deletion">- import &quot;../dist/react-easy-popup.min.css&quot;; // 此处不存在</span></span><br><span class="line"><span class="addition">+ import &#123; Portal &#125; from &#x27;../.&#x27;;</span></span><br><span class="line"></span><br><span class="line">// 创建自定义node节点</span><br><span class="line"><span class="addition">+ const node = document.createElement(&#x27;div&#x27;);</span></span><br><span class="line"><span class="addition">+ node.className = &#x27;react-easy-popup__test-node&#x27;;</span></span><br><span class="line"><span class="addition">+ document.body.appendChild(node);</span></span><br><span class="line"></span><br><span class="line">const App = () =&gt; &#123;</span><br><span class="line"> return (</span><br><span class="line"> &lt;div&gt;</span><br><span class="line"><span class="deletion">- &lt;Popup /&gt;</span></span><br><span class="line"><span class="addition">+ &lt;Portal&gt;123&lt;/Portal&gt;</span></span><br><span class="line"><span class="addition">+ &lt;Portal node=&#123;node&#125;&gt;456&lt;/Portal&gt;</span></span><br><span class="line"> &lt;/div&gt;</span><br><span class="line"> );</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">ReactDOM.render(&lt;App /&gt;, document.getElementById(&quot;root&quot;));</span><br></pre></td></tr></table></figure>
142
+
143
+ <p>在网页中看到预期的<code>DOM</code>结构。</p>
144
+ <p><img src="https://raw.githubusercontent.com/worldzhao/blog/master/images/react-popup-6.webp" alt="react-popup-6"></p>
145
+ <h2 id="实现-Popup"><a href="#实现-Popup" class="headerlink" title="实现 Popup"></a>实现 Popup</h2><h3 id="API-梳理"><a href="#API-梳理" class="headerlink" title="API 梳理"></a>API 梳理</h3><p>老规矩,先规划 API,写好类型定义,再动手写代码。</p>
146
+ <p>我写这个组件的时候参考了<a target="_blank" rel="noopener" href="https://didi.github.io/cube-ui/#/zh-CN/docs/popup">Popup-cube-ui</a>。</p>
147
+ <p>最终确定 API 如下:</p>
148
+ <p>属性 说明 类型 默认值<br>visible 可选,控制 popup 显隐 boolean false<br>position 可选,内容定位 ‘center’ &#x2F; ‘top’ &#x2F; ‘bottom’ &#x2F; ‘left’ &#x2F; ‘right’ ‘center’<br>mask 可选,控制蒙层显隐 boolean true<br>maskClosable 可选,点击蒙层是否可以关闭 boolean false<br>onClose 可选,关闭函数,若 maskClosable 为 true,点击蒙层调用该函数 function ()&#x3D;&gt;{}<br>node 可选,元素挂载节点 HTMLElement -<br>destroyOnClose 可选,关闭是否卸载内部元素 boolean false<br>wrapClassName 可选,自定义 Popup 外层容器类名 string ‘’<br><strong>src&#x2F;type.ts</strong></p>
149
+ <figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> <span class="title class_">Position</span> = <span class="string">&#x27;top&#x27;</span> | <span class="string">&#x27;right&#x27;</span> | <span class="string">&#x27;bottom&#x27;</span> | <span class="string">&#x27;left&#x27;</span> | <span class="string">&#x27;center&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> <span class="title class_">PopupPropsWithoutChildren</span> = &#123;</span><br><span class="line"> node?: <span class="title class_">HTMLElement</span>;</span><br><span class="line">&#125; &amp; <span class="keyword">typeof</span> defaultProps;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">type</span> <span class="title class_">PopupProps</span> = <span class="title class_">React</span>.<span class="property">PropsWithChildren</span>&lt;<span class="title class_">PopupPropsWithoutChildren</span>&gt;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 默认属性写在这儿很难受 实在是typescript 对react组件默认属性的声明就是得这么拧巴</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> defaultProps = &#123;</span><br><span class="line"> <span class="attr">visible</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">position</span>: <span class="string">&#x27;center&#x27;</span> <span class="keyword">as</span> <span class="title class_">Position</span>,</span><br><span class="line"> <span class="attr">mask</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">maskClosable</span>: <span class="literal">false</span>,</span><br><span class="line"> <span class="attr">onClose</span>: <span class="function">() =&gt;</span> &#123;&#125;,</span><br><span class="line"> <span class="attr">destroyOnClose</span>: <span class="literal">false</span>,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
150
+
151
+ <p>编写 <code>Popup</code> 的基本结构。</p>
152
+ <p><strong>src&#x2F;popup.tsx</strong></p>
153
+ <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> <span class="title class_">React</span> <span class="keyword">from</span> <span class="string">&#x27;react&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="title class_">PropTypes</span> <span class="keyword">from</span> <span class="string">&#x27;prop-types&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; <span class="title class_">PopupProps</span>, defaultProps &#125; <span class="keyword">from</span> <span class="string">&#x27;./type&#x27;</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">&#x27;./index.less&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">Popup</span> = (<span class="params">props: PopupProps</span>) =&gt; &#123;</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(props);</span><br><span class="line"> <span class="keyword">return</span> <span class="language-xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">className</span>=<span class="string">&quot;react-easy-popup&quot;</span>&gt;</span>hello,react-easy-popup<span class="tag">&lt;/<span class="name">div</span>&gt;</span></span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="title class_">Popup</span>.<span class="property">propTypes</span> = &#123;</span><br><span class="line"> <span class="attr">visible</span>: <span class="title class_">PropTypes</span>.<span class="property">bool</span>,</span><br><span class="line"> <span class="attr">position</span>: <span class="title class_">PropTypes</span>.<span class="title function_">oneOf</span>([<span class="string">&#x27;top&#x27;</span>, <span class="string">&#x27;right&#x27;</span>, <span class="string">&#x27;bottom&#x27;</span>, <span class="string">&#x27;left&#x27;</span>, <span class="string">&#x27;center&#x27;</span>]),</span><br><span class="line"> <span class="attr">mask</span>: <span class="title class_">PropTypes</span>.<span class="property">bool</span>,</span><br><span class="line"> <span class="attr">maskClosable</span>: <span class="title class_">PropTypes</span>.<span class="property">bool</span>,</span><br><span class="line"> <span class="attr">onClose</span>: <span class="title class_">PropTypes</span>.<span class="property">func</span>,</span><br><span class="line"> <span class="attr">stopScrollUnderMask</span>: <span class="title class_">PropTypes</span>.<span class="property">bool</span>,</span><br><span class="line"> <span class="attr">destroyOnClose</span>: <span class="title class_">PropTypes</span>.<span class="property">bool</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="title class_">Popup</span>.<span class="property">defaultProps</span> = defaultProps;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="title class_">Popup</span>;</span><br></pre></td></tr></table></figure>
154
+
155
+ <p>在入口文件进行导出。</p>
156
+ <p><strong>src&#x2F;index.ts</strong></p>
157
+ <figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="addition">+ export &#123; default as Popup &#125; from &#x27;./popup&#x27;;</span></span><br></pre></td></tr></table></figure>
158
+
159
+ <h3 id="前置-CSS-知识"><a href="#前置-CSS-知识" class="headerlink" title="前置 CSS 知识"></a>前置 CSS 知识</h3><p>在正式开发逻辑之前,先明确一点:</p>
160
+ <p>蒙层 Mask 以及内容 Content 入场以及出场均有动画效果。具体表现为:蒙层为 Fade 动画,内容则取决于当前 position,比如内容在中间(position &#x3D;&#x3D;&#x3D; ‘center’),则其动画效果为 Fade,如果在左边(position &#x3D;&#x3D;&#x3D; ‘left’),则其动画效果为 SlideRight,其他 position 以此类推。</p>
161
+ <p>再回顾张鑫旭大大的一篇文章:<a target="_blank" rel="noopener" href="https://www.zhangxinxu.com/wordpress/2013/05/transition-visibility-show-hide/">小 tip: transition 与 visibility</a></p>
162
+ <p>划重点:</p>
163
+ <ol>
164
+ <li><code>opacity</code>的值在 <code>0</code> 与 <code>1</code> 之间相互过渡(<code>transition</code>)可以实现 Fade 动画。然而元素即使透明度变成 0,肉眼看不见,在页面上却依旧点击,还是可以覆盖其他元素的,我们希望元素淡出动画结束后,元素可以自动隐藏;</li>
165
+ <li>元素隐藏很容易想到<code>display:none</code>。而<code>display:none</code> 无法应用 <code>transition</code> 效果,甚至是破坏作用;</li>
166
+ <li><code>visibility:hidden</code> 可以看成 <code>visibility:0</code>;<code>visibility:visible</code> 可以看成 <code>visibility:1</code>。实际上,只要 <code>visibility</code> 的值大于 <code>0</code> 就是显示的。</li>
167
+ </ol>
168
+ <p>总结一下:我们想用<code>opacity</code>实现淡入淡出的 Fade 动画,但是希望元素淡出后,能够隐藏,而不仅仅是透明度为 <code>0</code>,覆盖在其他元素上。所以需要配置 <code>visibility</code>属性,淡出动画结束时,<code>visibility</code>值也由<code>visible</code>变为了<code>hidden</code>,元素成功隐藏。</p>
169
+ <blockquote>
170
+ <p>如果蒙层淡出动画结束后仅仅是透明度变为 0,却未隐藏,那么蒙层在视觉上虽然消失了,实际还是覆盖在页面上,就无法触发页面上的事件。</p>
171
+ </blockquote>
172
+ <h3 id="预设动画样式"><a href="#预设动画样式" class="headerlink" title="预设动画样式"></a>预设动画样式</h3><p>借助<a target="_blank" rel="noopener" href="https://github.com/reactjs/react-transition-group">react-transition-group</a>完成动画效果,需要内置一些动画样式。</p>
173
+ <p>新建<code>animation.less</code>,写入以下动画样式。</p>
174
+ <p>展开查看代码</p>
175
+ <h3 id="完成基本逻辑"><a href="#完成基本逻辑" class="headerlink" title="完成基本逻辑"></a>完成基本逻辑</h3><p>安装相关依赖。</p>
176
+ <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">yarn add react-transition-group classnames</span><br><span class="line"></span><br><span class="line">yarn add @types/classnames @types/react-transition-group --dev</span><br></pre></td></tr></table></figure>
177
+
178
+ <ul>
179
+ <li>node: 透传给<code>Portal</code>即可;</li>
180
+ <li>visible: 将该属性赋值给蒙层以及内容外层<code>CSSTransition</code>组件的<code>in</code>属性,控制蒙层以及内容的过渡显隐;</li>
181
+ <li>destroyOnClose: 将该属性赋值给内容外层<code>CSSTransition</code>组件的<code>unmountOnExit</code>属性,决定隐藏时是否卸载内容节点;</li>
182
+ <li>wrapClassName: 拼接在外层容器节点的 <code>className</code> ;</li>
183
+ <li>position: 1)用于获取内容节点的对应动画名称;2)决定容器节点以及内容节点类名,配合样式决定内容节点位置;</li>
184
+ <li>mask: 决定蒙层节点的 <code>className</code>,从而控制蒙层有无;</li>
185
+ <li>maskClose: 决定点击蒙层是否触发 onClose 函数。</li>
186
+ </ul>
187
+ <p>用过 <code>antd</code> 的同学都知道,<code>antd</code>的<code>modal</code>在首次<code>visible === true</code>之前,内容节点是不会被挂载的,只有首次 <code>visible === true</code>,内容节点才挂载,而后都是样式上隐藏,而不会去卸载内容节点,除非手动设置 <code>destroyOnClose</code> 属性,我们也顺带实现这个特点。</p>
188
+ <blockquote>
189
+ <p>代码逻辑比较简单,在拼接类名时注意配合样式文件一起阅读,重要的点都有注释标出。</p>
190
+ </blockquote>
191
+ <p>展开查看逻辑代码<br>展开查看样式代码<br>组件编写完毕,接下来在<code>example/index.ts</code>中编写相关示例测试功能即可。</p>
192
+ <p><a target="_blank" rel="noopener" href="https://github.com/worldzhao/react-easy-popup/blob/master/example/index.tsx">example&#x2F;index.ts</a></p>
193
+ <h2 id="部署-github-pages"><a href="#部署-github-pages" class="headerlink" title="部署 github pages"></a>部署 github pages</h2><p>相信大多数人使用一个 npm 包会先看示例再看文档。</p>
194
+ <p>接下来将 <code>example</code> 中的示例项目打包,并部署到 github pages 上。</p>
195
+ <p>安装<code>gh-pages</code>。</p>
196
+ <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add gh-pages --dev</span><br></pre></td></tr></table></figure>
197
+
198
+ <p>package.json 新增脚本。</p>
199
+ <p><strong>package.json</strong></p>
200
+ <figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line"> <span class="attr">&quot;scripts&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> <span class="attr">&quot;predeploy&quot;</span><span class="punctuation">:</span> <span class="string">&quot;npm run build &amp;&amp; cd example &amp;&amp; npm run build&quot;</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">&quot;deploy&quot;</span><span class="punctuation">:</span> <span class="string">&quot;gh-pages -d ./example/dist&quot;</span></span><br><span class="line"> <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>
201
+
202
+ <p>由于 gh-pages 默认部署在<code>https://username.github.io/repo</code>下,而非根路径。为了能够正确引用到静态资源,还需要修改打包的 <code>public-url</code>。</p>
203
+ <p>修改 example 的 package.json 中的打包命令:</p>
204
+ <figure class="highlight diff"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line"> &quot;scripts&quot;:&#123;</span><br><span class="line"><span class="deletion">- &quot;build&quot;: &quot;parcel build index.html&quot;</span></span><br><span class="line"><span class="addition">+ &quot;build&quot;: &quot;parcel build index.html --public-url https://username.github.io/repo&quot;</span></span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
205
+
206
+ <blockquote>
207
+ <p><code>https://username.github.io/repo</code>记得换成你自己的哦。</p>
208
+ </blockquote>
209
+ <p>在根目录下执行 <code>yarn deploy</code>,等脚本执行完再去看看吧。</p>
210
+ <h2 id="编写-README-md"><a href="#编写-README-md" class="headerlink" title="编写 README.md"></a>编写 README.md</h2><p>一份规范的 README 会显得作者很专业,此处使用<code>readme-md-generator</code>生成基本框架,向里面填充内容即可。</p>
211
+ <p><a target="_blank" rel="noopener" href="https://github.com/kefranabg/readme-md-generator">readme-md-generator</a>:📄 CLI that generates beautiful README.md files</p>
212
+ <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npx readme-md-generator -y</span><br></pre></td></tr></table></figure>
213
+
214
+ <p><a target="_blank" rel="noopener" href="https://github.com/worldzhao/react-easy-popup/blob/master/README.md">README.md</a></p>
215
+ <h2 id="使用-np-发包"><a href="#使用-np-发包" class="headerlink" title="使用 np 发包"></a>使用 np 发包</h2><p>在上一篇文章中,专门编写了一个脚本来处理以下六点内容:</p>
216
+ <ol>
217
+ <li>版本更新</li>
218
+ <li><del>生成 CHANGELOG</del></li>
219
+ <li>推送至 git 仓库</li>
220
+ <li>组件打包</li>
221
+ <li>发布至 npm</li>
222
+ <li>打 tag 并推送至 git</li>
223
+ </ol>
224
+ <p>这次就不生成 CHANGELOG 文件了,其他五点配合<code>np</code>,操作十分简单。</p>
225
+ <p><a target="_blank" rel="noopener" href="https://github.com/sindresorhus/np">np</a>:A better <code>npm publish</code></p>
226
+ <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">yarn add np --dev</span><br></pre></td></tr></table></figure>
227
+
228
+ <p><strong>package.json</strong></p>
229
+ <figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span></span><br><span class="line"> <span class="attr">&quot;scripts&quot;</span><span class="punctuation">:</span> <span class="punctuation">&#123;</span></span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> <span class="attr">&quot;release&quot;</span><span class="punctuation">:</span> <span class="string">&quot;np --no-yarn --no-tests --no-cleanup&quot;</span></span><br><span class="line"> <span class="punctuation">&#125;</span></span><br><span class="line"><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure>
230
+
231
+ <figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm login</span><br><span class="line">npm run release</span><br></pre></td></tr></table></figure>
232
+
233
+ <ul>
234
+ <li><code>--no-yarn</code>: 不使用 <code>yarn</code>。发包时出现 npm 与 yarn 之间的一些问题;</li>
235
+ <li><code>--no-tests</code>:测试用例暂时还未编写,先跳过;</li>
236
+ <li><code>--no-cleanup</code>:发包时不要重新安装 node_modules;</li>
237
+ <li>首次发布新包时可能会<a target="_blank" rel="noopener" href="https://github.com/sindresorhus/np/issues/398">报错</a>,因为 np 进行了 npm 双因素认证,但依旧可以发布成功,等后续更新。</li>
238
+ </ul>
239
+ <p>更多配置请查看官方文档。</p>
240
+ <blockquote>
241
+ <p>本文转载自 <code>https://github.com/worldzhao/blog/issues/2</code></p>
242
+ </blockquote>
243
+ </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/624d1918.html">https://xuehuayu.cn/article/624d1918.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/46ee4850.html">再面试浏览器和网络协议,看完这36点就稳了</a><a class="next" href="/article/29abbd1c.html">一个不常见的开发问题,你遇到过吗?</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
244
+ const serverURL = origin.includes('cainiaoblog') ? 'https://cmts.cainiaoblog.cn' : 'https://cmts.xuehuayu.cn'
245
+ const locale = {
246
+ placeholder: '请正确填写昵称和邮箱,方便接收回复通知~',
247
+ sofa: '沙发空缺中,还不快抢~',
248
+ };
249
+ Waline.init({
250
+ el: '#vcomment',
251
+ serverURL: serverURL,
252
+ pageSize: '20',
253
+ visitor: true, // 阅读量统计
254
+ requiredMeta: ['nick', 'mail'],
255
+ emoji: [
256
+ 'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/weibo',
257
+ 'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/qq',
258
+ 'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/tw-emoji',
259
+ ],
260
+ locale,
261
+ pageview: true
262
+ })
263
+ </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() {
264
+ var val = $(this).val()
265
+ 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>'
266
+ 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>'
267
+ 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>'
268
+
269
+ if (val === 'self') {
270
+ $('#search').html(self)
271
+ var search_path = 'search.xml';
272
+ if (search_path.length == 0) {
273
+ search_path = 'search.xml';
274
+ }
275
+ var path = 'https://cdn.staticaly.com/gh/npljy/npljy.github.io/main/' + search_path;
276
+ searchFunc(path, 'local-search-input', 'local-search-result');
277
+ } else if (val === 'baidu') {
278
+ $('#search').html(baidu)
279
+ } else if (val === 'google') {
280
+ $('#search').html(google)
281
+ }
282
+ })</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(){
283
+ var cnb =window.location.origin.indexOf('cainiaoblog') !==-1
284
+ if (cnb) {
285
+ var gxba =document.getElementById('gxba')
286
+ var gaba =document.getElementById('gaba')
287
+ var gabaLink =document.getElementById('gaba-link')
288
+ gxba.innerText ='京ICP备20007647号-1'
289
+ gaba.innerText ='京公网安备 11010802031254号'
290
+ gabaLink.setAttribute('href','http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010802031254')
291
+ }
292
+ })()</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>