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.
- package/CNAME +1 -0
- package/MP_verify_o6dHCK5mIkxNpzvZ.txt +1 -0
- package/README.md +8 -0
- package/about/index.html +54 -0
- package/ads.txt +1 -0
- package/app/manual.html +104 -0
- package/appConfigs/config +1 -0
- package/archives/2018/index.html +48 -0
- package/archives/2019/index.html +48 -0
- package/archives/2019/page/2/index.html +48 -0
- package/archives/2020/index.html +48 -0
- package/archives/2021/index.html +48 -0
- package/archives/2022/index.html +48 -0
- package/archives/2023/index.html +48 -0
- package/archives/index.html +48 -0
- package/archives/page/2/index.html +48 -0
- package/archives/page/3/index.html +48 -0
- package/article/10047.html +148 -0
- package/article/10164.html +64 -0
- package/article/10624.html +436 -0
- package/article/10745.html +586 -0
- package/article/11674.html +71 -0
- package/article/11939.html +72 -0
- package/article/12238.html +86 -0
- package/article/1230.html +533 -0
- package/article/13265.html +69 -0
- package/article/13823.html +78 -0
- package/article/14125.html +72 -0
- package/article/14192.html +561 -0
- package/article/14375.html +85 -0
- package/article/14744.html +83 -0
- package/article/14936.html +451 -0
- package/article/1518.html +250 -0
- package/article/15428.html +71 -0
- package/article/15518.html +288 -0
- package/article/15569.html +418 -0
- package/article/1566.html +122 -0
- package/article/16017.html +156 -0
- package/article/16947.html +440 -0
- package/article/17096.html +82 -0
- package/article/17246.html +89 -0
- package/article/17575.html +350 -0
- package/article/179.html +757 -0
- package/article/18039.html +233 -0
- package/article/18071.html +375 -0
- package/article/18142.html +71 -0
- package/article/19909.html +160 -0
- package/article/20014.html +89 -0
- package/article/20021.html +389 -0
- package/article/20037.html +280 -0
- package/article/20056.html +95 -0
- package/article/20093.html +404 -0
- package/article/2021.html +607 -0
- package/article/20459.html +115 -0
- package/article/20613.html +263 -0
- package/article/20635.html +71 -0
- package/article/21317.html +71 -0
- package/article/21320.html +90 -0
- package/article/21782.html +548 -0
- package/article/22230.html +435 -0
- package/article/22237.html +347 -0
- package/article/22398.html +109 -0
- package/article/22422.html +270 -0
- package/article/22565.html +300 -0
- package/article/23046.html +250 -0
- package/article/23083.html +215 -0
- package/article/23195.html +297 -0
- package/article/23301.html +835 -0
- package/article/23589.html +285 -0
- package/article/23655.html +338 -0
- package/article/23864.html +76 -0
- package/article/23914.html +96 -0
- package/article/24169.html +135 -0
- package/article/24581.html +349 -0
- package/article/2475.html +500 -0
- package/article/24826.html +77 -0
- package/article/24924.html +247 -0
- package/article/25164.html +1495 -0
- package/article/25318.html +394 -0
- package/article/25710.html +1129 -0
- package/article/25734.html +83 -0
- package/article/26083.html +174 -0
- package/article/26147.html +283 -0
- package/article/26291.html +110 -0
- package/article/26354.html +71 -0
- package/article/26624.html +145 -0
- package/article/26639.html +298 -0
- package/article/26850.html +138 -0
- package/article/26940.html +258 -0
- package/article/27796.html +190 -0
- package/article/27800.html +161 -0
- package/article/28963.html +159 -0
- package/article/28976.html +247 -0
- package/article/29042.html +72 -0
- package/article/29596.html +81 -0
- package/article/29619.html +183 -0
- package/article/29623.html +329 -0
- package/article/29829.html +205 -0
- package/article/29abbd1c.html +112 -0
- package/article/30215.html +178 -0
- package/article/30552.html +78 -0
- package/article/30818.html +201 -0
- package/article/31330.html +364 -0
- package/article/3154.html +577 -0
- package/article/31695.html +348 -0
- package/article/31826.html +71 -0
- package/article/31843.html +132 -0
- package/article/32280.html +212 -0
- package/article/32431.html +74 -0
- package/article/32443.html +72 -0
- package/article/32732.html +122 -0
- package/article/3286.html +251 -0
- package/article/32960.html +198 -0
- package/article/33713.html +72 -0
- package/article/33899.html +615 -0
- package/article/34351.html +74 -0
- package/article/34372.html +89 -0
- package/article/34410.html +74 -0
- package/article/34431.html +118 -0
- package/article/3447.html +75 -0
- package/article/34474.html +142 -0
- package/article/34871.html +147 -0
- package/article/35397.html +175 -0
- package/article/3544.html +199 -0
- package/article/35503.html +115 -0
- package/article/35624.html +84 -0
- package/article/35697.html +384 -0
- package/article/35863.html +73 -0
- package/article/36037.html +253 -0
- package/article/36118.html +71 -0
- package/article/3621.html +119 -0
- package/article/36251.html +296 -0
- package/article/36642.html +352 -0
- package/article/37102.html +763 -0
- package/article/37336.html +351 -0
- package/article/37351.html +89 -0
- package/article/3760.html +294 -0
- package/article/37749.html +75 -0
- package/article/37789.html +83 -0
- package/article/38005.html +765 -0
- package/article/38009.html +284 -0
- package/article/38269.html +194 -0
- package/article/38344.html +88 -0
- package/article/38359.html +334 -0
- package/article/38423.html +379 -0
- package/article/38530.html +213 -0
- package/article/38743.html +268 -0
- package/article/38753.html +204 -0
- package/article/38881.html +71 -0
- package/article/39087.html +414 -0
- package/article/39309.html +84 -0
- package/article/39486.html +74 -0
- package/article/39637.html +306 -0
- package/article/39839.html +72 -0
- package/article/39937.html +152 -0
- package/article/39970.html +71 -0
- package/article/402.html +559 -0
- package/article/40698.html +71 -0
- package/article/40811.html +71 -0
- package/article/40964.html +462 -0
- package/article/41011.html +322 -0
- package/article/41152.html +78 -0
- package/article/41696.html +71 -0
- package/article/41717.html +281 -0
- package/article/41777.html +149 -0
- package/article/419.html +985 -0
- package/article/4233.html +136 -0
- package/article/4292.html +86 -0
- package/article/43302.html +96 -0
- package/article/43397.html +106 -0
- package/article/43724.html +281 -0
- package/article/43999.html +224 -0
- package/article/44426.html +77 -0
- package/article/44964.html +137 -0
- package/article/45004.html +123 -0
- package/article/4510.html +83 -0
- package/article/45156.html +320 -0
- package/article/45157.html +52 -0
- package/article/45267.html +87 -0
- package/article/45641.html +207 -0
- package/article/45960.html +75 -0
- package/article/46100.html +1262 -0
- package/article/46366.html +74 -0
- package/article/46771.html +96 -0
- package/article/46911.html +517 -0
- package/article/46948.html +137 -0
- package/article/46ee4850.html +349 -0
- package/article/47019.html +84 -0
- package/article/47532.html +94 -0
- package/article/4783.html +429 -0
- package/article/47972.html +72 -0
- package/article/48216.html +127 -0
- package/article/48252.html +185 -0
- package/article/48520.html +74 -0
- package/article/48722.html +75 -0
- package/article/48807.html +99 -0
- package/article/49126.html +323 -0
- package/article/50001.html +88 -0
- package/article/50058.html +151 -0
- package/article/50343.html +410 -0
- package/article/50510.html +82 -0
- package/article/51082.html +98 -0
- package/article/51677.html +111 -0
- package/article/5204.html +1085 -0
- package/article/52500.html +538 -0
- package/article/52689.html +259 -0
- package/article/52877.html +569 -0
- package/article/53125.html +191 -0
- package/article/53428.html +251 -0
- package/article/53524.html +74 -0
- package/article/53698.html +322 -0
- package/article/53803e6.html +93 -0
- package/article/53924.html +291 -0
- package/article/53962.html +253 -0
- package/article/54006.html +71 -0
- package/article/54018.html +88 -0
- package/article/54161.html +83 -0
- package/article/54178.html +69 -0
- package/article/54258.html +327 -0
- package/article/54295.html +214 -0
- package/article/54313.html +1976 -0
- package/article/54351.html +90 -0
- package/article/54604.html +80 -0
- package/article/54632.html +71 -0
- package/article/54912.html +756 -0
- package/article/54918.html +129 -0
- package/article/55119.html +136 -0
- package/article/55756.html +118 -0
- package/article/55769.html +136 -0
- package/article/55870.html +161 -0
- package/article/56139.html +92 -0
- package/article/56557.html +69 -0
- package/article/56604.html +69 -0
- package/article/56816.html +266 -0
- package/article/56926.html +153 -0
- package/article/57747.html +409 -0
- package/article/57965.html +475 -0
- package/article/58816.html +78 -0
- package/article/59059.html +435 -0
- package/article/59095.html +350 -0
- package/article/59349.html +244 -0
- package/article/59402.html +216 -0
- package/article/59448.html +55 -0
- package/article/59870.html +94 -0
- package/article/59877.html +208 -0
- package/article/5dbb6f41.html +86 -0
- package/article/6008.html +75 -0
- package/article/60327.html +126 -0
- package/article/6035.html +76 -0
- package/article/60428.html +71 -0
- package/article/60544.html +128 -0
- package/article/61319.html +95 -0
- package/article/61367.html +93 -0
- package/article/624d1918.html +292 -0
- package/article/62543.html +71 -0
- package/article/62887.html +169 -0
- package/article/63074.html +120 -0
- package/article/63266.html +311 -0
- package/article/63351.html +74 -0
- package/article/63542.html +104 -0
- package/article/63824.html +275 -0
- package/article/64051.html +653 -0
- package/article/64189b69.html +72 -0
- package/article/64354.html +75 -0
- package/article/64869.html +79 -0
- package/article/64923.html +596 -0
- package/article/65128.html +95 -0
- package/article/65295.html +79 -0
- package/article/6888.html +75 -0
- package/article/7065.html +84 -0
- package/article/7455.html +85 -0
- package/article/7a3ef8f.html +315 -0
- package/article/8140.html +188 -0
- package/article/8190.html +85 -0
- package/article/8225.html +78 -0
- package/article/84c9ccd6.html +159 -0
- package/article/8f9a9ae3.html +82 -0
- package/article/9174.html +73 -0
- package/article/9738.html +294 -0
- package/article/a4145266.html +71 -0
- package/article/bedea419.html +72 -0
- package/article/bf7e7421.html +78 -0
- package/article/c1867fbf.html +103 -0
- package/article/c5576490.html +70 -0
- package/article/dcb0db28.html +76 -0
- package/article/fc3b727a.html +79 -0
- package/atom.xml +445 -0
- package/baidusitemap.xml +1079 -0
- package/blocks/dns.txt +264 -0
- package/blocks/filter.txt +1070 -0
- package/blocks/script.js +42 -0
- package/categories/CDN/index.html +48 -0
- package/categories/IDE/index.html +48 -0
- package/categories/git/index.html +48 -0
- package/categories/hexo/index.html +48 -0
- package/categories//345/205/254/345/205/261/350/212/202/346/227/245/index.html +48 -0
- package/categories//345/210/206/344/272/253/index.html +48 -0
- package/categories//345/211/215/347/253/257/index.html +48 -0
- package/categories//345/211/215/347/253/257/page/2/index.html +48 -0
- package/categories//345/211/215/347/253/257//346/265/217/350/247/210/345/231/250/index.html +48 -0
- 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
- package/categories//345/215/232/345/256/242/index.html +48 -0
- package/categories//345/267/245/345/205/267/index.html +48 -0
- package/categories//345/271/277/345/221/212/350/277/207/346/273/244/index.html +48 -0
- 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
- package/categories//346/255/243/345/210/231/index.html +48 -0
- package/categories//346/265/217/350/247/210/345/231/250/index.html +48 -0
- package/categories//347/256/227/346/263/225/index.html +48 -0
- package/categories//350/265/204/350/256/257/index.html +48 -0
- package/categories//350/275/273/346/235/276/344/270/200/345/210/273/index.html +48 -0
- package/cdn/css/font-awesome.min.css +4 -0
- package/cdn/css/grids-responsive-min.css +7 -0
- package/cdn/css/normalize.css +349 -0
- package/cdn/css/pure-min.css +11 -0
- package/cdn/fonts/fontawesome-webfont.eot +0 -0
- package/cdn/fonts/fontawesome-webfont.svg +2671 -0
- package/cdn/fonts/fontawesome-webfont.ttf +0 -0
- package/cdn/fonts/fontawesome-webfont.woff +0 -0
- package/cdn/fonts/fontawesome-webfont.woff2 +0 -0
- package/cdn/js/Valine.min.js +17 -0
- package/cdn/js/canvas-nest.js +1 -0
- package/cdn/js/clipboard.min.js +7 -0
- package/cdn/js/crypto-js.js +25 -0
- package/cdn/js/jquery.min.js +2 -0
- package/css/default.css +1144 -0
- package/css/donate.css +338 -0
- package/css/hbe.style.css +749 -0
- package/css/style.css +2095 -0
- package/donate/index.html +39 -0
- package/google917dcf72f6e5c7f5.html +1 -0
- package/guestbook/index.html +68 -0
- package/hosts/ads.txt +149 -0
- package/hosts/flash.txt +9 -0
- package/hosts/github.txt +6 -0
- package/hosts/google.txt +16936 -0
- package/hosts/winrar.txt +2 -0
- package/img/alipay.svg +46 -0
- package/img/bitcoin.svg +135 -0
- package/img/blog.ico +0 -0
- package/img/blog.png +0 -0
- package/img/gaba.png +0 -0
- package/img/github.svg +1 -0
- package/img/like.svg +1 -0
- package/img/mp-mini.jpg +0 -0
- package/img/mp.png +0 -0
- package/img/paypal.svg +63 -0
- package/img/upy_logo.svg +59 -0
- package/img/wechat.svg +49 -0
- package/index.html +48 -0
- package/jd_root.txt +1 -0
- package/js/codeblock-resizer.js +51 -0
- package/js/dark.js +12 -0
- package/js/donate.js +87 -0
- package/js/fixedPage.js +106 -0
- package/js/gitment.browser.js +3751 -0
- package/js/search.js +86 -0
- package/js/share.js +60 -0
- package/js/smartresize.js +32 -0
- package/js/totop.js +12 -0
- package/kankan/index.html +77 -0
- package/lib/hbe.js +293 -0
- package/package.json +25 -0
- package/page/10/index.html +49 -0
- package/page/11/index.html +59 -0
- package/page/12/index.html +67 -0
- package/page/13/index.html +63 -0
- package/page/14/index.html +66 -0
- package/page/15/index.html +60 -0
- package/page/16/index.html +60 -0
- package/page/17/index.html +62 -0
- package/page/18/index.html +61 -0
- package/page/19/index.html +58 -0
- package/page/2/index.html +48 -0
- package/page/20/index.html +57 -0
- package/page/21/index.html +61 -0
- package/page/22/index.html +55 -0
- package/page/23/index.html +64 -0
- package/page/24/index.html +60 -0
- package/page/25/index.html +58 -0
- package/page/26/index.html +48 -0
- package/page/27/index.html +58 -0
- package/page/3/index.html +48 -0
- package/page/4/index.html +48 -0
- package/page/5/index.html +51 -0
- package/page/6/index.html +51 -0
- package/page/7/index.html +51 -0
- package/page/8/index.html +53 -0
- package/page/9/index.html +48 -0
- package/randomcall/README.html +1 -0
- package/randomcall/lucker.html +290 -0
- package/randomcall/lucky.png +0 -0
- package/robots.txt +22 -0
- package/search.xml +6740 -0
- package/sitemap.xml +2669 -0
- package/tags//345/216/237/345/210/233/index.html +48 -0
- package/tags//345/250/261/344/271/220/index.html +48 -0
- package/tags//346/211/213/345/206/231/index.html +48 -0
- package/tags//347/226/253/346/203/205/index.html +48 -0
- package/tags//350/275/254/350/275/275/index.html +48 -0
- package/tags//350/275/254/350/275/275/page/2/index.html +48 -0
- package/v.html +90 -0
|
@@ -0,0 +1,347 @@
|
|
|
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>轻松理解webpack热更新原理 | 前端壹菜鸟</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">轻松理解webpack热更新原理</h1><div class="post-meta"><span class="date">2019-12-05</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"> 4.4k</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"> 17</span><span class="post-meta-item-text"> 分钟</span></span></span></div><a class="disqus-comment-count" href="/article/22237.html#vcomment"><span class="waline-comment-count" data-path="/article/22237.html"></span><span> 条评论</span></a><div class="post-content"><p><code>原文地址:https://juejin.im/post/5de0cfe46fb9a071665d3df0</code></p>
|
|
20
|
+
<blockquote>
|
|
21
|
+
<p><code>Hot Module Replacement</code>,简称<code>HMR</code>,无需完全刷新整个页面的同时,更新模块。<code>HMR</code>的好处,在日常开发工作中体会颇深:<strong>节省宝贵的开发时间、提升开发体验</strong>。</p>
|
|
22
|
+
</blockquote>
|
|
23
|
+
<span id="more"></span>
|
|
24
|
+
|
|
25
|
+
<h2 id="一、前言-webpack热更新"><a href="#一、前言-webpack热更新" class="headerlink" title="一、前言 - webpack热更新"></a>一、前言 - webpack热更新</h2><blockquote>
|
|
26
|
+
<p><code>Hot Module Replacement</code>,简称<code>HMR</code>,无需完全刷新整个页面的同时,更新模块。<code>HMR</code>的好处,在日常开发工作中体会颇深:<strong>节省宝贵的开发时间、提升开发体验</strong>。</p>
|
|
27
|
+
</blockquote>
|
|
28
|
+
<p>刷新我们一般分为两种:</p>
|
|
29
|
+
<ul>
|
|
30
|
+
<li>一种是页面刷新,不保留页面状态,就是简单粗暴,直接<code>window.location.reload()</code>。</li>
|
|
31
|
+
<li>另一种是基于<code>WDS (Webpack-dev-server)</code>的模块热替换,只需要局部刷新页面上发生变化的模块,同时可以保留当前的页面状态,比如复选框的选中状态、输入框的输入等。</li>
|
|
32
|
+
</ul>
|
|
33
|
+
<p><code>HMR</code>作为一个<code>Webpack</code>内置的功能,可以通过<code>HotModuleReplacementPlugin</code>或<code>--hot</code>开启。那么,<code>HMR</code>到底是怎么实现热更新的呢?下面让我们来了解一下吧!</p>
|
|
34
|
+
<h2 id="二、webpack的编译构建过程"><a href="#二、webpack的编译构建过程" class="headerlink" title="二、webpack的编译构建过程"></a>二、webpack的编译构建过程</h2><p>项目启动后,进行构建打包,控制台会输出构建过程,我们可以观察到生成了一个 <strong>Hash值</strong>:<code>a93fd735d02d98633356</code>。</p>
|
|
35
|
+
<p><img src="https://user-gold-cdn.xitu.io/2019/12/1/16ec043909c70b12?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="首次构建控制台输出日志"><br>然后,在我们每次修改代码保存后,控制台都会出现 <code>Compiling…</code>字样,触发新的编译中…可以在控制台中观察到:</p>
|
|
36
|
+
<ul>
|
|
37
|
+
<li><strong>新的Hash值</strong>:<code>a61bdd6e82294ed06fa3</code></li>
|
|
38
|
+
<li><strong>新的json文件</strong>: <code>a93fd735d02d98633356.hot-update.json</code></li>
|
|
39
|
+
<li><strong>新的js文件</strong>:<code>index.a93fd735d02d98633356.hot-update.js</code></li>
|
|
40
|
+
</ul>
|
|
41
|
+
<p><img src="https://user-gold-cdn.xitu.io/2019/12/1/16ec04454e1167f7?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="修改代码的编译日志"><br>首先,我们知道<code>Hash</code>值代表每一次编译的标识。其次,根据新生成文件名可以发现,上次输出的<code>Hash</code>值会作为本次编译新生成的文件标识。依次类推,本次输出的<code>Hash</code>值会被作为下次热更新的标识。</p>
|
|
42
|
+
<p>然后看一下,新生成的文件是什么?每次修改代码,紧接着触发重新编译,然后浏览器就会发出 2 次请求。请求的便是本次新生成的 2 个文件。如下:</p>
|
|
43
|
+
<p><img src="https://user-gold-cdn.xitu.io/2019/12/1/16ec04289af752da?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="浏览器请求"><br>首先看<code>json</code>文件,返回的结果中,<code>h</code>代表本次新生成的<code>Hash</code>值,用于下次文件热更新请求的前缀。<code>c</code>表示当前要热更新的文件对应的是<code>index</code>模块。<br>再看下生成的<code>js</code>文件,那就是本次修改的代码,重新编译打包后的。</p>
|
|
44
|
+
<p><img src="https://user-gold-cdn.xitu.io/2019/12/1/16ec04316d6ac5e3?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>还有一种情况是,如果没有任何代码改动,直接保存文件,控制台也会输出编译打包信息的。</p>
|
|
45
|
+
<ul>
|
|
46
|
+
<li><strong>新的Hash值</strong>:<code>d2e4208eca62aa1c5389</code></li>
|
|
47
|
+
<li><strong>新的json文件</strong>:<code>a61bdd6e82294ed06fa3.hot-update.json</code></li>
|
|
48
|
+
</ul>
|
|
49
|
+
<p><img src="https://user-gold-cdn.xitu.io/2019/12/1/16ec04bd0d47eae4?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>但是我们发现,并没有生成新的<code>js</code>文件,因为没有改动任何代码,同时浏览器发出的请求,可以看到<code>c</code>值为空,代表本次没有需要更新的代码。</p>
|
|
50
|
+
<p><img src="https://user-gold-cdn.xitu.io/2019/12/1/16ec04c7b158cb3b?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>小声说下,<code>webapck</code>以前的版本这种情况<code>hash</code>值是不会变的,后面可能出于什么原因改版了。细节不用在意,了解原理才是真谛!!!</p>
|
|
51
|
+
<p>最后思考下🤔,浏览器是如何知道本地代码重新编译了,并迅速请求了新生成的文件?是谁告知了浏览器?浏览器获得这些文件又是如何热更新成功的?那让我们带着疑问看下热更新的过程,从源码的角度看原理。</p>
|
|
52
|
+
<h2 id="三、热更新实现原理"><a href="#三、热更新实现原理" class="headerlink" title="三、热更新实现原理"></a>三、热更新实现原理</h2><p>相信大家都会配置<code>webpack-dev-server</code>热更新,我就不示意例子了。自己网上查下即可。接下来我们就来看下<code>webpack-dev-server</code>是如何实现热更新的?(源码都是精简过的,第一行会注明代码路径,看完最好结合源码食用一次)。</p>
|
|
53
|
+
<h3 id="1-webpack-dev-server启动本地服务"><a href="#1-webpack-dev-server启动本地服务" class="headerlink" title="1. webpack-dev-server启动本地服务"></a>1. webpack-dev-server启动本地服务</h3><p>我们根据<code>webpack-dev-server</code>的<code>package.json</code>中的<code>bin</code>命令,可以找到命令的入口文件<code>bin/webpack-dev-server.js</code>。</p>
|
|
54
|
+
<pre><code>// node_modules/webpack-dev-server/bin/webpack-dev-server.js// 生成webpack编译主引擎 compilerlet compiler = webpack(config);
|
|
55
|
+
|
|
56
|
+
// 启动本地服务let server = new Server(compiler, options, log);
|
|
57
|
+
server.listen(options.port, options.host, (err) => {
|
|
58
|
+
if (err) {throw err};
|
|
59
|
+
});
|
|
60
|
+
</code></pre>
|
|
61
|
+
<p>本地服务代码:</p>
|
|
62
|
+
<pre><code>// node_modules/webpack-dev-server/lib/Server.jsclassServer{
|
|
63
|
+
constructor() {
|
|
64
|
+
this.setupApp();
|
|
65
|
+
this.createServer();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
setupApp() {
|
|
69
|
+
// 依赖了expressthis.app = new express();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
createServer() {
|
|
73
|
+
this.listeningApp = http.createServer(this.app);
|
|
74
|
+
}
|
|
75
|
+
listen(port, hostname, fn) {
|
|
76
|
+
returnthis.listeningApp.listen(port, hostname, (err) => {
|
|
77
|
+
// 启动express服务后,启动websocket服务this.createSocketServer();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
</code></pre>
|
|
82
|
+
<p>这一小节代码主要做了三件事:</p>
|
|
83
|
+
<ul>
|
|
84
|
+
<li>启动<code>webpack</code>,生成<code>compiler</code>实例。<code>compiler</code>上有很多方法,比如可以启动 <code>webpack</code> 所有<strong>编译</strong>工作,以及<strong>监听</strong>本地文件的变化。</li>
|
|
85
|
+
<li>使用<code>express</code>框架启动本地<code>server</code>,让浏览器可以请求本地的<strong>静态资源</strong>。</li>
|
|
86
|
+
<li>本地<code>server</code>启动之后,再去启动<code>websocket</code>服务,如果不了解<code>websocket</code>,建议简单了解一下<a target="_blank" rel="noopener" href="https://www.ruanyifeng.com/blog/2017/05/websocket.html">websocket速成</a>。通过<code>websocket</code>,可以建立本地服务和浏览器的双向通信。这样就可以实现当本地文件发生变化,立马告知浏览器可以热更新代码啦!</li>
|
|
87
|
+
</ul>
|
|
88
|
+
<p>上述代码主要干了三件事,但是源码在启动服务前又做了很多事,接下来便看看<code>webpack-dev-server/lib/Server.js</code>还做了哪些事?</p>
|
|
89
|
+
<h3 id="2-修改webpack-config-js的entry配置"><a href="#2-修改webpack-config-js的entry配置" class="headerlink" title="2. 修改webpack.config.js的entry配置"></a>2. 修改webpack.config.js的entry配置</h3><p>启动本地服务前,调用了<code>updateCompiler(this.compiler)</code>方法。这个方法中有 2 段关键性代码。一个是获取<code>websocket</code>客户端代码路径,另一个是根据配置获取<code>webpack</code>热更新代码路径。</p>
|
|
90
|
+
<pre><code>// 获取websocket客户端代码const clientEntry = `${require.resolve(
|
|
91
|
+
'../../client/'
|
|
92
|
+
)}?${domain}${sockHost}${sockPath}${sockPort}`;
|
|
93
|
+
|
|
94
|
+
// 根据配置获取热更新代码let hotEntry;
|
|
95
|
+
if (options.hotOnly) {
|
|
96
|
+
hotEntry = require.resolve('webpack/hot/only-dev-server');
|
|
97
|
+
} elseif (options.hot) {
|
|
98
|
+
hotEntry = require.resolve('webpack/hot/dev-server');
|
|
99
|
+
}
|
|
100
|
+
</code></pre>
|
|
101
|
+
<p>修改后的<code>webpack</code>入口配置如下:</p>
|
|
102
|
+
<pre><code>// 修改后的entry入口
|
|
103
|
+
{ entry:
|
|
104
|
+
{ index:
|
|
105
|
+
[
|
|
106
|
+
// 上面获取的clientEntry'xxx/node_modules/webpack-dev-server/client/index.js?http://localhost:8080',
|
|
107
|
+
// 上面获取的hotEntry'xxx/node_modules/webpack/hot/dev-server.js',
|
|
108
|
+
// 开发配置的入口'./src/index.js'
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
</code></pre>
|
|
113
|
+
<p>为什么要新增了 2 个文件?在入口默默增加了 2 个文件,那就意味会一同打包到<code>bundle</code>文件中去,也就是线上运行时。</p>
|
|
114
|
+
<p><strong>(1)webpack-dev-server/client/index.js</strong></p>
|
|
115
|
+
<p>首先这个文件用于<code>websocket</code>的,因为<code>websoket</code>是双向通信,如果不了解<code>websocket</code>,建议简单了解一下<a target="_blank" rel="noopener" href="https://www.ruanyifeng.com/blog/2017/05/websocket.html">websocket速成</a>。我们在第 1 步 <code>webpack-dev-server</code>初始化 的过程中,启动的是本地服务端的<code>websocket</code>。那客户端也就是我们的浏览器,浏览器还没有和服务端通信的代码呢?总不能让开发者去写吧hhhhhh。因此我们需要把<code>websocket</code>客户端通信代码偷偷塞到我们的代码中。客户端具体的代码后面会在合适的时机细讲哦。</p>
|
|
116
|
+
<p><strong>(2)webpack/hot/dev-server.js</strong></p>
|
|
117
|
+
<p>这个文件主要是用于检查更新逻辑的,这里大家知道就好,代码后面会在合适的时机(<strong>第5步</strong>)细讲。</p>
|
|
118
|
+
<h3 id="3-监听webpack编译结束"><a href="#3-监听webpack编译结束" class="headerlink" title="3. 监听webpack编译结束"></a>3. 监听webpack编译结束</h3><p>修改好入口配置后,又调用了<code>setupHooks</code>方法。这个方法是用来注册监听事件的,监听每次<code>webpack</code>编译完成。</p>
|
|
119
|
+
<pre><code>// node_modules/webpack-dev-server/lib/Server.js// 绑定监听事件
|
|
120
|
+
setupHooks() {
|
|
121
|
+
const {done} = compiler.hooks;
|
|
122
|
+
// 监听webpack的done钩子,tapable提供的监听方法
|
|
123
|
+
done.tap('webpack-dev-server', (stats) => {
|
|
124
|
+
this._sendStats(this.sockets, this.getStats(stats));
|
|
125
|
+
this._stats = stats;
|
|
126
|
+
});
|
|
127
|
+
};
|
|
128
|
+
</code></pre>
|
|
129
|
+
<p>当监听到一次<code>webpack</code>编译结束,就会调用<code>_sendStats</code>方法通过<code>websocket</code>给浏览器发送通知,<code>ok</code>和<code>hash</code>事件,这样浏览器就可以拿到最新的<code>hash</code>值了,做检查更新逻辑。</p>
|
|
130
|
+
<pre><code>// 通过websoket给客户端发消息
|
|
131
|
+
_sendStats() {
|
|
132
|
+
this.sockWrite(sockets, 'hash', stats.hash);
|
|
133
|
+
this.sockWrite(sockets, 'ok');
|
|
134
|
+
}
|
|
135
|
+
</code></pre>
|
|
136
|
+
<h3 id="4-webpack监听文件变化"><a href="#4-webpack监听文件变化" class="headerlink" title="4. webpack监听文件变化"></a>4. webpack监听文件变化</h3><p>每次修改代码,就会触发编译。说明我们还需要监听本地代码的变化,主要是通过<code>setupDevMiddleware</code>方法实现的。</p>
|
|
137
|
+
<p>这个方法主要执行了<code>webpack-dev-middleware</code>库。很多人分不清<code>webpack-dev-middleware</code>和<code>webpack-dev-server</code>的区别。其实就是因为<code>webpack-dev-server</code>只负责启动服务和前置准备工作,所有文件相关的操作都抽离到<code>webpack-dev-middleware</code>库了,主要是本地文件的<strong>编译</strong>和<strong>输出</strong>以及<strong>监听</strong>,无非就是职责的划分更清晰了。</p>
|
|
138
|
+
<p>那我们来看下<code>webpack-dev-middleware</code>源码里做了什么事:</p>
|
|
139
|
+
<pre><code>// node_modules/webpack-dev-middleware/index.js
|
|
140
|
+
compiler.watch(options.watchOptions, (err) => {
|
|
141
|
+
if (err) { /*错误处理*/ }
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// 通过“memory-fs”库将打包后的文件写入内存
|
|
145
|
+
setFs(context, compiler);
|
|
146
|
+
</code></pre>
|
|
147
|
+
<p>(1)调用了<code>compiler.watch</code>方法,在第 1 步中也提到过,<code>compiler</code>的强大。这个方法主要就做了 2 件事:</p>
|
|
148
|
+
<ul>
|
|
149
|
+
<li>首先对本地文件代码进行编译打包,也就是<code>webpack</code>的一系列编译流程。</li>
|
|
150
|
+
<li>其次编译结束后,开启对本地文件的监听,当文件发生变化,重新编译,编译完成之后继续监听。</li>
|
|
151
|
+
</ul>
|
|
152
|
+
<p>为什么代码的改动保存会自动编译,重新打包?这一系列的重新检测编译就归功于<code>compiler.watch</code>这个方法了。监听本地文件的变化主要是通过<strong>文件的生成时间</strong>是否有变化,这里就不细讲了。</p>
|
|
153
|
+
<p>(2)执行<code>setFs</code>方法,这个方法主要目的就是将编译后的文件打包到内存。这就是为什么在开发的过程中,你会发现<code>dist</code>目录没有打包后的代码,因为都在内存中。原因就在于访问内存中的代码比访问文件系统中的文件更快,而且也减少了代码写入文件的开销,这一切都归功于<code>memory-fs</code>。</p>
|
|
154
|
+
<h3 id="5-浏览器接收到热更新的通知"><a href="#5-浏览器接收到热更新的通知" class="headerlink" title="5. 浏览器接收到热更新的通知"></a>5. 浏览器接收到热更新的通知</h3><p>我们已经可以监听到文件的变化了,当文件发生变化,就触发重新编译。同时还监听了每次编译结束的事件。当监听到一次<code>webpack</code>编译结束,<code>_sendStats</code>方法就通过<code>websoket</code>给浏览器发送通知,检查下是否需要热更新。下面重点讲的就是<code>_sendStats</code>方法中的<code>ok</code>和<code>hash</code>事件都做了什么。</p>
|
|
155
|
+
<p>那浏览器是如何接收到<code>websocket</code>的消息呢?回忆下第 2 步骤增加的入口文件,也就是<code>websocket</code>客户端代码。</p>
|
|
156
|
+
<pre><code>'xxx/node_modules/webpack-dev-server/client/index.js?http://localhost:8080'
|
|
157
|
+
</code></pre>
|
|
158
|
+
<p>这个文件的代码会被打包到<code>bundle.js</code>中,运行在浏览器中。来看下这个文件的核心代码吧。</p>
|
|
159
|
+
<pre><code>// webpack-dev-server/client/index.jsvar socket = require('./socket');
|
|
160
|
+
var onSocketMessage = {
|
|
161
|
+
hash: functionhash(_hash) {
|
|
162
|
+
// 更新currentHash值
|
|
163
|
+
status.currentHash = _hash;
|
|
164
|
+
},
|
|
165
|
+
ok: functionok() {
|
|
166
|
+
sendMessage('Ok');
|
|
167
|
+
// 进行更新检查等操作
|
|
168
|
+
reloadApp(options, status);
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
// 连接服务地址socketUrl,?http://localhost:8080,本地服务地址
|
|
172
|
+
socket(socketUrl, onSocketMessage);
|
|
173
|
+
|
|
174
|
+
functionreloadApp() {
|
|
175
|
+
if (hot) {
|
|
176
|
+
log.info('[WDS] App hot update...');
|
|
177
|
+
|
|
178
|
+
// hotEmitter其实就是EventEmitter的实例var hotEmitter = require('webpack/hot/emitter');
|
|
179
|
+
hotEmitter.emit('webpackHotUpdate', currentHash);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
</code></pre>
|
|
183
|
+
<p><code>socket</code>方法建立了<code>websocket</code>和服务端的连接,并注册了 2 个监听事件。</p>
|
|
184
|
+
<ul>
|
|
185
|
+
<li><code>hash</code>事件,更新最新一次打包后的<code>hash</code>值。</li>
|
|
186
|
+
<li><code>ok</code>事件,进行热更新检查。</li>
|
|
187
|
+
</ul>
|
|
188
|
+
<p>热更新检查事件是调用<code>reloadApp</code>方法。比较奇怪的是,这个方法又利用<code>node.js</code>的<code>EventEmitter</code>,发出<code>webpackHotUpdate</code>消息。这是为什么?为什么不直接进行检查更新呢?</p>
|
|
189
|
+
<p>个人理解就是为了更好的维护代码,以及职责划分的更明确。<code>websocket</code>仅仅用于客户端(浏览器)和服务端进行通信。而真正做事情的活还是交回给了<code>webpack</code>。</p>
|
|
190
|
+
<p>那<code>webpack</code>怎么做的呢?再来回忆下第 2 步。入口文件还有一个文件没有讲到,就是:</p>
|
|
191
|
+
<pre><code>'xxx/node_modules/webpack/hot/dev-server.js'
|
|
192
|
+
</code></pre>
|
|
193
|
+
<p>这个文件的代码同样会被打包到<code>bundle.js</code>中,运行在浏览器中。这个文件做了什么就显而易见了吧!先瞄一眼代码:</p>
|
|
194
|
+
<pre><code>// node_modules/webpack/hot/dev-server.jsvar check = functioncheck() {
|
|
195
|
+
module.hot.check(true)
|
|
196
|
+
.then(function(updatedModules) {
|
|
197
|
+
// 容错,直接刷新页面if (!updatedModules) {
|
|
198
|
+
window.location.reload();
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// 热更新结束,打印信息if (upToDate()) {
|
|
203
|
+
log("info", "[HMR] App is up to date.");
|
|
204
|
+
}
|
|
205
|
+
})
|
|
206
|
+
.catch(function(err) {
|
|
207
|
+
window.location.reload();
|
|
208
|
+
});
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
var hotEmitter = require("./emitter");
|
|
212
|
+
hotEmitter.on("webpackHotUpdate", function(currentHash) {
|
|
213
|
+
lastHash = currentHash;
|
|
214
|
+
check();
|
|
215
|
+
});
|
|
216
|
+
</code></pre>
|
|
217
|
+
<p>这里<code>webpack</code>监听到了<code>webpackHotUpdate</code>事件,并获取最新了最新的<code>hash</code>值,然后终于进行检查更新了。检查更新呢调用的是<code>module.hot.check</code>方法。那么问题又来了,<code>module.hot.check</code>又是哪里冒出来了的!答案是<code>HotModuleReplacementPlugin</code>搞得鬼。这里留个疑问,继续往下看。</p>
|
|
218
|
+
<h3 id="6-HotModuleReplacementPlugin"><a href="#6-HotModuleReplacementPlugin" class="headerlink" title="6. HotModuleReplacementPlugin"></a>6. HotModuleReplacementPlugin</h3><p>前面好像一直是<code>webpack-dev-server</code>做的事,那<code>HotModuleReplacementPlugin</code>在热更新过程中又做了什么伟大的事业呢?</p>
|
|
219
|
+
<p>首先你可以对比下,配置热更新和不配置时<code>bundle.js</code>的区别。内存中看不到?直接执行<code>webpack</code>命令就可以看到生成的<code>bundle.js</code>文件啦。不要用<code>webpack-dev-server</code>启动就好了。</p>
|
|
220
|
+
<p>(1)没有配置的。</p>
|
|
221
|
+
<p><img src="https://user-gold-cdn.xitu.io/2019/12/1/16ec0c9e8fd12349?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>(2)配置了<code>HotModuleReplacementPlugin</code>或<code>--hot</code>的。<br><img src="https://user-gold-cdn.xitu.io/2019/12/1/16ec0c90092fa0ac?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>哦~ 我们发现<code>moudle</code>新增了一个属性为<code>hot</code>,再看<code>hotCreateModule</code>方法。<br>这不就找到<code>module.hot.check</code>是哪里冒出来的。<br><img src="https://user-gold-cdn.xitu.io/2019/12/1/16ec0dc36018973f?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>经过对比打包后的文件,<code>__webpack_require__</code>中的<code>moudle</code>以及代码行数的不同。我们都可以发现<code>HotModuleReplacementPlugin</code>原来也是默默的塞了很多代码到<code>bundle.js</code>中呀。这和第 2 步骤很是相似哦!为什么,因为检查更新是在浏览器中操作呀。这些代码必须在运行时的环境。</p>
|
|
222
|
+
<p>你也可以直接看浏览器<code>Sources</code>下的代码,会发现<code>webpack</code>和<code>plugin</code>偷偷加的代码都在哦。在这里调试也很方便。</p>
|
|
223
|
+
<p><img src="https://user-gold-cdn.xitu.io/2019/12/1/16ec0d4634af2b3c?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><code>HotModuleReplacementPlugin</code>如何做到的?这里我就不讲了,因为这需要你对<code>tapable</code>以及<code>plugin</code>机制有一定了解,可以看下我写的文章<a target="_blank" rel="noopener" href="https://juejin.im/post/5dc169b0f265da4d542092c6">Webpack插件机制之Tapable-源码解析</a>。当然你也可以选择跳过,只关心热更新机制即可,毕竟信息量太大。</p>
|
|
224
|
+
<h3 id="7-moudle-hot-check-开始热更新"><a href="#7-moudle-hot-check-开始热更新" class="headerlink" title="7. moudle.hot.check 开始热更新"></a>7. moudle.hot.check 开始热更新</h3><p>通过第 6 步,我们就可以知道<code>moudle.hot.check</code>方法是如何来的啦。那都做了什么?之后的源码都是<code>HotModuleReplacementPlugin</code>塞入到<code>bundle.js</code>中的哦,我就不写文件路径了。</p>
|
|
225
|
+
<ul>
|
|
226
|
+
<li><p>利用上一次保存的<code>hash</code>值,调用<code>hotDownloadManifest</code>发送<code>xxx/hash.hot-update.json</code>的<code>ajax</code>请求;</p>
|
|
227
|
+
</li>
|
|
228
|
+
<li><p>请求结果获取热更新模块,以及下次热更新的<code>Hash</code> 标识,并进入热更新准备阶段。</p>
|
|
229
|
+
<p> hotAvailableFilesMap = update.c; // 需要更新的文件<br> hotUpdateNewHash = update.h; // 更新下次热更新hash值<br> hotSetStatus(“prepare”); // 进入热更新准备状态</p>
|
|
230
|
+
</li>
|
|
231
|
+
<li><p>调用<code>hotDownloadUpdateChunk</code>发送<code>xxx/hash.hot-update.js</code> 请求,通过<code>JSONP</code>方式。</p>
|
|
232
|
+
<p> functionhotDownloadUpdateChunk(chunkId) {<br> var script = document.createElement(“script”);<br> script.charset = “utf-8”;<br> script.src = <strong>webpack_require</strong>.p + “” + chunkId + “.” + hotCurrentHash + “.hot-update.js”;<br> if (null) script.crossOrigin = null;<br> document.head.appendChild(script);<br> }</p>
|
|
233
|
+
</li>
|
|
234
|
+
</ul>
|
|
235
|
+
<p>这个函数体为什么要单独拿出来,因为这里要解释下为什么使用<code>JSONP</code>获取最新代码?主要是因为<code>JSONP</code>获取的代码可以直接执行。为什么要直接执行?我们来回忆下<code>/hash.hot-update.js</code>的代码格式是怎么样的。</p>
|
|
236
|
+
<p><img src="https://user-gold-cdn.xitu.io/2019/12/1/16ec04316d6ac5e3?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"><br>可以发现,新编译后的代码是在一个<code>webpackHotUpdate</code>函数体内部的。也就是要立即执行<code>webpackHotUpdate</code>这个方法。</p>
|
|
237
|
+
<p>再看下<code>webpackHotUpdate</code>这个方法。</p>
|
|
238
|
+
<pre><code>window["webpackHotUpdate"] = function (chunkId, moreModules) {
|
|
239
|
+
hotAddUpdateChunk(chunkId, moreModules);
|
|
240
|
+
} ;
|
|
241
|
+
</code></pre>
|
|
242
|
+
<ul>
|
|
243
|
+
<li><p><code>hotAddUpdateChunk</code>方法会把更新的模块<code>moreModules</code>赋值给全局全量<code>hotUpdate</code>。</p>
|
|
244
|
+
</li>
|
|
245
|
+
<li><p><code>hotUpdateDownloaded</code>方法会调用<code>hotApply</code>进行代码的替换。</p>
|
|
246
|
+
<p> functionhotAddUpdateChunk(chunkId, moreModules) {<br> // 更新的模块moreModules赋值给全局全量hotUpdatefor (var moduleId in moreModules) {<br> if (Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {<br> hotUpdate[moduleId] = moreModules[moduleId];<br> }<br> }<br> // 调用hotApply进行模块的替换<br> hotUpdateDownloaded();<br> }</p>
|
|
247
|
+
</li>
|
|
248
|
+
</ul>
|
|
249
|
+
<h3 id="8-hotApply-热更新模块替换"><a href="#8-hotApply-热更新模块替换" class="headerlink" title="8. hotApply 热更新模块替换"></a>8. hotApply 热更新模块替换</h3><p>热更新的核心逻辑就在<code>hotApply</code>方法了。<br><code>hotApply</code>代码有将近 400 行,还是挑重点讲了,看哭😭</p>
|
|
250
|
+
<h4 id="①删除过期的模块,就是需要替换的模块"><a href="#①删除过期的模块,就是需要替换的模块" class="headerlink" title="①删除过期的模块,就是需要替换的模块"></a>①删除过期的模块,就是需要替换的模块</h4><p>通过<code>hotUpdate</code>可以找到旧模块</p>
|
|
251
|
+
<pre><code>var queue = outdatedModules.slice();
|
|
252
|
+
while (queue.length > 0) {
|
|
253
|
+
moduleId = queue.pop();
|
|
254
|
+
// 从缓存中删除过期的模块module = installedModules[moduleId];
|
|
255
|
+
// 删除过期的依赖delete outdatedDependencies[moduleId];
|
|
256
|
+
|
|
257
|
+
// 存储了被删掉的模块id,便于更新代码
|
|
258
|
+
outdatedSelfAcceptedModules.push({
|
|
259
|
+
module: moduleId
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
</code></pre>
|
|
263
|
+
<h4 id="②将新的模块添加到-modules-中"><a href="#②将新的模块添加到-modules-中" class="headerlink" title="②将新的模块添加到 modules 中"></a>②将新的模块添加到 modules 中</h4><pre><code>appliedUpdate[moduleId] = hotUpdate[moduleId];
|
|
264
|
+
for (moduleId in appliedUpdate) {
|
|
265
|
+
if (Object.prototype.hasOwnProperty.call(appliedUpdate, moduleId)) {
|
|
266
|
+
modules[moduleId] = appliedUpdate[moduleId];
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
</code></pre>
|
|
270
|
+
<h4 id="③通过-webpack-require-执行相关模块的代码"><a href="#③通过-webpack-require-执行相关模块的代码" class="headerlink" title="③通过__webpack_require__执行相关模块的代码"></a>③通过__webpack_require__执行相关模块的代码</h4><pre><code>for (i = 0; i < outdatedSelfAcceptedModules.length; i++) {
|
|
271
|
+
var item = outdatedSelfAcceptedModules[i];
|
|
272
|
+
moduleId = item.module;
|
|
273
|
+
try {
|
|
274
|
+
// 执行最新的代码
|
|
275
|
+
__webpack_require__(moduleId);
|
|
276
|
+
} catch (err) {
|
|
277
|
+
// ...容错处理
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
</code></pre>
|
|
281
|
+
<p><code>hotApply</code>的确比较复杂,知道大概流程就好了,这一小节,要求你对webpack打包后的文件如何执行的有一些了解,大家可以自去看下。</p>
|
|
282
|
+
<h2 id="四、总结"><a href="#四、总结" class="headerlink" title="四、总结"></a>四、总结</h2><p>还是以阅读源码的形式画的图,①-④的小标记,是文件发生变化的一个流程。</p>
|
|
283
|
+
<p><img src="https://user-gold-cdn.xitu.io/2019/12/1/16ec13499800dfce?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="xuehuayu.cn"></p>
|
|
284
|
+
<h2 id="写在最后"><a href="#写在最后" class="headerlink" title="写在最后"></a>写在最后</h2><p>本次是以阅读源码的方式讲解原理,是因为觉得热更新这块涉及的知识量比较多。所以知识把关键性代码拿出来,因为每一个块细节说起来都能写一篇文章了,大家可以自己对着源码再理解下。</p>
|
|
285
|
+
<p>还是建议提前了解以下知识会更好理解热更新:</p>
|
|
286
|
+
<ul>
|
|
287
|
+
<li><strong>websocket</strong>:<a target="_blank" rel="noopener" href="https://www.ruanyifeng.com/blog/2017/05/websocket.html">websocket基础知识了解</a></li>
|
|
288
|
+
<li>打包后的<code>bundle</code>文件如何运行的。</li>
|
|
289
|
+
<li><code>webpack</code>启动流程,<code>webpack</code>生命周期。</li>
|
|
290
|
+
<li><strong>tapable</strong>: <a target="_blank" rel="noopener" href="https://juejin.im/post/5dc169b0f265da4d542092c6">Webpack插件机制之Tapable-源码解析</a></li>
|
|
291
|
+
</ul>
|
|
292
|
+
<h2 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h2><ul>
|
|
293
|
+
<li><a target="_blank" rel="noopener" href="https://github.com/Jocs/jocs.github.io/issues/15">Webpack Hot Module Replacement 的原理解析</a></li>
|
|
294
|
+
<li><a target="_blank" rel="noopener" href="https://juejin.im/post/5d8b755fe51d45781332e919">看完这篇,面试再也不怕被问 Webpack 热更新</a></li>
|
|
295
|
+
</ul>
|
|
296
|
+
<p>参考的文章大家也可以看下,但是由于源码版本不同,所以不要太纠结与细节。</p>
|
|
297
|
+
<p>原创不易,走过路过点个赞吧~😊</p>
|
|
298
|
+
</div><iframe src="/donate/?AliPayQR=https://store.xuehuayu.cn/store/public/alipay_qr.jpg&WeChatQR=https://store.xuehuayu.cn/store/public/wechat_qr.png&UnionPayQR=null&GitHub=null&BTCQR=null&BTCKEY=null&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/22237.html">https://xuehuayu.cn/article/22237.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/2021.html">不容错过的 Babel7 知识</a><a class="next" href="/article/22565.html">使用 Hooks 优化 React 组件</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
|
|
299
|
+
const serverURL = origin.includes('cainiaoblog') ? 'https://cmts.cainiaoblog.cn' : 'https://cmts.xuehuayu.cn'
|
|
300
|
+
const locale = {
|
|
301
|
+
placeholder: '请正确填写昵称和邮箱,方便接收回复通知~',
|
|
302
|
+
sofa: '沙发空缺中,还不快抢~',
|
|
303
|
+
};
|
|
304
|
+
Waline.init({
|
|
305
|
+
el: '#vcomment',
|
|
306
|
+
serverURL: serverURL,
|
|
307
|
+
pageSize: '20',
|
|
308
|
+
visitor: true, // 阅读量统计
|
|
309
|
+
requiredMeta: ['nick', 'mail'],
|
|
310
|
+
emoji: [
|
|
311
|
+
'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/weibo',
|
|
312
|
+
'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/qq',
|
|
313
|
+
'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/tw-emoji',
|
|
314
|
+
],
|
|
315
|
+
locale,
|
|
316
|
+
pageview: true
|
|
317
|
+
})
|
|
318
|
+
</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() {
|
|
319
|
+
var val = $(this).val()
|
|
320
|
+
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>'
|
|
321
|
+
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>'
|
|
322
|
+
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>'
|
|
323
|
+
|
|
324
|
+
if (val === 'self') {
|
|
325
|
+
$('#search').html(self)
|
|
326
|
+
var search_path = 'search.xml';
|
|
327
|
+
if (search_path.length == 0) {
|
|
328
|
+
search_path = 'search.xml';
|
|
329
|
+
}
|
|
330
|
+
var path = 'https://cdn.staticaly.com/gh/npljy/npljy.github.io/main/' + search_path;
|
|
331
|
+
searchFunc(path, 'local-search-input', 'local-search-result');
|
|
332
|
+
} else if (val === 'baidu') {
|
|
333
|
+
$('#search').html(baidu)
|
|
334
|
+
} else if (val === 'google') {
|
|
335
|
+
$('#search').html(google)
|
|
336
|
+
}
|
|
337
|
+
})</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(){
|
|
338
|
+
var cnb =window.location.origin.indexOf('cainiaoblog') !==-1
|
|
339
|
+
if (cnb) {
|
|
340
|
+
var gxba =document.getElementById('gxba')
|
|
341
|
+
var gaba =document.getElementById('gaba')
|
|
342
|
+
var gabaLink =document.getElementById('gaba-link')
|
|
343
|
+
gxba.innerText ='京ICP备20007647号-1'
|
|
344
|
+
gaba.innerText ='京公网安备 11010802031254号'
|
|
345
|
+
gabaLink.setAttribute('href','http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010802031254')
|
|
346
|
+
}
|
|
347
|
+
})()</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&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>
|
|
@@ -0,0 +1,109 @@
|
|
|
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>巧用 CSS 实现酷炫的充电动画 | 前端壹菜鸟</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">巧用 CSS 实现酷炫的充电动画</h1><div class="post-meta"><span class="date">2019-12-26</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"> 1.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"> 4</span><span class="post-meta-item-text"> 分钟</span></span></span></div><a class="disqus-comment-count" href="/article/22398.html#vcomment"><span class="waline-comment-count" data-path="/article/22398.html"></span><span> 条评论</span></a><div class="post-content"><p><code>原文地址:https://juejin.im/post/5e00240ee51d45583c1cc9a7</code></p>
|
|
20
|
+
<p>循序渐进,看看只使用 CSS ,可以鼓捣出什么样的充电动画效果。</p>
|
|
21
|
+
<span id="more"></span>
|
|
22
|
+
<p><img src="https://store.xuehuayu.cn/store/20191226/1.webp" alt="xuehuayu.cn"></p>
|
|
23
|
+
<h2 id="画个电池"><a href="#画个电池" class="headerlink" title="画个电池"></a>画个电池</h2><p>当然,电池充电,首先得用 CSS 画一个电池,这个不难,随便整一个:<br><img src="https://store.xuehuayu.cn/store/20191226/2.webp" alt="xuehuayu.cn"><br>欧了,勉强就是它了。有了电池,那接下来直接充电吧。最最简单的动画,那应该是用色彩把整个电池灌满即可。</p>
|
|
24
|
+
<p>方法很多,代码也很简单,直接看效果:<br><img src="https://store.xuehuayu.cn/store/20191226/3.gif" alt="xuehuayu.cn"><br>有内味了,如果要求不高,这个勉强也就能够交差了。通过蓝色渐变表示电量,通过色块的位移动画实现充电的动画。但是总感觉少了点什么。</p>
|
|
25
|
+
<h3 id="增加阴影及颜色的变化"><a href="#增加阴影及颜色的变化" class="headerlink" title="增加阴影及颜色的变化"></a>增加阴影及颜色的变化</h3><p>如果要继续优化的话,需要添加点细节。</p>
|
|
26
|
+
<p>我们知道,低电量时,电量通常表示为红色,高电量时表示为绿色。再给整个色块添加点阴影的变化,呼吸的感觉,让充电的效果看起来确实是在动。<br><img src="https://store.xuehuayu.cn/store/20191226/4.gif" alt="xuehuayu.cn"></p>
|
|
27
|
+
<h3 id="知识点"><a href="#知识点" class="headerlink" title="知识点"></a>知识点</h3><p>到这里,其实只有一个知识点:</p>
|
|
28
|
+
<ul>
|
|
29
|
+
<li>使用 filter: hue-rotate() 对渐变色彩进行色彩过渡变换动画</li>
|
|
30
|
+
</ul>
|
|
31
|
+
<p>我们无法对一个渐变色直接进行 animation ,这里通过滤镜对色相进行调整,从而实现了渐变色的变换动画。</p>
|
|
32
|
+
<p>上述例子完整的 Demo: <a target="_blank" rel="noopener" href="https://codepen.io/Chokcoco/pen/bGNqyra?editors=1100">CodePen Demo – Battery Animation One</a></p>
|
|
33
|
+
<h2 id="添加波浪"><a href="#添加波浪" class="headerlink" title="添加波浪"></a>添加波浪</h2><p>ok,刚刚算一个小里程碑,接下来再进一步。电量的顶部为一条直线有点呆呆的感觉,这里我们进行改造一下,如果能将顶部直线,改为波浪滚动,效果会更为逼真一点。</p>
|
|
34
|
+
<p>改造之后的效果:<br><img src="https://store.xuehuayu.cn/store/20191226/5.gif" alt="xuehuayu.cn"><br>使用 CSS 实现这种波浪滚动效果,其实只是用了一种障眼法,具体的可以我早期写的这篇文章:</p>
|
|
35
|
+
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/coco1s/p/7197662.html">纯 CSS 实现波浪效果!</a></p>
|
|
36
|
+
<h3 id="知识点-1"><a href="#知识点-1" class="headerlink" title="知识点"></a>知识点</h3><p>这里的一个知识点就是上述说的使用 CSS 实现简易的波浪效果,通过障眼法实现,看看图就明白了:<br><img src="https://store.xuehuayu.cn/store/20191226/6.gif" alt="xuehuayu.cn"><br>上述例子完整的 Demo: <a target="_blank" rel="noopener" href="https://codepen.io/Chokcoco/pen/qBErGoO">CodePen Demo – Battery Animation Two</a></p>
|
|
37
|
+
<p>OK,到这,上述效果加上数字变化已经算是一个比较不错的效果了。当然上面的效果看上去还是很 CSS 的,就是一眼看到就觉得用 CSS 是可以做到的。</p>
|
|
38
|
+
<h2 id="使用强大的-CSS-滤镜实现安卓充电动画效果"><a href="#使用强大的-CSS-滤镜实现安卓充电动画效果" class="headerlink" title="使用强大的 CSS 滤镜实现安卓充电动画效果"></a>使用强大的 CSS 滤镜实现安卓充电动画效果</h2><p>那下面这个呢?<br><img src="https://store.xuehuayu.cn/store/20191226/7.webp" alt="xuehuayu.cn"><br>用安卓手机的同学肯定不陌生,这个是安卓手机在充电的时候的效果。看到这个我就很好奇,使用 CSS 能做到吗?</p>
|
|
39
|
+
<p>经过一番尝试,发现使用 CSS 也是可以很好的模拟这种动画效果:<br><img src="https://store.xuehuayu.cn/store/20191226/8.gif" alt="xuehuayu.cn"><br>上述 Gif 录制的效果图是完全使用 CSS 模拟的效果。</p>
|
|
40
|
+
<p>上述例子完整的 Demo: <a target="_blank" rel="noopener" href="https://codepen.io/Chokcoco/pen/vYExwvm">HuaWei Battery Charging Animation</a></p>
|
|
41
|
+
<h3 id="知识点-2"><a href="#知识点-2" class="headerlink" title="知识点"></a>知识点</h3><p>拆解一下知识点,最主要的其实是用到了 <code>filter: contrast()</code> 以及 <code>filter: blur()</code> 这两个滤镜,可以很好的实现这种融合效果。</p>
|
|
42
|
+
<p>单独将两个滤镜拿出来,它们的作用分别是:</p>
|
|
43
|
+
<ol>
|
|
44
|
+
<li><code>filter: blur()</code>: 给图像设置高斯模糊效果。</li>
|
|
45
|
+
<li><code>filter: contrast()</code>: 调整图像的对比度。</li>
|
|
46
|
+
</ol>
|
|
47
|
+
<p>但是,当他们“合体”的时候,产生了奇妙的融合现象。</p>
|
|
48
|
+
<p>先来看一个简单的例子:<br><img src="https://store.xuehuayu.cn/store/20191226/9.gif" alt="xuehuayu.cn"><br>仔细看两圆相交的过程,在边与边接触的时候,会产生一种边界融合的效果,通过对比度滤镜把高斯模糊的模糊边缘给干掉,利用高斯模糊实现融合效果。</p>
|
|
49
|
+
<p>当然,这种效果在之前的文章也多次提及过,更具体的,可以看看:</p>
|
|
50
|
+
<ul>
|
|
51
|
+
<li><a target="_blank" rel="noopener" href="https://github.com/chokcoco/iCSS/issues/62">CSS 火焰?不在话下 </a></li>
|
|
52
|
+
<li><a target="_blank" rel="noopener" href="https://github.com/chokcoco/iCSS/issues/30">你所不知道的 CSS 滤镜技巧与细节</a></li>
|
|
53
|
+
</ul>
|
|
54
|
+
<h3 id="颜色的变换"><a href="#颜色的变换" class="headerlink" title="颜色的变换"></a>颜色的变换</h3><p>当然,这里也是可以加上颜色的变换,效果也很不错:<br><img src="https://store.xuehuayu.cn/store/20191226/10.gif" alt="xuehuayu.cn"><br>上述例子完整的 Demo: <a target="_blank" rel="noopener" href="https://codepen.io/Chokcoco/pen/vYExwvm">HuaWei Battery Charging Animation</a></p>
|
|
55
|
+
<h3 id="容易忽视的点"><a href="#容易忽视的点" class="headerlink" title="容易忽视的点"></a>容易忽视的点</h3><p>通过调节 <code>filter: blur()</code> 及 <code>filter: contrast()</code> 属性的值,动画效果其实会有很大程度的变化,好的效果需要不断的调试。当然,经验在其中也是发挥了很重要的作用,说到底还是要多尝试。</p>
|
|
56
|
+
<h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>本文给出的几个充电动画,效果渐进增强,本文只指出了最核心的知识点。但是在实际输出的过程中有很多小细节是本文没有提及的,感兴趣的同学还是应该点进 Demo 好好看看源码或者自己动手实现一遍。</p>
|
|
57
|
+
<p>更多精彩 CSS 技术文章汇总在我的 <a target="_blank" rel="noopener" href="https://github.com/chokcoco/iCSS">Github – iCSS</a> ,持续更新,欢迎点个 star 订阅收藏。</p>
|
|
58
|
+
<p>好了,本文到此结束,希望对你有帮助 :)</p>
|
|
59
|
+
<p>如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。</p>
|
|
60
|
+
</div><iframe src="/donate/?AliPayQR=https://store.xuehuayu.cn/store/public/alipay_qr.jpg&WeChatQR=https://store.xuehuayu.cn/store/public/wechat_qr.png&UnionPayQR=null&GitHub=null&BTCQR=null&BTCKEY=null&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/22398.html">https://xuehuayu.cn/article/22398.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/56139.html">第 4 种 Web 语言来了:WebAssembly</a><a class="next" href="/article/13265.html">string-similarity: 可以帮你检查两个字符串的相似性,轻松完成模糊匹配</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
|
|
61
|
+
const serverURL = origin.includes('cainiaoblog') ? 'https://cmts.cainiaoblog.cn' : 'https://cmts.xuehuayu.cn'
|
|
62
|
+
const locale = {
|
|
63
|
+
placeholder: '请正确填写昵称和邮箱,方便接收回复通知~',
|
|
64
|
+
sofa: '沙发空缺中,还不快抢~',
|
|
65
|
+
};
|
|
66
|
+
Waline.init({
|
|
67
|
+
el: '#vcomment',
|
|
68
|
+
serverURL: serverURL,
|
|
69
|
+
pageSize: '20',
|
|
70
|
+
visitor: true, // 阅读量统计
|
|
71
|
+
requiredMeta: ['nick', 'mail'],
|
|
72
|
+
emoji: [
|
|
73
|
+
'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/weibo',
|
|
74
|
+
'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/qq',
|
|
75
|
+
'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/tw-emoji',
|
|
76
|
+
],
|
|
77
|
+
locale,
|
|
78
|
+
pageview: true
|
|
79
|
+
})
|
|
80
|
+
</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() {
|
|
81
|
+
var val = $(this).val()
|
|
82
|
+
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>'
|
|
83
|
+
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>'
|
|
84
|
+
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>'
|
|
85
|
+
|
|
86
|
+
if (val === 'self') {
|
|
87
|
+
$('#search').html(self)
|
|
88
|
+
var search_path = 'search.xml';
|
|
89
|
+
if (search_path.length == 0) {
|
|
90
|
+
search_path = 'search.xml';
|
|
91
|
+
}
|
|
92
|
+
var path = 'https://cdn.staticaly.com/gh/npljy/npljy.github.io/main/' + search_path;
|
|
93
|
+
searchFunc(path, 'local-search-input', 'local-search-result');
|
|
94
|
+
} else if (val === 'baidu') {
|
|
95
|
+
$('#search').html(baidu)
|
|
96
|
+
} else if (val === 'google') {
|
|
97
|
+
$('#search').html(google)
|
|
98
|
+
}
|
|
99
|
+
})</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(){
|
|
100
|
+
var cnb =window.location.origin.indexOf('cainiaoblog') !==-1
|
|
101
|
+
if (cnb) {
|
|
102
|
+
var gxba =document.getElementById('gxba')
|
|
103
|
+
var gaba =document.getElementById('gaba')
|
|
104
|
+
var gabaLink =document.getElementById('gaba-link')
|
|
105
|
+
gxba.innerText ='京ICP备20007647号-1'
|
|
106
|
+
gaba.innerText ='京公网安备 11010802031254号'
|
|
107
|
+
gabaLink.setAttribute('href','http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010802031254')
|
|
108
|
+
}
|
|
109
|
+
})()</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&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>
|