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,283 @@
|
|
|
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>实现双向绑定Proxy比defineproperty优劣如何 | 前端壹菜鸟</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">实现双向绑定Proxy比defineproperty优劣如何</h1><div class="post-meta"><span class="date">2019-09-29</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.6k</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"> 18</span><span class="post-meta-item-text"> 分钟</span></span></span></div><a class="disqus-comment-count" href="/article/26147.html#vcomment"><span class="waline-comment-count" data-path="/article/26147.html"></span><span> 条评论</span></a><div class="post-content"><p><code>原文地址:https://www.jianshu.com/p/2df6dcddb0d7</code><br><strong>双向绑定</strong>其实已经是一个老掉牙的问题了,只要涉及到MVVM框架就不得不谈的知识点,但它毕竟是Vue的三要素之一.<br><strong>Vue三要素</strong></p>
|
|
20
|
+
<ul>
|
|
21
|
+
<li>响应式: 例如如何监听数据变化,其中的实现方法就是我们提到的双向绑定</li>
|
|
22
|
+
<li>模板引擎: 如何解析模板</li>
|
|
23
|
+
<li>渲染: Vue如何将监听到的数据变化和解析后的HTML进行渲染</li>
|
|
24
|
+
</ul>
|
|
25
|
+
<p>可以实现双向绑定的方法有很多,KnockoutJS基于观察者模式的双向绑定,Ember基于数据模型的双向绑定,Angular基于脏检查的双向绑定,本篇文章我们重点讲面试中常见的基于<strong>数据劫持</strong>的双向绑定。</p>
|
|
26
|
+
<span id="more"></span>
|
|
27
|
+
|
|
28
|
+
<p>常见的基于数据劫持的双向绑定有两种实现,一个是目前Vue在用的<code>Object.defineProperty</code>,另一个是ES2015中新增的<code>Proxy</code>,而Vue的作者宣称将在Vue3.0版本后加入<code>Proxy</code>从而代替<code>Object.defineProperty</code>,通过本文你也可以知道为什么Vue未来会选择<code>Proxy</code>。</p>
|
|
29
|
+
<blockquote>
|
|
30
|
+
<p>严格来讲Proxy应该被称为『代理』而非『劫持』,不过由于作用有很多相似之处,我们在下文中就不再做区分,统一叫『劫持』。</p>
|
|
31
|
+
</blockquote>
|
|
32
|
+
<p>我们可以通过下图清楚看到以上两种方法在<strong>双向绑定</strong>体系中的关系.</p>
|
|
33
|
+
<figure style="display: block; margin: 22px auto; text-align: center;">[图片上传中...(image-6f9b58-1526012269856-2)]
|
|
34
|
+
|
|
35
|
+
<figcaption style="display: block; text-align: center; font-size: 1rem; line-height: 1.6; color: rgb(144, 144, 144); margin-top: 2px;"></figcaption>
|
|
36
|
+
|
|
37
|
+
</figure>
|
|
38
|
+
|
|
39
|
+
<blockquote>
|
|
40
|
+
<p>基于数据劫持的当然还有已经凉透的<code>Object.observe</code>方法,已被废弃。<br><strong>提前声明:</strong> 我们没有对传入的参数进行及时判断而规避错误,仅仅对核心方法进行了实现.</p>
|
|
41
|
+
</blockquote>
|
|
42
|
+
<hr>
|
|
43
|
+
<h3 id="文章目录"><a href="#文章目录" class="headerlink" title="文章目录"></a>文章目录</h3><ol>
|
|
44
|
+
<li>基于数据劫持实现的双向绑定的特点</li>
|
|
45
|
+
<li>基于Object.defineProperty双向绑定的特点</li>
|
|
46
|
+
<li>基于Proxy双向绑定的特点</li>
|
|
47
|
+
</ol>
|
|
48
|
+
<hr>
|
|
49
|
+
<h3 id="1-基于数据劫持实现的双向绑定的特点"><a href="#1-基于数据劫持实现的双向绑定的特点" class="headerlink" title="1.基于数据劫持实现的双向绑定的特点"></a>1.基于数据劫持实现的双向绑定的特点</h3><h4 id="1-1-什么是数据劫持"><a href="#1-1-什么是数据劫持" class="headerlink" title="1.1 什么是数据劫持"></a>1.1 什么是数据劫持</h4><p>数据劫持比较好理解,通常我们利用<code>Object.defineProperty</code>劫持对象的访问器,在属性值发生变化时我们可以获取变化,从而进行进一步操作。</p>
|
|
50
|
+
<pre><code>// 这是将要被劫持的对象const data ={
|
|
51
|
+
name:'',};functionsay(name){if(name ==='古天乐'){
|
|
52
|
+
console.log('给大家推荐一款超好玩的游戏');}elseif(name ==='渣渣辉'){
|
|
53
|
+
console.log('戏我演过很多,可游戏我只玩贪玩懒月');}else{
|
|
54
|
+
console.log('来做我的兄弟');} }// 遍历对象,对其属性值进行劫持
|
|
55
|
+
Object.keys(data).forEach(function(key){
|
|
56
|
+
Object.defineProperty(data, key,{
|
|
57
|
+
enumerable:true,
|
|
58
|
+
configurable:true,get:function(){
|
|
59
|
+
console.log('get');},set:function(newVal){// 当属性值发生变化时我们可以进行额外操作
|
|
60
|
+
console.log(`大家好,我系${newVal}`);say(newVal);},});});
|
|
61
|
+
|
|
62
|
+
data.name ='渣渣辉';//大家好,我系渣渣辉//戏我演过很多,可游戏我只玩贪玩懒月
|
|
63
|
+
</code></pre>
|
|
64
|
+
<h4 id="1-2-数据劫持的优势"><a href="#1-2-数据劫持的优势" class="headerlink" title="1.2 数据劫持的优势"></a>1.2 数据劫持的优势</h4><p>目前业界分为两个大的流派,一个是以React为首的单向数据绑定,另一个是以Angular、Vue为主的双向数据绑定。</p>
|
|
65
|
+
<blockquote>
|
|
66
|
+
<p>其实三大框架都是既可以双向绑定也可以单向绑定,比如React可以手动绑定onChange和value实现双向绑定,也可以调用一些双向绑定库,Vue也加入了props这种单向流的api,不过都并非主流卖点。</p>
|
|
67
|
+
</blockquote>
|
|
68
|
+
<p>单向或者双向的优劣不在我们的讨论范围,我们需要讨论一下对比其他双向绑定的实现方法,数据劫持的优势所在。</p>
|
|
69
|
+
<ol>
|
|
70
|
+
<li>无需显示调用: 例如Vue运用数据劫持+发布订阅,直接可以通知变化并驱动视图,上面的例子也是比较简单的实现<code>data.name = '渣渣辉'</code>后直接触发变更,而比如Angular的脏检测则需要显示调用<code>markForCheck</code>(可以用zone.js避免显示调用,不展开),react需要显示调用<code>setState</code>。</li>
|
|
71
|
+
<li>可精确得知变化数据:还是上面的小例子,我们劫持了属性的setter,当属性值改变,我们可以精确获知变化的内容<code>newVal</code>,因此在这部分不需要额外的diff操作,否则我们只知道数据发生了变化而不知道具体哪些数据变化了,这个时候需要大量diff来找出变化值,这是额外性能损耗。</li>
|
|
72
|
+
</ol>
|
|
73
|
+
<h4 id="1-3-基于数据劫持双向绑定的实现思路"><a href="#1-3-基于数据劫持双向绑定的实现思路" class="headerlink" title="1.3 基于数据劫持双向绑定的实现思路"></a>1.3 基于数据劫持双向绑定的实现思路</h4><p><strong>数据劫持</strong>是双向绑定各种方案中比较流行的一种,最著名的实现就是Vue。</p>
|
|
74
|
+
<p>基于数据劫持的双向绑定离不开<code>Proxy</code>与<code>Object.defineProperty</code>等方法对对象/对象属性的”劫持”,我们要实现一个完整的双向绑定需要以下几个要点。</p>
|
|
75
|
+
<ol>
|
|
76
|
+
<li>利用<code>Proxy</code>或<code>Object.defineProperty</code>生成的Observer针对对象/对象的属性进行”劫持”,在属性发生变化后通知订阅者</li>
|
|
77
|
+
<li>解析器Compile解析模板中的<code>Directive</code>(指令),收集指令所依赖的方法和数据,等待数据变化然后进行渲染</li>
|
|
78
|
+
<li>Watcher属于Observer和Compile桥梁,它将接收到的Observer产生的数据变化,并根据Compile提供的指令进行视图渲染,使得数据变化促使视图变化</li>
|
|
79
|
+
</ol>
|
|
80
|
+
<figure style="display: block; margin: 22px auto; text-align: center;">[图片上传中...(image-1f5ab-1526012269856-1)]
|
|
81
|
+
|
|
82
|
+
<figcaption style="display: block; text-align: center; font-size: 1rem; line-height: 1.6; color: rgb(144, 144, 144); margin-top: 2px;"></figcaption>
|
|
83
|
+
|
|
84
|
+
</figure>
|
|
85
|
+
|
|
86
|
+
<blockquote>
|
|
87
|
+
<p>我们看到,虽然Vue运用了数据劫持,但是依然离不开<strong>发布订阅</strong>的模式,之所以在系列2做了<a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fjuejin.im%252Fpost%252F5ac2fb886fb9a028b86e328c">Event Bus的实现</a>,就是因为我们不管在学习一些框架的原理还是一些流行库(例如Redux、Vuex),基本上都离不开<strong>发布订阅</strong>模式,而<em>Event</em>模块则是此模式的经典实现,所以如果不熟悉<strong>发布订阅</strong>模式,建议读一下系列2的文章。</p>
|
|
88
|
+
</blockquote>
|
|
89
|
+
<hr>
|
|
90
|
+
<h3 id="2-基于Object-defineProperty双向绑定的特点"><a href="#2-基于Object-defineProperty双向绑定的特点" class="headerlink" title="2.基于Object.defineProperty双向绑定的特点"></a>2.基于Object.defineProperty双向绑定的特点</h3><p>关于<code>Object.defineProperty</code>的文章在网络上已经汗牛充栋,我们不想花过多时间在<code>Object.defineProperty</code>上面,本节我们主要讲解<code>Object.defineProperty</code>的特点,方便接下来与<code>Proxy</code>进行对比。</p>
|
|
91
|
+
<blockquote>
|
|
92
|
+
<p>对<code>Object.defineProperty</code>还不了解的请阅读<a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fdeveloper.mozilla.org%252Fzh-CN%252Fdocs%252FWeb%252FJavaScript%252FReference%252FGlobal_Objects%252FObject%252FdefineProperty">文档</a></p>
|
|
93
|
+
</blockquote>
|
|
94
|
+
<p>两年前就有人写过基于<code>Object.defineProperty</code>实现的<a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fsegmentfault.com%252Fa%252F1190000006599500">文章</a>,想深入理解<code>Object.defineProperty</code>实现的推荐阅读,本文也做了相关参考。</p>
|
|
95
|
+
<blockquote>
|
|
96
|
+
<p>上面我们推荐的文章为比较完整的实现(400行代码),我们在本节只提供一个极简版(20行)和一个简化版(150行)的实现,读者可以循序渐进地阅读。</p>
|
|
97
|
+
</blockquote>
|
|
98
|
+
<h4 id="2-1-极简版的双向绑定"><a href="#2-1-极简版的双向绑定" class="headerlink" title="2.1 极简版的双向绑定"></a>2.1 极简版的双向绑定</h4><p>我们都知道,<code>Object.defineProperty</code>的作用就是劫持一个对象的属性,通常我们对属性的<code>getter</code>和<code>setter</code>方法进行劫持,在对象的属性发生变化时进行特定的操作。</p>
|
|
99
|
+
<p>我们就对对象<code>obj</code>的<code>text</code>属性进行劫持,在获取此属性的值时打印<code>'get val'</code>,在更改属性值的时候对DOM进行操作,这就是一个极简的双向绑定。</p>
|
|
100
|
+
<pre><code>const obj ={};
|
|
101
|
+
Object.defineProperty(obj,'text',{get:function(){
|
|
102
|
+
console.log('get val');&emsp;},set:function(newVal){
|
|
103
|
+
console.log('set val:'+ newVal);
|
|
104
|
+
document.getElementById('input').value = newVal;
|
|
105
|
+
document.getElementById('span').innerHTML = newVal;} });const input = document.getElementById('input');
|
|
106
|
+
input.addEventListener('keyup',function(e){
|
|
107
|
+
obj.text = e.target.value;})
|
|
108
|
+
</code></pre>
|
|
109
|
+
<p>在线示例 <a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io%252Fxiaomuzhu%252Fpen%252FgzmEab%252F">极简版双向绑定</a> by Iwobi (<a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io%252Fxiaomuzhu">@xiaomuzhu</a>) on <a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io">CodePen</a>.</p>
|
|
110
|
+
<h4 id="2-2-升级改造"><a href="#2-2-升级改造" class="headerlink" title="2.2 升级改造"></a>2.2 升级改造</h4><p>我们很快会发现,这个所谓的<em>双向绑定</em>貌似并没有什么乱用。。。</p>
|
|
111
|
+
<p>原因如下:</p>
|
|
112
|
+
<ol>
|
|
113
|
+
<li>我们只监听了一个属性,一个对象不可能只有一个属性,我们需要对对象每个属性进行监听。</li>
|
|
114
|
+
<li>违反开放封闭原则,我们如果了解<a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fzh.wikipedia.org%252Fzh-hans%252F%2525E5%2525BC%252580%2525E9%252597%2525AD%2525E5%25258E%25259F%2525E5%252588%252599">开放封闭原则</a>的话,上述代码是明显违反此原则,我们每次修改都需要进入方法内部,这是需要坚决杜绝的。</li>
|
|
115
|
+
<li>代码耦合严重,我们的数据、方法和DOM都是耦合在一起的,就是传说中的面条代码。</li>
|
|
116
|
+
</ol>
|
|
117
|
+
<p>那么如何解决上述问题?</p>
|
|
118
|
+
<p>Vue的操作就是加入了<strong>发布订阅</strong>模式,结合<code>Object.defineProperty</code>的劫持能力,实现了可用性很高的双向绑定。</p>
|
|
119
|
+
<p>首先,我们以<strong>发布订阅</strong>的角度看我们第一部分写的那一坨代码,会发现它的<em>监听</em>、<em>发布</em>和<em>订阅</em>都是写在一起的,我们首先要做的就是解耦。</p>
|
|
120
|
+
<p>我们先实现一个订阅发布中心,即消息管理员(Dep),它负责储存订阅者和消息的分发,不管是订阅者还是发布者都需要依赖于它。</p>
|
|
121
|
+
<pre><code> let uid =0;// 用于储存订阅者并发布消息class Dep {constructor(){// 设置id,用于区分新Watcher和只改变属性值后新产生的Watcherthis.id = uid++;// 储存订阅者的数组this.subs =[];}// 触发target上的Watcher中的addDep方法,参数为dep的实例本身depend(){
|
|
122
|
+
Dep.target.addDep(this);}// 添加订阅者addSub(sub){this.subs.push(sub);}notify(){// 通知所有的订阅者(Watcher),触发订阅者的相应逻辑处理this.subs.forEach(sub => sub.update());} }// 为Dep类设置一个静态属性,默认为null,工作时指向当前的Watcher
|
|
123
|
+
Dep.target =null;
|
|
124
|
+
</code></pre>
|
|
125
|
+
<p>现在我们需要实现监听者(Observer),用于监听属性值的变化。</p>
|
|
126
|
+
<pre><code>// 监听者,监听对象属性值的变化classObserver{constructor(value){this.value=value;this.walk(value);}// 遍历属性值并监听walk(value){
|
|
127
|
+
Object.keys(value).forEach(key =>this.convert(key,value[key]));}// 执行监听的具体方法convert(key, val){defineReactive(this.value, key, val);} }
|
|
128
|
+
|
|
129
|
+
function defineReactive(obj, key, val){const dep =newDep();// 给当前属性的值添加监听let chlidOb =observe(val);
|
|
130
|
+
Object.defineProperty(obj, key,{
|
|
131
|
+
enumerable:true,
|
|
132
|
+
configurable:true,get:()=>{// 如果Dep类存在target属性,将其添加到dep实例的subs数组中// target指向一个Watcher实例,每个Watcher都是一个订阅者// Watcher实例在实例化过程中,会读取data中的某个属性,从而触发当前get方法if(Dep.target){
|
|
133
|
+
dep.depend();}return val;},set: newVal =>{if(val === newVal)return;
|
|
134
|
+
val = newVal;// 对新值进行监听
|
|
135
|
+
chlidOb =observe(newVal);// 通知所有订阅者,数值被改变了
|
|
136
|
+
dep.notify();},});}
|
|
137
|
+
|
|
138
|
+
function observe(value){// 当值不存在,或者不是复杂数据类型时,不再需要继续深入监听if(!value||typeofvalue!=='object'){return;}returnnewObserver(value);}
|
|
139
|
+
</code></pre>
|
|
140
|
+
<p>那么接下来就简单了,我们需要实现一个订阅者(Watcher)。</p>
|
|
141
|
+
<pre><code>class Watcher {constructor(vm, expOrFn, cb){this.depIds ={};// hash储存订阅者的id,避免重复的订阅者this.vm = vm;// 被订阅的数据一定来自于当前Vue实例this.cb = cb;// 当数据更新时想要做的事情this.expOrFn = expOrFn;// 被订阅的数据this.val =this.get();// 维护更新之前的数据}// 对外暴露的接口,用于在订阅的数据被更新时,由订阅者管理员(Dep)调用update(){this.run();}addDep(dep){// 如果在depIds的hash中没有当前的id,可以判断是新Watcher,因此可以添加到dep的数组中储存// 此判断是避免同id的Watcher被多次储存if(!this.depIds.hasOwnProperty(dep.id)){
|
|
142
|
+
dep.addSub(this);this.depIds[dep.id]= dep;} }run(){constval=this.get();
|
|
143
|
+
console.log(val);if(val!==this.val){this.val =val;this.cb.call(this.vm,val);} }get(){// 当前订阅者(Watcher)读取被订阅数据的最新更新后的值时,通知订阅者管理员收集当前订阅者
|
|
144
|
+
Dep.target =this;constval=this.vm._data[this.expOrFn];// 置空,用于下一个Watcher使用
|
|
145
|
+
Dep.target =null;returnval;} }
|
|
146
|
+
</code></pre>
|
|
147
|
+
<p>那么我们最后完成Vue,将上述方法挂载在Vue上。</p>
|
|
148
|
+
<pre><code>class Vue {constructor(options ={}){// 简化了$options的处理this.$options = options;// 简化了对data的处理
|
|
149
|
+
let data=(this._data =this.$options.data);// 将所有data最外层属性代理到Vue实例上
|
|
150
|
+
Object.keys(data).forEach(key =>this._proxy(key));// 监听数据observe(data);}// 对外暴露调用订阅者的接口,内部主要在指令中使用订阅者
|
|
151
|
+
$watch(expOrFn, cb){
|
|
152
|
+
new Watcher(this, expOrFn, cb);}_proxy(key){
|
|
153
|
+
Object.defineProperty(this, key,{
|
|
154
|
+
configurable:true,
|
|
155
|
+
enumerable:true,get:()=>this._data[key],set:val=>{this._data[key]=val;},});} }
|
|
156
|
+
</code></pre>
|
|
157
|
+
<p>看下效果:</p>
|
|
158
|
+
<figure style="display: block; margin: 22px auto; text-align: center;">[图片上传中...(image-1da193-1526012269854-0)]
|
|
159
|
+
|
|
160
|
+
<figcaption style="display: block; text-align: center; font-size: 1rem; line-height: 1.6; color: rgb(144, 144, 144); margin-top: 2px;"></figcaption>
|
|
161
|
+
|
|
162
|
+
</figure>
|
|
163
|
+
|
|
164
|
+
<p>在线示例 <a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io%252Fxiaomuzhu%252Fpen%252FjxBRgj%252F">双向绑定实现—无漏洞版</a> by Iwobi (<a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io%252Fxiaomuzhu">@xiaomuzhu</a>) on <a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io">CodePen</a>.</p>
|
|
165
|
+
<p>至此,一个简单的双向绑定算是被我们实现了。</p>
|
|
166
|
+
<h4 id="2-3-Object-defineProperty的缺陷"><a href="#2-3-Object-defineProperty的缺陷" class="headerlink" title="2.3 Object.defineProperty的缺陷"></a>2.3 Object.defineProperty的缺陷</h4><p>其实我们升级版的双向绑定依然存在漏洞,比如我们将属性值改为数组。</p>
|
|
167
|
+
<pre><code>let demo =newVue({
|
|
168
|
+
data:{list:[1],},});constlist= document.getElementById('list');const btn = document.getElementById('btn');
|
|
169
|
+
|
|
170
|
+
btn.addEventListener('click',function(){
|
|
171
|
+
demo.list.push(1);});const render = arr =>{const fragment = document.createDocumentFragment();for(let i =0; i < arr.length; i++){const li = document.createElement('li');
|
|
172
|
+
li.textContent = arr[i];
|
|
173
|
+
fragment.appendChild(li);}list.appendChild(fragment);};// 监听数组,每次数组变化则触发渲染函数,然而...无法监听
|
|
174
|
+
demo.$watch('list',list=>render(list));setTimeout(function(){alert(demo.list);},5000,);
|
|
175
|
+
</code></pre>
|
|
176
|
+
<p>在线示例 <a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io%252Fxiaomuzhu%252Fpen%252FNMjKxV%252F">双向绑定-数组漏洞</a> by Iwobi (<a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io%252Fxiaomuzhu">@xiaomuzhu</a>) on <a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io">CodePen</a>.</p>
|
|
177
|
+
<p>是的,<code>Object.defineProperty</code>的第一个缺陷,无法监听数组变化。 然而<a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcn.vuejs.org%252Fv2%252Fguide%252Flist.html%2523%2525E6%252595%2525B0%2525E7%2525BB%252584%2525E6%25259B%2525B4%2525E6%252596%2525B0%2525E6%2525A3%252580%2525E6%2525B5%25258B">Vue的文档</a>提到了Vue是可以检测到数组变化的,但是只有以下八种方法,<code>vm.items[indexOfItem] = newValue</code>这种是无法检测的。</p>
|
|
178
|
+
<pre><code>push()pop()shift()unshift()splice()sort()reverse()
|
|
179
|
+
</code></pre>
|
|
180
|
+
<p>其实作者在这里用了一些奇技淫巧,把无法监听数组的情况hack掉了,以下是方法示例。</p>
|
|
181
|
+
<pre><code>const aryMethods =['push','pop','shift','unshift','splice','sort','reverse'];const arrayAugmentations =[];
|
|
182
|
+
|
|
183
|
+
aryMethods.forEach((method)=>{// 这里是原生Array的原型方法let original =Array.prototype[method];// 将push, pop等封装好的方法定义在对象arrayAugmentations的属性上// 注意:是属性而非原型属性
|
|
184
|
+
arrayAugmentations[method]=function(){
|
|
185
|
+
console.log('我被改变啦!');// 调用对应的原生方法并返回结果returnoriginal.apply(this, arguments);};});let list =['a','b','c'];// 将我们要监听的数组的原型指针指向上面定义的空数组对象// 别忘了这个空数组的属性上定义了我们封装好的push等方法
|
|
186
|
+
list.__proto__ = arrayAugmentations;
|
|
187
|
+
list.push('d');// 我被改变啦! 4// 这里的list2没有被重新定义原型指针,所以就正常输出let list2 =['a','b','c'];
|
|
188
|
+
list2.push('d');// 4
|
|
189
|
+
</code></pre>
|
|
190
|
+
<p>由于只针对了八种方法进行了hack,所以其他数组的属性也是检测不到的,其中的坑很多,可以阅读上面提到的文档。</p>
|
|
191
|
+
<p>我们应该注意到在上文中的实现里,我们多次用遍历方法遍历对象的属性,这就引出了<code>Object.defineProperty</code>的第二个缺陷,只能劫持对象的属性,因此我们需要对每个对象的每个属性进行遍历,如果属性值也是对象那么需要深度遍历,显然能劫持一个完整的对象是更好的选择。</p>
|
|
192
|
+
<pre><code>Object.keys(value).forEach(key=>this.convert(key, value[key]));
|
|
193
|
+
</code></pre>
|
|
194
|
+
<hr>
|
|
195
|
+
<h3 id="3-Proxy实现的双向绑定的特点"><a href="#3-Proxy实现的双向绑定的特点" class="headerlink" title="3.Proxy实现的双向绑定的特点"></a>3.Proxy实现的双向绑定的特点</h3><p>Proxy在ES2015规范中被正式发布,它在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写,我们可以这样认为,Proxy是<code>Object.defineProperty</code>的全方位加强版,具体的文档可以查看<a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=http%253A%252F%252Fes6.ruanyifeng.com%252F%2523docs%252Fproxy">此处</a>;</p>
|
|
196
|
+
<h4 id="3-1-Proxy可以直接监听对象而非属性"><a href="#3-1-Proxy可以直接监听对象而非属性" class="headerlink" title="3.1 Proxy可以直接监听对象而非属性"></a>3.1 Proxy可以直接监听对象而非属性</h4><p>我们还是以上文中用<code>Object.defineProperty</code>实现的极简版双向绑定为例,用Proxy进行改写。</p>
|
|
197
|
+
<pre><code>const input = document.getElementById('input');const p = document.getElementById('p');const obj ={};const newObj =newProxy(obj,{get:function(target, key, receiver){
|
|
198
|
+
console.log(`getting ${key}!`);return Reflect.get(target, key, receiver);},set:function(target, key, value, receiver){
|
|
199
|
+
console.log(target, key, value, receiver);if(key ==='text'){
|
|
200
|
+
input.value = value;
|
|
201
|
+
p.innerHTML = value;}return Reflect.set(target, key, value, receiver);},});
|
|
202
|
+
|
|
203
|
+
input.addEventListener('keyup',function(e){
|
|
204
|
+
newObj.text = e.target.value;});
|
|
205
|
+
</code></pre>
|
|
206
|
+
<p>在线示例 <a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io%252Fxiaomuzhu%252Fpen%252FKRmwRE%252F">Proxy版</a> by Iwobi (<a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io%252Fxiaomuzhu">@xiaomuzhu</a>) on <a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io">CodePen</a>.</p>
|
|
207
|
+
<p>我们可以看到,Proxy直接可以劫持整个对象,并返回一个新对象,不管是操作便利程度还是底层功能上都远强于<code>Object.defineProperty</code>。</p>
|
|
208
|
+
<h4 id="3-2-Proxy可以直接监听数组的变化"><a href="#3-2-Proxy可以直接监听数组的变化" class="headerlink" title="3.2 Proxy可以直接监听数组的变化"></a>3.2 Proxy可以直接监听数组的变化</h4><p>当我们对数组进行操作(push、shift、splice等)时,会触发对应的方法名称和<em>length</em>的变化,我们可以借此进行操作,以上文中<code>Object.defineProperty</code>无法生效的列表渲染为例。</p>
|
|
209
|
+
<pre><code>const list = document.getElementById('list');const btn = document.getElementById('btn');// 渲染列表const Render ={// 初始化init:function(arr){const fragment = document.createDocumentFragment();for(let i =0; i < arr.length; i++){const li = document.createElement('li');
|
|
210
|
+
li.textContent = arr[i];
|
|
211
|
+
fragment.appendChild(li);}
|
|
212
|
+
list.appendChild(fragment);},// 我们只考虑了增加的情况,仅作为示例change:function(val){const li = document.createElement('li');
|
|
213
|
+
li.textContent = val;
|
|
214
|
+
list.appendChild(li);},};// 初始数组const arr =[1,2,3,4];// 监听数组const newArr =newProxy(arr,{get:function(target, key, receiver){
|
|
215
|
+
console.log(key);return Reflect.get(target, key, receiver);},set:function(target, key, value, receiver){
|
|
216
|
+
console.log(target, key, value, receiver);if(key !=='length'){
|
|
217
|
+
Render.change(value);}return Reflect.set(target, key, value, receiver);},});// 初始化
|
|
218
|
+
window.onload=function(){
|
|
219
|
+
Render.init(arr);}// push数字
|
|
220
|
+
btn.addEventListener('click',function(){
|
|
221
|
+
newArr.push(6);});
|
|
222
|
+
</code></pre>
|
|
223
|
+
<p>在线示例 <a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io%252Fxiaomuzhu%252Fpen%252FzjwGoN%252F">Proxy列表渲染</a> by Iwobi (<a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io%252Fxiaomuzhu">@xiaomuzhu</a>) on <a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fcodepen.io">CodePen</a>.</p>
|
|
224
|
+
<p>很显然,Proxy不需要那么多hack(即使hack也无法完美实现监听)就可以无压力监听数组的变化,我们都知道,标准永远优先于hack。</p>
|
|
225
|
+
<h4 id="3-3-Proxy的其他优势"><a href="#3-3-Proxy的其他优势" class="headerlink" title="3.3 Proxy的其他优势"></a>3.3 Proxy的其他优势</h4><p>Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等是<code>Object.defineProperty</code>不具备的。</p>
|
|
226
|
+
<p>Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而<code>Object.defineProperty</code>只能遍历对象属性直接修改。</p>
|
|
227
|
+
<p>Proxy作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利。</p>
|
|
228
|
+
<p>当然,Proxy的劣势就是兼容性问题,而且无法用polyfill磨平,因此Vue的作者才声明需要等到下个大版本(3.0)才能用Proxy重写。</p>
|
|
229
|
+
<h3 id="往期"><a href="#往期" class="headerlink" title="往期"></a>往期</h3><ul>
|
|
230
|
+
<li><a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fjuejin.im%252Fpost%252F5abb55ee6fb9a028e33b7e0a">面试官系列(1): 如何实现深克隆</a></li>
|
|
231
|
+
<li><a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fjuejin.im%252Fpost%252F5ac2fb886fb9a028b86e328c">面试官系列(2): Event Bus的实现</a></li>
|
|
232
|
+
<li><a href="https://link.jianshu.com/?t=https://link.juejin.im/?target=https%253A%252F%252Fjuejin.im%252Fpost%252F5ac61da66fb9a028c71eae1b">面试官系列(3): 前端路由的实现</a></li>
|
|
233
|
+
</ul>
|
|
234
|
+
</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/26147.html">https://xuehuayu.cn/article/26147.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/10047.html">2019年Web前端入门的自学路线</a><a class="next" href="/article/29042.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
|
|
235
|
+
const serverURL = origin.includes('cainiaoblog') ? 'https://cmts.cainiaoblog.cn' : 'https://cmts.xuehuayu.cn'
|
|
236
|
+
const locale = {
|
|
237
|
+
placeholder: '请正确填写昵称和邮箱,方便接收回复通知~',
|
|
238
|
+
sofa: '沙发空缺中,还不快抢~',
|
|
239
|
+
};
|
|
240
|
+
Waline.init({
|
|
241
|
+
el: '#vcomment',
|
|
242
|
+
serverURL: serverURL,
|
|
243
|
+
pageSize: '20',
|
|
244
|
+
visitor: true, // 阅读量统计
|
|
245
|
+
requiredMeta: ['nick', 'mail'],
|
|
246
|
+
emoji: [
|
|
247
|
+
'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/weibo',
|
|
248
|
+
'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/qq',
|
|
249
|
+
'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/tw-emoji',
|
|
250
|
+
],
|
|
251
|
+
locale,
|
|
252
|
+
pageview: true
|
|
253
|
+
})
|
|
254
|
+
</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() {
|
|
255
|
+
var val = $(this).val()
|
|
256
|
+
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>'
|
|
257
|
+
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>'
|
|
258
|
+
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>'
|
|
259
|
+
|
|
260
|
+
if (val === 'self') {
|
|
261
|
+
$('#search').html(self)
|
|
262
|
+
var search_path = 'search.xml';
|
|
263
|
+
if (search_path.length == 0) {
|
|
264
|
+
search_path = 'search.xml';
|
|
265
|
+
}
|
|
266
|
+
var path = 'https://cdn.staticaly.com/gh/npljy/npljy.github.io/main/' + search_path;
|
|
267
|
+
searchFunc(path, 'local-search-input', 'local-search-result');
|
|
268
|
+
} else if (val === 'baidu') {
|
|
269
|
+
$('#search').html(baidu)
|
|
270
|
+
} else if (val === 'google') {
|
|
271
|
+
$('#search').html(google)
|
|
272
|
+
}
|
|
273
|
+
})</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(){
|
|
274
|
+
var cnb =window.location.origin.indexOf('cainiaoblog') !==-1
|
|
275
|
+
if (cnb) {
|
|
276
|
+
var gxba =document.getElementById('gxba')
|
|
277
|
+
var gaba =document.getElementById('gaba')
|
|
278
|
+
var gabaLink =document.getElementById('gaba-link')
|
|
279
|
+
gxba.innerText ='京ICP备20007647号-1'
|
|
280
|
+
gaba.innerText ='京公网安备 11010802031254号'
|
|
281
|
+
gabaLink.setAttribute('href','http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010802031254')
|
|
282
|
+
}
|
|
283
|
+
})()</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,110 @@
|
|
|
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>重磅!微软发布 Visual Studio Online:Web 版 VS Code + 云开发环境 | 前端壹菜鸟</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">重磅!微软发布 Visual Studio Online:Web 版 VS Code + 云开发环境</h1><div class="post-meta"><span class="date">2019-11-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/%E8%B5%84%E8%AE%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.1k</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/26291.html#vcomment"><span class="waline-comment-count" data-path="/article/26291.html"></span><span> 条评论</span></a><div class="post-content"><p><code>原文地址:https://zhuanlan.zhihu.com/p/90094288</code></p>
|
|
20
|
+
<p>今天(北京时间 2019 年 11 月 4 日),在 Microsoft Ignite 2019 大会上,微软正式发布了 Visual Studio Online 公开预览版!</p>
|
|
21
|
+
<p>今天发布的 Visual Studio Online 与五月份发布的 <a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/64505333">Visual Studio Code Remote Development</a> 可以说是微软今年在开发工具领域最重磅的两个新产品了。</p>
|
|
22
|
+
<span id="more"></span>
|
|
23
|
+
|
|
24
|
+
<h2 id="概览"><a href="#概览" class="headerlink" title="概览"></a>概览</h2><p>Visual Studio Online 提供了由云服务支撑的开发环境。无论是一个长期项目,或是像 review pull request 这样的短期任务,都可以使用 Visual Studio Online。你可以通过 VS Code、VS IDE 或者 Web 版 VS Code 来连接上云端开发环境。此外,你还可以连接到自己的环境,不需要任何费用!</p>
|
|
25
|
+
<p>简单来说,Visual Studio Online 由两部分组成:“前端”与“后端”。</p>
|
|
26
|
+
<ul>
|
|
27
|
+
<li>“前端”:VS Code、VS IDE 和 Web 版 VS Code。</li>
|
|
28
|
+
<li>“后端”:由云服务支撑的开发环境。</li>
|
|
29
|
+
</ul>
|
|
30
|
+
<h2 id="云开发环境"><a href="#云开发环境" class="headerlink" title="云开发环境"></a>云开发环境</h2><p><img src="https://pic1.zhimg.com/v2-cbd8dc97b5c09258e0312f19d1a67398_b.jpg" alt="xuehuayu.cn"><img src="https://pic1.zhimg.com/80/v2-cbd8dc97b5c09258e0312f19d1a67398_hd.jpg" alt="xuehuayu.cn"><br>云开发环境是 Visual Studio Online 的“后端”,背后由 Azure 云计算平台提供强有力的支持。目前,Visual Studio Online 云开发环境支持 Linux 云开发环境,有以下两种环境可供选择:</p>
|
|
31
|
+
<ul>
|
|
32
|
+
<li>Standard (4 cores, 8 GB RAM, 64 GB HDD)</li>
|
|
33
|
+
<li>Premium (8 cores, 16 GB RAM, 64 GB HDD)</li>
|
|
34
|
+
</ul>
|
|
35
|
+
<p>目前,Windows 云开发环境处于 Private Preview 阶段。</p>
|
|
36
|
+
<p>Visual Studio Online 云开发环境包含了所有软件开发的内容:编译、调试、开发环境还原,等等。当你需要开发一个新项目,或者要 review PR 时,你可以快速地启动一个云开发环境。它会自动配置你需要在项目上工作的所有内容:源代码、运行时、编译器、调试器、编辑器、自定义的 dotfiles、相关的插件等。</p>
|
|
37
|
+
<p>Visual Studio Online 云开发环境享有诸多云计算带来的好处:</p>
|
|
38
|
+
<ul>
|
|
39
|
+
<li>可以快速地创建或者关闭云端的开发环境。这使得开发者可以有更多的时间在 coding 上,而不是把时间浪费在搭建开发环境上。</li>
|
|
40
|
+
<li>水平扩展:你也可以创建多个不同的开发环境。</li>
|
|
41
|
+
<li>垂直扩展:如果需要更强的运算能力,可以选择更强的开发环境,使用更强的 CPU 与内存。</li>
|
|
42
|
+
<li>由于是托管的环境,无维护成本。</li>
|
|
43
|
+
<li>不同的项目有独立的开发与运行环境,相互不会干扰。</li>
|
|
44
|
+
<li>按使用量付费。Visual Studio Online 也有内置的自动挂起机制,防止额外的开销。</li>
|
|
45
|
+
</ul>
|
|
46
|
+
<h2 id="Web-版-VS-Code"><a href="#Web-版-VS-Code" class="headerlink" title="Web 版 VS Code"></a>Web 版 VS Code</h2><p><img src="https://pic3.zhimg.com/v2-1b2de8724df57cfbe851247a99eb5d46_b.jpg" alt="xuehuayu.cn"><img src="https://pic3.zhimg.com/80/v2-1b2de8724df57cfbe851247a99eb5d46_hd.jpg" alt="xuehuayu.cn"><br>相信读者们<a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/64874266">对 Web 版 VS Code 期待已久</a>了。对 VS Code 熟悉的朋友应该知道,VS Code 是基于 Electron 开发的,而 Electron 是使用 Web 技术栈(JavaScript, HTML 和 CSS)来开发跨平台桌面应用的。所以,把 VS Code 搬到浏览器中,则是必然的趋势。</p>
|
|
47
|
+
<p>Visual Studio Online 包含了基于 VS Code 的 Web 版编辑器,它作为 Visual Studio Online 的“前端”,有以下的这些亮点:</p>
|
|
48
|
+
<ul>
|
|
49
|
+
<li>可以直接在 Web 版 VS Code 中打开你的 Git 项目。</li>
|
|
50
|
+
<li>支持 VS Code 插件。丰富的插件生态,任你挑选你喜欢的插件。</li>
|
|
51
|
+
<li>内置的集成终端。犹如在本地的 Terminal 一样强大!</li>
|
|
52
|
+
<li>内置的 Visual Studio IntelliCode,利用 AI 提供更强大的代码自动补全,</li>
|
|
53
|
+
<li>内置的 <a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/78515361">Visual Studio Live Share</a>,使得多个开发者可以在 VS Code、VS IDE 或者 Web 版 VS Code 中进行实时的协同开发和调试。</li>
|
|
54
|
+
<li>在任何设备上编辑、运行、调试你的项目。是的!可以在 iPad 上运行 VS Code(Web 版)了!</li>
|
|
55
|
+
</ul>
|
|
56
|
+
<h2 id="不止-Web"><a href="#不止-Web" class="headerlink" title="不止 Web"></a>不止 Web</h2><p>除了 Web 版 VS Code 之外,Visual Studio Online 还支持通过 VS Code 和 VS IDE(Private Preview)连接 Visual Studio Online 云开发环境。</p>
|
|
57
|
+
<p>Linux 云开发环境(Public Preview)可以通过两种“前端”编辑器连接:VS Code 和 Web 版 VS Code。</p>
|
|
58
|
+
<p>Windows 云开发环境(Private Preview)可以通过所有的三种“前端”编辑器连接:VS Code、VS IDE 和 Web 版 VS Code。</p>
|
|
59
|
+
<p>目前,VS IDE 对于 Visual Studio Online 的支持处于 Private Preview 阶段。</p>
|
|
60
|
+
<h2 id="自托管的环境"><a href="#自托管的环境" class="headerlink" title="自托管的环境"></a>自托管的环境</h2><p>除了连上由 Azure 支持的 Visual Studio Online 云开发环境,开发者还可以连上自己搭建的开发环境。其实,通过 <a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/64505333">Visual Studio Code Remote Development</a>,开发者早已可以通过 VS Code 来连上远程的开发环境了。现在,有了 Web 版 VS Code,我们可以直接在浏览器中连上自己的远程开发环境,更加地方便!而且,这是完全免费的。</p>
|
|
61
|
+
</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/26291.html">https://xuehuayu.cn/article/26291.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/10745.html">React Hooks 详解+项目实战</a><a class="next" href="/article/63542.html">webpack常用的plugin</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
|
|
62
|
+
const serverURL = origin.includes('cainiaoblog') ? 'https://cmts.cainiaoblog.cn' : 'https://cmts.xuehuayu.cn'
|
|
63
|
+
const locale = {
|
|
64
|
+
placeholder: '请正确填写昵称和邮箱,方便接收回复通知~',
|
|
65
|
+
sofa: '沙发空缺中,还不快抢~',
|
|
66
|
+
};
|
|
67
|
+
Waline.init({
|
|
68
|
+
el: '#vcomment',
|
|
69
|
+
serverURL: serverURL,
|
|
70
|
+
pageSize: '20',
|
|
71
|
+
visitor: true, // 阅读量统计
|
|
72
|
+
requiredMeta: ['nick', 'mail'],
|
|
73
|
+
emoji: [
|
|
74
|
+
'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/weibo',
|
|
75
|
+
'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/qq',
|
|
76
|
+
'https://cdn.jsdelivr.net/gh/walinejs/emojis@1.0.0/tw-emoji',
|
|
77
|
+
],
|
|
78
|
+
locale,
|
|
79
|
+
pageview: true
|
|
80
|
+
})
|
|
81
|
+
</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() {
|
|
82
|
+
var val = $(this).val()
|
|
83
|
+
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>'
|
|
84
|
+
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>'
|
|
85
|
+
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>'
|
|
86
|
+
|
|
87
|
+
if (val === 'self') {
|
|
88
|
+
$('#search').html(self)
|
|
89
|
+
var search_path = 'search.xml';
|
|
90
|
+
if (search_path.length == 0) {
|
|
91
|
+
search_path = 'search.xml';
|
|
92
|
+
}
|
|
93
|
+
var path = 'https://cdn.staticaly.com/gh/npljy/npljy.github.io/main/' + search_path;
|
|
94
|
+
searchFunc(path, 'local-search-input', 'local-search-result');
|
|
95
|
+
} else if (val === 'baidu') {
|
|
96
|
+
$('#search').html(baidu)
|
|
97
|
+
} else if (val === 'google') {
|
|
98
|
+
$('#search').html(google)
|
|
99
|
+
}
|
|
100
|
+
})</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(){
|
|
101
|
+
var cnb =window.location.origin.indexOf('cainiaoblog') !==-1
|
|
102
|
+
if (cnb) {
|
|
103
|
+
var gxba =document.getElementById('gxba')
|
|
104
|
+
var gaba =document.getElementById('gaba')
|
|
105
|
+
var gabaLink =document.getElementById('gaba-link')
|
|
106
|
+
gxba.innerText ='京ICP备20007647号-1'
|
|
107
|
+
gaba.innerText ='京公网安备 11010802031254号'
|
|
108
|
+
gabaLink.setAttribute('href','http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11010802031254')
|
|
109
|
+
}
|
|
110
|
+
})()</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>
|