[{"data":1,"prerenderedAt":22452},["ShallowReactive",2],{"page-\u002Finterview-notes":3},{"id":4,"title":5,"body":6,"description":101,"extension":22446,"meta":22447,"navigation":306,"path":22448,"seo":22449,"stem":22450,"__hash__":22451},"content\u002Finterview-notes.md","Interview Notes",{"type":7,"value":8,"toc":22370},"minimark",[9,13,17,48,51,54,92,95,213,216,223,258,268,271,358,368,387,395,398,401,404,460,463,534,550,557,733,745,765,771,774,822,828,831,834,940,957,963,966,977,1152,1155,1279,1285,1294,1311,1333,1339,1367,1373,1376,1454,1471,1481,1484,1599,1602,1608,1615,1697,1704,1793,1796,1858,1861,1886,1889,1894,1897,1915,1927,1930,1974,1985,1988,2003,2006,2009,2012,2015,2018,2021,2024,2030,2033,2036,2046,2049,2052,2055,2188,2190,2261,2273,2286,2352,2366,2369,2460,2474,2477,2565,2568,2577,2584,2755,2759,2762,2775,2889,2899,2911,2915,2918,2924,2943,2997,3010,3013,3017,3020,3120,3127,3130,3133,3136,3142,3145,3148,3154,3225,3232,3239,3340,3344,3347,3350,3353,3394,3433,3436,3485,3489,3492,3535,3538,3544,3547,3559,3581,3605,3632,3635,3641,3644,3647,4930,4933,4936,4962,4965,5296,5300,5303,5306,5317,5320,5323,5333,5426,5430,5433,5436,5439,5446,5449,5452,5458,5461,5464,5563,5566,5695,5702,5837,5847,5894,5897,5998,6001,6231,6234,6386,6389,6600,6603,6677,6680,6710,6728,6739,6987,6991,6994,7001,7342,7347,7484,7487,7490,7567,7570,7573,7576,7582,7585,7591,7594,7600,7603,7609,7613,7616,7619,7622,7626,7632,7635,7638,7641,7644,7647,7650,7666,7669,7675,7678,7684,7695,7698,7701,7704,7710,7713,7716,7722,7725,7728,7731,7737,7762,7768,7779,7782,7785,7788,7791,7794,7800,7804,7808,7844,7850,7976,8000,8007,8027,8030,8033,8105,8134,8137,8159,8165,8206,8222,8225,8386,8415,8506,8509,8512,8689,8691,8737,8740,8785,8788,8863,8866,8869,8945,8951,9052,9058,9132,9144,9147,9155,9158,9212,9359,9369,9417,9420,9429,9655,9658,9669,9673,9676,9679,9682,9685,9691,9694,9700,9703,9709,9712,9799,9802,9805,9808,9814,9817,9820,9823,9830,9836,9839,9875,9878,9884,9887,9952,9955,9969,9972,10010,10013,10016,10109,10112,10115,10118,10121,10132,10135,10143,10214,10217,10346,10349,10352,10359,10365,10368,10371,10819,10822,10825,10828,10916,10919,10921,10924,11029,11032,11271,11278,11281,11291,11423,11439,11472,11475,11484,11487,11503,11506,11641,11647,11689,11702,11705,11799,13877,13880,13883,13886,13889,13892,13895,13898,13925,13931,13966,13971,13974,14116,14119,14284,14287,14393,14400,14408,14472,14475,14478,14481,14584,14793,14848,14857,15061,15064,15227,15240,15243,15246,15249,15269,15272,15372,15375,15527,15530,15649,15652,15655,15745,15748,15828,15831,15998,16001,16004,16288,16302,16593,16596,16627,16634,16987,16990,16993,16996,16999,17144,17147,17275,17278,17448,17451,17454,17528,17531,17661,17664,17777,17779,17786,17793,17804,17807,17818,17821,17824,17830,17833,17837,17840,17847,17850,17853,17862,17868,17874,17893,17897,17900,17907,18045,18048,18052,18055,18058,18405,18408,18411,19081,19084,19086,19089,19092,19145,19148,19222,19225,19270,19273,19307,19310,19313,19316,19356,19359,19362,19381,19387,19390,19396,19407,19410,19500,19503,19568,19571,19634,19637,19640,19643,19646,19649,19656,19662,19668,19671,19725,19728,19731,19734,19737,19772,19804,19863,19877,19890,19897,19903,19928,19931,19934,19954,19960,20062,20068,20078,20095,20098,20143,20146,20149,20155,20158,20270,20273,20278,20466,20469,20473,20476,20479,20482,20488,20491,20494,20498,20504,20510,20513,20522,20525,20528,20531,20537,20540,20584,20587,20593,20604,20607,20612,20623,20626,20629,20632,20635,20638,20641,20738,20741,20744,20747,20750,20753,20756,20880,20886,20896,20899,20902,20905,20911,20914,20917,20920,20926,20929,20932,20938,20953,20956,20959,20965,20976,20979,20985,20996,21002,21005,21008,21011,21014,21020,21023,21029,21036,21042,21045,21048,21051,21054,21060,21063,21066,21072,21078,21081,21084,21095,21098,21104,21108,21117,21123,21126,21133,21136,21142,21145,21149,21152,21155,21158,21335,21338,21384,21402,21431,21434,21440,21443,21559,21562,21565,21568,21571,21574,21797,21800,21803,21934,21937,22064,22067,22078,22089,22092,22095,22098,22101,22104,22112,22120,22124,22127,22130,22136,22150,22156,22159,22162,22166,22169,22172,22175,22178,22181,22184,22199,22202,22205,22212,22215,22221,22227,22331,22334,22337,22340,22343,22346,22349,22352,22355,22358,22361,22364,22367],[10,11,12],"h2",{"id":12},"样式",[14,15,16],"h3",{"id":16},"postcss部分",[18,19,20,27,28,31,32,35,36,41,42,47],"p",{},[21,22,23],"strong",{},[24,25,26],"code",{},"autoprefixer"," 插件比较常用，可以根据browserslist 自动加浏览器前缀（",[24,29,30],{},"-webkit-","、",[24,33,34],{},"-moz-","），",[21,37,38],{},[24,39,40],{},"postcss-preset-env"," 插件可以帮我们把CSS语法降低为兼容性语法。",[21,43,44],{},[24,45,46],{},"cssnano"," 可以帮我们压缩CSS、去掉空格、注释、合并规则。并且要注意的是postcss的插件是按照数组顺序来配置的·，并且是按照顺序执行，而与之不同的是webpack的loader，是从下往上执行。",[18,49,50],{},"postcss处理的是标准css，把标准css给解析成AST，然后让插件改，sass\u002Fless是预处理器，处理的是他们自己的语法，编译成css之后postcss才接管。",[14,52,53],{"id":53},"tailwind",[18,55,56,57,60,61,67,71,72,75,76,79,80,83,84,87,88,91],{},"tailwind3开始 jit就是默认引擎了，它",[21,58,59],{},"只生成你用到的类","，而不是先生成全部再“清除”未使用的",[62,63],"a",{"href":64,"rel":65},"https:\u002F\u002Fstackoverflow.com\u002Frevisions\u002Fdb144099-3e0d-4d40-ac2c-1c2372c9caff\u002Fview-source",[66],"nofollow",[62,68],{"href":69,"rel":70},"https:\u002F\u002Fgithub.com\u002Ftailwindlabs\u002Ftailwindcss\u002Fdiscussions\u002F17570?sort=top#1",[66],"。所以 ",[24,73,74],{},"purge"," 选项在 v3 里改名叫 ",[24,77,78],{},"content","，而且 ",[24,81,82],{},"mode: 'jit'"," 这个配置项",[21,85,86],{},"已经不需要了","，写了反而会警告",[62,89],{"href":64,"rel":90},[66],"。",[18,93,94],{},"从版本4开始，@theme取代了tailwind.config.js，我们填写主题配置都在css里面直接配置",[96,97,102],"pre",{"className":98,"code":99,"language":100,"meta":101,"style":101},"language-css shiki shiki-themes github-light github-dark","@theme {\n    --color-primary: #3b82f6\n    --breakpoint-sm: 640px;\n    --breakpoint-md: 768px;\n    --breakpoint-lg: 1024px; %%自定义断点%%\n    --breakpoint-xl: 1280px;\n    --breakpoint-2xl: 1536px;\n}\n","css","",[24,103,104,117,131,146,161,177,192,207],{"__ignoreMap":101},[105,106,109,113],"span",{"class":107,"line":108},"line",1,[105,110,112],{"class":111},"szBVR","@theme",[105,114,116],{"class":115},"sVt8B"," {\n",[105,118,120,124,127],{"class":107,"line":119},2,[105,121,123],{"class":122},"s4XuR","    --color-primary",[105,125,126],{"class":115},": ",[105,128,130],{"class":129},"sj4cs","#3b82f6\n",[105,132,134,137,140,143],{"class":107,"line":133},3,[105,135,136],{"class":115},"    --breakpoint-sm: ",[105,138,139],{"class":129},"640",[105,141,142],{"class":111},"px",[105,144,145],{"class":115},";\n",[105,147,149,152,154,157,159],{"class":107,"line":148},4,[105,150,151],{"class":122},"    --breakpoint-md",[105,153,126],{"class":115},[105,155,156],{"class":129},"768",[105,158,142],{"class":111},[105,160,145],{"class":115},[105,162,164,167,169,172,174],{"class":107,"line":163},5,[105,165,166],{"class":122},"    --breakpoint-lg",[105,168,126],{"class":115},[105,170,171],{"class":129},"1024",[105,173,142],{"class":111},[105,175,176],{"class":115},"; %%自定义断点%%\n",[105,178,180,183,185,188,190],{"class":107,"line":179},6,[105,181,182],{"class":122},"    --breakpoint-xl",[105,184,126],{"class":115},[105,186,187],{"class":129},"1280",[105,189,142],{"class":111},[105,191,145],{"class":115},[105,193,195,198,200,203,205],{"class":107,"line":194},7,[105,196,197],{"class":122},"    --breakpoint-2xl",[105,199,126],{"class":115},[105,201,202],{"class":129},"1536",[105,204,142],{"class":111},[105,206,145],{"class":115},[105,208,210],{"class":107,"line":209},8,[105,211,212],{"class":115},"}\n",[18,214,215],{},"到时候会自动生成bg-primary或者text-等这些类。",[18,217,218,219,222],{},"有一点比较重要,tailwind不能动态拼接类名，比如说 ",[24,220,221],{},"text-${color}-600"," 是无效的，因为 JIT 扫描的是静态字符串，它不认识运行时才拼出来的类名。正确做法是用完整的类名做条件判断：",[96,224,228],{"className":225,"code":226,"language":227,"meta":101,"style":101},"language-html shiki shiki-themes github-light github-dark","\u003Cdiv :class=\"error ? 'text-red-600' : 'text-green-600'\">\u003C\u002Fdiv>\n","html",[24,229,230],{"__ignoreMap":101},[105,231,232,235,239,243,246,250,253,255],{"class":107,"line":108},[105,233,234],{"class":115},"\u003C",[105,236,238],{"class":237},"s9eBZ","div",[105,240,242],{"class":241},"sScJk"," :class",[105,244,245],{"class":115},"=",[105,247,249],{"class":248},"sZZnC","\"error ? 'text-red-600' : 'text-green-600'\"",[105,251,252],{"class":115},">\u003C\u002F",[105,254,238],{"class":237},[105,256,257],{"class":115},">\n",[18,259,260,261,264,265],{},"如果需要动态值，用任意值语法 ",[24,262,263],{},"text-[#bada55]","，或者用 CSS 变量 ",[24,266,267],{},"text-(--my-color)",[18,269,270],{},"tailwindv4 通过@import 'tailwindcss'; 一个命令等价于：",[96,272,274],{"className":98,"code":273,"language":100,"meta":101,"style":101},"@layer theme, base, components, utilities;\n\n@import \"tailwindcss\u002Ftheme.css\" layer(theme);\n@import \"tailwindcss\u002Fpreflight.css\" layer(base);\n@import \"tailwindcss\u002Futilities.css\" layer(utilities);\n",[24,275,276,302,308,328,343],{"__ignoreMap":101},[105,277,278,281,284,287,290,292,295,297,300],{"class":107,"line":108},[105,279,280],{"class":111},"@layer",[105,282,283],{"class":122}," theme",[105,285,286],{"class":115},", ",[105,288,289],{"class":122},"base",[105,291,286],{"class":115},[105,293,294],{"class":122},"components",[105,296,286],{"class":115},[105,298,299],{"class":122},"utilities",[105,301,145],{"class":115},[105,303,304],{"class":107,"line":119},[105,305,307],{"emptyLinePlaceholder":306},true,"\n",[105,309,310,313,316,319,322,325],{"class":107,"line":133},[105,311,312],{"class":111},"@import",[105,314,315],{"class":248}," \"tailwindcss\u002Ftheme.css\"",[105,317,318],{"class":129}," layer",[105,320,321],{"class":115},"(",[105,323,324],{"class":122},"theme",[105,326,327],{"class":115},");\n",[105,329,330,332,335,337,339,341],{"class":107,"line":148},[105,331,312],{"class":111},[105,333,334],{"class":248}," \"tailwindcss\u002Fpreflight.css\"",[105,336,318],{"class":129},[105,338,321],{"class":115},[105,340,289],{"class":122},[105,342,327],{"class":115},[105,344,345,347,350,352,354,356],{"class":107,"line":163},[105,346,312],{"class":111},[105,348,349],{"class":248}," \"tailwindcss\u002Futilities.css\"",[105,351,318],{"class":129},[105,353,321],{"class":115},[105,355,299],{"class":122},[105,357,327],{"class":115},[18,359,360,361,31,364,367],{},"我们从这四个层级来说的话，越靠后优先级越高，我们经常用theme来设计颜色、间距、字体、断体等，会自动生成对应的工具类。\nbase用来元素重置和全局基础样式，也可以叫prelight，需要重置 HTML 元素默认样式时，比如去掉标题的默认字号、列表的圆点。\ncomponents是可复用可覆盖的组件类，封装像 ",[24,362,363],{},".btn",[24,365,366],{},".card"," 这样的组件模式，希望被工具类覆盖时\nutilities是原子化工具类比如说flex bg-red-500等，日常写样式的主要方式，优先级最高，能覆盖前面的所有层。css有个规则， @layer，同一层叠层内的规则将层叠在一起，这给予了 web 开发者对层叠机制的更多控制",[18,369,370,375,376,379,380,382,383,386],{},[371,372],"img",{"alt":373,"src":374},"层叠层优先级示意图","https:\u002F\u002Fmdn.github.io\u002Fshared-assets\u002Fimages\u002Fdiagrams\u002Fcss\u002Fat-rules\u002Flayer-cascade.svg","\n带 ",[24,377,378],{},"!important"," 标志的声明，优先于_普通声明_，即无 ",[24,381,378],{}," 标志的常规声明，过渡具有最高的优先级。按 ",[21,384,385],{},"CSS Cascade 5"," 规范，优先级从高到低是：",[96,388,393],{"className":389,"code":391,"language":392},[390],"language-text","1. 过渡（Transition）声明\n2. 重要用户代理声明（!important user agent）\n3. 重要用户声明（!important user）\n4. 重要作者声明（!important author）\n5. 动画（Animation）声明\n6. 普通作者声明（Normal author）\n7. 普通用户声明（Normal user）\n8. 普通用户代理声明（Normal user agent）\n","text",[24,394,391],{"__ignoreMap":101},[18,396,397],{},"看起来像个镜面对称一样，user agent \u002F user\u002F author | author\u002F user\u002F user agent",[18,399,400],{},"这里提一嘴用户代理和过渡声明：",[18,402,403],{},"用户代理是浏览器自带的默认样式表，你写一个没加任何 CSS 的 HTML：",[96,405,407],{"className":225,"code":406,"language":227,"meta":101,"style":101},"\u003Ch1>标题\u003C\u002Fh1>\n\u003Cp>段落\u003C\u002Fp>\n\u003Cul>\u003Cli>列表项\u003C\u002Fli>\u003C\u002Ful>\n",[24,408,409,423,436],{"__ignoreMap":101},[105,410,411,413,416,419,421],{"class":107,"line":108},[105,412,234],{"class":115},[105,414,415],{"class":237},"h1",[105,417,418],{"class":115},">标题\u003C\u002F",[105,420,415],{"class":237},[105,422,257],{"class":115},[105,424,425,427,429,432,434],{"class":107,"line":119},[105,426,234],{"class":115},[105,428,18],{"class":237},[105,430,431],{"class":115},">段落\u003C\u002F",[105,433,18],{"class":237},[105,435,257],{"class":115},[105,437,438,440,443,446,449,452,454,456,458],{"class":107,"line":133},[105,439,234],{"class":115},[105,441,442],{"class":237},"ul",[105,444,445],{"class":115},">\u003C",[105,447,448],{"class":237},"li",[105,450,451],{"class":115},">列表项\u003C\u002F",[105,453,448],{"class":237},[105,455,252],{"class":115},[105,457,442],{"class":237},[105,459,257],{"class":115},[18,461,462],{},"浏览器会自己加上h1的字号、字重、上下margin，p的上下margin，ul的左padding和原点，body的8px margin，因此我们引入@base的时候就会移除默认样式，当然，我们可以在@layer base中加入一些自己的基础样式，比如说：",[96,464,466],{"className":98,"code":465,"language":100,"meta":101,"style":101},"@import \"tailwindcss\";\n\n@layer base {\n  h1 {\n    @apply text-3xl font-bold;\n  }\n  body {\n    @apply bg-gray-50 text-gray-900;\n  }\n}\n",[24,467,468,477,481,490,497,505,510,517,524,529],{"__ignoreMap":101},[105,469,470,472,475],{"class":107,"line":108},[105,471,312],{"class":111},[105,473,474],{"class":248}," \"tailwindcss\"",[105,476,145],{"class":115},[105,478,479],{"class":107,"line":119},[105,480,307],{"emptyLinePlaceholder":306},[105,482,483,485,488],{"class":107,"line":133},[105,484,280],{"class":111},[105,486,487],{"class":122}," base",[105,489,116],{"class":115},[105,491,492,495],{"class":107,"line":148},[105,493,494],{"class":237},"  h1",[105,496,116],{"class":115},[105,498,499,502],{"class":107,"line":163},[105,500,501],{"class":111},"    @apply",[105,503,504],{"class":115}," text-3xl font-bold;\n",[105,506,507],{"class":107,"line":179},[105,508,509],{"class":115},"  }\n",[105,511,512,515],{"class":107,"line":194},[105,513,514],{"class":237},"  body",[105,516,116],{"class":115},[105,518,519,521],{"class":107,"line":209},[105,520,501],{"class":111},[105,522,523],{"class":115}," bg-gray-50 text-gray-900;\n",[105,525,527],{"class":107,"line":526},9,[105,528,509],{"class":115},[105,530,532],{"class":107,"line":531},10,[105,533,212],{"class":115},[18,535,536,537,540,541,544,545],{},"然后呢，过渡优先级最高这个说法是来自CSS级联里面一个比较特殊的设计，和常规的优先级不是一个维度，过渡不是样式，是状态间插值，color:red -> color:blue 的情况，比如说你加了 transition: color 0.3s，浏览器会在0.3s内持续计算中间值，这个中间值在某一瞬间是 ",[24,538,539],{},"rgb(128, 0, 128)"," 之类的，它",[21,542,543],{},"既不是 red 也不是 blue","。如果这个中间值能被其他 CSS 规则覆盖，过渡就会“抖动”或“卡顿”。所以规范规定：",[21,546,547,548,91],{},"过渡期间计算出来的值，优先级高于任何其他声明，包括 ",[24,549,378],{},[18,551,552,553,556],{},"如果说想要在css里模拟wpf的动画控制，可以使用web Animation API WAAPI来实现，WAAPI 是浏览器提供的",[21,554,555],{},"命令式动画 API","，更接近 WPF：",[96,558,562],{"className":559,"code":560,"language":561,"meta":101,"style":101},"language-js shiki shiki-themes github-light github-dark","const animation = element.animate(\n  [{ transform: 'translateX(0)' }, { transform: 'translateX(100px)' }],\n  { duration: 300, easing: 'ease' }\n);\n\n\u002F\u002F 检测\nconsole.log(animation.playState);  \u002F\u002F 'running' | 'paused' | 'finished'\n\n\u002F\u002F 打断\nanimation.cancel();\n\n\u002F\u002F 暂停、继续\nanimation.pause();\nanimation.play();\n\n\u002F\u002F 监听\nanimation.onfinish = () => { \u002F* ... *\u002F };\n","js",[24,563,564,584,601,618,622,626,632,646,650,655,666,671,677,687,697,702,708],{"__ignoreMap":101},[105,565,566,569,572,575,578,581],{"class":107,"line":108},[105,567,568],{"class":111},"const",[105,570,571],{"class":129}," animation",[105,573,574],{"class":111}," =",[105,576,577],{"class":115}," element.",[105,579,580],{"class":241},"animate",[105,582,583],{"class":115},"(\n",[105,585,586,589,592,595,598],{"class":107,"line":119},[105,587,588],{"class":115},"  [{ transform: ",[105,590,591],{"class":248},"'translateX(0)'",[105,593,594],{"class":115}," }, { transform: ",[105,596,597],{"class":248},"'translateX(100px)'",[105,599,600],{"class":115}," }],\n",[105,602,603,606,609,612,615],{"class":107,"line":133},[105,604,605],{"class":115},"  { duration: ",[105,607,608],{"class":129},"300",[105,610,611],{"class":115},", easing: ",[105,613,614],{"class":248},"'ease'",[105,616,617],{"class":115}," }\n",[105,619,620],{"class":107,"line":148},[105,621,327],{"class":115},[105,623,624],{"class":107,"line":163},[105,625,307],{"emptyLinePlaceholder":306},[105,627,628],{"class":107,"line":179},[105,629,631],{"class":630},"sJ8bj","\u002F\u002F 检测\n",[105,633,634,637,640,643],{"class":107,"line":194},[105,635,636],{"class":115},"console.",[105,638,639],{"class":241},"log",[105,641,642],{"class":115},"(animation.playState);  ",[105,644,645],{"class":630},"\u002F\u002F 'running' | 'paused' | 'finished'\n",[105,647,648],{"class":107,"line":209},[105,649,307],{"emptyLinePlaceholder":306},[105,651,652],{"class":107,"line":526},[105,653,654],{"class":630},"\u002F\u002F 打断\n",[105,656,657,660,663],{"class":107,"line":531},[105,658,659],{"class":115},"animation.",[105,661,662],{"class":241},"cancel",[105,664,665],{"class":115},"();\n",[105,667,669],{"class":107,"line":668},11,[105,670,307],{"emptyLinePlaceholder":306},[105,672,674],{"class":107,"line":673},12,[105,675,676],{"class":630},"\u002F\u002F 暂停、继续\n",[105,678,680,682,685],{"class":107,"line":679},13,[105,681,659],{"class":115},[105,683,684],{"class":241},"pause",[105,686,665],{"class":115},[105,688,690,692,695],{"class":107,"line":689},14,[105,691,659],{"class":115},[105,693,694],{"class":241},"play",[105,696,665],{"class":115},[105,698,700],{"class":107,"line":699},15,[105,701,307],{"emptyLinePlaceholder":306},[105,703,705],{"class":107,"line":704},16,[105,706,707],{"class":630},"\u002F\u002F 监听\n",[105,709,711,713,716,718,721,724,727,730],{"class":107,"line":710},17,[105,712,659],{"class":115},[105,714,715],{"class":241},"onfinish",[105,717,574],{"class":111},[105,719,720],{"class":115}," () ",[105,722,723],{"class":111},"=>",[105,725,726],{"class":115}," { ",[105,728,729],{"class":630},"\u002F* ... *\u002F",[105,731,732],{"class":115}," };\n",[18,734,735,737,738,741,742,744],{},[24,736,280],{}," 定义的是",[21,739,740],{},"作者样式内部","的优先级。同一层级的作者样式里，",[24,743,280],{}," 的规则是：",[442,746,747,756,762],{},[448,748,749,752,753,755],{},[21,750,751],{},"未分层的样式优先级最高","（也就是没加 ",[24,754,280],{}," 的普通 CSS）。",[448,757,758,761],{},[21,759,760],{},"分层样式按声明的顺序","，后声明的层优先级更高。",[448,763,764],{},"层级内部的样式，再按常规的选择器优先级比较。",[96,766,769],{"className":767,"code":768,"language":392},[390],"@layer base, components, utilities;\n\n@layer utilities {\n  .text-red { color: red; }\n}\n\n@layer components {\n  .text-red { color: blue; }\n}\n\n.text-red { color: green; }  \u002F* 无层级，优先级最高 *\u002F\n",[24,770,768],{"__ignoreMap":101},[18,772,773],{},"所以，如果想要两个一起使用，我们就得把css也一起放入@layer中，而如果是手写的css组件样式，希望被tailwind工具类覆盖，就得放入@layer components中。",[96,775,777],{"className":98,"code":776,"language":100,"meta":101,"style":101},"@import \"tailwindcss\";\n\n@layer components {\n  .card {\n    @apply rounded-lg bg-white p-4 shadow;\n  }\n}\n",[24,778,779,787,791,800,807,814,818],{"__ignoreMap":101},[105,780,781,783,785],{"class":107,"line":108},[105,782,312],{"class":111},[105,784,474],{"class":248},[105,786,145],{"class":115},[105,788,789],{"class":107,"line":119},[105,790,307],{"emptyLinePlaceholder":306},[105,792,793,795,798],{"class":107,"line":133},[105,794,280],{"class":111},[105,796,797],{"class":122}," components",[105,799,116],{"class":115},[105,801,802,805],{"class":107,"line":148},[105,803,804],{"class":241},"  .card",[105,806,116],{"class":115},[105,808,809,811],{"class":107,"line":163},[105,810,501],{"class":111},[105,812,813],{"class":115}," rounded-lg bg-white p-4 shadow;\n",[105,815,816],{"class":107,"line":179},[105,817,509],{"class":115},[105,819,820],{"class":107,"line":194},[105,821,212],{"class":115},[18,823,824,825,91],{},"有个很有趣的争议。叫做@apply的到底该不该用，@apply会把原子类的样式内联到自定义css内， 如果到处用，CSS 体积会膨胀，而且失去了原子化“组合灵活”的优势。所以我们只会在比较高频使用的重复类组合比如.btn来使用，或者需要",[21,826,827],{},"给第三方库的类注入样式",[14,829,830],{"id":100},"CSS",[18,832,833],{},"普通作者声明内部优先级从高到低是：",[835,836,837,853],"table",{},[838,839,840],"thead",{},[841,842,843,847,850],"tr",{},[844,845,846],"th",{},"优先级",[844,848,849],{},"来源",[844,851,852],{},"例子",[854,855,856,872,887,907,924],"tbody",{},[841,857,858,862,867],{},[859,860,861],"td",{},"1",[859,863,864],{},[21,865,866],{},"行内 style",[859,868,869],{},[24,870,871],{},"\u003Cdiv style=\"color: red\">",[841,873,874,877,882],{},[859,875,876],{},"2",[859,878,879],{},[21,880,881],{},"ID 选择器",[859,883,884],{},[24,885,886],{},"#header",[841,888,889,892,897],{},[859,890,891],{},"3",[859,893,894],{},[21,895,896],{},"类 \u002F 属性 \u002F 伪类",[859,898,899,31,901,31,904],{},[24,900,363],{},[24,902,903],{},"[type=\"text\"]",[24,905,906],{},":hover",[841,908,909,912,917],{},[859,910,911],{},"4",[859,913,914],{},[21,915,916],{},"元素 \u002F 伪元素",[859,918,919,31,921],{},[24,920,238],{},[24,922,923],{},"::before",[841,925,926,929,934],{},[859,927,928],{},"5",[859,930,931],{},[21,932,933],{},"通配符 \u002F 继承",[859,935,936,939],{},[24,937,938],{},"*","、继承来的值",[18,941,942,943,946,947,949,950,953,954,91],{},"CSS有一个叫做(a, b, c) 三元组表示法来表示选择器优先级，行内 style 的优先级是 ",[24,944,945],{},"(1, 0, 0, 0)","，比任何选择器都高，",[24,948,378],{}," 的优先级",[21,951,952],{},"高于所有普通声明","，包括行内 style。比较规则：",[21,955,956],{},"从左到右逐位比，大的赢",[96,958,961],{"className":959,"code":960,"language":392},[390],"#header .nav a { }     \u002F* (1, 1, 1) *\u002F\n.nav a { }             \u002F* (0, 1, 1) *\u002F\na { }                  \u002F* (0, 0, 1) *\u002F\n",[24,962,960],{"__ignoreMap":101},[18,964,965],{},"常用选择器：",[18,967,968,969,972,973,976],{},"后代选择器： 空格\n直接子选择器： >\n相邻兄弟： +\n通用兄弟： ~\n父选择器： ",[970,971],"has",{}," ，它本质是",[21,974,975],{},"关系选择器","，可以做很多以前需要 JS 才能做的事：",[96,978,980],{"className":98,"code":979,"language":100,"meta":101,"style":101},"\u002F* 选中「内部包含 img 的 div」 *\u002F\ndiv:has(img) {\n  border: 1px solid #ccc;\n}\n\n\u002F* 表单里有 input 聚焦时，给表单加样式 *\u002F\nform:has(input:focus) {\n  border-color: blue;\n}\n\n\u002F* 选中「后面跟着 p 的 h2」 *\u002F\nh2:has(+ p) {\n  margin-bottom: 0;\n}\n\n\u002F* 选中「不包含任何子元素的空 div」 *\u002F\ndiv:not(:has(*)) {\n  display: none;\n}\n",[24,981,982,987,1001,1020,1024,1028,1033,1050,1062,1066,1070,1075,1091,1103,1107,1111,1116,1134,1147],{"__ignoreMap":101},[105,983,984],{"class":107,"line":108},[105,985,986],{"class":630},"\u002F* 选中「内部包含 img 的 div」 *\u002F\n",[105,988,989,991,994,996,998],{"class":107,"line":119},[105,990,238],{"class":237},[105,992,993],{"class":241},":has",[105,995,321],{"class":115},[105,997,371],{"class":237},[105,999,1000],{"class":115},") {\n",[105,1002,1003,1006,1008,1010,1012,1015,1018],{"class":107,"line":133},[105,1004,1005],{"class":129},"  border",[105,1007,126],{"class":115},[105,1009,861],{"class":129},[105,1011,142],{"class":111},[105,1013,1014],{"class":129}," solid",[105,1016,1017],{"class":129}," #ccc",[105,1019,145],{"class":115},[105,1021,1022],{"class":107,"line":148},[105,1023,212],{"class":115},[105,1025,1026],{"class":107,"line":163},[105,1027,307],{"emptyLinePlaceholder":306},[105,1029,1030],{"class":107,"line":179},[105,1031,1032],{"class":630},"\u002F* 表单里有 input 聚焦时，给表单加样式 *\u002F\n",[105,1034,1035,1038,1040,1042,1045,1048],{"class":107,"line":194},[105,1036,1037],{"class":237},"form",[105,1039,993],{"class":241},[105,1041,321],{"class":115},[105,1043,1044],{"class":237},"input",[105,1046,1047],{"class":241},":focus",[105,1049,1000],{"class":115},[105,1051,1052,1055,1057,1060],{"class":107,"line":209},[105,1053,1054],{"class":129},"  border-color",[105,1056,126],{"class":115},[105,1058,1059],{"class":129},"blue",[105,1061,145],{"class":115},[105,1063,1064],{"class":107,"line":526},[105,1065,212],{"class":115},[105,1067,1068],{"class":107,"line":531},[105,1069,307],{"emptyLinePlaceholder":306},[105,1071,1072],{"class":107,"line":668},[105,1073,1074],{"class":630},"\u002F* 选中「后面跟着 p 的 h2」 *\u002F\n",[105,1076,1077,1079,1081,1083,1086,1089],{"class":107,"line":673},[105,1078,10],{"class":237},[105,1080,993],{"class":241},[105,1082,321],{"class":115},[105,1084,1085],{"class":111},"+",[105,1087,1088],{"class":237}," p",[105,1090,1000],{"class":115},[105,1092,1093,1096,1098,1101],{"class":107,"line":679},[105,1094,1095],{"class":129},"  margin-bottom",[105,1097,126],{"class":115},[105,1099,1100],{"class":129},"0",[105,1102,145],{"class":115},[105,1104,1105],{"class":107,"line":689},[105,1106,212],{"class":115},[105,1108,1109],{"class":107,"line":699},[105,1110,307],{"emptyLinePlaceholder":306},[105,1112,1113],{"class":107,"line":704},[105,1114,1115],{"class":630},"\u002F* 选中「不包含任何子元素的空 div」 *\u002F\n",[105,1117,1118,1120,1123,1125,1127,1129,1131],{"class":107,"line":710},[105,1119,238],{"class":237},[105,1121,1122],{"class":241},":not",[105,1124,321],{"class":115},[105,1126,993],{"class":241},[105,1128,321],{"class":115},[105,1130,938],{"class":237},[105,1132,1133],{"class":115},")) {\n",[105,1135,1137,1140,1142,1145],{"class":107,"line":1136},18,[105,1138,1139],{"class":129},"  display",[105,1141,126],{"class":115},[105,1143,1144],{"class":129},"none",[105,1146,145],{"class":115},[105,1148,1150],{"class":107,"line":1149},19,[105,1151,212],{"class":115},[18,1153,1154],{},"伪类和伪元素（常和关系选择器配合）",[835,1156,1157,1170],{},[838,1158,1159],{},[841,1160,1161,1164,1167],{},[844,1162,1163],{},"类型",[844,1165,1166],{},"写法",[844,1168,1169],{},"含义",[854,1171,1172,1184,1195,1207,1219,1231,1243,1255,1267],{},[841,1173,1174,1177,1181],{},[859,1175,1176],{},"伪类",[859,1178,1179],{},[24,1180,906],{},[859,1182,1183],{},"鼠标悬停",[841,1185,1186,1188,1192],{},[859,1187,1176],{},[859,1189,1190],{},[24,1191,1047],{},[859,1193,1194],{},"获得焦点",[841,1196,1197,1199,1204],{},[859,1198,1176],{},[859,1200,1201],{},[24,1202,1203],{},":first-child",[859,1205,1206],{},"第一个子元素",[841,1208,1209,1211,1216],{},[859,1210,1176],{},[859,1212,1213],{},[24,1214,1215],{},":last-child",[859,1217,1218],{},"最后一个子元素",[841,1220,1221,1223,1228],{},[859,1222,1176],{},[859,1224,1225],{},[24,1226,1227],{},":nth-child(n)",[859,1229,1230],{},"第 n 个子元素",[841,1232,1233,1235,1240],{},[859,1234,1176],{},[859,1236,1237],{},[24,1238,1239],{},":not(selector)",[859,1241,1242],{},"排除某个选择器",[841,1244,1245,1248,1252],{},[859,1246,1247],{},"伪元素",[859,1249,1250],{},[24,1251,923],{},[859,1253,1254],{},"元素前插入内容",[841,1256,1257,1259,1264],{},[859,1258,1247],{},[859,1260,1261],{},[24,1262,1263],{},"::after",[859,1265,1266],{},"元素后插入内容",[841,1268,1269,1271,1276],{},[859,1270,1247],{},[859,1272,1273],{},[24,1274,1275],{},"::placeholder",[859,1277,1278],{},"输入框占位符",[18,1280,1281,1282,91],{},"@namespace是CSS中一个少用，但是特定场景下需要使用的一个规则，作用是",[21,1283,1284],{},"给选择器里的元素和属性指定 XML 命名空间",[18,1286,1287,1288,1290,1291,1293],{},"在 HTML 里，元素名是全局的，",[24,1289,238],{}," 就是 ",[24,1292,238],{},"。但在 XML 或 SVG 里，不同命名空间可以有同名元素",[96,1295,1299],{"className":1296,"code":1297,"language":1298,"meta":101,"style":101},"language-xml shiki shiki-themes github-light github-dark","\u003Chtml:div>HTML 的 div\u003C\u002Fhtml:div>\n\u003Csvg:div>SVG 的 div\u003C\u002Fsvg:div>\n","xml",[24,1300,1301,1306],{"__ignoreMap":101},[105,1302,1303],{"class":107,"line":108},[105,1304,1305],{},"\u003Chtml:div>HTML 的 div\u003C\u002Fhtml:div>\n",[105,1307,1308],{"class":107,"line":119},[105,1309,1310],{},"\u003Csvg:div>SVG 的 div\u003C\u002Fsvg:div>\n",[18,1312,1313,1314,1316,1317,91,1319,1322,1323,1325,1326,1329,1330,1332],{},"如果直接用 ",[24,1315,238],{}," 选择，浏览器不知道你要选哪个命名空间下的 ",[24,1318,238],{},[24,1320,1321],{},"@namespace"," 就是用来区分它们的。",[24,1324,1321],{}," 必须写在所有样式规则之前（除了 ",[24,1327,1328],{},"@charset"," 和 ",[24,1331,312],{},"）。",[96,1334,1337],{"className":1335,"code":1336,"language":392},[390],"\u002F* 给某个命名空间起别名 *\u002F\n@namespace svg url(http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg);\n\n\u002F* 默认命名空间，没有前缀的选择器都属于它 *\u002F\n@namespace url(http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml);\n\n\u002F* 之后用 `svg|` 前缀来指定该命名空间 *\u002F\nsvg|circle {\n  fill: red;\n}\n",[24,1338,1336],{"__ignoreMap":101},[18,1340,1341,1342,1345,1346,1329,1349,1352,1353,1356,1357,1360,1361,1363,1364,1366],{},"HTML5 允许在 ",[24,1343,1344],{},"\u003Chtml>"," 里直接写 ",[24,1347,1348],{},"\u003Csvg>",[24,1350,1351],{},"\u003Cmath>","，浏览器会自动给它们分配 SVG 和 MathML 命名空间，这里",[21,1354,1355],{},"不需要","写 ",[24,1358,1359],{},"xmlns","，浏览器知道 ",[24,1362,1348],{}," 是 SVG 命名空间。但在独立 SVG 文件里，",[24,1365,1359],{}," 是必须的。",[96,1368,1371],{"className":1369,"code":1370,"language":392},[390],"\u003Csvg width=\"100\" height=\"100\">\n  \u003Ccircle cx=\"50\" cy=\"50\" r=\"40\" fill=\"red\" \u002F>\n\u003C\u002Fsvg>\n",[24,1372,1370],{"__ignoreMap":101},[18,1374,1375],{},"这里需要了解一个机制，html其实是有命名空间的，HTML5 和 XML 是两套不同的解析模型，只是语法上有相似之处，HTML5 文档里，元素可以属于三个命名空间：",[835,1377,1378,1391],{},[838,1379,1380],{},[841,1381,1382,1385,1388],{},[844,1383,1384],{},"命名空间 URI",[844,1386,1387],{},"前缀",[844,1389,1390],{},"用在哪些元素",[854,1392,1393,1413,1430,1446],{},[841,1394,1395,1400,1403],{},[859,1396,1397],{},[24,1398,1399],{},"http:\u002F\u002Fwww.w3.org\u002F1999\u002Fxhtml",[859,1401,1402],{},"无（默认）",[859,1404,1405,1406,31,1408,31,1410,1412],{},"所有 HTML 元素（",[24,1407,238],{},[24,1409,18],{},[24,1411,105],{},"...）",[841,1414,1415,1420,1425],{},[859,1416,1417],{},[24,1418,1419],{},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[859,1421,1422],{},[24,1423,1424],{},"svg:",[859,1426,1427,1429],{},[24,1428,1348],{}," 及其子元素",[841,1431,1432,1437,1442],{},[859,1433,1434],{},[24,1435,1436],{},"http:\u002F\u002Fwww.w3.org\u002F1998\u002FMath\u002FMathML",[859,1438,1439],{},[24,1440,1441],{},"math:",[859,1443,1444,1429],{},[24,1445,1351],{},[841,1447,1448,1450,1452],{},[859,1449],{},[859,1451],{},[859,1453],{},[18,1455,1456,1459,1460,1462,1463,1466,1467,1470],{},[21,1457,1458],{},"HTML 解析器是“预定义”的。"," 规范里明确写了：当解析器遇到 ",[24,1461,1348],{}," 标签时，",[21,1464,1465],{},"自动切换到 SVG 命名空间","，直到遇到 ",[24,1468,1469],{},"\u003C\u002Fsvg>"," 闭合。",[18,1472,1473,1474,31,1477,1480],{},"在DOM中，属性分为两个类型，一个叫做无命名空间属性，一个叫做有命名空间属性。 前者是 属性名就是普通字符串，没有命名空间，",[24,1475,1476],{},"checked",[24,1478,1479],{},"disabled","。 后者属于有命名空间的属性，属性名带前缀，属于某个命名空间，xml:lang，xlink:href。",[18,1482,1483],{},"你可以在devtools中验证：",[96,1485,1487],{"className":559,"code":1486,"language":561,"meta":101,"style":101},"const input = document.querySelector('input')\ninput.checked = true\n\n\u002F\u002F 查看属性节点\nconst attr = input.getAttributeNode('checked')\nconsole.log(attr.namespaceURI)  \u002F\u002F null\nconsole.log(attr.name)          \u002F\u002F \"checked\"\nconsole.log(attr.localName)     \u002F\u002F \"checked\"\nconsole.log(attr.prefix)        \u002F\u002F null\n",[24,1488,1489,1512,1522,1526,1531,1553,1565,1577,1588],{"__ignoreMap":101},[105,1490,1491,1493,1496,1498,1501,1504,1506,1509],{"class":107,"line":108},[105,1492,568],{"class":111},[105,1494,1495],{"class":129}," input",[105,1497,574],{"class":111},[105,1499,1500],{"class":115}," document.",[105,1502,1503],{"class":241},"querySelector",[105,1505,321],{"class":115},[105,1507,1508],{"class":248},"'input'",[105,1510,1511],{"class":115},")\n",[105,1513,1514,1517,1519],{"class":107,"line":119},[105,1515,1516],{"class":115},"input.checked ",[105,1518,245],{"class":111},[105,1520,1521],{"class":129}," true\n",[105,1523,1524],{"class":107,"line":133},[105,1525,307],{"emptyLinePlaceholder":306},[105,1527,1528],{"class":107,"line":148},[105,1529,1530],{"class":630},"\u002F\u002F 查看属性节点\n",[105,1532,1533,1535,1538,1540,1543,1546,1548,1551],{"class":107,"line":163},[105,1534,568],{"class":111},[105,1536,1537],{"class":129}," attr",[105,1539,574],{"class":111},[105,1541,1542],{"class":115}," input.",[105,1544,1545],{"class":241},"getAttributeNode",[105,1547,321],{"class":115},[105,1549,1550],{"class":248},"'checked'",[105,1552,1511],{"class":115},[105,1554,1555,1557,1559,1562],{"class":107,"line":179},[105,1556,636],{"class":115},[105,1558,639],{"class":241},[105,1560,1561],{"class":115},"(attr.namespaceURI)  ",[105,1563,1564],{"class":630},"\u002F\u002F null\n",[105,1566,1567,1569,1571,1574],{"class":107,"line":194},[105,1568,636],{"class":115},[105,1570,639],{"class":241},[105,1572,1573],{"class":115},"(attr.name)          ",[105,1575,1576],{"class":630},"\u002F\u002F \"checked\"\n",[105,1578,1579,1581,1583,1586],{"class":107,"line":209},[105,1580,636],{"class":115},[105,1582,639],{"class":241},[105,1584,1585],{"class":115},"(attr.localName)     ",[105,1587,1576],{"class":630},[105,1589,1590,1592,1594,1597],{"class":107,"line":526},[105,1591,636],{"class":115},[105,1593,639],{"class":241},[105,1595,1596],{"class":115},"(attr.prefix)        ",[105,1598,1564],{"class":630},[18,1600,1601],{},"那么真正没有默认命名空间，需要xmlns的文档是哪些文件，就比如说Avalonia的axaml。",[96,1603,1606],{"className":1604,"code":1605,"language":392},[390],"\u003CWindow xmlns=\"https:\u002F\u002Fgithub.com\u002Favaloniaui\"\n        xmlns:x=\"http:\u002F\u002Fschemas.microsoft.com\u002Fwinfx\u002F2006\u002Fxaml\">\n",[24,1607,1605],{"__ignoreMap":101},[18,1609,1610,1611,1614],{},"CSS自定义属性： 属性名以 ",[24,1612,1613],{},"--"," 开头，值可以是任意 CSS 值。",[96,1616,1618],{"className":98,"code":1617,"language":100,"meta":101,"style":101},"\u003Cdiv class=\"item\" style=\"--i: 0\">\u003C\u002Fdiv>\n\u003Cdiv class=\"item\" style=\"--i: 1\">\u003C\u002Fdiv>\n\u003Cdiv class=\"item\" style=\"--i: 2\">\u003C\u002Fdiv>\n:root {\n  --primary-color: #3b82f6;\n  --spacing: 16px;\n  --i: 1;\n}\n",[24,1619,1620,1631,1640,1649,1656,1668,1682,1693],{"__ignoreMap":101},[105,1621,1622,1625,1628],{"class":107,"line":108},[105,1623,1624],{"class":115},"\u003Cdiv class=\"item\" style=\"--i: 0\"",[105,1626,1627],{"class":111},">",[105,1629,1630],{"class":115},"\u003C\u002Fdiv>\n",[105,1632,1633,1636,1638],{"class":107,"line":119},[105,1634,1635],{"class":115},"\u003Cdiv class=\"item\" style=\"--i: 1\"",[105,1637,1627],{"class":111},[105,1639,1630],{"class":115},[105,1641,1642,1645,1647],{"class":107,"line":133},[105,1643,1644],{"class":115},"\u003Cdiv class=\"item\" style=\"--i: 2\"",[105,1646,1627],{"class":111},[105,1648,1630],{"class":115},[105,1650,1651,1654],{"class":107,"line":148},[105,1652,1653],{"class":241},":root",[105,1655,116],{"class":115},[105,1657,1658,1661,1663,1666],{"class":107,"line":163},[105,1659,1660],{"class":122},"  --primary-color",[105,1662,126],{"class":115},[105,1664,1665],{"class":129},"#3b82f6",[105,1667,145],{"class":115},[105,1669,1670,1673,1675,1678,1680],{"class":107,"line":179},[105,1671,1672],{"class":122},"  --spacing",[105,1674,126],{"class":115},[105,1676,1677],{"class":129},"16",[105,1679,142],{"class":111},[105,1681,145],{"class":115},[105,1683,1684,1687,1689,1691],{"class":107,"line":194},[105,1685,1686],{"class":122},"  --i",[105,1688,126],{"class":115},[105,1690,861],{"class":129},[105,1692,145],{"class":115},[105,1694,1695],{"class":107,"line":209},[105,1696,212],{"class":115},[18,1698,1699,1700,1703],{},"用 ",[24,1701,1702],{},"var()"," 函数。",[96,1705,1707],{"className":98,"code":1706,"language":100,"meta":101,"style":101},"\u003Cdiv class=\"item\" style=\"--i: 0\">\u003C\u002Fdiv>\n\u003Cdiv class=\"item\" style=\"--i: 1\">\u003C\u002Fdiv>\n\u003Cdiv class=\"item\" style=\"--i: 2\">\u003C\u002Fdiv>\n.button {\n  color: var(--primary-color);\n  padding: var(--spacing);\n  z-index: var(--i);\n}\n",[24,1708,1709,1717,1725,1733,1740,1757,1773,1789],{"__ignoreMap":101},[105,1710,1711,1713,1715],{"class":107,"line":108},[105,1712,1624],{"class":115},[105,1714,1627],{"class":111},[105,1716,1630],{"class":115},[105,1718,1719,1721,1723],{"class":107,"line":119},[105,1720,1635],{"class":115},[105,1722,1627],{"class":111},[105,1724,1630],{"class":115},[105,1726,1727,1729,1731],{"class":107,"line":133},[105,1728,1644],{"class":115},[105,1730,1627],{"class":111},[105,1732,1630],{"class":115},[105,1734,1735,1738],{"class":107,"line":148},[105,1736,1737],{"class":241},".button",[105,1739,116],{"class":115},[105,1741,1742,1745,1747,1750,1752,1755],{"class":107,"line":163},[105,1743,1744],{"class":129},"  color",[105,1746,126],{"class":115},[105,1748,1749],{"class":129},"var",[105,1751,321],{"class":115},[105,1753,1754],{"class":122},"--primary-color",[105,1756,327],{"class":115},[105,1758,1759,1762,1764,1766,1768,1771],{"class":107,"line":179},[105,1760,1761],{"class":129},"  padding",[105,1763,126],{"class":115},[105,1765,1749],{"class":129},[105,1767,321],{"class":115},[105,1769,1770],{"class":122},"--spacing",[105,1772,327],{"class":115},[105,1774,1775,1778,1780,1782,1784,1787],{"class":107,"line":194},[105,1776,1777],{"class":129},"  z-index",[105,1779,126],{"class":115},[105,1781,1749],{"class":129},[105,1783,321],{"class":115},[105,1785,1786],{"class":122},"--i",[105,1788,327],{"class":115},[105,1790,1791],{"class":107,"line":209},[105,1792,212],{"class":115},[18,1794,1795],{},"css变量最大优势是运行时可变，可以让主题切换、动态计算、响应式调整变得更加容易：",[96,1797,1799],{"className":559,"code":1798,"language":561,"meta":101,"style":101},"\u002F\u002F 读\nconst value = getComputedStyle(element).getPropertyValue('--i')\n\n\u002F\u002F 写\nelement.style.setProperty('--i', 3)\n",[24,1800,1801,1806,1831,1835,1840],{"__ignoreMap":101},[105,1802,1803],{"class":107,"line":108},[105,1804,1805],{"class":630},"\u002F\u002F 读\n",[105,1807,1808,1810,1813,1815,1818,1821,1824,1826,1829],{"class":107,"line":119},[105,1809,568],{"class":111},[105,1811,1812],{"class":129}," value",[105,1814,574],{"class":111},[105,1816,1817],{"class":241}," getComputedStyle",[105,1819,1820],{"class":115},"(element).",[105,1822,1823],{"class":241},"getPropertyValue",[105,1825,321],{"class":115},[105,1827,1828],{"class":248},"'--i'",[105,1830,1511],{"class":115},[105,1832,1833],{"class":107,"line":133},[105,1834,307],{"emptyLinePlaceholder":306},[105,1836,1837],{"class":107,"line":148},[105,1838,1839],{"class":630},"\u002F\u002F 写\n",[105,1841,1842,1845,1848,1850,1852,1854,1856],{"class":107,"line":163},[105,1843,1844],{"class":115},"element.style.",[105,1846,1847],{"class":241},"setProperty",[105,1849,321],{"class":115},[105,1851,1828],{"class":248},[105,1853,286],{"class":115},[105,1855,891],{"class":129},[105,1857,1511],{"class":115},[18,1859,1860],{},"css的其他重要知识点：",[18,1862,1863,1864,1867,1868,1867,1871,1867,1874,1877,1878,1881,1882,1885],{},"z-index 不是全局的，是在一个层叠上下文里比较，层叠上下文是",[24,1865,1866],{},"relative"," \u002F ",[24,1869,1870],{},"absolute",[24,1872,1873],{},"fixed",[24,1875,1876],{},"sticky","，且 ",[24,1879,1880],{},"z-index"," 不是 ",[24,1883,1884],{},"auto","，父元素有transform，子元素的z-index: 9999 压不过父元素外面元素，子元素在父元素的层叠上下文里。",[18,1887,1888],{},"我们可以通过设置position + z-index来创建一个层叠上下文，比如说position: relative + z-index:1 ，或者你可以直接设置position: fixed\u002Fsticky也是不需要z-index的。或者opacity \u003C 1 ；trnsform != none 比如transform: translateX(0)；Isolation: isolate （专门创建层叠上下文)。",[1890,1891,1893],"h4",{"id":1892},"bfc","BFC",[18,1895,1896],{},"bfc全称块级格式化上下文，是一个独立的渲染区域，内部布局不影响外部，创建bfc的方法有：",[1898,1899,1900,1903,1906,1909,1912],"ol",{},[448,1901,1902],{},"overflow 不是 Visible",[448,1904,1905],{},"display: flow-root",[448,1907,1908],{},"position: absolute\u002Ffixed",[448,1910,1911],{},"float不是none",[448,1913,1914],{},"display: inline-block \u002Fflex\u002F grid",[18,1916,1917,1918,1922,1923,1926],{},"能够解决： 清除浮动，父元素创建bfc，保住浮动子元素。防止margin塌陷，两个相邻的bfc的margin不会合并。阻止元素被浮动元素覆盖。",[21,1919,1920],{},[24,1921,1905],{},"，它是专门为“创建 BFC 且不影响其他布局”设计的，比 ",[24,1924,1925],{},"overflow: hidden"," 干净。",[18,1928,1929],{},"margin塌陷，垂直方向两个margin会合并，取比较大那个：",[96,1931,1933],{"className":98,"code":1932,"language":100,"meta":101,"style":101},".a { margin-bottom: 20px; }\n.b { margin-top: 30px; }\n",[24,1934,1935,1955],{"__ignoreMap":101},[105,1936,1937,1940,1942,1945,1947,1950,1952],{"class":107,"line":108},[105,1938,1939],{"class":241},".a",[105,1941,726],{"class":115},[105,1943,1944],{"class":129},"margin-bottom",[105,1946,126],{"class":115},[105,1948,1949],{"class":129},"20",[105,1951,142],{"class":111},[105,1953,1954],{"class":115},"; }\n",[105,1956,1957,1960,1962,1965,1967,1970,1972],{"class":107,"line":119},[105,1958,1959],{"class":241},".b",[105,1961,726],{"class":115},[105,1963,1964],{"class":129},"margin-top",[105,1966,126],{"class":115},[105,1968,1969],{"class":129},"30",[105,1971,142],{"class":111},[105,1973,1954],{"class":115},[18,1975,1976,1977,1980,1981,1984],{},"父子塌陷，父元素没有padding、border、overflow等元素情况下，子元素的margin-top会溢出到父元素外面。可以让父元素加 ",[24,1978,1979],{},"padding-top"," 或 ",[24,1982,1983],{},"border-top","或者父元素创建bfc，或者在flex\u002Fgrid容器里面用gap来替代margin。",[18,1986,1987],{},"Flex和Grid是两种不同的布局思想，flex是内容驱动，子元素决定怎么排列，grid是容器定义网格，子元素填入。前者适合的是导航、工具栏、卡片列表，后者适合页面整体布局 复杂网格。CSS的正常布局模式中包含以下几种：\nblock布局，叫做块级，inline布局 行内布局， inline-block、Flex布局、Grid布局、定位布局(position)、浮动布局(float)、表格布局(table)\nflex grid会改变元素的外部显示类型和内部格式上下文，我们称元素对外表现叫block还是inline，对内部子元素按什么规则排列。\n比如你想让元素对内部flex，对外部block，可以这么写:",[96,1989,1991],{"className":98,"code":1990,"language":100,"meta":101,"style":101},"display: block flex; %%对外block 对内flex%%\ndisplay: inline-flex %%对外inline，对内flex%%\n",[24,1992,1993,1998],{"__ignoreMap":101},[105,1994,1995],{"class":107,"line":108},[105,1996,1997],{"class":115},"display: block flex; %%对外block 对内flex%%\n",[105,1999,2000],{"class":107,"line":119},[105,2001,2002],{"class":115},"display: inline-flex %%对外inline，对内flex%%\n",[18,2004,2005],{},"这里真的有坑，display: flex 是 display: block flex的简写，表示对外block，对内flex。display: block才是display: block flow的简写，表示对外block，对内flow，子元素会受到float的影响，会发生margin塌陷。",[18,2007,2008],{},"ok 我们来谈谈塌陷问题，display: block表示内部格式化上下文为BFC 块级格式化上下文，display: flex 对内flex，会发生FFC flex格式化上下文。",[18,2010,2011],{},"那么bfc为啥要发生，因为它规则之一是相邻块级元素垂直margin会合并，这个上面讲过，这就是规范行为，目的是让段落之间差距更加自然，ffc呢，flex容器里子元素不是块级元素，而是flex item，这样会让item的margin不会和其他item合并，margin差就是它们的和。",[18,2013,2014],{},"grid也是为ui布局设计的，margin语义是明确间距，因此grid item也不会塌陷。如果你想在bfc里避免margin塌陷，就给父元素加padding、border，或者让父元素创建bfc，display: flow-root、用gap替代，其中gap替代是最推荐的，gap是flex、grid原生属性，不受margin塌陷影响。",[18,2016,2017],{},"最后我们再把inline单独拿出来说，inline对应的是ifc 行内格式化上下文，和bfc并列。inline元素会像文字一样排列在一行里，从左到右排列，一行放不下就换行，并且不能设置width height，宽高由内容决定，垂直margin不生效，水平margin是生效的，只是无法撑开行高。每一行文字，浏览器都会创建一个叫做linebox行盒的东西，行盒的高度由这一行最高的元素决定，行盒的垂直对齐vertical-align只有在ifc中有效，在flex、grid中无效，用align-item替代。",[18,2019,2020],{},"常见的inline元素有span 、a、strong、em、img、button、input。",[18,2022,2023],{},"inline-block是折中的方式，表示对外inline，对内flow-root，能设置inline不能设置的宽高。ifc会合并连续空格、换行。",[96,2025,2028],{"className":2026,"code":2027,"language":392},[390],"\u003Cspan>a\u003C\u002Fspan>\n\u003Cspan>b\u003C\u002Fspan>\n",[24,2029,2027],{"__ignoreMap":101},[18,2031,2032],{},"这样的渲染出来是a b， 中间只有一个空格，因为html源码里的换行被当成一个空格，如果不想要有空隙，可以压缩到一行去，或者用flex布局，flex不处理whitespace，相对来说，flex默认不换行，如果需要换行，就要设置flex-wrap，flex-wrap: wrap。",[18,2034,2035],{},"然后我们来讲脱离文档流的程度，float是半脱离，absolute是完全脱离，float是原来位置不保留，后面的元素会上移，absolute也是，后面的元素会上移。但是float会影响周围文字，比如说文字环绕，但是absolute是不影响的，文字不会环绕，float不积压周围块级元素，但是会被其他块级元素无视，absolute则是完全独立，不积压。所以说float是保留空间影响，absolute是完全去掉影响，完全脱离文档流。",[18,2037,2038,2039,2045],{},"最后，我们以行\u002F列轨道概念结尾，",[21,2040,2041,2044],{},[24,2042,2043],{},"align-content"," 操作的是“行\u002F列轨道”，不是单个元素。"," 在Flex和Grid中，有两个层级，分别是轨道和项目，英文叫做track和item，比如说flex默认一行轨道，当flex-wrap: wrap时候有多行。",[18,2047,2048],{},"flex里有四个对齐属性，一个叫做justify-content，用于所有item整体，作用方向是主轴；一个叫做align-item，用于处理单个item，方向是交叉轴；align-content作用于track，作用方向是交叉轨道；align-self作用于单个item覆盖align-items，方向是交叉轴。",[18,2050,2051],{},"grid中，justify-content处理整个网格，为行轴；align-content处理整个网格，方向为列轴；justify-items作用于每个item在单元格内，行轴；align-items处理每个item在单元格内，方向列轴。",[1890,2053,2054],{"id":2054},"单位",[835,2056,2057,2069],{},[838,2058,2059],{},[841,2060,2061,2063,2066],{},[844,2062,2054],{},[844,2064,2065],{},"参照物",[844,2067,2068],{},"适用",[854,2070,2071,2083,2099,2114,2127,2143,2159,2175],{},[841,2072,2073,2077,2080],{},[859,2074,2075],{},[24,2076,142],{},[859,2078,2079],{},"绝对像素",[859,2081,2082],{},"边框、小间距",[841,2084,2085,2090,2096],{},[859,2086,2087],{},[24,2088,2089],{},"rem",[859,2091,2092,2093],{},"根元素 ",[24,2094,2095],{},"font-size",[859,2097,2098],{},"全局缩放、字号",[841,2100,2101,2106,2111],{},[859,2102,2103],{},[24,2104,2105],{},"em",[859,2107,2108,2109],{},"父元素 ",[24,2110,2095],{},[859,2112,2113],{},"局部相对缩放",[841,2115,2116,2121,2124],{},[859,2117,2118],{},[24,2119,2120],{},"%",[859,2122,2123],{},"父元素对应属性",[859,2125,2126],{},"宽度、高度",[841,2128,2129,2137,2140],{},[859,2130,2131,1867,2134],{},[24,2132,2133],{},"vw",[24,2135,2136],{},"vh",[859,2138,2139],{},"视口宽\u002F高",[859,2141,2142],{},"全屏布局",[841,2144,2145,2153,2156],{},[859,2146,2147,1867,2150],{},[24,2148,2149],{},"vmin",[24,2151,2152],{},"vmax",[859,2154,2155],{},"视口较小\u002F较大边",[859,2157,2158],{},"正方形自适应",[841,2160,2161,2166,2172],{},[859,2162,2163],{},[24,2164,2165],{},"ch",[859,2167,2168,2169,2171],{},"字符 ",[24,2170,1100],{}," 的宽度",[859,2173,2174],{},"输入框宽度",[841,2176,2177,2182,2185],{},[859,2178,2179],{},[24,2180,2181],{},"fr",[859,2183,2184],{},"Grid 剩余空间",[859,2186,2187],{},"Grid 布局",[1890,2189,1247],{"id":1247},[835,2191,2192,2202],{},[838,2193,2194],{},[841,2195,2196,2198,2200],{},[844,2197],{},[844,2199,1176],{},[844,2201,1247],{},[854,2203,2204,2219,2230,2251],{},[841,2205,2206,2209,2214],{},[859,2207,2208],{},"语法",[859,2210,2211,2212],{},"单冒号 ",[24,2213,906],{},[859,2215,2216,2217],{},"双冒号 ",[24,2218,923],{},[841,2220,2221,2224,2227],{},[859,2222,2223],{},"作用",[859,2225,2226],{},"描述元素的状态",[859,2228,2229],{},"创建虚拟元素",[841,2231,2232,2234,2243],{},[859,2233,852],{},[859,2235,2236,31,2238,31,2240],{},[24,2237,906],{},[24,2239,1047],{},[24,2241,2242],{},":nth-child",[859,2244,2245,31,2247,31,2249],{},[24,2246,923],{},[24,2248,1263],{},[24,2250,1275],{},[841,2252,2253,2256,2259],{},[859,2254,2255],{},"是否在 DOM 里",[859,2257,2258],{},"不创建新元素",[859,2260,2229],{},[18,2262,2263,2266,2267,1329,2270,2272],{},[21,2264,2265],{},"面试问","：“",[24,2268,2269],{},":before",[24,2271,923],{}," 有什么区别？”答“单冒号是 CSS2 语法，双冒号是 CSS3 语法，现代写法用双冒号”。",[1890,2274,2276,2279,2280,2279,2283],{"id":2275},"display-none-vs-visibility-hidden-vs-opacity-0",[24,2277,2278],{},"display: none"," vs ",[24,2281,2282],{},"visibility: hidden",[24,2284,2285],{},"opacity: 0",[835,2287,2288,2306],{},[838,2289,2290],{},[841,2291,2292,2294,2297,2300,2303],{},[844,2293],{},[844,2295,2296],{},"占据空间",[844,2298,2299],{},"可交互",[844,2301,2302],{},"触发重排",[844,2304,2305],{},"触发重绘",[854,2307,2308,2324,2338],{},[841,2309,2310,2314,2317,2319,2322],{},[859,2311,2312],{},[24,2313,2278],{},[859,2315,2316],{},"❌",[859,2318,2316],{},[859,2320,2321],{},"✅",[859,2323,2321],{},[841,2325,2326,2330,2332,2334,2336],{},[859,2327,2328],{},[24,2329,2282],{},[859,2331,2321],{},[859,2333,2316],{},[859,2335,2316],{},[859,2337,2321],{},[841,2339,2340,2344,2346,2348,2350],{},[859,2341,2342],{},[24,2343,2285],{},[859,2345,2321],{},[859,2347,2321],{},[859,2349,2316],{},[859,2351,2321],{},[18,2353,2354,2356,2357,2359,2360,2362,2363,2365],{},[21,2355,2265],{},"：“三者区别？”答“",[24,2358,2278],{}," 完全移除，",[24,2361,2282],{}," 保留空间但不可交互，",[24,2364,2285],{}," 保留空间且可交互”。",[1890,2367,2368],{"id":2368},"重排和重绘",[835,2370,2371,2383],{},[838,2372,2373],{},[841,2374,2375,2377,2380],{},[844,2376],{},[844,2378,2379],{},"重排（Reflow）",[844,2381,2382],{},"重绘（Repaint）",[854,2384,2385,2396,2407,2443],{},[841,2386,2387,2390,2393],{},[859,2388,2389],{},"触发",[859,2391,2392],{},"几何属性变化",[859,2394,2395],{},"颜色、背景变化",[841,2397,2398,2401,2404],{},[859,2399,2400],{},"代价",[859,2402,2403],{},"高",[859,2405,2406],{},"低",[841,2408,2409,2412,2432],{},[859,2410,2411],{},"属性",[859,2413,2414,31,2417,31,2420,31,2423,31,2426,31,2429],{},[24,2415,2416],{},"width",[24,2418,2419],{},"height",[24,2421,2422],{},"margin",[24,2424,2425],{},"padding",[24,2427,2428],{},"display",[24,2430,2431],{},"position",[859,2433,2434,31,2437,31,2440],{},[24,2435,2436],{},"color",[24,2438,2439],{},"background-color",[24,2441,2442],{},"visibility",[841,2444,2445,2448,2457],{},[859,2446,2447],{},"优化",[859,2449,1699,2450,31,2453,2456],{},[24,2451,2452],{},"transform",[24,2454,2455],{},"opacity","（只触发合成）",[859,2458,2459],{},"—",[18,2461,2462,2464,2465,1867,2467,2469,2470,2473],{},[21,2463,2265],{},"：“怎么优化动画？”答“用 ",[24,2466,2452],{},[24,2468,2455],{},"，加 ",[24,2471,2472],{},"will-change","，避免触发重排”。",[1890,2475,2476],{"id":2476},"滚动相关",[835,2478,2479,2487],{},[838,2480,2481],{},[841,2482,2483,2485],{},[844,2484,2411],{},[844,2486,2223],{},[854,2488,2489,2499,2509,2519,2529,2539,2549],{},[841,2490,2491,2496],{},[859,2492,2493],{},[24,2494,2495],{},"overflow",[859,2497,2498],{},"控制溢出",[841,2500,2501,2506],{},[859,2502,2503],{},[24,2504,2505],{},"overscroll-behavior",[859,2507,2508],{},"控制滚动边界（防止滚动穿透）",[841,2510,2511,2516],{},[859,2512,2513],{},[24,2514,2515],{},"scroll-snap-type",[859,2517,2518],{},"滚动吸附",[841,2520,2521,2526],{},[859,2522,2523],{},[24,2524,2525],{},"scroll-behavior: smooth",[859,2527,2528],{},"平滑滚动",[841,2530,2531,2536],{},[859,2532,2533],{},[24,2534,2535],{},"position: sticky",[859,2537,2538],{},"粘性定位",[841,2540,2541,2546],{},[859,2542,2543],{},[24,2544,2545],{},"scrollbar-gutter",[859,2547,2548],{},"预留滚动条空间，避免布局抖动",[841,2550,2551,2563],{},[859,2552,2553,2555,2556,2558,2559,2562],{},[21,2554,2265],{},"：“怎么做滚动吸附？”答“",[24,2557,2515],{}," + ",[24,2560,2561],{},"scroll-snap-align","”。没有滚动吸附时，用户滑动轮播图，可能停在两张图中间，一半露一张、一半露另一张，视觉上很别扭。",[859,2564],{},[18,2566,2567],{},"怎么处理移动端上，1px变成2px的问题，根源是因为设备像素比DPR，高DPR屏幕上，1CSS像素可能对应着2、3个甚至更多的物理像素，导致1px的边框在物理上渲染成了2px或者3px宽。问题根源：",[96,2569,2571],{"className":98,"code":2570,"language":100,"meta":101,"style":101},"border: 1px solid #ccc;\n",[24,2572,2573],{"__ignoreMap":101},[105,2574,2575],{"class":107,"line":108},[105,2576,2570],{"class":115},[18,2578,2579,2580,2583],{},"DPR = 1 : 1个CSS像素=1个物理像素\n",[21,2581,2582],{},"DPR = 2","：1 个 CSS 像素 = 2×2 个物理像素，1px 边框在物理上占 2 个像素\n这个时候可以试试伪元素绘制边框，再缩放：",[96,2585,2587],{"className":98,"code":2586,"language":100,"meta":101,"style":101},".border-1px {\n  position: relative;\n}\n\n.border-1px::after {\n  content: '';\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 200%;\n  height: 200%;\n  border: 1px solid #ccc;\n  transform: scale(0.5);\n  transform-origin: 0 0;\n  pointer-events: none;\n}\n",[24,2588,2589,2596,2607,2611,2615,2622,2634,2644,2655,2666,2680,2693,2709,2726,2740,2751],{"__ignoreMap":101},[105,2590,2591,2594],{"class":107,"line":108},[105,2592,2593],{"class":241},".border-1px",[105,2595,116],{"class":115},[105,2597,2598,2601,2603,2605],{"class":107,"line":119},[105,2599,2600],{"class":129},"  position",[105,2602,126],{"class":115},[105,2604,1866],{"class":129},[105,2606,145],{"class":115},[105,2608,2609],{"class":107,"line":133},[105,2610,212],{"class":115},[105,2612,2613],{"class":107,"line":148},[105,2614,307],{"emptyLinePlaceholder":306},[105,2616,2617,2620],{"class":107,"line":163},[105,2618,2619],{"class":241},".border-1px::after",[105,2621,116],{"class":115},[105,2623,2624,2627,2629,2632],{"class":107,"line":179},[105,2625,2626],{"class":129},"  content",[105,2628,126],{"class":115},[105,2630,2631],{"class":248},"''",[105,2633,145],{"class":115},[105,2635,2636,2638,2640,2642],{"class":107,"line":194},[105,2637,2600],{"class":129},[105,2639,126],{"class":115},[105,2641,1870],{"class":129},[105,2643,145],{"class":115},[105,2645,2646,2649,2651,2653],{"class":107,"line":209},[105,2647,2648],{"class":129},"  top",[105,2650,126],{"class":115},[105,2652,1100],{"class":129},[105,2654,145],{"class":115},[105,2656,2657,2660,2662,2664],{"class":107,"line":526},[105,2658,2659],{"class":129},"  left",[105,2661,126],{"class":115},[105,2663,1100],{"class":129},[105,2665,145],{"class":115},[105,2667,2668,2671,2673,2676,2678],{"class":107,"line":531},[105,2669,2670],{"class":129},"  width",[105,2672,126],{"class":115},[105,2674,2675],{"class":129},"200",[105,2677,2120],{"class":111},[105,2679,145],{"class":115},[105,2681,2682,2685,2687,2689,2691],{"class":107,"line":668},[105,2683,2684],{"class":129},"  height",[105,2686,126],{"class":115},[105,2688,2675],{"class":129},[105,2690,2120],{"class":111},[105,2692,145],{"class":115},[105,2694,2695,2697,2699,2701,2703,2705,2707],{"class":107,"line":673},[105,2696,1005],{"class":129},[105,2698,126],{"class":115},[105,2700,861],{"class":129},[105,2702,142],{"class":111},[105,2704,1014],{"class":129},[105,2706,1017],{"class":129},[105,2708,145],{"class":115},[105,2710,2711,2714,2716,2719,2721,2724],{"class":107,"line":679},[105,2712,2713],{"class":129},"  transform",[105,2715,126],{"class":115},[105,2717,2718],{"class":129},"scale",[105,2720,321],{"class":115},[105,2722,2723],{"class":129},"0.5",[105,2725,327],{"class":115},[105,2727,2728,2731,2733,2735,2738],{"class":107,"line":689},[105,2729,2730],{"class":129},"  transform-origin",[105,2732,126],{"class":115},[105,2734,1100],{"class":129},[105,2736,2737],{"class":129}," 0",[105,2739,145],{"class":115},[105,2741,2742,2745,2747,2749],{"class":107,"line":699},[105,2743,2744],{"class":129},"  pointer-events",[105,2746,126],{"class":115},[105,2748,1144],{"class":129},[105,2750,145],{"class":115},[105,2752,2753],{"class":107,"line":704},[105,2754,212],{"class":115},[10,2756,2758],{"id":2757},"pinia","Pinia",[18,2760,2761],{},"pinia的核心原理是使用vue的核心响应式系统包装一个状态对象，用闭包管理store实例，这个和composable的思想是一致的，用插件机制扩展能力。",[18,2763,2764,2767,2768,2774],{},[24,2765,2766],{},"defineStore(id, options)"," 返回一个 ",[21,2769,2770,2773],{},[24,2771,2772],{},"useStore"," 函数","，不是 store 本身。",[96,2776,2778],{"className":559,"code":2777,"language":561,"meta":101,"style":101},"const useCounter = defineStore('counter', {\n  state: () => ({ count: 0 }),\n  getters: {\n    double: (state) => state.count * 2,\n  },\n  actions: {\n    increment() { this.count++ },\n  },\n})\n",[24,2779,2780,2800,2818,2823,2850,2855,2860,2880,2884],{"__ignoreMap":101},[105,2781,2782,2784,2787,2789,2792,2794,2797],{"class":107,"line":108},[105,2783,568],{"class":111},[105,2785,2786],{"class":129}," useCounter",[105,2788,574],{"class":111},[105,2790,2791],{"class":241}," defineStore",[105,2793,321],{"class":115},[105,2795,2796],{"class":248},"'counter'",[105,2798,2799],{"class":115},", {\n",[105,2801,2802,2805,2808,2810,2813,2815],{"class":107,"line":119},[105,2803,2804],{"class":241},"  state",[105,2806,2807],{"class":115},": () ",[105,2809,723],{"class":111},[105,2811,2812],{"class":115}," ({ count: ",[105,2814,1100],{"class":129},[105,2816,2817],{"class":115}," }),\n",[105,2819,2820],{"class":107,"line":133},[105,2821,2822],{"class":115},"  getters: {\n",[105,2824,2825,2828,2831,2834,2837,2839,2842,2844,2847],{"class":107,"line":148},[105,2826,2827],{"class":241},"    double",[105,2829,2830],{"class":115},": (",[105,2832,2833],{"class":122},"state",[105,2835,2836],{"class":115},") ",[105,2838,723],{"class":111},[105,2840,2841],{"class":115}," state.count ",[105,2843,938],{"class":111},[105,2845,2846],{"class":129}," 2",[105,2848,2849],{"class":115},",\n",[105,2851,2852],{"class":107,"line":163},[105,2853,2854],{"class":115},"  },\n",[105,2856,2857],{"class":107,"line":179},[105,2858,2859],{"class":115},"  actions: {\n",[105,2861,2862,2865,2868,2871,2874,2877],{"class":107,"line":194},[105,2863,2864],{"class":241},"    increment",[105,2866,2867],{"class":115},"() { ",[105,2869,2870],{"class":129},"this",[105,2872,2873],{"class":115},".count",[105,2875,2876],{"class":111},"++",[105,2878,2879],{"class":115}," },\n",[105,2881,2882],{"class":107,"line":209},[105,2883,2854],{"class":115},[105,2885,2886],{"class":107,"line":526},[105,2887,2888],{"class":115},"})\n",[18,2890,2891,2894,2895,2898],{},[24,2892,2893],{},"useStore()"," 内部做的事，会检查pinia实例上有没有这个id的store，有的话就返回，没有就创建，它会把state变成reactive，把getters变成computed，把actions挂载到store上，然后存进一个叫做",[24,2896,2897],{},"pinia._s"," 的Map，key是store id。所以store是单例的，同一个pinia实例里，useCounter()多次调用都只返回同一个对象。",[18,2900,2901,2903,2904,2910],{},[24,2902,2833],{}," 是一个 ",[21,2905,2906,2909],{},[24,2907,2908],{},"reactive"," 对象","。所有对 state 的读写都经过 Vue 的 Proxy，触发依赖收集和更新，注意这里state是一个Function，而非一个obj，因为store可能被多个组件实例创建，如果state是对象，那么多个store会共享同一个引用，函数每次调用返回新对象，保证每个store都有独立的状态。",[14,2912,2914],{"id":2913},"ssr隔离","SSR隔离",[18,2916,2917],{},"前面我们提到每个请求都有独立的pinia实例，这个是ssr安全的关键：",[96,2919,2922],{"className":2920,"code":2921,"language":392},[390],"\u002F\u002F 服务端每次请求\nconst pinia = createPinia()\napp.use(pinia)\n\n\u002F\u002F 客户端激活时\nconst pinia = createPinia()\napp.use(pinia)\n",[24,2923,2921],{"__ignoreMap":101},[18,2925,2926,2927,2930,2931,2933,2934,2939,2940,2942],{},"在nuxt中，每个请求都会创建新的pinia实例，客户端从payload中恢复store状态。Nuxt 渲染页面时，如果组件在 ",[24,2928,2929],{},"setup"," 里调用了 ",[24,2932,2893],{},"，",[21,2935,2936,2937],{},"服务端会执行这个 ",[24,2938,2929],{},"，于是 ",[24,2941,2893],{}," 也会在服务端执行，store 实例就被创建了。",[96,2944,2948],{"className":2945,"code":2946,"language":2947,"meta":101,"style":101},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup lang=\"ts\">\nconst userStore = useUserStore()  \u002F\u002F 服务端和客户端都会执行\n\u003C\u002Fscript>\n","vue",[24,2949,2950,2970,2988],{"__ignoreMap":101},[105,2951,2952,2954,2957,2960,2963,2965,2968],{"class":107,"line":108},[105,2953,234],{"class":115},[105,2955,2956],{"class":237},"script",[105,2958,2959],{"class":241}," setup",[105,2961,2962],{"class":241}," lang",[105,2964,245],{"class":115},[105,2966,2967],{"class":248},"\"ts\"",[105,2969,257],{"class":115},[105,2971,2972,2974,2977,2979,2982,2985],{"class":107,"line":119},[105,2973,568],{"class":111},[105,2975,2976],{"class":129}," userStore",[105,2978,574],{"class":111},[105,2980,2981],{"class":241}," useUserStore",[105,2983,2984],{"class":115},"()  ",[105,2986,2987],{"class":630},"\u002F\u002F 服务端和客户端都会执行\n",[105,2989,2990,2993,2995],{"class":107,"line":133},[105,2991,2992],{"class":115},"\u003C\u002F",[105,2994,2956],{"class":237},[105,2996,257],{"class":115},[18,2998,2999,3000,3003,3004,3006,3007,3009],{},"如果store里面有state初始化，getter计算，甚至action里的异步请求，这这些都会在服务端里跑一次。",[24,3001,3002],{},"@pinia\u002Fnuxt"," 模块会自动处理，让每个 SSR 请求有独立的 ",[24,3005,2757],{}," 实例。",[24,3008,2897],{}," 是请求级的 Map，不同请求的 store 不会互相污染。",[10,3011,3012],{"id":227},"HTML",[14,3014,3016],{"id":3015},"src-和-href-的区别","Src 和 href 的区别",[18,3018,3019],{},"src是替换当前元素，href是建立关联，src会把外部资源嵌入到当前的元素的位置，替换掉这个元素，它的加载行为：浏览器会阻塞解析，等资源下载完再继续，因为资源是当前文档的一部分，必须等到位。",[96,3021,3023],{"className":225,"code":3022,"language":227,"meta":101,"style":101},"\u003Cimg src=\"a.jpg\">\n\u003Cscript src=\"a.js\">\u003C\u002Fscript>\n\u003Ciframe src=\"page.html\">\u003C\u002Fiframe>\n\u003Cvideo src=\"a.mp4\">\u003C\u002Fvideo>\n\u003Caudio src=\"a.mp3\">\u003C\u002Faudio>\n",[24,3024,3025,3041,3060,3080,3100],{"__ignoreMap":101},[105,3026,3027,3029,3031,3034,3036,3039],{"class":107,"line":108},[105,3028,234],{"class":115},[105,3030,371],{"class":237},[105,3032,3033],{"class":241}," src",[105,3035,245],{"class":115},[105,3037,3038],{"class":248},"\"a.jpg\"",[105,3040,257],{"class":115},[105,3042,3043,3045,3047,3049,3051,3054,3056,3058],{"class":107,"line":119},[105,3044,234],{"class":115},[105,3046,2956],{"class":237},[105,3048,3033],{"class":241},[105,3050,245],{"class":115},[105,3052,3053],{"class":248},"\"a.js\"",[105,3055,252],{"class":115},[105,3057,2956],{"class":237},[105,3059,257],{"class":115},[105,3061,3062,3064,3067,3069,3071,3074,3076,3078],{"class":107,"line":133},[105,3063,234],{"class":115},[105,3065,3066],{"class":237},"iframe",[105,3068,3033],{"class":241},[105,3070,245],{"class":115},[105,3072,3073],{"class":248},"\"page.html\"",[105,3075,252],{"class":115},[105,3077,3066],{"class":237},[105,3079,257],{"class":115},[105,3081,3082,3084,3087,3089,3091,3094,3096,3098],{"class":107,"line":148},[105,3083,234],{"class":115},[105,3085,3086],{"class":237},"video",[105,3088,3033],{"class":241},[105,3090,245],{"class":115},[105,3092,3093],{"class":248},"\"a.mp4\"",[105,3095,252],{"class":115},[105,3097,3086],{"class":237},[105,3099,257],{"class":115},[105,3101,3102,3104,3107,3109,3111,3114,3116,3118],{"class":107,"line":163},[105,3103,234],{"class":115},[105,3105,3106],{"class":237},"audio",[105,3108,3033],{"class":241},[105,3110,245],{"class":115},[105,3112,3113],{"class":248},"\"a.mp3\"",[105,3115,252],{"class":115},[105,3117,3106],{"class":237},[105,3119,257],{"class":115},[18,3121,3122,3123,3126],{},"href作用是",[21,3124,3125],{},"建立当前文档和尾部资源的关联","，不会替换元素本身，加载行为：浏览器不阻塞解析，并行下载，资源是关联，非当前文档一部分。",[14,3128,3129],{"id":3129},"iframe标签的优缺点",[18,3131,3132],{},"这个标签作用是创建一块独立的文档区域，有自己的DOM、js上下文（不同全局对象），css作用域，cookie和存储。",[18,3134,3135],{},"父页面的css不会污染iframe内容，反之亦然，这在嵌入第三方内容时非常关键。",[96,3137,3140],{"className":3138,"code":3139,"language":392},[390],"\u003C!-- 第三方广告、地图、视频播放器 -->\n\u003Ciframe src=\"https:\u002F\u002Fmaps.example.com\">\u003C\u002Fiframe>\n",[24,3141,3139],{"__ignoreMap":101},[18,3143,3144],{},"跨域部分，想在页面中显示另一个域的内容，iframe就是唯一的直接支持，其他方案会受到CORS限制，而且脚本不会执行。",[18,3146,3147],{},"sandbox属性可以精确控制iframe能力：",[96,3149,3152],{"className":3150,"code":3151,"language":392},[390],"\u003Ciframe\n  src=\"https:\u002F\u002Funtrusted.com\"\n  sandbox=\"allow-scripts\"\n>\u003C\u002Fiframe>\n",[24,3153,3151],{"__ignoreMap":101},[835,3155,3156,3169],{},[838,3157,3158],{},[841,3159,3160,3166],{},[844,3161,3162,3165],{},[24,3163,3164],{},"sandbox"," 值",[844,3167,3168],{},"允许的能力",[854,3170,3171,3181,3191,3205,3215],{},[841,3172,3173,3178],{},[859,3174,3175],{},[24,3176,3177],{},"allow-scripts",[859,3179,3180],{},"执行 JS",[841,3182,3183,3188],{},[859,3184,3185],{},[24,3186,3187],{},"allow-forms",[859,3189,3190],{},"提交表单",[841,3192,3193,3198],{},[859,3194,3195],{},[24,3196,3197],{},"allow-same-origin",[859,3199,3200,3201,3204],{},"保持同源（",[21,3202,3203],{},"慎用","）",[841,3206,3207,3212],{},[859,3208,3209],{},[24,3210,3211],{},"allow-popups",[859,3213,3214],{},"打开弹窗",[841,3216,3217,3222],{},[859,3218,3219],{},[24,3220,3221],{},"allow-top-navigation",[859,3223,3224],{},"跳转顶层页面",[18,3226,3227,3228,3231],{},"iframe的加载和主页面解耦，不会拖慢主页面渲染，如果iframe脚本崩溃，不会影响父页面。但是每一个iframe是一个独立的浏览上下文，是独立的js引擎实例，独立的dom树，独立事件循环，内存开销大，多个iframe会显著增加内存占用。搜索引擎",[21,3229,3230],{},"通常不索引 iframe 内容","。如果关键内容在 iframe 里，SEO 基本失效。",[18,3233,3234,3235,3238],{},"父页面和 iframe 通信只能通过 ",[24,3236,3237],{},"postMessage"," ，这里一定要和BroadcastChannel分开，这是两个不同技术。",[96,3240,3244],{"className":3241,"code":3242,"language":3243,"meta":101,"style":101},"language-ts shiki shiki-themes github-light github-dark","\u002F\u002F 父页面发消息\niframe.contentWindow.postMessage('hello', 'https:\u002F\u002Fchild.com')\n\n\u002F\u002F 子页面接收\nwindow.addEventListener('message', (e) => {\n  if (e.origin !== 'https:\u002F\u002Fparent.com') return  \u002F\u002F 必须校验 origin\n  console.log(e.data)\n})\n","ts",[24,3245,3246,3251,3270,3274,3279,3304,3326,3336],{"__ignoreMap":101},[105,3247,3248],{"class":107,"line":108},[105,3249,3250],{"class":630},"\u002F\u002F 父页面发消息\n",[105,3252,3253,3256,3258,3260,3263,3265,3268],{"class":107,"line":119},[105,3254,3255],{"class":115},"iframe.contentWindow.",[105,3257,3237],{"class":241},[105,3259,321],{"class":115},[105,3261,3262],{"class":248},"'hello'",[105,3264,286],{"class":115},[105,3266,3267],{"class":248},"'https:\u002F\u002Fchild.com'",[105,3269,1511],{"class":115},[105,3271,3272],{"class":107,"line":133},[105,3273,307],{"emptyLinePlaceholder":306},[105,3275,3276],{"class":107,"line":148},[105,3277,3278],{"class":630},"\u002F\u002F 子页面接收\n",[105,3280,3281,3284,3287,3289,3292,3295,3298,3300,3302],{"class":107,"line":163},[105,3282,3283],{"class":115},"window.",[105,3285,3286],{"class":241},"addEventListener",[105,3288,321],{"class":115},[105,3290,3291],{"class":248},"'message'",[105,3293,3294],{"class":115},", (",[105,3296,3297],{"class":122},"e",[105,3299,2836],{"class":115},[105,3301,723],{"class":111},[105,3303,116],{"class":115},[105,3305,3306,3309,3312,3315,3318,3320,3323],{"class":107,"line":179},[105,3307,3308],{"class":111},"  if",[105,3310,3311],{"class":115}," (e.origin ",[105,3313,3314],{"class":111},"!==",[105,3316,3317],{"class":248}," 'https:\u002F\u002Fparent.com'",[105,3319,2836],{"class":115},[105,3321,3322],{"class":111},"return",[105,3324,3325],{"class":630},"  \u002F\u002F 必须校验 origin\n",[105,3327,3328,3331,3333],{"class":107,"line":194},[105,3329,3330],{"class":115},"  console.",[105,3332,639],{"class":241},[105,3334,3335],{"class":115},"(e.data)\n",[105,3337,3338],{"class":107,"line":209},[105,3339,2888],{"class":115},[14,3341,3343],{"id":3342},"canvas和svg","Canvas和SVG",[18,3345,3346],{},"canvas是位图，svg是矢量图，canvas是立即模式渲染，逐像素绘制，svg是保留模式绘制，DOM节点描述。canvas本质是像素数组，svg本质是dom树。",[18,3348,3349],{},"稍微触底来看看，canvas像素数据在js层面使用的是uint8ClampedArray，不同于wasm里的uint8heap，都是字节数组，但是约束和用途不同。",[18,3351,3352],{},"使用getImageData()获取canvas像素的时候，返回的是ImageData对象，里面有一个data属性，类型就是Uint8ClampedArray。",[96,3354,3356],{"className":559,"code":3355,"language":561,"meta":101,"style":101},"const imageData = ctx.getImageData(0, 0, width, height);\n\u002F\u002F imageData.data 是 Uint8ClampedArray\n\u002F\u002F 顺序是 RGBA：R, G, B, A, R, G, B, A...\n",[24,3357,3358,3384,3389],{"__ignoreMap":101},[105,3359,3360,3362,3365,3367,3370,3373,3375,3377,3379,3381],{"class":107,"line":108},[105,3361,568],{"class":111},[105,3363,3364],{"class":129}," imageData",[105,3366,574],{"class":111},[105,3368,3369],{"class":115}," ctx.",[105,3371,3372],{"class":241},"getImageData",[105,3374,321],{"class":115},[105,3376,1100],{"class":129},[105,3378,286],{"class":115},[105,3380,1100],{"class":129},[105,3382,3383],{"class":115},", width, height);\n",[105,3385,3386],{"class":107,"line":119},[105,3387,3388],{"class":630},"\u002F\u002F imageData.data 是 Uint8ClampedArray\n",[105,3390,3391],{"class":107,"line":133},[105,3392,3393],{"class":630},"\u002F\u002F 顺序是 RGBA：R, G, B, A, R, G, B, A...\n",[18,3395,3396,3397,3400,3401,3404,3405,3408,3409,3411,3412,3415,3416,3419,3420,3422,3423,3426,3427,3429,3430,91],{},"这个数组的每个元素是一个 ",[21,3398,3399],{},"0 到 255 之间的整数","，每 4 个一组（红、绿、蓝、透明度），",[21,3402,3403],{},"“Clamped”就是“钳制”的意思。"," 如果你给 ",[24,3406,3407],{},"Uint8ClampedArray"," 的元素赋值 ",[24,3410,608],{},"，它会被自动截断为 ",[24,3413,3414],{},"255","；赋值 ",[24,3417,3418],{},"-10","，会变成 ",[24,3421,1100],{},"。而 ",[24,3424,3425],{},"Uint8Array"," 会做模运算，",[24,3428,608],{}," 会变成 ",[24,3431,3432],{},"44",[18,3434,3435],{},"而wasm里面的线性内存WebAssembly.Memory中，在js侧是通过ArrayBuffer暴露的，我们可以用不同的TypedArray视图去查看：",[96,3437,3439],{"className":559,"code":3438,"language":561,"meta":101,"style":101},"\u002F\u002F WebAssembly 的内存\nconst waMemory = waInstance.exports.memory;\n\n\u002F\u002F 用 Uint8Array 视图看这块内存\nconst u8Heap = new Uint8Array(waMemory.buffer);\n",[24,3440,3441,3446,3458,3462,3467],{"__ignoreMap":101},[105,3442,3443],{"class":107,"line":108},[105,3444,3445],{"class":630},"\u002F\u002F WebAssembly 的内存\n",[105,3447,3448,3450,3453,3455],{"class":107,"line":119},[105,3449,568],{"class":111},[105,3451,3452],{"class":129}," waMemory",[105,3454,574],{"class":111},[105,3456,3457],{"class":115}," waInstance.exports.memory;\n",[105,3459,3460],{"class":107,"line":133},[105,3461,307],{"emptyLinePlaceholder":306},[105,3463,3464],{"class":107,"line":148},[105,3465,3466],{"class":630},"\u002F\u002F 用 Uint8Array 视图看这块内存\n",[105,3468,3469,3471,3474,3476,3479,3482],{"class":107,"line":163},[105,3470,568],{"class":111},[105,3472,3473],{"class":129}," u8Heap",[105,3475,574],{"class":111},[105,3477,3478],{"class":111}," new",[105,3480,3481],{"class":241}," Uint8Array",[105,3483,3484],{"class":115},"(waMemory.buffer);\n",[14,3486,3488],{"id":3487},"typescript的dom类型定义","TypeScript的DOM类型定义",[18,3490,3491],{},"ts中的dom类型定义里，Element继承链是EventTarget-> Node-> element，",[96,3493,3495],{"className":3241,"code":3494,"language":3243,"meta":101,"style":101},"interface EventTarget { }\ninterface Node extends EventTarget { }\ninterface Element extends Node { }\n",[24,3496,3497,3508,3522],{"__ignoreMap":101},[105,3498,3499,3502,3505],{"class":107,"line":108},[105,3500,3501],{"class":111},"interface",[105,3503,3504],{"class":241}," EventTarget",[105,3506,3507],{"class":115}," { }\n",[105,3509,3510,3512,3515,3518,3520],{"class":107,"line":119},[105,3511,3501],{"class":111},[105,3513,3514],{"class":241}," Node",[105,3516,3517],{"class":111}," extends",[105,3519,3504],{"class":241},[105,3521,3507],{"class":115},[105,3523,3524,3526,3529,3531,3533],{"class":107,"line":133},[105,3525,3501],{"class":111},[105,3527,3528],{"class":241}," Element",[105,3530,3517],{"class":111},[105,3532,3514],{"class":241},[105,3534,3507],{"class":115},[18,3536,3537],{},"再往下的话，就是具体元素HTMLElement、SVGElement、MathElement 都是继承自Element了。",[96,3539,3542],{"className":3540,"code":3541,"language":392},[390],"EventTarget\n  └── Node\n        └── Element\n              ├── HTMLElement\n              │     ├── HTMLDivElement\n              │     ├── HTMLInputElement\n              │     └── ...\n              ├── SVGElement\n              └── MathMLElement\n",[24,3543,3541],{"__ignoreMap":101},[18,3545,3546],{},"然后接下来可以看看每一层都提供了什么，这也是DOM的原型设计，记了有很多好处，如果后续想要自己开发跨平台组件的话，也可以参考看看。",[18,3548,3549,3550,31,3552,31,3555,3558],{},"EventTarget提供了 ",[24,3551,3286],{},[24,3553,3554],{},"removeEventListener",[24,3556,3557],{},"dispatchEvent"," ，让DOM组件有了事件能力。",[18,3560,3561,3562,31,3565,31,3568,31,3571,31,3574,31,3577,3580],{},"Node提供了 ",[24,3563,3564],{},"parentNode",[24,3566,3567],{},"childNodes",[24,3569,3570],{},"appendChild",[24,3572,3573],{},"removeChild",[24,3575,3576],{},"nodeType",[24,3578,3579],{},"textContent","，可以让我们随意增删节点和设置文本内容。",[18,3582,3583,3584,31,3587,31,3590,31,3593,31,3596,31,3598,31,3601,3604],{},"Element提供了",[24,3585,3586],{},"id",[24,3588,3589],{},"className",[24,3591,3592],{},"tagName",[24,3594,3595],{},"attributes",[24,3597,1503],{},[24,3599,3600],{},"getAttribute",[24,3602,3603],{},"innerHTML","，让我们可以在DOM组件上设置标识、查询后代、读写属性。",[18,3606,3607,3612,3613,31,3616,31,3619,31,3622,31,3625,31,3628,3631],{},[21,3608,3609],{},[24,3610,3611],{},"HTMLElement"," 提供了 ",[24,3614,3615],{},"style",[24,3617,3618],{},"dataset",[24,3620,3621],{},"offsetWidth",[24,3623,3624],{},"click()",[24,3626,3627],{},"focus()",[24,3629,3630],{},"hidden","，让DOM组件可以控制样式，读写data属性，测量尺寸，触发交互、控制可见性。",[14,3633,3634],{"id":3634},"虚拟列表",[18,3636,3637,3638,91],{},"这是一种前端优化的性能技术，用于高效渲染超长列表，和WPF的虚拟滚动是一种意思，",[21,3639,3640],{},"只渲染视口内可见的列表项，而不是一次性渲染全部数据",[18,3642,3643],{},"在遇到超多DOM的情况下可以做到小占用，滚动流程。只不过虚拟列表可能更常用，因为在前端UI组件中更加常用，组件叫做VirtualList。虚拟滚动在技术文章和浏览器API里讨论常见，描述滚动时动态更新内容这个行为。",[18,3645,3646],{},"我们需要保留视口内的容器，让新的数据替换掉，重新渲染。",[96,3648,3650],{"className":2945,"code":3649,"language":2947,"meta":101,"style":101},"\u003Ctemplate>\n  \u003Cdiv\n    ref=\"viewportRef\"\n    class=\"virtual-list\"\n    @scroll.passive=\"onScroll\"\n  >\n    \u003C!-- 占位层：撑起总高度，让滚动条正确 -->\n    \u003Cdiv class=\"phantom\" :style=\"{ height: totalHeight + 'px' }\">\u003C\u002Fdiv>\n\n    \u003C!-- 渲染层：只放可见项 -->\n    \u003Cdiv class=\"render-layer\" :style=\"{ transform: `translateY(${offsetY}px)` }\">\n      \u003Cdiv\n        v-for=\"item in visibleItems\"\n        :key=\"item.id\"\n        class=\"list-item\"\n        :style=\"{ height: itemHeight + 'px' }\"\n      >\n        {{ item.name }}\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { ref, computed, onMounted, onBeforeUnmount } from 'vue';\n\nconst props = defineProps({\n  list: { type: Array, default: () => [] },\n  itemHeight: { type: Number, default: 50 },\n  buffer: { type: Number, default: 3 } \u002F\u002F 上下缓冲区，防止快速滚动白屏\n});\n\nconst viewportRef = ref(null);\nconst scrollTop = ref(0);\nconst viewportHeight = ref(0);\n\n\u002F\u002F 总高度：撑起滚动条\nconst totalHeight = computed(() => props.list.length * props.itemHeight);\n\n\u002F\u002F 可见起始索引（含缓冲区）\nconst startIndex = computed(() => {\n  const raw = Math.floor(scrollTop.value \u002F props.itemHeight);\n  return Math.max(0, raw - props.buffer);\n});\n\n\u002F\u002F 可见结束索引（含缓冲区）\nconst endIndex = computed(() => {\n  const raw = Math.ceil((scrollTop.value + viewportHeight.value) \u002F props.itemHeight);\n  return Math.min(props.list.length - 1, raw + props.buffer);\n});\n\n\u002F\u002F 当前可见的数据切片\nconst visibleItems = computed(() =>\n  props.list.slice(startIndex.value, endIndex.value + 1)\n);\n\n\u002F\u002F 渲染层的偏移量\nconst offsetY = computed(() => startIndex.value * props.itemHeight);\n\n\u002F\u002F 滚动处理（用 rAF 节流）\nlet ticking = false;\nconst onScroll = (e) => {\n  if (ticking) return;\n  ticking = true;\n  requestAnimationFrame(() => {\n    scrollTop.value = e.target.scrollTop;\n    ticking = false;\n  });\n};\n\n\u002F\u002F 初始化视口高度\nconst updateViewportHeight = () => {\n  if (viewportRef.value) {\n    viewportHeight.value = viewportRef.value.clientHeight;\n  }\n};\n\nonMounted(() => {\n  updateViewportHeight();\n  window.addEventListener('resize', updateViewportHeight);\n});\n\nonBeforeUnmount(() => {\n  window.removeEventListener('resize', updateViewportHeight);\n});\n\u003C\u002Fscript>\n\n\u003Cstyle scoped>\n.virtual-list {\n  height: 500px;          \u002F* 固定视口高度 *\u002F\n  overflow-y: auto;\n  position: relative;\n  border: 1px solid #ddd;\n}\n\n.phantom {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  z-index: -1;            \u002F* 不遮挡内容，只撑高度 *\u002F\n}\n\n.render-layer {\n  position: absolute;\n  top: 0;\n  left: 0;\n  right: 0;\n  will-change: transform; \u002F* 提示浏览器优化 *\u002F\n}\n\n.list-item {\n  box-sizing: border-box;\n  border-bottom: 1px solid #eee;\n  display: flex;\n  align-items: center;\n  padding: 0 12px;\n}\n\u003C\u002Fstyle>\n",[24,3651,3652,3661,3669,3679,3689,3699,3704,3709,3738,3742,3747,3769,3776,3786,3796,3806,3816,3821,3826,3835,3845,3855,3864,3869,3880,3897,3902,3918,3934,3945,3959,3965,3970,3990,4008,4026,4031,4037,4067,4072,4078,4096,4121,4145,4150,4155,4161,4179,4205,4232,4237,4242,4248,4265,4283,4288,4293,4299,4322,4327,4333,4349,4370,4382,4395,4407,4418,4430,4436,4442,4447,4453,4469,4477,4488,4493,4498,4503,4515,4523,4539,4544,4549,4561,4574,4579,4588,4593,4605,4613,4631,4643,4654,4672,4677,4682,4690,4701,4712,4723,4735,4751,4756,4761,4769,4780,4791,4802,4813,4825,4830,4835,4843,4856,4875,4887,4900,4916,4921],{"__ignoreMap":101},[105,3653,3654,3656,3659],{"class":107,"line":108},[105,3655,234],{"class":115},[105,3657,3658],{"class":237},"template",[105,3660,257],{"class":115},[105,3662,3663,3666],{"class":107,"line":119},[105,3664,3665],{"class":115},"  \u003C",[105,3667,3668],{"class":237},"div\n",[105,3670,3671,3674,3676],{"class":107,"line":133},[105,3672,3673],{"class":241},"    ref",[105,3675,245],{"class":115},[105,3677,3678],{"class":248},"\"viewportRef\"\n",[105,3680,3681,3684,3686],{"class":107,"line":148},[105,3682,3683],{"class":241},"    class",[105,3685,245],{"class":115},[105,3687,3688],{"class":248},"\"virtual-list\"\n",[105,3690,3691,3694,3696],{"class":107,"line":163},[105,3692,3693],{"class":241},"    @scroll.passive",[105,3695,245],{"class":115},[105,3697,3698],{"class":248},"\"onScroll\"\n",[105,3700,3701],{"class":107,"line":179},[105,3702,3703],{"class":115},"  >\n",[105,3705,3706],{"class":107,"line":194},[105,3707,3708],{"class":630},"    \u003C!-- 占位层：撑起总高度，让滚动条正确 -->\n",[105,3710,3711,3714,3716,3719,3721,3724,3727,3729,3732,3734,3736],{"class":107,"line":209},[105,3712,3713],{"class":115},"    \u003C",[105,3715,238],{"class":237},[105,3717,3718],{"class":241}," class",[105,3720,245],{"class":115},[105,3722,3723],{"class":248},"\"phantom\"",[105,3725,3726],{"class":241}," :style",[105,3728,245],{"class":115},[105,3730,3731],{"class":248},"\"{ height: totalHeight + 'px' }\"",[105,3733,252],{"class":115},[105,3735,238],{"class":237},[105,3737,257],{"class":115},[105,3739,3740],{"class":107,"line":526},[105,3741,307],{"emptyLinePlaceholder":306},[105,3743,3744],{"class":107,"line":531},[105,3745,3746],{"class":630},"    \u003C!-- 渲染层：只放可见项 -->\n",[105,3748,3749,3751,3753,3755,3757,3760,3762,3764,3767],{"class":107,"line":668},[105,3750,3713],{"class":115},[105,3752,238],{"class":237},[105,3754,3718],{"class":241},[105,3756,245],{"class":115},[105,3758,3759],{"class":248},"\"render-layer\"",[105,3761,3726],{"class":241},[105,3763,245],{"class":115},[105,3765,3766],{"class":248},"\"{ transform: `translateY(${offsetY}px)` }\"",[105,3768,257],{"class":115},[105,3770,3771,3774],{"class":107,"line":673},[105,3772,3773],{"class":115},"      \u003C",[105,3775,3668],{"class":237},[105,3777,3778,3781,3783],{"class":107,"line":679},[105,3779,3780],{"class":241},"        v-for",[105,3782,245],{"class":115},[105,3784,3785],{"class":248},"\"item in visibleItems\"\n",[105,3787,3788,3791,3793],{"class":107,"line":689},[105,3789,3790],{"class":241},"        :key",[105,3792,245],{"class":115},[105,3794,3795],{"class":248},"\"item.id\"\n",[105,3797,3798,3801,3803],{"class":107,"line":699},[105,3799,3800],{"class":241},"        class",[105,3802,245],{"class":115},[105,3804,3805],{"class":248},"\"list-item\"\n",[105,3807,3808,3811,3813],{"class":107,"line":704},[105,3809,3810],{"class":241},"        :style",[105,3812,245],{"class":115},[105,3814,3815],{"class":248},"\"{ height: itemHeight + 'px' }\"\n",[105,3817,3818],{"class":107,"line":710},[105,3819,3820],{"class":115},"      >\n",[105,3822,3823],{"class":107,"line":1136},[105,3824,3825],{"class":115},"        {{ item.name }}\n",[105,3827,3828,3831,3833],{"class":107,"line":1149},[105,3829,3830],{"class":115},"      \u003C\u002F",[105,3832,238],{"class":237},[105,3834,257],{"class":115},[105,3836,3838,3841,3843],{"class":107,"line":3837},20,[105,3839,3840],{"class":115},"    \u003C\u002F",[105,3842,238],{"class":237},[105,3844,257],{"class":115},[105,3846,3848,3851,3853],{"class":107,"line":3847},21,[105,3849,3850],{"class":115},"  \u003C\u002F",[105,3852,238],{"class":237},[105,3854,257],{"class":115},[105,3856,3858,3860,3862],{"class":107,"line":3857},22,[105,3859,2992],{"class":115},[105,3861,3658],{"class":237},[105,3863,257],{"class":115},[105,3865,3867],{"class":107,"line":3866},23,[105,3868,307],{"emptyLinePlaceholder":306},[105,3870,3872,3874,3876,3878],{"class":107,"line":3871},24,[105,3873,234],{"class":115},[105,3875,2956],{"class":237},[105,3877,2959],{"class":241},[105,3879,257],{"class":115},[105,3881,3883,3886,3889,3892,3895],{"class":107,"line":3882},25,[105,3884,3885],{"class":111},"import",[105,3887,3888],{"class":115}," { ref, computed, onMounted, onBeforeUnmount } ",[105,3890,3891],{"class":111},"from",[105,3893,3894],{"class":248}," 'vue'",[105,3896,145],{"class":115},[105,3898,3900],{"class":107,"line":3899},26,[105,3901,307],{"emptyLinePlaceholder":306},[105,3903,3905,3907,3910,3912,3915],{"class":107,"line":3904},27,[105,3906,568],{"class":111},[105,3908,3909],{"class":129}," props",[105,3911,574],{"class":111},[105,3913,3914],{"class":241}," defineProps",[105,3916,3917],{"class":115},"({\n",[105,3919,3921,3924,3927,3929,3931],{"class":107,"line":3920},28,[105,3922,3923],{"class":115},"  list: { type: Array, ",[105,3925,3926],{"class":241},"default",[105,3928,2807],{"class":115},[105,3930,723],{"class":111},[105,3932,3933],{"class":115}," [] },\n",[105,3935,3937,3940,3943],{"class":107,"line":3936},29,[105,3938,3939],{"class":115},"  itemHeight: { type: Number, default: ",[105,3941,3942],{"class":129},"50",[105,3944,2879],{"class":115},[105,3946,3948,3951,3953,3956],{"class":107,"line":3947},30,[105,3949,3950],{"class":115},"  buffer: { type: Number, default: ",[105,3952,891],{"class":129},[105,3954,3955],{"class":115}," } ",[105,3957,3958],{"class":630},"\u002F\u002F 上下缓冲区，防止快速滚动白屏\n",[105,3960,3962],{"class":107,"line":3961},31,[105,3963,3964],{"class":115},"});\n",[105,3966,3968],{"class":107,"line":3967},32,[105,3969,307],{"emptyLinePlaceholder":306},[105,3971,3973,3975,3978,3980,3983,3985,3988],{"class":107,"line":3972},33,[105,3974,568],{"class":111},[105,3976,3977],{"class":129}," viewportRef",[105,3979,574],{"class":111},[105,3981,3982],{"class":241}," ref",[105,3984,321],{"class":115},[105,3986,3987],{"class":129},"null",[105,3989,327],{"class":115},[105,3991,3993,3995,3998,4000,4002,4004,4006],{"class":107,"line":3992},34,[105,3994,568],{"class":111},[105,3996,3997],{"class":129}," scrollTop",[105,3999,574],{"class":111},[105,4001,3982],{"class":241},[105,4003,321],{"class":115},[105,4005,1100],{"class":129},[105,4007,327],{"class":115},[105,4009,4011,4013,4016,4018,4020,4022,4024],{"class":107,"line":4010},35,[105,4012,568],{"class":111},[105,4014,4015],{"class":129}," viewportHeight",[105,4017,574],{"class":111},[105,4019,3982],{"class":241},[105,4021,321],{"class":115},[105,4023,1100],{"class":129},[105,4025,327],{"class":115},[105,4027,4029],{"class":107,"line":4028},36,[105,4030,307],{"emptyLinePlaceholder":306},[105,4032,4034],{"class":107,"line":4033},37,[105,4035,4036],{"class":630},"\u002F\u002F 总高度：撑起滚动条\n",[105,4038,4040,4042,4045,4047,4050,4053,4055,4058,4061,4064],{"class":107,"line":4039},38,[105,4041,568],{"class":111},[105,4043,4044],{"class":129}," totalHeight",[105,4046,574],{"class":111},[105,4048,4049],{"class":241}," computed",[105,4051,4052],{"class":115},"(() ",[105,4054,723],{"class":111},[105,4056,4057],{"class":115}," props.list.",[105,4059,4060],{"class":129},"length",[105,4062,4063],{"class":111}," *",[105,4065,4066],{"class":115}," props.itemHeight);\n",[105,4068,4070],{"class":107,"line":4069},39,[105,4071,307],{"emptyLinePlaceholder":306},[105,4073,4075],{"class":107,"line":4074},40,[105,4076,4077],{"class":630},"\u002F\u002F 可见起始索引（含缓冲区）\n",[105,4079,4081,4083,4086,4088,4090,4092,4094],{"class":107,"line":4080},41,[105,4082,568],{"class":111},[105,4084,4085],{"class":129}," startIndex",[105,4087,574],{"class":111},[105,4089,4049],{"class":241},[105,4091,4052],{"class":115},[105,4093,723],{"class":111},[105,4095,116],{"class":115},[105,4097,4099,4102,4105,4107,4110,4113,4116,4119],{"class":107,"line":4098},42,[105,4100,4101],{"class":111},"  const",[105,4103,4104],{"class":129}," raw",[105,4106,574],{"class":111},[105,4108,4109],{"class":115}," Math.",[105,4111,4112],{"class":241},"floor",[105,4114,4115],{"class":115},"(scrollTop.value ",[105,4117,4118],{"class":111},"\u002F",[105,4120,4066],{"class":115},[105,4122,4124,4127,4129,4132,4134,4136,4139,4142],{"class":107,"line":4123},43,[105,4125,4126],{"class":111},"  return",[105,4128,4109],{"class":115},[105,4130,4131],{"class":241},"max",[105,4133,321],{"class":115},[105,4135,1100],{"class":129},[105,4137,4138],{"class":115},", raw ",[105,4140,4141],{"class":111},"-",[105,4143,4144],{"class":115}," props.buffer);\n",[105,4146,4148],{"class":107,"line":4147},44,[105,4149,3964],{"class":115},[105,4151,4153],{"class":107,"line":4152},45,[105,4154,307],{"emptyLinePlaceholder":306},[105,4156,4158],{"class":107,"line":4157},46,[105,4159,4160],{"class":630},"\u002F\u002F 可见结束索引（含缓冲区）\n",[105,4162,4164,4166,4169,4171,4173,4175,4177],{"class":107,"line":4163},47,[105,4165,568],{"class":111},[105,4167,4168],{"class":129}," endIndex",[105,4170,574],{"class":111},[105,4172,4049],{"class":241},[105,4174,4052],{"class":115},[105,4176,723],{"class":111},[105,4178,116],{"class":115},[105,4180,4182,4184,4186,4188,4190,4193,4196,4198,4201,4203],{"class":107,"line":4181},48,[105,4183,4101],{"class":111},[105,4185,4104],{"class":129},[105,4187,574],{"class":111},[105,4189,4109],{"class":115},[105,4191,4192],{"class":241},"ceil",[105,4194,4195],{"class":115},"((scrollTop.value ",[105,4197,1085],{"class":111},[105,4199,4200],{"class":115}," viewportHeight.value) ",[105,4202,4118],{"class":111},[105,4204,4066],{"class":115},[105,4206,4208,4210,4212,4215,4218,4220,4223,4226,4228,4230],{"class":107,"line":4207},49,[105,4209,4126],{"class":111},[105,4211,4109],{"class":115},[105,4213,4214],{"class":241},"min",[105,4216,4217],{"class":115},"(props.list.",[105,4219,4060],{"class":129},[105,4221,4222],{"class":111}," -",[105,4224,4225],{"class":129}," 1",[105,4227,4138],{"class":115},[105,4229,1085],{"class":111},[105,4231,4144],{"class":115},[105,4233,4235],{"class":107,"line":4234},50,[105,4236,3964],{"class":115},[105,4238,4240],{"class":107,"line":4239},51,[105,4241,307],{"emptyLinePlaceholder":306},[105,4243,4245],{"class":107,"line":4244},52,[105,4246,4247],{"class":630},"\u002F\u002F 当前可见的数据切片\n",[105,4249,4251,4253,4256,4258,4260,4262],{"class":107,"line":4250},53,[105,4252,568],{"class":111},[105,4254,4255],{"class":129}," visibleItems",[105,4257,574],{"class":111},[105,4259,4049],{"class":241},[105,4261,4052],{"class":115},[105,4263,4264],{"class":111},"=>\n",[105,4266,4268,4271,4274,4277,4279,4281],{"class":107,"line":4267},54,[105,4269,4270],{"class":115},"  props.list.",[105,4272,4273],{"class":241},"slice",[105,4275,4276],{"class":115},"(startIndex.value, endIndex.value ",[105,4278,1085],{"class":111},[105,4280,4225],{"class":129},[105,4282,1511],{"class":115},[105,4284,4286],{"class":107,"line":4285},55,[105,4287,327],{"class":115},[105,4289,4291],{"class":107,"line":4290},56,[105,4292,307],{"emptyLinePlaceholder":306},[105,4294,4296],{"class":107,"line":4295},57,[105,4297,4298],{"class":630},"\u002F\u002F 渲染层的偏移量\n",[105,4300,4302,4304,4307,4309,4311,4313,4315,4318,4320],{"class":107,"line":4301},58,[105,4303,568],{"class":111},[105,4305,4306],{"class":129}," offsetY",[105,4308,574],{"class":111},[105,4310,4049],{"class":241},[105,4312,4052],{"class":115},[105,4314,723],{"class":111},[105,4316,4317],{"class":115}," startIndex.value ",[105,4319,938],{"class":111},[105,4321,4066],{"class":115},[105,4323,4325],{"class":107,"line":4324},59,[105,4326,307],{"emptyLinePlaceholder":306},[105,4328,4330],{"class":107,"line":4329},60,[105,4331,4332],{"class":630},"\u002F\u002F 滚动处理（用 rAF 节流）\n",[105,4334,4336,4339,4342,4344,4347],{"class":107,"line":4335},61,[105,4337,4338],{"class":111},"let",[105,4340,4341],{"class":115}," ticking ",[105,4343,245],{"class":111},[105,4345,4346],{"class":129}," false",[105,4348,145],{"class":115},[105,4350,4352,4354,4357,4359,4362,4364,4366,4368],{"class":107,"line":4351},62,[105,4353,568],{"class":111},[105,4355,4356],{"class":241}," onScroll",[105,4358,574],{"class":111},[105,4360,4361],{"class":115}," (",[105,4363,3297],{"class":122},[105,4365,2836],{"class":115},[105,4367,723],{"class":111},[105,4369,116],{"class":115},[105,4371,4373,4375,4378,4380],{"class":107,"line":4372},63,[105,4374,3308],{"class":111},[105,4376,4377],{"class":115}," (ticking) ",[105,4379,3322],{"class":111},[105,4381,145],{"class":115},[105,4383,4385,4388,4390,4393],{"class":107,"line":4384},64,[105,4386,4387],{"class":115},"  ticking ",[105,4389,245],{"class":111},[105,4391,4392],{"class":129}," true",[105,4394,145],{"class":115},[105,4396,4398,4401,4403,4405],{"class":107,"line":4397},65,[105,4399,4400],{"class":241},"  requestAnimationFrame",[105,4402,4052],{"class":115},[105,4404,723],{"class":111},[105,4406,116],{"class":115},[105,4408,4410,4413,4415],{"class":107,"line":4409},66,[105,4411,4412],{"class":115},"    scrollTop.value ",[105,4414,245],{"class":111},[105,4416,4417],{"class":115}," e.target.scrollTop;\n",[105,4419,4421,4424,4426,4428],{"class":107,"line":4420},67,[105,4422,4423],{"class":115},"    ticking ",[105,4425,245],{"class":111},[105,4427,4346],{"class":129},[105,4429,145],{"class":115},[105,4431,4433],{"class":107,"line":4432},68,[105,4434,4435],{"class":115},"  });\n",[105,4437,4439],{"class":107,"line":4438},69,[105,4440,4441],{"class":115},"};\n",[105,4443,4445],{"class":107,"line":4444},70,[105,4446,307],{"emptyLinePlaceholder":306},[105,4448,4450],{"class":107,"line":4449},71,[105,4451,4452],{"class":630},"\u002F\u002F 初始化视口高度\n",[105,4454,4456,4458,4461,4463,4465,4467],{"class":107,"line":4455},72,[105,4457,568],{"class":111},[105,4459,4460],{"class":241}," updateViewportHeight",[105,4462,574],{"class":111},[105,4464,720],{"class":115},[105,4466,723],{"class":111},[105,4468,116],{"class":115},[105,4470,4472,4474],{"class":107,"line":4471},73,[105,4473,3308],{"class":111},[105,4475,4476],{"class":115}," (viewportRef.value) {\n",[105,4478,4480,4483,4485],{"class":107,"line":4479},74,[105,4481,4482],{"class":115},"    viewportHeight.value ",[105,4484,245],{"class":111},[105,4486,4487],{"class":115}," viewportRef.value.clientHeight;\n",[105,4489,4491],{"class":107,"line":4490},75,[105,4492,509],{"class":115},[105,4494,4496],{"class":107,"line":4495},76,[105,4497,4441],{"class":115},[105,4499,4501],{"class":107,"line":4500},77,[105,4502,307],{"emptyLinePlaceholder":306},[105,4504,4506,4509,4511,4513],{"class":107,"line":4505},78,[105,4507,4508],{"class":241},"onMounted",[105,4510,4052],{"class":115},[105,4512,723],{"class":111},[105,4514,116],{"class":115},[105,4516,4518,4521],{"class":107,"line":4517},79,[105,4519,4520],{"class":241},"  updateViewportHeight",[105,4522,665],{"class":115},[105,4524,4526,4529,4531,4533,4536],{"class":107,"line":4525},80,[105,4527,4528],{"class":115},"  window.",[105,4530,3286],{"class":241},[105,4532,321],{"class":115},[105,4534,4535],{"class":248},"'resize'",[105,4537,4538],{"class":115},", updateViewportHeight);\n",[105,4540,4542],{"class":107,"line":4541},81,[105,4543,3964],{"class":115},[105,4545,4547],{"class":107,"line":4546},82,[105,4548,307],{"emptyLinePlaceholder":306},[105,4550,4552,4555,4557,4559],{"class":107,"line":4551},83,[105,4553,4554],{"class":241},"onBeforeUnmount",[105,4556,4052],{"class":115},[105,4558,723],{"class":111},[105,4560,116],{"class":115},[105,4562,4564,4566,4568,4570,4572],{"class":107,"line":4563},84,[105,4565,4528],{"class":115},[105,4567,3554],{"class":241},[105,4569,321],{"class":115},[105,4571,4535],{"class":248},[105,4573,4538],{"class":115},[105,4575,4577],{"class":107,"line":4576},85,[105,4578,3964],{"class":115},[105,4580,4582,4584,4586],{"class":107,"line":4581},86,[105,4583,2992],{"class":115},[105,4585,2956],{"class":237},[105,4587,257],{"class":115},[105,4589,4591],{"class":107,"line":4590},87,[105,4592,307],{"emptyLinePlaceholder":306},[105,4594,4596,4598,4600,4603],{"class":107,"line":4595},88,[105,4597,234],{"class":115},[105,4599,3615],{"class":237},[105,4601,4602],{"class":241}," scoped",[105,4604,257],{"class":115},[105,4606,4608,4611],{"class":107,"line":4607},89,[105,4609,4610],{"class":241},".virtual-list",[105,4612,116],{"class":115},[105,4614,4616,4618,4620,4623,4625,4628],{"class":107,"line":4615},90,[105,4617,2684],{"class":129},[105,4619,126],{"class":115},[105,4621,4622],{"class":129},"500",[105,4624,142],{"class":111},[105,4626,4627],{"class":115},";          ",[105,4629,4630],{"class":630},"\u002F* 固定视口高度 *\u002F\n",[105,4632,4634,4637,4639,4641],{"class":107,"line":4633},91,[105,4635,4636],{"class":129},"  overflow-y",[105,4638,126],{"class":115},[105,4640,1884],{"class":129},[105,4642,145],{"class":115},[105,4644,4646,4648,4650,4652],{"class":107,"line":4645},92,[105,4647,2600],{"class":129},[105,4649,126],{"class":115},[105,4651,1866],{"class":129},[105,4653,145],{"class":115},[105,4655,4657,4659,4661,4663,4665,4667,4670],{"class":107,"line":4656},93,[105,4658,1005],{"class":129},[105,4660,126],{"class":115},[105,4662,861],{"class":129},[105,4664,142],{"class":111},[105,4666,1014],{"class":129},[105,4668,4669],{"class":129}," #ddd",[105,4671,145],{"class":115},[105,4673,4675],{"class":107,"line":4674},94,[105,4676,212],{"class":115},[105,4678,4680],{"class":107,"line":4679},95,[105,4681,307],{"emptyLinePlaceholder":306},[105,4683,4685,4688],{"class":107,"line":4684},96,[105,4686,4687],{"class":241},".phantom",[105,4689,116],{"class":115},[105,4691,4693,4695,4697,4699],{"class":107,"line":4692},97,[105,4694,2600],{"class":129},[105,4696,126],{"class":115},[105,4698,1870],{"class":129},[105,4700,145],{"class":115},[105,4702,4704,4706,4708,4710],{"class":107,"line":4703},98,[105,4705,2648],{"class":129},[105,4707,126],{"class":115},[105,4709,1100],{"class":129},[105,4711,145],{"class":115},[105,4713,4715,4717,4719,4721],{"class":107,"line":4714},99,[105,4716,2659],{"class":129},[105,4718,126],{"class":115},[105,4720,1100],{"class":129},[105,4722,145],{"class":115},[105,4724,4726,4729,4731,4733],{"class":107,"line":4725},100,[105,4727,4728],{"class":129},"  right",[105,4730,126],{"class":115},[105,4732,1100],{"class":129},[105,4734,145],{"class":115},[105,4736,4738,4740,4742,4745,4748],{"class":107,"line":4737},101,[105,4739,1777],{"class":129},[105,4741,126],{"class":115},[105,4743,4744],{"class":129},"-1",[105,4746,4747],{"class":115},";            ",[105,4749,4750],{"class":630},"\u002F* 不遮挡内容，只撑高度 *\u002F\n",[105,4752,4754],{"class":107,"line":4753},102,[105,4755,212],{"class":115},[105,4757,4759],{"class":107,"line":4758},103,[105,4760,307],{"emptyLinePlaceholder":306},[105,4762,4764,4767],{"class":107,"line":4763},104,[105,4765,4766],{"class":241},".render-layer",[105,4768,116],{"class":115},[105,4770,4772,4774,4776,4778],{"class":107,"line":4771},105,[105,4773,2600],{"class":129},[105,4775,126],{"class":115},[105,4777,1870],{"class":129},[105,4779,145],{"class":115},[105,4781,4783,4785,4787,4789],{"class":107,"line":4782},106,[105,4784,2648],{"class":129},[105,4786,126],{"class":115},[105,4788,1100],{"class":129},[105,4790,145],{"class":115},[105,4792,4794,4796,4798,4800],{"class":107,"line":4793},107,[105,4795,2659],{"class":129},[105,4797,126],{"class":115},[105,4799,1100],{"class":129},[105,4801,145],{"class":115},[105,4803,4805,4807,4809,4811],{"class":107,"line":4804},108,[105,4806,4728],{"class":129},[105,4808,126],{"class":115},[105,4810,1100],{"class":129},[105,4812,145],{"class":115},[105,4814,4816,4819,4822],{"class":107,"line":4815},109,[105,4817,4818],{"class":129},"  will-change",[105,4820,4821],{"class":115},": transform; ",[105,4823,4824],{"class":630},"\u002F* 提示浏览器优化 *\u002F\n",[105,4826,4828],{"class":107,"line":4827},110,[105,4829,212],{"class":115},[105,4831,4833],{"class":107,"line":4832},111,[105,4834,307],{"emptyLinePlaceholder":306},[105,4836,4838,4841],{"class":107,"line":4837},112,[105,4839,4840],{"class":241},".list-item",[105,4842,116],{"class":115},[105,4844,4846,4849,4851,4854],{"class":107,"line":4845},113,[105,4847,4848],{"class":129},"  box-sizing",[105,4850,126],{"class":115},[105,4852,4853],{"class":129},"border-box",[105,4855,145],{"class":115},[105,4857,4859,4862,4864,4866,4868,4870,4873],{"class":107,"line":4858},114,[105,4860,4861],{"class":129},"  border-bottom",[105,4863,126],{"class":115},[105,4865,861],{"class":129},[105,4867,142],{"class":111},[105,4869,1014],{"class":129},[105,4871,4872],{"class":129}," #eee",[105,4874,145],{"class":115},[105,4876,4878,4880,4882,4885],{"class":107,"line":4877},115,[105,4879,1139],{"class":129},[105,4881,126],{"class":115},[105,4883,4884],{"class":129},"flex",[105,4886,145],{"class":115},[105,4888,4890,4893,4895,4898],{"class":107,"line":4889},116,[105,4891,4892],{"class":129},"  align-items",[105,4894,126],{"class":115},[105,4896,4897],{"class":129},"center",[105,4899,145],{"class":115},[105,4901,4903,4905,4907,4909,4912,4914],{"class":107,"line":4902},117,[105,4904,1761],{"class":129},[105,4906,126],{"class":115},[105,4908,1100],{"class":129},[105,4910,4911],{"class":129}," 12",[105,4913,142],{"class":111},[105,4915,145],{"class":115},[105,4917,4919],{"class":107,"line":4918},118,[105,4920,212],{"class":115},[105,4922,4924,4926,4928],{"class":107,"line":4923},119,[105,4925,2992],{"class":115},[105,4927,3615],{"class":237},[105,4929,257],{"class":115},[10,4931,4932],{"id":4932},"浏览器",[18,4934,4935],{},"多标签通信办法，在同源策略下，可以使用BroadcastChannel API，这个是专门为了这个场景而设计的，比localStorage事件、SharedWorker这些老方案更加干净。",[96,4937,4939],{"className":559,"code":4938,"language":561,"meta":101,"style":101},"const channel = new BroadcastChannel('my-channel')\n",[24,4940,4941],{"__ignoreMap":101},[105,4942,4943,4945,4948,4950,4952,4955,4957,4960],{"class":107,"line":108},[105,4944,568],{"class":111},[105,4946,4947],{"class":129}," channel",[105,4949,574],{"class":111},[105,4951,3478],{"class":111},[105,4953,4954],{"class":241}," BroadcastChannel",[105,4956,321],{"class":115},[105,4958,4959],{"class":248},"'my-channel'",[105,4961,1511],{"class":115},[18,4963,4964],{},"通用的通信逻辑可以放在composables这种，具体的业务响应可以放在组件里：",[96,4966,4968],{"className":3241,"code":4967,"language":3243,"meta":101,"style":101},"\u002F\u002F composables\u002FuseBroadcast.ts —— 通用封装\nexport function useBroadcast\u003CT>(name: string) {\n  const channel = shallowRef\u003CBroadcastChannel | null>(null)\n  const handlers = new Set\u003C(data: T) => void>()\n\n  onMounted(() => {\n    channel.value = new BroadcastChannel(name)\n    channel.value.onmessage = (e) => {\n      handlers.forEach(h => h(e.data))\n    }\n  })\n\n  onUnmounted(() => {\n    channel.value?.close()\n  })\n\n  function post(data: T) {\n    channel.value?.postMessage(data)\n  }\n\n  function on(handler: (data: T) => void) {\n    handlers.add(handler)\n    onUnmounted(() => handlers.delete(handler))\n  }\n\n  return { post, on }\n}\n",[24,4969,4970,4975,5005,5033,5068,5072,5083,5097,5117,5139,5144,5149,5153,5164,5175,5179,5183,5201,5210,5214,5218,5248,5259,5277,5281,5285,5292],{"__ignoreMap":101},[105,4971,4972],{"class":107,"line":108},[105,4973,4974],{"class":630},"\u002F\u002F composables\u002FuseBroadcast.ts —— 通用封装\n",[105,4976,4977,4980,4983,4986,4988,4991,4994,4997,5000,5003],{"class":107,"line":119},[105,4978,4979],{"class":111},"export",[105,4981,4982],{"class":111}," function",[105,4984,4985],{"class":241}," useBroadcast",[105,4987,234],{"class":115},[105,4989,4990],{"class":241},"T",[105,4992,4993],{"class":115},">(",[105,4995,4996],{"class":122},"name",[105,4998,4999],{"class":111},":",[105,5001,5002],{"class":129}," string",[105,5004,1000],{"class":115},[105,5006,5007,5009,5011,5013,5016,5018,5021,5024,5027,5029,5031],{"class":107,"line":133},[105,5008,4101],{"class":111},[105,5010,4947],{"class":129},[105,5012,574],{"class":111},[105,5014,5015],{"class":241}," shallowRef",[105,5017,234],{"class":115},[105,5019,5020],{"class":241},"BroadcastChannel",[105,5022,5023],{"class":111}," |",[105,5025,5026],{"class":129}," null",[105,5028,4993],{"class":115},[105,5030,3987],{"class":129},[105,5032,1511],{"class":115},[105,5034,5035,5037,5040,5042,5044,5047,5050,5053,5055,5058,5060,5062,5065],{"class":107,"line":148},[105,5036,4101],{"class":111},[105,5038,5039],{"class":129}," handlers",[105,5041,574],{"class":111},[105,5043,3478],{"class":111},[105,5045,5046],{"class":241}," Set",[105,5048,5049],{"class":115},"\u003C(",[105,5051,5052],{"class":122},"data",[105,5054,4999],{"class":111},[105,5056,5057],{"class":241}," T",[105,5059,2836],{"class":115},[105,5061,723],{"class":111},[105,5063,5064],{"class":129}," void",[105,5066,5067],{"class":115},">()\n",[105,5069,5070],{"class":107,"line":163},[105,5071,307],{"emptyLinePlaceholder":306},[105,5073,5074,5077,5079,5081],{"class":107,"line":179},[105,5075,5076],{"class":241},"  onMounted",[105,5078,4052],{"class":115},[105,5080,723],{"class":111},[105,5082,116],{"class":115},[105,5084,5085,5088,5090,5092,5094],{"class":107,"line":194},[105,5086,5087],{"class":115},"    channel.value ",[105,5089,245],{"class":111},[105,5091,3478],{"class":111},[105,5093,4954],{"class":241},[105,5095,5096],{"class":115},"(name)\n",[105,5098,5099,5102,5105,5107,5109,5111,5113,5115],{"class":107,"line":209},[105,5100,5101],{"class":115},"    channel.value.",[105,5103,5104],{"class":241},"onmessage",[105,5106,574],{"class":111},[105,5108,4361],{"class":115},[105,5110,3297],{"class":122},[105,5112,2836],{"class":115},[105,5114,723],{"class":111},[105,5116,116],{"class":115},[105,5118,5119,5122,5125,5127,5130,5133,5136],{"class":107,"line":526},[105,5120,5121],{"class":115},"      handlers.",[105,5123,5124],{"class":241},"forEach",[105,5126,321],{"class":115},[105,5128,5129],{"class":122},"h",[105,5131,5132],{"class":111}," =>",[105,5134,5135],{"class":241}," h",[105,5137,5138],{"class":115},"(e.data))\n",[105,5140,5141],{"class":107,"line":531},[105,5142,5143],{"class":115},"    }\n",[105,5145,5146],{"class":107,"line":668},[105,5147,5148],{"class":115},"  })\n",[105,5150,5151],{"class":107,"line":673},[105,5152,307],{"emptyLinePlaceholder":306},[105,5154,5155,5158,5160,5162],{"class":107,"line":679},[105,5156,5157],{"class":241},"  onUnmounted",[105,5159,4052],{"class":115},[105,5161,723],{"class":111},[105,5163,116],{"class":115},[105,5165,5166,5169,5172],{"class":107,"line":689},[105,5167,5168],{"class":115},"    channel.value?.",[105,5170,5171],{"class":241},"close",[105,5173,5174],{"class":115},"()\n",[105,5176,5177],{"class":107,"line":699},[105,5178,5148],{"class":115},[105,5180,5181],{"class":107,"line":704},[105,5182,307],{"emptyLinePlaceholder":306},[105,5184,5185,5188,5191,5193,5195,5197,5199],{"class":107,"line":710},[105,5186,5187],{"class":111},"  function",[105,5189,5190],{"class":241}," post",[105,5192,321],{"class":115},[105,5194,5052],{"class":122},[105,5196,4999],{"class":111},[105,5198,5057],{"class":241},[105,5200,1000],{"class":115},[105,5202,5203,5205,5207],{"class":107,"line":1136},[105,5204,5168],{"class":115},[105,5206,3237],{"class":241},[105,5208,5209],{"class":115},"(data)\n",[105,5211,5212],{"class":107,"line":1149},[105,5213,509],{"class":115},[105,5215,5216],{"class":107,"line":3837},[105,5217,307],{"emptyLinePlaceholder":306},[105,5219,5220,5222,5225,5227,5230,5232,5234,5236,5238,5240,5242,5244,5246],{"class":107,"line":3847},[105,5221,5187],{"class":111},[105,5223,5224],{"class":241}," on",[105,5226,321],{"class":115},[105,5228,5229],{"class":241},"handler",[105,5231,4999],{"class":111},[105,5233,4361],{"class":115},[105,5235,5052],{"class":122},[105,5237,4999],{"class":111},[105,5239,5057],{"class":241},[105,5241,2836],{"class":115},[105,5243,723],{"class":111},[105,5245,5064],{"class":129},[105,5247,1000],{"class":115},[105,5249,5250,5253,5256],{"class":107,"line":3857},[105,5251,5252],{"class":115},"    handlers.",[105,5254,5255],{"class":241},"add",[105,5257,5258],{"class":115},"(handler)\n",[105,5260,5261,5264,5266,5268,5271,5274],{"class":107,"line":3866},[105,5262,5263],{"class":241},"    onUnmounted",[105,5265,4052],{"class":115},[105,5267,723],{"class":111},[105,5269,5270],{"class":115}," handlers.",[105,5272,5273],{"class":241},"delete",[105,5275,5276],{"class":115},"(handler))\n",[105,5278,5279],{"class":107,"line":3871},[105,5280,509],{"class":115},[105,5282,5283],{"class":107,"line":3882},[105,5284,307],{"emptyLinePlaceholder":306},[105,5286,5287,5289],{"class":107,"line":3899},[105,5288,4126],{"class":111},[105,5290,5291],{"class":115}," { post, on }\n",[105,5293,5294],{"class":107,"line":3904},[105,5295,212],{"class":115},[14,5297,5299],{"id":5298},"localstorage加密邪教技术","localStorage加密邪教技术",[18,5301,5302],{},"cookie和LocalStorage是web开发中最常用的两个存储方式，cookie主要保存在内存中，生命周期和浏览器相关，浏览器关闭之后直接消失（会话cookie），持久cookie是保存在硬盘的特定文件里，生命周期是设置的过期时间。",[18,5304,5305],{},"在Chrome这种浏览器中，持久化cookie存储在用户数据目录里面的cookies SQLITE数据库里，例如 C:\\Users\\用户名\\AppData\\Local\\Google\\Chrome\\User Data\\Default\\cookies",[18,5307,5308,5309,5312,5313,5316],{},"然后看看localStorage，这个始终是存储在硬盘里的独立区域的，Chrome呢，存储在User Data\\Default\\Local Storage\\目录里，- ",[21,5310,5311],{},"Firefox","：存储在 ",[24,5314,5315],{},"webappsstore.sqlite"," 文件中。",[18,5318,5319],{},"cookie的原理是http状态管理，服务器在http响应头里可以加Set-Cookie，浏览器收到后就会保存，每一次向同源服务器发请求的时候浏览器都会自动把Cookie塞进Cookie请求头里，每个Cookie最大4KB，每次请求都会携带，太大会拖慢网络，他有安全属性，HttpOnly让JS读不到，Secure只走HTTPS，SameSite可以控制跨站发送。",[18,5321,5322],{},"localStorage的原理是Web Storage API，是纯客户端，数据只会在浏览器本地，永远不会自动发给服务器，也是按源来隔离的，提供了一些同步API，可以通过setItem、getItem、removeItem等方法直接读写键值对。容量更大，通常为5MB左右，远大于Cookie的4KB，只存字符串，存对象需要先JSON.stringify，取出来再JSON.parse。",[18,5324,5325,5326,31,5329,5332],{},"聊完了之前技术，有一个好玩的东西，有些库",[24,5327,5328],{},"secure-ls",[24,5330,5331],{},"@cyberadityacode\u002Fsecure-ls"," 等可以让我们在localSotrage中存储加密数据，用起来和普通的localStorage差不多，原理是在存入前加密，库会封装setItem和getItem，传入的对象会被AES之类的算法加密成密文，再存进localStorage中，读取的时候自动解密，给我们原始对象。",[96,5334,5336],{"className":3241,"code":5335,"language":3243,"meta":101,"style":101},"\u002F\u002F 普通 localStorage\nlocalStorage.setItem('user', JSON.stringify(user))\n\n\u002F\u002F 加密库的用法（API 类似，但底层自动加解密）\nawait storage.setItem('user', user)  \u002F\u002F 存进去的是密文\nconst user = await storage.getItem('user')  \u002F\u002F 取出来是明文对象\n",[24,5337,5338,5343,5370,5374,5379,5399],{"__ignoreMap":101},[105,5339,5340],{"class":107,"line":108},[105,5341,5342],{"class":630},"\u002F\u002F 普通 localStorage\n",[105,5344,5345,5348,5351,5353,5356,5358,5361,5364,5367],{"class":107,"line":119},[105,5346,5347],{"class":115},"localStorage.",[105,5349,5350],{"class":241},"setItem",[105,5352,321],{"class":115},[105,5354,5355],{"class":248},"'user'",[105,5357,286],{"class":115},[105,5359,5360],{"class":129},"JSON",[105,5362,5363],{"class":115},".",[105,5365,5366],{"class":241},"stringify",[105,5368,5369],{"class":115},"(user))\n",[105,5371,5372],{"class":107,"line":133},[105,5373,307],{"emptyLinePlaceholder":306},[105,5375,5376],{"class":107,"line":148},[105,5377,5378],{"class":630},"\u002F\u002F 加密库的用法（API 类似，但底层自动加解密）\n",[105,5380,5381,5384,5387,5389,5391,5393,5396],{"class":107,"line":163},[105,5382,5383],{"class":111},"await",[105,5385,5386],{"class":115}," storage.",[105,5388,5350],{"class":241},[105,5390,321],{"class":115},[105,5392,5355],{"class":248},[105,5394,5395],{"class":115},", user)  ",[105,5397,5398],{"class":630},"\u002F\u002F 存进去的是密文\n",[105,5400,5401,5403,5406,5408,5411,5413,5416,5418,5420,5423],{"class":107,"line":179},[105,5402,568],{"class":111},[105,5404,5405],{"class":129}," user",[105,5407,574],{"class":111},[105,5409,5410],{"class":111}," await",[105,5412,5386],{"class":115},[105,5414,5415],{"class":241},"getItem",[105,5417,321],{"class":115},[105,5419,5355],{"class":248},[105,5421,5422],{"class":115},")  ",[105,5424,5425],{"class":630},"\u002F\u002F 取出来是明文对象\n",[14,5427,5429],{"id":5428},"service-worker","Service Worker",[18,5431,5432],{},"HTML5的离线存储，也叫做Application Cache，也在已经废弃，继任者叫做Service Worker，ServiceWorker是一个在浏览器后台运行的独立脚本，充当网络请求的代理，可以拦截页面所有请求，并决定是返回缓存资源还是去网络获取，使用分为三步：",[18,5434,5435],{},"注册、安装于缓存、拦截和响应。",[18,5437,5438],{},"注册会在js中注册一个service worker的脚本，安装和缓存会在serviceWorker的install事件中，将需要离线使用的核心资源比如html css js加入到缓存，拦截和响应会在fetch事件中，拦截页面的网络请求，可以制定策略，比如说缓存优先，看看有没有资源，没有再去请求网络，或者网络优先，先请求，失败再回退缓存。",[18,5440,5441,5442,5445],{},"相比 AppCache，Service Worker 提供了",[21,5443,5444],{},"更精细、更灵活的控制能力","。你可以精确管理缓存的版本、更新策略，并且它完全通过 JavaScript 编程实现，不再依赖那个容易出错的 manifest 文件。",[18,5447,5448],{},"service worker不会阻塞主线程，也不能访问dom，靠事件唤醒，空闲时候被终止，能够拦截fetch，再决定怎么响应。注册之后可以长期存在，页面关了也在，必须https或者localhost，只能控制注册路径和子路径。",[18,5450,5451],{},"有service worker之后可以决定",[96,5453,5456],{"className":5454,"code":5455,"language":392},[390],"页面 → 浏览器 → Service Worker（拦截）→ 决定：\n                    ├─ 用缓存响应\n                    ├─ 走网络\n                    └─ 自定义响应\n",[24,5457,5455],{"__ignoreMap":101},[18,5459,5460],{},"Service Worker 的生命周期和普通脚本完全不同，分几个阶段：",[18,5462,5463],{},"首先是注册：",[96,5465,5467],{"className":559,"code":5466,"language":561,"meta":101,"style":101},"\u002F\u002F 主线程\nif ('serviceWorker' in navigator) {\n    navigator.serviceWorker.register('\u002Fsw.js')\n        .then(reg => console.log('注册成功', reg))\n        .catch(err => console.log('注册失败', err));\n}\n",[24,5468,5469,5474,5490,5505,5533,5559],{"__ignoreMap":101},[105,5470,5471],{"class":107,"line":108},[105,5472,5473],{"class":630},"\u002F\u002F 主线程\n",[105,5475,5476,5479,5481,5484,5487],{"class":107,"line":119},[105,5477,5478],{"class":111},"if",[105,5480,4361],{"class":115},[105,5482,5483],{"class":248},"'serviceWorker'",[105,5485,5486],{"class":111}," in",[105,5488,5489],{"class":115}," navigator) {\n",[105,5491,5492,5495,5498,5500,5503],{"class":107,"line":133},[105,5493,5494],{"class":115},"    navigator.serviceWorker.",[105,5496,5497],{"class":241},"register",[105,5499,321],{"class":115},[105,5501,5502],{"class":248},"'\u002Fsw.js'",[105,5504,1511],{"class":115},[105,5506,5507,5510,5513,5515,5518,5520,5523,5525,5527,5530],{"class":107,"line":148},[105,5508,5509],{"class":115},"        .",[105,5511,5512],{"class":241},"then",[105,5514,321],{"class":115},[105,5516,5517],{"class":122},"reg",[105,5519,5132],{"class":111},[105,5521,5522],{"class":115}," console.",[105,5524,639],{"class":241},[105,5526,321],{"class":115},[105,5528,5529],{"class":248},"'注册成功'",[105,5531,5532],{"class":115},", reg))\n",[105,5534,5535,5537,5540,5542,5545,5547,5549,5551,5553,5556],{"class":107,"line":163},[105,5536,5509],{"class":115},[105,5538,5539],{"class":241},"catch",[105,5541,321],{"class":115},[105,5543,5544],{"class":122},"err",[105,5546,5132],{"class":111},[105,5548,5522],{"class":115},[105,5550,639],{"class":241},[105,5552,321],{"class":115},[105,5554,5555],{"class":248},"'注册失败'",[105,5557,5558],{"class":115},", err));\n",[105,5560,5561],{"class":107,"line":179},[105,5562,212],{"class":115},[18,5564,5565],{},"注册的时候文件不会加载，不会立即生效，然后install的时候才会。",[96,5567,5569],{"className":559,"code":5568,"language":561,"meta":101,"style":101},"\u002F\u002F sw.js\nself.addEventListener('install', (event) => {\n    event.waitUntil(\n        caches.open('v1').then(cache => {\n            return cache.addAll([\n                '\u002F',\n                '\u002Findex.html',\n                '\u002Fstyle.css',\n                '\u002Fapp.js'\n            ]);\n        })\n    );\n});\n",[24,5570,5571,5576,5599,5609,5636,5650,5657,5664,5671,5676,5681,5686,5691],{"__ignoreMap":101},[105,5572,5573],{"class":107,"line":108},[105,5574,5575],{"class":630},"\u002F\u002F sw.js\n",[105,5577,5578,5581,5583,5585,5588,5590,5593,5595,5597],{"class":107,"line":119},[105,5579,5580],{"class":115},"self.",[105,5582,3286],{"class":241},[105,5584,321],{"class":115},[105,5586,5587],{"class":248},"'install'",[105,5589,3294],{"class":115},[105,5591,5592],{"class":122},"event",[105,5594,2836],{"class":115},[105,5596,723],{"class":111},[105,5598,116],{"class":115},[105,5600,5601,5604,5607],{"class":107,"line":133},[105,5602,5603],{"class":115},"    event.",[105,5605,5606],{"class":241},"waitUntil",[105,5608,583],{"class":115},[105,5610,5611,5614,5617,5619,5622,5625,5627,5629,5632,5634],{"class":107,"line":148},[105,5612,5613],{"class":115},"        caches.",[105,5615,5616],{"class":241},"open",[105,5618,321],{"class":115},[105,5620,5621],{"class":248},"'v1'",[105,5623,5624],{"class":115},").",[105,5626,5512],{"class":241},[105,5628,321],{"class":115},[105,5630,5631],{"class":122},"cache",[105,5633,5132],{"class":111},[105,5635,116],{"class":115},[105,5637,5638,5641,5644,5647],{"class":107,"line":163},[105,5639,5640],{"class":111},"            return",[105,5642,5643],{"class":115}," cache.",[105,5645,5646],{"class":241},"addAll",[105,5648,5649],{"class":115},"([\n",[105,5651,5652,5655],{"class":107,"line":179},[105,5653,5654],{"class":248},"                '\u002F'",[105,5656,2849],{"class":115},[105,5658,5659,5662],{"class":107,"line":194},[105,5660,5661],{"class":248},"                '\u002Findex.html'",[105,5663,2849],{"class":115},[105,5665,5666,5669],{"class":107,"line":209},[105,5667,5668],{"class":248},"                '\u002Fstyle.css'",[105,5670,2849],{"class":115},[105,5672,5673],{"class":107,"line":526},[105,5674,5675],{"class":248},"                '\u002Fapp.js'\n",[105,5677,5678],{"class":107,"line":531},[105,5679,5680],{"class":115},"            ]);\n",[105,5682,5683],{"class":107,"line":668},[105,5684,5685],{"class":115},"        })\n",[105,5687,5688],{"class":107,"line":673},[105,5689,5690],{"class":115},"    );\n",[105,5692,5693],{"class":107,"line":679},[105,5694,3964],{"class":115},[18,5696,5697,5698,5701],{},"install事件在sw首次注册的时候或者文件内容变化的时候触发，通常在预缓存静态资源。 ",[24,5699,5700],{},"event.waitUntil(promise)","让浏览器\"等这个 Promise 完成再算安装成功\"。然后是activate激活阶段：",[96,5703,5705],{"className":559,"code":5704,"language":561,"meta":101,"style":101},"self.addEventListener('activate', (event) => {\n    event.waitUntil(\n        caches.keys().then(keys => {\n            return Promise.all(\n                keys.filter(k => k !== 'v1')\n                    .map(k => caches.delete(k))   \u002F\u002F 清理旧缓存\n            );\n        })\n    );\n});\n",[24,5706,5707,5728,5736,5756,5770,5795,5820,5825,5829,5833],{"__ignoreMap":101},[105,5708,5709,5711,5713,5715,5718,5720,5722,5724,5726],{"class":107,"line":108},[105,5710,5580],{"class":115},[105,5712,3286],{"class":241},[105,5714,321],{"class":115},[105,5716,5717],{"class":248},"'activate'",[105,5719,3294],{"class":115},[105,5721,5592],{"class":122},[105,5723,2836],{"class":115},[105,5725,723],{"class":111},[105,5727,116],{"class":115},[105,5729,5730,5732,5734],{"class":107,"line":119},[105,5731,5603],{"class":115},[105,5733,5606],{"class":241},[105,5735,583],{"class":115},[105,5737,5738,5740,5743,5746,5748,5750,5752,5754],{"class":107,"line":133},[105,5739,5613],{"class":115},[105,5741,5742],{"class":241},"keys",[105,5744,5745],{"class":115},"().",[105,5747,5512],{"class":241},[105,5749,321],{"class":115},[105,5751,5742],{"class":122},[105,5753,5132],{"class":111},[105,5755,116],{"class":115},[105,5757,5758,5760,5763,5765,5768],{"class":107,"line":148},[105,5759,5640],{"class":111},[105,5761,5762],{"class":129}," Promise",[105,5764,5363],{"class":115},[105,5766,5767],{"class":241},"all",[105,5769,583],{"class":115},[105,5771,5772,5775,5778,5780,5783,5785,5788,5790,5793],{"class":107,"line":163},[105,5773,5774],{"class":115},"                keys.",[105,5776,5777],{"class":241},"filter",[105,5779,321],{"class":115},[105,5781,5782],{"class":122},"k",[105,5784,5132],{"class":111},[105,5786,5787],{"class":115}," k ",[105,5789,3314],{"class":111},[105,5791,5792],{"class":248}," 'v1'",[105,5794,1511],{"class":115},[105,5796,5797,5800,5803,5805,5807,5809,5812,5814,5817],{"class":107,"line":179},[105,5798,5799],{"class":115},"                    .",[105,5801,5802],{"class":241},"map",[105,5804,321],{"class":115},[105,5806,5782],{"class":122},[105,5808,5132],{"class":111},[105,5810,5811],{"class":115}," caches.",[105,5813,5273],{"class":241},[105,5815,5816],{"class":115},"(k))   ",[105,5818,5819],{"class":630},"\u002F\u002F 清理旧缓存\n",[105,5821,5822],{"class":107,"line":194},[105,5823,5824],{"class":115},"            );\n",[105,5826,5827],{"class":107,"line":209},[105,5828,5685],{"class":115},[105,5830,5831],{"class":107,"line":526},[105,5832,5690],{"class":115},[105,5834,5835],{"class":107,"line":531},[105,5836,3964],{"class":115},[18,5838,5839,5842,5843,5846],{},[24,5840,5841],{},"activate"," 在安装成功后、SW 正式接管页面前触发。通常在这里",[21,5844,5845],{},"清理旧版本缓存","。最后是接管：",[96,5848,5850],{"className":559,"code":5849,"language":561,"meta":101,"style":101},"self.addEventListener('activate', (event) => {\n    event.waitUntil(self.clients.claim());   \u002F\u002F 立即接管\n});\n",[24,5851,5852,5872,5890],{"__ignoreMap":101},[105,5853,5854,5856,5858,5860,5862,5864,5866,5868,5870],{"class":107,"line":108},[105,5855,5580],{"class":115},[105,5857,3286],{"class":241},[105,5859,321],{"class":115},[105,5861,5717],{"class":248},[105,5863,3294],{"class":115},[105,5865,5592],{"class":122},[105,5867,2836],{"class":115},[105,5869,723],{"class":111},[105,5871,116],{"class":115},[105,5873,5874,5876,5878,5881,5884,5887],{"class":107,"line":119},[105,5875,5603],{"class":115},[105,5877,5606],{"class":241},[105,5879,5880],{"class":115},"(self.clients.",[105,5882,5883],{"class":241},"claim",[105,5885,5886],{"class":115},"());   ",[105,5888,5889],{"class":630},"\u002F\u002F 立即接管\n",[105,5891,5892],{"class":107,"line":133},[105,5893,3964],{"class":115},[18,5895,5896],{},"默认情况下，sw安装不会立即开始接管已经打开的窗口，需要等待页面刷新，使用这个情况我们可以在这里激活后直接接管。\n之后service worker就可以帮我们来完成一系列工作了，比如说拦截请求：",[96,5898,5900],{"className":559,"code":5899,"language":561,"meta":101,"style":101},"self.addEventListener('fetch', (event) => {\n    event.respondWith(\n        caches.match(event.request).then(cached => {\n            \u002F\u002F 缓存命中 → 用缓存\n            if (cached) return cached;\n            \u002F\u002F 没命中 → 走网络\n            return fetch(event.request);\n        })\n    );\n});\n",[24,5901,5902,5923,5932,5953,5958,5971,5976,5986,5990,5994],{"__ignoreMap":101},[105,5903,5904,5906,5908,5910,5913,5915,5917,5919,5921],{"class":107,"line":108},[105,5905,5580],{"class":115},[105,5907,3286],{"class":241},[105,5909,321],{"class":115},[105,5911,5912],{"class":248},"'fetch'",[105,5914,3294],{"class":115},[105,5916,5592],{"class":122},[105,5918,2836],{"class":115},[105,5920,723],{"class":111},[105,5922,116],{"class":115},[105,5924,5925,5927,5930],{"class":107,"line":119},[105,5926,5603],{"class":115},[105,5928,5929],{"class":241},"respondWith",[105,5931,583],{"class":115},[105,5933,5934,5936,5939,5942,5944,5946,5949,5951],{"class":107,"line":133},[105,5935,5613],{"class":115},[105,5937,5938],{"class":241},"match",[105,5940,5941],{"class":115},"(event.request).",[105,5943,5512],{"class":241},[105,5945,321],{"class":115},[105,5947,5948],{"class":122},"cached",[105,5950,5132],{"class":111},[105,5952,116],{"class":115},[105,5954,5955],{"class":107,"line":148},[105,5956,5957],{"class":630},"            \u002F\u002F 缓存命中 → 用缓存\n",[105,5959,5960,5963,5966,5968],{"class":107,"line":163},[105,5961,5962],{"class":111},"            if",[105,5964,5965],{"class":115}," (cached) ",[105,5967,3322],{"class":111},[105,5969,5970],{"class":115}," cached;\n",[105,5972,5973],{"class":107,"line":179},[105,5974,5975],{"class":630},"            \u002F\u002F 没命中 → 走网络\n",[105,5977,5978,5980,5983],{"class":107,"line":194},[105,5979,5640],{"class":111},[105,5981,5982],{"class":241}," fetch",[105,5984,5985],{"class":115},"(event.request);\n",[105,5987,5988],{"class":107,"line":209},[105,5989,5685],{"class":115},[105,5991,5992],{"class":107,"line":526},[105,5993,5690],{"class":115},[105,5995,5996],{"class":107,"line":531},[105,5997,3964],{"class":115},[18,5999,6000],{},"我们可以在这里设置一个缓存优先策略\u002F网络优先策略，这个是fetch事件的常见写法，适合需要最新数据但是也要离线兜底的接口。",[96,6002,6004],{"className":559,"code":6003,"language":561,"meta":101,"style":101},"self.addEventListener('fetch', (event) => {\n    event.respondWith(\n        caches.match(event.request).then(cached => {\n            return cached || fetch(event.request);\n        })\n    );\n}); \u002F\u002F 缓存\n\nself.addEventListener('fetch', (event) => {\n    event.respondWith(\n        fetch(event.request)\n            .then(response => {\n                \u002F\u002F 网络成功，更新缓存\n                const clone = response.clone();\n                caches.open('v1').then(cache => cache.put(event.request, clone));\n                return response;\n            })\n            .catch(() => caches.match(event.request))   \u002F\u002F 网络失败用缓存\n    );\n});\n",[24,6005,6006,6026,6034,6052,6066,6070,6074,6082,6086,6106,6114,6122,6138,6143,6161,6190,6198,6203,6223,6227],{"__ignoreMap":101},[105,6007,6008,6010,6012,6014,6016,6018,6020,6022,6024],{"class":107,"line":108},[105,6009,5580],{"class":115},[105,6011,3286],{"class":241},[105,6013,321],{"class":115},[105,6015,5912],{"class":248},[105,6017,3294],{"class":115},[105,6019,5592],{"class":122},[105,6021,2836],{"class":115},[105,6023,723],{"class":111},[105,6025,116],{"class":115},[105,6027,6028,6030,6032],{"class":107,"line":119},[105,6029,5603],{"class":115},[105,6031,5929],{"class":241},[105,6033,583],{"class":115},[105,6035,6036,6038,6040,6042,6044,6046,6048,6050],{"class":107,"line":133},[105,6037,5613],{"class":115},[105,6039,5938],{"class":241},[105,6041,5941],{"class":115},[105,6043,5512],{"class":241},[105,6045,321],{"class":115},[105,6047,5948],{"class":122},[105,6049,5132],{"class":111},[105,6051,116],{"class":115},[105,6053,6054,6056,6059,6062,6064],{"class":107,"line":148},[105,6055,5640],{"class":111},[105,6057,6058],{"class":115}," cached ",[105,6060,6061],{"class":111},"||",[105,6063,5982],{"class":241},[105,6065,5985],{"class":115},[105,6067,6068],{"class":107,"line":163},[105,6069,5685],{"class":115},[105,6071,6072],{"class":107,"line":179},[105,6073,5690],{"class":115},[105,6075,6076,6079],{"class":107,"line":194},[105,6077,6078],{"class":115},"}); ",[105,6080,6081],{"class":630},"\u002F\u002F 缓存\n",[105,6083,6084],{"class":107,"line":209},[105,6085,307],{"emptyLinePlaceholder":306},[105,6087,6088,6090,6092,6094,6096,6098,6100,6102,6104],{"class":107,"line":526},[105,6089,5580],{"class":115},[105,6091,3286],{"class":241},[105,6093,321],{"class":115},[105,6095,5912],{"class":248},[105,6097,3294],{"class":115},[105,6099,5592],{"class":122},[105,6101,2836],{"class":115},[105,6103,723],{"class":111},[105,6105,116],{"class":115},[105,6107,6108,6110,6112],{"class":107,"line":531},[105,6109,5603],{"class":115},[105,6111,5929],{"class":241},[105,6113,583],{"class":115},[105,6115,6116,6119],{"class":107,"line":668},[105,6117,6118],{"class":241},"        fetch",[105,6120,6121],{"class":115},"(event.request)\n",[105,6123,6124,6127,6129,6131,6134,6136],{"class":107,"line":673},[105,6125,6126],{"class":115},"            .",[105,6128,5512],{"class":241},[105,6130,321],{"class":115},[105,6132,6133],{"class":122},"response",[105,6135,5132],{"class":111},[105,6137,116],{"class":115},[105,6139,6140],{"class":107,"line":679},[105,6141,6142],{"class":630},"                \u002F\u002F 网络成功，更新缓存\n",[105,6144,6145,6148,6151,6153,6156,6159],{"class":107,"line":689},[105,6146,6147],{"class":111},"                const",[105,6149,6150],{"class":129}," clone",[105,6152,574],{"class":111},[105,6154,6155],{"class":115}," response.",[105,6157,6158],{"class":241},"clone",[105,6160,665],{"class":115},[105,6162,6163,6166,6168,6170,6172,6174,6176,6178,6180,6182,6184,6187],{"class":107,"line":699},[105,6164,6165],{"class":115},"                caches.",[105,6167,5616],{"class":241},[105,6169,321],{"class":115},[105,6171,5621],{"class":248},[105,6173,5624],{"class":115},[105,6175,5512],{"class":241},[105,6177,321],{"class":115},[105,6179,5631],{"class":122},[105,6181,5132],{"class":111},[105,6183,5643],{"class":115},[105,6185,6186],{"class":241},"put",[105,6188,6189],{"class":115},"(event.request, clone));\n",[105,6191,6192,6195],{"class":107,"line":704},[105,6193,6194],{"class":111},"                return",[105,6196,6197],{"class":115}," response;\n",[105,6199,6200],{"class":107,"line":710},[105,6201,6202],{"class":115},"            })\n",[105,6204,6205,6207,6209,6211,6213,6215,6217,6220],{"class":107,"line":1136},[105,6206,6126],{"class":115},[105,6208,5539],{"class":241},[105,6210,4052],{"class":115},[105,6212,723],{"class":111},[105,6214,5811],{"class":115},[105,6216,5938],{"class":241},[105,6218,6219],{"class":115},"(event.request))   ",[105,6221,6222],{"class":630},"\u002F\u002F 网络失败用缓存\n",[105,6224,6225],{"class":107,"line":1149},[105,6226,5690],{"class":115},[105,6228,6229],{"class":107,"line":3837},[105,6230,3964],{"class":115},[18,6232,6233],{},"如果是能够短暂接受旧数据，但是还是想尽快响应的，场景，可以用缓存优先+后台更新：",[96,6235,6237],{"className":559,"code":6236,"language":561,"meta":101,"style":101},"self.addEventListener('fetch', (event) => {\n    event.respondWith(\n        caches.match(event.request).then(cached => {\n            const fetchPromise = fetch(event.request).then(response => {\n                const clone = response.clone();\n                caches.open('v1').then(cache => cache.put(event.request, clone));\n                return response;\n            });\n            return cached || fetchPromise;   \u002F\u002F 立即返回缓存，后台更新\n        })\n    );\n});\n",[24,6238,6239,6259,6267,6285,6309,6323,6349,6355,6360,6374,6378,6382],{"__ignoreMap":101},[105,6240,6241,6243,6245,6247,6249,6251,6253,6255,6257],{"class":107,"line":108},[105,6242,5580],{"class":115},[105,6244,3286],{"class":241},[105,6246,321],{"class":115},[105,6248,5912],{"class":248},[105,6250,3294],{"class":115},[105,6252,5592],{"class":122},[105,6254,2836],{"class":115},[105,6256,723],{"class":111},[105,6258,116],{"class":115},[105,6260,6261,6263,6265],{"class":107,"line":119},[105,6262,5603],{"class":115},[105,6264,5929],{"class":241},[105,6266,583],{"class":115},[105,6268,6269,6271,6273,6275,6277,6279,6281,6283],{"class":107,"line":133},[105,6270,5613],{"class":115},[105,6272,5938],{"class":241},[105,6274,5941],{"class":115},[105,6276,5512],{"class":241},[105,6278,321],{"class":115},[105,6280,5948],{"class":122},[105,6282,5132],{"class":111},[105,6284,116],{"class":115},[105,6286,6287,6290,6293,6295,6297,6299,6301,6303,6305,6307],{"class":107,"line":148},[105,6288,6289],{"class":111},"            const",[105,6291,6292],{"class":129}," fetchPromise",[105,6294,574],{"class":111},[105,6296,5982],{"class":241},[105,6298,5941],{"class":115},[105,6300,5512],{"class":241},[105,6302,321],{"class":115},[105,6304,6133],{"class":122},[105,6306,5132],{"class":111},[105,6308,116],{"class":115},[105,6310,6311,6313,6315,6317,6319,6321],{"class":107,"line":163},[105,6312,6147],{"class":111},[105,6314,6150],{"class":129},[105,6316,574],{"class":111},[105,6318,6155],{"class":115},[105,6320,6158],{"class":241},[105,6322,665],{"class":115},[105,6324,6325,6327,6329,6331,6333,6335,6337,6339,6341,6343,6345,6347],{"class":107,"line":179},[105,6326,6165],{"class":115},[105,6328,5616],{"class":241},[105,6330,321],{"class":115},[105,6332,5621],{"class":248},[105,6334,5624],{"class":115},[105,6336,5512],{"class":241},[105,6338,321],{"class":115},[105,6340,5631],{"class":122},[105,6342,5132],{"class":111},[105,6344,5643],{"class":115},[105,6346,6186],{"class":241},[105,6348,6189],{"class":115},[105,6350,6351,6353],{"class":107,"line":194},[105,6352,6194],{"class":111},[105,6354,6197],{"class":115},[105,6356,6357],{"class":107,"line":209},[105,6358,6359],{"class":115},"            });\n",[105,6361,6362,6364,6366,6368,6371],{"class":107,"line":526},[105,6363,5640],{"class":111},[105,6365,6058],{"class":115},[105,6367,6061],{"class":111},[105,6369,6370],{"class":115}," fetchPromise;   ",[105,6372,6373],{"class":630},"\u002F\u002F 立即返回缓存，后台更新\n",[105,6375,6376],{"class":107,"line":531},[105,6377,5685],{"class":115},[105,6379,6380],{"class":107,"line":668},[105,6381,5690],{"class":115},[105,6383,6384],{"class":107,"line":673},[105,6385,3964],{"class":115},[18,6387,6388],{},"sw里面可以用CacheStorage来管理缓存：",[96,6390,6392],{"className":559,"code":6391,"language":561,"meta":101,"style":101},"\u002F\u002F 打开\u002F创建缓存\nconst cache = await caches.open('v1');\n\n\u002F\u002F 添加\nawait cache.add('\u002Findex.html');\nawait cache.addAll(['\u002Fa.js', '\u002Fb.css']);\nawait cache.put(request, response);   \u002F\u002F 手动放入\n\n\u002F\u002F 查询\nconst response = await cache.match('\u002Findex.html');\nconst responses = await cache.matchAll();\n\n\u002F\u002F 删除\nawait cache.delete('\u002Findex.html');\n\n\u002F\u002F 列出所有缓存\nconst keys = await caches.keys();\n\n\u002F\u002F 删除整个缓存\nawait caches.delete('v1');\n",[24,6393,6394,6399,6420,6424,6429,6444,6466,6480,6484,6489,6510,6528,6532,6537,6551,6555,6560,6577,6581,6586],{"__ignoreMap":101},[105,6395,6396],{"class":107,"line":108},[105,6397,6398],{"class":630},"\u002F\u002F 打开\u002F创建缓存\n",[105,6400,6401,6403,6406,6408,6410,6412,6414,6416,6418],{"class":107,"line":119},[105,6402,568],{"class":111},[105,6404,6405],{"class":129}," cache",[105,6407,574],{"class":111},[105,6409,5410],{"class":111},[105,6411,5811],{"class":115},[105,6413,5616],{"class":241},[105,6415,321],{"class":115},[105,6417,5621],{"class":248},[105,6419,327],{"class":115},[105,6421,6422],{"class":107,"line":133},[105,6423,307],{"emptyLinePlaceholder":306},[105,6425,6426],{"class":107,"line":148},[105,6427,6428],{"class":630},"\u002F\u002F 添加\n",[105,6430,6431,6433,6435,6437,6439,6442],{"class":107,"line":163},[105,6432,5383],{"class":111},[105,6434,5643],{"class":115},[105,6436,5255],{"class":241},[105,6438,321],{"class":115},[105,6440,6441],{"class":248},"'\u002Findex.html'",[105,6443,327],{"class":115},[105,6445,6446,6448,6450,6452,6455,6458,6460,6463],{"class":107,"line":179},[105,6447,5383],{"class":111},[105,6449,5643],{"class":115},[105,6451,5646],{"class":241},[105,6453,6454],{"class":115},"([",[105,6456,6457],{"class":248},"'\u002Fa.js'",[105,6459,286],{"class":115},[105,6461,6462],{"class":248},"'\u002Fb.css'",[105,6464,6465],{"class":115},"]);\n",[105,6467,6468,6470,6472,6474,6477],{"class":107,"line":194},[105,6469,5383],{"class":111},[105,6471,5643],{"class":115},[105,6473,6186],{"class":241},[105,6475,6476],{"class":115},"(request, response);   ",[105,6478,6479],{"class":630},"\u002F\u002F 手动放入\n",[105,6481,6482],{"class":107,"line":209},[105,6483,307],{"emptyLinePlaceholder":306},[105,6485,6486],{"class":107,"line":526},[105,6487,6488],{"class":630},"\u002F\u002F 查询\n",[105,6490,6491,6493,6496,6498,6500,6502,6504,6506,6508],{"class":107,"line":531},[105,6492,568],{"class":111},[105,6494,6495],{"class":129}," response",[105,6497,574],{"class":111},[105,6499,5410],{"class":111},[105,6501,5643],{"class":115},[105,6503,5938],{"class":241},[105,6505,321],{"class":115},[105,6507,6441],{"class":248},[105,6509,327],{"class":115},[105,6511,6512,6514,6517,6519,6521,6523,6526],{"class":107,"line":668},[105,6513,568],{"class":111},[105,6515,6516],{"class":129}," responses",[105,6518,574],{"class":111},[105,6520,5410],{"class":111},[105,6522,5643],{"class":115},[105,6524,6525],{"class":241},"matchAll",[105,6527,665],{"class":115},[105,6529,6530],{"class":107,"line":673},[105,6531,307],{"emptyLinePlaceholder":306},[105,6533,6534],{"class":107,"line":679},[105,6535,6536],{"class":630},"\u002F\u002F 删除\n",[105,6538,6539,6541,6543,6545,6547,6549],{"class":107,"line":689},[105,6540,5383],{"class":111},[105,6542,5643],{"class":115},[105,6544,5273],{"class":241},[105,6546,321],{"class":115},[105,6548,6441],{"class":248},[105,6550,327],{"class":115},[105,6552,6553],{"class":107,"line":699},[105,6554,307],{"emptyLinePlaceholder":306},[105,6556,6557],{"class":107,"line":704},[105,6558,6559],{"class":630},"\u002F\u002F 列出所有缓存\n",[105,6561,6562,6564,6567,6569,6571,6573,6575],{"class":107,"line":710},[105,6563,568],{"class":111},[105,6565,6566],{"class":129}," keys",[105,6568,574],{"class":111},[105,6570,5410],{"class":111},[105,6572,5811],{"class":115},[105,6574,5742],{"class":241},[105,6576,665],{"class":115},[105,6578,6579],{"class":107,"line":1136},[105,6580,307],{"emptyLinePlaceholder":306},[105,6582,6583],{"class":107,"line":1149},[105,6584,6585],{"class":630},"\u002F\u002F 删除整个缓存\n",[105,6587,6588,6590,6592,6594,6596,6598],{"class":107,"line":3837},[105,6589,5383],{"class":111},[105,6591,5811],{"class":115},[105,6593,5273],{"class":241},[105,6595,321],{"class":115},[105,6597,5621],{"class":248},[105,6599,327],{"class":115},[18,6601,6602],{},"sw和页面不能直接通信，需要使用postMessage：",[96,6604,6606],{"className":559,"code":6605,"language":561,"meta":101,"style":101},"\u002F\u002F 主线程\nnavigator.serviceWorker.controller.postMessage({\n    type: 'SKIP_WAITING'\n});\n\nnavigator.serviceWorker.addEventListener('message', (event) => {\n    console.log('收到 SW 消息', event.data);\n});\n",[24,6607,6608,6612,6621,6629,6633,6637,6658,6673],{"__ignoreMap":101},[105,6609,6610],{"class":107,"line":108},[105,6611,5473],{"class":630},[105,6613,6614,6617,6619],{"class":107,"line":119},[105,6615,6616],{"class":115},"navigator.serviceWorker.controller.",[105,6618,3237],{"class":241},[105,6620,3917],{"class":115},[105,6622,6623,6626],{"class":107,"line":133},[105,6624,6625],{"class":115},"    type: ",[105,6627,6628],{"class":248},"'SKIP_WAITING'\n",[105,6630,6631],{"class":107,"line":148},[105,6632,3964],{"class":115},[105,6634,6635],{"class":107,"line":163},[105,6636,307],{"emptyLinePlaceholder":306},[105,6638,6639,6642,6644,6646,6648,6650,6652,6654,6656],{"class":107,"line":179},[105,6640,6641],{"class":115},"navigator.serviceWorker.",[105,6643,3286],{"class":241},[105,6645,321],{"class":115},[105,6647,3291],{"class":248},[105,6649,3294],{"class":115},[105,6651,5592],{"class":122},[105,6653,2836],{"class":115},[105,6655,723],{"class":111},[105,6657,116],{"class":115},[105,6659,6660,6663,6665,6667,6670],{"class":107,"line":194},[105,6661,6662],{"class":115},"    console.",[105,6664,639],{"class":241},[105,6666,321],{"class":115},[105,6668,6669],{"class":248},"'收到 SW 消息'",[105,6671,6672],{"class":115},", event.data);\n",[105,6674,6675],{"class":107,"line":209},[105,6676,3964],{"class":115},[18,6678,6679],{},"这个架构设计其实普遍常见，可以看做后端架构里的Worker，比如说Cloudflare Worker或者 Web Workers、后台任务队列：",[442,6681,6682,6687,6692,6697,6702],{},[448,6683,6684],{},[21,6685,6686],{},"独立执行单元",[448,6688,6689],{},[21,6690,6691],{},"事件驱动",[448,6693,6694],{},[21,6695,6696],{},"无状态或短生命周期",[448,6698,6699],{},[21,6700,6701],{},"通过消息通信",[448,6703,6704,6707,91],{},[21,6705,6706],{},"不直接访问主进程资源",[21,6708,6709],{},"Service Worker 完全符合这个模型",[18,6711,6712,6713,6716,6717,6720,6721,31,6724,6727],{},"提一嘴，",[24,6714,6715],{},"CacheStorage"," 存在",[21,6718,6719],{},"磁盘","上，和 ",[24,6722,6723],{},"IndexedDB",[24,6725,6726],{},"localStorage"," 一样是持久化存储。",[18,6729,6730,6731,6734,6735,6738],{},"在vite项目中，我们可以使用vue专用sw包装的插件vite-plugin-pwa，它通过一个虚拟模块 ",[24,6732,6733],{},"virtual:pwa-register\u002Fvue","，提供了一个专门给 Vue 3 用的 ",[24,6736,6737],{},"useRegisterSW"," 函数：",[96,6740,6742],{"className":2945,"code":6741,"language":2947,"meta":101,"style":101},"\u003Cscript setup lang=\"ts\">\nimport { useRegisterSW } from 'virtual:pwa-register\u002Fvue'\n\nconst {\n  offlineReady, \u002F\u002F 应用准备好离线使用（Ref\u003Cboolean>）\n  needRefresh,  \u002F\u002F 有新版本可用（Ref\u003Cboolean>）\n  updateServiceWorker, \u002F\u002F 更新 SW 并刷新页面（函数）\n} = useRegisterSW({\n  onRegisteredSW(swUrl) {\n    console.log(`SW 已注册: ${swUrl}`)\n  },\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv v-if=\"offlineReady || needRefresh\" class=\"pwa-toast\">\n    \u003Cspan v-if=\"offlineReady\">应用已可离线使用\u003C\u002Fspan>\n    \u003Cspan v-else>有新内容可用，点击刷新\u003C\u002Fspan>\n    \u003Cbutton v-if=\"needRefresh\" @click=\"updateServiceWorker()\">刷新\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[24,6743,6744,6760,6772,6776,6782,6792,6803,6813,6825,6837,6855,6859,6863,6871,6875,6883,6906,6926,6942,6971,6979],{"__ignoreMap":101},[105,6745,6746,6748,6750,6752,6754,6756,6758],{"class":107,"line":108},[105,6747,234],{"class":115},[105,6749,2956],{"class":237},[105,6751,2959],{"class":241},[105,6753,2962],{"class":241},[105,6755,245],{"class":115},[105,6757,2967],{"class":248},[105,6759,257],{"class":115},[105,6761,6762,6764,6767,6769],{"class":107,"line":119},[105,6763,3885],{"class":111},[105,6765,6766],{"class":115}," { useRegisterSW } ",[105,6768,3891],{"class":111},[105,6770,6771],{"class":248}," 'virtual:pwa-register\u002Fvue'\n",[105,6773,6774],{"class":107,"line":133},[105,6775,307],{"emptyLinePlaceholder":306},[105,6777,6778,6780],{"class":107,"line":148},[105,6779,568],{"class":111},[105,6781,116],{"class":115},[105,6783,6784,6787,6789],{"class":107,"line":163},[105,6785,6786],{"class":129},"  offlineReady",[105,6788,286],{"class":115},[105,6790,6791],{"class":630},"\u002F\u002F 应用准备好离线使用（Ref\u003Cboolean>）\n",[105,6793,6794,6797,6800],{"class":107,"line":179},[105,6795,6796],{"class":129},"  needRefresh",[105,6798,6799],{"class":115},",  ",[105,6801,6802],{"class":630},"\u002F\u002F 有新版本可用（Ref\u003Cboolean>）\n",[105,6804,6805,6808,6810],{"class":107,"line":194},[105,6806,6807],{"class":129},"  updateServiceWorker",[105,6809,286],{"class":115},[105,6811,6812],{"class":630},"\u002F\u002F 更新 SW 并刷新页面（函数）\n",[105,6814,6815,6818,6820,6823],{"class":107,"line":209},[105,6816,6817],{"class":115},"} ",[105,6819,245],{"class":111},[105,6821,6822],{"class":241}," useRegisterSW",[105,6824,3917],{"class":115},[105,6826,6827,6830,6832,6835],{"class":107,"line":526},[105,6828,6829],{"class":241},"  onRegisteredSW",[105,6831,321],{"class":115},[105,6833,6834],{"class":122},"swUrl",[105,6836,1000],{"class":115},[105,6838,6839,6841,6843,6845,6848,6850,6853],{"class":107,"line":531},[105,6840,6662],{"class":115},[105,6842,639],{"class":241},[105,6844,321],{"class":115},[105,6846,6847],{"class":248},"`SW 已注册: ${",[105,6849,6834],{"class":115},[105,6851,6852],{"class":248},"}`",[105,6854,1511],{"class":115},[105,6856,6857],{"class":107,"line":668},[105,6858,2854],{"class":115},[105,6860,6861],{"class":107,"line":673},[105,6862,2888],{"class":115},[105,6864,6865,6867,6869],{"class":107,"line":679},[105,6866,2992],{"class":115},[105,6868,2956],{"class":237},[105,6870,257],{"class":115},[105,6872,6873],{"class":107,"line":689},[105,6874,307],{"emptyLinePlaceholder":306},[105,6876,6877,6879,6881],{"class":107,"line":699},[105,6878,234],{"class":115},[105,6880,3658],{"class":237},[105,6882,257],{"class":115},[105,6884,6885,6887,6889,6892,6894,6897,6899,6901,6904],{"class":107,"line":704},[105,6886,3665],{"class":115},[105,6888,238],{"class":237},[105,6890,6891],{"class":241}," v-if",[105,6893,245],{"class":115},[105,6895,6896],{"class":248},"\"offlineReady || needRefresh\"",[105,6898,3718],{"class":241},[105,6900,245],{"class":115},[105,6902,6903],{"class":248},"\"pwa-toast\"",[105,6905,257],{"class":115},[105,6907,6908,6910,6912,6914,6916,6919,6922,6924],{"class":107,"line":710},[105,6909,3713],{"class":115},[105,6911,105],{"class":237},[105,6913,6891],{"class":241},[105,6915,245],{"class":115},[105,6917,6918],{"class":248},"\"offlineReady\"",[105,6920,6921],{"class":115},">应用已可离线使用\u003C\u002F",[105,6923,105],{"class":237},[105,6925,257],{"class":115},[105,6927,6928,6930,6932,6935,6938,6940],{"class":107,"line":1136},[105,6929,3713],{"class":115},[105,6931,105],{"class":237},[105,6933,6934],{"class":241}," v-else",[105,6936,6937],{"class":115},">有新内容可用，点击刷新\u003C\u002F",[105,6939,105],{"class":237},[105,6941,257],{"class":115},[105,6943,6944,6946,6949,6951,6953,6956,6959,6961,6964,6967,6969],{"class":107,"line":1149},[105,6945,3713],{"class":115},[105,6947,6948],{"class":237},"button",[105,6950,6891],{"class":241},[105,6952,245],{"class":115},[105,6954,6955],{"class":248},"\"needRefresh\"",[105,6957,6958],{"class":241}," @click",[105,6960,245],{"class":115},[105,6962,6963],{"class":248},"\"updateServiceWorker()\"",[105,6965,6966],{"class":115},">刷新\u003C\u002F",[105,6968,6948],{"class":237},[105,6970,257],{"class":115},[105,6972,6973,6975,6977],{"class":107,"line":3837},[105,6974,3850],{"class":115},[105,6976,238],{"class":237},[105,6978,257],{"class":115},[105,6980,6981,6983,6985],{"class":107,"line":3847},[105,6982,2992],{"class":115},[105,6984,3658],{"class":237},[105,6986,257],{"class":115},[14,6988,6990],{"id":6989},"web-worker","Web Worker",[18,6992,6993],{},"刚才提到的service worker常用于网络代理层，web worker常用于后台计算线程，能够把耗时计算移出主线程，生命周期是随着页面的创建和销毁。这个是html5提供的一个api，允许我们在后台线程运行js代码，从而避免主线程，它是浏览器实现多线程的标准方式。",[18,6995,6996,6997,7000],{},"因为Js是单线程，主线程会负责执行js代码，处理dom渲染，响应用户交互。如果主线程执行一个耗时任务比如大量计算、处理大数组，页面就会卡死，用户点击没反应、动画掉帧。Web Worker 把耗时任务放到",[21,6998,6999],{},"独立线程","，主线程继续响应用户，两者互不阻塞。",[96,7002,7004],{"className":559,"code":7003,"language":561,"meta":101,"style":101},"\u002F\u002F 1. 创建 Worker\nconst worker = new Worker('worker.js');\n\n\u002F\u002F 2. 发送数据给 Worker\nworker.postMessage({ type: 'compute', data: [1, 2, 3, 4, 5] });\n\n\u002F\u002F 3. 接收 Worker 的结果\nworker.onmessage = (e) => {\n    console.log('结果:', e.data);\n};\n\n\u002F\u002F 4. 监听错误\nworker.onerror = (err) => {\n    console.error('Worker 错误:', err.message);\n};\n\n\u002F\u002F 5. 终止 Worker\n\u002F\u002F worker.terminate();\n\n\u002F\u002F worker.js\n\u002F\u002F 接收主线程的消息\nself.onmessage = (e) => {\n    const { type, data } = e.data;\n\n    if (type === 'compute') {\n        \u002F\u002F 执行耗时计算\n        const result = data.reduce((sum, n) => sum + n * n, 0);\n\n        \u002F\u002F 把结果发回主线程\n        self.postMessage({ type: 'result', value: result });\n    }\n};\n",[24,7005,7006,7011,7032,7036,7041,7078,7082,7087,7105,7119,7123,7127,7132,7151,7166,7170,7174,7179,7184,7188,7193,7198,7216,7237,7241,7257,7262,7310,7314,7319,7334,7338],{"__ignoreMap":101},[105,7007,7008],{"class":107,"line":108},[105,7009,7010],{"class":630},"\u002F\u002F 1. 创建 Worker\n",[105,7012,7013,7015,7018,7020,7022,7025,7027,7030],{"class":107,"line":119},[105,7014,568],{"class":111},[105,7016,7017],{"class":129}," worker",[105,7019,574],{"class":111},[105,7021,3478],{"class":111},[105,7023,7024],{"class":241}," Worker",[105,7026,321],{"class":115},[105,7028,7029],{"class":248},"'worker.js'",[105,7031,327],{"class":115},[105,7033,7034],{"class":107,"line":133},[105,7035,307],{"emptyLinePlaceholder":306},[105,7037,7038],{"class":107,"line":148},[105,7039,7040],{"class":630},"\u002F\u002F 2. 发送数据给 Worker\n",[105,7042,7043,7046,7048,7051,7054,7057,7059,7061,7063,7065,7067,7069,7071,7073,7075],{"class":107,"line":163},[105,7044,7045],{"class":115},"worker.",[105,7047,3237],{"class":241},[105,7049,7050],{"class":115},"({ type: ",[105,7052,7053],{"class":248},"'compute'",[105,7055,7056],{"class":115},", data: [",[105,7058,861],{"class":129},[105,7060,286],{"class":115},[105,7062,876],{"class":129},[105,7064,286],{"class":115},[105,7066,891],{"class":129},[105,7068,286],{"class":115},[105,7070,911],{"class":129},[105,7072,286],{"class":115},[105,7074,928],{"class":129},[105,7076,7077],{"class":115},"] });\n",[105,7079,7080],{"class":107,"line":179},[105,7081,307],{"emptyLinePlaceholder":306},[105,7083,7084],{"class":107,"line":194},[105,7085,7086],{"class":630},"\u002F\u002F 3. 接收 Worker 的结果\n",[105,7088,7089,7091,7093,7095,7097,7099,7101,7103],{"class":107,"line":209},[105,7090,7045],{"class":115},[105,7092,5104],{"class":241},[105,7094,574],{"class":111},[105,7096,4361],{"class":115},[105,7098,3297],{"class":122},[105,7100,2836],{"class":115},[105,7102,723],{"class":111},[105,7104,116],{"class":115},[105,7106,7107,7109,7111,7113,7116],{"class":107,"line":526},[105,7108,6662],{"class":115},[105,7110,639],{"class":241},[105,7112,321],{"class":115},[105,7114,7115],{"class":248},"'结果:'",[105,7117,7118],{"class":115},", e.data);\n",[105,7120,7121],{"class":107,"line":531},[105,7122,4441],{"class":115},[105,7124,7125],{"class":107,"line":668},[105,7126,307],{"emptyLinePlaceholder":306},[105,7128,7129],{"class":107,"line":673},[105,7130,7131],{"class":630},"\u002F\u002F 4. 监听错误\n",[105,7133,7134,7136,7139,7141,7143,7145,7147,7149],{"class":107,"line":679},[105,7135,7045],{"class":115},[105,7137,7138],{"class":241},"onerror",[105,7140,574],{"class":111},[105,7142,4361],{"class":115},[105,7144,5544],{"class":122},[105,7146,2836],{"class":115},[105,7148,723],{"class":111},[105,7150,116],{"class":115},[105,7152,7153,7155,7158,7160,7163],{"class":107,"line":689},[105,7154,6662],{"class":115},[105,7156,7157],{"class":241},"error",[105,7159,321],{"class":115},[105,7161,7162],{"class":248},"'Worker 错误:'",[105,7164,7165],{"class":115},", err.message);\n",[105,7167,7168],{"class":107,"line":699},[105,7169,4441],{"class":115},[105,7171,7172],{"class":107,"line":704},[105,7173,307],{"emptyLinePlaceholder":306},[105,7175,7176],{"class":107,"line":710},[105,7177,7178],{"class":630},"\u002F\u002F 5. 终止 Worker\n",[105,7180,7181],{"class":107,"line":1136},[105,7182,7183],{"class":630},"\u002F\u002F worker.terminate();\n",[105,7185,7186],{"class":107,"line":1149},[105,7187,307],{"emptyLinePlaceholder":306},[105,7189,7190],{"class":107,"line":3837},[105,7191,7192],{"class":630},"\u002F\u002F worker.js\n",[105,7194,7195],{"class":107,"line":3847},[105,7196,7197],{"class":630},"\u002F\u002F 接收主线程的消息\n",[105,7199,7200,7202,7204,7206,7208,7210,7212,7214],{"class":107,"line":3857},[105,7201,5580],{"class":115},[105,7203,5104],{"class":241},[105,7205,574],{"class":111},[105,7207,4361],{"class":115},[105,7209,3297],{"class":122},[105,7211,2836],{"class":115},[105,7213,723],{"class":111},[105,7215,116],{"class":115},[105,7217,7218,7221,7223,7226,7228,7230,7232,7234],{"class":107,"line":3866},[105,7219,7220],{"class":111},"    const",[105,7222,726],{"class":115},[105,7224,7225],{"class":129},"type",[105,7227,286],{"class":115},[105,7229,5052],{"class":129},[105,7231,3955],{"class":115},[105,7233,245],{"class":111},[105,7235,7236],{"class":115}," e.data;\n",[105,7238,7239],{"class":107,"line":3871},[105,7240,307],{"emptyLinePlaceholder":306},[105,7242,7243,7246,7249,7252,7255],{"class":107,"line":3882},[105,7244,7245],{"class":111},"    if",[105,7247,7248],{"class":115}," (type ",[105,7250,7251],{"class":111},"===",[105,7253,7254],{"class":248}," 'compute'",[105,7256,1000],{"class":115},[105,7258,7259],{"class":107,"line":3899},[105,7260,7261],{"class":630},"        \u002F\u002F 执行耗时计算\n",[105,7263,7264,7267,7270,7272,7275,7278,7281,7284,7286,7289,7291,7293,7296,7298,7301,7303,7306,7308],{"class":107,"line":3904},[105,7265,7266],{"class":111},"        const",[105,7268,7269],{"class":129}," result",[105,7271,574],{"class":111},[105,7273,7274],{"class":115}," data.",[105,7276,7277],{"class":241},"reduce",[105,7279,7280],{"class":115},"((",[105,7282,7283],{"class":122},"sum",[105,7285,286],{"class":115},[105,7287,7288],{"class":122},"n",[105,7290,2836],{"class":115},[105,7292,723],{"class":111},[105,7294,7295],{"class":115}," sum ",[105,7297,1085],{"class":111},[105,7299,7300],{"class":115}," n ",[105,7302,938],{"class":111},[105,7304,7305],{"class":115}," n, ",[105,7307,1100],{"class":129},[105,7309,327],{"class":115},[105,7311,7312],{"class":107,"line":3920},[105,7313,307],{"emptyLinePlaceholder":306},[105,7315,7316],{"class":107,"line":3936},[105,7317,7318],{"class":630},"        \u002F\u002F 把结果发回主线程\n",[105,7320,7321,7324,7326,7328,7331],{"class":107,"line":3947},[105,7322,7323],{"class":115},"        self.",[105,7325,3237],{"class":241},[105,7327,7050],{"class":115},[105,7329,7330],{"class":248},"'result'",[105,7332,7333],{"class":115},", value: result });\n",[105,7335,7336],{"class":107,"line":3961},[105,7337,5143],{"class":115},[105,7339,7340],{"class":107,"line":3967},[105,7341,4441],{"class":115},[18,7343,7344,7346],{},[24,7345,3237],{}," 支持两种传输方式，分别是结构化克隆、转移所有权。默认是结构化克隆：",[96,7348,7350],{"className":559,"code":7349,"language":561,"meta":101,"style":101},"worker.postMessage({\n    num: 1,\n    str: 'hello',\n    arr: [1, 2, 3],\n    obj: { a: 1 },\n    map: new Map([['k', 'v']]),\n    set: new Set([1, 2]),\n    date: new Date(),\n    buffer: new ArrayBuffer(8)\n});\n",[24,7351,7352,7360,7369,7378,7396,7405,7430,7450,7463,7480],{"__ignoreMap":101},[105,7353,7354,7356,7358],{"class":107,"line":108},[105,7355,7045],{"class":115},[105,7357,3237],{"class":241},[105,7359,3917],{"class":115},[105,7361,7362,7365,7367],{"class":107,"line":119},[105,7363,7364],{"class":115},"    num: ",[105,7366,861],{"class":129},[105,7368,2849],{"class":115},[105,7370,7371,7374,7376],{"class":107,"line":133},[105,7372,7373],{"class":115},"    str: ",[105,7375,3262],{"class":248},[105,7377,2849],{"class":115},[105,7379,7380,7383,7385,7387,7389,7391,7393],{"class":107,"line":148},[105,7381,7382],{"class":115},"    arr: [",[105,7384,861],{"class":129},[105,7386,286],{"class":115},[105,7388,876],{"class":129},[105,7390,286],{"class":115},[105,7392,891],{"class":129},[105,7394,7395],{"class":115},"],\n",[105,7397,7398,7401,7403],{"class":107,"line":163},[105,7399,7400],{"class":115},"    obj: { a: ",[105,7402,861],{"class":129},[105,7404,2879],{"class":115},[105,7406,7407,7410,7413,7416,7419,7422,7424,7427],{"class":107,"line":179},[105,7408,7409],{"class":115},"    map: ",[105,7411,7412],{"class":111},"new",[105,7414,7415],{"class":241}," Map",[105,7417,7418],{"class":115},"([[",[105,7420,7421],{"class":248},"'k'",[105,7423,286],{"class":115},[105,7425,7426],{"class":248},"'v'",[105,7428,7429],{"class":115},"]]),\n",[105,7431,7432,7435,7437,7439,7441,7443,7445,7447],{"class":107,"line":194},[105,7433,7434],{"class":115},"    set: ",[105,7436,7412],{"class":111},[105,7438,5046],{"class":241},[105,7440,6454],{"class":115},[105,7442,861],{"class":129},[105,7444,286],{"class":115},[105,7446,876],{"class":129},[105,7448,7449],{"class":115},"]),\n",[105,7451,7452,7455,7457,7460],{"class":107,"line":209},[105,7453,7454],{"class":115},"    date: ",[105,7456,7412],{"class":111},[105,7458,7459],{"class":241}," Date",[105,7461,7462],{"class":115},"(),\n",[105,7464,7465,7468,7470,7473,7475,7478],{"class":107,"line":526},[105,7466,7467],{"class":115},"    buffer: ",[105,7469,7412],{"class":111},[105,7471,7472],{"class":241}," ArrayBuffer",[105,7474,321],{"class":115},[105,7476,7477],{"class":129},"8",[105,7479,1511],{"class":115},[105,7481,7482],{"class":107,"line":531},[105,7483,3964],{"class":115},[18,7485,7486],{},"不支持dom节点、error对象（部分）、函数。",[18,7488,7489],{},"转移所有权有点像c++的移动move概念。对于大块数据比如ArrayBuffer，可以用转移替代复制，实现零拷贝：",[96,7491,7493],{"className":559,"code":7492,"language":561,"meta":101,"style":101},"const buffer = new ArrayBuffer(1024 * 1024 * 100); \u002F\u002F 100MB\n\n\u002F\u002F 转移所有权，主线程不再拥有该 buffer\nworker.postMessage({ buffer }, [buffer]);\n\n\u002F\u002F 此时 buffer 在主线程已不可用\nconsole.log(buffer.byteLength); \u002F\u002F 0\n",[24,7494,7495,7528,7532,7537,7546,7550,7555],{"__ignoreMap":101},[105,7496,7497,7499,7502,7504,7506,7508,7510,7512,7514,7517,7519,7522,7525],{"class":107,"line":108},[105,7498,568],{"class":111},[105,7500,7501],{"class":129}," buffer",[105,7503,574],{"class":111},[105,7505,3478],{"class":111},[105,7507,7472],{"class":241},[105,7509,321],{"class":115},[105,7511,171],{"class":129},[105,7513,4063],{"class":111},[105,7515,7516],{"class":129}," 1024",[105,7518,4063],{"class":111},[105,7520,7521],{"class":129}," 100",[105,7523,7524],{"class":115},"); ",[105,7526,7527],{"class":630},"\u002F\u002F 100MB\n",[105,7529,7530],{"class":107,"line":119},[105,7531,307],{"emptyLinePlaceholder":306},[105,7533,7534],{"class":107,"line":133},[105,7535,7536],{"class":630},"\u002F\u002F 转移所有权，主线程不再拥有该 buffer\n",[105,7538,7539,7541,7543],{"class":107,"line":148},[105,7540,7045],{"class":115},[105,7542,3237],{"class":241},[105,7544,7545],{"class":115},"({ buffer }, [buffer]);\n",[105,7547,7548],{"class":107,"line":163},[105,7549,307],{"emptyLinePlaceholder":306},[105,7551,7552],{"class":107,"line":179},[105,7553,7554],{"class":630},"\u002F\u002F 此时 buffer 在主线程已不可用\n",[105,7556,7557,7559,7561,7564],{"class":107,"line":194},[105,7558,636],{"class":115},[105,7560,639],{"class":241},[105,7562,7563],{"class":115},"(buffer.byteLength); ",[105,7565,7566],{"class":630},"\u002F\u002F 0\n",[18,7568,7569],{},"处理大数组、图像数据、音视频时，转移比复制快得多。典型的使用场景就有wasm，我们一般就是在worker中运行wasm模块的。可以用于图像处理、像素操作、canvas离屏渲染。还能用于轮询和长连接，后台定时请求、websocket心跳，这也是为了主线程不会去做大json解析、二进制处理处理占用主线程。",[14,7571,6723],{"id":7572},"indexeddb",[18,7574,7575],{},"这个是浏览器的嵌入式数据库，用kv存储，支持索引、事务、游标。能存储任意结构化数据，localStorage只能存储5-10MB，但是这个可以存储上GB的信息量。一个indexedDB可以创建多个数据库：",[96,7577,7580],{"className":7578,"code":7579,"language":392},[390],"const request = indexedDB.open('myDB', 1);   \u002F\u002F 名字 + 版本号\n",[24,7581,7579],{"__ignoreMap":101},[18,7583,7584],{},"他可以使用对象仓库，类似于表：",[96,7586,7589],{"className":7587,"code":7588,"language":392},[390],"db.createObjectStore('users', { keyPath: 'id' });\n",[24,7590,7588],{"__ignoreMap":101},[18,7592,7593],{},"可以使用索引加速查询：",[96,7595,7598],{"className":7596,"code":7597,"language":392},[390],"store.createIndex('email', 'email', { unique: true });\nstore.createIndex('age', 'age');\nstore.createIndex('name_age', ['name', 'age']);   \u002F\u002F 复合索引\n",[24,7599,7597],{"__ignoreMap":101},[18,7601,7602],{},"支持事务，能确保原子性和自动提交，出错能自动回滚：",[96,7604,7607],{"className":7605,"code":7606,"language":392},[390],"const tx = db.transaction('users', 'readwrite');\nconst store = tx.objectStore('users');\nstore.add({ id: 1, name: 'Alice' });\ntx.oncomplete = () => console.log('提交成功');\ntx.onerror = () => console.log('失败');\n",[24,7608,7606],{"__ignoreMap":101},[14,7610,7612],{"id":7611},"ajax技术","Ajax技术",[18,7614,7615],{},"Ajax是一组技术的组合，用于在不刷新页面的前提下用js异步向服务器请求数据并且更新页面的方法。",[18,7617,7618],{},"我们已知，HTML\u002FCSS用于展示内容，DOM动态更新页面，JS用于逻辑控制，那么XMLHttpRequest可以用于一步请求，XML\u002FJSON可以用于数据展示。",[18,7620,7621],{},"我们都知道现代ajax技术经过了发展从XMLHttpRequest变成了fetch，又变成了axios。XMLHttpRequest是1999发明，es6之后，发布了fetch。",[14,7623,7625],{"id":7624},"pwa","PWA",[18,7627,7628,7631],{},[21,7629,7630],{},"Progressive Web App"," 是一组能力的集合，核心是Service Worker + Web App Manifest。他可以做到离线可用，断网也可以打开，可安装，桌面\u002F主屏有图标，独立窗口。可以推送通知，可以后台同步。",[18,7633,7634],{},"service worker之前讲过，是一个跑在浏览器后台独立线程的脚本，可以拦截页面的网络请求、缓存资源、离线响应。",[14,7636,7637],{"id":7637},"浏览器缓存技术",[18,7639,7640],{},"浏览器本身给我们提供了一些缓存技术，最基础的缓存是HTTP响应头机制，通过强缓存可以将带内容哈希的静态资源比如app.a1b2c3.js缓存。",[18,7642,7643],{},"Cache-Control: 这个头我们在缓存里最常用，可以设置max-age= 单位s来以高优先级设置缓存资源，也可以用immutable设置资源永不变，刷新也不请求。头部可以设置Expires来设置绝对时间过期，优先级比较低。",[18,7645,7646],{},"Cache-Control: public, max-age=31536000, immutable 比如这样。",[18,7648,7649],{},"浏览器可以发送请求询问服务器是否可用，这个叫做协商缓存：",[18,7651,7652,1867,7655,7658,7659,1867,7662,7665],{},[24,7653,7654],{},"ETag",[24,7656,7657],{},"If-None-Match"," 可以基于内容哈希，精度比较高，",[24,7660,7661],{},"Last-Modified",[24,7663,7664],{},"If-Modified-Since","基于时间戳，精度比较低。响应为304 Not Modified，不传响应体，比较节省带宽。 ETag是服务器为了某个资源生成的唯一标识符，通常基于文件内容计算比如哈希值，这样客户端在下次请求的时候就会通知服务器本地存有这个版本，并判断是否资源最新的。",[18,7667,7668],{},"这是协商的完整过程：",[96,7670,7673],{"className":7671,"code":7672,"language":392},[390],"第一次请求：\n  客户端 ──GET \u002Findex.html──→ 服务器\n  客户端 ←──200 OK────────── 服务器\n              ETag: \"abc123\"\n              Cache-Control: no-cache\n\u002F\u002F - `no-cache` 表示**每次都要向服务器确认**，不能直接用本地缓存。\n\n第二次请求：\n  客户端 ──GET \u002Findex.html──→ 服务器\n              If-None-Match: \"abc123\"\n  客户端 ←──304 Not Modified── 服务器\n              （无响应体）\n\u002F\u002F 如果没变，返回 `304`，**不传响应体**，节省带宽\n\n如果内容变了：\n  客户端 ←──200 OK────────── 服务器\n              ETag: \"def456\"\n              （返回新内容）\n",[24,7674,7672],{"__ignoreMap":101},[18,7676,7677],{},"适用于HTML文件、API响应等需要及时更新的资源：",[96,7679,7682],{"className":7680,"code":7681,"language":392},[390],"Cache-Control: no-cache\nETag: \"abc123\"\n",[24,7683,7681],{"__ignoreMap":101},[18,7685,7686,7687,7690,7691,7694],{},"vary: Origin表示响应内容依赖请求的Origin头，如果同一个URL被不同 ",[24,7688,7689],{},"Origin"," 请求，浏览器会分别缓存。这在开发环境下常见，因为 Vite 的 dev server 需要处理 CORS。生产环境下，如果 CDN 配置不当，",[24,7692,7693],{},"vary: Origin"," 可能导致缓存命中率下降。",[18,7696,7697],{},"缓存策略组合：",[18,7699,7700],{},"HTML这种资源推荐使用no-cache+ ETag每次协商，js\u002Fcss推荐用max-age和immutable进行缓存。图片和字体也可以通过max-age+ETag进行缓存。API响应可以通过no-store或者短max-age + ETag进行缓存。",[18,7702,7703],{},"内存缓存是浏览器自己自动管理的，同一页面重复请求同一资源的时候就会直接从内存读取，关闭标签页就会失效。浏览器在收到一个资源的时候，会根据响应头决定存储在哪里。内存缓存的存储位置是RAM，而非DISK，读取速度很快，像js css 图片资源等高频使用的资源可以存，生命周期是当前标签页会话，关闭就失效。磁盘缓存的位置是硬盘，生命周期是由Cache-Control \u002F Expires 决定，读取速度较快，像大文件，不常变动的资源可以缓存。service worker缓存存储的位置是Cache API，独立于HTTP缓存，需要手动控制生命周期，像PWA、离线资源可以存。",[96,7705,7708],{"className":7706,"code":7707,"language":392},[390],"1. 内存缓存命中？\n   ├─ 是 → 直接用（from memory cache）\n   └─ 否 → 下一步\n\n2. Service Worker 缓存命中？\n   ├─ 是 → 用 SW 缓存\n   └─ 否 → 下一步\n\n3. 磁盘缓存命中？\n   ├─ 强缓存有效？\n   │   ├─ 是 → 直接用（from disk cache）\n   │   └─ 否 → 协商缓存（发请求带 If-None-Match）\n   │              ├─ 304 → 用本地\n   │              └─ 200 → 下载新资源\n   └─ 否 → 发请求下载\n",[24,7709,7707],{"__ignoreMap":101},[18,7711,7712],{},"![[Pasted image 20260930221146.png]]",[18,7714,7715],{},"![[Pasted image 20260930221200.png]]",[18,7717,7718,7719,91],{},"public, max-age=2592000, immutable 会让我们强缓存30天永不变，cf-cache-status: HIT表示这是Cloudflare CDN边缘节点已缓存。etag是协商的缓存标识，last-modified是最后的修改时间。这个组合会让浏览器把图片存入磁盘缓存，下次请求同一URL时候浏览器直接读磁盘，不发网络请求，状态码会显示 ",[24,7720,7721],{},"200 (from disk cache)",[14,7723,7724],{"id":7724},"标签页和v8",[18,7726,7727],{},"每个标签页其实不会分配一个独立的v8实例，v8实例是绑定在渲染进程RendererProcess上的，而不是直接绑定在标签页上。正确的关系是，一个v8实例可能服务多个标签页和iframe。在早期，确实一个标签页一个进程，现在策略已经变了，如果我们打开两个标签都访问同一个网站比如example.com，chrome可能让它们共享一个渲染进程，也就是共享一个v8实例，用来节省内存。如果网站嵌入了跨站iframe，chrome会为了跨站ifame单独创建一个渲染进程也就是OOPIF，跨进程iframe，一个标签页内部可能对于好几个渲染进程，有好几个v8实例。",[18,7729,7730],{},"可以查看chromium.org内看文档：离线处理架构OOPIFs：",[96,7732,7735],{"className":7733,"code":7734,"language":392},[390],"这种技术允许页面的子框架由与父框架不同的进程来渲染。采用独立进程内嵌框架的做法主要是出于安全考虑，比如 Site Isolation 项目所提出的解决方案——通过这种方式，即使存在跨域内嵌框架，也能让某个渲染进程专注于处理某个特定的网站内容。不过，独立进程内嵌框架也是一种通用的机制，其应用范围不仅限于安全相关场景，还可以用于其他功能，例如 Chrome 应用程序中的\u003Cwebview>标签。\n",[24,7736,7734],{"__ignoreMap":101},[18,7738,7739,7740,7743,7744,91,7747,7750,7751,7754,7755,7757,7758,7761],{},"OOPIF 要求 Chromium 的架构从“以标签页为中心”转向“",[21,7741,7742],{},"以 frame 为中心","”，因为一个 frame 在其生命周期内可能被渲染到不同进程中。浏览器进程现在直接追踪",[21,7745,7746],{},"完整的 frame 树",[24,7748,7749],{},"WebContents"," 持有一个 ",[24,7752,7753],{},"FrameTreeNode"," 树，镜像当前页面的 frame 结构。每个 ",[24,7756,7753],{}," 包含该 frame 的元信息（名称、origin 等），其 ",[24,7759,7760],{},"RenderFrameHostManager"," 负责处理该 frame 的跨进程导航，并支持状态复制和消息路由",[96,7763,7766],{"className":7764,"code":7765,"language":392},[390],"OOPIF 的渲染面临一个核心安全问题：**能合成一个纹理，就意味着能读取它的像素**（Cross Site Texture Stealing）。因此 OOPIF 的完整安全优势依赖于 **Übercompositor** 架构[](https:\u002F\u002Fwww.chromium.org\u002Fdevelopers\u002Fdesign-documents\u002Foop-iframes\u002Foop-iframes-rendering\u002F)。\n\n在 Übercompositor 下：\n\n- 每个渲染进程的子合成器生成 **CompositorFrame**，包含 quads 和纹理引用。\n    \n- 渲染进程通过 **IPC 将 CompositorFrame 发给浏览器进程**，而不是直接发给 GPU。\n    \n- 浏览器进程的父合成器与 GPU 进程交互，完成最终合成[](https:\u002F\u002Fwww.chromium.org\u002Fdevelopers\u002Fdesign-documents\u002Foop-iframes\u002Foop-iframes-rendering\u002F)。\n    \n\n纹理在不同进程间通过 **TextureMailbox** 传递所有权：一个进程用 `glProduceTextureCHROMIUM` 生成纹理，另一个进程用 `glConsumeTextureCHROMIUM` 消费它[](https:\u002F\u002Fwww.chromium.org\u002Fdevelopers\u002Fdesign-documents\u002Foop-iframes\u002Foop-iframes-rendering\u002F)。\n",[24,7767,7765],{"__ignoreMap":101},[18,7769,7770,7771,7774,7775,7778],{},"这地方了解着玩玩就行，不用急太深，quad是合成器提交给gpu的最小绘制单元，可以理解为一个带纹理矩形，描述了哪块的纹理，用什么变换，绘制到屏幕哪个位置。渲染进程的合成器不直接操作 GPU，而是把一帧画面",[21,7772,7773],{},"拆解成一组 quad","，打包成 ",[24,7776,7777],{},"CompositorFrame","，通过 IPC 发给浏览器进程或 GPU 进程。GPU 再按顺序把这些 quad 绘制出来，合成最终画面。",[18,7780,7781],{},"再深入挖掘就是架构图形栈知识了，不讲不讲...",[14,7783,7784],{"id":7784},"viz进程架构",[18,7786,7787],{},"viz是chromium渲染管线现代化的核心，主要为了解决GPU进程职责过重，站点隔离下的性能和安全瓶颈，以及图形栈的服务化需求。",[18,7789,7790],{},"在 Spectre 等侧信道攻击的背景下，跨站 iframe 需要严格的进程隔离（OOPIF）。VIZ将核心的合成和光栅化集中到独立进程中，使得来自不同渲染进程的合成器帧可以在一个安全的、收信任环境下统一聚合，从而支持“无额外性能代价的站点隔离”。Viz 是 Chromium 向 Mojo 服务化架构转型的一部分。它将合成、GL、命中测试、媒体等能力封装为标准服务，通过 Mojo 接口对外提供，使得 Browser 进程不再是唯一的核心枢纽，架构上更清晰、可维护性更强。",[18,7792,7793],{},"skia是chromium的底层2D图形库，负责我们几乎我们所有看到的像素绘制，从文字到图形、形状、路径、渐变、阴影、图层混合等。",[96,7795,7798],{"className":7796,"code":7797,"language":392},[390],"Chromium 图形栈（从上层到下层）：\n\n┌─────────────────────────────────┐\n│  Blink（渲染引擎）              │  ← HTML\u002FCSS 解析、布局、绘制指令\n├─────────────────────────────────┤\n│  cc \u002F viz（合成器）             │  ← 图层合成、光栅化调度、GPU 提交\n├─────────────────────────────────┤\n│  Skia                           │  ← 2D 绘制：路径、文字、图片、混合\n├─────────────────────────────────┤\n│  GPU 驱动 \u002F Vulkan \u002F Metal \u002F GL │  ← 硬件加速\n└─────────────────────────────────┘\n",[24,7799,7797],{"__ignoreMap":101},[10,7801,7803],{"id":7802},"javascript","JavaScript",[14,7805,7807],{"id":7806},"_01-02-03-嘛为什么","0.1 + 0.2 === 0.3 嘛？为什么？",[18,7809,7810,7813,7814,7817,7818,7821,7822,7825,7826,7829,7830,31,7833,31,7836,7839,7840,7843],{},[21,7811,7812],{},"不等于。"," ",[24,7815,7816],{},"0.1 + 0.2 === 0.3"," 返回 ",[24,7819,7820],{},"false","，实际结果是 ",[24,7823,7824],{},"0.30000000000000004","，因为 ",[21,7827,7828],{},"JavaScript 的数字用 IEEE 754 双精度浮点数表示","，而 ",[24,7831,7832],{},"0.1",[24,7834,7835],{},"0.2",[24,7837,7838],{},"0.3"," 这些十进制小数，在二进制里都是",[21,7841,7842],{},"无限循环小数","，无法精确表示。",[18,7845,7846,7847,7849],{},"把十进制的 ",[24,7848,7832],{}," 转成二进制：",[96,7851,7853],{"className":559,"code":7852,"language":561,"meta":101,"style":101},"0.1 × 2 = 0.2    → 0\n0.2 × 2 = 0.4    → 0\n0.4 × 2 = 0.8    → 0\n0.8 × 2 = 1.6    → 1\n0.6 × 2 = 1.2    → 1\n0.2 × 2 = 0.4    → 0  ← 回到 0.2，开始循环\n...\n",[24,7854,7855,7875,7892,7910,7929,7947,7971],{"__ignoreMap":101},[105,7856,7857,7859,7862,7864,7866,7869,7872],{"class":107,"line":108},[105,7858,7832],{"class":129},[105,7860,7861],{"class":115}," × ",[105,7863,876],{"class":129},[105,7865,574],{"class":111},[105,7867,7868],{"class":129}," 0.2",[105,7870,7871],{"class":115},"    → ",[105,7873,7874],{"class":129},"0\n",[105,7876,7877,7879,7881,7883,7885,7888,7890],{"class":107,"line":119},[105,7878,7835],{"class":129},[105,7880,7861],{"class":115},[105,7882,876],{"class":129},[105,7884,574],{"class":111},[105,7886,7887],{"class":129}," 0.4",[105,7889,7871],{"class":115},[105,7891,7874],{"class":129},[105,7893,7894,7897,7899,7901,7903,7906,7908],{"class":107,"line":133},[105,7895,7896],{"class":129},"0.4",[105,7898,7861],{"class":115},[105,7900,876],{"class":129},[105,7902,574],{"class":111},[105,7904,7905],{"class":129}," 0.8",[105,7907,7871],{"class":115},[105,7909,7874],{"class":129},[105,7911,7912,7915,7917,7919,7921,7924,7926],{"class":107,"line":148},[105,7913,7914],{"class":129},"0.8",[105,7916,7861],{"class":115},[105,7918,876],{"class":129},[105,7920,574],{"class":111},[105,7922,7923],{"class":129}," 1.6",[105,7925,7871],{"class":115},[105,7927,7928],{"class":129},"1\n",[105,7930,7931,7934,7936,7938,7940,7943,7945],{"class":107,"line":163},[105,7932,7933],{"class":129},"0.6",[105,7935,7861],{"class":115},[105,7937,876],{"class":129},[105,7939,574],{"class":111},[105,7941,7942],{"class":129}," 1.2",[105,7944,7871],{"class":115},[105,7946,7928],{"class":129},[105,7948,7949,7951,7953,7955,7957,7959,7961,7963,7966,7968],{"class":107,"line":179},[105,7950,7835],{"class":129},[105,7952,7861],{"class":115},[105,7954,876],{"class":129},[105,7956,574],{"class":111},[105,7958,7887],{"class":129},[105,7960,7871],{"class":115},[105,7962,1100],{"class":129},[105,7964,7965],{"class":115},"  ← 回到 ",[105,7967,7835],{"class":129},[105,7969,7970],{"class":115},"，开始循环\n",[105,7972,7973],{"class":107,"line":194},[105,7974,7975],{"class":111},"...\n",[18,7977,7978,7979,7982,7983,7986,7987,7990,7991,7993,7994,7996,7997,91],{},"结果是 ",[24,7980,7981],{},"0.000110011001100110011...","，无限循环。",[21,7984,7985],{},"双精度浮点数只有 52 位尾数","，存不下无限循环，所以只能",[21,7988,7989],{},"截断或舍入","。存进去的 ",[24,7992,7832],{}," 已经不是精确的 ",[24,7995,7832],{},"，而是一个",[21,7998,7999],{},"非常接近但略有偏差的值",[18,8001,8002,8003,8006],{},"JavaScript 的设计目标之一是",[21,8004,8005],{},"简单","。Brendan Eich 在 1995 年设计 JS 时，选择了“一种数字类型”的方案：",[442,8008,8009,8018,8021],{},[448,8010,8011,8012,1329,8015,91],{},"不用区分 ",[24,8013,8014],{},"int",[24,8016,8017],{},"float",[448,8019,8020],{},"所有数字都是 64 位双精度浮点数。",[448,8022,8023,8024,91],{},"整数的最大安全范围是 ",[24,8025,8026],{},"2^53 - 1",[14,8028,8029],{"id":8029},"运行时类型检测",[18,8031,8032],{},"在js中，在运行时检测对象不能仅仅凭借typeof，因为typeof只能区分原始类型和对象，无法区分具体的对象类型，要判断是不是 Map 这种类型，就需要使用instanceof 或者Object.prototype.toString。",[96,8034,8036],{"className":559,"code":8035,"language":561,"meta":101,"style":101},"typeof new Map()      \u002F\u002F \"object\"\ntypeof {}             \u002F\u002F \"object\"\ntypeof []             \u002F\u002F \"object\"\ntypeof new Date()     \u002F\u002F \"object\"\ntypeof new Set()      \u002F\u002F \"object\"\ntypeof null           \u002F\u002F \"object\"\n",[24,8037,8038,8053,8062,8071,8084,8096],{"__ignoreMap":101},[105,8039,8040,8043,8045,8047,8050],{"class":107,"line":108},[105,8041,8042],{"class":111},"typeof",[105,8044,3478],{"class":111},[105,8046,7415],{"class":241},[105,8048,8049],{"class":115},"()      ",[105,8051,8052],{"class":630},"\u002F\u002F \"object\"\n",[105,8054,8055,8057,8060],{"class":107,"line":119},[105,8056,8042],{"class":111},[105,8058,8059],{"class":115}," {}             ",[105,8061,8052],{"class":630},[105,8063,8064,8066,8069],{"class":107,"line":133},[105,8065,8042],{"class":111},[105,8067,8068],{"class":115}," []             ",[105,8070,8052],{"class":630},[105,8072,8073,8075,8077,8079,8082],{"class":107,"line":148},[105,8074,8042],{"class":111},[105,8076,3478],{"class":111},[105,8078,7459],{"class":241},[105,8080,8081],{"class":115},"()     ",[105,8083,8052],{"class":630},[105,8085,8086,8088,8090,8092,8094],{"class":107,"line":163},[105,8087,8042],{"class":111},[105,8089,3478],{"class":111},[105,8091,5046],{"class":241},[105,8093,8049],{"class":115},[105,8095,8052],{"class":630},[105,8097,8098,8100,8102],{"class":107,"line":179},[105,8099,8042],{"class":111},[105,8101,5026],{"class":129},[105,8103,8104],{"class":630},"           \u002F\u002F \"object\"\n",[18,8106,8107,8108,8118,8119,8121,8122,31,8125,31,8128,31,8131,91],{},"typeof只会返回几种字符串，分别是undefined、object、boolean、number、bigint、string、symbol、function，",[21,8109,8110,8111,8114,8115,91],{},"除了 ",[24,8112,8113],{},"function","，所有引用类型都是 ",[24,8116,8117],{},"\"object\""," 所以 ",[24,8120,8042],{}," 无法区分 ",[24,8123,8124],{},"Map",[24,8126,8127],{},"Set",[24,8129,8130],{},"Array",[24,8132,8133],{},"Date",[18,8135,8136],{},"instanceof检查的是原型链，也就是对象的原型链上有没有这个构造函数的prototype。",[96,8138,8140],{"className":559,"code":8139,"language":561,"meta":101,"style":101},"new Map() instanceof Map  \u002F\u002F true\n",[24,8141,8142],{"__ignoreMap":101},[105,8143,8144,8146,8148,8151,8154,8156],{"class":107,"line":108},[105,8145,7412],{"class":111},[105,8147,7415],{"class":241},[105,8149,8150],{"class":115},"() ",[105,8152,8153],{"class":111},"instanceof",[105,8155,7415],{"class":241},[105,8157,8158],{"class":630},"  \u002F\u002F true\n",[18,8160,8161,8164],{},[21,8162,8163],{},"跨 iframe \u002F 跨 window 失效"," ：",[96,8166,8168],{"className":559,"code":8167,"language":561,"meta":101,"style":101},"\u002F\u002F iframe 里的 Map\nconst iframeMap = iframe.contentWindow.Map\n\nnew Map() instanceof iframeMap  \u002F\u002F false ❌\n",[24,8169,8170,8175,8187,8191],{"__ignoreMap":101},[105,8171,8172],{"class":107,"line":108},[105,8173,8174],{"class":630},"\u002F\u002F iframe 里的 Map\n",[105,8176,8177,8179,8182,8184],{"class":107,"line":119},[105,8178,568],{"class":111},[105,8180,8181],{"class":129}," iframeMap",[105,8183,574],{"class":111},[105,8185,8186],{"class":115}," iframe.contentWindow.Map\n",[105,8188,8189],{"class":107,"line":133},[105,8190,307],{"emptyLinePlaceholder":306},[105,8192,8193,8195,8197,8199,8201,8203],{"class":107,"line":148},[105,8194,7412],{"class":111},[105,8196,7415],{"class":241},[105,8198,8150],{"class":115},[105,8200,8153],{"class":111},[105,8202,8181],{"class":241},[105,8204,8205],{"class":630},"  \u002F\u002F false ❌\n",[18,8207,8208,8209,8211,8212,8215,8216],{},"因为 iframe 有自己的 ",[24,8210,8124],{}," 构造函数，",[24,8213,8214],{},"Map.prototype"," 不是同一个对象。",[21,8217,8218,8219,8221],{},"原型链上找的是当前 window 的 ",[24,8220,8214],{},"，不是 iframe 的。",[18,8223,8224],{},"更加可靠的是 使用Object.prototype.toString方法：",[96,8226,8228],{"className":559,"code":8227,"language":561,"meta":101,"style":101},"Object.prototype.toString.call(new Map())      \u002F\u002F \"[object Map]\"\nObject.prototype.toString.call(new Set())      \u002F\u002F \"[object Set]\"\nObject.prototype.toString.call([])             \u002F\u002F \"[object Array]\"\nObject.prototype.toString.call(new Date())     \u002F\u002F \"[object Date]\"\nObject.prototype.toString.call({})             \u002F\u002F \"[object Object]\"\nObject.prototype.toString.call(null)           \u002F\u002F \"[object Null]\"\nObject.prototype.toString.call(undefined)      \u002F\u002F \"[object Undefined]\"\n",[24,8229,8230,8258,8281,8299,8323,8341,8363],{"__ignoreMap":101},[105,8231,8232,8235,8237,8240,8243,8246,8248,8250,8252,8255],{"class":107,"line":108},[105,8233,8234],{"class":129},"Object",[105,8236,5363],{"class":115},[105,8238,8239],{"class":129},"prototype",[105,8241,8242],{"class":115},".toString.",[105,8244,8245],{"class":241},"call",[105,8247,321],{"class":115},[105,8249,7412],{"class":111},[105,8251,7415],{"class":241},[105,8253,8254],{"class":115},"())      ",[105,8256,8257],{"class":630},"\u002F\u002F \"[object Map]\"\n",[105,8259,8260,8262,8264,8266,8268,8270,8272,8274,8276,8278],{"class":107,"line":119},[105,8261,8234],{"class":129},[105,8263,5363],{"class":115},[105,8265,8239],{"class":129},[105,8267,8242],{"class":115},[105,8269,8245],{"class":241},[105,8271,321],{"class":115},[105,8273,7412],{"class":111},[105,8275,5046],{"class":241},[105,8277,8254],{"class":115},[105,8279,8280],{"class":630},"\u002F\u002F \"[object Set]\"\n",[105,8282,8283,8285,8287,8289,8291,8293,8296],{"class":107,"line":133},[105,8284,8234],{"class":129},[105,8286,5363],{"class":115},[105,8288,8239],{"class":129},[105,8290,8242],{"class":115},[105,8292,8245],{"class":241},[105,8294,8295],{"class":115},"([])             ",[105,8297,8298],{"class":630},"\u002F\u002F \"[object Array]\"\n",[105,8300,8301,8303,8305,8307,8309,8311,8313,8315,8317,8320],{"class":107,"line":148},[105,8302,8234],{"class":129},[105,8304,5363],{"class":115},[105,8306,8239],{"class":129},[105,8308,8242],{"class":115},[105,8310,8245],{"class":241},[105,8312,321],{"class":115},[105,8314,7412],{"class":111},[105,8316,7459],{"class":241},[105,8318,8319],{"class":115},"())     ",[105,8321,8322],{"class":630},"\u002F\u002F \"[object Date]\"\n",[105,8324,8325,8327,8329,8331,8333,8335,8338],{"class":107,"line":163},[105,8326,8234],{"class":129},[105,8328,5363],{"class":115},[105,8330,8239],{"class":129},[105,8332,8242],{"class":115},[105,8334,8245],{"class":241},[105,8336,8337],{"class":115},"({})             ",[105,8339,8340],{"class":630},"\u002F\u002F \"[object Object]\"\n",[105,8342,8343,8345,8347,8349,8351,8353,8355,8357,8360],{"class":107,"line":179},[105,8344,8234],{"class":129},[105,8346,5363],{"class":115},[105,8348,8239],{"class":129},[105,8350,8242],{"class":115},[105,8352,8245],{"class":241},[105,8354,321],{"class":115},[105,8356,3987],{"class":129},[105,8358,8359],{"class":115},")           ",[105,8361,8362],{"class":630},"\u002F\u002F \"[object Null]\"\n",[105,8364,8365,8367,8369,8371,8373,8375,8377,8380,8383],{"class":107,"line":194},[105,8366,8234],{"class":129},[105,8368,5363],{"class":115},[105,8370,8239],{"class":129},[105,8372,8242],{"class":115},[105,8374,8245],{"class":241},[105,8376,321],{"class":115},[105,8378,8379],{"class":129},"undefined",[105,8381,8382],{"class":115},")      ",[105,8384,8385],{"class":630},"\u002F\u002F \"[object Undefined]\"\n",[18,8387,8388,8391,8392,1980,8395,8398,8399,91,8402,8405,8406,8408,8409,8412,8413,91],{},[24,8389,8390],{},"Object.prototype.toString"," 会读取对象内部的 ",[24,8393,8394],{},"[[Class]]",[24,8396,8397],{},"Symbol.toStringTag"," 属性，返回 ",[24,8400,8401],{},"[object Xxx]",[21,8403,8404],{},"跨 iframe 有效"," ， 因为 ",[24,8407,8394],{}," 是引擎内部的标记，不受 window 隔离影响。",[21,8410,8411],{},"不受原型链修改影响","：因为不依赖 ",[24,8414,8239],{},[96,8416,8418],{"className":559,"code":8417,"language":561,"meta":101,"style":101},"export function getType(value: unknown) {\n  return Object.prototype.toString.call(value).slice(8, -1).toLocaleLowerCase()\n}\n\nconsole.log(getType(new Map()))\n\n",[24,8419,8420,8441,8478,8482,8486],{"__ignoreMap":101},[105,8421,8422,8424,8426,8429,8431,8434,8436,8439],{"class":107,"line":108},[105,8423,4979],{"class":111},[105,8425,4982],{"class":111},[105,8427,8428],{"class":241}," getType",[105,8430,321],{"class":115},[105,8432,8433],{"class":122},"value",[105,8435,4999],{"class":111},[105,8437,8438],{"class":129}," unknown",[105,8440,1000],{"class":115},[105,8442,8443,8445,8448,8450,8452,8454,8456,8459,8461,8463,8465,8467,8469,8471,8473,8476],{"class":107,"line":119},[105,8444,4126],{"class":111},[105,8446,8447],{"class":129}," Object",[105,8449,5363],{"class":115},[105,8451,8239],{"class":129},[105,8453,8242],{"class":115},[105,8455,8245],{"class":241},[105,8457,8458],{"class":115},"(value).",[105,8460,4273],{"class":241},[105,8462,321],{"class":115},[105,8464,7477],{"class":129},[105,8466,286],{"class":115},[105,8468,4141],{"class":111},[105,8470,861],{"class":129},[105,8472,5624],{"class":115},[105,8474,8475],{"class":241},"toLocaleLowerCase",[105,8477,5174],{"class":115},[105,8479,8480],{"class":107,"line":133},[105,8481,212],{"class":115},[105,8483,8484],{"class":107,"line":148},[105,8485,307],{"emptyLinePlaceholder":306},[105,8487,8488,8490,8492,8494,8497,8499,8501,8503],{"class":107,"line":163},[105,8489,636],{"class":115},[105,8491,639],{"class":241},[105,8493,321],{"class":115},[105,8495,8496],{"class":241},"getType",[105,8498,321],{"class":115},[105,8500,7412],{"class":111},[105,8502,7415],{"class":241},[105,8504,8505],{"class":115},"()))\n",[14,8507,8508],{"id":8508},"脚本和模块",[18,8510,8511],{},"在JS中，如果没有export，js就会被当成脚本，但是有export就会当做模块，这是 JavaScript 的一个核心区别。",[835,8513,8514,8527],{},[838,8515,8516],{},[841,8517,8518,8521,8524],{},[844,8519,8520],{},"维度",[844,8522,8523],{},"Script",[844,8525,8526],{},"Module（ESM）",[854,8528,8529,8550,8570,8583,8602,8619,8632,8645,8660,8674],{},[841,8530,8531,8536,8543],{},[859,8532,8533],{},[21,8534,8535],{},"触发条件",[859,8537,8538,8539,1867,8541],{},"没有 ",[24,8540,3885],{},[24,8542,4979],{},[859,8544,8545,8546,1867,8548],{},"有顶层 ",[24,8547,3885],{},[24,8549,4979],{},[841,8551,8552,8560,8565],{},[859,8553,8554],{},[21,8555,8556,8559],{},[24,8557,8558],{},"\u003Cscript>"," 标签",[859,8561,8562,8564],{},[24,8563,8558],{},"（默认）",[859,8566,8567],{},[24,8568,8569],{},"\u003Cscript type=\"module\">",[841,8571,8572,8577,8580],{},[859,8573,8574],{},[21,8575,8576],{},"作用域",[859,8578,8579],{},"全局",[859,8581,8582],{},"模块级（文件作用域）",[841,8584,8585,8592,8598],{},[859,8586,8587],{},[21,8588,8589,8590],{},"顶层 ",[24,8591,2870],{},[859,8593,8594,8597],{},[24,8595,8596],{},"window","（浏览器）",[859,8599,8600],{},[24,8601,8379],{},[841,8603,8604,8609,8614],{},[859,8605,8606],{},[21,8607,8608],{},"变量共享",[859,8610,8611,8612],{},"全局变量挂到 ",[24,8613,8596],{},[859,8615,8616,8617],{},"不挂到 ",[24,8618,8596],{},[841,8620,8621,8626,8629],{},[859,8622,8623],{},[21,8624,8625],{},"变量提升",[859,8627,8628],{},"有",[859,8630,8631],{},"有，但有 TDZ",[841,8633,8634,8639,8642],{},[859,8635,8636],{},[21,8637,8638],{},"严格模式",[859,8640,8641],{},"默认非严格",[859,8643,8644],{},"默认严格",[841,8646,8647,8654,8657],{},[859,8648,8649],{},[21,8650,8651,8653],{},[24,8652,5383],{}," 顶层",[859,8655,8656],{},"不允许",[859,8658,8659],{},"允许",[841,8661,8662,8670,8672],{},[859,8663,8664],{},[21,8665,8666,8669],{},[24,8667,8668],{},"import()"," 动态导入",[859,8671,8659],{},[859,8673,8659],{},[841,8675,8676,8684,8686],{},[859,8677,8678],{},[21,8679,8680,8683],{},[24,8681,8682],{},".js"," 默认解析",[859,8685,8523],{},[859,8687,8688],{},"Module",[18,8690,2956],{},[96,8692,8694],{"className":559,"code":8693,"language":561,"meta":101,"style":101},"\u002F\u002F a.js\nvar foo = 'hello'  \u002F\u002F 挂到 window.foo\n\n\u002F\u002F b.js\nconsole.log(foo)   \u002F\u002F \"hello\"，能访问\n",[24,8695,8696,8701,8716,8720,8725],{"__ignoreMap":101},[105,8697,8698],{"class":107,"line":108},[105,8699,8700],{"class":630},"\u002F\u002F a.js\n",[105,8702,8703,8705,8708,8710,8713],{"class":107,"line":119},[105,8704,1749],{"class":111},[105,8706,8707],{"class":115}," foo ",[105,8709,245],{"class":111},[105,8711,8712],{"class":248}," 'hello'",[105,8714,8715],{"class":630},"  \u002F\u002F 挂到 window.foo\n",[105,8717,8718],{"class":107,"line":133},[105,8719,307],{"emptyLinePlaceholder":306},[105,8721,8722],{"class":107,"line":148},[105,8723,8724],{"class":630},"\u002F\u002F b.js\n",[105,8726,8727,8729,8731,8734],{"class":107,"line":163},[105,8728,636],{"class":115},[105,8730,639],{"class":241},[105,8732,8733],{"class":115},"(foo)   ",[105,8735,8736],{"class":630},"\u002F\u002F \"hello\"，能访问\n",[18,8738,8739],{},"module",[96,8741,8743],{"className":559,"code":8742,"language":561,"meta":101,"style":101},"\u002F\u002F a.js\nexport const foo = 'hello'  \u002F\u002F 模块级，不挂到 window\n\n\u002F\u002F b.js\nconsole.log(foo)   \u002F\u002F ❌ ReferenceError，访问不到\n",[24,8744,8745,8749,8766,8770,8774],{"__ignoreMap":101},[105,8746,8747],{"class":107,"line":108},[105,8748,8700],{"class":630},[105,8750,8751,8753,8756,8759,8761,8763],{"class":107,"line":119},[105,8752,4979],{"class":111},[105,8754,8755],{"class":111}," const",[105,8757,8758],{"class":129}," foo",[105,8760,574],{"class":111},[105,8762,8712],{"class":248},[105,8764,8765],{"class":630},"  \u002F\u002F 模块级，不挂到 window\n",[105,8767,8768],{"class":107,"line":133},[105,8769,307],{"emptyLinePlaceholder":306},[105,8771,8772],{"class":107,"line":148},[105,8773,8724],{"class":630},[105,8775,8776,8778,8780,8782],{"class":107,"line":163},[105,8777,636],{"class":115},[105,8779,639],{"class":241},[105,8781,8733],{"class":115},[105,8783,8784],{"class":630},"\u002F\u002F ❌ ReferenceError，访问不到\n",[18,8786,8787],{},"判断一个文件是script还是module可以看有没有import\u002Fexport。也可以看script标签：",[96,8789,8791],{"className":559,"code":8790,"language":561,"meta":101,"style":101},"\u003C!-- Script -->\n\u003Cscript src=\"script.js\">\u003C\u002Fscript>\n\n\u003C!-- Module -->\n\u003Cscript type=\"module\" src=\"module.js\">\u003C\u002Fscript>\n",[24,8792,8793,8804,8823,8827,8836],{"__ignoreMap":101},[105,8794,8795,8798,8801],{"class":107,"line":108},[105,8796,8797],{"class":111},"\u003C!--",[105,8799,8800],{"class":115}," Script ",[105,8802,8803],{"class":111},"-->\n",[105,8805,8806,8808,8810,8812,8814,8817,8819,8821],{"class":107,"line":119},[105,8807,234],{"class":115},[105,8809,2956],{"class":237},[105,8811,3033],{"class":241},[105,8813,245],{"class":111},[105,8815,8816],{"class":248},"\"script.js\"",[105,8818,252],{"class":115},[105,8820,2956],{"class":237},[105,8822,257],{"class":115},[105,8824,8825],{"class":107,"line":133},[105,8826,307],{"emptyLinePlaceholder":306},[105,8828,8829,8831,8834],{"class":107,"line":148},[105,8830,8797],{"class":111},[105,8832,8833],{"class":115}," Module ",[105,8835,8803],{"class":111},[105,8837,8838,8840,8842,8845,8847,8850,8852,8854,8857,8859,8861],{"class":107,"line":163},[105,8839,234],{"class":115},[105,8841,2956],{"class":237},[105,8843,8844],{"class":241}," type",[105,8846,245],{"class":111},[105,8848,8849],{"class":248},"\"module\"",[105,8851,3033],{"class":241},[105,8853,245],{"class":111},[105,8855,8856],{"class":248},"\"module.js\"",[105,8858,252],{"class":115},[105,8860,2956],{"class":237},[105,8862,257],{"class":115},[18,8864,8865],{},"也可以看pacakge.json里的type字段，type:module 中.js文件默认当做module，type:commonjs或者没有.js文件默认当做cmj，mjs当做module，cjs当做cjs.",[18,8867,8868],{},"还有一点，我们都知道import esm是异步方法，我们可以看看@types+node的类型定义层面，",[96,8870,8872],{"className":3241,"code":8871,"language":3243,"meta":101,"style":101},"            \u002F**\n             * The absolute `file:` URL of the module.\n             *\n             * This is defined exactly the same as it is in browsers providing the URL of the\n             * current module file.\n             *\n             * This enables useful patterns such as relative file loading:\n             *\n             * ```js\n             * import { readFileSync } from 'node:fs';\n             * const buffer = readFileSync(new URL('.\u002Fdata.proto', import.meta.url));\n             * ```\n             *\u002F\n            url: string;\n",[24,8873,8874,8879,8884,8889,8894,8899,8903,8908,8912,8917,8922,8927,8932,8937],{"__ignoreMap":101},[105,8875,8876],{"class":107,"line":108},[105,8877,8878],{"class":630},"            \u002F**\n",[105,8880,8881],{"class":107,"line":119},[105,8882,8883],{"class":630},"             * The absolute `file:` URL of the module.\n",[105,8885,8886],{"class":107,"line":133},[105,8887,8888],{"class":630},"             *\n",[105,8890,8891],{"class":107,"line":148},[105,8892,8893],{"class":630},"             * This is defined exactly the same as it is in browsers providing the URL of the\n",[105,8895,8896],{"class":107,"line":163},[105,8897,8898],{"class":630},"             * current module file.\n",[105,8900,8901],{"class":107,"line":179},[105,8902,8888],{"class":630},[105,8904,8905],{"class":107,"line":194},[105,8906,8907],{"class":630},"             * This enables useful patterns such as relative file loading:\n",[105,8909,8910],{"class":107,"line":209},[105,8911,8888],{"class":630},[105,8913,8914],{"class":107,"line":526},[105,8915,8916],{"class":630},"             * ```js\n",[105,8918,8919],{"class":107,"line":531},[105,8920,8921],{"class":630},"             * import { readFileSync } from 'node:fs';\n",[105,8923,8924],{"class":107,"line":668},[105,8925,8926],{"class":630},"             * const buffer = readFileSync(new URL('.\u002Fdata.proto', import.meta.url));\n",[105,8928,8929],{"class":107,"line":673},[105,8930,8931],{"class":630},"             * ```\n",[105,8933,8934],{"class":107,"line":679},[105,8935,8936],{"class":630},"             *\u002F\n",[105,8938,8939,8942],{"class":107,"line":689},[105,8940,8941],{"class":241},"            url",[105,8943,8944],{"class":115},": string;\n",[18,8946,8947,8950],{},[24,8948,8949],{},"import.meta"," 在 TypeScript 里的类型定义分几层：",[835,8952,8953,8963],{},[838,8954,8955],{},[841,8956,8957,8960],{},[844,8958,8959],{},"类型定义来源",[844,8961,8962],{},"提供什么",[854,8964,8965,8981,8996,9013,9034],{},[841,8966,8967,8975],{},[859,8968,8969],{},[21,8970,8971,8972,3204],{},"TypeScript 内置（",[24,8973,8974],{},"lib.es5.d.ts",[859,8976,8977,8980],{},[24,8978,8979],{},"ImportMeta"," 空接口",[841,8982,8983,8990],{},[859,8984,8985],{},[21,8986,8987],{},[24,8988,8989],{},"lib.dom.d.ts",[859,8991,8992,8995],{},[24,8993,8994],{},"import.meta.url","（浏览器环境）",[841,8997,8998,9005],{},[859,8999,9000],{},[21,9001,9002],{},[24,9003,9004],{},"@types\u002Fnode",[859,9006,9007,31,9009,9012],{},[24,9008,8994],{},[24,9010,9011],{},"import.meta.resolve","（Node 环境）",[841,9014,9015,9023],{},[859,9016,9017],{},[21,9018,9019,9020,3204],{},"Vite（",[24,9021,9022],{},"vite\u002Fclient",[859,9024,9025,31,9028,31,9031],{},[24,9026,9027],{},"import.meta.env",[24,9029,9030],{},"import.meta.hot",[24,9032,9033],{},"import.meta.glob",[841,9035,9036,9041],{},[859,9037,9038],{},[21,9039,9040],{},"Nuxt",[859,9042,9043,31,9046,31,9049],{},[24,9044,9045],{},"import.meta.client",[24,9047,9048],{},"import.meta.server",[24,9050,9051],{},"import.meta.dev",[18,9053,9054,9055,9057],{},"我们可以看看Vite \u002F Nuxt 对 ",[24,9056,8949],{}," 的类型扩展：",[96,9059,9061],{"className":3241,"code":9060,"language":3243,"meta":101,"style":101},"interface ImportMeta {\n    browser: boolean;\n    client: boolean;\n    dev: boolean;\n    server: boolean;\n    test: boolean;\n}\n",[24,9062,9063,9072,9084,9095,9106,9117,9128],{"__ignoreMap":101},[105,9064,9065,9067,9070],{"class":107,"line":108},[105,9066,3501],{"class":111},[105,9068,9069],{"class":241}," ImportMeta",[105,9071,116],{"class":115},[105,9073,9074,9077,9079,9082],{"class":107,"line":119},[105,9075,9076],{"class":122},"    browser",[105,9078,4999],{"class":111},[105,9080,9081],{"class":129}," boolean",[105,9083,145],{"class":115},[105,9085,9086,9089,9091,9093],{"class":107,"line":133},[105,9087,9088],{"class":122},"    client",[105,9090,4999],{"class":111},[105,9092,9081],{"class":129},[105,9094,145],{"class":115},[105,9096,9097,9100,9102,9104],{"class":107,"line":148},[105,9098,9099],{"class":122},"    dev",[105,9101,4999],{"class":111},[105,9103,9081],{"class":129},[105,9105,145],{"class":115},[105,9107,9108,9111,9113,9115],{"class":107,"line":163},[105,9109,9110],{"class":122},"    server",[105,9112,4999],{"class":111},[105,9114,9081],{"class":129},[105,9116,145],{"class":115},[105,9118,9119,9122,9124,9126],{"class":107,"line":179},[105,9120,9121],{"class":122},"    test",[105,9123,4999],{"class":111},[105,9125,9081],{"class":129},[105,9127,145],{"class":115},[105,9129,9130],{"class":107,"line":194},[105,9131,212],{"class":115},[18,9133,9134,9135,9137,9138,1980,9141,9143],{},"这些用来标记代码运行在什么环境。Vite 在构建时把 ",[24,9136,9045],{}," 替换成 ",[24,9139,9140],{},"true",[24,9142,7820],{},"，运行时零开销。",[14,9145,9146],{"id":9146},"await和事件循环",[18,9148,9149,9150],{},"我们一直说await是等待任务完成，但是原理模型是需要了解的，",[21,9151,9152,9154],{},[24,9153,5383],{}," 不是“阻塞”，它是“挂起当前函数，让出线程”。",[18,9156,9157],{},"同步阻塞说的类似于fs.readFileSync这样的node环境方法，线程卡在这里，什么都做不了，await会让当前函数暂停，让线程去做别的事情，也叫做suspending：",[96,9159,9161],{"className":559,"code":9160,"language":561,"meta":101,"style":101},"\u002F\u002F 异步挂起\nconst data = await fs.readFile('a.txt')  \u002F\u002F 当前函数暂停，线程去处理其他任务\nconsole.log('done')                      \u002F\u002F 等数据到了，函数恢复执行\n",[24,9162,9163,9168,9195],{"__ignoreMap":101},[105,9164,9165],{"class":107,"line":108},[105,9166,9167],{"class":630},"\u002F\u002F 异步挂起\n",[105,9169,9170,9172,9175,9177,9179,9182,9185,9187,9190,9192],{"class":107,"line":119},[105,9171,568],{"class":111},[105,9173,9174],{"class":129}," data",[105,9176,574],{"class":111},[105,9178,5410],{"class":111},[105,9180,9181],{"class":115}," fs.",[105,9183,9184],{"class":241},"readFile",[105,9186,321],{"class":115},[105,9188,9189],{"class":248},"'a.txt'",[105,9191,5422],{"class":115},[105,9193,9194],{"class":630},"\u002F\u002F 当前函数暂停，线程去处理其他任务\n",[105,9196,9197,9199,9201,9203,9206,9209],{"class":107,"line":133},[105,9198,636],{"class":115},[105,9200,639],{"class":241},[105,9202,321],{"class":115},[105,9204,9205],{"class":248},"'done'",[105,9207,9208],{"class":115},")                      ",[105,9210,9211],{"class":630},"\u002F\u002F 等数据到了，函数恢复执行\n",[96,9213,9215],{"className":559,"code":9214,"language":561,"meta":101,"style":101},"async function fetchData() {\n  console.log('1')\n  const data = await fetch('\u002Fapi')  \u002F\u002F 挂起点\n  console.log('2')\n}\n\nfetchData()\nconsole.log('3')\n\n1. 执行 fetchData，打印 \"1\"\n2. 遇到 await，fetchData 挂起，返回一个 Promise\n3. 主线程继续执行，打印 \"3\"\n4. fetch 完成后，fetchData 恢复，打印 \"2\"\n",[24,9216,9217,9230,9243,9265,9278,9282,9286,9293,9306,9310,9321,9337,9348],{"__ignoreMap":101},[105,9218,9219,9222,9224,9227],{"class":107,"line":108},[105,9220,9221],{"class":111},"async",[105,9223,4982],{"class":111},[105,9225,9226],{"class":241}," fetchData",[105,9228,9229],{"class":115},"() {\n",[105,9231,9232,9234,9236,9238,9241],{"class":107,"line":119},[105,9233,3330],{"class":115},[105,9235,639],{"class":241},[105,9237,321],{"class":115},[105,9239,9240],{"class":248},"'1'",[105,9242,1511],{"class":115},[105,9244,9245,9247,9249,9251,9253,9255,9257,9260,9262],{"class":107,"line":133},[105,9246,4101],{"class":111},[105,9248,9174],{"class":129},[105,9250,574],{"class":111},[105,9252,5410],{"class":111},[105,9254,5982],{"class":241},[105,9256,321],{"class":115},[105,9258,9259],{"class":248},"'\u002Fapi'",[105,9261,5422],{"class":115},[105,9263,9264],{"class":630},"\u002F\u002F 挂起点\n",[105,9266,9267,9269,9271,9273,9276],{"class":107,"line":148},[105,9268,3330],{"class":115},[105,9270,639],{"class":241},[105,9272,321],{"class":115},[105,9274,9275],{"class":248},"'2'",[105,9277,1511],{"class":115},[105,9279,9280],{"class":107,"line":163},[105,9281,212],{"class":115},[105,9283,9284],{"class":107,"line":179},[105,9285,307],{"emptyLinePlaceholder":306},[105,9287,9288,9291],{"class":107,"line":194},[105,9289,9290],{"class":241},"fetchData",[105,9292,5174],{"class":115},[105,9294,9295,9297,9299,9301,9304],{"class":107,"line":209},[105,9296,636],{"class":115},[105,9298,639],{"class":241},[105,9300,321],{"class":115},[105,9302,9303],{"class":248},"'3'",[105,9305,1511],{"class":115},[105,9307,9308],{"class":107,"line":526},[105,9309,307],{"emptyLinePlaceholder":306},[105,9311,9312,9315,9318],{"class":107,"line":531},[105,9313,9314],{"class":129},"1.",[105,9316,9317],{"class":115}," 执行 fetchData，打印 ",[105,9319,9320],{"class":248},"\"1\"\n",[105,9322,9323,9326,9329,9331,9334],{"class":107,"line":668},[105,9324,9325],{"class":129},"2.",[105,9327,9328],{"class":115}," 遇到 ",[105,9330,5383],{"class":111},[105,9332,9333],{"class":115},"，fetchData 挂起，返回一个 ",[105,9335,9336],{"class":129},"Promise\n",[105,9338,9339,9342,9345],{"class":107,"line":673},[105,9340,9341],{"class":129},"3.",[105,9343,9344],{"class":115}," 主线程继续执行，打印 ",[105,9346,9347],{"class":248},"\"3\"\n",[105,9349,9350,9353,9356],{"class":107,"line":679},[105,9351,9352],{"class":129},"4.",[105,9354,9355],{"class":115}," fetch 完成后，fetchData 恢复，打印 ",[105,9357,9358],{"class":248},"\"2\"\n",[18,9360,9361,9362,9365,9366],{},"我们知道await挂起的是async方法，不是整个程序，await之后的代码会被注册为Promise的.then回调，这个回调进入的是微任务队列，而不是宏任务队列。\n",[21,9363,9364],{},"微任务队列","和",[21,9367,9368],{},"宏任务队列",[835,9370,9371,9384],{},[838,9372,9373],{},[841,9374,9375,9378,9381],{},[844,9376,9377],{},"队列",[844,9379,9380],{},"存放内容",[844,9382,9383],{},"执行时机",[854,9385,9386,9401],{},[841,9387,9388,9392,9395],{},[859,9389,9390],{},[21,9391,9364],{},[859,9393,9394],{},"Promise.then、await 恢复、MutationObserver",[859,9396,9397,9398],{},"每个宏任务结束后，",[21,9399,9400],{},"清空所有微任务",[841,9402,9403,9407,9410],{},[859,9404,9405],{},[21,9406,9368],{},[859,9408,9409],{},"setTimeout、setInterval、I\u002FO、UI 渲染",[859,9411,9412,9413,9416],{},"每轮事件循环取",[21,9414,9415],{},"一个","宏任务",[18,9418,9419],{},"我们知道事件的循环是： 执行调用栈里的同步代码，调用栈清空，开始执行微任务队列，微任务队列清空，执行下一个宏任务，然后继续调用栈清空。",[18,9421,9422,9424,9425,9428],{},[24,9423,5383],{}," 恢复是微任务，所以",[21,9426,9427],{},"会在调用栈清空后立刻执行","，不会等 UI 渲染或 setTimeout。",[96,9430,9432],{"className":559,"code":9431,"language":561,"meta":101,"style":101},"console.log('A')\n\nsetTimeout(() => console.log('B'), 0)  \u002F\u002F 宏任务\n\nPromise.resolve().then(() => console.log('C'))  \u002F\u002F 微任务\n\nasync function foo() {\n  console.log('D')\n  await null  \u002F\u002F 挂起\n  console.log('E')  \u002F\u002F 微任务\n}\n\nfoo()\n\nconsole.log('F')\n\nA          ← 同步\nD          ← 同步\nF          ← 同步\nC          ← 微任务\nE          ← 微任务（await 恢复）\nB          ← 宏任务\n",[24,9433,9434,9447,9451,9479,9483,9516,9520,9530,9543,9553,9568,9572,9576,9583,9587,9600,9604,9612,9619,9626,9634,9647],{"__ignoreMap":101},[105,9435,9436,9438,9440,9442,9445],{"class":107,"line":108},[105,9437,636],{"class":115},[105,9439,639],{"class":241},[105,9441,321],{"class":115},[105,9443,9444],{"class":248},"'A'",[105,9446,1511],{"class":115},[105,9448,9449],{"class":107,"line":119},[105,9450,307],{"emptyLinePlaceholder":306},[105,9452,9453,9456,9458,9460,9462,9464,9466,9469,9472,9474,9476],{"class":107,"line":133},[105,9454,9455],{"class":241},"setTimeout",[105,9457,4052],{"class":115},[105,9459,723],{"class":111},[105,9461,5522],{"class":115},[105,9463,639],{"class":241},[105,9465,321],{"class":115},[105,9467,9468],{"class":248},"'B'",[105,9470,9471],{"class":115},"), ",[105,9473,1100],{"class":129},[105,9475,5422],{"class":115},[105,9477,9478],{"class":630},"\u002F\u002F 宏任务\n",[105,9480,9481],{"class":107,"line":148},[105,9482,307],{"emptyLinePlaceholder":306},[105,9484,9485,9488,9490,9493,9495,9497,9499,9501,9503,9505,9507,9510,9513],{"class":107,"line":163},[105,9486,9487],{"class":129},"Promise",[105,9489,5363],{"class":115},[105,9491,9492],{"class":241},"resolve",[105,9494,5745],{"class":115},[105,9496,5512],{"class":241},[105,9498,4052],{"class":115},[105,9500,723],{"class":111},[105,9502,5522],{"class":115},[105,9504,639],{"class":241},[105,9506,321],{"class":115},[105,9508,9509],{"class":248},"'C'",[105,9511,9512],{"class":115},"))  ",[105,9514,9515],{"class":630},"\u002F\u002F 微任务\n",[105,9517,9518],{"class":107,"line":179},[105,9519,307],{"emptyLinePlaceholder":306},[105,9521,9522,9524,9526,9528],{"class":107,"line":194},[105,9523,9221],{"class":111},[105,9525,4982],{"class":111},[105,9527,8758],{"class":241},[105,9529,9229],{"class":115},[105,9531,9532,9534,9536,9538,9541],{"class":107,"line":209},[105,9533,3330],{"class":115},[105,9535,639],{"class":241},[105,9537,321],{"class":115},[105,9539,9540],{"class":248},"'D'",[105,9542,1511],{"class":115},[105,9544,9545,9548,9550],{"class":107,"line":526},[105,9546,9547],{"class":111},"  await",[105,9549,5026],{"class":129},[105,9551,9552],{"class":630},"  \u002F\u002F 挂起\n",[105,9554,9555,9557,9559,9561,9564,9566],{"class":107,"line":531},[105,9556,3330],{"class":115},[105,9558,639],{"class":241},[105,9560,321],{"class":115},[105,9562,9563],{"class":248},"'E'",[105,9565,5422],{"class":115},[105,9567,9515],{"class":630},[105,9569,9570],{"class":107,"line":668},[105,9571,212],{"class":115},[105,9573,9574],{"class":107,"line":673},[105,9575,307],{"emptyLinePlaceholder":306},[105,9577,9578,9581],{"class":107,"line":679},[105,9579,9580],{"class":241},"foo",[105,9582,5174],{"class":115},[105,9584,9585],{"class":107,"line":689},[105,9586,307],{"emptyLinePlaceholder":306},[105,9588,9589,9591,9593,9595,9598],{"class":107,"line":699},[105,9590,636],{"class":115},[105,9592,639],{"class":241},[105,9594,321],{"class":115},[105,9596,9597],{"class":248},"'F'",[105,9599,1511],{"class":115},[105,9601,9602],{"class":107,"line":704},[105,9603,307],{"emptyLinePlaceholder":306},[105,9605,9606,9609],{"class":107,"line":710},[105,9607,9608],{"class":129},"A",[105,9610,9611],{"class":115},"          ← 同步\n",[105,9613,9614,9617],{"class":107,"line":1136},[105,9615,9616],{"class":129},"D",[105,9618,9611],{"class":115},[105,9620,9621,9624],{"class":107,"line":1149},[105,9622,9623],{"class":129},"F",[105,9625,9611],{"class":115},[105,9627,9628,9631],{"class":107,"line":3837},[105,9629,9630],{"class":129},"C",[105,9632,9633],{"class":115},"          ← 微任务\n",[105,9635,9636,9639,9642,9644],{"class":107,"line":3847},[105,9637,9638],{"class":129},"E",[105,9640,9641],{"class":115},"          ← 微任务（",[105,9643,5383],{"class":111},[105,9645,9646],{"class":115}," 恢复）\n",[105,9648,9649,9652],{"class":107,"line":3857},[105,9650,9651],{"class":129},"B",[105,9653,9654],{"class":115},"          ← 宏任务\n",[18,9656,9657],{},"E在B之前，因为await恢复是微任务，setTimeout是宏任务，我们分析就可以知道，微任务队列里面有Promise.then、await恢复，宏任务队列里面有SetTimeout。因此顺序就是同步A同步D同步F微任务C微任务E宏任务B。",[9659,9660,9661],"blockquote",{},[18,9662,9663,7813,9666],{},[105,9664,9665],{},"!tip",[21,9667,9668],{},"事件循环负责协调调用栈、微任务队列和宏任务队列。同步代码在调用栈中执行；调用栈清空后，先清空所有微任务；然后取一个宏任务执行；如此循环。",[14,9670,9672],{"id":9671},"js的核心运行机制","JS的核心运行机制",[18,9674,9675],{},"刚才讲了同步、异步方法的细节，现在来讲讲执行上下文，这个是理解JS运行原理的核心，await、事件循环、this、变量查找都建立在执行上下文。",[18,9677,9678],{},"执行上下文是代码执行的环境，记录了： 变量环境（var、函数声明）、词法环境（let、const、块级作用域）、this绑定（当前函数的this指向）、外部环境引用（作用域链，用于变量查找）、代码类型（全局、函数、eval）。 每一次函数调用，都会创建一个新的执行上下文。",[18,9680,9681],{},"我们知道，js有三种上下文，分别是全局上下文，这个是脚本加载的时候创建的，是整个脚本的顶层代码。然后是函数上下文，每次调用函数的时候创建。最后是Eval上下文，eval()调用的时候会创建，很少使用。",[18,9683,9684],{},"全局上下文只有一个，函数上下文每次调用都会调用新建。‘",[96,9686,9689],{"className":9687,"code":9688,"language":392},[390],"1. 创建阶段\n   ├── 创建变量环境（var、函数声明）\n   ├── 创建词法环境（let、const）\n   ├── 确定 this 绑定\n   └── 建立作用域链\n\n2. 执行阶段\n   ├── 逐行执行代码\n   ├── 变量赋值\n   └── 函数调用（创建新的执行上下文）\n\n3. 销毁阶段\n   └── 函数返回，上下文出栈\n",[24,9690,9688],{"__ignoreMap":101},[18,9692,9693],{},"我举例一段代码：",[96,9695,9698],{"className":9696,"code":9697,"language":392},[390],"function a() {\n  console.log('a')\n  b()\n}\n\nfunction b() {\n  console.log('b')\n  c()\n}\n\nfunction c() {\n  console.log('c')\n}\n\na()\n",[24,9699,9697],{"__ignoreMap":101},[18,9701,9702],{},"上下文栈的变化：",[96,9704,9707],{"className":9705,"code":9706,"language":392},[390],"1. 全局上下文入栈\n   [全局]\n\n2. 调用 a()，a 的上下文入栈\n   [全局, a]\n\n3. a 里调用 b()，b 的上下文入栈\n   [全局, a, b]\n\n4. b 里调用 c()，c 的上下文入栈\n   [全局, a, b, c]\n\n5. c 执行完，c 出栈\n   [全局, a, b]\n\n6. b 执行完，b 出栈\n   [全局, a]\n\n7. a 执行完，a 出栈\n   [全局]\n\n8. 全局执行完，全局出栈\n   []\n",[24,9708,9706],{"__ignoreMap":101},[18,9710,9711],{},"然后我们来聊聊执行上下文和作用域链的关系：",[96,9713,9715],{"className":559,"code":9714,"language":561,"meta":101,"style":101},"function outer() {\n  const a = 1\n\n  function inner() {\n    const b = 2\n    console.log(a + b)  \u002F\u002F 3\n  }\n\n  inner()\n}\n",[24,9716,9717,9726,9738,9742,9751,9763,9780,9784,9788,9795],{"__ignoreMap":101},[105,9718,9719,9721,9724],{"class":107,"line":108},[105,9720,8113],{"class":111},[105,9722,9723],{"class":241}," outer",[105,9725,9229],{"class":115},[105,9727,9728,9730,9733,9735],{"class":107,"line":119},[105,9729,4101],{"class":111},[105,9731,9732],{"class":129}," a",[105,9734,574],{"class":111},[105,9736,9737],{"class":129}," 1\n",[105,9739,9740],{"class":107,"line":133},[105,9741,307],{"emptyLinePlaceholder":306},[105,9743,9744,9746,9749],{"class":107,"line":148},[105,9745,5187],{"class":111},[105,9747,9748],{"class":241}," inner",[105,9750,9229],{"class":115},[105,9752,9753,9755,9758,9760],{"class":107,"line":163},[105,9754,7220],{"class":111},[105,9756,9757],{"class":129}," b",[105,9759,574],{"class":111},[105,9761,9762],{"class":129}," 2\n",[105,9764,9765,9767,9769,9772,9774,9777],{"class":107,"line":179},[105,9766,6662],{"class":115},[105,9768,639],{"class":241},[105,9770,9771],{"class":115},"(a ",[105,9773,1085],{"class":111},[105,9775,9776],{"class":115}," b)  ",[105,9778,9779],{"class":630},"\u002F\u002F 3\n",[105,9781,9782],{"class":107,"line":194},[105,9783,509],{"class":115},[105,9785,9786],{"class":107,"line":209},[105,9787,307],{"emptyLinePlaceholder":306},[105,9789,9790,9793],{"class":107,"line":526},[105,9791,9792],{"class":241},"  inner",[105,9794,5174],{"class":115},[105,9796,9797],{"class":107,"line":531},[105,9798,212],{"class":115},[18,9800,9801],{},"用全局的视角来看，outer函数里包裹一个innre函数，inner的执行上下文（变量环境有b: 2，词法环境有b: 2）里面应该是找不到a的，那么他就会沿着作用域向上寻找，它找到了outer上下文的词法环境（变量环境a:1，外部引用->全局上下文）。",[18,9803,9804],{},"作用域链就是执行上下文的外部引用链，变量查找会沿着这条链向上寻找。",[18,9806,9807],{},"再来深入一点点，刚才我们说了变量环境和词法环境，这两个都是决定变量存放地方和查找、可访问的概念。",[96,9809,9812],{"className":9810,"code":9811,"language":392},[390],"执行上下文\n├── 词法环境（Lexical Environment）\n│   ├── 环境记录（Environment Record）\n│   │   ├── 声明式环境记录（let、const、class、函数声明）\n│   │   └── 对象环境记录（with、global 的 var）\n│   └── 外部环境引用（outer）\n│\n├── 变量环境（Variable Environment）\n│   └── 环境记录（主要放 var、函数声明）\n│\n└── this 绑定\n",[24,9813,9811],{"__ignoreMap":101},[18,9815,9816],{},"词法环境由环境记录、外部环境引用组成，环境记录存放的是let const class 函数声明 catch参数组成。外部环境记录指向外部词法环境，形成了作用域链。全局词法环境的外部引用指向null。块级词法环境的外部引用指向全局词法环境。",[18,9818,9819],{},"var和函数声明进入的是变量环境，是因为var和let\u002Fconst的作用域规则不同，var的作用域是函数级，变量会提升，初始化为undefined，如果是全局的话，会挂载到window上，let\u002Fconst作用域是块级，可以提升但是不会初始化（TDZ，这个我们了解过），不会挂载到window。",[18,9821,9822],{},"变量查找顺序",[18,9824,9825,9826,9829],{},"当代码里访问一个变量 ",[24,9827,9828],{},"x","：",[96,9831,9834],{"className":9832,"code":9833,"language":392},[390],"1. 先查当前词法环境的环境记录\n2. 找不到 → 查当前变量环境的环境记录\n3. 找不到 → 沿词法环境的外部引用往上找\n4. 找不到 → 沿变量环境的外部引用往上找\n5. 到全局还找不到 → ReferenceError\n",[24,9835,9833],{"__ignoreMap":101},[18,9837,9838],{},"块级作用域实现",[96,9840,9842],{"className":559,"code":9841,"language":561,"meta":101,"style":101},"{\n  let a = 1\n  const b = 2\n}\n",[24,9843,9844,9849,9861,9871],{"__ignoreMap":101},[105,9845,9846],{"class":107,"line":108},[105,9847,9848],{"class":115},"{\n",[105,9850,9851,9854,9857,9859],{"class":107,"line":119},[105,9852,9853],{"class":111},"  let",[105,9855,9856],{"class":115}," a ",[105,9858,245],{"class":111},[105,9860,9737],{"class":129},[105,9862,9863,9865,9867,9869],{"class":107,"line":133},[105,9864,4101],{"class":111},[105,9866,9757],{"class":129},[105,9868,574],{"class":111},[105,9870,9762],{"class":129},[105,9872,9873],{"class":107,"line":148},[105,9874,212],{"class":115},[18,9876,9877],{},"每进入一个代码块{}，就会创建一个新的词法环境。",[96,9879,9882],{"className":9880,"code":9881,"language":392},[390],"外层词法环境\n  ├── 环境记录: {}\n  └── 外部引用: null\n\n内层词法环境（{} 内）\n  ├── 环境记录: { a: 1, b: 2 }\n  └── 外部引用: → 外层词法环境\n",[24,9883,9881],{"__ignoreMap":101},[18,9885,9886],{},"这个可以多记忆一点，因为块级作用域的本质就是进入块级的时候创建词法环境，离开的时候销毁。let也会创建一个块级词法环境，for循环是let面试经常被问到的，为什么let可以解决var闭包问题。",[96,9888,9890],{"className":559,"code":9889,"language":561,"meta":101,"style":101},"for (var i = 0; i \u003C 3; i++) {\n  setTimeout(() => console.log(i), 0)\n}\n\u002F\u002F 输出：3 3 3\n",[24,9891,9892,9923,9943,9947],{"__ignoreMap":101},[105,9893,9894,9897,9899,9901,9904,9906,9908,9911,9913,9916,9919,9921],{"class":107,"line":108},[105,9895,9896],{"class":111},"for",[105,9898,4361],{"class":115},[105,9900,1749],{"class":111},[105,9902,9903],{"class":115}," i ",[105,9905,245],{"class":111},[105,9907,2737],{"class":129},[105,9909,9910],{"class":115},"; i ",[105,9912,234],{"class":111},[105,9914,9915],{"class":129}," 3",[105,9917,9918],{"class":115},"; i",[105,9920,2876],{"class":111},[105,9922,1000],{"class":115},[105,9924,9925,9928,9930,9932,9934,9936,9939,9941],{"class":107,"line":119},[105,9926,9927],{"class":241},"  setTimeout",[105,9929,4052],{"class":115},[105,9931,723],{"class":111},[105,9933,5522],{"class":115},[105,9935,639],{"class":241},[105,9937,9938],{"class":115},"(i), ",[105,9940,1100],{"class":129},[105,9942,1511],{"class":115},[105,9944,9945],{"class":107,"line":133},[105,9946,212],{"class":115},[105,9948,9949],{"class":107,"line":148},[105,9950,9951],{"class":630},"\u002F\u002F 输出：3 3 3\n",[18,9953,9954],{},"var i 是函数级作用域，只有一个i，从结构上来看，全局执行上下文下是变量环境，变量环境里有i: 3（循环结束）。三个setTimeout回调在循环结束后执行，此时i = 3，三个闭包引用的是同一个i。而let在for循环中，每次迭代会创建一个新的词法环境，并复制上一轮的变量值。",[9659,9956,9957],{},[18,9958,9959,7813,9961],{},[105,9960,9665],{},[21,9962,9963,9965,9966,9968],{},[24,9964,4338],{}," 的“每次声明创建新绑定”语义，既导致了“不能重复声明”，也导致了“",[24,9967,9896],{}," 循环每轮新环境”。两者是同一规则的两个表现，不是因果关系。",[18,9970,9971],{},"函数作用域的实现",[96,9973,9975],{"className":559,"code":9974,"language":561,"meta":101,"style":101},"function foo() {\n  var a = 1\n  function bar() {}\n}\n",[24,9976,9977,9985,9996,10006],{"__ignoreMap":101},[105,9978,9979,9981,9983],{"class":107,"line":108},[105,9980,8113],{"class":111},[105,9982,8758],{"class":241},[105,9984,9229],{"class":115},[105,9986,9987,9990,9992,9994],{"class":107,"line":119},[105,9988,9989],{"class":111},"  var",[105,9991,9856],{"class":115},[105,9993,245],{"class":111},[105,9995,9737],{"class":129},[105,9997,9998,10000,10003],{"class":107,"line":133},[105,9999,5187],{"class":111},[105,10001,10002],{"class":241}," bar",[105,10004,10005],{"class":115},"() {}\n",[105,10007,10008],{"class":107,"line":148},[105,10009,212],{"class":115},[18,10011,10012],{},"函数在调用时候创建新的执行上下文，foo函数有词法环境、变量环境，这个都知道。然后this指向undefined。",[18,10014,10015],{},"闭包的实现",[96,10017,10019],{"className":559,"code":10018,"language":561,"meta":101,"style":101},"function outer() {\n  let a = 1\n\n  function inner() {\n    console.log(a)  \u002F\u002F 访问外层的 a\n  }\n\n  return inner\n}\n\nconst fn = outer()\nfn()  \u002F\u002F 1\n",[24,10020,10021,10029,10039,10043,10051,10063,10067,10071,10078,10082,10086,10099],{"__ignoreMap":101},[105,10022,10023,10025,10027],{"class":107,"line":108},[105,10024,8113],{"class":111},[105,10026,9723],{"class":241},[105,10028,9229],{"class":115},[105,10030,10031,10033,10035,10037],{"class":107,"line":119},[105,10032,9853],{"class":111},[105,10034,9856],{"class":115},[105,10036,245],{"class":111},[105,10038,9737],{"class":129},[105,10040,10041],{"class":107,"line":133},[105,10042,307],{"emptyLinePlaceholder":306},[105,10044,10045,10047,10049],{"class":107,"line":148},[105,10046,5187],{"class":111},[105,10048,9748],{"class":241},[105,10050,9229],{"class":115},[105,10052,10053,10055,10057,10060],{"class":107,"line":163},[105,10054,6662],{"class":115},[105,10056,639],{"class":241},[105,10058,10059],{"class":115},"(a)  ",[105,10061,10062],{"class":630},"\u002F\u002F 访问外层的 a\n",[105,10064,10065],{"class":107,"line":179},[105,10066,509],{"class":115},[105,10068,10069],{"class":107,"line":194},[105,10070,307],{"emptyLinePlaceholder":306},[105,10072,10073,10075],{"class":107,"line":209},[105,10074,4126],{"class":111},[105,10076,10077],{"class":115}," inner\n",[105,10079,10080],{"class":107,"line":526},[105,10081,212],{"class":115},[105,10083,10084],{"class":107,"line":531},[105,10085,307],{"emptyLinePlaceholder":306},[105,10087,10088,10090,10093,10095,10097],{"class":107,"line":668},[105,10089,568],{"class":111},[105,10091,10092],{"class":129}," fn",[105,10094,574],{"class":111},[105,10096,9723],{"class":241},[105,10098,5174],{"class":115},[105,10100,10101,10104,10106],{"class":107,"line":673},[105,10102,10103],{"class":241},"fn",[105,10105,2984],{"class":115},[105,10107,10108],{"class":630},"\u002F\u002F 1\n",[18,10110,10111],{},"inner返回后，它的执行上下文应该被销毁的，但是我们发现inner作用域引用了outer的词法环境，所以outer的词法环境不会被回收。闭包就相当于一个函数+他创建时候的词法环境链。",[18,10113,10114],{},"作用域和执行上下文的关系",[18,10116,10117],{},"这个可能在面试会被提及，作用域是静态的词法概念，执行上下文是运行时概念。前者决定能够访问哪些变量，后者是执行时的环境。我们写代码的时候发现inner可以访问outer里的属性，这个就是写代码时候确认的，inner的词法作用域链上包含outer。",[14,10119,10120],{"id":10120},"this问题",[18,10122,10123,10124,10127,10128,10131],{},"this是",[21,10125,10126],{},"函数执行时的上下文对象","，它的值取决于",[21,10129,10130],{},"函数怎么被调用","，而不是怎么被定义（箭头函数除外）。",[18,10133,10134],{},"js里有五种绑定this的方法，分别是new绑定，this就会指向新创建的对象，比如说new Foo()，然后是显示绑定，call\u002Fapply\u002Fbind 指定对象，比如foo.call(this)，再就是隐式绑定，this会指向调用方法的对象，比如obj.foo()，再来是默认绑定，this会指向undefined(严格)\u002F(非严格)，最后是箭头函数，this会指向外层作用域this，()->this。",[18,10136,10137,9829,10140,10142],{},[21,10138,10139],{},"优先级高的规则赢",[24,10141,7412],{}," > 显式 > 隐式 > 默认。\n箭头函数比较特殊，它不遵循前面4条规则，而是词法绑定，所以要单独列出来说。new呢，是最高优先级：",[96,10144,10146],{"className":559,"code":10145,"language":561,"meta":101,"style":101},"function Foo(name) {\n  this.name = name\n}\n\nconst obj = new Foo('张三')\nconsole.log(obj.name)  \u002F\u002F '张三'\n",[24,10147,10148,10161,10174,10178,10182,10202],{"__ignoreMap":101},[105,10149,10150,10152,10155,10157,10159],{"class":107,"line":108},[105,10151,8113],{"class":111},[105,10153,10154],{"class":241}," Foo",[105,10156,321],{"class":115},[105,10158,4996],{"class":122},[105,10160,1000],{"class":115},[105,10162,10163,10166,10169,10171],{"class":107,"line":119},[105,10164,10165],{"class":129},"  this",[105,10167,10168],{"class":115},".name ",[105,10170,245],{"class":111},[105,10172,10173],{"class":115}," name\n",[105,10175,10176],{"class":107,"line":133},[105,10177,212],{"class":115},[105,10179,10180],{"class":107,"line":148},[105,10181,307],{"emptyLinePlaceholder":306},[105,10183,10184,10186,10189,10191,10193,10195,10197,10200],{"class":107,"line":163},[105,10185,568],{"class":111},[105,10187,10188],{"class":129}," obj",[105,10190,574],{"class":111},[105,10192,3478],{"class":111},[105,10194,10154],{"class":241},[105,10196,321],{"class":115},[105,10198,10199],{"class":248},"'张三'",[105,10201,1511],{"class":115},[105,10203,10204,10206,10208,10211],{"class":107,"line":179},[105,10205,636],{"class":115},[105,10207,639],{"class":241},[105,10209,10210],{"class":115},"(obj.name)  ",[105,10212,10213],{"class":630},"\u002F\u002F '张三'\n",[18,10215,10216],{},"我们在typecsriptteam.native-preview中可以看看Function的定义：",[96,10218,10220],{"className":3241,"code":10219,"language":3243,"meta":101,"style":101},"interface FunctionConstructor {\n    \u002F**\n     * Creates a new function.\n     * @param args A list of arguments the function accepts.\n     *\u002F\n    new (...args: string[]): Function;\n    (...args: string[]): Function;\n    readonly prototype: Function;\n}\n\ndeclare var Function: FunctionConstructor;\n",[24,10221,10222,10231,10236,10241,10255,10260,10287,10308,10322,10326,10330],{"__ignoreMap":101},[105,10223,10224,10226,10229],{"class":107,"line":108},[105,10225,3501],{"class":111},[105,10227,10228],{"class":241}," FunctionConstructor",[105,10230,116],{"class":115},[105,10232,10233],{"class":107,"line":119},[105,10234,10235],{"class":630},"    \u002F**\n",[105,10237,10238],{"class":107,"line":133},[105,10239,10240],{"class":630},"     * Creates a new function.\n",[105,10242,10243,10246,10249,10252],{"class":107,"line":148},[105,10244,10245],{"class":630},"     * ",[105,10247,10248],{"class":111},"@param",[105,10250,10251],{"class":115}," args",[105,10253,10254],{"class":630}," A list of arguments the function accepts.\n",[105,10256,10257],{"class":107,"line":163},[105,10258,10259],{"class":630},"     *\u002F\n",[105,10261,10262,10265,10267,10270,10273,10275,10277,10280,10282,10285],{"class":107,"line":179},[105,10263,10264],{"class":111},"    new",[105,10266,4361],{"class":115},[105,10268,10269],{"class":111},"...",[105,10271,10272],{"class":122},"args",[105,10274,4999],{"class":111},[105,10276,5002],{"class":129},[105,10278,10279],{"class":115},"[])",[105,10281,4999],{"class":111},[105,10283,10284],{"class":241}," Function",[105,10286,145],{"class":115},[105,10288,10289,10292,10294,10296,10298,10300,10302,10304,10306],{"class":107,"line":194},[105,10290,10291],{"class":115},"    (",[105,10293,10269],{"class":111},[105,10295,10272],{"class":122},[105,10297,4999],{"class":111},[105,10299,5002],{"class":129},[105,10301,10279],{"class":115},[105,10303,4999],{"class":111},[105,10305,10284],{"class":241},[105,10307,145],{"class":115},[105,10309,10310,10313,10316,10318,10320],{"class":107,"line":209},[105,10311,10312],{"class":111},"    readonly",[105,10314,10315],{"class":241}," prototype",[105,10317,4999],{"class":111},[105,10319,10284],{"class":241},[105,10321,145],{"class":115},[105,10323,10324],{"class":107,"line":526},[105,10325,212],{"class":115},[105,10327,10328],{"class":107,"line":531},[105,10329,307],{"emptyLinePlaceholder":306},[105,10331,10332,10335,10338,10340,10342,10344],{"class":107,"line":668},[105,10333,10334],{"class":111},"declare",[105,10336,10337],{"class":111}," var",[105,10339,10284],{"class":115},[105,10341,4999],{"class":111},[105,10343,10228],{"class":241},[105,10345,145],{"class":115},[18,10347,10348],{},"new Function()和 Function()效果一样，都返回新函数，这个是Function的特殊之处，Function既是构造函数也是普通函数。readonly prototype: Function 表示Function.prototype是不可重新赋值的，表示Function.prototype是不可重新赋值。",[14,10350,10351],{"id":10351},"arguments",[18,10353,10354,10355,10358],{},"arguments是普通函数内部自动可用的一个",[21,10356,10357],{},"类数组对象","，包含了调用该函数时候传入的所有实参，他不是数组，但是长得像数组，可以按索引访问，也有length属性。",[96,10360,10363],{"className":10361,"code":10362,"language":392},[390],"function foo(a, b) {\n    console.log(arguments);        \u002F\u002F [1, 2, 3]（类数组）\n    console.log(arguments.length); \u002F\u002F 3\n    console.log(arguments[0]);     \u002F\u002F 1\n    console.log(arguments[1]);     \u002F\u002F 2\n    console.log(arguments[2]);     \u002F\u002F 3\n}\n\nfoo(1, 2, 3);\n",[24,10364,10362],{"__ignoreMap":101},[18,10366,10367],{},"而 fn: Function ， fn.length访问的就是这个方法至少需要多少个参数了，并且箭头函数也是有的，你可以访问得到。",[14,10369,10370],{"id":10370},"call实现",[96,10372,10374],{"className":3241,"code":10373,"language":3243,"meta":101,"style":101},"export {}\n\ndeclare global {\n  interface Function {\n    customBind(thisArg: any, ...args: any[]): (...innerArgs: any[]) => any\n  }\n}\n\nFunction.prototype.customBind = function (thisArg: any, ...args: any[]) {\n  const originalFn = this\n\n  if (typeof originalFn !== 'function') {\n    throw new TypeError('Function.prototype.customBind is not a function')\n  }\n\n  function boundFn(...innerArgs: any[]) {\n    const totalArgs = args.concat(innerArgs)\n\n    \u002F\u002F new 调用：忽略 thisArg，执行 new originalFn\n    if (new.target) {\n      return new (originalFn as any)(...totalArgs)\n    }\n\n    \u002F\u002F 普通调用：绑定 thisArg\n    return originalFn.apply(thisArg, totalArgs)\n  }\n\n  \u002F\u002F 不设置 boundFn.prototype，保持和原生 bind 一致\n  return boundFn\n}\n\n\u002F\u002F 测试\nconst obj = {\n    name: \"John\",\n    sayHello: function() {\n        console.log(`Hello, ${this.name}`);\n    }\n}\nconst sayHello = obj.sayHello.customBind(obj);\nsayHello(); \u002F\u002F Hello, John\nconst sayHello2 = obj.sayHello.bind(obj);\nsayHello2(); \u002F\u002F Hello, John\n",[24,10375,10376,10383,10387,10394,10403,10451,10455,10459,10463,10502,10514,10518,10536,10553,10557,10561,10580,10598,10602,10607,10622,10645,10649,10653,10658,10672,10676,10680,10685,10692,10696,10700,10705,10715,10725,10736,10758,10762,10766,10783,10794,10810],{"__ignoreMap":101},[105,10377,10378,10380],{"class":107,"line":108},[105,10379,4979],{"class":111},[105,10381,10382],{"class":115}," {}\n",[105,10384,10385],{"class":107,"line":119},[105,10386,307],{"emptyLinePlaceholder":306},[105,10388,10389,10391],{"class":107,"line":133},[105,10390,10334],{"class":111},[105,10392,10393],{"class":115}," global {\n",[105,10395,10396,10399,10401],{"class":107,"line":148},[105,10397,10398],{"class":111},"  interface",[105,10400,10284],{"class":241},[105,10402,116],{"class":115},[105,10404,10405,10408,10410,10413,10415,10418,10420,10422,10424,10426,10428,10430,10432,10434,10436,10439,10441,10443,10446,10448],{"class":107,"line":163},[105,10406,10407],{"class":241},"    customBind",[105,10409,321],{"class":115},[105,10411,10412],{"class":122},"thisArg",[105,10414,4999],{"class":111},[105,10416,10417],{"class":129}," any",[105,10419,286],{"class":115},[105,10421,10269],{"class":111},[105,10423,10272],{"class":122},[105,10425,4999],{"class":111},[105,10427,10417],{"class":129},[105,10429,10279],{"class":115},[105,10431,4999],{"class":111},[105,10433,4361],{"class":115},[105,10435,10269],{"class":111},[105,10437,10438],{"class":122},"innerArgs",[105,10440,4999],{"class":111},[105,10442,10417],{"class":129},[105,10444,10445],{"class":115},"[]) ",[105,10447,723],{"class":111},[105,10449,10450],{"class":129}," any\n",[105,10452,10453],{"class":107,"line":179},[105,10454,509],{"class":115},[105,10456,10457],{"class":107,"line":194},[105,10458,212],{"class":115},[105,10460,10461],{"class":107,"line":209},[105,10462,307],{"emptyLinePlaceholder":306},[105,10464,10465,10468,10470,10472,10474,10477,10479,10481,10483,10485,10487,10489,10491,10493,10495,10497,10499],{"class":107,"line":526},[105,10466,10467],{"class":129},"Function",[105,10469,5363],{"class":115},[105,10471,8239],{"class":129},[105,10473,5363],{"class":115},[105,10475,10476],{"class":241},"customBind",[105,10478,574],{"class":111},[105,10480,4982],{"class":111},[105,10482,4361],{"class":115},[105,10484,10412],{"class":122},[105,10486,4999],{"class":111},[105,10488,10417],{"class":129},[105,10490,286],{"class":115},[105,10492,10269],{"class":111},[105,10494,10272],{"class":122},[105,10496,4999],{"class":111},[105,10498,10417],{"class":129},[105,10500,10501],{"class":115},"[]) {\n",[105,10503,10504,10506,10509,10511],{"class":107,"line":531},[105,10505,4101],{"class":111},[105,10507,10508],{"class":129}," originalFn",[105,10510,574],{"class":111},[105,10512,10513],{"class":129}," this\n",[105,10515,10516],{"class":107,"line":668},[105,10517,307],{"emptyLinePlaceholder":306},[105,10519,10520,10522,10524,10526,10529,10531,10534],{"class":107,"line":673},[105,10521,3308],{"class":111},[105,10523,4361],{"class":115},[105,10525,8042],{"class":111},[105,10527,10528],{"class":115}," originalFn ",[105,10530,3314],{"class":111},[105,10532,10533],{"class":248}," 'function'",[105,10535,1000],{"class":115},[105,10537,10538,10541,10543,10546,10548,10551],{"class":107,"line":679},[105,10539,10540],{"class":111},"    throw",[105,10542,3478],{"class":111},[105,10544,10545],{"class":241}," TypeError",[105,10547,321],{"class":115},[105,10549,10550],{"class":248},"'Function.prototype.customBind is not a function'",[105,10552,1511],{"class":115},[105,10554,10555],{"class":107,"line":689},[105,10556,509],{"class":115},[105,10558,10559],{"class":107,"line":699},[105,10560,307],{"emptyLinePlaceholder":306},[105,10562,10563,10565,10568,10570,10572,10574,10576,10578],{"class":107,"line":704},[105,10564,5187],{"class":111},[105,10566,10567],{"class":241}," boundFn",[105,10569,321],{"class":115},[105,10571,10269],{"class":111},[105,10573,10438],{"class":122},[105,10575,4999],{"class":111},[105,10577,10417],{"class":129},[105,10579,10501],{"class":115},[105,10581,10582,10584,10587,10589,10592,10595],{"class":107,"line":710},[105,10583,7220],{"class":111},[105,10585,10586],{"class":129}," totalArgs",[105,10588,574],{"class":111},[105,10590,10591],{"class":115}," args.",[105,10593,10594],{"class":241},"concat",[105,10596,10597],{"class":115},"(innerArgs)\n",[105,10599,10600],{"class":107,"line":1136},[105,10601,307],{"emptyLinePlaceholder":306},[105,10603,10604],{"class":107,"line":1149},[105,10605,10606],{"class":630},"    \u002F\u002F new 调用：忽略 thisArg，执行 new originalFn\n",[105,10608,10609,10611,10613,10615,10617,10620],{"class":107,"line":3837},[105,10610,7245],{"class":111},[105,10612,4361],{"class":115},[105,10614,7412],{"class":111},[105,10616,5363],{"class":115},[105,10618,10619],{"class":129},"target",[105,10621,1000],{"class":115},[105,10623,10624,10627,10629,10632,10635,10637,10640,10642],{"class":107,"line":3847},[105,10625,10626],{"class":111},"      return",[105,10628,3478],{"class":111},[105,10630,10631],{"class":115}," (originalFn ",[105,10633,10634],{"class":111},"as",[105,10636,10417],{"class":129},[105,10638,10639],{"class":115},")(",[105,10641,10269],{"class":111},[105,10643,10644],{"class":115},"totalArgs)\n",[105,10646,10647],{"class":107,"line":3857},[105,10648,5143],{"class":115},[105,10650,10651],{"class":107,"line":3866},[105,10652,307],{"emptyLinePlaceholder":306},[105,10654,10655],{"class":107,"line":3871},[105,10656,10657],{"class":630},"    \u002F\u002F 普通调用：绑定 thisArg\n",[105,10659,10660,10663,10666,10669],{"class":107,"line":3882},[105,10661,10662],{"class":111},"    return",[105,10664,10665],{"class":115}," originalFn.",[105,10667,10668],{"class":241},"apply",[105,10670,10671],{"class":115},"(thisArg, totalArgs)\n",[105,10673,10674],{"class":107,"line":3899},[105,10675,509],{"class":115},[105,10677,10678],{"class":107,"line":3904},[105,10679,307],{"emptyLinePlaceholder":306},[105,10681,10682],{"class":107,"line":3920},[105,10683,10684],{"class":630},"  \u002F\u002F 不设置 boundFn.prototype，保持和原生 bind 一致\n",[105,10686,10687,10689],{"class":107,"line":3936},[105,10688,4126],{"class":111},[105,10690,10691],{"class":115}," boundFn\n",[105,10693,10694],{"class":107,"line":3947},[105,10695,212],{"class":115},[105,10697,10698],{"class":107,"line":3961},[105,10699,307],{"emptyLinePlaceholder":306},[105,10701,10702],{"class":107,"line":3967},[105,10703,10704],{"class":630},"\u002F\u002F 测试\n",[105,10706,10707,10709,10711,10713],{"class":107,"line":3972},[105,10708,568],{"class":111},[105,10710,10188],{"class":129},[105,10712,574],{"class":111},[105,10714,116],{"class":115},[105,10716,10717,10720,10723],{"class":107,"line":3992},[105,10718,10719],{"class":115},"    name: ",[105,10721,10722],{"class":248},"\"John\"",[105,10724,2849],{"class":115},[105,10726,10727,10730,10732,10734],{"class":107,"line":4010},[105,10728,10729],{"class":241},"    sayHello",[105,10731,126],{"class":115},[105,10733,8113],{"class":111},[105,10735,9229],{"class":115},[105,10737,10738,10741,10743,10745,10748,10750,10752,10754,10756],{"class":107,"line":4028},[105,10739,10740],{"class":115},"        console.",[105,10742,639],{"class":241},[105,10744,321],{"class":115},[105,10746,10747],{"class":248},"`Hello, ${",[105,10749,2870],{"class":129},[105,10751,5363],{"class":248},[105,10753,4996],{"class":115},[105,10755,6852],{"class":248},[105,10757,327],{"class":115},[105,10759,10760],{"class":107,"line":4033},[105,10761,5143],{"class":115},[105,10763,10764],{"class":107,"line":4039},[105,10765,212],{"class":115},[105,10767,10768,10770,10773,10775,10778,10780],{"class":107,"line":4069},[105,10769,568],{"class":111},[105,10771,10772],{"class":129}," sayHello",[105,10774,574],{"class":111},[105,10776,10777],{"class":115}," obj.sayHello.",[105,10779,10476],{"class":241},[105,10781,10782],{"class":115},"(obj);\n",[105,10784,10785,10788,10791],{"class":107,"line":4074},[105,10786,10787],{"class":241},"sayHello",[105,10789,10790],{"class":115},"(); ",[105,10792,10793],{"class":630},"\u002F\u002F Hello, John\n",[105,10795,10796,10798,10801,10803,10805,10808],{"class":107,"line":4080},[105,10797,568],{"class":111},[105,10799,10800],{"class":129}," sayHello2",[105,10802,574],{"class":111},[105,10804,10777],{"class":115},[105,10806,10807],{"class":241},"bind",[105,10809,10782],{"class":115},[105,10811,10812,10815,10817],{"class":107,"line":4098},[105,10813,10814],{"class":241},"sayHello2",[105,10816,10790],{"class":115},[105,10818,10793],{"class":630},[18,10820,10821],{},"这里实现非常简单，唯一有些新的是new.target属性，这个是ES6引入的原属性，只能在函数内部使用，值取决于函数怎么被调用。",[18,10823,10824],{},"如果一个函数是new Foo()这样被调用的话，那么new.target代表Foo函数本身，如果是Foo()调用的话，那么new.target代表undefined，所以说这个是用来区分构造调用和普通调用。",[18,10826,10827],{},"如果调用方式是：",[96,10829,10831],{"className":559,"code":10830,"language":561,"meta":101,"style":101},"const BoundPerson = Person.customBind({name: 'ignored'})\nconst p = new BoundPerson('real')\n\n而不能写成：\nif (this instanceof boundFn) {\n  return new originalFn(...totalArgs)\n}\n",[24,10832,10833,10855,10874,10878,10883,10898,10912],{"__ignoreMap":101},[105,10834,10835,10837,10840,10842,10845,10847,10850,10853],{"class":107,"line":108},[105,10836,568],{"class":111},[105,10838,10839],{"class":129}," BoundPerson",[105,10841,574],{"class":111},[105,10843,10844],{"class":115}," Person.",[105,10846,10476],{"class":241},[105,10848,10849],{"class":115},"({name: ",[105,10851,10852],{"class":248},"'ignored'",[105,10854,2888],{"class":115},[105,10856,10857,10859,10861,10863,10865,10867,10869,10872],{"class":107,"line":119},[105,10858,568],{"class":111},[105,10860,1088],{"class":129},[105,10862,574],{"class":111},[105,10864,3478],{"class":111},[105,10866,10839],{"class":241},[105,10868,321],{"class":115},[105,10870,10871],{"class":248},"'real'",[105,10873,1511],{"class":115},[105,10875,10876],{"class":107,"line":133},[105,10877,307],{"emptyLinePlaceholder":306},[105,10879,10880],{"class":107,"line":148},[105,10881,10882],{"class":115},"而不能写成：\n",[105,10884,10885,10887,10889,10891,10894,10896],{"class":107,"line":163},[105,10886,5478],{"class":111},[105,10888,4361],{"class":115},[105,10890,2870],{"class":129},[105,10892,10893],{"class":111}," instanceof",[105,10895,10567],{"class":241},[105,10897,1000],{"class":115},[105,10899,10900,10902,10904,10906,10908,10910],{"class":107,"line":179},[105,10901,4126],{"class":111},[105,10903,3478],{"class":111},[105,10905,10508],{"class":241},[105,10907,321],{"class":115},[105,10909,10269],{"class":111},[105,10911,10644],{"class":115},[105,10913,10914],{"class":107,"line":194},[105,10915,212],{"class":115},[18,10917,10918],{},"这里的调用方式会让new.target为BoundPerson，这样thisArgs就会被忽略了。如果写成了下面那种样子，this在new调用的时候是新创建的对象，它的原型是boundFn.prototype，如果boundFn.prototype是undefined，instanceof就会报错。",[14,10920,8245],{"id":8245},[18,10922,10923],{},"call的作用是立即调用一个函数，并手动指定函数内部的this指向，参数是逐个传入的。",[96,10925,10927],{"className":559,"code":10926,"language":561,"meta":101,"style":101},"function greet(greeting, punctuation) {\n  console.log(`${greeting}, ${this.name}${punctuation}`)\n}\n\nconst user = { name: '张三' }\n\ngreet.call(user, 'Hello', '!')  \u002F\u002F 'Hello, 张三!'\n",[24,10928,10929,10948,10979,10983,10987,11002,11006],{"__ignoreMap":101},[105,10930,10931,10933,10936,10938,10941,10943,10946],{"class":107,"line":108},[105,10932,8113],{"class":111},[105,10934,10935],{"class":241}," greet",[105,10937,321],{"class":115},[105,10939,10940],{"class":122},"greeting",[105,10942,286],{"class":115},[105,10944,10945],{"class":122},"punctuation",[105,10947,1000],{"class":115},[105,10949,10950,10952,10954,10956,10959,10961,10964,10966,10968,10970,10973,10975,10977],{"class":107,"line":119},[105,10951,3330],{"class":115},[105,10953,639],{"class":241},[105,10955,321],{"class":115},[105,10957,10958],{"class":248},"`${",[105,10960,10940],{"class":115},[105,10962,10963],{"class":248},"}, ${",[105,10965,2870],{"class":129},[105,10967,5363],{"class":248},[105,10969,4996],{"class":115},[105,10971,10972],{"class":248},"}${",[105,10974,10945],{"class":115},[105,10976,6852],{"class":248},[105,10978,1511],{"class":115},[105,10980,10981],{"class":107,"line":133},[105,10982,212],{"class":115},[105,10984,10985],{"class":107,"line":148},[105,10986,307],{"emptyLinePlaceholder":306},[105,10988,10989,10991,10993,10995,10998,11000],{"class":107,"line":163},[105,10990,568],{"class":111},[105,10992,5405],{"class":129},[105,10994,574],{"class":111},[105,10996,10997],{"class":115}," { name: ",[105,10999,10199],{"class":248},[105,11001,617],{"class":115},[105,11003,11004],{"class":107,"line":179},[105,11005,307],{"emptyLinePlaceholder":306},[105,11007,11008,11011,11013,11016,11019,11021,11024,11026],{"class":107,"line":194},[105,11009,11010],{"class":115},"greet.",[105,11012,8245],{"class":241},[105,11014,11015],{"class":115},"(user, ",[105,11017,11018],{"class":248},"'Hello'",[105,11020,286],{"class":115},[105,11022,11023],{"class":248},"'!'",[105,11025,5422],{"class":115},[105,11027,11028],{"class":630},"\u002F\u002F 'Hello, 张三!'\n",[18,11030,11031],{},"我们自己的实现大致如下：",[96,11033,11035],{"className":3241,"code":11034,"language":3243,"meta":101,"style":101},"export {}\n\ndeclare global {\n    interface Function {\n        customCall: (thisArgs : any, ...args: any[]) => any\n    }\n}\n\nFunction.prototype.customCall = function(thisArgs, ...args) {\n    thisArgs = thisArgs ?? globalThis\n    if (typeof thisArgs !== 'object' && typeof thisArgs !== 'function') {\n        thisArgs = Object(thisArgs)\n    }\n    const tempKey = Symbol('temp')\n    thisArgs[tempKey] = this\n    try{\n        return thisArgs[tempKey](...args)\n    } finally {\n        delete thisArgs[tempKey]\n    }\n}\n",[24,11036,11037,11043,11047,11053,11062,11095,11099,11103,11107,11136,11152,11181,11193,11197,11216,11225,11232,11245,11255,11263,11267],{"__ignoreMap":101},[105,11038,11039,11041],{"class":107,"line":108},[105,11040,4979],{"class":111},[105,11042,10382],{"class":115},[105,11044,11045],{"class":107,"line":119},[105,11046,307],{"emptyLinePlaceholder":306},[105,11048,11049,11051],{"class":107,"line":133},[105,11050,10334],{"class":111},[105,11052,10393],{"class":115},[105,11054,11055,11058,11060],{"class":107,"line":148},[105,11056,11057],{"class":111},"    interface",[105,11059,10284],{"class":241},[105,11061,116],{"class":115},[105,11063,11064,11067,11069,11071,11074,11077,11079,11081,11083,11085,11087,11089,11091,11093],{"class":107,"line":163},[105,11065,11066],{"class":241},"        customCall",[105,11068,4999],{"class":111},[105,11070,4361],{"class":115},[105,11072,11073],{"class":122},"thisArgs",[105,11075,11076],{"class":111}," :",[105,11078,10417],{"class":129},[105,11080,286],{"class":115},[105,11082,10269],{"class":111},[105,11084,10272],{"class":122},[105,11086,4999],{"class":111},[105,11088,10417],{"class":129},[105,11090,10445],{"class":115},[105,11092,723],{"class":111},[105,11094,10450],{"class":129},[105,11096,11097],{"class":107,"line":179},[105,11098,5143],{"class":115},[105,11100,11101],{"class":107,"line":194},[105,11102,212],{"class":115},[105,11104,11105],{"class":107,"line":209},[105,11106,307],{"emptyLinePlaceholder":306},[105,11108,11109,11111,11113,11115,11117,11120,11122,11124,11126,11128,11130,11132,11134],{"class":107,"line":526},[105,11110,10467],{"class":129},[105,11112,5363],{"class":115},[105,11114,8239],{"class":129},[105,11116,5363],{"class":115},[105,11118,11119],{"class":241},"customCall",[105,11121,574],{"class":111},[105,11123,4982],{"class":111},[105,11125,321],{"class":115},[105,11127,11073],{"class":122},[105,11129,286],{"class":115},[105,11131,10269],{"class":111},[105,11133,10272],{"class":122},[105,11135,1000],{"class":115},[105,11137,11138,11141,11143,11146,11149],{"class":107,"line":531},[105,11139,11140],{"class":115},"    thisArgs ",[105,11142,245],{"class":111},[105,11144,11145],{"class":115}," thisArgs ",[105,11147,11148],{"class":111},"??",[105,11150,11151],{"class":115}," globalThis\n",[105,11153,11154,11156,11158,11160,11162,11164,11167,11170,11173,11175,11177,11179],{"class":107,"line":668},[105,11155,7245],{"class":111},[105,11157,4361],{"class":115},[105,11159,8042],{"class":111},[105,11161,11145],{"class":115},[105,11163,3314],{"class":111},[105,11165,11166],{"class":248}," 'object'",[105,11168,11169],{"class":111}," &&",[105,11171,11172],{"class":111}," typeof",[105,11174,11145],{"class":115},[105,11176,3314],{"class":111},[105,11178,10533],{"class":248},[105,11180,1000],{"class":115},[105,11182,11183,11186,11188,11190],{"class":107,"line":673},[105,11184,11185],{"class":115},"        thisArgs ",[105,11187,245],{"class":111},[105,11189,8447],{"class":241},[105,11191,11192],{"class":115},"(thisArgs)\n",[105,11194,11195],{"class":107,"line":679},[105,11196,5143],{"class":115},[105,11198,11199,11201,11204,11206,11209,11211,11214],{"class":107,"line":689},[105,11200,7220],{"class":111},[105,11202,11203],{"class":129}," tempKey",[105,11205,574],{"class":111},[105,11207,11208],{"class":241}," Symbol",[105,11210,321],{"class":115},[105,11212,11213],{"class":248},"'temp'",[105,11215,1511],{"class":115},[105,11217,11218,11221,11223],{"class":107,"line":699},[105,11219,11220],{"class":115},"    thisArgs[tempKey] ",[105,11222,245],{"class":111},[105,11224,10513],{"class":129},[105,11226,11227,11230],{"class":107,"line":704},[105,11228,11229],{"class":111},"    try",[105,11231,9848],{"class":115},[105,11233,11234,11237,11240,11242],{"class":107,"line":710},[105,11235,11236],{"class":111},"        return",[105,11238,11239],{"class":115}," thisArgs[tempKey](",[105,11241,10269],{"class":111},[105,11243,11244],{"class":115},"args)\n",[105,11246,11247,11250,11253],{"class":107,"line":1136},[105,11248,11249],{"class":115},"    } ",[105,11251,11252],{"class":111},"finally",[105,11254,116],{"class":115},[105,11256,11257,11260],{"class":107,"line":1149},[105,11258,11259],{"class":111},"        delete",[105,11261,11262],{"class":115}," thisArgs[tempKey]\n",[105,11264,11265],{"class":107,"line":3837},[105,11266,5143],{"class":115},[105,11268,11269],{"class":107,"line":3847},[105,11270,212],{"class":115},[18,11272,11273,11274,11277],{},"apply和call有一点细致差别，仅仅是传递的非...args而是没展开的数组，直接传入 ",[24,11275,11276],{},"[...args]","即可。",[14,11279,11280],{"id":11280},"new的实现",[18,11282,11283,11284,11286,11287,11290],{},"现 ",[24,11285,7412],{}," 是理解 JS 原型链和构造函数机制的经典练习，一个new分为经典的四步，首先是创建一个新对象obj，然后把obj的原型指向Foo.prototype (obj.",[21,11288,11289],{},"proto"," === Foo.prototype)，紧接以obj为this，执行Foo，传入参数，判断Foo的返回值，如果返回值是对象或者函数，那么就用这个返回值，如果不是，就返回原始值\u002F无返回值，用之前创建的obj。",[96,11292,11294],{"className":3241,"code":11293,"language":3243,"meta":101,"style":101},"export function mynew(Constructor: Function, ...args: any[]) {\n    const obj = Object.create(Constructor.prototype)\n    const result = Constructor.apply(obj, args) \u002F\u002F constructor.\n    if (result !== null && (typeof result === 'object' || typeof result === 'function')) return result\n    return obj\n}\n",[24,11295,11296,11326,11350,11369,11412,11419],{"__ignoreMap":101},[105,11297,11298,11300,11302,11305,11307,11310,11312,11314,11316,11318,11320,11322,11324],{"class":107,"line":108},[105,11299,4979],{"class":111},[105,11301,4982],{"class":111},[105,11303,11304],{"class":241}," mynew",[105,11306,321],{"class":115},[105,11308,11309],{"class":241},"Constructor",[105,11311,4999],{"class":111},[105,11313,10284],{"class":241},[105,11315,286],{"class":115},[105,11317,10269],{"class":111},[105,11319,10272],{"class":122},[105,11321,4999],{"class":111},[105,11323,10417],{"class":129},[105,11325,10501],{"class":115},[105,11327,11328,11330,11332,11334,11337,11340,11342,11344,11346,11348],{"class":107,"line":119},[105,11329,7220],{"class":111},[105,11331,10188],{"class":129},[105,11333,574],{"class":111},[105,11335,11336],{"class":115}," Object.",[105,11338,11339],{"class":241},"create",[105,11341,321],{"class":115},[105,11343,11309],{"class":129},[105,11345,5363],{"class":115},[105,11347,8239],{"class":129},[105,11349,1511],{"class":115},[105,11351,11352,11354,11356,11358,11361,11363,11366],{"class":107,"line":133},[105,11353,7220],{"class":111},[105,11355,7269],{"class":129},[105,11357,574],{"class":111},[105,11359,11360],{"class":115}," Constructor.",[105,11362,10668],{"class":241},[105,11364,11365],{"class":115},"(obj, args) ",[105,11367,11368],{"class":630},"\u002F\u002F constructor.\n",[105,11370,11371,11373,11376,11378,11380,11382,11384,11386,11389,11391,11393,11396,11398,11400,11402,11404,11407,11409],{"class":107,"line":148},[105,11372,7245],{"class":111},[105,11374,11375],{"class":115}," (result ",[105,11377,3314],{"class":111},[105,11379,5026],{"class":129},[105,11381,11169],{"class":111},[105,11383,4361],{"class":115},[105,11385,8042],{"class":111},[105,11387,11388],{"class":115}," result ",[105,11390,7251],{"class":111},[105,11392,11166],{"class":248},[105,11394,11395],{"class":111}," ||",[105,11397,11172],{"class":111},[105,11399,11388],{"class":115},[105,11401,7251],{"class":111},[105,11403,10533],{"class":248},[105,11405,11406],{"class":115},")) ",[105,11408,3322],{"class":111},[105,11410,11411],{"class":115}," result\n",[105,11413,11414,11416],{"class":107,"line":163},[105,11415,10662],{"class":111},[105,11417,11418],{"class":115}," obj\n",[105,11420,11421],{"class":107,"line":179},[105,11422,212],{"class":115},[18,11424,11425,11426,11428,11429,11431,11432,11435,11436,11438],{},"前面我们已知，Function是一个特殊的东西，它自己是自己的构造函数，Function.",[21,11427,11289],{}," === Function.prototype，而Function.prototype.",[21,11430,11289],{}," === Object.prototype。一般对象的 ",[24,11433,11434],{},"__proto__"," 指向\"创建它的构造函数的 ",[24,11437,8239],{},"\"，我们都知道prototype一般是obj，但是Function.prototype 类型是function，这是唯一的例外。",[18,11440,11441,11444,11445,11448,11449],{},[24,11442,11443],{},"Function.prototype.__proto__"," 指向 ",[24,11446,11447],{},"Object.prototype","也只是刚好说明了函数也是对象，所以原型链指向最终的Object.prototype。",[21,11450,11451,11452,11454,11455,11458,11459,11461,11462,11465,11466,11468,11469,91],{},"默认情况下，用 ",[24,11453,8113],{}," 声明的函数，它的 ",[24,11456,11457],{},"constructor"," 都是 ",[24,11460,10467],{},"，它的 ",[24,11463,11464],{},"[[Prototype]]","（即 ",[24,11467,11434],{},"）都指向 ",[24,11470,11471],{},"Function.prototype",[14,11473,11474],{"id":11474},"promise",[18,11476,11477,11478,11483],{},"ECMA-262标准在Promise instance一节中，明确规定了Promise实例必须包含的核心内部槽，其中包括PromiseState： 一个字符串值，取值明确限定为pending，fulfilled、rejected三种状态，这个正式状态机的状态集合。然后是PromiseResult，用于存储promise完成的值，仅当PromiseState不为pending的时候才有意义。然后是一个PromiseFulfillReactions列表，存储当promise从pending转移到fulfilled的时候需要处理的记录，也就是注册的then回调。",[21,11479,11480],{},[24,11481,11482],{},"PromiseRejectReactions"," 是一个列表，用于存储当Promise从pending转移到reject的时候需要处理的记录。",[18,11485,11486],{},"标准的抽象操作定义了状态如果迁移，fulfillPromise可以讲一个状态为pending的promise转变为fulfilled，并设置PromiseResult，同时触发PromiseFulfillReactions中的任务。RejectPromise讲一个状态为pending的Promise转变为rejected。",[18,11488,11489,11490,11493,11494,1980,11497,11493,11499,11502],{},"这些定义构成了一个严格的单向状态机：",[24,11491,11492],{},"pending"," → ",[24,11495,11496],{},"fulfilled",[24,11498,11492],{},[24,11500,11501],{},"rejected","，且状态一旦改变便不可逆。",[18,11504,11505],{},"机制是十分简单的，只是说签名稍微复杂一点，特别是then方法等的签名，这里我先写出来：",[96,11507,11509],{"className":3241,"code":11508,"language":3243,"meta":101,"style":101},"    then\u003CTResult1 = T, TResult2 = never>(\n        onFulfilled?: ((value: T) => TResult1 | CustomPromise\u003CTResult1>) | null,\n        onRejected?: ((reason: any) => TResult2 | CustomPromise\u003CTResult2>) | null\n    ): CustomPromise\u003CTResult1 | TResult2>;\n",[24,11510,11511,11537,11582,11623],{"__ignoreMap":101},[105,11512,11513,11516,11518,11521,11523,11525,11528,11530,11533,11535],{"class":107,"line":108},[105,11514,11515],{"class":115},"    then",[105,11517,234],{"class":111},[105,11519,11520],{"class":115},"TResult1 ",[105,11522,245],{"class":111},[105,11524,5057],{"class":129},[105,11526,11527],{"class":115},", TResult2 ",[105,11529,245],{"class":111},[105,11531,11532],{"class":115}," never",[105,11534,1627],{"class":111},[105,11536,583],{"class":115},[105,11538,11539,11542,11545,11548,11550,11552,11554,11556,11558,11561,11564,11567,11569,11572,11574,11576,11578,11580],{"class":107,"line":119},[105,11540,11541],{"class":115},"        onFulfilled",[105,11543,11544],{"class":111},"?:",[105,11546,11547],{"class":115}," ((",[105,11549,8433],{"class":122},[105,11551,4999],{"class":111},[105,11553,5057],{"class":241},[105,11555,2836],{"class":115},[105,11557,723],{"class":111},[105,11559,11560],{"class":115}," TResult1 ",[105,11562,11563],{"class":111},"|",[105,11565,11566],{"class":115}," CustomPromise",[105,11568,234],{"class":111},[105,11570,11571],{"class":115},"TResult1",[105,11573,1627],{"class":111},[105,11575,2836],{"class":115},[105,11577,11563],{"class":111},[105,11579,5026],{"class":129},[105,11581,2849],{"class":115},[105,11583,11584,11587,11589,11591,11594,11596,11598,11600,11602,11605,11607,11609,11611,11614,11616,11618,11620],{"class":107,"line":133},[105,11585,11586],{"class":115},"        onRejected",[105,11588,11544],{"class":111},[105,11590,11547],{"class":115},[105,11592,11593],{"class":122},"reason",[105,11595,4999],{"class":111},[105,11597,10417],{"class":129},[105,11599,2836],{"class":115},[105,11601,723],{"class":111},[105,11603,11604],{"class":115}," TResult2 ",[105,11606,11563],{"class":111},[105,11608,11566],{"class":115},[105,11610,234],{"class":111},[105,11612,11613],{"class":115},"TResult2",[105,11615,1627],{"class":111},[105,11617,2836],{"class":115},[105,11619,11563],{"class":111},[105,11621,11622],{"class":129}," null\n",[105,11624,11625,11628,11630,11632,11634,11637,11639],{"class":107,"line":148},[105,11626,11627],{"class":115},"    ): CustomPromise",[105,11629,234],{"class":111},[105,11631,11520],{"class":115},[105,11633,11563],{"class":111},[105,11635,11636],{"class":115}," TResult2",[105,11638,1627],{"class":111},[105,11640,145],{"class":115},[18,11642,11643,11644,9057],{},"可以看到，then方法是接受一个回调函数，可以在回调函数里注册onFulfilled和onRejected，然后返回的是我们自己的Promise类型，拆开 ",[24,11645,11646],{},"onFulfilled",[96,11648,11650],{"className":3241,"code":11649,"language":3243,"meta":101,"style":101},"((value: T) => TResult1 | CustomPromise\u003CTResult1>) | null | undefined\n",[24,11651,11652],{"__ignoreMap":101},[105,11653,11654,11656,11658,11660,11662,11664,11666,11668,11670,11672,11674,11676,11678,11680,11682,11684,11686],{"class":107,"line":108},[105,11655,7280],{"class":115},[105,11657,8433],{"class":122},[105,11659,4999],{"class":111},[105,11661,5057],{"class":241},[105,11663,2836],{"class":115},[105,11665,723],{"class":111},[105,11667,11560],{"class":115},[105,11669,11563],{"class":111},[105,11671,11566],{"class":115},[105,11673,234],{"class":111},[105,11675,11571],{"class":115},[105,11677,1627],{"class":111},[105,11679,2836],{"class":115},[105,11681,11563],{"class":111},[105,11683,5026],{"class":129},[105,11685,5023],{"class":111},[105,11687,11688],{"class":129}," undefined\n",[18,11690,11691,11692,9365,11695,11698,11699,11701],{},"可能是一个函数，可能是null和undefined，类型上onFulfilled只可能是这些，但是T和Promise不在这个类型里面，它们出现在这个函数的",[21,11693,11694],{},"参数",[21,11696,11697],{},"返回值","里，不是 ",[24,11700,11646],{}," 本身。",[18,11703,11704],{},"这里需要复习常见类型在布尔运算里的真值表：",[835,11706,11707,11724],{},[838,11708,11709],{},[841,11710,11711,11713,11719],{},[844,11712,1163],{},[844,11714,11715,11718],{},[24,11716,11717],{},"if (x)"," 为真分支",[844,11720,11721,11723],{},[24,11722,11717],{}," 为假分支",[854,11725,11726,11742,11759,11773,11785],{},[841,11727,11728,11733,11738],{},[859,11729,11730],{},[24,11731,11732],{},"unknown",[859,11734,11735,11736],{},"不变，仍是 ",[24,11737,11732],{},[859,11739,11735,11740],{},[24,11741,11732],{},[841,11743,11744,11749,11755],{},[859,11745,11746],{},[24,11747,11748],{},"never",[859,11750,11751,11754],{},[21,11752,11753],{},"不进入","（分支不可达）",[859,11756,11757,11754],{},[21,11758,11753],{},[841,11760,11761,11765,11768],{},[859,11762,11763],{},[24,11764,3987],{},[859,11766,11767],{},"排除了",[859,11769,11770,11771],{},"只剩 ",[24,11772,3987],{},[841,11774,11775,11779,11781],{},[859,11776,11777],{},[24,11778,8379],{},[859,11780,11767],{},[859,11782,11770,11783],{},[24,11784,8379],{},[841,11786,11787,11792,11794],{},[859,11788,11789],{},[24,11790,11791],{},"null | undefined",[859,11793,11767],{},[859,11795,11796,11797],{},"剩 ",[24,11798,11791],{},[96,11800,11802],{"className":3241,"code":11801,"language":3243,"meta":101,"style":101},"type PromiseState = 'pending' | 'fulfilled' | 'rejected';\n\ninterface CustomPromise\u003CT> {\n    state: PromiseState;\n    result: T | undefined;\n    PromiseFulfillReactions: ((value: any) => void)[];\n    PromiseRejectReactions: ((reason: any) => void)[];\n\n    resolve(value: T): void;\n    reject(reason?: any): void;\n\n    then\u003CTResult1 = T, TResult2 = never>(\n        onFulfilled?: ((value: T) => TResult1 | CustomPromise\u003CTResult1>) | null,\n        onRejected?: ((reason: any) => TResult2 | CustomPromise\u003CTResult2>) | null\n    ): CustomPromise\u003CTResult1 | TResult2>;\n\n    catch\u003CTResult = never>(\n        onRejected?: ((reason: any) => TResult | CustomPromise\u003CTResult>) | null\n    ): CustomPromise\u003CT | TResult>;\n}\n\n\n\nclass MyPromise\u003CT> implements CustomPromise\u003CT> {\n    state: PromiseState = 'pending';\n    result: T | undefined = undefined;\n    PromiseFulfillReactions: ((value: any) => void)[] = [];\n    PromiseRejectReactions: ((reason: any) => void)[] = [];\n\n    constructor(\n        executor?: (\n            resolve: (value: T) => void,\n            reject: (reason?: any) => void\n        ) => void\n    ) {\n        if (typeof executor === 'function') {\n            try {\n                executor(\n                    (value) => this.resolve(value),\n                    (reason) => this.reject(reason)\n                );\n            } catch (e) {\n                this.reject(e);\n            }\n        }\n    }\n\n    resolve(value: T): void {\n        if (this.state !== 'pending') return;\n        if (value instanceof MyPromise) {\n            value.then(\n                (v) => this.resolve(v as T),\n                (r) => this.reject(r)\n            );\n            return;\n        }\n\n        this.state = 'fulfilled';\n        this.result = value;\n\n        const reactions = this.PromiseFulfillReactions;\n        this.PromiseFulfillReactions = [];\n        this.PromiseRejectReactions = [];\n        for (const reaction of reactions) {\n            queueMicrotask(() => reaction(value));\n        }\n    }\n\n    reject(reason?: any): void {\n        if (this.state !== 'pending') return;\n\n        this.state = 'rejected';\n        this.result = reason;\n\n        const reactions = this.PromiseRejectReactions;\n        this.PromiseFulfillReactions = [];\n        this.PromiseRejectReactions = [];\n        for (const reaction of reactions) {\n            queueMicrotask(() => reaction(reason));\n        }\n    }\n\n    then\u003CTResult1 = T, TResult2 = never>(\n        onFulfilled?: ((value: T) => TResult1 | CustomPromise\u003CTResult1>) | null,\n        onRejected?: ((reason: any) => TResult2 | CustomPromise\u003CTResult2>) | null\n    ): CustomPromise\u003CTResult1 | TResult2> {\n        return new MyPromise\u003CTResult1 | TResult2>((resolve, reject) => {\n            const handlerFulfilled = (value: T) => {\n                if (typeof onFulfilled !== 'function') {\n                    resolve(value as unknown as TResult1);\n                    return;\n                }\n                try {\n                    const r = onFulfilled(value);\n                    if (r instanceof MyPromise) {\n                        r.then(\n                            (v) => resolve(v as TResult1),\n                            (e) => reject(e)\n                        );\n                    } else {\n                        resolve(r as TResult1);\n                    }\n                } catch (e) {\n                    reject(e);\n                }\n            };\n\n            const handlerRejected = (reason: any) => {\n                if (typeof onRejected !== 'function') {\n                    reject(reason);\n                    return;\n                }\n                try {\n                    const r = onRejected(reason);\n                    if (r instanceof MyPromise) {\n                        r.then(\n                            (v) => resolve(v as TResult1),\n                            (e) => reject(e)\n                        );\n                    } else {\n                        resolve(r as unknown as TResult1);\n                    }\n                } catch (e) {\n                    reject(e);\n                }\n            };\n\n            if (this.state === 'pending') {\n                this.PromiseFulfillReactions.push(handlerFulfilled);\n                this.PromiseRejectReactions.push(handlerRejected);\n            } else if (this.state === 'fulfilled') {\n                queueMicrotask(() => handlerFulfilled(this.result as T));\n            } else {\n                queueMicrotask(() => handlerRejected(this.result));\n            }\n        });\n    }\n\n    catch\u003CTResult = never>(\n        onRejected?: ((reason: any) => TResult | CustomPromise\u003CTResult>) | null\n    ): CustomPromise\u003CT | TResult> {\n        return this.then(null, onRejected);\n    }\n\n    static resolve\u003CT>(value: T): MyPromise\u003CT> {\n        return new MyPromise\u003CT>((resolve) => resolve(value));\n    }\n\n    static reject\u003CT = never>(reason?: any): MyPromise\u003CT> {\n        return new MyPromise\u003CT>((_, reject) => reject(reason));\n    }\n}\n\nMyPromise.resolve(10).then(\n    (v) => console.log('fulfilled', v),   \u002F\u002F fulfilled 10\n    (r) => console.log('rejected', r)\n);\n",[24,11803,11804,11828,11832,11845,11856,11872,11896,11919,11923,11945,11966,11970,11993,12031,12065,12085,12089,12105,12140,12158,12162,12166,12170,12174,12200,12214,12232,12260,12286,12290,12297,12307,12330,12352,12361,12366,12384,12391,12398,12419,12439,12444,12454,12466,12471,12476,12480,12484,12504,12525,12538,12547,12575,12595,12599,12605,12609,12613,12626,12638,12642,12656,12667,12678,12696,12710,12714,12718,12722,12742,12762,12766,12778,12789,12793,12806,12816,12826,12840,12853,12857,12861,12865,12887,12923,12957,12975,13006,13029,13047,13066,13073,13078,13085,13101,13115,13124,13146,13162,13167,13177,13191,13196,13205,13212,13216,13221,13225,13248,13265,13272,13278,13282,13288,13301,13313,13321,13341,13355,13359,13368,13385,13390,13399,13406,13411,13416,13421,13438,13452,13465,13487,13512,13521,13539,13544,13550,13555,13560,13575,13610,13629,13647,13652,13657,13689,13714,13719,13724,13759,13789,13794,13799,13804,13823,13849,13872],{"__ignoreMap":101},[105,11805,11806,11808,11811,11813,11816,11818,11821,11823,11826],{"class":107,"line":108},[105,11807,7225],{"class":111},[105,11809,11810],{"class":241}," PromiseState",[105,11812,574],{"class":111},[105,11814,11815],{"class":248}," 'pending'",[105,11817,5023],{"class":111},[105,11819,11820],{"class":248}," 'fulfilled'",[105,11822,5023],{"class":111},[105,11824,11825],{"class":248}," 'rejected'",[105,11827,145],{"class":115},[105,11829,11830],{"class":107,"line":119},[105,11831,307],{"emptyLinePlaceholder":306},[105,11833,11834,11836,11838,11840,11842],{"class":107,"line":133},[105,11835,3501],{"class":111},[105,11837,11566],{"class":241},[105,11839,234],{"class":115},[105,11841,4990],{"class":241},[105,11843,11844],{"class":115},"> {\n",[105,11846,11847,11850,11852,11854],{"class":107,"line":148},[105,11848,11849],{"class":122},"    state",[105,11851,4999],{"class":111},[105,11853,11810],{"class":241},[105,11855,145],{"class":115},[105,11857,11858,11861,11863,11865,11867,11870],{"class":107,"line":163},[105,11859,11860],{"class":122},"    result",[105,11862,4999],{"class":111},[105,11864,5057],{"class":241},[105,11866,5023],{"class":111},[105,11868,11869],{"class":129}," undefined",[105,11871,145],{"class":115},[105,11873,11874,11877,11879,11881,11883,11885,11887,11889,11891,11893],{"class":107,"line":179},[105,11875,11876],{"class":122},"    PromiseFulfillReactions",[105,11878,4999],{"class":111},[105,11880,11547],{"class":115},[105,11882,8433],{"class":122},[105,11884,4999],{"class":111},[105,11886,10417],{"class":129},[105,11888,2836],{"class":115},[105,11890,723],{"class":111},[105,11892,5064],{"class":129},[105,11894,11895],{"class":115},")[];\n",[105,11897,11898,11901,11903,11905,11907,11909,11911,11913,11915,11917],{"class":107,"line":194},[105,11899,11900],{"class":122},"    PromiseRejectReactions",[105,11902,4999],{"class":111},[105,11904,11547],{"class":115},[105,11906,11593],{"class":122},[105,11908,4999],{"class":111},[105,11910,10417],{"class":129},[105,11912,2836],{"class":115},[105,11914,723],{"class":111},[105,11916,5064],{"class":129},[105,11918,11895],{"class":115},[105,11920,11921],{"class":107,"line":209},[105,11922,307],{"emptyLinePlaceholder":306},[105,11924,11925,11928,11930,11932,11934,11936,11939,11941,11943],{"class":107,"line":526},[105,11926,11927],{"class":241},"    resolve",[105,11929,321],{"class":115},[105,11931,8433],{"class":122},[105,11933,4999],{"class":111},[105,11935,5057],{"class":241},[105,11937,11938],{"class":115},")",[105,11940,4999],{"class":111},[105,11942,5064],{"class":129},[105,11944,145],{"class":115},[105,11946,11947,11950,11952,11954,11956,11958,11960,11962,11964],{"class":107,"line":531},[105,11948,11949],{"class":241},"    reject",[105,11951,321],{"class":115},[105,11953,11593],{"class":122},[105,11955,11544],{"class":111},[105,11957,10417],{"class":129},[105,11959,11938],{"class":115},[105,11961,4999],{"class":111},[105,11963,5064],{"class":129},[105,11965,145],{"class":115},[105,11967,11968],{"class":107,"line":668},[105,11969,307],{"emptyLinePlaceholder":306},[105,11971,11972,11974,11976,11978,11980,11982,11984,11986,11988,11990],{"class":107,"line":673},[105,11973,11515],{"class":241},[105,11975,234],{"class":115},[105,11977,11571],{"class":241},[105,11979,574],{"class":111},[105,11981,5057],{"class":241},[105,11983,286],{"class":115},[105,11985,11613],{"class":241},[105,11987,574],{"class":111},[105,11989,11532],{"class":129},[105,11991,11992],{"class":115},">(\n",[105,11994,11995,11997,11999,12001,12003,12005,12007,12009,12011,12014,12016,12018,12020,12022,12025,12027,12029],{"class":107,"line":679},[105,11996,11541],{"class":122},[105,11998,11544],{"class":111},[105,12000,11547],{"class":115},[105,12002,8433],{"class":122},[105,12004,4999],{"class":111},[105,12006,5057],{"class":241},[105,12008,2836],{"class":115},[105,12010,723],{"class":111},[105,12012,12013],{"class":241}," TResult1",[105,12015,5023],{"class":111},[105,12017,11566],{"class":241},[105,12019,234],{"class":115},[105,12021,11571],{"class":241},[105,12023,12024],{"class":115},">) ",[105,12026,11563],{"class":111},[105,12028,5026],{"class":129},[105,12030,2849],{"class":115},[105,12032,12033,12035,12037,12039,12041,12043,12045,12047,12049,12051,12053,12055,12057,12059,12061,12063],{"class":107,"line":689},[105,12034,11586],{"class":122},[105,12036,11544],{"class":111},[105,12038,11547],{"class":115},[105,12040,11593],{"class":122},[105,12042,4999],{"class":111},[105,12044,10417],{"class":129},[105,12046,2836],{"class":115},[105,12048,723],{"class":111},[105,12050,11636],{"class":241},[105,12052,5023],{"class":111},[105,12054,11566],{"class":241},[105,12056,234],{"class":115},[105,12058,11613],{"class":241},[105,12060,12024],{"class":115},[105,12062,11563],{"class":111},[105,12064,11622],{"class":129},[105,12066,12067,12070,12072,12074,12076,12078,12080,12082],{"class":107,"line":699},[105,12068,12069],{"class":115},"    )",[105,12071,4999],{"class":111},[105,12073,11566],{"class":241},[105,12075,234],{"class":115},[105,12077,11571],{"class":241},[105,12079,5023],{"class":111},[105,12081,11636],{"class":241},[105,12083,12084],{"class":115},">;\n",[105,12086,12087],{"class":107,"line":704},[105,12088,307],{"emptyLinePlaceholder":306},[105,12090,12091,12094,12096,12099,12101,12103],{"class":107,"line":710},[105,12092,12093],{"class":241},"    catch",[105,12095,234],{"class":115},[105,12097,12098],{"class":241},"TResult",[105,12100,574],{"class":111},[105,12102,11532],{"class":129},[105,12104,11992],{"class":115},[105,12106,12107,12109,12111,12113,12115,12117,12119,12121,12123,12126,12128,12130,12132,12134,12136,12138],{"class":107,"line":1136},[105,12108,11586],{"class":122},[105,12110,11544],{"class":111},[105,12112,11547],{"class":115},[105,12114,11593],{"class":122},[105,12116,4999],{"class":111},[105,12118,10417],{"class":129},[105,12120,2836],{"class":115},[105,12122,723],{"class":111},[105,12124,12125],{"class":241}," TResult",[105,12127,5023],{"class":111},[105,12129,11566],{"class":241},[105,12131,234],{"class":115},[105,12133,12098],{"class":241},[105,12135,12024],{"class":115},[105,12137,11563],{"class":111},[105,12139,11622],{"class":129},[105,12141,12142,12144,12146,12148,12150,12152,12154,12156],{"class":107,"line":1149},[105,12143,12069],{"class":115},[105,12145,4999],{"class":111},[105,12147,11566],{"class":241},[105,12149,234],{"class":115},[105,12151,4990],{"class":241},[105,12153,5023],{"class":111},[105,12155,12125],{"class":241},[105,12157,12084],{"class":115},[105,12159,12160],{"class":107,"line":3837},[105,12161,212],{"class":115},[105,12163,12164],{"class":107,"line":3847},[105,12165,307],{"emptyLinePlaceholder":306},[105,12167,12168],{"class":107,"line":3857},[105,12169,307],{"emptyLinePlaceholder":306},[105,12171,12172],{"class":107,"line":3866},[105,12173,307],{"emptyLinePlaceholder":306},[105,12175,12176,12179,12182,12184,12186,12189,12192,12194,12196,12198],{"class":107,"line":3871},[105,12177,12178],{"class":111},"class",[105,12180,12181],{"class":241}," MyPromise",[105,12183,234],{"class":115},[105,12185,4990],{"class":241},[105,12187,12188],{"class":115},"> ",[105,12190,12191],{"class":111},"implements",[105,12193,11566],{"class":241},[105,12195,234],{"class":115},[105,12197,4990],{"class":241},[105,12199,11844],{"class":115},[105,12201,12202,12204,12206,12208,12210,12212],{"class":107,"line":3882},[105,12203,11849],{"class":122},[105,12205,4999],{"class":111},[105,12207,11810],{"class":241},[105,12209,574],{"class":111},[105,12211,11815],{"class":248},[105,12213,145],{"class":115},[105,12215,12216,12218,12220,12222,12224,12226,12228,12230],{"class":107,"line":3899},[105,12217,11860],{"class":122},[105,12219,4999],{"class":111},[105,12221,5057],{"class":241},[105,12223,5023],{"class":111},[105,12225,11869],{"class":129},[105,12227,574],{"class":111},[105,12229,11869],{"class":129},[105,12231,145],{"class":115},[105,12233,12234,12236,12238,12240,12242,12244,12246,12248,12250,12252,12255,12257],{"class":107,"line":3904},[105,12235,11876],{"class":122},[105,12237,4999],{"class":111},[105,12239,11547],{"class":115},[105,12241,8433],{"class":122},[105,12243,4999],{"class":111},[105,12245,10417],{"class":129},[105,12247,2836],{"class":115},[105,12249,723],{"class":111},[105,12251,5064],{"class":129},[105,12253,12254],{"class":115},")[] ",[105,12256,245],{"class":111},[105,12258,12259],{"class":115}," [];\n",[105,12261,12262,12264,12266,12268,12270,12272,12274,12276,12278,12280,12282,12284],{"class":107,"line":3920},[105,12263,11900],{"class":122},[105,12265,4999],{"class":111},[105,12267,11547],{"class":115},[105,12269,11593],{"class":122},[105,12271,4999],{"class":111},[105,12273,10417],{"class":129},[105,12275,2836],{"class":115},[105,12277,723],{"class":111},[105,12279,5064],{"class":129},[105,12281,12254],{"class":115},[105,12283,245],{"class":111},[105,12285,12259],{"class":115},[105,12287,12288],{"class":107,"line":3936},[105,12289,307],{"emptyLinePlaceholder":306},[105,12291,12292,12295],{"class":107,"line":3947},[105,12293,12294],{"class":111},"    constructor",[105,12296,583],{"class":115},[105,12298,12299,12302,12304],{"class":107,"line":3961},[105,12300,12301],{"class":241},"        executor",[105,12303,11544],{"class":111},[105,12305,12306],{"class":115}," (\n",[105,12308,12309,12312,12314,12316,12318,12320,12322,12324,12326,12328],{"class":107,"line":3967},[105,12310,12311],{"class":241},"            resolve",[105,12313,4999],{"class":111},[105,12315,4361],{"class":115},[105,12317,8433],{"class":122},[105,12319,4999],{"class":111},[105,12321,5057],{"class":241},[105,12323,2836],{"class":115},[105,12325,723],{"class":111},[105,12327,5064],{"class":129},[105,12329,2849],{"class":115},[105,12331,12332,12335,12337,12339,12341,12343,12345,12347,12349],{"class":107,"line":3972},[105,12333,12334],{"class":241},"            reject",[105,12336,4999],{"class":111},[105,12338,4361],{"class":115},[105,12340,11593],{"class":122},[105,12342,11544],{"class":111},[105,12344,10417],{"class":129},[105,12346,2836],{"class":115},[105,12348,723],{"class":111},[105,12350,12351],{"class":129}," void\n",[105,12353,12354,12357,12359],{"class":107,"line":3992},[105,12355,12356],{"class":115},"        ) ",[105,12358,723],{"class":111},[105,12360,12351],{"class":129},[105,12362,12363],{"class":107,"line":4010},[105,12364,12365],{"class":115},"    ) {\n",[105,12367,12368,12371,12373,12375,12378,12380,12382],{"class":107,"line":4028},[105,12369,12370],{"class":111},"        if",[105,12372,4361],{"class":115},[105,12374,8042],{"class":111},[105,12376,12377],{"class":115}," executor ",[105,12379,7251],{"class":111},[105,12381,10533],{"class":248},[105,12383,1000],{"class":115},[105,12385,12386,12389],{"class":107,"line":4033},[105,12387,12388],{"class":111},"            try",[105,12390,116],{"class":115},[105,12392,12393,12396],{"class":107,"line":4039},[105,12394,12395],{"class":241},"                executor",[105,12397,583],{"class":115},[105,12399,12400,12403,12405,12407,12409,12412,12414,12416],{"class":107,"line":4069},[105,12401,12402],{"class":115},"                    (",[105,12404,8433],{"class":122},[105,12406,2836],{"class":115},[105,12408,723],{"class":111},[105,12410,12411],{"class":129}," this",[105,12413,5363],{"class":115},[105,12415,9492],{"class":241},[105,12417,12418],{"class":115},"(value),\n",[105,12420,12421,12423,12425,12427,12429,12431,12433,12436],{"class":107,"line":4074},[105,12422,12402],{"class":115},[105,12424,11593],{"class":122},[105,12426,2836],{"class":115},[105,12428,723],{"class":111},[105,12430,12411],{"class":129},[105,12432,5363],{"class":115},[105,12434,12435],{"class":241},"reject",[105,12437,12438],{"class":115},"(reason)\n",[105,12440,12441],{"class":107,"line":4080},[105,12442,12443],{"class":115},"                );\n",[105,12445,12446,12449,12451],{"class":107,"line":4098},[105,12447,12448],{"class":115},"            } ",[105,12450,5539],{"class":111},[105,12452,12453],{"class":115}," (e) {\n",[105,12455,12456,12459,12461,12463],{"class":107,"line":4123},[105,12457,12458],{"class":129},"                this",[105,12460,5363],{"class":115},[105,12462,12435],{"class":241},[105,12464,12465],{"class":115},"(e);\n",[105,12467,12468],{"class":107,"line":4147},[105,12469,12470],{"class":115},"            }\n",[105,12472,12473],{"class":107,"line":4152},[105,12474,12475],{"class":115},"        }\n",[105,12477,12478],{"class":107,"line":4157},[105,12479,5143],{"class":115},[105,12481,12482],{"class":107,"line":4163},[105,12483,307],{"emptyLinePlaceholder":306},[105,12485,12486,12488,12490,12492,12494,12496,12498,12500,12502],{"class":107,"line":4181},[105,12487,11927],{"class":241},[105,12489,321],{"class":115},[105,12491,8433],{"class":122},[105,12493,4999],{"class":111},[105,12495,5057],{"class":241},[105,12497,11938],{"class":115},[105,12499,4999],{"class":111},[105,12501,5064],{"class":129},[105,12503,116],{"class":115},[105,12505,12506,12508,12510,12512,12515,12517,12519,12521,12523],{"class":107,"line":4207},[105,12507,12370],{"class":111},[105,12509,4361],{"class":115},[105,12511,2870],{"class":129},[105,12513,12514],{"class":115},".state ",[105,12516,3314],{"class":111},[105,12518,11815],{"class":248},[105,12520,2836],{"class":115},[105,12522,3322],{"class":111},[105,12524,145],{"class":115},[105,12526,12527,12529,12532,12534,12536],{"class":107,"line":4234},[105,12528,12370],{"class":111},[105,12530,12531],{"class":115}," (value ",[105,12533,8153],{"class":111},[105,12535,12181],{"class":241},[105,12537,1000],{"class":115},[105,12539,12540,12543,12545],{"class":107,"line":4239},[105,12541,12542],{"class":115},"            value.",[105,12544,5512],{"class":241},[105,12546,583],{"class":115},[105,12548,12549,12552,12555,12557,12559,12561,12563,12565,12568,12570,12572],{"class":107,"line":4244},[105,12550,12551],{"class":115},"                (",[105,12553,12554],{"class":122},"v",[105,12556,2836],{"class":115},[105,12558,723],{"class":111},[105,12560,12411],{"class":129},[105,12562,5363],{"class":115},[105,12564,9492],{"class":241},[105,12566,12567],{"class":115},"(v ",[105,12569,10634],{"class":111},[105,12571,5057],{"class":241},[105,12573,12574],{"class":115},"),\n",[105,12576,12577,12579,12582,12584,12586,12588,12590,12592],{"class":107,"line":4250},[105,12578,12551],{"class":115},[105,12580,12581],{"class":122},"r",[105,12583,2836],{"class":115},[105,12585,723],{"class":111},[105,12587,12411],{"class":129},[105,12589,5363],{"class":115},[105,12591,12435],{"class":241},[105,12593,12594],{"class":115},"(r)\n",[105,12596,12597],{"class":107,"line":4267},[105,12598,5824],{"class":115},[105,12600,12601,12603],{"class":107,"line":4285},[105,12602,5640],{"class":111},[105,12604,145],{"class":115},[105,12606,12607],{"class":107,"line":4290},[105,12608,12475],{"class":115},[105,12610,12611],{"class":107,"line":4295},[105,12612,307],{"emptyLinePlaceholder":306},[105,12614,12615,12618,12620,12622,12624],{"class":107,"line":4301},[105,12616,12617],{"class":129},"        this",[105,12619,12514],{"class":115},[105,12621,245],{"class":111},[105,12623,11820],{"class":248},[105,12625,145],{"class":115},[105,12627,12628,12630,12633,12635],{"class":107,"line":4324},[105,12629,12617],{"class":129},[105,12631,12632],{"class":115},".result ",[105,12634,245],{"class":111},[105,12636,12637],{"class":115}," value;\n",[105,12639,12640],{"class":107,"line":4329},[105,12641,307],{"emptyLinePlaceholder":306},[105,12643,12644,12646,12649,12651,12653],{"class":107,"line":4335},[105,12645,7266],{"class":111},[105,12647,12648],{"class":129}," reactions",[105,12650,574],{"class":111},[105,12652,12411],{"class":129},[105,12654,12655],{"class":115},".PromiseFulfillReactions;\n",[105,12657,12658,12660,12663,12665],{"class":107,"line":4351},[105,12659,12617],{"class":129},[105,12661,12662],{"class":115},".PromiseFulfillReactions ",[105,12664,245],{"class":111},[105,12666,12259],{"class":115},[105,12668,12669,12671,12674,12676],{"class":107,"line":4372},[105,12670,12617],{"class":129},[105,12672,12673],{"class":115},".PromiseRejectReactions ",[105,12675,245],{"class":111},[105,12677,12259],{"class":115},[105,12679,12680,12683,12685,12687,12690,12693],{"class":107,"line":4384},[105,12681,12682],{"class":111},"        for",[105,12684,4361],{"class":115},[105,12686,568],{"class":111},[105,12688,12689],{"class":129}," reaction",[105,12691,12692],{"class":111}," of",[105,12694,12695],{"class":115}," reactions) {\n",[105,12697,12698,12701,12703,12705,12707],{"class":107,"line":4397},[105,12699,12700],{"class":241},"            queueMicrotask",[105,12702,4052],{"class":115},[105,12704,723],{"class":111},[105,12706,12689],{"class":241},[105,12708,12709],{"class":115},"(value));\n",[105,12711,12712],{"class":107,"line":4409},[105,12713,12475],{"class":115},[105,12715,12716],{"class":107,"line":4420},[105,12717,5143],{"class":115},[105,12719,12720],{"class":107,"line":4432},[105,12721,307],{"emptyLinePlaceholder":306},[105,12723,12724,12726,12728,12730,12732,12734,12736,12738,12740],{"class":107,"line":4438},[105,12725,11949],{"class":241},[105,12727,321],{"class":115},[105,12729,11593],{"class":122},[105,12731,11544],{"class":111},[105,12733,10417],{"class":129},[105,12735,11938],{"class":115},[105,12737,4999],{"class":111},[105,12739,5064],{"class":129},[105,12741,116],{"class":115},[105,12743,12744,12746,12748,12750,12752,12754,12756,12758,12760],{"class":107,"line":4444},[105,12745,12370],{"class":111},[105,12747,4361],{"class":115},[105,12749,2870],{"class":129},[105,12751,12514],{"class":115},[105,12753,3314],{"class":111},[105,12755,11815],{"class":248},[105,12757,2836],{"class":115},[105,12759,3322],{"class":111},[105,12761,145],{"class":115},[105,12763,12764],{"class":107,"line":4449},[105,12765,307],{"emptyLinePlaceholder":306},[105,12767,12768,12770,12772,12774,12776],{"class":107,"line":4455},[105,12769,12617],{"class":129},[105,12771,12514],{"class":115},[105,12773,245],{"class":111},[105,12775,11825],{"class":248},[105,12777,145],{"class":115},[105,12779,12780,12782,12784,12786],{"class":107,"line":4471},[105,12781,12617],{"class":129},[105,12783,12632],{"class":115},[105,12785,245],{"class":111},[105,12787,12788],{"class":115}," reason;\n",[105,12790,12791],{"class":107,"line":4479},[105,12792,307],{"emptyLinePlaceholder":306},[105,12794,12795,12797,12799,12801,12803],{"class":107,"line":4490},[105,12796,7266],{"class":111},[105,12798,12648],{"class":129},[105,12800,574],{"class":111},[105,12802,12411],{"class":129},[105,12804,12805],{"class":115},".PromiseRejectReactions;\n",[105,12807,12808,12810,12812,12814],{"class":107,"line":4495},[105,12809,12617],{"class":129},[105,12811,12662],{"class":115},[105,12813,245],{"class":111},[105,12815,12259],{"class":115},[105,12817,12818,12820,12822,12824],{"class":107,"line":4500},[105,12819,12617],{"class":129},[105,12821,12673],{"class":115},[105,12823,245],{"class":111},[105,12825,12259],{"class":115},[105,12827,12828,12830,12832,12834,12836,12838],{"class":107,"line":4505},[105,12829,12682],{"class":111},[105,12831,4361],{"class":115},[105,12833,568],{"class":111},[105,12835,12689],{"class":129},[105,12837,12692],{"class":111},[105,12839,12695],{"class":115},[105,12841,12842,12844,12846,12848,12850],{"class":107,"line":4517},[105,12843,12700],{"class":241},[105,12845,4052],{"class":115},[105,12847,723],{"class":111},[105,12849,12689],{"class":241},[105,12851,12852],{"class":115},"(reason));\n",[105,12854,12855],{"class":107,"line":4525},[105,12856,12475],{"class":115},[105,12858,12859],{"class":107,"line":4541},[105,12860,5143],{"class":115},[105,12862,12863],{"class":107,"line":4546},[105,12864,307],{"emptyLinePlaceholder":306},[105,12866,12867,12869,12871,12873,12875,12877,12879,12881,12883,12885],{"class":107,"line":4551},[105,12868,11515],{"class":241},[105,12870,234],{"class":115},[105,12872,11571],{"class":241},[105,12874,574],{"class":111},[105,12876,5057],{"class":241},[105,12878,286],{"class":115},[105,12880,11613],{"class":241},[105,12882,574],{"class":111},[105,12884,11532],{"class":129},[105,12886,11992],{"class":115},[105,12888,12889,12891,12893,12895,12897,12899,12901,12903,12905,12907,12909,12911,12913,12915,12917,12919,12921],{"class":107,"line":4563},[105,12890,11541],{"class":122},[105,12892,11544],{"class":111},[105,12894,11547],{"class":115},[105,12896,8433],{"class":122},[105,12898,4999],{"class":111},[105,12900,5057],{"class":241},[105,12902,2836],{"class":115},[105,12904,723],{"class":111},[105,12906,12013],{"class":241},[105,12908,5023],{"class":111},[105,12910,11566],{"class":241},[105,12912,234],{"class":115},[105,12914,11571],{"class":241},[105,12916,12024],{"class":115},[105,12918,11563],{"class":111},[105,12920,5026],{"class":129},[105,12922,2849],{"class":115},[105,12924,12925,12927,12929,12931,12933,12935,12937,12939,12941,12943,12945,12947,12949,12951,12953,12955],{"class":107,"line":4576},[105,12926,11586],{"class":122},[105,12928,11544],{"class":111},[105,12930,11547],{"class":115},[105,12932,11593],{"class":122},[105,12934,4999],{"class":111},[105,12936,10417],{"class":129},[105,12938,2836],{"class":115},[105,12940,723],{"class":111},[105,12942,11636],{"class":241},[105,12944,5023],{"class":111},[105,12946,11566],{"class":241},[105,12948,234],{"class":115},[105,12950,11613],{"class":241},[105,12952,12024],{"class":115},[105,12954,11563],{"class":111},[105,12956,11622],{"class":129},[105,12958,12959,12961,12963,12965,12967,12969,12971,12973],{"class":107,"line":4581},[105,12960,12069],{"class":115},[105,12962,4999],{"class":111},[105,12964,11566],{"class":241},[105,12966,234],{"class":115},[105,12968,11571],{"class":241},[105,12970,5023],{"class":111},[105,12972,11636],{"class":241},[105,12974,11844],{"class":115},[105,12976,12977,12979,12981,12983,12985,12987,12989,12991,12994,12996,12998,13000,13002,13004],{"class":107,"line":4590},[105,12978,11236],{"class":111},[105,12980,3478],{"class":111},[105,12982,12181],{"class":241},[105,12984,234],{"class":115},[105,12986,11571],{"class":241},[105,12988,5023],{"class":111},[105,12990,11636],{"class":241},[105,12992,12993],{"class":115},">((",[105,12995,9492],{"class":122},[105,12997,286],{"class":115},[105,12999,12435],{"class":122},[105,13001,2836],{"class":115},[105,13003,723],{"class":111},[105,13005,116],{"class":115},[105,13007,13008,13010,13013,13015,13017,13019,13021,13023,13025,13027],{"class":107,"line":4595},[105,13009,6289],{"class":111},[105,13011,13012],{"class":241}," handlerFulfilled",[105,13014,574],{"class":111},[105,13016,4361],{"class":115},[105,13018,8433],{"class":122},[105,13020,4999],{"class":111},[105,13022,5057],{"class":241},[105,13024,2836],{"class":115},[105,13026,723],{"class":111},[105,13028,116],{"class":115},[105,13030,13031,13034,13036,13038,13041,13043,13045],{"class":107,"line":4607},[105,13032,13033],{"class":111},"                if",[105,13035,4361],{"class":115},[105,13037,8042],{"class":111},[105,13039,13040],{"class":115}," onFulfilled ",[105,13042,3314],{"class":111},[105,13044,10533],{"class":248},[105,13046,1000],{"class":115},[105,13048,13049,13052,13055,13057,13059,13062,13064],{"class":107,"line":4615},[105,13050,13051],{"class":241},"                    resolve",[105,13053,13054],{"class":115},"(value ",[105,13056,10634],{"class":111},[105,13058,8438],{"class":129},[105,13060,13061],{"class":111}," as",[105,13063,12013],{"class":241},[105,13065,327],{"class":115},[105,13067,13068,13071],{"class":107,"line":4633},[105,13069,13070],{"class":111},"                    return",[105,13072,145],{"class":115},[105,13074,13075],{"class":107,"line":4645},[105,13076,13077],{"class":115},"                }\n",[105,13079,13080,13083],{"class":107,"line":4656},[105,13081,13082],{"class":111},"                try",[105,13084,116],{"class":115},[105,13086,13087,13090,13093,13095,13098],{"class":107,"line":4674},[105,13088,13089],{"class":111},"                    const",[105,13091,13092],{"class":129}," r",[105,13094,574],{"class":111},[105,13096,13097],{"class":241}," onFulfilled",[105,13099,13100],{"class":115},"(value);\n",[105,13102,13103,13106,13109,13111,13113],{"class":107,"line":4679},[105,13104,13105],{"class":111},"                    if",[105,13107,13108],{"class":115}," (r ",[105,13110,8153],{"class":111},[105,13112,12181],{"class":241},[105,13114,1000],{"class":115},[105,13116,13117,13120,13122],{"class":107,"line":4684},[105,13118,13119],{"class":115},"                        r.",[105,13121,5512],{"class":241},[105,13123,583],{"class":115},[105,13125,13126,13129,13131,13133,13135,13138,13140,13142,13144],{"class":107,"line":4692},[105,13127,13128],{"class":115},"                            (",[105,13130,12554],{"class":122},[105,13132,2836],{"class":115},[105,13134,723],{"class":111},[105,13136,13137],{"class":241}," resolve",[105,13139,12567],{"class":115},[105,13141,10634],{"class":111},[105,13143,12013],{"class":241},[105,13145,12574],{"class":115},[105,13147,13148,13150,13152,13154,13156,13159],{"class":107,"line":4703},[105,13149,13128],{"class":115},[105,13151,3297],{"class":122},[105,13153,2836],{"class":115},[105,13155,723],{"class":111},[105,13157,13158],{"class":241}," reject",[105,13160,13161],{"class":115},"(e)\n",[105,13163,13164],{"class":107,"line":4714},[105,13165,13166],{"class":115},"                        );\n",[105,13168,13169,13172,13175],{"class":107,"line":4725},[105,13170,13171],{"class":115},"                    } ",[105,13173,13174],{"class":111},"else",[105,13176,116],{"class":115},[105,13178,13179,13182,13185,13187,13189],{"class":107,"line":4737},[105,13180,13181],{"class":241},"                        resolve",[105,13183,13184],{"class":115},"(r ",[105,13186,10634],{"class":111},[105,13188,12013],{"class":241},[105,13190,327],{"class":115},[105,13192,13193],{"class":107,"line":4753},[105,13194,13195],{"class":115},"                    }\n",[105,13197,13198,13201,13203],{"class":107,"line":4758},[105,13199,13200],{"class":115},"                } ",[105,13202,5539],{"class":111},[105,13204,12453],{"class":115},[105,13206,13207,13210],{"class":107,"line":4763},[105,13208,13209],{"class":241},"                    reject",[105,13211,12465],{"class":115},[105,13213,13214],{"class":107,"line":4771},[105,13215,13077],{"class":115},[105,13217,13218],{"class":107,"line":4782},[105,13219,13220],{"class":115},"            };\n",[105,13222,13223],{"class":107,"line":4793},[105,13224,307],{"emptyLinePlaceholder":306},[105,13226,13227,13229,13232,13234,13236,13238,13240,13242,13244,13246],{"class":107,"line":4804},[105,13228,6289],{"class":111},[105,13230,13231],{"class":241}," handlerRejected",[105,13233,574],{"class":111},[105,13235,4361],{"class":115},[105,13237,11593],{"class":122},[105,13239,4999],{"class":111},[105,13241,10417],{"class":129},[105,13243,2836],{"class":115},[105,13245,723],{"class":111},[105,13247,116],{"class":115},[105,13249,13250,13252,13254,13256,13259,13261,13263],{"class":107,"line":4815},[105,13251,13033],{"class":111},[105,13253,4361],{"class":115},[105,13255,8042],{"class":111},[105,13257,13258],{"class":115}," onRejected ",[105,13260,3314],{"class":111},[105,13262,10533],{"class":248},[105,13264,1000],{"class":115},[105,13266,13267,13269],{"class":107,"line":4827},[105,13268,13209],{"class":241},[105,13270,13271],{"class":115},"(reason);\n",[105,13273,13274,13276],{"class":107,"line":4832},[105,13275,13070],{"class":111},[105,13277,145],{"class":115},[105,13279,13280],{"class":107,"line":4837},[105,13281,13077],{"class":115},[105,13283,13284,13286],{"class":107,"line":4845},[105,13285,13082],{"class":111},[105,13287,116],{"class":115},[105,13289,13290,13292,13294,13296,13299],{"class":107,"line":4858},[105,13291,13089],{"class":111},[105,13293,13092],{"class":129},[105,13295,574],{"class":111},[105,13297,13298],{"class":241}," onRejected",[105,13300,13271],{"class":115},[105,13302,13303,13305,13307,13309,13311],{"class":107,"line":4877},[105,13304,13105],{"class":111},[105,13306,13108],{"class":115},[105,13308,8153],{"class":111},[105,13310,12181],{"class":241},[105,13312,1000],{"class":115},[105,13314,13315,13317,13319],{"class":107,"line":4889},[105,13316,13119],{"class":115},[105,13318,5512],{"class":241},[105,13320,583],{"class":115},[105,13322,13323,13325,13327,13329,13331,13333,13335,13337,13339],{"class":107,"line":4902},[105,13324,13128],{"class":115},[105,13326,12554],{"class":122},[105,13328,2836],{"class":115},[105,13330,723],{"class":111},[105,13332,13137],{"class":241},[105,13334,12567],{"class":115},[105,13336,10634],{"class":111},[105,13338,12013],{"class":241},[105,13340,12574],{"class":115},[105,13342,13343,13345,13347,13349,13351,13353],{"class":107,"line":4918},[105,13344,13128],{"class":115},[105,13346,3297],{"class":122},[105,13348,2836],{"class":115},[105,13350,723],{"class":111},[105,13352,13158],{"class":241},[105,13354,13161],{"class":115},[105,13356,13357],{"class":107,"line":4923},[105,13358,13166],{"class":115},[105,13360,13362,13364,13366],{"class":107,"line":13361},120,[105,13363,13171],{"class":115},[105,13365,13174],{"class":111},[105,13367,116],{"class":115},[105,13369,13371,13373,13375,13377,13379,13381,13383],{"class":107,"line":13370},121,[105,13372,13181],{"class":241},[105,13374,13184],{"class":115},[105,13376,10634],{"class":111},[105,13378,8438],{"class":129},[105,13380,13061],{"class":111},[105,13382,12013],{"class":241},[105,13384,327],{"class":115},[105,13386,13388],{"class":107,"line":13387},122,[105,13389,13195],{"class":115},[105,13391,13393,13395,13397],{"class":107,"line":13392},123,[105,13394,13200],{"class":115},[105,13396,5539],{"class":111},[105,13398,12453],{"class":115},[105,13400,13402,13404],{"class":107,"line":13401},124,[105,13403,13209],{"class":241},[105,13405,12465],{"class":115},[105,13407,13409],{"class":107,"line":13408},125,[105,13410,13077],{"class":115},[105,13412,13414],{"class":107,"line":13413},126,[105,13415,13220],{"class":115},[105,13417,13419],{"class":107,"line":13418},127,[105,13420,307],{"emptyLinePlaceholder":306},[105,13422,13424,13426,13428,13430,13432,13434,13436],{"class":107,"line":13423},128,[105,13425,5962],{"class":111},[105,13427,4361],{"class":115},[105,13429,2870],{"class":129},[105,13431,12514],{"class":115},[105,13433,7251],{"class":111},[105,13435,11815],{"class":248},[105,13437,1000],{"class":115},[105,13439,13441,13443,13446,13449],{"class":107,"line":13440},129,[105,13442,12458],{"class":129},[105,13444,13445],{"class":115},".PromiseFulfillReactions.",[105,13447,13448],{"class":241},"push",[105,13450,13451],{"class":115},"(handlerFulfilled);\n",[105,13453,13455,13457,13460,13462],{"class":107,"line":13454},130,[105,13456,12458],{"class":129},[105,13458,13459],{"class":115},".PromiseRejectReactions.",[105,13461,13448],{"class":241},[105,13463,13464],{"class":115},"(handlerRejected);\n",[105,13466,13468,13470,13472,13475,13477,13479,13481,13483,13485],{"class":107,"line":13467},131,[105,13469,12448],{"class":115},[105,13471,13174],{"class":111},[105,13473,13474],{"class":111}," if",[105,13476,4361],{"class":115},[105,13478,2870],{"class":129},[105,13480,12514],{"class":115},[105,13482,7251],{"class":111},[105,13484,11820],{"class":248},[105,13486,1000],{"class":115},[105,13488,13490,13493,13495,13497,13499,13501,13503,13505,13507,13509],{"class":107,"line":13489},132,[105,13491,13492],{"class":241},"                queueMicrotask",[105,13494,4052],{"class":115},[105,13496,723],{"class":111},[105,13498,13012],{"class":241},[105,13500,321],{"class":115},[105,13502,2870],{"class":129},[105,13504,12632],{"class":115},[105,13506,10634],{"class":111},[105,13508,5057],{"class":241},[105,13510,13511],{"class":115},"));\n",[105,13513,13515,13517,13519],{"class":107,"line":13514},133,[105,13516,12448],{"class":115},[105,13518,13174],{"class":111},[105,13520,116],{"class":115},[105,13522,13524,13526,13528,13530,13532,13534,13536],{"class":107,"line":13523},134,[105,13525,13492],{"class":241},[105,13527,4052],{"class":115},[105,13529,723],{"class":111},[105,13531,13231],{"class":241},[105,13533,321],{"class":115},[105,13535,2870],{"class":129},[105,13537,13538],{"class":115},".result));\n",[105,13540,13542],{"class":107,"line":13541},135,[105,13543,12470],{"class":115},[105,13545,13547],{"class":107,"line":13546},136,[105,13548,13549],{"class":115},"        });\n",[105,13551,13553],{"class":107,"line":13552},137,[105,13554,5143],{"class":115},[105,13556,13558],{"class":107,"line":13557},138,[105,13559,307],{"emptyLinePlaceholder":306},[105,13561,13563,13565,13567,13569,13571,13573],{"class":107,"line":13562},139,[105,13564,12093],{"class":241},[105,13566,234],{"class":115},[105,13568,12098],{"class":241},[105,13570,574],{"class":111},[105,13572,11532],{"class":129},[105,13574,11992],{"class":115},[105,13576,13578,13580,13582,13584,13586,13588,13590,13592,13594,13596,13598,13600,13602,13604,13606,13608],{"class":107,"line":13577},140,[105,13579,11586],{"class":122},[105,13581,11544],{"class":111},[105,13583,11547],{"class":115},[105,13585,11593],{"class":122},[105,13587,4999],{"class":111},[105,13589,10417],{"class":129},[105,13591,2836],{"class":115},[105,13593,723],{"class":111},[105,13595,12125],{"class":241},[105,13597,5023],{"class":111},[105,13599,11566],{"class":241},[105,13601,234],{"class":115},[105,13603,12098],{"class":241},[105,13605,12024],{"class":115},[105,13607,11563],{"class":111},[105,13609,11622],{"class":129},[105,13611,13613,13615,13617,13619,13621,13623,13625,13627],{"class":107,"line":13612},141,[105,13614,12069],{"class":115},[105,13616,4999],{"class":111},[105,13618,11566],{"class":241},[105,13620,234],{"class":115},[105,13622,4990],{"class":241},[105,13624,5023],{"class":111},[105,13626,12125],{"class":241},[105,13628,11844],{"class":115},[105,13630,13632,13634,13636,13638,13640,13642,13644],{"class":107,"line":13631},142,[105,13633,11236],{"class":111},[105,13635,12411],{"class":129},[105,13637,5363],{"class":115},[105,13639,5512],{"class":241},[105,13641,321],{"class":115},[105,13643,3987],{"class":129},[105,13645,13646],{"class":115},", onRejected);\n",[105,13648,13650],{"class":107,"line":13649},143,[105,13651,5143],{"class":115},[105,13653,13655],{"class":107,"line":13654},144,[105,13656,307],{"emptyLinePlaceholder":306},[105,13658,13660,13663,13665,13667,13669,13671,13673,13675,13677,13679,13681,13683,13685,13687],{"class":107,"line":13659},145,[105,13661,13662],{"class":111},"    static",[105,13664,13137],{"class":241},[105,13666,234],{"class":115},[105,13668,4990],{"class":241},[105,13670,4993],{"class":115},[105,13672,8433],{"class":122},[105,13674,4999],{"class":111},[105,13676,5057],{"class":241},[105,13678,11938],{"class":115},[105,13680,4999],{"class":111},[105,13682,12181],{"class":241},[105,13684,234],{"class":115},[105,13686,4990],{"class":241},[105,13688,11844],{"class":115},[105,13690,13692,13694,13696,13698,13700,13702,13704,13706,13708,13710,13712],{"class":107,"line":13691},146,[105,13693,11236],{"class":111},[105,13695,3478],{"class":111},[105,13697,12181],{"class":241},[105,13699,234],{"class":115},[105,13701,4990],{"class":241},[105,13703,12993],{"class":115},[105,13705,9492],{"class":122},[105,13707,2836],{"class":115},[105,13709,723],{"class":111},[105,13711,13137],{"class":241},[105,13713,12709],{"class":115},[105,13715,13717],{"class":107,"line":13716},147,[105,13718,5143],{"class":115},[105,13720,13722],{"class":107,"line":13721},148,[105,13723,307],{"emptyLinePlaceholder":306},[105,13725,13727,13729,13731,13733,13735,13737,13739,13741,13743,13745,13747,13749,13751,13753,13755,13757],{"class":107,"line":13726},149,[105,13728,13662],{"class":111},[105,13730,13158],{"class":241},[105,13732,234],{"class":115},[105,13734,4990],{"class":241},[105,13736,574],{"class":111},[105,13738,11532],{"class":129},[105,13740,4993],{"class":115},[105,13742,11593],{"class":122},[105,13744,11544],{"class":111},[105,13746,10417],{"class":129},[105,13748,11938],{"class":115},[105,13750,4999],{"class":111},[105,13752,12181],{"class":241},[105,13754,234],{"class":115},[105,13756,4990],{"class":241},[105,13758,11844],{"class":115},[105,13760,13762,13764,13766,13768,13770,13772,13774,13777,13779,13781,13783,13785,13787],{"class":107,"line":13761},150,[105,13763,11236],{"class":111},[105,13765,3478],{"class":111},[105,13767,12181],{"class":241},[105,13769,234],{"class":115},[105,13771,4990],{"class":241},[105,13773,12993],{"class":115},[105,13775,13776],{"class":122},"_",[105,13778,286],{"class":115},[105,13780,12435],{"class":122},[105,13782,2836],{"class":115},[105,13784,723],{"class":111},[105,13786,13158],{"class":241},[105,13788,12852],{"class":115},[105,13790,13792],{"class":107,"line":13791},151,[105,13793,5143],{"class":115},[105,13795,13797],{"class":107,"line":13796},152,[105,13798,212],{"class":115},[105,13800,13802],{"class":107,"line":13801},153,[105,13803,307],{"emptyLinePlaceholder":306},[105,13805,13807,13810,13812,13814,13817,13819,13821],{"class":107,"line":13806},154,[105,13808,13809],{"class":115},"MyPromise.",[105,13811,9492],{"class":241},[105,13813,321],{"class":115},[105,13815,13816],{"class":129},"10",[105,13818,5624],{"class":115},[105,13820,5512],{"class":241},[105,13822,583],{"class":115},[105,13824,13826,13828,13830,13832,13834,13836,13838,13840,13843,13846],{"class":107,"line":13825},155,[105,13827,10291],{"class":115},[105,13829,12554],{"class":122},[105,13831,2836],{"class":115},[105,13833,723],{"class":111},[105,13835,5522],{"class":115},[105,13837,639],{"class":241},[105,13839,321],{"class":115},[105,13841,13842],{"class":248},"'fulfilled'",[105,13844,13845],{"class":115},", v),   ",[105,13847,13848],{"class":630},"\u002F\u002F fulfilled 10\n",[105,13850,13852,13854,13856,13858,13860,13862,13864,13866,13869],{"class":107,"line":13851},156,[105,13853,10291],{"class":115},[105,13855,12581],{"class":122},[105,13857,2836],{"class":115},[105,13859,723],{"class":111},[105,13861,5522],{"class":115},[105,13863,639],{"class":241},[105,13865,321],{"class":115},[105,13867,13868],{"class":248},"'rejected'",[105,13870,13871],{"class":115},", r)\n",[105,13873,13875],{"class":107,"line":13874},157,[105,13876,327],{"class":115},[18,13878,13879],{},"await就是专门为Promise设计的，我们一般喜欢await x 这样来使用，如果x不是thenable，就会直接返回x，如果x是thenable，就会等它敲定，比如fulfilled返回成功值，rejected跑出拒绝原因。因此await不要求x是Promise实例，只要有then方法就行，说白了，就是认定鸭子类型。",[14,13881,13882],{"id":13882},"原型链",[18,13884,13885],{},"原型链的最初设计目的是为了用最少的机制来解决几个问题，Brendan Eich 1995年设计js的时候，是让对象能够借用另一个对象的方法和属性，而不需要每个对象都复制一份。原型链的本质就是委托，当你访问b.greet的时候b自己没有，就会委托给它的原型a去找，这是一种运行时动态查找，而不是编译器的类继承。",[18,13887,13888],{},"Brendan Eich收Self语言影响，核心思想是不需要类，对象可以直接从对象继承。这个就和传统OOP倾向的语言比如Java\u002FC++不一样了。Self\u002FJS的原型模型那就是： 对象 -> 对象 -> 对象。继承关系在对象之间定义，没有类中间层，每个对象都可以有自己的原型，可以动态修改。",[18,13890,13891],{},"所以，完全可以得出一个结论： js选择原型，是因为更加简单，更加灵活，更加适合脚本语言。这样就可以解决好几个问题：",[18,13893,13894],{},"1是内存效率问题，通过千万个实例可以共享一个函数，而不是每个都复制一份。2是动态性，原型链本来就是运行时查找，改了原型，所有实例都会立即收到影响，这个是类继承做不到的。",[18,13896,13897],{},"es5之前，js是没有class语法的，定义类的方式就是构造函数+原型。es6之后引入class作为了关键字和语法糖。但是到现在都没有interface关键字，因为js是动态语言，运行时才检查类型，不需要接口作为编译器约束。",[18,13899,13900,13903,13904,13906,13907,13909,13910,13913,13914,7813,13921,13924],{},[21,13901,13902],{},"每个函数","都有一个 ",[24,13905,8239],{}," 属性，指向一个对象，",[24,13908,8239],{}," 是",[21,13911,13912],{},"函数才有的属性","，它的作用是：",[21,13915,13916,13917,13920],{},"当这个函数被用作构造函数（",[24,13918,13919],{},"new Foo()","）时，新对象的原型会指向它。",[21,13922,13923],{},"proto__就是每个对象内部的一个槽位，叫做[[Prototype]]，这个指向它的原型对象，访问方式是通过__proto","，或者Object.getPrototypeOf方法。 __proto__是对象才有的属性，指向继承。",[18,13926,13927,13928,9829],{},"默认情况下，",[24,13929,13930],{},"Foo.prototype.constructor === Foo",[96,13932,13934],{"className":3241,"code":13933,"language":3243,"meta":101,"style":101},"function Foo() {}\nFoo.prototype.constructor === Foo   \u002F\u002F true\n",[24,13935,13936,13944],{"__ignoreMap":101},[105,13937,13938,13940,13942],{"class":107,"line":108},[105,13939,8113],{"class":111},[105,13941,10154],{"class":241},[105,13943,10005],{"class":115},[105,13945,13946,13949,13951,13953,13955,13957,13960,13963],{"class":107,"line":119},[105,13947,13948],{"class":129},"Foo",[105,13950,5363],{"class":115},[105,13952,8239],{"class":129},[105,13954,5363],{"class":115},[105,13956,11457],{"class":129},[105,13958,13959],{"class":111}," ===",[105,13961,13962],{"class":115}," Foo   ",[105,13964,13965],{"class":630},"\u002F\u002F true\n",[18,13967,13968,13970],{},[24,13969,11457],{}," 让实例能反向找到\"创建我的构造函数\"。",[18,13972,13973],{},"还记得我们之前提到过的类型检测方法吗：",[96,13975,13976],{"className":3241,"code":8227,"language":3243,"meta":101,"style":101},[24,13977,13978,14000,14022,14038,14060,14076,14096],{"__ignoreMap":101},[105,13979,13980,13982,13984,13986,13988,13990,13992,13994,13996,13998],{"class":107,"line":108},[105,13981,8234],{"class":129},[105,13983,5363],{"class":115},[105,13985,8239],{"class":129},[105,13987,8242],{"class":115},[105,13989,8245],{"class":241},[105,13991,321],{"class":115},[105,13993,7412],{"class":111},[105,13995,7415],{"class":241},[105,13997,8254],{"class":115},[105,13999,8257],{"class":630},[105,14001,14002,14004,14006,14008,14010,14012,14014,14016,14018,14020],{"class":107,"line":119},[105,14003,8234],{"class":129},[105,14005,5363],{"class":115},[105,14007,8239],{"class":129},[105,14009,8242],{"class":115},[105,14011,8245],{"class":241},[105,14013,321],{"class":115},[105,14015,7412],{"class":111},[105,14017,5046],{"class":241},[105,14019,8254],{"class":115},[105,14021,8280],{"class":630},[105,14023,14024,14026,14028,14030,14032,14034,14036],{"class":107,"line":133},[105,14025,8234],{"class":129},[105,14027,5363],{"class":115},[105,14029,8239],{"class":129},[105,14031,8242],{"class":115},[105,14033,8245],{"class":241},[105,14035,8295],{"class":115},[105,14037,8298],{"class":630},[105,14039,14040,14042,14044,14046,14048,14050,14052,14054,14056,14058],{"class":107,"line":148},[105,14041,8234],{"class":129},[105,14043,5363],{"class":115},[105,14045,8239],{"class":129},[105,14047,8242],{"class":115},[105,14049,8245],{"class":241},[105,14051,321],{"class":115},[105,14053,7412],{"class":111},[105,14055,7459],{"class":241},[105,14057,8319],{"class":115},[105,14059,8322],{"class":630},[105,14061,14062,14064,14066,14068,14070,14072,14074],{"class":107,"line":163},[105,14063,8234],{"class":129},[105,14065,5363],{"class":115},[105,14067,8239],{"class":129},[105,14069,8242],{"class":115},[105,14071,8245],{"class":241},[105,14073,8337],{"class":115},[105,14075,8340],{"class":630},[105,14077,14078,14080,14082,14084,14086,14088,14090,14092,14094],{"class":107,"line":179},[105,14079,8234],{"class":129},[105,14081,5363],{"class":115},[105,14083,8239],{"class":129},[105,14085,8242],{"class":115},[105,14087,8245],{"class":241},[105,14089,321],{"class":115},[105,14091,3987],{"class":129},[105,14093,8359],{"class":115},[105,14095,8362],{"class":630},[105,14097,14098,14100,14102,14104,14106,14108,14110,14112,14114],{"class":107,"line":194},[105,14099,8234],{"class":129},[105,14101,5363],{"class":115},[105,14103,8239],{"class":129},[105,14105,8242],{"class":115},[105,14107,8245],{"class":241},[105,14109,321],{"class":115},[105,14111,8379],{"class":129},[105,14113,8382],{"class":115},[105,14115,8385],{"class":630},[18,14117,14118],{},"这里可以发现Object本身就是函数，所以他有prototype，这个也很有趣，Object既是函数，是一个构造函数，我们可以断定的是Object.prototype是所有普通对象的原型链顶端：",[96,14120,14122],{"className":3241,"code":14121,"language":3243,"meta":101,"style":101},"const obj = {};\nobj.__proto__ === Object.prototype   \u002F\u002F true\n\nObject.getPrototypeOf(obj) === Object.prototype   \u002F\u002F true\n\n`Object.prototype` 上挂着所有对象共享的方法：\n\nObject.prototype.toString\nObject.prototype.hasOwnProperty\nObject.prototype.valueOf\nObject.prototype.isPrototypeOf\nObject.prototype.propertyIsEnumerable\nObject.prototype.toLocaleString\n\nObject.prototype.constructor === Object \u002F\u002F true , constructor也是指向Object自己\n",[24,14123,14124,14135,14153,14157,14178,14182,14190,14194,14205,14216,14227,14238,14249,14260,14264],{"__ignoreMap":101},[105,14125,14126,14128,14130,14132],{"class":107,"line":108},[105,14127,568],{"class":111},[105,14129,10188],{"class":129},[105,14131,574],{"class":111},[105,14133,14134],{"class":115}," {};\n",[105,14136,14137,14140,14142,14144,14146,14148,14150],{"class":107,"line":119},[105,14138,14139],{"class":115},"obj.",[105,14141,11434],{"class":129},[105,14143,13959],{"class":111},[105,14145,8447],{"class":129},[105,14147,5363],{"class":115},[105,14149,8239],{"class":129},[105,14151,14152],{"class":630},"   \u002F\u002F true\n",[105,14154,14155],{"class":107,"line":133},[105,14156,307],{"emptyLinePlaceholder":306},[105,14158,14159,14162,14165,14168,14170,14172,14174,14176],{"class":107,"line":148},[105,14160,14161],{"class":115},"Object.",[105,14163,14164],{"class":241},"getPrototypeOf",[105,14166,14167],{"class":115},"(obj) ",[105,14169,7251],{"class":111},[105,14171,8447],{"class":129},[105,14173,5363],{"class":115},[105,14175,8239],{"class":129},[105,14177,14152],{"class":630},[105,14179,14180],{"class":107,"line":163},[105,14181,307],{"emptyLinePlaceholder":306},[105,14183,14184,14187],{"class":107,"line":179},[105,14185,14186],{"class":248},"`Object.prototype`",[105,14188,14189],{"class":115}," 上挂着所有对象共享的方法：\n",[105,14191,14192],{"class":107,"line":194},[105,14193,307],{"emptyLinePlaceholder":306},[105,14195,14196,14198,14200,14202],{"class":107,"line":209},[105,14197,8234],{"class":129},[105,14199,5363],{"class":115},[105,14201,8239],{"class":129},[105,14203,14204],{"class":115},".toString\n",[105,14206,14207,14209,14211,14213],{"class":107,"line":526},[105,14208,8234],{"class":129},[105,14210,5363],{"class":115},[105,14212,8239],{"class":129},[105,14214,14215],{"class":115},".hasOwnProperty\n",[105,14217,14218,14220,14222,14224],{"class":107,"line":531},[105,14219,8234],{"class":129},[105,14221,5363],{"class":115},[105,14223,8239],{"class":129},[105,14225,14226],{"class":115},".valueOf\n",[105,14228,14229,14231,14233,14235],{"class":107,"line":668},[105,14230,8234],{"class":129},[105,14232,5363],{"class":115},[105,14234,8239],{"class":129},[105,14236,14237],{"class":115},".isPrototypeOf\n",[105,14239,14240,14242,14244,14246],{"class":107,"line":673},[105,14241,8234],{"class":129},[105,14243,5363],{"class":115},[105,14245,8239],{"class":129},[105,14247,14248],{"class":115},".propertyIsEnumerable\n",[105,14250,14251,14253,14255,14257],{"class":107,"line":679},[105,14252,8234],{"class":129},[105,14254,5363],{"class":115},[105,14256,8239],{"class":129},[105,14258,14259],{"class":115},".toLocaleString\n",[105,14261,14262],{"class":107,"line":689},[105,14263,307],{"emptyLinePlaceholder":306},[105,14265,14266,14268,14270,14272,14274,14276,14278,14281],{"class":107,"line":699},[105,14267,8234],{"class":129},[105,14269,5363],{"class":115},[105,14271,8239],{"class":129},[105,14273,5363],{"class":115},[105,14275,11457],{"class":129},[105,14277,13959],{"class":111},[105,14279,14280],{"class":115}," Object ",[105,14282,14283],{"class":630},"\u002F\u002F true , constructor也是指向Object自己\n",[18,14285,14286],{},"instanceof 的原理是： obj instanceof Foo 检查 Foo.prototype 是否在obj的原型链上，你甚至也可以手动去实现：",[96,14288,14290],{"className":3241,"code":14289,"language":3243,"meta":101,"style":101},"function myInstanceof(obj, Constructor) {\n    let proto = Object.getPrototypeOf(obj);\n    while (proto !== null) {\n        if (proto === Constructor.prototype) return true;\n        proto = Object.getPrototypeOf(proto);\n    }\n    return false;\n}\n",[24,14291,14292,14310,14326,14340,14363,14377,14381,14389],{"__ignoreMap":101},[105,14293,14294,14296,14299,14301,14304,14306,14308],{"class":107,"line":108},[105,14295,8113],{"class":111},[105,14297,14298],{"class":241}," myInstanceof",[105,14300,321],{"class":115},[105,14302,14303],{"class":122},"obj",[105,14305,286],{"class":115},[105,14307,11309],{"class":122},[105,14309,1000],{"class":115},[105,14311,14312,14315,14318,14320,14322,14324],{"class":107,"line":119},[105,14313,14314],{"class":111},"    let",[105,14316,14317],{"class":115}," proto ",[105,14319,245],{"class":111},[105,14321,11336],{"class":115},[105,14323,14164],{"class":241},[105,14325,10782],{"class":115},[105,14327,14328,14331,14334,14336,14338],{"class":107,"line":133},[105,14329,14330],{"class":111},"    while",[105,14332,14333],{"class":115}," (proto ",[105,14335,3314],{"class":111},[105,14337,5026],{"class":129},[105,14339,1000],{"class":115},[105,14341,14342,14344,14346,14348,14351,14353,14355,14357,14359,14361],{"class":107,"line":148},[105,14343,12370],{"class":111},[105,14345,14333],{"class":115},[105,14347,7251],{"class":111},[105,14349,14350],{"class":129}," Constructor",[105,14352,5363],{"class":115},[105,14354,8239],{"class":129},[105,14356,2836],{"class":115},[105,14358,3322],{"class":111},[105,14360,4392],{"class":129},[105,14362,145],{"class":115},[105,14364,14365,14368,14370,14372,14374],{"class":107,"line":163},[105,14366,14367],{"class":115},"        proto ",[105,14369,245],{"class":111},[105,14371,11336],{"class":115},[105,14373,14164],{"class":241},[105,14375,14376],{"class":115},"(proto);\n",[105,14378,14379],{"class":107,"line":179},[105,14380,5143],{"class":115},[105,14382,14383,14385,14387],{"class":107,"line":194},[105,14384,10662],{"class":111},[105,14386,4346],{"class":129},[105,14388,145],{"class":115},[105,14390,14391],{"class":107,"line":209},[105,14392,212],{"class":115},[18,14394,14395,14396,14399],{},"所以函数是一种特殊的对象，之所以能被调用是因为有 ",[24,14397,14398],{},"[[Call]]","，同时具备对象的一切能力，能挂载属性，有原型，能作为参数传递，函数既可以被当作普通函数，也可以被当成构造函数。",[18,14401,14402],{},[21,14403,14404,14405,14407],{},"两个 ",[24,14406,11457],{},"，别混：",[835,14409,14410,14422],{},[838,14411,14412],{},[841,14413,14414,14417,14420],{},[844,14415,14416],{},"表达式",[844,14418,14419],{},"结果",[844,14421,1169],{},[854,14423,14424,14444,14463],{},[841,14425,14426,14431,14435],{},[859,14427,14428],{},[24,14429,14430],{},"Foo.constructor",[859,14432,14433],{},[24,14434,10467],{},[859,14436,14437,14439,14440,14443],{},[24,14438,13948],{}," 这个函数",[21,14441,14442],{},"对象","由谁创建",[841,14445,14446,14451,14455],{},[859,14447,14448],{},[24,14449,14450],{},"Foo.prototype.constructor",[859,14452,14453],{},[24,14454,13948],{},[859,14456,14457,14459,14460,14443],{},[24,14458,13948],{}," 的",[21,14461,14462],{},"实例",[841,14464,14465,14468,14470],{},[859,14466,14467],{},"new Foo()执行的就是Foo这个函数，而不是Foo.constructor。",[859,14469],{},[859,14471],{},[14,14473,14474],{"id":14474},"数组可以调用的方法",[18,14476,14477],{},"数组可以调用的函数分为实例方法，挂载到Array.prototype上的，数组可以直接使用。静态方法是挂载在Array的构造函数上的，通过Array.xxx调用。",[18,14479,14480],{},"实例方法：",[835,14482,14483,14492],{},[838,14484,14485],{},[841,14486,14487,14490],{},[844,14488,14489],{},"方法",[844,14491,2223],{},[854,14493,14494,14504,14514,14524,14534,14544,14554,14564,14574],{},[841,14495,14496,14501],{},[859,14497,14498],{},[24,14499,14500],{},"push(...items)",[859,14502,14503],{},"末尾添加，返回新长度",[841,14505,14506,14511],{},[859,14507,14508],{},[24,14509,14510],{},"pop()",[859,14512,14513],{},"删除末尾，返回被删元素",[841,14515,14516,14521],{},[859,14517,14518],{},[24,14519,14520],{},"unshift(...items)",[859,14522,14523],{},"开头添加，返回新长度",[841,14525,14526,14531],{},[859,14527,14528],{},[24,14529,14530],{},"shift()",[859,14532,14533],{},"删除开头，返回被删元素",[841,14535,14536,14541],{},[859,14537,14538],{},[24,14539,14540],{},"splice(start, deleteCount, ...items)",[859,14542,14543],{},"任意位置增删，返回被删元素数组",[841,14545,14546,14551],{},[859,14547,14548],{},[24,14549,14550],{},"reverse()",[859,14552,14553],{},"反转，返回原数组",[841,14555,14556,14561],{},[859,14557,14558],{},[24,14559,14560],{},"sort(compareFn?)",[859,14562,14563],{},"排序，返回原数组",[841,14565,14566,14571],{},[859,14567,14568],{},[24,14569,14570],{},"fill(value, start?, end?)",[859,14572,14573],{},"填充，返回原数组",[841,14575,14576,14581],{},[859,14577,14578],{},[24,14579,14580],{},"copyWithin(target, start?, end?)",[859,14582,14583],{},"内部复制，返回原数组",[835,14585,14586,14594],{},[838,14587,14588],{},[841,14589,14590,14592],{},[844,14591,14489],{},[844,14593,2223],{},[854,14595,14596,14606,14616,14626,14636,14646,14656,14666,14676,14686,14696,14706,14716,14726,14736,14746,14756,14766,14776,14786],{},[841,14597,14598,14603],{},[859,14599,14600],{},[24,14601,14602],{},"concat(...items)",[859,14604,14605],{},"合并，返回新数组",[841,14607,14608,14613],{},[859,14609,14610],{},[24,14611,14612],{},"slice(start?, end?)",[859,14614,14615],{},"截取，返回新数组",[841,14617,14618,14623],{},[859,14619,14620],{},[24,14621,14622],{},"join(sep?)",[859,14624,14625],{},"转字符串",[841,14627,14628,14633],{},[859,14629,14630],{},[24,14631,14632],{},"indexOf(item, from?)",[859,14634,14635],{},"找索引，找不到返回 -1",[841,14637,14638,14643],{},[859,14639,14640],{},[24,14641,14642],{},"lastIndexOf(item, from?)",[859,14644,14645],{},"从后找索引",[841,14647,14648,14653],{},[859,14649,14650],{},[24,14651,14652],{},"includes(item, from?)",[859,14654,14655],{},"是否包含，返回 boolean",[841,14657,14658,14663],{},[859,14659,14660],{},[24,14661,14662],{},"find(cb)",[859,14664,14665],{},"找第一个满足的元素",[841,14667,14668,14673],{},[859,14669,14670],{},[24,14671,14672],{},"findIndex(cb)",[859,14674,14675],{},"找第一个满足的索引",[841,14677,14678,14683],{},[859,14679,14680],{},[24,14681,14682],{},"findLast(cb)",[859,14684,14685],{},"从后找第一个满足的元素（ES2023）",[841,14687,14688,14693],{},[859,14689,14690],{},[24,14691,14692],{},"findLastIndex(cb)",[859,14694,14695],{},"从后找索引（ES2023）",[841,14697,14698,14703],{},[859,14699,14700],{},[24,14701,14702],{},"filter(cb)",[859,14704,14705],{},"过滤，返回新数组",[841,14707,14708,14713],{},[859,14709,14710],{},[24,14711,14712],{},"map(cb)",[859,14714,14715],{},"映射，返回新数组",[841,14717,14718,14723],{},[859,14719,14720],{},[24,14721,14722],{},"flat(depth?)",[859,14724,14725],{},"扁平化",[841,14727,14728,14733],{},[859,14729,14730],{},[24,14731,14732],{},"flatMap(cb)",[859,14734,14735],{},"map + flat",[841,14737,14738,14743],{},[859,14739,14740],{},[24,14741,14742],{},"at(index)",[859,14744,14745],{},"支持负数索引",[841,14747,14748,14753],{},[859,14749,14750],{},[24,14751,14752],{},"with(index, value)",[859,14754,14755],{},"返回替换后的新数组（ES2023）",[841,14757,14758,14763],{},[859,14759,14760],{},[24,14761,14762],{},"toReversed()",[859,14764,14765],{},"反转副本（ES2023）",[841,14767,14768,14773],{},[859,14769,14770],{},[24,14771,14772],{},"toSorted(cmp?)",[859,14774,14775],{},"排序副本（ES2023）",[841,14777,14778,14783],{},[859,14779,14780],{},[24,14781,14782],{},"toSpliced(...)",[859,14784,14785],{},"splice 副本（ES2023）",[841,14787,14788,14791],{},[859,14789,14790],{},"静态方法：",[859,14792],{},[835,14794,14795,14803],{},[838,14796,14797],{},[841,14798,14799,14801],{},[844,14800,14489],{},[844,14802,2223],{},[854,14804,14805,14815,14828,14838],{},[841,14806,14807,14812],{},[859,14808,14809],{},[24,14810,14811],{},"Array.from(iterable, mapFn?, thisArg?)",[859,14813,14814],{},"从类数组\u002F可迭代对象创建数组",[841,14816,14817,14822],{},[859,14818,14819],{},[24,14820,14821],{},"Array.fromAsync(iterable)",[859,14823,14824,14825,14827],{},"异步版 ",[24,14826,3891],{},"（ES2023）",[841,14829,14830,14835],{},[859,14831,14832],{},[24,14833,14834],{},"Array.of(...items)",[859,14836,14837],{},"用参数创建数组",[841,14839,14840,14845],{},[859,14841,14842],{},[24,14843,14844],{},"Array.isArray(x)",[859,14846,14847],{},"判断是否数组",[18,14849,14850,14852,14853,14856],{},[24,14851,8130],{}," 是 ",[24,14854,14855],{},"ArrayConstructor"," ，这个可以在ts的lib.es5.d.ts里找，大致如下：",[96,14858,14860],{"className":3241,"code":14859,"language":3243,"meta":101,"style":101},"interface ArrayConstructor {\n    new (arrayLength?: number): any[];\n    new \u003CT>(arrayLength: number): T[];\n    new \u003CT>(...items: T[]): T[];\n    isArray(arg: any): arg is any[];\n    from\u003CT>(arrayLike: ArrayLike\u003CT>): T[];\n    of\u003CT>(...items: T[]): T[];\n    \u002F\u002F ...\n}\n\ndeclare var Array: ArrayConstructor;\n",[24,14861,14862,14871,14894,14919,14946,14974,15006,15033,15038,15042,15046],{"__ignoreMap":101},[105,14863,14864,14866,14869],{"class":107,"line":108},[105,14865,3501],{"class":111},[105,14867,14868],{"class":241}," ArrayConstructor",[105,14870,116],{"class":115},[105,14872,14873,14875,14877,14880,14882,14885,14887,14889,14891],{"class":107,"line":119},[105,14874,10264],{"class":111},[105,14876,4361],{"class":115},[105,14878,14879],{"class":122},"arrayLength",[105,14881,11544],{"class":111},[105,14883,14884],{"class":129}," number",[105,14886,11938],{"class":115},[105,14888,4999],{"class":111},[105,14890,10417],{"class":129},[105,14892,14893],{"class":115},"[];\n",[105,14895,14896,14898,14901,14903,14905,14907,14909,14911,14913,14915,14917],{"class":107,"line":133},[105,14897,10264],{"class":111},[105,14899,14900],{"class":115}," \u003C",[105,14902,4990],{"class":241},[105,14904,4993],{"class":115},[105,14906,14879],{"class":122},[105,14908,4999],{"class":111},[105,14910,14884],{"class":129},[105,14912,11938],{"class":115},[105,14914,4999],{"class":111},[105,14916,5057],{"class":241},[105,14918,14893],{"class":115},[105,14920,14921,14923,14925,14927,14929,14931,14934,14936,14938,14940,14942,14944],{"class":107,"line":148},[105,14922,10264],{"class":111},[105,14924,14900],{"class":115},[105,14926,4990],{"class":241},[105,14928,4993],{"class":115},[105,14930,10269],{"class":111},[105,14932,14933],{"class":122},"items",[105,14935,4999],{"class":111},[105,14937,5057],{"class":241},[105,14939,10279],{"class":115},[105,14941,4999],{"class":111},[105,14943,5057],{"class":241},[105,14945,14893],{"class":115},[105,14947,14948,14951,14953,14956,14958,14960,14962,14964,14967,14970,14972],{"class":107,"line":163},[105,14949,14950],{"class":241},"    isArray",[105,14952,321],{"class":115},[105,14954,14955],{"class":122},"arg",[105,14957,4999],{"class":111},[105,14959,10417],{"class":129},[105,14961,11938],{"class":115},[105,14963,4999],{"class":111},[105,14965,14966],{"class":122}," arg",[105,14968,14969],{"class":111}," is",[105,14971,10417],{"class":129},[105,14973,14893],{"class":115},[105,14975,14976,14979,14981,14983,14985,14988,14990,14993,14995,14997,15000,15002,15004],{"class":107,"line":179},[105,14977,14978],{"class":241},"    from",[105,14980,234],{"class":115},[105,14982,4990],{"class":241},[105,14984,4993],{"class":115},[105,14986,14987],{"class":122},"arrayLike",[105,14989,4999],{"class":111},[105,14991,14992],{"class":241}," ArrayLike",[105,14994,234],{"class":115},[105,14996,4990],{"class":241},[105,14998,14999],{"class":115},">)",[105,15001,4999],{"class":111},[105,15003,5057],{"class":241},[105,15005,14893],{"class":115},[105,15007,15008,15011,15013,15015,15017,15019,15021,15023,15025,15027,15029,15031],{"class":107,"line":194},[105,15009,15010],{"class":241},"    of",[105,15012,234],{"class":115},[105,15014,4990],{"class":241},[105,15016,4993],{"class":115},[105,15018,10269],{"class":111},[105,15020,14933],{"class":122},[105,15022,4999],{"class":111},[105,15024,5057],{"class":241},[105,15026,10279],{"class":115},[105,15028,4999],{"class":111},[105,15030,5057],{"class":241},[105,15032,14893],{"class":115},[105,15034,15035],{"class":107,"line":209},[105,15036,15037],{"class":630},"    \u002F\u002F ...\n",[105,15039,15040],{"class":107,"line":526},[105,15041,212],{"class":115},[105,15043,15044],{"class":107,"line":531},[105,15045,307],{"emptyLinePlaceholder":306},[105,15047,15048,15050,15052,15055,15057,15059],{"class":107,"line":668},[105,15049,10334],{"class":111},[105,15051,10337],{"class":111},[105,15053,15054],{"class":115}," Array",[105,15056,4999],{"class":111},[105,15058,14868],{"class":241},[105,15060,145],{"class":115},[18,15062,15063],{},"Array是ArrayConstructor类型的变量，它同时是函数。push在Array.prototype上：",[96,15065,15067],{"className":3241,"code":15066,"language":3243,"meta":101,"style":101},"interface Array\u003CT> {\n    push(...items: T[]): number;\n    pop(): T | undefined;\n    map\u003CU>(cb: (v: T, i: number, arr: T[]) => U): U[];\n    \u002F\u002F ...\n}\n\ninterface ArrayConstructor {\n    \u002F\u002F 静态方法\n}\n\ndeclare var Array: ArrayConstructor;\n",[24,15068,15069,15081,15104,15122,15180,15184,15188,15192,15200,15205,15209,15213],{"__ignoreMap":101},[105,15070,15071,15073,15075,15077,15079],{"class":107,"line":108},[105,15072,3501],{"class":111},[105,15074,15054],{"class":241},[105,15076,234],{"class":115},[105,15078,4990],{"class":241},[105,15080,11844],{"class":115},[105,15082,15083,15086,15088,15090,15092,15094,15096,15098,15100,15102],{"class":107,"line":119},[105,15084,15085],{"class":241},"    push",[105,15087,321],{"class":115},[105,15089,10269],{"class":111},[105,15091,14933],{"class":122},[105,15093,4999],{"class":111},[105,15095,5057],{"class":241},[105,15097,10279],{"class":115},[105,15099,4999],{"class":111},[105,15101,14884],{"class":129},[105,15103,145],{"class":115},[105,15105,15106,15109,15112,15114,15116,15118,15120],{"class":107,"line":133},[105,15107,15108],{"class":241},"    pop",[105,15110,15111],{"class":115},"()",[105,15113,4999],{"class":111},[105,15115,5057],{"class":241},[105,15117,5023],{"class":111},[105,15119,11869],{"class":129},[105,15121,145],{"class":115},[105,15123,15124,15127,15129,15132,15134,15137,15139,15141,15143,15145,15147,15149,15152,15154,15156,15158,15161,15163,15165,15167,15169,15172,15174,15176,15178],{"class":107,"line":148},[105,15125,15126],{"class":241},"    map",[105,15128,234],{"class":115},[105,15130,15131],{"class":241},"U",[105,15133,4993],{"class":115},[105,15135,15136],{"class":241},"cb",[105,15138,4999],{"class":111},[105,15140,4361],{"class":115},[105,15142,12554],{"class":122},[105,15144,4999],{"class":111},[105,15146,5057],{"class":241},[105,15148,286],{"class":115},[105,15150,15151],{"class":122},"i",[105,15153,4999],{"class":111},[105,15155,14884],{"class":129},[105,15157,286],{"class":115},[105,15159,15160],{"class":122},"arr",[105,15162,4999],{"class":111},[105,15164,5057],{"class":241},[105,15166,10445],{"class":115},[105,15168,723],{"class":111},[105,15170,15171],{"class":241}," U",[105,15173,11938],{"class":115},[105,15175,4999],{"class":111},[105,15177,15171],{"class":241},[105,15179,14893],{"class":115},[105,15181,15182],{"class":107,"line":163},[105,15183,15037],{"class":630},[105,15185,15186],{"class":107,"line":179},[105,15187,212],{"class":115},[105,15189,15190],{"class":107,"line":194},[105,15191,307],{"emptyLinePlaceholder":306},[105,15193,15194,15196,15198],{"class":107,"line":209},[105,15195,3501],{"class":111},[105,15197,14868],{"class":241},[105,15199,116],{"class":115},[105,15201,15202],{"class":107,"line":526},[105,15203,15204],{"class":630},"    \u002F\u002F 静态方法\n",[105,15206,15207],{"class":107,"line":531},[105,15208,212],{"class":115},[105,15210,15211],{"class":107,"line":668},[105,15212,307],{"emptyLinePlaceholder":306},[105,15214,15215,15217,15219,15221,15223,15225],{"class":107,"line":673},[105,15216,10334],{"class":111},[105,15218,10337],{"class":111},[105,15220,15054],{"class":115},[105,15222,4999],{"class":111},[105,15224,14868],{"class":241},[105,15226,145],{"class":115},[18,15228,15229,15230,15233,15234,15236,15237,15239],{},"可以看到push是定义在",[24,15231,15232],{},"Array\u003CT>","上的，对应Array.prototype上，所有数组实例共享。",[105,15235],{},"或者new Array()都可以得到Array实例。",[105,15238],{},"是语法糖，或者严格来说是数组字面量，由规范里的ArrayLiteral生产式定义。两者创建的对象原型链完全一样。",[14,15241,15242],{"id":15242},"生成器与迭代器",[18,15244,15245],{},"[[生成器与迭代器]]",[14,15247,15248],{"id":15248},"流式操作",[18,15250,15251,15252,15255,15256,31,15259,31,15262,15265,15266],{},"流式操作远不止“读取大文件”。围绕 ",[21,15253,15254],{},"Web Streams API","（",[24,15257,15258],{},"ReadableStream",[24,15260,15261],{},"WritableStream",[24,15263,15264],{},"TransformStream","），可以构建出一整套",[21,15267,15268],{},"数据处理管道",[18,15270,15271],{},"比如说，读取视频文件，你可以把流接到目标：",[96,15273,15275],{"className":3241,"code":15274,"language":3243,"meta":101,"style":101},"const response = await fetch('\u002Fvideo.mp4');\nconst writable = new WritableStream({\n    write(chunk) {\n        console.log('收到', chunk.length, '字节');\n    }\n});\n\nawait response.body.pipeTo(writable);\n",[24,15276,15277,15296,15312,15324,15347,15351,15355,15359],{"__ignoreMap":101},[105,15278,15279,15281,15283,15285,15287,15289,15291,15294],{"class":107,"line":108},[105,15280,568],{"class":111},[105,15282,6495],{"class":129},[105,15284,574],{"class":111},[105,15286,5410],{"class":111},[105,15288,5982],{"class":241},[105,15290,321],{"class":115},[105,15292,15293],{"class":248},"'\u002Fvideo.mp4'",[105,15295,327],{"class":115},[105,15297,15298,15300,15303,15305,15307,15310],{"class":107,"line":119},[105,15299,568],{"class":111},[105,15301,15302],{"class":129}," writable",[105,15304,574],{"class":111},[105,15306,3478],{"class":111},[105,15308,15309],{"class":241}," WritableStream",[105,15311,3917],{"class":115},[105,15313,15314,15317,15319,15322],{"class":107,"line":133},[105,15315,15316],{"class":241},"    write",[105,15318,321],{"class":115},[105,15320,15321],{"class":122},"chunk",[105,15323,1000],{"class":115},[105,15325,15326,15328,15330,15332,15335,15338,15340,15342,15345],{"class":107,"line":148},[105,15327,10740],{"class":115},[105,15329,639],{"class":241},[105,15331,321],{"class":115},[105,15333,15334],{"class":248},"'收到'",[105,15336,15337],{"class":115},", chunk.",[105,15339,4060],{"class":129},[105,15341,286],{"class":115},[105,15343,15344],{"class":248},"'字节'",[105,15346,327],{"class":115},[105,15348,15349],{"class":107,"line":163},[105,15350,5143],{"class":115},[105,15352,15353],{"class":107,"line":179},[105,15354,3964],{"class":115},[105,15356,15357],{"class":107,"line":194},[105,15358,307],{"emptyLinePlaceholder":306},[105,15360,15361,15363,15366,15369],{"class":107,"line":209},[105,15362,5383],{"class":111},[105,15364,15365],{"class":115}," response.body.",[105,15367,15368],{"class":241},"pipeTo",[105,15370,15371],{"class":115},"(writable);\n",[18,15373,15374],{},"流可以经过转换器，body有个方法叫做pipeThrough，能够把字节转换为字符串，字符串转换为大写：",[96,15376,15378],{"className":3241,"code":15377,"language":3243,"meta":101,"style":101},"const response = await fetch('\u002Fdata.txt');\nconst stream = response.body\n    .pipeThrough(new TextDecoderStream())      \u002F\u002F 字节 → 字符串\n    .pipeThrough(new TransformStream({         \u002F\u002F 字符串 → 大写\n        transform(chunk, controller) {\n            controller.enqueue(chunk.toUpperCase());\n        }\n    }));\n\nfor await (const chunk of stream) {\n    console.log(chunk);\n}\n",[24,15379,15380,15399,15411,15431,15450,15466,15483,15487,15492,15496,15514,15523],{"__ignoreMap":101},[105,15381,15382,15384,15386,15388,15390,15392,15394,15397],{"class":107,"line":108},[105,15383,568],{"class":111},[105,15385,6495],{"class":129},[105,15387,574],{"class":111},[105,15389,5410],{"class":111},[105,15391,5982],{"class":241},[105,15393,321],{"class":115},[105,15395,15396],{"class":248},"'\u002Fdata.txt'",[105,15398,327],{"class":115},[105,15400,15401,15403,15406,15408],{"class":107,"line":119},[105,15402,568],{"class":111},[105,15404,15405],{"class":129}," stream",[105,15407,574],{"class":111},[105,15409,15410],{"class":115}," response.body\n",[105,15412,15413,15416,15419,15421,15423,15426,15428],{"class":107,"line":133},[105,15414,15415],{"class":115},"    .",[105,15417,15418],{"class":241},"pipeThrough",[105,15420,321],{"class":115},[105,15422,7412],{"class":111},[105,15424,15425],{"class":241}," TextDecoderStream",[105,15427,8254],{"class":115},[105,15429,15430],{"class":630},"\u002F\u002F 字节 → 字符串\n",[105,15432,15433,15435,15437,15439,15441,15444,15447],{"class":107,"line":148},[105,15434,15415],{"class":115},[105,15436,15418],{"class":241},[105,15438,321],{"class":115},[105,15440,7412],{"class":111},[105,15442,15443],{"class":241}," TransformStream",[105,15445,15446],{"class":115},"({         ",[105,15448,15449],{"class":630},"\u002F\u002F 字符串 → 大写\n",[105,15451,15452,15455,15457,15459,15461,15464],{"class":107,"line":163},[105,15453,15454],{"class":241},"        transform",[105,15456,321],{"class":115},[105,15458,15321],{"class":122},[105,15460,286],{"class":115},[105,15462,15463],{"class":122},"controller",[105,15465,1000],{"class":115},[105,15467,15468,15471,15474,15477,15480],{"class":107,"line":179},[105,15469,15470],{"class":115},"            controller.",[105,15472,15473],{"class":241},"enqueue",[105,15475,15476],{"class":115},"(chunk.",[105,15478,15479],{"class":241},"toUpperCase",[105,15481,15482],{"class":115},"());\n",[105,15484,15485],{"class":107,"line":194},[105,15486,12475],{"class":115},[105,15488,15489],{"class":107,"line":209},[105,15490,15491],{"class":115},"    }));\n",[105,15493,15494],{"class":107,"line":526},[105,15495,307],{"emptyLinePlaceholder":306},[105,15497,15498,15500,15502,15504,15506,15509,15511],{"class":107,"line":531},[105,15499,9896],{"class":111},[105,15501,5410],{"class":111},[105,15503,4361],{"class":115},[105,15505,568],{"class":111},[105,15507,15508],{"class":129}," chunk",[105,15510,12692],{"class":111},[105,15512,15513],{"class":115}," stream) {\n",[105,15515,15516,15518,15520],{"class":107,"line":668},[105,15517,6662],{"class":115},[105,15519,639],{"class":241},[105,15521,15522],{"class":115},"(chunk);\n",[105,15524,15525],{"class":107,"line":673},[105,15526,212],{"class":115},[18,15528,15529],{},"你甚至可以用tee把流直接一分为2交给两个消费者：",[96,15531,15533],{"className":3241,"code":15532,"language":3243,"meta":101,"style":101},"const response = await fetch('\u002Fdata');\nconst [stream1, stream2] = response.body.tee();\n\n\u002F\u002F 两个消费者同时处理\nstream1.pipeTo(new WritableStream({ write(c) { \u002F* ... *\u002F } }));\nstream2.pipeTo(new WritableStream({ write(c) { \u002F* ... *\u002F } }));\n",[24,15534,15535,15554,15581,15585,15590,15622],{"__ignoreMap":101},[105,15536,15537,15539,15541,15543,15545,15547,15549,15552],{"class":107,"line":108},[105,15538,568],{"class":111},[105,15540,6495],{"class":129},[105,15542,574],{"class":111},[105,15544,5410],{"class":111},[105,15546,5982],{"class":241},[105,15548,321],{"class":115},[105,15550,15551],{"class":248},"'\u002Fdata'",[105,15553,327],{"class":115},[105,15555,15556,15558,15561,15564,15566,15569,15572,15574,15576,15579],{"class":107,"line":119},[105,15557,568],{"class":111},[105,15559,15560],{"class":115}," [",[105,15562,15563],{"class":129},"stream1",[105,15565,286],{"class":115},[105,15567,15568],{"class":129},"stream2",[105,15570,15571],{"class":115},"] ",[105,15573,245],{"class":111},[105,15575,15365],{"class":115},[105,15577,15578],{"class":241},"tee",[105,15580,665],{"class":115},[105,15582,15583],{"class":107,"line":133},[105,15584,307],{"emptyLinePlaceholder":306},[105,15586,15587],{"class":107,"line":148},[105,15588,15589],{"class":630},"\u002F\u002F 两个消费者同时处理\n",[105,15591,15592,15595,15597,15599,15601,15603,15606,15609,15611,15614,15617,15619],{"class":107,"line":163},[105,15593,15594],{"class":115},"stream1.",[105,15596,15368],{"class":241},[105,15598,321],{"class":115},[105,15600,7412],{"class":111},[105,15602,15309],{"class":241},[105,15604,15605],{"class":115},"({ ",[105,15607,15608],{"class":241},"write",[105,15610,321],{"class":115},[105,15612,15613],{"class":122},"c",[105,15615,15616],{"class":115},") { ",[105,15618,729],{"class":630},[105,15620,15621],{"class":115}," } }));\n",[105,15623,15624,15627,15629,15631,15633,15635,15637,15639,15641,15643,15645,15647],{"class":107,"line":179},[105,15625,15626],{"class":115},"stream2.",[105,15628,15368],{"class":241},[105,15630,321],{"class":115},[105,15632,7412],{"class":111},[105,15634,15309],{"class":241},[105,15636,15605],{"class":115},[105,15638,15608],{"class":241},[105,15640,321],{"class":115},[105,15642,15613],{"class":122},[105,15644,15616],{"class":115},[105,15646,729],{"class":630},[105,15648,15621],{"class":115},[1890,15650,15651],{"id":15651},"常用场景",[18,15653,15654],{},"解压操作gz；",[96,15656,15658],{"className":3241,"code":15657,"language":3243,"meta":101,"style":101},"const response = await fetch('\u002Fdata.gz');\nconst stream = response.body\n    .pipeThrough(new DecompressionStream('gzip'));\n\nconst text = await new Response(stream).text();\nconsole.log(text);\n",[24,15659,15660,15679,15689,15709,15713,15736],{"__ignoreMap":101},[105,15661,15662,15664,15666,15668,15670,15672,15674,15677],{"class":107,"line":108},[105,15663,568],{"class":111},[105,15665,6495],{"class":129},[105,15667,574],{"class":111},[105,15669,5410],{"class":111},[105,15671,5982],{"class":241},[105,15673,321],{"class":115},[105,15675,15676],{"class":248},"'\u002Fdata.gz'",[105,15678,327],{"class":115},[105,15680,15681,15683,15685,15687],{"class":107,"line":119},[105,15682,568],{"class":111},[105,15684,15405],{"class":129},[105,15686,574],{"class":111},[105,15688,15410],{"class":115},[105,15690,15691,15693,15695,15697,15699,15702,15704,15707],{"class":107,"line":133},[105,15692,15415],{"class":115},[105,15694,15418],{"class":241},[105,15696,321],{"class":115},[105,15698,7412],{"class":111},[105,15700,15701],{"class":241}," DecompressionStream",[105,15703,321],{"class":115},[105,15705,15706],{"class":248},"'gzip'",[105,15708,13511],{"class":115},[105,15710,15711],{"class":107,"line":148},[105,15712,307],{"emptyLinePlaceholder":306},[105,15714,15715,15717,15720,15722,15724,15726,15729,15732,15734],{"class":107,"line":163},[105,15716,568],{"class":111},[105,15718,15719],{"class":129}," text",[105,15721,574],{"class":111},[105,15723,5410],{"class":111},[105,15725,3478],{"class":111},[105,15727,15728],{"class":241}," Response",[105,15730,15731],{"class":115},"(stream).",[105,15733,392],{"class":241},[105,15735,665],{"class":115},[105,15737,15738,15740,15742],{"class":107,"line":179},[105,15739,636],{"class":115},[105,15741,639],{"class":241},[105,15743,15744],{"class":115},"(text);\n",[18,15746,15747],{},"压缩为gzip：",[96,15749,15751],{"className":3241,"code":15750,"language":3243,"meta":101,"style":101},"const stream = new Blob(['hello world'])\n    .stream()\n    .pipeThrough(new CompressionStream('gzip'));\n\nconst compressed = await new Response(stream).arrayBuffer();\n",[24,15752,15753,15774,15783,15802,15806],{"__ignoreMap":101},[105,15754,15755,15757,15759,15761,15763,15766,15768,15771],{"class":107,"line":108},[105,15756,568],{"class":111},[105,15758,15405],{"class":129},[105,15760,574],{"class":111},[105,15762,3478],{"class":111},[105,15764,15765],{"class":241}," Blob",[105,15767,6454],{"class":115},[105,15769,15770],{"class":248},"'hello world'",[105,15772,15773],{"class":115},"])\n",[105,15775,15776,15778,15781],{"class":107,"line":119},[105,15777,15415],{"class":115},[105,15779,15780],{"class":241},"stream",[105,15782,5174],{"class":115},[105,15784,15785,15787,15789,15791,15793,15796,15798,15800],{"class":107,"line":133},[105,15786,15415],{"class":115},[105,15788,15418],{"class":241},[105,15790,321],{"class":115},[105,15792,7412],{"class":111},[105,15794,15795],{"class":241}," CompressionStream",[105,15797,321],{"class":115},[105,15799,15706],{"class":248},[105,15801,13511],{"class":115},[105,15803,15804],{"class":107,"line":148},[105,15805,307],{"emptyLinePlaceholder":306},[105,15807,15808,15810,15813,15815,15817,15819,15821,15823,15826],{"class":107,"line":163},[105,15809,568],{"class":111},[105,15811,15812],{"class":129}," compressed",[105,15814,574],{"class":111},[105,15816,5410],{"class":111},[105,15818,3478],{"class":111},[105,15820,15728],{"class":241},[105,15822,15731],{"class":115},[105,15824,15825],{"class":241},"arrayBuffer",[105,15827,665],{"class":115},[18,15829,15830],{},"文本接编码；",[96,15832,15834],{"className":3241,"code":15833,"language":3243,"meta":101,"style":101},"const response = await fetch('\u002Fdata.txt');\nconst stream = response.body\n    .pipeThrough(new TextDecoderStream());\n\nfor await (const chunk of stream) {\n    console.log(chunk);  \u002F\u002F 字符串\n}\n\nconst stream = new ReadableStream({\n    start(controller) {\n        controller.enqueue('hello');\n        controller.enqueue('world');\n        controller.close();\n    }\n}).pipeThrough(new TextEncoderStream());\n",[24,15835,15836,15854,15864,15878,15882,15898,15910,15914,15918,15933,15944,15957,15970,15978,15982],{"__ignoreMap":101},[105,15837,15838,15840,15842,15844,15846,15848,15850,15852],{"class":107,"line":108},[105,15839,568],{"class":111},[105,15841,6495],{"class":129},[105,15843,574],{"class":111},[105,15845,5410],{"class":111},[105,15847,5982],{"class":241},[105,15849,321],{"class":115},[105,15851,15396],{"class":248},[105,15853,327],{"class":115},[105,15855,15856,15858,15860,15862],{"class":107,"line":119},[105,15857,568],{"class":111},[105,15859,15405],{"class":129},[105,15861,574],{"class":111},[105,15863,15410],{"class":115},[105,15865,15866,15868,15870,15872,15874,15876],{"class":107,"line":133},[105,15867,15415],{"class":115},[105,15869,15418],{"class":241},[105,15871,321],{"class":115},[105,15873,7412],{"class":111},[105,15875,15425],{"class":241},[105,15877,15482],{"class":115},[105,15879,15880],{"class":107,"line":148},[105,15881,307],{"emptyLinePlaceholder":306},[105,15883,15884,15886,15888,15890,15892,15894,15896],{"class":107,"line":163},[105,15885,9896],{"class":111},[105,15887,5410],{"class":111},[105,15889,4361],{"class":115},[105,15891,568],{"class":111},[105,15893,15508],{"class":129},[105,15895,12692],{"class":111},[105,15897,15513],{"class":115},[105,15899,15900,15902,15904,15907],{"class":107,"line":179},[105,15901,6662],{"class":115},[105,15903,639],{"class":241},[105,15905,15906],{"class":115},"(chunk);  ",[105,15908,15909],{"class":630},"\u002F\u002F 字符串\n",[105,15911,15912],{"class":107,"line":194},[105,15913,212],{"class":115},[105,15915,15916],{"class":107,"line":209},[105,15917,307],{"emptyLinePlaceholder":306},[105,15919,15920,15922,15924,15926,15928,15931],{"class":107,"line":526},[105,15921,568],{"class":111},[105,15923,15405],{"class":129},[105,15925,574],{"class":111},[105,15927,3478],{"class":111},[105,15929,15930],{"class":241}," ReadableStream",[105,15932,3917],{"class":115},[105,15934,15935,15938,15940,15942],{"class":107,"line":531},[105,15936,15937],{"class":241},"    start",[105,15939,321],{"class":115},[105,15941,15463],{"class":122},[105,15943,1000],{"class":115},[105,15945,15946,15949,15951,15953,15955],{"class":107,"line":668},[105,15947,15948],{"class":115},"        controller.",[105,15950,15473],{"class":241},[105,15952,321],{"class":115},[105,15954,3262],{"class":248},[105,15956,327],{"class":115},[105,15958,15959,15961,15963,15965,15968],{"class":107,"line":673},[105,15960,15948],{"class":115},[105,15962,15473],{"class":241},[105,15964,321],{"class":115},[105,15966,15967],{"class":248},"'world'",[105,15969,327],{"class":115},[105,15971,15972,15974,15976],{"class":107,"line":679},[105,15973,15948],{"class":115},[105,15975,5171],{"class":241},[105,15977,665],{"class":115},[105,15979,15980],{"class":107,"line":689},[105,15981,5143],{"class":115},[105,15983,15984,15987,15989,15991,15993,15996],{"class":107,"line":699},[105,15985,15986],{"class":115},"}).",[105,15988,15418],{"class":241},[105,15990,321],{"class":115},[105,15992,7412],{"class":111},[105,15994,15995],{"class":241}," TextEncoderStream",[105,15997,15482],{"class":115},[1890,15999,16000],{"id":16000},"流式数据格式解析",[18,16002,16003],{},"NDJSON，每行有一个json:",[96,16005,16007],{"className":3241,"code":16006,"language":3243,"meta":101,"style":101},"async function* parseNDJSON(stream) {\n    const reader = stream\n        .pipeThrough(new TextDecoderStream())\n        .getReader();\n    let buffer = '';\n\n    while (true) {\n        const { done, value } = await reader.read();\n        if (done) break;\n        buffer += value;\n        const lines = buffer.split('\\n');\n        buffer = lines.pop();\n        for (const line of lines) {\n            if (line.trim()) yield JSON.parse(line);\n        }\n    }\n}\n\nconst response = await fetch('\u002Flogs.ndjson');\nfor await (const obj of parseNDJSON(response.body)) {\n    console.log(obj);\n}\n",[24,16008,16009,16025,16037,16052,16061,16075,16079,16089,16116,16128,16138,16165,16179,16195,16222,16226,16230,16234,16238,16257,16276,16284],{"__ignoreMap":101},[105,16010,16011,16013,16016,16019,16021,16023],{"class":107,"line":108},[105,16012,9221],{"class":111},[105,16014,16015],{"class":111}," function*",[105,16017,16018],{"class":241}," parseNDJSON",[105,16020,321],{"class":115},[105,16022,15780],{"class":122},[105,16024,1000],{"class":115},[105,16026,16027,16029,16032,16034],{"class":107,"line":119},[105,16028,7220],{"class":111},[105,16030,16031],{"class":129}," reader",[105,16033,574],{"class":111},[105,16035,16036],{"class":115}," stream\n",[105,16038,16039,16041,16043,16045,16047,16049],{"class":107,"line":133},[105,16040,5509],{"class":115},[105,16042,15418],{"class":241},[105,16044,321],{"class":115},[105,16046,7412],{"class":111},[105,16048,15425],{"class":241},[105,16050,16051],{"class":115},"())\n",[105,16053,16054,16056,16059],{"class":107,"line":148},[105,16055,5509],{"class":115},[105,16057,16058],{"class":241},"getReader",[105,16060,665],{"class":115},[105,16062,16063,16065,16068,16070,16073],{"class":107,"line":163},[105,16064,14314],{"class":111},[105,16066,16067],{"class":115}," buffer ",[105,16069,245],{"class":111},[105,16071,16072],{"class":248}," ''",[105,16074,145],{"class":115},[105,16076,16077],{"class":107,"line":179},[105,16078,307],{"emptyLinePlaceholder":306},[105,16080,16081,16083,16085,16087],{"class":107,"line":194},[105,16082,14330],{"class":111},[105,16084,4361],{"class":115},[105,16086,9140],{"class":129},[105,16088,1000],{"class":115},[105,16090,16091,16093,16095,16098,16100,16102,16104,16106,16108,16111,16114],{"class":107,"line":209},[105,16092,7266],{"class":111},[105,16094,726],{"class":115},[105,16096,16097],{"class":129},"done",[105,16099,286],{"class":115},[105,16101,8433],{"class":129},[105,16103,3955],{"class":115},[105,16105,245],{"class":111},[105,16107,5410],{"class":111},[105,16109,16110],{"class":115}," reader.",[105,16112,16113],{"class":241},"read",[105,16115,665],{"class":115},[105,16117,16118,16120,16123,16126],{"class":107,"line":526},[105,16119,12370],{"class":111},[105,16121,16122],{"class":115}," (done) ",[105,16124,16125],{"class":111},"break",[105,16127,145],{"class":115},[105,16129,16130,16133,16136],{"class":107,"line":531},[105,16131,16132],{"class":115},"        buffer ",[105,16134,16135],{"class":111},"+=",[105,16137,12637],{"class":115},[105,16139,16140,16142,16145,16147,16150,16153,16155,16158,16161,16163],{"class":107,"line":668},[105,16141,7266],{"class":111},[105,16143,16144],{"class":129}," lines",[105,16146,574],{"class":111},[105,16148,16149],{"class":115}," buffer.",[105,16151,16152],{"class":241},"split",[105,16154,321],{"class":115},[105,16156,16157],{"class":248},"'",[105,16159,16160],{"class":129},"\\n",[105,16162,16157],{"class":248},[105,16164,327],{"class":115},[105,16166,16167,16169,16171,16174,16177],{"class":107,"line":673},[105,16168,16132],{"class":115},[105,16170,245],{"class":111},[105,16172,16173],{"class":115}," lines.",[105,16175,16176],{"class":241},"pop",[105,16178,665],{"class":115},[105,16180,16181,16183,16185,16187,16190,16192],{"class":107,"line":679},[105,16182,12682],{"class":111},[105,16184,4361],{"class":115},[105,16186,568],{"class":111},[105,16188,16189],{"class":129}," line",[105,16191,12692],{"class":111},[105,16193,16194],{"class":115}," lines) {\n",[105,16196,16197,16199,16202,16205,16208,16211,16214,16216,16219],{"class":107,"line":689},[105,16198,5962],{"class":111},[105,16200,16201],{"class":115}," (line.",[105,16203,16204],{"class":241},"trim",[105,16206,16207],{"class":115},"()) ",[105,16209,16210],{"class":111},"yield",[105,16212,16213],{"class":129}," JSON",[105,16215,5363],{"class":115},[105,16217,16218],{"class":241},"parse",[105,16220,16221],{"class":115},"(line);\n",[105,16223,16224],{"class":107,"line":699},[105,16225,12475],{"class":115},[105,16227,16228],{"class":107,"line":704},[105,16229,5143],{"class":115},[105,16231,16232],{"class":107,"line":710},[105,16233,212],{"class":115},[105,16235,16236],{"class":107,"line":1136},[105,16237,307],{"emptyLinePlaceholder":306},[105,16239,16240,16242,16244,16246,16248,16250,16252,16255],{"class":107,"line":1149},[105,16241,568],{"class":111},[105,16243,6495],{"class":129},[105,16245,574],{"class":111},[105,16247,5410],{"class":111},[105,16249,5982],{"class":241},[105,16251,321],{"class":115},[105,16253,16254],{"class":248},"'\u002Flogs.ndjson'",[105,16256,327],{"class":115},[105,16258,16259,16261,16263,16265,16267,16269,16271,16273],{"class":107,"line":3837},[105,16260,9896],{"class":111},[105,16262,5410],{"class":111},[105,16264,4361],{"class":115},[105,16266,568],{"class":111},[105,16268,10188],{"class":129},[105,16270,12692],{"class":111},[105,16272,16018],{"class":241},[105,16274,16275],{"class":115},"(response.body)) {\n",[105,16277,16278,16280,16282],{"class":107,"line":3847},[105,16279,6662],{"class":115},[105,16281,639],{"class":241},[105,16283,10782],{"class":115},[105,16285,16286],{"class":107,"line":3857},[105,16287,212],{"class":115},[18,16289,16290,16291,1980,16294,16297,16298,16301],{},"SSE数据处理，sse服务器可以发送默认消息和自定义事件，消息没有event字段，浏览器默认按照message事件处理，客户端用",[24,16292,16293],{},"es.onmessage",[24,16295,16296],{},"es.addEventListener('message', ...)"," 接收。自定义事件有event字段，指定事件类型，- 客户端用 ",[24,16299,16300],{},"es.addEventListener('eventName', ...)"," 接收。",[96,16303,16305],{"className":3241,"code":16304,"language":3243,"meta":101,"style":101},"async function* parseSSE(stream) {\n    const reader = stream\n        .pipeThrough(new TextDecoderStream())\n        .getReader();\n    let buffer = '';\n\n    while (true) {\n        const { done, value } = await reader.read();\n        if (done) break;\n        buffer += value;\n        const events = buffer.split('\\n\\n');\n        buffer = events.pop();\n        for (const event of events) {\n            const data = event\n                .split('\\n')\n                .filter(l => l.startsWith('data:'))\n                .map(l => l.slice(5).trim())\n                .join('\\n');\n            yield data;\n        }\n    }\n}\n",[24,16306,16307,16322,16332,16346,16354,16366,16370,16380,16404,16414,16422,16446,16459,16475,16486,16503,16530,16556,16573,16581,16585,16589],{"__ignoreMap":101},[105,16308,16309,16311,16313,16316,16318,16320],{"class":107,"line":108},[105,16310,9221],{"class":111},[105,16312,16015],{"class":111},[105,16314,16315],{"class":241}," parseSSE",[105,16317,321],{"class":115},[105,16319,15780],{"class":122},[105,16321,1000],{"class":115},[105,16323,16324,16326,16328,16330],{"class":107,"line":119},[105,16325,7220],{"class":111},[105,16327,16031],{"class":129},[105,16329,574],{"class":111},[105,16331,16036],{"class":115},[105,16333,16334,16336,16338,16340,16342,16344],{"class":107,"line":133},[105,16335,5509],{"class":115},[105,16337,15418],{"class":241},[105,16339,321],{"class":115},[105,16341,7412],{"class":111},[105,16343,15425],{"class":241},[105,16345,16051],{"class":115},[105,16347,16348,16350,16352],{"class":107,"line":148},[105,16349,5509],{"class":115},[105,16351,16058],{"class":241},[105,16353,665],{"class":115},[105,16355,16356,16358,16360,16362,16364],{"class":107,"line":163},[105,16357,14314],{"class":111},[105,16359,16067],{"class":115},[105,16361,245],{"class":111},[105,16363,16072],{"class":248},[105,16365,145],{"class":115},[105,16367,16368],{"class":107,"line":179},[105,16369,307],{"emptyLinePlaceholder":306},[105,16371,16372,16374,16376,16378],{"class":107,"line":194},[105,16373,14330],{"class":111},[105,16375,4361],{"class":115},[105,16377,9140],{"class":129},[105,16379,1000],{"class":115},[105,16381,16382,16384,16386,16388,16390,16392,16394,16396,16398,16400,16402],{"class":107,"line":209},[105,16383,7266],{"class":111},[105,16385,726],{"class":115},[105,16387,16097],{"class":129},[105,16389,286],{"class":115},[105,16391,8433],{"class":129},[105,16393,3955],{"class":115},[105,16395,245],{"class":111},[105,16397,5410],{"class":111},[105,16399,16110],{"class":115},[105,16401,16113],{"class":241},[105,16403,665],{"class":115},[105,16405,16406,16408,16410,16412],{"class":107,"line":526},[105,16407,12370],{"class":111},[105,16409,16122],{"class":115},[105,16411,16125],{"class":111},[105,16413,145],{"class":115},[105,16415,16416,16418,16420],{"class":107,"line":531},[105,16417,16132],{"class":115},[105,16419,16135],{"class":111},[105,16421,12637],{"class":115},[105,16423,16424,16426,16429,16431,16433,16435,16437,16439,16442,16444],{"class":107,"line":668},[105,16425,7266],{"class":111},[105,16427,16428],{"class":129}," events",[105,16430,574],{"class":111},[105,16432,16149],{"class":115},[105,16434,16152],{"class":241},[105,16436,321],{"class":115},[105,16438,16157],{"class":248},[105,16440,16441],{"class":129},"\\n\\n",[105,16443,16157],{"class":248},[105,16445,327],{"class":115},[105,16447,16448,16450,16452,16455,16457],{"class":107,"line":673},[105,16449,16132],{"class":115},[105,16451,245],{"class":111},[105,16453,16454],{"class":115}," events.",[105,16456,16176],{"class":241},[105,16458,665],{"class":115},[105,16460,16461,16463,16465,16467,16470,16472],{"class":107,"line":679},[105,16462,12682],{"class":111},[105,16464,4361],{"class":115},[105,16466,568],{"class":111},[105,16468,16469],{"class":129}," event",[105,16471,12692],{"class":111},[105,16473,16474],{"class":115}," events) {\n",[105,16476,16477,16479,16481,16483],{"class":107,"line":689},[105,16478,6289],{"class":111},[105,16480,9174],{"class":129},[105,16482,574],{"class":111},[105,16484,16485],{"class":115}," event\n",[105,16487,16488,16491,16493,16495,16497,16499,16501],{"class":107,"line":699},[105,16489,16490],{"class":115},"                .",[105,16492,16152],{"class":241},[105,16494,321],{"class":115},[105,16496,16157],{"class":248},[105,16498,16160],{"class":129},[105,16500,16157],{"class":248},[105,16502,1511],{"class":115},[105,16504,16505,16507,16509,16511,16514,16516,16519,16522,16524,16527],{"class":107,"line":704},[105,16506,16490],{"class":115},[105,16508,5777],{"class":241},[105,16510,321],{"class":115},[105,16512,16513],{"class":122},"l",[105,16515,5132],{"class":111},[105,16517,16518],{"class":115}," l.",[105,16520,16521],{"class":241},"startsWith",[105,16523,321],{"class":115},[105,16525,16526],{"class":248},"'data:'",[105,16528,16529],{"class":115},"))\n",[105,16531,16532,16534,16536,16538,16540,16542,16544,16546,16548,16550,16552,16554],{"class":107,"line":710},[105,16533,16490],{"class":115},[105,16535,5802],{"class":241},[105,16537,321],{"class":115},[105,16539,16513],{"class":122},[105,16541,5132],{"class":111},[105,16543,16518],{"class":115},[105,16545,4273],{"class":241},[105,16547,321],{"class":115},[105,16549,928],{"class":129},[105,16551,5624],{"class":115},[105,16553,16204],{"class":241},[105,16555,16051],{"class":115},[105,16557,16558,16560,16563,16565,16567,16569,16571],{"class":107,"line":1136},[105,16559,16490],{"class":115},[105,16561,16562],{"class":241},"join",[105,16564,321],{"class":115},[105,16566,16157],{"class":248},[105,16568,16160],{"class":129},[105,16570,16157],{"class":248},[105,16572,327],{"class":115},[105,16574,16575,16578],{"class":107,"line":1149},[105,16576,16577],{"class":111},"            yield",[105,16579,16580],{"class":115}," data;\n",[105,16582,16583],{"class":107,"line":3837},[105,16584,12475],{"class":115},[105,16586,16587],{"class":107,"line":3847},[105,16588,5143],{"class":115},[105,16590,16591],{"class":107,"line":3857},[105,16592,212],{"class":115},[18,16594,16595],{},"CSV流式解析，CSV的结构如下：",[96,16597,16601],{"className":16598,"code":16599,"language":16600,"meta":101,"style":101},"language-csv shiki shiki-themes github-light github-dark","name,age,city\nAlice,30,Beijing\nBob,25,Shanghai\nCharlie,35,Guangzhou\n...\n","csv",[24,16602,16603,16608,16613,16618,16623],{"__ignoreMap":101},[105,16604,16605],{"class":107,"line":108},[105,16606,16607],{},"name,age,city\n",[105,16609,16610],{"class":107,"line":119},[105,16611,16612],{},"Alice,30,Beijing\n",[105,16614,16615],{"class":107,"line":133},[105,16616,16617],{},"Bob,25,Shanghai\n",[105,16619,16620],{"class":107,"line":148},[105,16621,16622],{},"Charlie,35,Guangzhou\n",[105,16624,16625],{"class":107,"line":163},[105,16626,7975],{},[18,16628,16629,16630,16633],{},"每条记录以\\n或者\\r\\n结束，列分隔用,分隔，不像json有嵌套结构，不需要等整个文档解析完。无全局头，第一行是列名，之后每行独立。因此我们需要从ReadableStream或者文件读取一块数据，用TextDecorder按UTF-8解码，按照\\n切分，最后一行可能不完整，留在缓冲区。把每行按 ",[24,16631,16632],{},","," 切分成字段，每行独立处理，不需要等下一行。",[96,16635,16637],{"className":3241,"code":16636,"language":3243,"meta":101,"style":101},"async function* parseCSV(stream) {\n    const reader = stream\n        .pipeThrough(new TextDecoderStream())\n        .getReader();\n    let buffer = '';\n    let headers = null;\n\n    while (true) {\n        const { done, value } = await reader.read();\n        if (done) break;\n        buffer += value;\n        const lines = buffer.split('\\n');\n        buffer = lines.pop();\n\n        for (const line of lines) {\n            const cells = line.split(',');\n            if (!headers) {\n                headers = cells;\n            } else {\n                yield Object.fromEntries(\n                    headers.map((h, i) => [h, cells[i]])\n                );\n            }\n        }\n    }\n}\n\n\u002F\u002F 使用\nconst response = await fetch('\u002Fdata.csv');\nfor await (const row of parseCSV(response.body)) {\n    console.log(row);  \u002F\u002F { name: 'Alice', age: '30', city: 'Beijing' }\n}\n",[24,16638,16639,16654,16664,16678,16686,16698,16711,16715,16725,16749,16759,16767,16789,16801,16805,16819,16840,16852,16862,16870,16882,16904,16908,16912,16916,16920,16924,16928,16933,16952,16971,16983],{"__ignoreMap":101},[105,16640,16641,16643,16645,16648,16650,16652],{"class":107,"line":108},[105,16642,9221],{"class":111},[105,16644,16015],{"class":111},[105,16646,16647],{"class":241}," parseCSV",[105,16649,321],{"class":115},[105,16651,15780],{"class":122},[105,16653,1000],{"class":115},[105,16655,16656,16658,16660,16662],{"class":107,"line":119},[105,16657,7220],{"class":111},[105,16659,16031],{"class":129},[105,16661,574],{"class":111},[105,16663,16036],{"class":115},[105,16665,16666,16668,16670,16672,16674,16676],{"class":107,"line":133},[105,16667,5509],{"class":115},[105,16669,15418],{"class":241},[105,16671,321],{"class":115},[105,16673,7412],{"class":111},[105,16675,15425],{"class":241},[105,16677,16051],{"class":115},[105,16679,16680,16682,16684],{"class":107,"line":148},[105,16681,5509],{"class":115},[105,16683,16058],{"class":241},[105,16685,665],{"class":115},[105,16687,16688,16690,16692,16694,16696],{"class":107,"line":163},[105,16689,14314],{"class":111},[105,16691,16067],{"class":115},[105,16693,245],{"class":111},[105,16695,16072],{"class":248},[105,16697,145],{"class":115},[105,16699,16700,16702,16705,16707,16709],{"class":107,"line":179},[105,16701,14314],{"class":111},[105,16703,16704],{"class":115}," headers ",[105,16706,245],{"class":111},[105,16708,5026],{"class":129},[105,16710,145],{"class":115},[105,16712,16713],{"class":107,"line":194},[105,16714,307],{"emptyLinePlaceholder":306},[105,16716,16717,16719,16721,16723],{"class":107,"line":209},[105,16718,14330],{"class":111},[105,16720,4361],{"class":115},[105,16722,9140],{"class":129},[105,16724,1000],{"class":115},[105,16726,16727,16729,16731,16733,16735,16737,16739,16741,16743,16745,16747],{"class":107,"line":526},[105,16728,7266],{"class":111},[105,16730,726],{"class":115},[105,16732,16097],{"class":129},[105,16734,286],{"class":115},[105,16736,8433],{"class":129},[105,16738,3955],{"class":115},[105,16740,245],{"class":111},[105,16742,5410],{"class":111},[105,16744,16110],{"class":115},[105,16746,16113],{"class":241},[105,16748,665],{"class":115},[105,16750,16751,16753,16755,16757],{"class":107,"line":531},[105,16752,12370],{"class":111},[105,16754,16122],{"class":115},[105,16756,16125],{"class":111},[105,16758,145],{"class":115},[105,16760,16761,16763,16765],{"class":107,"line":668},[105,16762,16132],{"class":115},[105,16764,16135],{"class":111},[105,16766,12637],{"class":115},[105,16768,16769,16771,16773,16775,16777,16779,16781,16783,16785,16787],{"class":107,"line":673},[105,16770,7266],{"class":111},[105,16772,16144],{"class":129},[105,16774,574],{"class":111},[105,16776,16149],{"class":115},[105,16778,16152],{"class":241},[105,16780,321],{"class":115},[105,16782,16157],{"class":248},[105,16784,16160],{"class":129},[105,16786,16157],{"class":248},[105,16788,327],{"class":115},[105,16790,16791,16793,16795,16797,16799],{"class":107,"line":679},[105,16792,16132],{"class":115},[105,16794,245],{"class":111},[105,16796,16173],{"class":115},[105,16798,16176],{"class":241},[105,16800,665],{"class":115},[105,16802,16803],{"class":107,"line":689},[105,16804,307],{"emptyLinePlaceholder":306},[105,16806,16807,16809,16811,16813,16815,16817],{"class":107,"line":699},[105,16808,12682],{"class":111},[105,16810,4361],{"class":115},[105,16812,568],{"class":111},[105,16814,16189],{"class":129},[105,16816,12692],{"class":111},[105,16818,16194],{"class":115},[105,16820,16821,16823,16826,16828,16831,16833,16835,16838],{"class":107,"line":704},[105,16822,6289],{"class":111},[105,16824,16825],{"class":129}," cells",[105,16827,574],{"class":111},[105,16829,16830],{"class":115}," line.",[105,16832,16152],{"class":241},[105,16834,321],{"class":115},[105,16836,16837],{"class":248},"','",[105,16839,327],{"class":115},[105,16841,16842,16844,16846,16849],{"class":107,"line":710},[105,16843,5962],{"class":111},[105,16845,4361],{"class":115},[105,16847,16848],{"class":111},"!",[105,16850,16851],{"class":115},"headers) {\n",[105,16853,16854,16857,16859],{"class":107,"line":1136},[105,16855,16856],{"class":115},"                headers ",[105,16858,245],{"class":111},[105,16860,16861],{"class":115}," cells;\n",[105,16863,16864,16866,16868],{"class":107,"line":1149},[105,16865,12448],{"class":115},[105,16867,13174],{"class":111},[105,16869,116],{"class":115},[105,16871,16872,16875,16877,16880],{"class":107,"line":3837},[105,16873,16874],{"class":111},"                yield",[105,16876,11336],{"class":115},[105,16878,16879],{"class":241},"fromEntries",[105,16881,583],{"class":115},[105,16883,16884,16887,16889,16891,16893,16895,16897,16899,16901],{"class":107,"line":3847},[105,16885,16886],{"class":115},"                    headers.",[105,16888,5802],{"class":241},[105,16890,7280],{"class":115},[105,16892,5129],{"class":122},[105,16894,286],{"class":115},[105,16896,15151],{"class":122},[105,16898,2836],{"class":115},[105,16900,723],{"class":111},[105,16902,16903],{"class":115}," [h, cells[i]])\n",[105,16905,16906],{"class":107,"line":3857},[105,16907,12443],{"class":115},[105,16909,16910],{"class":107,"line":3866},[105,16911,12470],{"class":115},[105,16913,16914],{"class":107,"line":3871},[105,16915,12475],{"class":115},[105,16917,16918],{"class":107,"line":3882},[105,16919,5143],{"class":115},[105,16921,16922],{"class":107,"line":3899},[105,16923,212],{"class":115},[105,16925,16926],{"class":107,"line":3904},[105,16927,307],{"emptyLinePlaceholder":306},[105,16929,16930],{"class":107,"line":3920},[105,16931,16932],{"class":630},"\u002F\u002F 使用\n",[105,16934,16935,16937,16939,16941,16943,16945,16947,16950],{"class":107,"line":3936},[105,16936,568],{"class":111},[105,16938,6495],{"class":129},[105,16940,574],{"class":111},[105,16942,5410],{"class":111},[105,16944,5982],{"class":241},[105,16946,321],{"class":115},[105,16948,16949],{"class":248},"'\u002Fdata.csv'",[105,16951,327],{"class":115},[105,16953,16954,16956,16958,16960,16962,16965,16967,16969],{"class":107,"line":3947},[105,16955,9896],{"class":111},[105,16957,5410],{"class":111},[105,16959,4361],{"class":115},[105,16961,568],{"class":111},[105,16963,16964],{"class":129}," row",[105,16966,12692],{"class":111},[105,16968,16647],{"class":241},[105,16970,16275],{"class":115},[105,16972,16973,16975,16977,16980],{"class":107,"line":3961},[105,16974,6662],{"class":115},[105,16976,639],{"class":241},[105,16978,16979],{"class":115},"(row);  ",[105,16981,16982],{"class":630},"\u002F\u002F { name: 'Alice', age: '30', city: 'Beijing' }\n",[105,16984,16985],{"class":107,"line":3967},[105,16986,212],{"class":115},[18,16988,16989],{},"SSR流式HTML渲染，如果这个放在web worker之后就需要使用postMessage把HTML片段传回主线程，由主线程来插入DOM，这是因为Worker不能操作DOM，因为DOM不是线程安全的，多线程操作DOM会导致状态不一致，worker是独立环境，也没有DOM api",[18,16991,16992],{},"这个有点像之前微软设计STA模型的时候同样的根本性考虑，为了保护一个非线程安全的共享资源，比如限制它只能被特定的线程访问。DOM和COM组件都不是线程安全的。",[18,16994,16995],{},"它们的解决方案也很相似，两者都采用了代理proxy+消息传递的模式来安全地转发调用，COM STA中，来自其他线程的调用视图访问STA对象时候，COM运行时不会直接调用，而是通过一个代理将调用编组成一条窗口消息，投递到对象所属STA线程的消息队列里，对象线程通过GetMessage\u002FDispatchMessage取出消息并处理这个消息。",[18,16997,16998],{},"Worker也一样，他必须通过postMessage将数据序列化后发送给主线程，主线程收到了消息之后再自己的执行环境下执行真正的DOM操作。",[96,17000,17002],{"className":3241,"code":17001,"language":3243,"meta":101,"style":101},"const response = await fetch('\u002Fpage');\n\nconst stream = response.body\n    .pipeThrough(new TextDecoderStream())\n    .pipeThrough(new TransformStream({\n        transform(chunk, controller) {\n            \u002F\u002F 在 HTML 到达时逐块插入 DOM\n            document.getElementById('app').insertAdjacentHTML('beforeend', chunk);\n            controller.enqueue(chunk);\n        }\n    }));\n\nawait new Response(stream).text();\n",[24,17003,17004,17023,17027,17037,17051,17065,17079,17084,17110,17118,17122,17126,17130],{"__ignoreMap":101},[105,17005,17006,17008,17010,17012,17014,17016,17018,17021],{"class":107,"line":108},[105,17007,568],{"class":111},[105,17009,6495],{"class":129},[105,17011,574],{"class":111},[105,17013,5410],{"class":111},[105,17015,5982],{"class":241},[105,17017,321],{"class":115},[105,17019,17020],{"class":248},"'\u002Fpage'",[105,17022,327],{"class":115},[105,17024,17025],{"class":107,"line":119},[105,17026,307],{"emptyLinePlaceholder":306},[105,17028,17029,17031,17033,17035],{"class":107,"line":133},[105,17030,568],{"class":111},[105,17032,15405],{"class":129},[105,17034,574],{"class":111},[105,17036,15410],{"class":115},[105,17038,17039,17041,17043,17045,17047,17049],{"class":107,"line":148},[105,17040,15415],{"class":115},[105,17042,15418],{"class":241},[105,17044,321],{"class":115},[105,17046,7412],{"class":111},[105,17048,15425],{"class":241},[105,17050,16051],{"class":115},[105,17052,17053,17055,17057,17059,17061,17063],{"class":107,"line":163},[105,17054,15415],{"class":115},[105,17056,15418],{"class":241},[105,17058,321],{"class":115},[105,17060,7412],{"class":111},[105,17062,15443],{"class":241},[105,17064,3917],{"class":115},[105,17066,17067,17069,17071,17073,17075,17077],{"class":107,"line":179},[105,17068,15454],{"class":241},[105,17070,321],{"class":115},[105,17072,15321],{"class":122},[105,17074,286],{"class":115},[105,17076,15463],{"class":122},[105,17078,1000],{"class":115},[105,17080,17081],{"class":107,"line":194},[105,17082,17083],{"class":630},"            \u002F\u002F 在 HTML 到达时逐块插入 DOM\n",[105,17085,17086,17089,17092,17094,17097,17099,17102,17104,17107],{"class":107,"line":209},[105,17087,17088],{"class":115},"            document.",[105,17090,17091],{"class":241},"getElementById",[105,17093,321],{"class":115},[105,17095,17096],{"class":248},"'app'",[105,17098,5624],{"class":115},[105,17100,17101],{"class":241},"insertAdjacentHTML",[105,17103,321],{"class":115},[105,17105,17106],{"class":248},"'beforeend'",[105,17108,17109],{"class":115},", chunk);\n",[105,17111,17112,17114,17116],{"class":107,"line":526},[105,17113,15470],{"class":115},[105,17115,15473],{"class":241},[105,17117,15522],{"class":115},[105,17119,17120],{"class":107,"line":531},[105,17121,12475],{"class":115},[105,17123,17124],{"class":107,"line":668},[105,17125,15491],{"class":115},[105,17127,17128],{"class":107,"line":673},[105,17129,307],{"emptyLinePlaceholder":306},[105,17131,17132,17134,17136,17138,17140,17142],{"class":107,"line":679},[105,17133,5383],{"class":111},[105,17135,3478],{"class":111},[105,17137,15728],{"class":241},[105,17139,15731],{"class":115},[105,17141,392],{"class":241},[105,17143,665],{"class":115},[18,17145,17146],{},"流式Markdown:",[96,17148,17150],{"className":3241,"code":17149,"language":3243,"meta":101,"style":101},"const response = await fetch('\u002Farticle.md');\nconst stream = response.body\n    .pipeThrough(new TextDecoderStream())\n    .pipeThrough(new TransformStream({\n        transform(chunk, controller) {\n            const html = markdownToHTML(chunk);\n            document.getElementById('content').insertAdjacentHTML('beforeend', html);\n            controller.enqueue(chunk);\n        }\n    }));\n",[24,17151,17152,17171,17181,17195,17209,17223,17237,17259,17267,17271],{"__ignoreMap":101},[105,17153,17154,17156,17158,17160,17162,17164,17166,17169],{"class":107,"line":108},[105,17155,568],{"class":111},[105,17157,6495],{"class":129},[105,17159,574],{"class":111},[105,17161,5410],{"class":111},[105,17163,5982],{"class":241},[105,17165,321],{"class":115},[105,17167,17168],{"class":248},"'\u002Farticle.md'",[105,17170,327],{"class":115},[105,17172,17173,17175,17177,17179],{"class":107,"line":119},[105,17174,568],{"class":111},[105,17176,15405],{"class":129},[105,17178,574],{"class":111},[105,17180,15410],{"class":115},[105,17182,17183,17185,17187,17189,17191,17193],{"class":107,"line":133},[105,17184,15415],{"class":115},[105,17186,15418],{"class":241},[105,17188,321],{"class":115},[105,17190,7412],{"class":111},[105,17192,15425],{"class":241},[105,17194,16051],{"class":115},[105,17196,17197,17199,17201,17203,17205,17207],{"class":107,"line":148},[105,17198,15415],{"class":115},[105,17200,15418],{"class":241},[105,17202,321],{"class":115},[105,17204,7412],{"class":111},[105,17206,15443],{"class":241},[105,17208,3917],{"class":115},[105,17210,17211,17213,17215,17217,17219,17221],{"class":107,"line":163},[105,17212,15454],{"class":241},[105,17214,321],{"class":115},[105,17216,15321],{"class":122},[105,17218,286],{"class":115},[105,17220,15463],{"class":122},[105,17222,1000],{"class":115},[105,17224,17225,17227,17230,17232,17235],{"class":107,"line":179},[105,17226,6289],{"class":111},[105,17228,17229],{"class":129}," html",[105,17231,574],{"class":111},[105,17233,17234],{"class":241}," markdownToHTML",[105,17236,15522],{"class":115},[105,17238,17239,17241,17243,17245,17248,17250,17252,17254,17256],{"class":107,"line":194},[105,17240,17088],{"class":115},[105,17242,17091],{"class":241},[105,17244,321],{"class":115},[105,17246,17247],{"class":248},"'content'",[105,17249,5624],{"class":115},[105,17251,17101],{"class":241},[105,17253,321],{"class":115},[105,17255,17106],{"class":248},[105,17257,17258],{"class":115},", html);\n",[105,17260,17261,17263,17265],{"class":107,"line":209},[105,17262,15470],{"class":115},[105,17264,15473],{"class":241},[105,17266,15522],{"class":115},[105,17268,17269],{"class":107,"line":526},[105,17270,12475],{"class":115},[105,17272,17273],{"class":107,"line":531},[105,17274,15491],{"class":115},[18,17276,17277],{},"AI 流式输出",[96,17279,17281],{"className":3241,"code":17280,"language":3243,"meta":101,"style":101},"const response = await fetch('\u002Fapi\u002Fchat', {\n    method: 'POST',\n    body: JSON.stringify({ prompt: 'Hello' })\n});\n\nconst reader = response.body\n    .pipeThrough(new TextDecoderStream())\n    .getReader();\n\nwhile (true) {\n    const { done, value } = await reader.read();\n    if (done) break;\n    \u002F\u002F 逐字输出\n    document.getElementById('output').textContent += value;\n}\n",[24,17282,17283,17302,17312,17331,17335,17339,17349,17363,17371,17375,17386,17410,17420,17425,17444],{"__ignoreMap":101},[105,17284,17285,17287,17289,17291,17293,17295,17297,17300],{"class":107,"line":108},[105,17286,568],{"class":111},[105,17288,6495],{"class":129},[105,17290,574],{"class":111},[105,17292,5410],{"class":111},[105,17294,5982],{"class":241},[105,17296,321],{"class":115},[105,17298,17299],{"class":248},"'\u002Fapi\u002Fchat'",[105,17301,2799],{"class":115},[105,17303,17304,17307,17310],{"class":107,"line":119},[105,17305,17306],{"class":115},"    method: ",[105,17308,17309],{"class":248},"'POST'",[105,17311,2849],{"class":115},[105,17313,17314,17317,17319,17321,17323,17326,17328],{"class":107,"line":133},[105,17315,17316],{"class":115},"    body: ",[105,17318,5360],{"class":129},[105,17320,5363],{"class":115},[105,17322,5366],{"class":241},[105,17324,17325],{"class":115},"({ prompt: ",[105,17327,11018],{"class":248},[105,17329,17330],{"class":115}," })\n",[105,17332,17333],{"class":107,"line":148},[105,17334,3964],{"class":115},[105,17336,17337],{"class":107,"line":163},[105,17338,307],{"emptyLinePlaceholder":306},[105,17340,17341,17343,17345,17347],{"class":107,"line":179},[105,17342,568],{"class":111},[105,17344,16031],{"class":129},[105,17346,574],{"class":111},[105,17348,15410],{"class":115},[105,17350,17351,17353,17355,17357,17359,17361],{"class":107,"line":194},[105,17352,15415],{"class":115},[105,17354,15418],{"class":241},[105,17356,321],{"class":115},[105,17358,7412],{"class":111},[105,17360,15425],{"class":241},[105,17362,16051],{"class":115},[105,17364,17365,17367,17369],{"class":107,"line":209},[105,17366,15415],{"class":115},[105,17368,16058],{"class":241},[105,17370,665],{"class":115},[105,17372,17373],{"class":107,"line":526},[105,17374,307],{"emptyLinePlaceholder":306},[105,17376,17377,17380,17382,17384],{"class":107,"line":531},[105,17378,17379],{"class":111},"while",[105,17381,4361],{"class":115},[105,17383,9140],{"class":129},[105,17385,1000],{"class":115},[105,17387,17388,17390,17392,17394,17396,17398,17400,17402,17404,17406,17408],{"class":107,"line":668},[105,17389,7220],{"class":111},[105,17391,726],{"class":115},[105,17393,16097],{"class":129},[105,17395,286],{"class":115},[105,17397,8433],{"class":129},[105,17399,3955],{"class":115},[105,17401,245],{"class":111},[105,17403,5410],{"class":111},[105,17405,16110],{"class":115},[105,17407,16113],{"class":241},[105,17409,665],{"class":115},[105,17411,17412,17414,17416,17418],{"class":107,"line":673},[105,17413,7245],{"class":111},[105,17415,16122],{"class":115},[105,17417,16125],{"class":111},[105,17419,145],{"class":115},[105,17421,17422],{"class":107,"line":679},[105,17423,17424],{"class":630},"    \u002F\u002F 逐字输出\n",[105,17426,17427,17430,17432,17434,17437,17440,17442],{"class":107,"line":689},[105,17428,17429],{"class":115},"    document.",[105,17431,17091],{"class":241},[105,17433,321],{"class":115},[105,17435,17436],{"class":248},"'output'",[105,17438,17439],{"class":115},").textContent ",[105,17441,16135],{"class":111},[105,17443,12637],{"class":115},[105,17445,17446],{"class":107,"line":699},[105,17447,212],{"class":115},[1890,17449,17450],{"id":17450},"音视频处理",[18,17452,17453],{},"音频流式",[96,17455,17457],{"className":3241,"code":17456,"language":3243,"meta":101,"style":101},"const response = await fetch('\u002Faudio.mp3');\nconst audioContext = new AudioContext();\nconst source = audioContext.createMediaStreamSource(\n    new MediaStream([\u002F* ... *\u002F])\n);\n",[24,17458,17459,17478,17494,17511,17524],{"__ignoreMap":101},[105,17460,17461,17463,17465,17467,17469,17471,17473,17476],{"class":107,"line":108},[105,17462,568],{"class":111},[105,17464,6495],{"class":129},[105,17466,574],{"class":111},[105,17468,5410],{"class":111},[105,17470,5982],{"class":241},[105,17472,321],{"class":115},[105,17474,17475],{"class":248},"'\u002Faudio.mp3'",[105,17477,327],{"class":115},[105,17479,17480,17482,17485,17487,17489,17492],{"class":107,"line":119},[105,17481,568],{"class":111},[105,17483,17484],{"class":129}," audioContext",[105,17486,574],{"class":111},[105,17488,3478],{"class":111},[105,17490,17491],{"class":241}," AudioContext",[105,17493,665],{"class":115},[105,17495,17496,17498,17501,17503,17506,17509],{"class":107,"line":133},[105,17497,568],{"class":111},[105,17499,17500],{"class":129}," source",[105,17502,574],{"class":111},[105,17504,17505],{"class":115}," audioContext.",[105,17507,17508],{"class":241},"createMediaStreamSource",[105,17510,583],{"class":115},[105,17512,17513,17515,17518,17520,17522],{"class":107,"line":148},[105,17514,10264],{"class":111},[105,17516,17517],{"class":241}," MediaStream",[105,17519,6454],{"class":115},[105,17521,729],{"class":630},[105,17523,15773],{"class":115},[105,17525,17526],{"class":107,"line":163},[105,17527,327],{"class":115},[18,17529,17530],{},"视频流式录制：",[96,17532,17534],{"className":3241,"code":17533,"language":3243,"meta":101,"style":101},"const stream = await navigator.mediaDevices.getUserMedia({ video: true });\nconst recorder = new MediaRecorder(stream);\n\nconst writable = new WritableStream({\n    write(chunk) {\n        \u002F\u002F 把录制的数据块上传或保存\n    }\n});\n\nrecorder.ondataavailable = (e) => {\n    writable.getWriter().write(e.data);\n};\n",[24,17535,17536,17560,17577,17581,17595,17605,17610,17614,17618,17622,17642,17657],{"__ignoreMap":101},[105,17537,17538,17540,17542,17544,17546,17549,17552,17555,17557],{"class":107,"line":108},[105,17539,568],{"class":111},[105,17541,15405],{"class":129},[105,17543,574],{"class":111},[105,17545,5410],{"class":111},[105,17547,17548],{"class":115}," navigator.mediaDevices.",[105,17550,17551],{"class":241},"getUserMedia",[105,17553,17554],{"class":115},"({ video: ",[105,17556,9140],{"class":129},[105,17558,17559],{"class":115}," });\n",[105,17561,17562,17564,17567,17569,17571,17574],{"class":107,"line":119},[105,17563,568],{"class":111},[105,17565,17566],{"class":129}," recorder",[105,17568,574],{"class":111},[105,17570,3478],{"class":111},[105,17572,17573],{"class":241}," MediaRecorder",[105,17575,17576],{"class":115},"(stream);\n",[105,17578,17579],{"class":107,"line":133},[105,17580,307],{"emptyLinePlaceholder":306},[105,17582,17583,17585,17587,17589,17591,17593],{"class":107,"line":148},[105,17584,568],{"class":111},[105,17586,15302],{"class":129},[105,17588,574],{"class":111},[105,17590,3478],{"class":111},[105,17592,15309],{"class":241},[105,17594,3917],{"class":115},[105,17596,17597,17599,17601,17603],{"class":107,"line":163},[105,17598,15316],{"class":241},[105,17600,321],{"class":115},[105,17602,15321],{"class":122},[105,17604,1000],{"class":115},[105,17606,17607],{"class":107,"line":179},[105,17608,17609],{"class":630},"        \u002F\u002F 把录制的数据块上传或保存\n",[105,17611,17612],{"class":107,"line":194},[105,17613,5143],{"class":115},[105,17615,17616],{"class":107,"line":209},[105,17617,3964],{"class":115},[105,17619,17620],{"class":107,"line":526},[105,17621,307],{"emptyLinePlaceholder":306},[105,17623,17624,17627,17630,17632,17634,17636,17638,17640],{"class":107,"line":531},[105,17625,17626],{"class":115},"recorder.",[105,17628,17629],{"class":241},"ondataavailable",[105,17631,574],{"class":111},[105,17633,4361],{"class":115},[105,17635,3297],{"class":122},[105,17637,2836],{"class":115},[105,17639,723],{"class":111},[105,17641,116],{"class":115},[105,17643,17644,17647,17650,17652,17654],{"class":107,"line":668},[105,17645,17646],{"class":115},"    writable.",[105,17648,17649],{"class":241},"getWriter",[105,17651,5745],{"class":115},[105,17653,15608],{"class":241},[105,17655,17656],{"class":115},"(e.data);\n",[105,17658,17659],{"class":107,"line":673},[105,17660,4441],{"class":115},[18,17662,17663],{},"vueuse提供了两个处理web worker的组合式函数：useWebWorker和useWebWorkerFn，分别对应托管现成Worker脚本和直接包装函数体两种场景。",[96,17665,17667],{"className":3241,"code":17666,"language":3243,"meta":101,"style":101},"import { useWebWorkerFn } from '@vueuse\u002Fcore'\n\nconst { workerFn } = useWebWorkerFn((numbers) => {\n  \u002F\u002F 这里是耗时的重计算\n  return numbers.sort((a, b) => a - b)\n})\n\n\u002F\u002F 调用 workerFn，返回 Promise\nconst sorted = await workerFn(hugeArray)\n\n",[24,17668,17669,17681,17685,17712,17717,17747,17751,17755,17760],{"__ignoreMap":101},[105,17670,17671,17673,17676,17678],{"class":107,"line":108},[105,17672,3885],{"class":111},[105,17674,17675],{"class":115}," { useWebWorkerFn } ",[105,17677,3891],{"class":111},[105,17679,17680],{"class":248}," '@vueuse\u002Fcore'\n",[105,17682,17683],{"class":107,"line":119},[105,17684,307],{"emptyLinePlaceholder":306},[105,17686,17687,17689,17691,17694,17696,17698,17701,17703,17706,17708,17710],{"class":107,"line":133},[105,17688,568],{"class":111},[105,17690,726],{"class":115},[105,17692,17693],{"class":129},"workerFn",[105,17695,3955],{"class":115},[105,17697,245],{"class":111},[105,17699,17700],{"class":241}," useWebWorkerFn",[105,17702,7280],{"class":115},[105,17704,17705],{"class":122},"numbers",[105,17707,2836],{"class":115},[105,17709,723],{"class":111},[105,17711,116],{"class":115},[105,17713,17714],{"class":107,"line":148},[105,17715,17716],{"class":630},"  \u002F\u002F 这里是耗时的重计算\n",[105,17718,17719,17721,17724,17727,17729,17731,17733,17736,17738,17740,17742,17744],{"class":107,"line":163},[105,17720,4126],{"class":111},[105,17722,17723],{"class":115}," numbers.",[105,17725,17726],{"class":241},"sort",[105,17728,7280],{"class":115},[105,17730,62],{"class":122},[105,17732,286],{"class":115},[105,17734,17735],{"class":122},"b",[105,17737,2836],{"class":115},[105,17739,723],{"class":111},[105,17741,9856],{"class":115},[105,17743,4141],{"class":111},[105,17745,17746],{"class":115}," b)\n",[105,17748,17749],{"class":107,"line":179},[105,17750,2888],{"class":115},[105,17752,17753],{"class":107,"line":194},[105,17754,307],{"emptyLinePlaceholder":306},[105,17756,17757],{"class":107,"line":209},[105,17758,17759],{"class":630},"\u002F\u002F 调用 workerFn，返回 Promise\n",[105,17761,17762,17764,17767,17769,17771,17774],{"class":107,"line":526},[105,17763,568],{"class":111},[105,17765,17766],{"class":129}," sorted",[105,17768,574],{"class":111},[105,17770,5410],{"class":111},[105,17772,17773],{"class":241}," workerFn",[105,17775,17776],{"class":115},"(hugeArray)\n",[14,17778,10015],{"id":10015},[18,17780,17781,17782,17785],{},"闭包主要是通过上下文对象和作用域链实现的，",[21,17783,17784],{},"内部函数引用了外部函数的变量的时候","，v8会把这些变量从栈上搬到堆上的上下文对象中，这样内部的函数可以通过作用域链来访问它们。正常情况下函数执行的时候局部变量会分配在调用栈上，函数一旦返回，变量就会随着栈帧一起销毁。但是闭包会打破这个规则，v8发现一个内部函数引用了外部函数变量，那么就让这个变量在外部函数返回后仍需存活，v8会把这些被引用的变量以及可能其他被引用的变量从栈提升到堆上，封装成一个上下文对象。",[18,17787,17788,17789,17792],{},"可以把上下文理解为一个特殊的环境记录，用来保存那些需要活过外部函数生命周期的变量。每个函数对象内部都有一个隐藏的 ",[24,17790,17791],{},"[[Environment]]","槽，称为Scope内部属性，当函数被创建的时候，这个属性会指向它定义时候所处的词法环境，也就是外层context对象。闭包执行并需要访问外部的变量，v8就会沿着这条链去找。",[18,17794,17795,17796,17799,17800,17803],{},"v8不会盲目保存所有变量，而是依靠惰性解析，v8首次解析代码的时候遇到",[21,17797,17798],{},"函数声明","会跳过其内部代码，",[21,17801,17802],{},"只生成顶层的AST和字节码","，然后进行静态分析，只把内部函数实际引用的变量提升到上下文对象，而不是外部函数所有的局部变量，如果某个变量没有被闭包引用，他依然留在栈上，执行完销毁。",[18,17805,17806],{},"结合之前的js核心运行机制可以推断出：",[18,17808,17809,17810,17813,17814,17817],{},"词法环境和变量环境是抽象概念，v8层面，它们会被具象化为context对象，变量提升到context对象，",[21,17811,17812],{},"发生在外层函数执行的时候","，而不是内层函数查找的时候。V8 在编译外层函数时，就已经通过",[21,17815,17816],{},"静态分析","知道了 哪些变量会被内层函数引用，这些变量应该放在上下文的哪个槽位，所以外层函数一执行，V8 就直接在堆上创建 Context，把这些变量放进去。内层函数创建时，直接拿到这个 Context 的引用。",[18,17819,17820],{},"静态分析和逃逸分析",[18,17822,17823],{},"静态分析和逃逸分析是包含关系：",[96,17825,17828],{"className":17826,"code":17827,"language":392},[390],"编译优化\n  ├── 静态分析（Static Analysis）        ← 大类：编译期分析代码性质\n  │     ├── 逃逸分析（Escape Analysis）   ← 子类：分析变量是否“逃逸”\n  │     ├── 死代码消除\n  │     ├── 常量折叠\n  │     └── 内联分析\n  └── 动态分析（Dynamic Analysis）        ← 运行时分析\n",[24,17829,17827],{"__ignoreMap":101},[18,17831,17832],{},"静态分析是“在编译期分析代码性质”的总称，逃逸分析是其中专门用来判断“变量是否会逃出当前作用域”的技术。逃逸本意是指一个变量或者对象的生命周期是否会超出它被创建时的作用域，变量只在当前函数内使用，函数返回即销毁就叫做不逃逸，反之就是逃逸到堆。变量被传递到其他线程就叫做逃逸到其他线程。",[14,17834,17836],{"id":17835},"gc机制","GC机制",[18,17838,17839],{},"js的垃圾回收是引擎自动管理内存的机制，负责识别并回收不再被使用的内存，防止内存泄漏，v8的gc策略是分代回收+增量标记+并发清理的组合策略。",[18,17841,17842,17843,17846],{},"gc不关心这个变量叫什么，只关心这个对象还能不能访问到，从根对象出发，沿着引用链遍历，",[21,17844,17845],{},"能达到的对象就是可达的","，不能达到的就是不可达，会被回收掉。",[18,17848,17849],{},"根对象包括了window\u002FglobalThis，调用栈上的变量（当前执行函数的局部变量、参数）。闭包捕获的变量、DOM引用。",[18,17851,17852],{},"v8把堆内存分为了新生代和老生代，针对不同生命周期采用不同策略，新生代比较小可能就1-8MB，老生代比较大，可能几百MB甚至GB，新生代的生命周期比较短，老生代周期比较长。",[18,17854,17855,17856,9365,17859,91],{},"新生代使用Scavenge复制算法进行回收，老生代使用mark-sweep+mark-compact。把新生代分为两个半区：",[21,17857,17858],{},"From 空间",[21,17860,17861],{},"To 空间",[96,17863,17866],{"className":17864,"code":17865,"language":392},[390],"From 空间（使用中）    To 空间（空闲）\n┌─────────────┐      ┌─────────────┐\n│ 活对象       │      │             │\n│ 死对象       │      │             │\n└─────────────┘      └─────────────┘\n",[24,17867,17865],{"__ignoreMap":101},[18,17869,17870,17871,91],{},"从根出发，标记from空间中的活对象，把活对象复制到To空间，清空From空间，交换From和To的对象，如果对象经过多次 Scavenge 仍然存活，会被",[21,17872,17873],{},"晋升到老生代",[18,17875,17876,17877,17880,17881,17884,17888,17892],{},"分代思想是个很成熟的模型，就像CLR也是采用的三代模型的gc回收。第一代充当缓冲区，短期存活的对象有机会再0代回收后被快速清理，而不必升到2代，clr有大型对象堆LOH，任何大于85KB的对象会直接分配到LOH上，并被标记为2代， 它",[21,17878,17879],{},"只在完全 GC（第 2 代回收）时才会被清理","，且",[21,17882,17883],{},"默认不会被压缩",[62,17885],{"href":17886,"rel":17887},"https:\u002F\u002Fgithub.com\u002Frambhawan\u002FComputer-Compiler-Lang\u002Fblob\u002F825822d3e676b2dc5a93d649ae8a4555cb606b93\u002FPro%20.Net%20Performance.pdf?_x_output_type_b6db407c4_=embedded_pdf#53#19",[66],[62,17889],{"href":17890,"rel":17891},"https:\u002F\u002Fm.yisu.com\u002Fzixun\u002F102253.html",[66],"。这是因为移动大对象内存的代价很高，V8 没有专门的 LOH，所有对象都在新生代或老生代中处理。",[14,17894,17896],{"id":17895},"ts的类型","TS的类型",[18,17898,17899],{},"在ts之中，函数会被看做值，类型是函数类型(x: number) => string。方法是对象里的函数属性，类型也是函数类型。类方法是类原型上的函数，类型也是函数类型。因此可以推理得出：方法的泛型 = 函数类型的泛型，不是所有方法都有独立的类型系统。",[18,17901,17902,17903,17906],{},"泛型参数 ",[24,17904,17905],{},"\u003CT>","可以写在函数签名上，而不是写在类型别名\u002F接口\u002F类上。我们知道泛型有两种写法：",[96,17908,17910],{"className":3241,"code":17909,"language":3243,"meta":101,"style":101},"type Box\u003CT> = {\n    value: T;\n};\n\u002F\u002F   ^^^ T 是 Box 的类型参数\nconst b: Box\u003Cnumber> = { value: 1 };   \u002F\u002F 用类型时确定 T\n\nfunction identity\u003CT>(x: T): T {\n    \u002F\u002F           ^^^ T 是函数的类型参数\n    return x;\n}\nidentity\u003Cnumber>(1);   \u002F\u002F 调用函数时确定 T\n",[24,17911,17912,17929,17940,17944,17949,17979,17983,18010,18015,18022,18026],{"__ignoreMap":101},[105,17913,17914,17916,17919,17921,17923,17925,17927],{"class":107,"line":108},[105,17915,7225],{"class":111},[105,17917,17918],{"class":241}," Box",[105,17920,234],{"class":115},[105,17922,4990],{"class":241},[105,17924,12188],{"class":115},[105,17926,245],{"class":111},[105,17928,116],{"class":115},[105,17930,17931,17934,17936,17938],{"class":107,"line":119},[105,17932,17933],{"class":122},"    value",[105,17935,4999],{"class":111},[105,17937,5057],{"class":241},[105,17939,145],{"class":115},[105,17941,17942],{"class":107,"line":133},[105,17943,4441],{"class":115},[105,17945,17946],{"class":107,"line":148},[105,17947,17948],{"class":630},"\u002F\u002F   ^^^ T 是 Box 的类型参数\n",[105,17950,17951,17953,17955,17957,17959,17961,17964,17966,17968,17971,17973,17976],{"class":107,"line":163},[105,17952,568],{"class":111},[105,17954,9757],{"class":129},[105,17956,4999],{"class":111},[105,17958,17918],{"class":241},[105,17960,234],{"class":115},[105,17962,17963],{"class":129},"number",[105,17965,12188],{"class":115},[105,17967,245],{"class":111},[105,17969,17970],{"class":115}," { value: ",[105,17972,861],{"class":129},[105,17974,17975],{"class":115}," };   ",[105,17977,17978],{"class":630},"\u002F\u002F 用类型时确定 T\n",[105,17980,17981],{"class":107,"line":179},[105,17982,307],{"emptyLinePlaceholder":306},[105,17984,17985,17987,17990,17992,17994,17996,17998,18000,18002,18004,18006,18008],{"class":107,"line":194},[105,17986,8113],{"class":111},[105,17988,17989],{"class":241}," identity",[105,17991,234],{"class":115},[105,17993,4990],{"class":241},[105,17995,4993],{"class":115},[105,17997,9828],{"class":122},[105,17999,4999],{"class":111},[105,18001,5057],{"class":241},[105,18003,11938],{"class":115},[105,18005,4999],{"class":111},[105,18007,5057],{"class":241},[105,18009,116],{"class":115},[105,18011,18012],{"class":107,"line":209},[105,18013,18014],{"class":630},"    \u002F\u002F           ^^^ T 是函数的类型参数\n",[105,18016,18017,18019],{"class":107,"line":526},[105,18018,10662],{"class":111},[105,18020,18021],{"class":115}," x;\n",[105,18023,18024],{"class":107,"line":531},[105,18025,212],{"class":115},[105,18027,18028,18031,18033,18035,18037,18039,18042],{"class":107,"line":668},[105,18029,18030],{"class":241},"identity",[105,18032,234],{"class":115},[105,18034,17963],{"class":129},[105,18036,4993],{"class":115},[105,18038,861],{"class":129},[105,18040,18041],{"class":115},");   ",[105,18043,18044],{"class":630},"\u002F\u002F 调用函数时确定 T\n",[18,18046,18047],{},"两种泛型位置不同，分别叫做类型级泛型和调用级泛型。",[14,18049,18051],{"id":18050},"ts的基本模式","Ts的基本模式",[18,18053,18054],{},"ts有个非常常见的模式，使用interface来描述实例，一份interface描述构造函数并且包含静态方法，然后class同时满足。",[18,18056,18057],{},"我们使用的Array Function Promise全都是按照这个模式来编写的：",[96,18059,18061],{"className":3241,"code":18060,"language":3243,"meta":101,"style":101},"\u002F\u002F 1. 实例接口\ninterface User {\n    name: string;\n    age: number;\n    greet(): string;\n}\n\n\u002F\u002F 2. 构造函数接口（含静态方法）\ninterface UserConstructor {\n    new (name: string, age: number): User;   \u002F\u002F 构造签名\n    create(name: string, age: number): User; \u002F\u002F 静态方法\n    readonly MAX_AGE: number;                 \u002F\u002F 静态属性\n}\n\n\u002F\u002F 3. 类实现两者\nclass UserImpl implements User {\n    static MAX_AGE = 150;\n    \n    constructor(public name: string, public age: number) {}\n    \n    greet() {\n        return `Hi, I'm ${this.name}`;\n    }\n    \n    static create(name: string, age: number): User {\n        return new UserImpl(name, age);\n    }\n}\n\n\u002F\u002F 4. 用构造函数接口约束类本身\nconst UserClass: UserConstructor = UserImpl;   \u002F\u002F ✅\n",[24,18062,18063,18068,18077,18088,18099,18112,18116,18120,18125,18134,18167,18200,18217,18221,18225,18230,18244,18257,18262,18292,18296,18302,18319,18323,18327,18358,18369,18373,18377,18381,18386],{"__ignoreMap":101},[105,18064,18065],{"class":107,"line":108},[105,18066,18067],{"class":630},"\u002F\u002F 1. 实例接口\n",[105,18069,18070,18072,18075],{"class":107,"line":119},[105,18071,3501],{"class":111},[105,18073,18074],{"class":241}," User",[105,18076,116],{"class":115},[105,18078,18079,18082,18084,18086],{"class":107,"line":133},[105,18080,18081],{"class":122},"    name",[105,18083,4999],{"class":111},[105,18085,5002],{"class":129},[105,18087,145],{"class":115},[105,18089,18090,18093,18095,18097],{"class":107,"line":148},[105,18091,18092],{"class":122},"    age",[105,18094,4999],{"class":111},[105,18096,14884],{"class":129},[105,18098,145],{"class":115},[105,18100,18101,18104,18106,18108,18110],{"class":107,"line":163},[105,18102,18103],{"class":241},"    greet",[105,18105,15111],{"class":115},[105,18107,4999],{"class":111},[105,18109,5002],{"class":129},[105,18111,145],{"class":115},[105,18113,18114],{"class":107,"line":179},[105,18115,212],{"class":115},[105,18117,18118],{"class":107,"line":194},[105,18119,307],{"emptyLinePlaceholder":306},[105,18121,18122],{"class":107,"line":209},[105,18123,18124],{"class":630},"\u002F\u002F 2. 构造函数接口（含静态方法）\n",[105,18126,18127,18129,18132],{"class":107,"line":526},[105,18128,3501],{"class":111},[105,18130,18131],{"class":241}," UserConstructor",[105,18133,116],{"class":115},[105,18135,18136,18138,18140,18142,18144,18146,18148,18151,18153,18155,18157,18159,18161,18164],{"class":107,"line":531},[105,18137,10264],{"class":111},[105,18139,4361],{"class":115},[105,18141,4996],{"class":122},[105,18143,4999],{"class":111},[105,18145,5002],{"class":129},[105,18147,286],{"class":115},[105,18149,18150],{"class":122},"age",[105,18152,4999],{"class":111},[105,18154,14884],{"class":129},[105,18156,11938],{"class":115},[105,18158,4999],{"class":111},[105,18160,18074],{"class":241},[105,18162,18163],{"class":115},";   ",[105,18165,18166],{"class":630},"\u002F\u002F 构造签名\n",[105,18168,18169,18172,18174,18176,18178,18180,18182,18184,18186,18188,18190,18192,18194,18197],{"class":107,"line":668},[105,18170,18171],{"class":241},"    create",[105,18173,321],{"class":115},[105,18175,4996],{"class":122},[105,18177,4999],{"class":111},[105,18179,5002],{"class":129},[105,18181,286],{"class":115},[105,18183,18150],{"class":122},[105,18185,4999],{"class":111},[105,18187,14884],{"class":129},[105,18189,11938],{"class":115},[105,18191,4999],{"class":111},[105,18193,18074],{"class":241},[105,18195,18196],{"class":115},"; ",[105,18198,18199],{"class":630},"\u002F\u002F 静态方法\n",[105,18201,18202,18204,18207,18209,18211,18214],{"class":107,"line":673},[105,18203,10312],{"class":111},[105,18205,18206],{"class":122}," MAX_AGE",[105,18208,4999],{"class":111},[105,18210,14884],{"class":129},[105,18212,18213],{"class":115},";                 ",[105,18215,18216],{"class":630},"\u002F\u002F 静态属性\n",[105,18218,18219],{"class":107,"line":679},[105,18220,212],{"class":115},[105,18222,18223],{"class":107,"line":689},[105,18224,307],{"emptyLinePlaceholder":306},[105,18226,18227],{"class":107,"line":699},[105,18228,18229],{"class":630},"\u002F\u002F 3. 类实现两者\n",[105,18231,18232,18234,18237,18240,18242],{"class":107,"line":704},[105,18233,12178],{"class":111},[105,18235,18236],{"class":241}," UserImpl",[105,18238,18239],{"class":111}," implements",[105,18241,18074],{"class":241},[105,18243,116],{"class":115},[105,18245,18246,18248,18250,18252,18255],{"class":107,"line":710},[105,18247,13662],{"class":111},[105,18249,18206],{"class":122},[105,18251,574],{"class":111},[105,18253,18254],{"class":129}," 150",[105,18256,145],{"class":115},[105,18258,18259],{"class":107,"line":1136},[105,18260,18261],{"class":115},"    \n",[105,18263,18264,18266,18268,18271,18274,18276,18278,18280,18282,18285,18287,18289],{"class":107,"line":1149},[105,18265,12294],{"class":111},[105,18267,321],{"class":115},[105,18269,18270],{"class":111},"public",[105,18272,18273],{"class":122}," name",[105,18275,4999],{"class":111},[105,18277,5002],{"class":129},[105,18279,286],{"class":115},[105,18281,18270],{"class":111},[105,18283,18284],{"class":122}," age",[105,18286,4999],{"class":111},[105,18288,14884],{"class":129},[105,18290,18291],{"class":115},") {}\n",[105,18293,18294],{"class":107,"line":3837},[105,18295,18261],{"class":115},[105,18297,18298,18300],{"class":107,"line":3847},[105,18299,18103],{"class":241},[105,18301,9229],{"class":115},[105,18303,18304,18306,18309,18311,18313,18315,18317],{"class":107,"line":3857},[105,18305,11236],{"class":111},[105,18307,18308],{"class":248}," `Hi, I'm ${",[105,18310,2870],{"class":129},[105,18312,5363],{"class":248},[105,18314,4996],{"class":115},[105,18316,6852],{"class":248},[105,18318,145],{"class":115},[105,18320,18321],{"class":107,"line":3866},[105,18322,5143],{"class":115},[105,18324,18325],{"class":107,"line":3871},[105,18326,18261],{"class":115},[105,18328,18329,18331,18334,18336,18338,18340,18342,18344,18346,18348,18350,18352,18354,18356],{"class":107,"line":3882},[105,18330,13662],{"class":111},[105,18332,18333],{"class":241}," create",[105,18335,321],{"class":115},[105,18337,4996],{"class":122},[105,18339,4999],{"class":111},[105,18341,5002],{"class":129},[105,18343,286],{"class":115},[105,18345,18150],{"class":122},[105,18347,4999],{"class":111},[105,18349,14884],{"class":129},[105,18351,11938],{"class":115},[105,18353,4999],{"class":111},[105,18355,18074],{"class":241},[105,18357,116],{"class":115},[105,18359,18360,18362,18364,18366],{"class":107,"line":3899},[105,18361,11236],{"class":111},[105,18363,3478],{"class":111},[105,18365,18236],{"class":241},[105,18367,18368],{"class":115},"(name, age);\n",[105,18370,18371],{"class":107,"line":3904},[105,18372,5143],{"class":115},[105,18374,18375],{"class":107,"line":3920},[105,18376,212],{"class":115},[105,18378,18379],{"class":107,"line":3936},[105,18380,307],{"emptyLinePlaceholder":306},[105,18382,18383],{"class":107,"line":3947},[105,18384,18385],{"class":630},"\u002F\u002F 4. 用构造函数接口约束类本身\n",[105,18387,18388,18390,18393,18395,18397,18399,18402],{"class":107,"line":3961},[105,18389,568],{"class":111},[105,18391,18392],{"class":129}," UserClass",[105,18394,4999],{"class":111},[105,18396,18131],{"class":241},[105,18398,574],{"class":111},[105,18400,18401],{"class":115}," UserImpl;   ",[105,18403,18404],{"class":630},"\u002F\u002F ✅\n",[18,18406,18407],{},"这样的方式可以让我们解耦接口和实现，还支持依赖注入、工厂函数和类统一。以及还忘记提了，可以仿照Promise一样，使用declare var Promise: PromiseConstructor",[18,18409,18410],{},"这是lib.es2015.promise.d.ts里的做法，用一个var声明把值（构造函数）和类型（实例）分开，这个是ts声明库类型时的经典模式。",[96,18412,18414],{"className":3241,"code":18413,"language":3243,"meta":101,"style":101},"\u002F\u002F ===== 1. 实例接口 =====\ninterface Promise\u003CT> {\n    then\u003CTResult1 = T, TResult2 = never>(\n        onfulfilled?: ((value: T) => TResult1 | PromiseLike\u003CTResult1>) | null,\n        onrejected?: ((reason: any) => TResult2 | PromiseLike\u003CTResult2>) | null\n    ): Promise\u003CTResult1 | TResult2>;\n    catch\u003CTResult = never>(\n        onrejected?: ((reason: any) => TResult | PromiseLike\u003CTResult>) | null\n    ): Promise\u003CT | TResult>;\n    finally(onfinally?: (() => void) | null): Promise\u003CT>;\n    readonly [Symbol.toStringTag]: string;\n}\n\n\u002F\u002F ===== 2. 构造函数接口 =====\ninterface PromiseConstructor {\n    readonly prototype: Promise\u003Cany>;\n    \n    \u002F\u002F new 签名\n    new \u003CT>(\n        executor: (\n            resolve: (value: T | PromiseLike\u003CT>) => void,\n            reject: (reason?: any) => void\n        ) => void\n    ): Promise\u003CT>;\n    \n    \u002F\u002F 静态方法\n    resolve(): Promise\u003Cvoid>;\n    resolve\u003CT>(value: T | PromiseLike\u003CT>): Promise\u003CT>;\n    reject\u003CT = never>(reason?: any): Promise\u003CT>;\n    all\u003CT extends readonly unknown[] | []>(values: T): Promise\u003C...>;\n    race\u003CT>(values: Iterable\u003CT | PromiseLike\u003CT>>): Promise\u003CT>;\n    allSettled\u003CT>(values: Iterable\u003CT | PromiseLike\u003CT>>): Promise\u003C...>;\n    any\u003CT>(values: Iterable\u003CT | PromiseLike\u003CT>>): Promise\u003CT>;\n}\n\n\u002F\u002F ===== 3. 用 declare var 声明\"值\" =====\ndeclare var Promise: PromiseConstructor;\n",[24,18415,18416,18421,18433,18455,18493,18528,18546,18560,18594,18612,18649,18662,18666,18670,18675,18684,18701,18705,18710,18720,18728,18758,18778,18786,18800,18804,18808,18825,18861,18893,18933,18976,19013,19054,19058,19062,19067],{"__ignoreMap":101},[105,18417,18418],{"class":107,"line":108},[105,18419,18420],{"class":630},"\u002F\u002F ===== 1. 实例接口 =====\n",[105,18422,18423,18425,18427,18429,18431],{"class":107,"line":119},[105,18424,3501],{"class":111},[105,18426,5762],{"class":241},[105,18428,234],{"class":115},[105,18430,4990],{"class":241},[105,18432,11844],{"class":115},[105,18434,18435,18437,18439,18441,18443,18445,18447,18449,18451,18453],{"class":107,"line":133},[105,18436,11515],{"class":241},[105,18438,234],{"class":115},[105,18440,11571],{"class":241},[105,18442,574],{"class":111},[105,18444,5057],{"class":241},[105,18446,286],{"class":115},[105,18448,11613],{"class":241},[105,18450,574],{"class":111},[105,18452,11532],{"class":129},[105,18454,11992],{"class":115},[105,18456,18457,18460,18462,18464,18466,18468,18470,18472,18474,18476,18478,18481,18483,18485,18487,18489,18491],{"class":107,"line":148},[105,18458,18459],{"class":122},"        onfulfilled",[105,18461,11544],{"class":111},[105,18463,11547],{"class":115},[105,18465,8433],{"class":122},[105,18467,4999],{"class":111},[105,18469,5057],{"class":241},[105,18471,2836],{"class":115},[105,18473,723],{"class":111},[105,18475,12013],{"class":241},[105,18477,5023],{"class":111},[105,18479,18480],{"class":241}," PromiseLike",[105,18482,234],{"class":115},[105,18484,11571],{"class":241},[105,18486,12024],{"class":115},[105,18488,11563],{"class":111},[105,18490,5026],{"class":129},[105,18492,2849],{"class":115},[105,18494,18495,18498,18500,18502,18504,18506,18508,18510,18512,18514,18516,18518,18520,18522,18524,18526],{"class":107,"line":163},[105,18496,18497],{"class":122},"        onrejected",[105,18499,11544],{"class":111},[105,18501,11547],{"class":115},[105,18503,11593],{"class":122},[105,18505,4999],{"class":111},[105,18507,10417],{"class":129},[105,18509,2836],{"class":115},[105,18511,723],{"class":111},[105,18513,11636],{"class":241},[105,18515,5023],{"class":111},[105,18517,18480],{"class":241},[105,18519,234],{"class":115},[105,18521,11613],{"class":241},[105,18523,12024],{"class":115},[105,18525,11563],{"class":111},[105,18527,11622],{"class":129},[105,18529,18530,18532,18534,18536,18538,18540,18542,18544],{"class":107,"line":179},[105,18531,12069],{"class":115},[105,18533,4999],{"class":111},[105,18535,5762],{"class":241},[105,18537,234],{"class":115},[105,18539,11571],{"class":241},[105,18541,5023],{"class":111},[105,18543,11636],{"class":241},[105,18545,12084],{"class":115},[105,18547,18548,18550,18552,18554,18556,18558],{"class":107,"line":194},[105,18549,12093],{"class":241},[105,18551,234],{"class":115},[105,18553,12098],{"class":241},[105,18555,574],{"class":111},[105,18557,11532],{"class":129},[105,18559,11992],{"class":115},[105,18561,18562,18564,18566,18568,18570,18572,18574,18576,18578,18580,18582,18584,18586,18588,18590,18592],{"class":107,"line":209},[105,18563,18497],{"class":122},[105,18565,11544],{"class":111},[105,18567,11547],{"class":115},[105,18569,11593],{"class":122},[105,18571,4999],{"class":111},[105,18573,10417],{"class":129},[105,18575,2836],{"class":115},[105,18577,723],{"class":111},[105,18579,12125],{"class":241},[105,18581,5023],{"class":111},[105,18583,18480],{"class":241},[105,18585,234],{"class":115},[105,18587,12098],{"class":241},[105,18589,12024],{"class":115},[105,18591,11563],{"class":111},[105,18593,11622],{"class":129},[105,18595,18596,18598,18600,18602,18604,18606,18608,18610],{"class":107,"line":526},[105,18597,12069],{"class":115},[105,18599,4999],{"class":111},[105,18601,5762],{"class":241},[105,18603,234],{"class":115},[105,18605,4990],{"class":241},[105,18607,5023],{"class":111},[105,18609,12125],{"class":241},[105,18611,12084],{"class":115},[105,18613,18614,18617,18619,18622,18624,18627,18629,18631,18633,18635,18637,18639,18641,18643,18645,18647],{"class":107,"line":531},[105,18615,18616],{"class":241},"    finally",[105,18618,321],{"class":115},[105,18620,18621],{"class":122},"onfinally",[105,18623,11544],{"class":111},[105,18625,18626],{"class":115}," (() ",[105,18628,723],{"class":111},[105,18630,5064],{"class":129},[105,18632,2836],{"class":115},[105,18634,11563],{"class":111},[105,18636,5026],{"class":129},[105,18638,11938],{"class":115},[105,18640,4999],{"class":111},[105,18642,5762],{"class":241},[105,18644,234],{"class":115},[105,18646,4990],{"class":241},[105,18648,12084],{"class":115},[105,18650,18651,18653,18656,18658,18660],{"class":107,"line":668},[105,18652,10312],{"class":111},[105,18654,18655],{"class":115}," [Symbol.toStringTag]",[105,18657,4999],{"class":111},[105,18659,5002],{"class":129},[105,18661,145],{"class":115},[105,18663,18664],{"class":107,"line":673},[105,18665,212],{"class":115},[105,18667,18668],{"class":107,"line":679},[105,18669,307],{"emptyLinePlaceholder":306},[105,18671,18672],{"class":107,"line":689},[105,18673,18674],{"class":630},"\u002F\u002F ===== 2. 构造函数接口 =====\n",[105,18676,18677,18679,18682],{"class":107,"line":699},[105,18678,3501],{"class":111},[105,18680,18681],{"class":241}," PromiseConstructor",[105,18683,116],{"class":115},[105,18685,18686,18688,18690,18692,18694,18696,18699],{"class":107,"line":704},[105,18687,10312],{"class":111},[105,18689,10315],{"class":122},[105,18691,4999],{"class":111},[105,18693,5762],{"class":241},[105,18695,234],{"class":115},[105,18697,18698],{"class":129},"any",[105,18700,12084],{"class":115},[105,18702,18703],{"class":107,"line":710},[105,18704,18261],{"class":115},[105,18706,18707],{"class":107,"line":1136},[105,18708,18709],{"class":630},"    \u002F\u002F new 签名\n",[105,18711,18712,18714,18716,18718],{"class":107,"line":1149},[105,18713,10264],{"class":111},[105,18715,14900],{"class":115},[105,18717,4990],{"class":241},[105,18719,11992],{"class":115},[105,18721,18722,18724,18726],{"class":107,"line":3837},[105,18723,12301],{"class":241},[105,18725,4999],{"class":111},[105,18727,12306],{"class":115},[105,18729,18730,18732,18734,18736,18738,18740,18742,18744,18746,18748,18750,18752,18754,18756],{"class":107,"line":3847},[105,18731,12311],{"class":241},[105,18733,4999],{"class":111},[105,18735,4361],{"class":115},[105,18737,8433],{"class":122},[105,18739,4999],{"class":111},[105,18741,5057],{"class":241},[105,18743,5023],{"class":111},[105,18745,18480],{"class":241},[105,18747,234],{"class":115},[105,18749,4990],{"class":241},[105,18751,12024],{"class":115},[105,18753,723],{"class":111},[105,18755,5064],{"class":129},[105,18757,2849],{"class":115},[105,18759,18760,18762,18764,18766,18768,18770,18772,18774,18776],{"class":107,"line":3857},[105,18761,12334],{"class":241},[105,18763,4999],{"class":111},[105,18765,4361],{"class":115},[105,18767,11593],{"class":122},[105,18769,11544],{"class":111},[105,18771,10417],{"class":129},[105,18773,2836],{"class":115},[105,18775,723],{"class":111},[105,18777,12351],{"class":129},[105,18779,18780,18782,18784],{"class":107,"line":3866},[105,18781,12356],{"class":115},[105,18783,723],{"class":111},[105,18785,12351],{"class":129},[105,18787,18788,18790,18792,18794,18796,18798],{"class":107,"line":3871},[105,18789,12069],{"class":115},[105,18791,4999],{"class":111},[105,18793,5762],{"class":241},[105,18795,234],{"class":115},[105,18797,4990],{"class":241},[105,18799,12084],{"class":115},[105,18801,18802],{"class":107,"line":3882},[105,18803,18261],{"class":115},[105,18805,18806],{"class":107,"line":3899},[105,18807,15204],{"class":630},[105,18809,18810,18812,18814,18816,18818,18820,18823],{"class":107,"line":3904},[105,18811,11927],{"class":241},[105,18813,15111],{"class":115},[105,18815,4999],{"class":111},[105,18817,5762],{"class":241},[105,18819,234],{"class":115},[105,18821,18822],{"class":129},"void",[105,18824,12084],{"class":115},[105,18826,18827,18829,18831,18833,18835,18837,18839,18841,18843,18845,18847,18849,18851,18853,18855,18857,18859],{"class":107,"line":3920},[105,18828,11927],{"class":241},[105,18830,234],{"class":115},[105,18832,4990],{"class":241},[105,18834,4993],{"class":115},[105,18836,8433],{"class":122},[105,18838,4999],{"class":111},[105,18840,5057],{"class":241},[105,18842,5023],{"class":111},[105,18844,18480],{"class":241},[105,18846,234],{"class":115},[105,18848,4990],{"class":241},[105,18850,14999],{"class":115},[105,18852,4999],{"class":111},[105,18854,5762],{"class":241},[105,18856,234],{"class":115},[105,18858,4990],{"class":241},[105,18860,12084],{"class":115},[105,18862,18863,18865,18867,18869,18871,18873,18875,18877,18879,18881,18883,18885,18887,18889,18891],{"class":107,"line":3936},[105,18864,11949],{"class":241},[105,18866,234],{"class":115},[105,18868,4990],{"class":241},[105,18870,574],{"class":111},[105,18872,11532],{"class":129},[105,18874,4993],{"class":115},[105,18876,11593],{"class":122},[105,18878,11544],{"class":111},[105,18880,10417],{"class":129},[105,18882,11938],{"class":115},[105,18884,4999],{"class":111},[105,18886,5762],{"class":241},[105,18888,234],{"class":115},[105,18890,4990],{"class":241},[105,18892,12084],{"class":115},[105,18894,18895,18898,18900,18902,18904,18907,18909,18912,18914,18917,18920,18922,18924,18926,18928,18930],{"class":107,"line":3947},[105,18896,18897],{"class":241},"    all",[105,18899,234],{"class":115},[105,18901,4990],{"class":241},[105,18903,3517],{"class":111},[105,18905,18906],{"class":111}," readonly",[105,18908,8438],{"class":129},[105,18910,18911],{"class":115},"[] ",[105,18913,11563],{"class":111},[105,18915,18916],{"class":115}," []>(",[105,18918,18919],{"class":122},"values",[105,18921,4999],{"class":111},[105,18923,5057],{"class":241},[105,18925,11938],{"class":115},[105,18927,4999],{"class":111},[105,18929,5762],{"class":241},[105,18931,18932],{"class":115},"\u003C...>;\n",[105,18934,18935,18938,18940,18942,18944,18946,18948,18951,18953,18955,18957,18959,18961,18963,18966,18968,18970,18972,18974],{"class":107,"line":3961},[105,18936,18937],{"class":241},"    race",[105,18939,234],{"class":115},[105,18941,4990],{"class":241},[105,18943,4993],{"class":115},[105,18945,18919],{"class":122},[105,18947,4999],{"class":111},[105,18949,18950],{"class":241}," Iterable",[105,18952,234],{"class":115},[105,18954,4990],{"class":241},[105,18956,5023],{"class":111},[105,18958,18480],{"class":241},[105,18960,234],{"class":115},[105,18962,4990],{"class":241},[105,18964,18965],{"class":115},">>)",[105,18967,4999],{"class":111},[105,18969,5762],{"class":241},[105,18971,234],{"class":115},[105,18973,4990],{"class":241},[105,18975,12084],{"class":115},[105,18977,18978,18981,18983,18985,18987,18989,18991,18993,18995,18997,18999,19001,19003,19005,19007,19009,19011],{"class":107,"line":3967},[105,18979,18980],{"class":241},"    allSettled",[105,18982,234],{"class":115},[105,18984,4990],{"class":241},[105,18986,4993],{"class":115},[105,18988,18919],{"class":122},[105,18990,4999],{"class":111},[105,18992,18950],{"class":241},[105,18994,234],{"class":115},[105,18996,4990],{"class":241},[105,18998,5023],{"class":111},[105,19000,18480],{"class":241},[105,19002,234],{"class":115},[105,19004,4990],{"class":241},[105,19006,18965],{"class":115},[105,19008,4999],{"class":111},[105,19010,5762],{"class":241},[105,19012,18932],{"class":115},[105,19014,19015,19018,19020,19022,19024,19026,19028,19030,19032,19034,19036,19038,19040,19042,19044,19046,19048,19050,19052],{"class":107,"line":3972},[105,19016,19017],{"class":241},"    any",[105,19019,234],{"class":115},[105,19021,4990],{"class":241},[105,19023,4993],{"class":115},[105,19025,18919],{"class":122},[105,19027,4999],{"class":111},[105,19029,18950],{"class":241},[105,19031,234],{"class":115},[105,19033,4990],{"class":241},[105,19035,5023],{"class":111},[105,19037,18480],{"class":241},[105,19039,234],{"class":115},[105,19041,4990],{"class":241},[105,19043,18965],{"class":115},[105,19045,4999],{"class":111},[105,19047,5762],{"class":241},[105,19049,234],{"class":115},[105,19051,4990],{"class":241},[105,19053,12084],{"class":115},[105,19055,19056],{"class":107,"line":3992},[105,19057,212],{"class":115},[105,19059,19060],{"class":107,"line":4010},[105,19061,307],{"emptyLinePlaceholder":306},[105,19063,19064],{"class":107,"line":4028},[105,19065,19066],{"class":630},"\u002F\u002F ===== 3. 用 declare var 声明\"值\" =====\n",[105,19068,19069,19071,19073,19075,19077,19079],{"class":107,"line":4033},[105,19070,10334],{"class":111},[105,19072,10337],{"class":111},[105,19074,5762],{"class":115},[105,19076,4999],{"class":111},[105,19078,18681],{"class":241},[105,19080,145],{"class":115},[18,19082,19083],{},"这种三段式方法很有用，declare var....这种一般用来告诉ts运行时有个全局变量叫做Promsie，类型为PromiseConsturctor，但是声明不会生成代码，如果没有declare的话，ts就把他当做变量声明了，导致结果就是编译成js，代码执行，全局Promise覆盖为undefined。",[10,19085,2947],{"id":2947},[14,19087,19088],{"id":19088},"vue-router",[18,19090,19091],{},"vue-router是提供了一系列组价比如说router-link，它是官方的路由库，核心职责是把URL和组件映射起来，但URL变化渲染对应的组件。",[96,19093,19095],{"className":3241,"code":19094,"language":3243,"meta":101,"style":101},"const routes = [\n    { path: '\u002F', component: Home },\n    { path: '\u002Fabout', component: About },\n    { path: '\u002Fuser\u002F:id', component: User }\n];\n",[24,19096,19097,19109,19120,19130,19140],{"__ignoreMap":101},[105,19098,19099,19101,19104,19106],{"class":107,"line":108},[105,19100,568],{"class":111},[105,19102,19103],{"class":129}," routes",[105,19105,574],{"class":111},[105,19107,19108],{"class":115}," [\n",[105,19110,19111,19114,19117],{"class":107,"line":119},[105,19112,19113],{"class":115},"    { path: ",[105,19115,19116],{"class":248},"'\u002F'",[105,19118,19119],{"class":115},", component: Home },\n",[105,19121,19122,19124,19127],{"class":107,"line":133},[105,19123,19113],{"class":115},[105,19125,19126],{"class":248},"'\u002Fabout'",[105,19128,19129],{"class":115},", component: About },\n",[105,19131,19132,19134,19137],{"class":107,"line":148},[105,19133,19113],{"class":115},[105,19135,19136],{"class":248},"'\u002Fuser\u002F:id'",[105,19138,19139],{"class":115},", component: User }\n",[105,19141,19142],{"class":107,"line":163},[105,19143,19144],{"class":115},"];\n",[18,19146,19147],{},"当你访问\u002Fabout的时候就会渲染Aboud组件，渲染\u002Fuser\u002F1的时候就会渲染User，id=1。SPA单页应用没有真正的页面跳转，URL变化由Vue Router接管，用JS切换组件，这个就是前端路由。",[96,19149,19151],{"className":559,"code":19150,"language":561,"meta":101,"style":101},"import { createRouter, createWebHistory } from 'vue-router';\n\nconst router = createRouter({\n    history: createWebHistory(),   \u002F\u002F 用 HTML5 History 模式\n    routes\n});\n\nexport default router;\n",[24,19152,19153,19167,19171,19185,19199,19204,19208,19212],{"__ignoreMap":101},[105,19154,19155,19157,19160,19162,19165],{"class":107,"line":108},[105,19156,3885],{"class":111},[105,19158,19159],{"class":115}," { createRouter, createWebHistory } ",[105,19161,3891],{"class":111},[105,19163,19164],{"class":248}," 'vue-router'",[105,19166,145],{"class":115},[105,19168,19169],{"class":107,"line":119},[105,19170,307],{"emptyLinePlaceholder":306},[105,19172,19173,19175,19178,19180,19183],{"class":107,"line":133},[105,19174,568],{"class":111},[105,19176,19177],{"class":129}," router",[105,19179,574],{"class":111},[105,19181,19182],{"class":241}," createRouter",[105,19184,3917],{"class":115},[105,19186,19187,19190,19193,19196],{"class":107,"line":148},[105,19188,19189],{"class":115},"    history: ",[105,19191,19192],{"class":241},"createWebHistory",[105,19194,19195],{"class":115},"(),   ",[105,19197,19198],{"class":630},"\u002F\u002F 用 HTML5 History 模式\n",[105,19200,19201],{"class":107,"line":163},[105,19202,19203],{"class":115},"    routes\n",[105,19205,19206],{"class":107,"line":179},[105,19207,3964],{"class":115},[105,19209,19210],{"class":107,"line":194},[105,19211,307],{"emptyLinePlaceholder":306},[105,19213,19214,19216,19219],{"class":107,"line":209},[105,19215,4979],{"class":111},[105,19217,19218],{"class":111}," default",[105,19220,19221],{"class":115}," router;\n",[18,19223,19224],{},"挂载到app:",[96,19226,19228],{"className":559,"code":19227,"language":561,"meta":101,"style":101},"const app = createApp(App);\napp.use(router);\napp.mount('#app');\n",[24,19229,19230,19245,19256],{"__ignoreMap":101},[105,19231,19232,19234,19237,19239,19242],{"class":107,"line":108},[105,19233,568],{"class":111},[105,19235,19236],{"class":129}," app",[105,19238,574],{"class":111},[105,19240,19241],{"class":241}," createApp",[105,19243,19244],{"class":115},"(App);\n",[105,19246,19247,19250,19253],{"class":107,"line":119},[105,19248,19249],{"class":115},"app.",[105,19251,19252],{"class":241},"use",[105,19254,19255],{"class":115},"(router);\n",[105,19257,19258,19260,19263,19265,19268],{"class":107,"line":133},[105,19259,19249],{"class":115},[105,19261,19262],{"class":241},"mount",[105,19264,321],{"class":115},[105,19266,19267],{"class":248},"'#app'",[105,19269,327],{"class":115},[18,19271,19272],{},"组件渲染的位置：",[96,19274,19276],{"className":2945,"code":19275,"language":2947,"meta":101,"style":101},"\u003Ctemplate>\n    \u003Crouter-view \u002F>   \u003C!-- 当前路由的组件渲染在这 -->\n\u003C\u002Ftemplate>\n",[24,19277,19278,19286,19299],{"__ignoreMap":101},[105,19279,19280,19282,19284],{"class":107,"line":108},[105,19281,234],{"class":115},[105,19283,3658],{"class":237},[105,19285,257],{"class":115},[105,19287,19288,19290,19293,19296],{"class":107,"line":119},[105,19289,3713],{"class":115},[105,19291,19292],{"class":237},"router-view",[105,19294,19295],{"class":115}," \u002F>   ",[105,19297,19298],{"class":630},"\u003C!-- 当前路由的组件渲染在这 -->\n",[105,19300,19301,19303,19305],{"class":107,"line":133},[105,19302,2992],{"class":115},[105,19304,3658],{"class":237},[105,19306,257],{"class":115},[18,19308,19309],{},"router-view是路由的出口，URL匹配到哪个组件，渲染就会在这里。",[18,19311,19312],{},"router-link标签是导航链接。",[18,19314,19315],{},"active-class这个也比较重要，因为用户怎么知道自己在哪个页面，导航栏对应的链接应该高亮，这个就是active-class的由来，当前的URL匹配某个router-link的时候，vue-router就会自动给他的a加一个类，然后你使用CSS让他高亮。",[96,19317,19319],{"className":98,"code":19318,"language":100,"meta":101,"style":101},".router-link-active {\n    color: red;\n    font-weight: bold;\n}\n",[24,19320,19321,19328,19340,19352],{"__ignoreMap":101},[105,19322,19323,19326],{"class":107,"line":108},[105,19324,19325],{"class":241},".router-link-active",[105,19327,116],{"class":115},[105,19329,19330,19333,19335,19338],{"class":107,"line":119},[105,19331,19332],{"class":129},"    color",[105,19334,126],{"class":115},[105,19336,19337],{"class":129},"red",[105,19339,145],{"class":115},[105,19341,19342,19345,19347,19350],{"class":107,"line":133},[105,19343,19344],{"class":129},"    font-weight",[105,19346,126],{"class":115},[105,19348,19349],{"class":129},"bold",[105,19351,145],{"class":115},[105,19353,19354],{"class":107,"line":148},[105,19355,212],{"class":115},[18,19357,19358],{},"动态路由",[18,19360,19361],{},"刚才我们讲了:id的路由形式，这个是动态路由参数，是vue-router的最常用的动态形式，但是这个词其实涵盖了好几种类别。",[18,19363,19364,19365,19368,19369,19371,19372,19375,19376,1329,19379,91],{},":开头的叫做参数占位符，能够匹配任意非\u002F字符串，通过route.params可以访问，?表示可选，+表示一个或者多个，可以匹配 匹配 ",[24,19366,19367],{},"\u002Fuser\u002F1\u002F2\u002F3","。params.ids是数组。",[24,19370,938],{}," 表示",[21,19373,19374],{},"零个或多个","，匹配 ",[24,19377,19378],{},"\u002Fuser",[24,19380,19367],{},[96,19382,19385],{"className":19383,"code":19384,"language":392},[390],"{ path: '\u002Fuser\u002F:id?', component: User }\n{ path: '\u002Fuser\u002F:ids+', component: User }\n{ path: '\u002Fuser\u002F:ids*', component: User }\n",[24,19386,19384],{"__ignoreMap":101},[18,19388,19389],{},"路由也是支持自定义正则的：",[96,19391,19394],{"className":19392,"code":19393,"language":392},[390],"{ path: '\u002Fuser\u002F:id(\\\\d+)', component: User }\n",[24,19395,19393],{"__ignoreMap":101},[18,19397,19398,19399,19402,19403,19406],{},"括号里是",[21,19400,19401],{},"正则","，限制参数格式。",[24,19404,19405],{},"(\\\\d+)"," 只匹配数字。",[18,19408,19409],{},"路由也可以使用alias来设置别名，我们可以做到一个路由可以有多个路径：",[96,19411,19413],{"className":559,"code":19412,"language":561,"meta":101,"style":101},"{\n    path: '\u002Fuser',\n    component: User,\n    alias: '\u002Fu'   \u002F\u002F \u002Fu 也渲染 User\n}\n{\n    path: '\u002Fuser',\n    component: User,\n    alias: ['\u002Fu', '\u002Fmember']   \u002F\u002F 多个别名\n}\n",[24,19414,19415,19419,19431,19439,19452,19456,19460,19470,19476,19496],{"__ignoreMap":101},[105,19416,19417],{"class":107,"line":108},[105,19418,9848],{"class":115},[105,19420,19421,19424,19426,19429],{"class":107,"line":119},[105,19422,19423],{"class":241},"    path",[105,19425,126],{"class":115},[105,19427,19428],{"class":248},"'\u002Fuser'",[105,19430,2849],{"class":115},[105,19432,19433,19436],{"class":107,"line":133},[105,19434,19435],{"class":241},"    component",[105,19437,19438],{"class":115},": User,\n",[105,19440,19441,19444,19446,19449],{"class":107,"line":148},[105,19442,19443],{"class":241},"    alias",[105,19445,126],{"class":115},[105,19447,19448],{"class":248},"'\u002Fu'",[105,19450,19451],{"class":630},"   \u002F\u002F \u002Fu 也渲染 User\n",[105,19453,19454],{"class":107,"line":163},[105,19455,212],{"class":115},[105,19457,19458],{"class":107,"line":179},[105,19459,9848],{"class":115},[105,19461,19462,19464,19466,19468],{"class":107,"line":194},[105,19463,19423],{"class":241},[105,19465,126],{"class":115},[105,19467,19428],{"class":248},[105,19469,2849],{"class":115},[105,19471,19472,19474],{"class":107,"line":209},[105,19473,19435],{"class":241},[105,19475,19438],{"class":115},[105,19477,19478,19480,19483,19485,19487,19490,19493],{"class":107,"line":526},[105,19479,19443],{"class":241},[105,19481,19482],{"class":115},": [",[105,19484,19448],{"class":248},[105,19486,286],{"class":115},[105,19488,19489],{"class":248},"'\u002Fmember'",[105,19491,19492],{"class":115},"]   ",[105,19494,19495],{"class":630},"\u002F\u002F 多个别名\n",[105,19497,19498],{"class":107,"line":531},[105,19499,212],{"class":115},[18,19501,19502],{},"命名路由可以给路由起名字，导航的时候用名字而不是路径。",[96,19504,19506],{"className":559,"code":19505,"language":561,"meta":101,"style":101},"const routes = [\n    { path: '\u002Fuser\u002F:id', name: 'user', component: User }\n];\n\u003Crouter-link :to=\"{ name: 'user', params: { id: 123 } }\">用户\u003C\u002Frouter-link>\n",[24,19507,19508,19518,19531,19535],{"__ignoreMap":101},[105,19509,19510,19512,19514,19516],{"class":107,"line":108},[105,19511,568],{"class":111},[105,19513,19103],{"class":129},[105,19515,574],{"class":111},[105,19517,19108],{"class":115},[105,19519,19520,19522,19524,19527,19529],{"class":107,"line":119},[105,19521,19113],{"class":115},[105,19523,19136],{"class":248},[105,19525,19526],{"class":115},", name: ",[105,19528,5355],{"class":248},[105,19530,19139],{"class":115},[105,19532,19533],{"class":107,"line":133},[105,19534,19144],{"class":115},[105,19536,19537,19539,19542,19546,19549,19552,19554,19557,19560,19563,19565],{"class":107,"line":148},[105,19538,234],{"class":115},[105,19540,19541],{"class":129},"router-link",[105,19543,19545],{"class":19544},"s7hpK"," :to=\"{",[105,19547,19548],{"class":19544}," name:",[105,19550,19551],{"class":248}," 'user'",[105,19553,16632],{"class":19544},[105,19555,19556],{"class":19544}," params:",[105,19558,19559],{"class":115}," { id: ",[105,19561,19562],{"class":129},"123",[105,19564,3955],{"class":115},[105,19566,19567],{"class":19544},"}\">用户\u003C\u002Frouter-link>\n",[18,19569,19570],{},"嵌套路由：",[96,19572,19574],{"className":559,"code":19573,"language":561,"meta":101,"style":101},"{\n    path: '\u002Fuser',\n    component: UserLayout,\n    children: [\n        { path: '', component: UserHome },\n        { path: 'profile', component: UserProfile }\n    ]\n}\n",[24,19575,19576,19580,19590,19597,19605,19615,19625,19630],{"__ignoreMap":101},[105,19577,19578],{"class":107,"line":108},[105,19579,9848],{"class":115},[105,19581,19582,19584,19586,19588],{"class":107,"line":119},[105,19583,19423],{"class":241},[105,19585,126],{"class":115},[105,19587,19428],{"class":248},[105,19589,2849],{"class":115},[105,19591,19592,19594],{"class":107,"line":133},[105,19593,19435],{"class":241},[105,19595,19596],{"class":115},": UserLayout,\n",[105,19598,19599,19602],{"class":107,"line":148},[105,19600,19601],{"class":241},"    children",[105,19603,19604],{"class":115},": [\n",[105,19606,19607,19610,19612],{"class":107,"line":163},[105,19608,19609],{"class":115},"        { path: ",[105,19611,2631],{"class":248},[105,19613,19614],{"class":115},", component: UserHome },\n",[105,19616,19617,19619,19622],{"class":107,"line":179},[105,19618,19609],{"class":115},[105,19620,19621],{"class":248},"'profile'",[105,19623,19624],{"class":115},", component: UserProfile }\n",[105,19626,19627],{"class":107,"line":194},[105,19628,19629],{"class":115},"    ]\n",[105,19631,19632],{"class":107,"line":209},[105,19633,212],{"class":115},[18,19635,19636],{},"这不过我们在nuxt中可能更加习惯使用layout来解决。",[18,19638,19639],{},"路由模式",[18,19641,19642],{},"Vue-Router有两种主要路由模式，外加一个SSR使用的。首先是HASH模式，可以通过createWebHashHistory来实现，特征是url中带#。#后面是路由路径，#后面的变化不会触发页面刷新。 优点有： 兼容性好、不需要服务端配置、部署简单。缺点有：Url丑、seo不友好、锚点错误。",[18,19644,19645],{},"历史模式则是使用html5 history api （pushState\u002FrepelaceState）来管理，url变化也不会刷新页面，监听poststate事件。缺点和优点和上面优点相反，这个需要服务单配置，刷新页面的时候服务端必须要返回index.html，要配置nginx\u002Fapache的fallback。",[18,19647,19648],{},"下面来讲底层原理：",[18,19650,19651,19652,19655],{},"hash模式下 我们知道url组成为 ",[24,19653,19654],{},"URL: example.com\u002F#\u002Fuser\u002F123"," ,#后面的部分变化不会触发页面刷新，#是url的片段标识符fragment，本来用于页面内锚点(阅览文档有时候会发现，就是锚点可以自动滚动那个功能)，浏览器不会吧#后面的内容发给服务端，所以服务端只会永远看到\u002F，不需要配置。静态托管包含github pages\u002Fnetify\u002Fvercel静态模式、对象存储、CDN，特点就是只提供文件，请求什么路径就返回什么文件。不能写服务端配置，没有nginx配置，也没有fallback规则。没有路由重写能力，不能把\u002Fuser\u002F123重写成\u002Findex.html。如果你在静态托管使用历史模式的话，前端跳转到\u002Fuser\u002F123会让url变化无刷新，然后用户手动刷新会发现\u002Fuser\u002F123文件不存在-> 404。哈希不一样，就算你请求example.com\u002F#\u002Fuser\u002F123，浏览器也只会请求example.com，这样永远都能找到了，然后前端去读取片段符号后的内容\u002Fuser\u002F123，来渲染对应组件。",[18,19657,19658,19661],{},[24,19659,19660],{},"URL: example.com\u002Fuser\u002F123","\nhistory不一样，pushState能改url但是不刷新页面，刷新的时候浏览器真的会请求\u002Fuser\u002F123，服务端必须返回index.html",[96,19663,19666],{"className":19664,"code":19665,"language":392},[390],"history.pushState({}, '', '\u002Fa');   \u002F\u002F 不触发 popstate\nhistory.back();                     \u002F\u002F 触发 popstate\n",[24,19667,19665],{"__ignoreMap":101},[18,19669,19670],{},"用户访问的时候nginx不把\u002Fuser\u002F123当文件找，而是返回index.html让前端路由接管，类似于这样：",[96,19672,19676],{"className":19673,"code":19674,"language":19675,"meta":101,"style":101},"language-nginx shiki shiki-themes github-light github-dark","server {\n    listen 80;\n    server_name example.com;\n    root \u002Fvar\u002Fwww\u002Fmy-app;\n    index index.html;\n\n    location \u002F {\n        try_files $uri $uri\u002F \u002Findex.html;\n    }\n}\n","nginx",[24,19677,19678,19683,19688,19693,19698,19703,19707,19712,19717,19721],{"__ignoreMap":101},[105,19679,19680],{"class":107,"line":108},[105,19681,19682],{},"server {\n",[105,19684,19685],{"class":107,"line":119},[105,19686,19687],{},"    listen 80;\n",[105,19689,19690],{"class":107,"line":133},[105,19691,19692],{},"    server_name example.com;\n",[105,19694,19695],{"class":107,"line":148},[105,19696,19697],{},"    root \u002Fvar\u002Fwww\u002Fmy-app;\n",[105,19699,19700],{"class":107,"line":163},[105,19701,19702],{},"    index index.html;\n",[105,19704,19705],{"class":107,"line":179},[105,19706,307],{"emptyLinePlaceholder":306},[105,19708,19709],{"class":107,"line":194},[105,19710,19711],{},"    location \u002F {\n",[105,19713,19714],{"class":107,"line":209},[105,19715,19716],{},"        try_files $uri $uri\u002F \u002Findex.html;\n",[105,19718,19719],{"class":107,"line":526},[105,19720,5143],{},[105,19722,19723],{"class":107,"line":531},[105,19724,212],{},[18,19726,19727],{},"实际项目往往还有 API 请求、HTTPS、gzip 等。",[18,19729,19730],{},"导航钩子",[18,19732,19733],{},"导航守卫有三类，分别是全局守卫、路由独享的守卫、组件内守卫。",[18,19735,19736],{},"全局守卫在每次路由的时候都会触发，通常用于全局路由检验，比如说用于全局的权限检验、登录检查或者页面标题更新。",[18,19738,19739,9829,19744,19747,19748,9829,19753,19756,19757,9829,19762,19765,19766,19771],{},[21,19740,19741],{},[24,19742,19743],{},"beforeEach",[21,19745,19746],{},"全局前置守卫","。在导航被确认之前触发，是进行权限验证（如检查登录状态）最常用的钩子。",[21,19749,19750],{},[24,19751,19752],{},"beforeResolve",[21,19754,19755],{},"全局解析守卫","。它在所有组件内守卫和异步路由组件被解析之后、导航被确认之前触发。适合在最终确认导航前执行一些数据获取或权限校验。",[21,19758,19759],{},[24,19760,19761],{},"afterEach",[21,19763,19764],{},"全局后置钩子","。导航完成后触发，",[21,19767,8538,19768,2773],{},[24,19769,19770],{},"next","，不能改变导航本身。常用于日志记录、分析或更新页面标题",[18,19773,19774,19775,19780,19781,19784,19785,31,19788,19791,19792,19795,19796,19799,19800,19803],{},"路由独享守卫定义为具体的路由配置中，只对特定路由生效。",[21,19776,19777],{},[24,19778,19779],{},"beforeEnter","：定义在路由的配置对象里。",[21,19782,19783],{},"仅在进入该路由时触发","，如果只是参数（",[24,19786,19787],{},"params",[24,19789,19790],{},"query","）发生变化（例如从 ",[24,19793,19794],{},"\u002Fusers\u002F1"," 到 ",[24,19797,19798],{},"\u002Fusers\u002F2","），它",[21,19801,19802],{},"不会","被触发。它和全局前置守卫有相同的函数签名",[18,19805,19806,19807,19812,19813,19816,19817,19820,19821,19824,19825,19827,19828,19833,19834,19837,19838,19841,19842,19845,19846,19849,19850,19812,19855,19858,19859,19862],{},"组件内收尾在路由组件内部，用于控制该组件相关的导航：\n",[21,19808,19809],{},[24,19810,19811],{},"beforeRouteEnter","：在",[21,19814,19815],{},"进入","该组件对应的路由前调用。",[21,19818,19819],{},"注意","，此时组件实例",[21,19822,19823],{},"尚未创建","，所以不能访问 this，不过，可以通过给 ",[24,19826,19770],{}," 传一个回调函数来访问最终的组件实例。 ",[21,19829,19830],{},[24,19831,19832],{},"beforeRouteUpdate","：在当前路由改变，但",[21,19835,19836],{},"该组件被复用时","调用。例如，对于一个动态路由 ",[24,19839,19840],{},"\u002Ffoo\u002F:id","，从 ",[24,19843,19844],{},"\u002Ffoo\u002F1"," 导航到 ",[24,19847,19848],{},"\u002Ffoo\u002F2"," 时会触发。",[21,19851,19852],{},[24,19853,19854],{},"beforeRouteLeave",[21,19856,19857],{},"离开","该组件对应的路由时调用。通常用于",[21,19860,19861],{},"防止用户在未保存更改时离开页面","，可以弹出确认框。",[18,19864,19865,19866,19869,19870,1329,19873,19876],{},"在 Vue 3 的 ",[24,19867,19868],{},"\u003Cscript setup>"," 中，组件内守卫通过 ",[24,19871,19872],{},"onBeforeRouteUpdate",[24,19874,19875],{},"onBeforeRouteLeave"," 这两个函数来使用",[18,19878,19879,19880,19886,19887,19889],{},"在nuxt中，导航钩子的实现被称为路由中间件，提供了比原生vue-router守卫更好的开发体验，它最大的特点是",[21,19881,19882,19883,2773],{},"去掉了 ",[24,19884,19885],{},"next()","，改用",[21,19888,11697],{},"来控制导航流程。可以做到发行：返回nothing；重定向：返回navigationTo('\u002Fpath')；终止导航：返回abortNavigation()等。",[18,19891,19892,19893,19896],{},"可以写在页面组件内，仅对当前页面生效，通过definePageMeta来定义；可以放在app\u002Fmiddleware目录里，在多个页面复用，然后在页面中通过 ",[24,19894,19895],{},"definePageMeta({ middleware: 'auth' })"," 引入。全局中间件同样放在middleware目录里，文件名需要以.global.ts结尾，会每次路由变化的时候自动执行",[96,19898,19901],{"className":19899,"code":19900,"language":392},[390],"\u003Cscript setup lang=\"ts\">\ndefinePageMeta({\n  middleware: [\n    function (to, from) {\n      \u002F\u002F 仅对当前页面生效的钩子逻辑\n    }\n  ]\n})\n\u003C\u002Fscript>\n\n\u002F\u002F app\u002Fmiddleware\u002Fauth.ts\nexport default defineNuxtRouteMiddleware((to, from) => {\n  if (!useCookie('token').value) {\n    return navigateTo('\u002Flogin')\n  }\n})\n\n\u002F\u002F app\u002Fmiddleware\u002Fanalytics.global.ts\nexport default defineNuxtRouteMiddleware((to, from) => {\n  \u002F\u002F 每次导航都会执行\n})\n",[24,19902,19900],{"__ignoreMap":101},[18,19904,19905,19906,19909,19910,19912,19913,19916,19917,19919,19920,19923,19924,19927],{},"要说明的是，如果你项目里有 ",[24,19907,19908],{},"app\u002Fpages\u002F"," 目录，Nuxt 底层依然会基于 ",[24,19911,19088],{}," 构建路由实例，",[24,19914,19915],{},"useRouter()"," 返回的也是完整的 Router 实例，它依然提供 ",[24,19918,19743],{}," 等原生守卫方法。但官方明确建议：",[21,19921,19922],{},"绝大多数导航拦截需求都应优先使用路由中间件","，而不是直接调用 ",[24,19925,19926],{},"router.beforeEach","，因为中间件在 SSR 处理、重定向语义和开发体验上都做了更好的封装",[14,19929,19930],{"id":19930},"v-model双向绑定原理",[18,19932,19933],{},"v-model是vue语法糖，本质是:value和@input组合，这里要讲到模板编译和运行时绑定。",[18,19935,19936,19939,19940,19943,19945,19946,19949,19950,19953],{},[24,19937,19938],{},"\u003Cinput v-model=\"msg\">","在编译之后会等价于\n",[24,19941,19942],{},"\u003Cinput :value=\"msg\" @input=\"msg = $event.target.value\">",[8433,19944],{},"=\"msg\"是数据->视图的表现，@input是视图->数据，每次input变化的时候就会把新的值写回msg。",[24,19947,19948],{},"$event"," 是原生 DOM 事件对象，",[24,19951,19952],{},"$event.target.value"," 是输入框的当前值。",[18,19955,19956,19957],{},"Vue 会根据元素类型，自动选",[21,19958,19959],{},"合适的 prop 和事件",[835,19961,19962,19975],{},[838,19963,19964],{},[841,19965,19966,19969,19972],{},[844,19967,19968],{},"元素",[844,19970,19971],{},"绑定的 prop",[844,19973,19974],{},"监听的事件",[854,19976,19977,19992,20008,20023,20038,20053],{},[841,19978,19979,19984,19988],{},[859,19980,19981],{},[24,19982,19983],{},"\u003Cinput type=\"text\">",[859,19985,19986],{},[24,19987,8433],{},[859,19989,19990],{},[24,19991,1044],{},[841,19993,19994,19999,20003],{},[859,19995,19996],{},[24,19997,19998],{},"\u003Cinput type=\"checkbox\">",[859,20000,20001],{},[24,20002,1476],{},[859,20004,20005],{},[24,20006,20007],{},"change",[841,20009,20010,20015,20019],{},[859,20011,20012],{},[24,20013,20014],{},"\u003Cinput type=\"radio\">",[859,20016,20017],{},[24,20018,1476],{},[859,20020,20021],{},[24,20022,20007],{},[841,20024,20025,20030,20034],{},[859,20026,20027],{},[24,20028,20029],{},"\u003Cselect>",[859,20031,20032],{},[24,20033,8433],{},[859,20035,20036],{},[24,20037,20007],{},[841,20039,20040,20045,20049],{},[859,20041,20042],{},[24,20043,20044],{},"\u003Ctextarea>",[859,20046,20047],{},[24,20048,8433],{},[859,20050,20051],{},[24,20052,1044],{},[841,20054,20055,20058,20060],{},[859,20056,20057],{},"比如说",[859,20059],{},[859,20061],{},[96,20063,20066],{"className":20064,"code":20065,"language":392},[390],"\u003Cinput type=\"checkbox\" v-model=\"checked\">\n展开就是：\n\u003Cinput\n    type=\"checkbox\"\n    :checked=\"checked\"\n    @change=\"checked = $event.target.checked\"\n>\nSELECT:\n\u003Cselect v-model=\"selected\">\n    \u003Coption value=\"a\">A\u003C\u002Foption>\n    \u003Coption value=\"b\">B\u003C\u002Foption>\n\u003C\u002Fselect>\n展开就是：\n\u003Cselect\n    :value=\"selected\"\n    @change=\"selected = $event.target.value\"\n>\n",[24,20067,20065],{"__ignoreMap":101},[18,20069,20070,20073,20074,20077],{},[24,20071,20072],{},"v-model"," 支持几个修饰符，改变行为，在v-model之后加上.lazy，就把input事件改为change事件，失焦或者回车才会更新。加上.number，会自动把输入转成数字 ",[24,20075,20076],{},"\u003Cinput v-model.number=\"age\" type=\"number\">","。.trim会自动去掉收尾空格。",[18,20079,20080,20081,20084,20085,20088,20089],{},"@input是vue模板里的事件绑定语法糖，等价于v-on:input，不是原生DOM属性。 ",[24,20082,20083],{},"\u003Cinput @input=\"handler\">"," 展开就是 ",[24,20086,20087],{},"\u003Cinput v-on:input=\"handler\">"," ，",[21,20090,20091,20092,91],{},"它编译后不是 DOM 属性，而是 ",[24,20093,20094],{},"addEventListener('input', handler)",[18,20096,20097],{},"我们知道vue模板最终会编译为render方法，@input会编译为onInput这个prop，大致如下：",[96,20099,20101],{"className":559,"code":20100,"language":561,"meta":101,"style":101},"h('input', {\n    value: msg,\n    onInput: (e) => { msg = e.target.value; }\n})\n",[24,20102,20103,20113,20118,20139],{"__ignoreMap":101},[105,20104,20105,20107,20109,20111],{"class":107,"line":108},[105,20106,5129],{"class":241},[105,20108,321],{"class":115},[105,20110,1508],{"class":248},[105,20112,2799],{"class":115},[105,20114,20115],{"class":107,"line":119},[105,20116,20117],{"class":115},"    value: msg,\n",[105,20119,20120,20123,20125,20127,20129,20131,20134,20136],{"class":107,"line":133},[105,20121,20122],{"class":241},"    onInput",[105,20124,2830],{"class":115},[105,20126,3297],{"class":122},[105,20128,2836],{"class":115},[105,20130,723],{"class":111},[105,20132,20133],{"class":115}," { msg ",[105,20135,245],{"class":111},[105,20137,20138],{"class":115}," e.target.value; }\n",[105,20140,20141],{"class":107,"line":148},[105,20142,2888],{"class":115},[18,20144,20145],{},"onInput不是DOM属性，是vue渲染函数里的一个特殊prop，vue运行时识别到on开头的prop之后会用addEventListener来绑定，就和onClick一样。而$event是DOM事件对象，组件事件 $event是emit的参数。",[18,20147,20148],{},"然后这个vnode对象大致为",[96,20150,20153],{"className":20151,"code":20152,"language":392},[390],"{\n    type: 'input',           \u002F\u002F 标签名 或 组件\n    props: { onInput: fn },  \u002F\u002F 属性\u002F事件\n    children: null,\n    key: null,\n    el: null                 \u002F\u002F 真实 DOM，初始为 null\n}\n",[24,20154,20152],{"__ignoreMap":101},[18,20156,20157],{},"经过@vue\u002Fruntime-core里的patch之后，vnode变成dom：",[96,20159,20161],{"className":559,"code":20160,"language":561,"meta":101,"style":101},"\u002F\u002F 1. 创建\nconst el = document.createElement('input');\n\n\u002F\u002F 2. 设置 value（DOM property，不是 setAttribute）\nel.value = msg;\n\n\u002F\u002F 3. 绑定事件（addEventListener）\nel.addEventListener('input', (e) => {\n    msg = e.target.value;\n});\n\n\u002F\u002F 4. 插入\ncontainer.appendChild(el);\n",[24,20162,20163,20168,20188,20192,20197,20207,20211,20216,20237,20247,20251,20255,20260],{"__ignoreMap":101},[105,20164,20165],{"class":107,"line":108},[105,20166,20167],{"class":630},"\u002F\u002F 1. 创建\n",[105,20169,20170,20172,20175,20177,20179,20182,20184,20186],{"class":107,"line":119},[105,20171,568],{"class":111},[105,20173,20174],{"class":129}," el",[105,20176,574],{"class":111},[105,20178,1500],{"class":115},[105,20180,20181],{"class":241},"createElement",[105,20183,321],{"class":115},[105,20185,1508],{"class":248},[105,20187,327],{"class":115},[105,20189,20190],{"class":107,"line":133},[105,20191,307],{"emptyLinePlaceholder":306},[105,20193,20194],{"class":107,"line":148},[105,20195,20196],{"class":630},"\u002F\u002F 2. 设置 value（DOM property，不是 setAttribute）\n",[105,20198,20199,20202,20204],{"class":107,"line":163},[105,20200,20201],{"class":115},"el.value ",[105,20203,245],{"class":111},[105,20205,20206],{"class":115}," msg;\n",[105,20208,20209],{"class":107,"line":179},[105,20210,307],{"emptyLinePlaceholder":306},[105,20212,20213],{"class":107,"line":194},[105,20214,20215],{"class":630},"\u002F\u002F 3. 绑定事件（addEventListener）\n",[105,20217,20218,20221,20223,20225,20227,20229,20231,20233,20235],{"class":107,"line":209},[105,20219,20220],{"class":115},"el.",[105,20222,3286],{"class":241},[105,20224,321],{"class":115},[105,20226,1508],{"class":248},[105,20228,3294],{"class":115},[105,20230,3297],{"class":122},[105,20232,2836],{"class":115},[105,20234,723],{"class":111},[105,20236,116],{"class":115},[105,20238,20239,20242,20244],{"class":107,"line":526},[105,20240,20241],{"class":115},"    msg ",[105,20243,245],{"class":111},[105,20245,20246],{"class":115}," e.target.value;\n",[105,20248,20249],{"class":107,"line":531},[105,20250,3964],{"class":115},[105,20252,20253],{"class":107,"line":668},[105,20254,307],{"emptyLinePlaceholder":306},[105,20256,20257],{"class":107,"line":673},[105,20258,20259],{"class":630},"\u002F\u002F 4. 插入\n",[105,20261,20262,20265,20267],{"class":107,"line":679},[105,20263,20264],{"class":115},"container.",[105,20266,3570],{"class":241},[105,20268,20269],{"class":115},"(el);\n",[18,20271,20272],{},"从数据->视图的实现是:value是吧，数据变化的时候vue自动重新执行渲染函数，生成新的vnode，和旧的vnode进行对比，将diff应用到真实dom中。",[18,20274,20275,20277],{},[8433,20276],{},"之所以可以绑定到ref和自动解包，运用的是编译器的转换。:value = \"msg\"编译后不是value:msg，而是value:msg.value。",[96,20279,20281],{"className":559,"code":20280,"language":561,"meta":101,"style":101},"\u003Cscript setup>\nimport { ref } from 'vue';\nconst msg = ref('hello');\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n    \u003Cinput :value=\"msg\">\n\u003C\u002Ftemplate>\n\n编译后大致是：\n\nimport { ref, unref } from 'vue';\n\nconst msg = ref('hello');\n\nfunction render() {\n    return h('input', {\n        value: unref(msg)   \u002F\u002F ← 关键：编译器加了 unref\n    });\n}\n\nfunction unref(r) {\n    return isRef(r) ? r.value : r;\n}\n",[24,20282,20283,20293,20298,20303,20311,20315,20323,20332,20337,20341,20346,20350,20363,20367,20379,20383,20392,20405,20419,20424,20428,20432,20441,20462],{"__ignoreMap":101},[105,20284,20285,20287,20289,20291],{"class":107,"line":108},[105,20286,234],{"class":115},[105,20288,2956],{"class":237},[105,20290,2959],{"class":241},[105,20292,257],{"class":115},[105,20294,20295],{"class":107,"line":119},[105,20296,20297],{"class":115},"import { ref } from 'vue';\n",[105,20299,20300],{"class":107,"line":133},[105,20301,20302],{"class":115},"const msg = ref('hello');\n",[105,20304,20305,20307,20309],{"class":107,"line":148},[105,20306,2992],{"class":115},[105,20308,2956],{"class":237},[105,20310,257],{"class":115},[105,20312,20313],{"class":107,"line":163},[105,20314,307],{"emptyLinePlaceholder":306},[105,20316,20317,20319,20321],{"class":107,"line":179},[105,20318,234],{"class":115},[105,20320,3658],{"class":237},[105,20322,257],{"class":115},[105,20324,20325,20327,20329],{"class":107,"line":194},[105,20326,3713],{"class":115},[105,20328,1044],{"class":237},[105,20330,20331],{"class":19544}," :value=\"msg\">\n",[105,20333,20334],{"class":107,"line":209},[105,20335,20336],{"class":19544},"\u003C\u002Ftemplate>\n",[105,20338,20339],{"class":107,"line":526},[105,20340,307],{"emptyLinePlaceholder":306},[105,20342,20343],{"class":107,"line":531},[105,20344,20345],{"class":19544},"编译后大致是：\n",[105,20347,20348],{"class":107,"line":668},[105,20349,307],{"emptyLinePlaceholder":306},[105,20351,20352,20354,20357,20359,20361],{"class":107,"line":673},[105,20353,3885],{"class":241},[105,20355,20356],{"class":115}," { ref, unref } ",[105,20358,3891],{"class":241},[105,20360,3894],{"class":248},[105,20362,145],{"class":19544},[105,20364,20365],{"class":107,"line":679},[105,20366,307],{"emptyLinePlaceholder":306},[105,20368,20369,20371,20374,20376],{"class":107,"line":689},[105,20370,568],{"class":241},[105,20372,20373],{"class":241}," msg",[105,20375,574],{"class":19544},[105,20377,20378],{"class":19544}," ref('hello');\n",[105,20380,20381],{"class":107,"line":699},[105,20382,307],{"emptyLinePlaceholder":306},[105,20384,20385,20387,20390],{"class":107,"line":704},[105,20386,8113],{"class":241},[105,20388,20389],{"class":19544}," render()",[105,20391,116],{"class":115},[105,20393,20394,20397,20399,20401,20403],{"class":107,"line":710},[105,20395,20396],{"class":115},"    return ",[105,20398,5129],{"class":241},[105,20400,321],{"class":115},[105,20402,1508],{"class":248},[105,20404,2799],{"class":115},[105,20406,20407,20410,20413,20416],{"class":107,"line":1136},[105,20408,20409],{"class":115},"        value: ",[105,20411,20412],{"class":241},"unref",[105,20414,20415],{"class":115},"(msg)   ",[105,20417,20418],{"class":630},"\u002F\u002F ← 关键：编译器加了 unref\n",[105,20420,20421],{"class":107,"line":1149},[105,20422,20423],{"class":115},"    });\n",[105,20425,20426],{"class":107,"line":3837},[105,20427,212],{"class":115},[105,20429,20430],{"class":107,"line":3847},[105,20431,307],{"emptyLinePlaceholder":306},[105,20433,20434,20436,20439],{"class":107,"line":3857},[105,20435,8113],{"class":241},[105,20437,20438],{"class":19544}," unref(r)",[105,20440,116],{"class":115},[105,20442,20443,20445,20448,20451,20454,20457,20459],{"class":107,"line":3866},[105,20444,20396],{"class":115},[105,20446,20447],{"class":241},"isRef",[105,20449,20450],{"class":115},"(r) ",[105,20452,20453],{"class":111},"?",[105,20455,20456],{"class":115}," r.value ",[105,20458,4999],{"class":111},[105,20460,20461],{"class":115}," r;\n",[105,20463,20464],{"class":107,"line":3871},[105,20465,212],{"class":115},[18,20467,20468],{},"这个里面主要就看sfc编译器实现了，有兴趣可以看看编译器的源码。",[14,20470,20472],{"id":20471},"mvvm-mvc","mvvm mVC",[18,20474,20475],{},"mvvm和mvc是个经常提起的话题，作为一个合格的客户端开发工程师，那必须熟悉。mvvm原来就三个分离： model、view、viewmodel。",[18,20477,20478],{},"view就对应vue的模板，描述样式。viewmodel要注意一下，原本含义是view和model之间的桥梁，可以把model的数据暴露给view，view的操作也可以传递给model。在vue里，composables是一部分，但是viewmodel还要其他几个组成，比如说script setup里使用的ref reactive 或者watch操作。 script setup整体那就是viewmodel，它把 Model 的数据\"包装\"成响应式，暴露给模板。model是数据+业务逻辑，所以组成就应该是user post实体这类数据结构+ 业务逻辑比如计算、校验、转换和数据操作比如crud、持久化等。",[18,20480,20481],{},"后端中MVC用的比较多，view就是模板，中间层就是Controller，视图开发中mvc有些不一样，在vue\u002Freact这类现代视图开发中mvc的分层思想虽然在，但是控制器手动更新view被淘汰了。\n经典mvc:",[96,20483,20486],{"className":20484,"code":20485,"language":392},[390],"用户操作 → Controller → 改 Model → Model 通知 View → View 更新\n\n\u002F\u002F 经典 MVC 的写法\nclass UserController {\n    handleClick() {\n        const user = userModel.getUser();\n        userView.render(user);   \u002F\u002F 手动调 View 的 render\n    }\n}\n",[24,20487,20485],{"__ignoreMap":101},[18,20489,20490],{},"现代后端也发展了很多，用asp.net来说，2009支持了asp.net mvc，使用mvc来分层、可测试、restful。从2021的.net6开始，又支持了minimal api，极简风格，用于几行代码的服务。2022+之后又增强到可以使用路由组、过滤器、AOT。",[10,20492,20493],{"id":20493},"计算机网络",[14,20495,20497],{"id":20496},"restful-api和graphql","RESTful API和GraphQL",[18,20499,20500,20503],{},[21,20501,20502],{},"RESTful API 和 GraphQL 是两种对立的 API 设计范式"," ，restfulapi的哲学是一切皆资源，用url标识资源：",[96,20505,20508],{"className":20506,"code":20507,"language":392},[390],"\u002Fusers          → 用户集合\n\u002Fusers\u002F123      → 单个用户\n\u002Fusers\u002F123\u002Fposts → 用户的文章\n",[24,20509,20507],{"__ignoreMap":101},[18,20511,20512],{},"http方法表意动词操作资源，比如GET方法读取 GET \u002Fuser\u002F123，POST创建，POST \u002Fusers，PUT表全量更新 PATCH部分更新 DELETE删除。",[18,20514,20515,91,20518,20521],{},[21,20516,20517],{},"方法 + URL = 完整语义",[24,20519,20520],{},"DELETE \u002Fusers\u002F123"," 一看就知道\"删除 123 号用户\"。",[18,20523,20524],{},"每个请求都是独立的，服务端不会保存客户端状态，有统一接口，状态码表意。",[18,20526,20527],{},"REST好用是好用，也有几个缺点，一个是过渡获取，客户端要的字段少服务器返回一堆，比如你可能只要name，但是一次性拿到的是全部（其实和这个要后端端点的设计），一个页面需要多个资源可能要多个请求。后端要版本管理，加字段可能破坏兼容性。",[18,20529,20530],{},"GraphQL核心思想是描述客户端需求之后服务端再返回这些。只需要单一端点：",[96,20532,20535],{"className":20533,"code":20534,"language":392},[390],"POST \u002Fgraphql\n",[24,20536,20534],{"__ignoreMap":101},[18,20538,20539],{},"客户端指定好字段就行：",[96,20541,20545],{"className":20542,"code":20543,"language":20544,"meta":101,"style":101},"language-graphql shiki shiki-themes github-light github-dark","query {\n    user(id: 123) {\n        name\n        posts {\n            title\n        }\n    }\n}\n","graphql",[24,20546,20547,20552,20557,20562,20567,20572,20576,20580],{"__ignoreMap":101},[105,20548,20549],{"class":107,"line":108},[105,20550,20551],{},"query {\n",[105,20553,20554],{"class":107,"line":119},[105,20555,20556],{},"    user(id: 123) {\n",[105,20558,20559],{"class":107,"line":133},[105,20560,20561],{},"        name\n",[105,20563,20564],{"class":107,"line":148},[105,20565,20566],{},"        posts {\n",[105,20568,20569],{"class":107,"line":163},[105,20570,20571],{},"            title\n",[105,20573,20574],{"class":107,"line":179},[105,20575,12475],{},[105,20577,20578],{"class":107,"line":194},[105,20579,5143],{},[105,20581,20582],{"class":107,"line":209},[105,20583,212],{},[18,20585,20586],{},"直接返回",[96,20588,20591],{"className":20589,"code":20590,"language":392},[390],"{\n    \"data\": {\n        \"user\": {\n            \"name\": \"Alice\",\n            \"posts\": [\n                { \"title\": \"文章 1\" },\n                { \"title\": \"文章 2\" }\n            ]\n        }\n    }\n}\n",[24,20592,20590],{"__ignoreMap":101},[18,20594,20595],{},[21,20596,20597,20598,1329,20600,20603],{},"只要 ",[24,20599,4996],{},[24,20601,20602],{},"posts.title","，就只返回这些。",[18,20605,20606],{},"缺点是缓存复杂，rest可以用HTTP天然缓存，GRAPHQL要客户端缓存，查询有复杂度，嵌套查询可能触发大量数据库查询。",[18,20608,20609,9829],{},[21,20610,20611],{},"很多项目 REST + GraphQL 并存",[442,20613,20614,20617,20620],{},[448,20615,20616],{},"简单接口用 REST",[448,20618,20619],{},"复杂查询用 GraphQL",[448,20621,20622],{},"文件上传用 Request",[18,20624,20625],{},"除此外，还有好几种其他的： gRPC、tRPC、WS、SOAP。",[14,20627,20628],{"id":20628},"请求头里的缓存",[18,20630,20631],{},"这里有个容易搞错的误区，我们知道http请求头里Cache-Control、ETag等控制的是浏览器HTTP缓存，但不是CacheStorage，两者是完全独立的存储系统。",[18,20633,20634],{},"HTTP由浏览器网络层管理，CacheStorage是通过sw管理的。http缓存会由浏览器自动写入完成，读取也是浏览器自动的，存储在磁盘\u002F内存。你可以在devtools里的disk cache里显示。",[18,20636,20637],{},"我们常见的fetch操作会默认走http缓存，如果http缓存有且未过期，就会直接返回缓存，而不经过网络请求。",[18,20639,20640],{},"如果要让资源进入cacheStorage里面的话，需要手动注册sw，在install或者fetch里显示设置cache.put()\u002Fcache.addAll()",[96,20642,20644],{"className":559,"code":20643,"language":561,"meta":101,"style":101},"\u002F\u002F install 时预缓存\nself.addEventListener('install', (event) => {\n    event.waitUntil(\n        caches.open('v1').then(cache => \n            cache.addAll(['\u002Fstyle.css', '\u002Fapp.js'])   \u002F\u002F 显式写入\n        )\n    );\n});\n",[24,20645,20646,20651,20671,20679,20702,20725,20730,20734],{"__ignoreMap":101},[105,20647,20648],{"class":107,"line":108},[105,20649,20650],{"class":630},"\u002F\u002F install 时预缓存\n",[105,20652,20653,20655,20657,20659,20661,20663,20665,20667,20669],{"class":107,"line":119},[105,20654,5580],{"class":115},[105,20656,3286],{"class":241},[105,20658,321],{"class":115},[105,20660,5587],{"class":248},[105,20662,3294],{"class":115},[105,20664,5592],{"class":122},[105,20666,2836],{"class":115},[105,20668,723],{"class":111},[105,20670,116],{"class":115},[105,20672,20673,20675,20677],{"class":107,"line":133},[105,20674,5603],{"class":115},[105,20676,5606],{"class":241},[105,20678,583],{"class":115},[105,20680,20681,20683,20685,20687,20689,20691,20693,20695,20697,20699],{"class":107,"line":148},[105,20682,5613],{"class":115},[105,20684,5616],{"class":241},[105,20686,321],{"class":115},[105,20688,5621],{"class":248},[105,20690,5624],{"class":115},[105,20692,5512],{"class":241},[105,20694,321],{"class":115},[105,20696,5631],{"class":122},[105,20698,5132],{"class":111},[105,20700,20701],{"class":115}," \n",[105,20703,20704,20707,20709,20711,20714,20716,20719,20722],{"class":107,"line":163},[105,20705,20706],{"class":115},"            cache.",[105,20708,5646],{"class":241},[105,20710,6454],{"class":115},[105,20712,20713],{"class":248},"'\u002Fstyle.css'",[105,20715,286],{"class":115},[105,20717,20718],{"class":248},"'\u002Fapp.js'",[105,20720,20721],{"class":115},"])   ",[105,20723,20724],{"class":630},"\u002F\u002F 显式写入\n",[105,20726,20727],{"class":107,"line":179},[105,20728,20729],{"class":115},"        )\n",[105,20731,20732],{"class":107,"line":194},[105,20733,5690],{"class":115},[105,20735,20736],{"class":107,"line":209},[105,20737,3964],{"class":115},[18,20739,20740],{},"至于性能上，数据读取上CacheStorage比http缓存慢，因为多了一层sw的启动和js执行开销。我们做pwa应用的时候可能会使用到。",[14,20742,20743],{"id":20743},"http",[18,20745,20746],{},"HTTP是工作在应用层的协议，用于在客户端比如浏览器和服务器之间传输数据，是web的基石，采用的是请求-响应模型，无状态、可扩展。",[18,20748,20749],{},"状态码来说：",[18,20751,20752],{},"1xx比如101 Switching Protocols。2xx开头是成功的，比如200 OK 201 Created、204 No Content。3xx开头为重定向，301是永久重定向，302是临时的。4xx开头是客户端错误，5开头的是服务端错误。",[18,20754,20755],{},"常见请求头\u002F响应头",[835,20757,20758,20767],{},[838,20759,20760],{},[841,20761,20762,20765],{},[844,20763,20764],{},"头部",[844,20766,2223],{},[854,20768,20769,20779,20789,20799,20809,20819,20832,20842,20853,20863,20873],{},[841,20770,20771,20776],{},[859,20772,20773],{},[24,20774,20775],{},"Host",[859,20777,20778],{},"目标主机（HTTP\u002F1.1 必需）",[841,20780,20781,20786],{},[859,20782,20783],{},[24,20784,20785],{},"Content-Type",[859,20787,20788],{},"报文体的 MIME 类型",[841,20790,20791,20796],{},[859,20792,20793],{},[24,20794,20795],{},"Content-Length",[859,20797,20798],{},"报文体长度",[841,20800,20801,20806],{},[859,20802,20803],{},[24,20804,20805],{},"Accept",[859,20807,20808],{},"客户端可接受的类型",[841,20810,20811,20816],{},[859,20812,20813],{},[24,20814,20815],{},"Authorization",[859,20817,20818],{},"认证凭证（如 Bearer Token）",[841,20820,20821,20829],{},[859,20822,20823,1867,20826],{},[24,20824,20825],{},"Cookie",[24,20827,20828],{},"Set-Cookie",[859,20830,20831],{},"会话状态管理",[841,20833,20834,20839],{},[859,20835,20836],{},[24,20837,20838],{},"Cache-Control",[859,20840,20841],{},"缓存策略",[841,20843,20844,20850],{},[859,20845,20846,1867,20848],{},[24,20847,7654],{},[24,20849,7657],{},[859,20851,20852],{},"协商缓存",[841,20854,20855,20861],{},[859,20856,20857,1867,20859],{},[24,20858,7661],{},[24,20860,7664],{},[859,20862,20852],{},[841,20864,20865,20870],{},[859,20866,20867],{},[24,20868,20869],{},"Access-Control-Allow-Origin",[859,20871,20872],{},"CORS 跨域许可",[841,20874,20875,20878],{},[859,20876,20877],{},"浏览器同源策略限制跨域请求。CORS 通过响应头授权：",[859,20879],{},[96,20881,20884],{"className":20882,"code":20883,"language":392},[390],"Access-Control-Allow-Origin: https:\u002F\u002Fexample.com\nAccess-Control-Allow-Methods: GET, POST\nAccess-Control-Allow-Headers: Content-Type\nAccess-Control-Allow-Credentials: true\n",[24,20885,20883],{"__ignoreMap":101},[18,20887,20888,20891,20892,20895],{},[21,20889,20890],{},"预检请求","：非简单请求（如 PUT、自定义头）会先发 ",[24,20893,20894],{},"OPTIONS"," 请求确认。OPTIONS请求，cors预检请求的响应状态码通常是204 No Content。当使用了非简单方法比如PUT、DELETE、PATCH等的时候就会自动发送这个，或是使用了自定义请求头Authorization、X-Custom-Header也会。",[14,20897,20898],{"id":20898},"http的长连接",[18,20900,20901],{},"http长连接核心是复用一个tcp连接发送多个请求，避免每次都重新握手等。短连接是http\u002F1.0默认，每次都要经过tcp握手。长连接是http\u002F1.1默认，多个请求会复用一个tcp。但是http\u002F1.1会有队头阻塞问题，同一个连接上，前一个请求还没有响应完，后面的请求要等待。",[18,20903,20904],{},"http\u002F2的多路复用解决了这个问题：",[96,20906,20909],{"className":20907,"code":20908,"language":392},[390],"一个 TCP 连接\n    ├── Stream 1: 请求 A\n    ├── Stream 2: 请求 B\n    ├── Stream 3: 请求 C\n    └── 并发传输，互不阻塞\n",[24,20910,20908],{"__ignoreMap":101},[18,20912,20913],{},"通过一个连接并发多个请求，将请求\u002F响应拆成帧，交错传输和头部压缩的方式解决了，http\u002F2的长连接更加高效，一个连接搞定所有请求。",[18,20915,20916],{},"讲一下长连接和头部压缩：",[18,20918,20919],{},"长连接通过Connection: keep-alive声明，依靠tcp keep-alive传输层：",[96,20921,20924],{"className":20922,"code":20923,"language":392},[390],"setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));\n",[24,20925,20923],{"__ignoreMap":101},[18,20927,20928],{},"可以检测连接是否还或者，对端挂了，网络断了。应用层自己会发送心跳包，比如说30s一个，来保持链接活跃。如果说应用崩溃，连接不会通过FIN关闭的话，内核会替他关闭，可能是RST，也可能是FIN，比如说进程如果被SIGKILL 杀死，还有未发送的消息，他就会发RST，处理完了就会发FIN。",[18,20930,20931],{},"头部压缩式http\u002F2的核心优化，因为http\u002F1.1头部每次都会携带大量重复头部：",[96,20933,20936],{"className":20934,"code":20935,"language":392},[390],"GET \u002Findex.html\nHost: example.com\nUser-Agent: Mozilla\u002F5.0 (Windows NT 10.0; Win64; x64) ...\nAccept: text\u002Fhtml,application\u002Fxhtml+xml,...\nAccept-Language: zh-CN,zh;q=0.9\nAccept-Encoding: gzip, deflate, br\nCookie: session=abc123; theme=dark; ...\nConnection: keep-alive\n",[24,20937,20935],{"__ignoreMap":101},[18,20939,20940,20941,20944,20945,20948,20949,20952],{},"下一个请求也是一样。每个头部都会重复传递这些，显然会浪费带宽，",[21,20942,20943],{},"HTTP\u002F1.1 用 gzip 压缩 body，但不压缩头部","——头部一直是明文。",[21,20946,20947],{},"HTTP\u002F2 引入头部压缩","——",[21,20950,20951],{},"HPACK","。61个常见头部，然后给编号压缩，这就是静态表，对于连接期间动态维护的头部，使用索引来压缩，这就是静态表。对字符串使用哈夫曼编码，这样就可以让头部压缩很大一部分。",[14,20954,20955],{"id":20955},"https",[18,20957,20958],{},"我们都知道http是明文传输，数据裸奔，中间人可以直接看到密码、cookie等信息 还能篡改，https可以用tls解决这些问题，https分为四层：",[96,20960,20963],{"className":20961,"code":20962,"language":392},[390],"HTTP          ← 应用层\n  ↓\nTLS\u002FSSL       ← 安全层（加解密、认证、完整性）\n  ↓\nTCP           ← 传输层\n  ↓\nIP            ← 网络层\n",[24,20964,20962],{"__ignoreMap":101},[18,20966,20967,20968,20971,20972,20975],{},"https = http + tls，http报文会被tls加密之后走tcp传输。tls使用非对称加密，在握手阶段解决。公钥是公开的，私钥是服务器自己保留，握手阶段会协商出",[21,20969,20970],{},"会话秘钥","，对称加密使用一个秘钥加解密，在",[21,20973,20974],{},"非对称加密协商","的时候，这个秘钥会被协商出来。",[18,20977,20978],{},"我们用tls1.2版本解释：",[96,20980,20983],{"className":20981,"code":20982,"language":392},[390],"客户端                                服务器\n  │                                    │\n  │ ① Client Hello                    │\n  │   - 支持的 TLS 版本                 │\n  │   - 支持的加密套件                   │\n  │   - 客户端随机数（Random1）          │\n  │ ─────────────────────────────────> │\n  │                                    │\n  │ ② Server Hello                    │\n  │   - 选定的 TLS 版本                 │\n  │   - 选定的加密套件                   │\n  │   - 服务器随机数（Random2）          │\n  │ \u003C───────────────────────────────── │\n  │                                    │\n  │ ③ Certificate                     │\n  │   - 服务器证书（含公钥）             │\n  │ \u003C───────────────────────────────── │\n  │                                    │\n  │ ④ Server Key Exchange（可选）      │\n  │   - 密钥交换参数                    │\n  │ \u003C───────────────────────────────── │\n  │                                    │\n  │ ⑤ Server Hello Done               │\n  │ \u003C───────────────────────────────── │\n  │                                    │\n  │ ⑥ 客户端验证证书                    │\n  │   - 检查证书是否可信                 │\n  │   - 检查域名是否匹配                 │\n  │   - 检查是否过期                    │\n  │                                    │\n  │ ⑦ Client Key Exchange             │\n  │   - 用服务器公钥加密\"预主密钥\"       │\n  │ ─────────────────────────────────> │\n  │                                    │\n  │ ⑧ Change Cipher Spec              │\n  │   - 通知\"后续用协商的密钥加密\"       │\n  │ ─────────────────────────────────> │\n  │                                    │\n  │ ⑨ Finished                        │\n  │   - 加密的握手摘要                  │\n  │ ─────────────────────────────────> │\n  │                                    │\n  │ ⑩ Change Cipher Spec              │\n  │ \u003C───────────────────────────────── │\n  │                                    │\n  │ ⑪ Finished                        │\n  │ \u003C───────────────────────────────── │\n  │                                    │\n  │ ⑫ 开始加密通信（对称加密）           │\n  │ \u003C════════════════════════════════> │\n",[24,20984,20982],{"__ignoreMap":101},[18,20986,20987,20988,20991,20992,20995],{},"客户端会检查证书链，证书是否是可信CA签发，浏览器会内置CA列表。证书里面的域名是否匹配访问的域名，以及是否过期。客户端会生成",[21,20989,20990],{},"预主秘钥","，用于服务器公钥加密，发送给服务器，服务器用",[21,20993,20994],{},"私钥解密","，拿到预主密钥。",[96,20997,21000],{"className":20998,"code":20999,"language":392},[390],"主密钥 = PRF(预主密钥, \"master secret\", Random1 + Random2)\n",[24,21001,20999],{"__ignoreMap":101},[18,21003,21004],{},"主密钥用来生成会话秘钥，也就是对称加密的秘钥，这样握手就完成了。",[14,21006,21007],{"id":21007},"tcp的可靠",[18,21009,21010],{},"tcp工作在不可靠的IP层之上，它之所以可靠是因为依靠 序列号、ACK、超时重传、快速重传、校验和、连接管理。",[18,21012,21013],{},"传输过程中每个字节都有自己的编号：",[96,21015,21018],{"className":21016,"code":21017,"language":392},[390],"发送方：字节 1-1000 是第 1 段，1001-2000 是第 2 段\n接收方：收到后按序号重组，乱序也能还原\n",[24,21019,21017],{"__ignoreMap":101},[18,21021,21022],{},"这样重复的段会按照序号丢弃，乱序到达之后会按照序号重排，确认应答后告诉对方收到了哪些。接收方收到数据之后会回复一个ACK：",[96,21024,21027],{"className":21025,"code":21026,"language":392},[390],"发送方 → [SEQ=1, 1000字节] → 接收方\n发送方 ← [ACK=1001]        ← 接收方\n",[24,21028,21026],{"__ignoreMap":101},[18,21030,21031,21032,21035],{},"ACK=1001 意思是\"1000 及之前的字节我都收到了，下一个期望 1001。然后这样累计确认即可。发送发发完数据之后会启动定时器，接着等待ACK，如果没有就会重传SEQ=1， ",[21,21033,21034],{},"超时时间（RTO）动态计算","：根据 RTT（往返时间）动态调整。快速重传不需要等待超时，收到3个重复ACK就重传。",[96,21037,21040],{"className":21038,"code":21039,"language":392},[390],"发送 [1][2][3][4][5]\n接收方收到 [1][3][4][5]，缺 [2]\n接收方回 ACK=2（重复 3 次）\n发送方收到 3 个 ACK=2 → 立即重传 [2]\n",[24,21041,21039],{"__ignoreMap":101},[18,21043,21044],{},"每个TCP都有校验和，用于检测数据是否损坏。连接管理让双方完成三次握手建立连接、四次挥手关闭，能够确保双方都准备好，确保数据都传输完成。",[18,21046,21047],{},"顺便讲解udp，udp是故意不做可靠性保证的，他把控制器交给了应用层，udp也有校验和，但是没有序列号、ack、重传、排序、去重、流量控制等等行为。",[14,21049,21050],{"id":21050},"tcp窗口",[18,21052,21053],{},"窗口是tcp的流量控制机制，接收方可以提醒发送方自己还能接受多少。接受窗口rwnd是接收方的剩余缓冲区大小：",[96,21055,21058],{"className":21056,"code":21057,"language":392},[390],"接收方缓冲区 64KB\n已用 16KB\n剩余 48KB → 告诉发送方\"窗口 = 48KB\"\n",[24,21059,21057],{"__ignoreMap":101},[18,21061,21062],{},"然后发送方就可以根据窗口大小决定能发多少，这是为了防止接收方被大量的流量淹没，有窗口才能让双方能够按照节奏来发送。",[18,21064,21065],{},"窗口为0的话，发送方就会停止发送，并启动零窗口探测定时器，询问接收方窗口是否开了，当接收方处理完之后窗口才会>0，然后通知发送方继续，如果接收方窗口开了但通知丢了，发送方靠探测恢复。",[18,21067,21068,21069],{},"并且有指标可以计算：  ",[21,21070,21071],{},"吞吐量 = 窗口大小 \u002F RTT",[96,21073,21076],{"className":21074,"code":21075,"language":392},[390],"窗口 64KB，RTT 100ms\n吞吐量 = 64KB \u002F 0.1s = 640KB\u002Fs ≈ 5Mbps\n\n窗口 64KB，RTT 10ms\n吞吐量 = 64KB \u002F 0.01s = 6.4MB\u002Fs ≈ 51Mbps\n",[24,21077,21075],{"__ignoreMap":101},[18,21079,21080],{},"RTT越大，窗口越重要，RTT越大的话，我们自然需要窗口越大，不然吞吐量上不去。",[18,21082,21083],{},"TCP头部窗口字段只有16位，也就是最大64KB，如果还不够，可以使用窗口缩放选项RFC 1323，能够把窗口扩大到8MB，现代TCP窗口都会启用窗口缩放。",[18,21085,21086,21087,21090,21091,21094],{},"tcp窗口分几个层次：内核自动管理、系统参数调整、应用层通过socket选项来间接影响。应用层",[21,21088,21089],{},"看不到也改不了"," TCP 窗口字段本身。窗口是内核根据",[21,21092,21093],{},"接收缓冲区剩余空间","自动计算的。",[18,21096,21097],{},"linux可以通过命令查看：",[96,21099,21102],{"className":21100,"code":21101,"language":392},[390],"# 看系统级参数\nsysctl net.ipv4.tcp_rmem\nsysctl net.ipv4.tcp_wmem\n\n# 输出示例：\n# net.ipv4.tcp_rmem = 4096 131072 6291456\n#                    min  default  max\n",[24,21103,21101],{"__ignoreMap":101},[14,21105,21107],{"id":21106},"三次握手四次挥手","三次握手、四次挥手",[18,21109,21110,21111,9365,21114,91],{},"TCP 的三次握手建立连接，四次挥手关闭连接。这两个过程的核心是",[21,21112,21113],{},"序列号同步",[21,21115,21116],{},"双向确认",[96,21118,21121],{"className":21119,"code":21120,"language":392},[390],"客户端                                服务器\n  │                                    │\n  │ ① SYN                              │\n  │   SEQ=x（客户端初始序列号）           │\n  │   SYN=1                            │\n  │ ─────────────────────────────────> │\n  │                                    │\n  │ ② SYN + ACK                        │\n  │   SEQ=y（服务器初始序列号）           │\n  │   ACK=x+1（确认收到 x）              │\n  │   SYN=1, ACK=1                     │\n  │ \u003C───────────────────────────────── │\n  │                                    │\n  │ ③ ACK                              │\n  │   SEQ=x+1                          │\n  │   ACK=y+1（确认收到 y）              │\n  │   ACK=1                            │\n  │ ─────────────────────────────────> │\n  │                                    │\n  │ 连接建立，开始传数据                  │\n",[24,21122,21120],{"__ignoreMap":101},[18,21124,21125],{},"第一次握手客户端进入SYN_SENT状态，发送SYN=1，SEQ=X。第二次服务器发送SYN=1 ACK=X+1  SEQ=Y,服务器进入SYN_RCVD。第三次握手客户端收到Y，连接建立，回复ACK=Y+1，双方进入ESTABLISHED。",[18,21127,21128,21129,21132],{},"初始序列号是随机的，是",[21,21130,21131],{},"防止历史连接的数据混入新连接","。当新连接的序列号和旧连接不同，旧包会被丢弃，ISN生成基于时钟+哈希防止被猜测。",[18,21134,21135],{},"四次挥手用于关闭连接：",[96,21137,21140],{"className":21138,"code":21139,"language":392},[390],"客户端                                服务器\n  │                                    │\n  │ ① FIN                              │\n  │   SEQ=u                            │\n  │   FIN=1                            │\n  │ ─────────────────────────────────> │\n  │                                    │\n  │ ② ACK                              │\n  │   SEQ=v                            │\n  │   ACK=u+1                          │\n  │   ACK=1                            │\n  │ \u003C───────────────────────────────── │\n  │                                    │\n  │   （服务器可能还在发数据）            │\n  │                                    │\n  │ ③ FIN                              │\n  │   SEQ=w                            │\n  │   ACK=u+1                          │\n  │   FIN=1, ACK=1                     │\n  │ \u003C───────────────────────────────── │\n  │                                    │\n  │ ④ ACK                              │\n  │   SEQ=u+1                          │\n  │   ACK=w+1                          │\n  │   ACK=1                            │\n  │ ─────────────────────────────────> │\n  │                                    │\n  │ 连接关闭                            │\n",[24,21141,21139],{"__ignoreMap":101},[18,21143,21144],{},"第一次挥手，客户端发送FIN=1 SEQ=U，客户端进入FIN_WAIT_1，第二次挥手服务端收到，发送ACK=U+1 SEQ=V，服务器进入CLOSE_WAIT，客户端进入FIN_WAIT_2，第三次挥手，服务器已经发完数据，发送FIN=1 ACK=1 SEQ=W ACK=U+1，服务器进入LAST_ACK状态，最后一次客户端收到FIN，发送SEQ=U+1 ACK=W+1，客户端进入TIME_WAIT状态，2MSL之后关闭。",[14,21146,21148],{"id":21147},"sse","SSE",[18,21150,21151],{},"全称叫做服务器推送事件，允许服务器单向、持续地向客户端推送文本数据的技术，基于HTTP协议，专门用于实现服务器到客户端的实时通信。",[18,21153,21154],{},"只有服务器能够主动推送给客户端，而不允许客户端通过同一连接反向发送。使用标准的HTTP请求，无需特殊协议或者端口，只支持UTF8文本，二进制数据需要编码比如base64,。浏览器内置了断线重连机制，默认约3s后重试，相比ws，实现更加简单，适合推送场景。",[18,21156,21157],{},"客户端发起请求的时候会通过EventSource API向服务器发起一个普通HTTP GET请求，服务器响应头设置Content-Type: text\u002Fevent\u002Fstream，之后就可以保持连接不关闭，服务器会按照SSE格式不断向客户端发送数据，客户端接受事件之后浏览器会解析数据流，出发对应的事件回调。",[96,21159,21161],{"className":3241,"code":21160,"language":3243,"meta":101,"style":101},"const eventSource = new EventSource('\u002Fapi\u002Fstream');\n\n\u002F\u002F 接收默认消息\neventSource.onmessage = (event) => {\n    console.log('收到消息:', event.data);\n};\n\n\u002F\u002F 接收自定义事件\neventSource.addEventListener('update', (event) => {\n    console.log('更新事件:', event.data);\n});\n\n\u002F\u002F 错误处理\neventSource.onerror = (err) => {\n    console.error('连接错误:', err);\n};\n\n\u002F\u002F 关闭连接\n\u002F\u002F eventSource.close();\n",[24,21162,21163,21184,21188,21193,21212,21225,21229,21233,21238,21259,21272,21276,21280,21285,21303,21317,21321,21325,21330],{"__ignoreMap":101},[105,21164,21165,21167,21170,21172,21174,21177,21179,21182],{"class":107,"line":108},[105,21166,568],{"class":111},[105,21168,21169],{"class":129}," eventSource",[105,21171,574],{"class":111},[105,21173,3478],{"class":111},[105,21175,21176],{"class":241}," EventSource",[105,21178,321],{"class":115},[105,21180,21181],{"class":248},"'\u002Fapi\u002Fstream'",[105,21183,327],{"class":115},[105,21185,21186],{"class":107,"line":119},[105,21187,307],{"emptyLinePlaceholder":306},[105,21189,21190],{"class":107,"line":133},[105,21191,21192],{"class":630},"\u002F\u002F 接收默认消息\n",[105,21194,21195,21198,21200,21202,21204,21206,21208,21210],{"class":107,"line":148},[105,21196,21197],{"class":115},"eventSource.",[105,21199,5104],{"class":241},[105,21201,574],{"class":111},[105,21203,4361],{"class":115},[105,21205,5592],{"class":122},[105,21207,2836],{"class":115},[105,21209,723],{"class":111},[105,21211,116],{"class":115},[105,21213,21214,21216,21218,21220,21223],{"class":107,"line":163},[105,21215,6662],{"class":115},[105,21217,639],{"class":241},[105,21219,321],{"class":115},[105,21221,21222],{"class":248},"'收到消息:'",[105,21224,6672],{"class":115},[105,21226,21227],{"class":107,"line":179},[105,21228,4441],{"class":115},[105,21230,21231],{"class":107,"line":194},[105,21232,307],{"emptyLinePlaceholder":306},[105,21234,21235],{"class":107,"line":209},[105,21236,21237],{"class":630},"\u002F\u002F 接收自定义事件\n",[105,21239,21240,21242,21244,21246,21249,21251,21253,21255,21257],{"class":107,"line":526},[105,21241,21197],{"class":115},[105,21243,3286],{"class":241},[105,21245,321],{"class":115},[105,21247,21248],{"class":248},"'update'",[105,21250,3294],{"class":115},[105,21252,5592],{"class":122},[105,21254,2836],{"class":115},[105,21256,723],{"class":111},[105,21258,116],{"class":115},[105,21260,21261,21263,21265,21267,21270],{"class":107,"line":531},[105,21262,6662],{"class":115},[105,21264,639],{"class":241},[105,21266,321],{"class":115},[105,21268,21269],{"class":248},"'更新事件:'",[105,21271,6672],{"class":115},[105,21273,21274],{"class":107,"line":668},[105,21275,3964],{"class":115},[105,21277,21278],{"class":107,"line":673},[105,21279,307],{"emptyLinePlaceholder":306},[105,21281,21282],{"class":107,"line":679},[105,21283,21284],{"class":630},"\u002F\u002F 错误处理\n",[105,21286,21287,21289,21291,21293,21295,21297,21299,21301],{"class":107,"line":689},[105,21288,21197],{"class":115},[105,21290,7138],{"class":241},[105,21292,574],{"class":111},[105,21294,4361],{"class":115},[105,21296,5544],{"class":122},[105,21298,2836],{"class":115},[105,21300,723],{"class":111},[105,21302,116],{"class":115},[105,21304,21305,21307,21309,21311,21314],{"class":107,"line":699},[105,21306,6662],{"class":115},[105,21308,7157],{"class":241},[105,21310,321],{"class":115},[105,21312,21313],{"class":248},"'连接错误:'",[105,21315,21316],{"class":115},", err);\n",[105,21318,21319],{"class":107,"line":704},[105,21320,4441],{"class":115},[105,21322,21323],{"class":107,"line":710},[105,21324,307],{"emptyLinePlaceholder":306},[105,21326,21327],{"class":107,"line":1136},[105,21328,21329],{"class":630},"\u002F\u002F 关闭连接\n",[105,21331,21332],{"class":107,"line":1149},[105,21333,21334],{"class":630},"\u002F\u002F eventSource.close();\n",[18,21336,21337],{},"响应头：",[96,21339,21343],{"className":21340,"code":21341,"language":21342,"meta":101,"style":101},"language-cpp shiki shiki-themes github-light github-dark","std::string get_sse_headers() {\n    response \u003C\u003C \"HTTP\u002F1.1 200 OK\\r\\n\"\n        \u003C\u003C \"Content-Type: text\u002Fevent-stream\\r\\n\"   \u002F\u002F 必需：SSE 的 MIME 类型\n        \u003C\u003C \"Cache-Control: no-cache\\r\\n\"           \u002F\u002F 禁止缓存\n        \u003C\u003C \"Connection: keep-alive\\r\\n\"            \u002F\u002F 保持长连接\n        \u003C\u003C \"Access-Control-Allow-Origin: *\\r\\n\"    \u002F\u002F CORS 跨域\n        \u003C\u003C \"\\r\\n\";\n}\n","cpp",[24,21344,21345,21350,21355,21360,21365,21370,21375,21380],{"__ignoreMap":101},[105,21346,21347],{"class":107,"line":108},[105,21348,21349],{},"std::string get_sse_headers() {\n",[105,21351,21352],{"class":107,"line":119},[105,21353,21354],{},"    response \u003C\u003C \"HTTP\u002F1.1 200 OK\\r\\n\"\n",[105,21356,21357],{"class":107,"line":133},[105,21358,21359],{},"        \u003C\u003C \"Content-Type: text\u002Fevent-stream\\r\\n\"   \u002F\u002F 必需：SSE 的 MIME 类型\n",[105,21361,21362],{"class":107,"line":148},[105,21363,21364],{},"        \u003C\u003C \"Cache-Control: no-cache\\r\\n\"           \u002F\u002F 禁止缓存\n",[105,21366,21367],{"class":107,"line":163},[105,21368,21369],{},"        \u003C\u003C \"Connection: keep-alive\\r\\n\"            \u002F\u002F 保持长连接\n",[105,21371,21372],{"class":107,"line":179},[105,21373,21374],{},"        \u003C\u003C \"Access-Control-Allow-Origin: *\\r\\n\"    \u002F\u002F CORS 跨域\n",[105,21376,21377],{"class":107,"line":194},[105,21378,21379],{},"        \u003C\u003C \"\\r\\n\";\n",[105,21381,21382],{"class":107,"line":209},[105,21383,212],{},[18,21385,21386,21389,21390,21393,21394,21397,21398,21401],{},[24,21387,21388],{},"Content-Type: text\u002Fevent-stream"," 是 SSE 的",[21,21391,21392],{},"标志性头部","，浏览器据此识别为事件流。 头部以 ",[24,21395,21396],{},"\\r\\n"," 分隔，最后用一个空行 ",[24,21399,21400],{},"\\r\\n\\r\\n"," 结束。",[96,21403,21405],{"className":21340,"code":21404,"language":21342,"meta":101,"style":101},"void static send_sse_event(SOCKET client, const std::string& event, const std::string data) {\n    std::string message = \"event: \" + event + \"\\r\\n\"\n        \"data: \" + data + \"\\r\\n\\r\\n\";\n    send(client, message.c_str(), message.size(), 0);\n}\n",[24,21406,21407,21412,21417,21422,21427],{"__ignoreMap":101},[105,21408,21409],{"class":107,"line":108},[105,21410,21411],{},"void static send_sse_event(SOCKET client, const std::string& event, const std::string data) {\n",[105,21413,21414],{"class":107,"line":119},[105,21415,21416],{},"    std::string message = \"event: \" + event + \"\\r\\n\"\n",[105,21418,21419],{"class":107,"line":133},[105,21420,21421],{},"        \"data: \" + data + \"\\r\\n\\r\\n\";\n",[105,21423,21424],{"class":107,"line":148},[105,21425,21426],{},"    send(client, message.c_str(), message.size(), 0);\n",[105,21428,21429],{"class":107,"line":163},[105,21430,212],{},[18,21432,21433],{},"这个是SSE中自定义事件的完整报文格式，由两行字短组成，用空行结束：",[96,21435,21438],{"className":21436,"code":21437,"language":392},[390],"event: heartbeat\\r\\n\ndata: ping count: 5\\r\\n\n\\r\\n\n",[24,21439,21437],{"__ignoreMap":101},[18,21441,21442],{},"浏览器侧：",[96,21444,21446],{"className":3241,"code":21445,"language":3243,"meta":101,"style":101},"const es = new EventSource('\u002Fapi\u002Fstream');\n\n\u002F\u002F 监听默认消息\nes.onmessage = (e) => {\n    console.log('默认消息:', e.data);\n};\n\n\u002F\u002F 监听 heartbeat 自定义事件\nes.addEventListener('heartbeat', (e) => {\n    console.log('心跳事件:', e.data);\n});\n",[24,21447,21448,21467,21471,21476,21495,21508,21512,21516,21521,21542,21555],{"__ignoreMap":101},[105,21449,21450,21452,21455,21457,21459,21461,21463,21465],{"class":107,"line":108},[105,21451,568],{"class":111},[105,21453,21454],{"class":129}," es",[105,21456,574],{"class":111},[105,21458,3478],{"class":111},[105,21460,21176],{"class":241},[105,21462,321],{"class":115},[105,21464,21181],{"class":248},[105,21466,327],{"class":115},[105,21468,21469],{"class":107,"line":119},[105,21470,307],{"emptyLinePlaceholder":306},[105,21472,21473],{"class":107,"line":133},[105,21474,21475],{"class":630},"\u002F\u002F 监听默认消息\n",[105,21477,21478,21481,21483,21485,21487,21489,21491,21493],{"class":107,"line":148},[105,21479,21480],{"class":115},"es.",[105,21482,5104],{"class":241},[105,21484,574],{"class":111},[105,21486,4361],{"class":115},[105,21488,3297],{"class":122},[105,21490,2836],{"class":115},[105,21492,723],{"class":111},[105,21494,116],{"class":115},[105,21496,21497,21499,21501,21503,21506],{"class":107,"line":163},[105,21498,6662],{"class":115},[105,21500,639],{"class":241},[105,21502,321],{"class":115},[105,21504,21505],{"class":248},"'默认消息:'",[105,21507,7118],{"class":115},[105,21509,21510],{"class":107,"line":179},[105,21511,4441],{"class":115},[105,21513,21514],{"class":107,"line":194},[105,21515,307],{"emptyLinePlaceholder":306},[105,21517,21518],{"class":107,"line":209},[105,21519,21520],{"class":630},"\u002F\u002F 监听 heartbeat 自定义事件\n",[105,21522,21523,21525,21527,21529,21532,21534,21536,21538,21540],{"class":107,"line":526},[105,21524,21480],{"class":115},[105,21526,3286],{"class":241},[105,21528,321],{"class":115},[105,21530,21531],{"class":248},"'heartbeat'",[105,21533,3294],{"class":115},[105,21535,3297],{"class":122},[105,21537,2836],{"class":115},[105,21539,723],{"class":111},[105,21541,116],{"class":115},[105,21543,21544,21546,21548,21550,21553],{"class":107,"line":531},[105,21545,6662],{"class":115},[105,21547,639],{"class":241},[105,21549,321],{"class":115},[105,21551,21552],{"class":248},"'心跳事件:'",[105,21554,7118],{"class":115},[105,21556,21557],{"class":107,"line":668},[105,21558,3964],{"class":115},[10,21560,21561],{"id":21561},"跨端和高性能",[14,21563,21564],{"id":21564},"electron",[18,21566,21567],{},"Electron 的 IPC是它的核心架构，主进程是一个node环境，用于管理窗口、声明周期、系统api，能够访问完整的nodejs api，比如说fs、path、child_process，不能直接操作dom，渲染进程是chromium环境，每个窗口一个，能够操作dom、跑前端代码，默认不能访问nodejs。",[18,21569,21570],{},"每个渲染进程在启动前，会加载一个预加载脚本，我们一般叫做preload.js，用于定义能访问的部分nodejs api，是主进程和渲染进程的bridge。",[18,21572,21573],{},"它提供的有三种模式，分别是渲染进程->主进程，有invoke和handle，渲染进程请求、主进程响应：",[96,21575,21577],{"className":559,"code":21576,"language":561,"meta":101,"style":101},"\u002F\u002F 主进程\nconst { ipcMain } = require('electron');\n\nipcMain.handle('read-file', async (event, filePath) => {\n    const content = await fs.promises.readFile(filePath, 'utf-8');\n    return content;   \u002F\u002F 返回给渲染进程\n});\n\n\u002F\u002F 预加载脚本\nconst { contextBridge, ipcRenderer } = require('electron');\n\ncontextBridge.exposeInMainWorld('api', {\n    readFile: (path) => ipcRenderer.invoke('read-file', path)\n});\n\n\u002F\u002F 渲染进程\nconst content = await window.api.readFile('\u002Fpath\u002Fto\u002Ffile');\n",[24,21578,21579,21584,21607,21611,21643,21667,21677,21681,21685,21690,21716,21720,21735,21762,21766,21770,21775],{"__ignoreMap":101},[105,21580,21581],{"class":107,"line":108},[105,21582,21583],{"class":630},"\u002F\u002F 主进程\n",[105,21585,21586,21588,21590,21593,21595,21597,21600,21602,21605],{"class":107,"line":119},[105,21587,568],{"class":111},[105,21589,726],{"class":115},[105,21591,21592],{"class":129},"ipcMain",[105,21594,3955],{"class":115},[105,21596,245],{"class":111},[105,21598,21599],{"class":241}," require",[105,21601,321],{"class":115},[105,21603,21604],{"class":248},"'electron'",[105,21606,327],{"class":115},[105,21608,21609],{"class":107,"line":133},[105,21610,307],{"emptyLinePlaceholder":306},[105,21612,21613,21616,21619,21621,21624,21626,21628,21630,21632,21634,21637,21639,21641],{"class":107,"line":148},[105,21614,21615],{"class":115},"ipcMain.",[105,21617,21618],{"class":241},"handle",[105,21620,321],{"class":115},[105,21622,21623],{"class":248},"'read-file'",[105,21625,286],{"class":115},[105,21627,9221],{"class":111},[105,21629,4361],{"class":115},[105,21631,5592],{"class":122},[105,21633,286],{"class":115},[105,21635,21636],{"class":122},"filePath",[105,21638,2836],{"class":115},[105,21640,723],{"class":111},[105,21642,116],{"class":115},[105,21644,21645,21647,21650,21652,21654,21657,21659,21662,21665],{"class":107,"line":163},[105,21646,7220],{"class":111},[105,21648,21649],{"class":129}," content",[105,21651,574],{"class":111},[105,21653,5410],{"class":111},[105,21655,21656],{"class":115}," fs.promises.",[105,21658,9184],{"class":241},[105,21660,21661],{"class":115},"(filePath, ",[105,21663,21664],{"class":248},"'utf-8'",[105,21666,327],{"class":115},[105,21668,21669,21671,21674],{"class":107,"line":179},[105,21670,10662],{"class":111},[105,21672,21673],{"class":115}," content;   ",[105,21675,21676],{"class":630},"\u002F\u002F 返回给渲染进程\n",[105,21678,21679],{"class":107,"line":194},[105,21680,3964],{"class":115},[105,21682,21683],{"class":107,"line":209},[105,21684,307],{"emptyLinePlaceholder":306},[105,21686,21687],{"class":107,"line":526},[105,21688,21689],{"class":630},"\u002F\u002F 预加载脚本\n",[105,21691,21692,21694,21696,21699,21701,21704,21706,21708,21710,21712,21714],{"class":107,"line":531},[105,21693,568],{"class":111},[105,21695,726],{"class":115},[105,21697,21698],{"class":129},"contextBridge",[105,21700,286],{"class":115},[105,21702,21703],{"class":129},"ipcRenderer",[105,21705,3955],{"class":115},[105,21707,245],{"class":111},[105,21709,21599],{"class":241},[105,21711,321],{"class":115},[105,21713,21604],{"class":248},[105,21715,327],{"class":115},[105,21717,21718],{"class":107,"line":668},[105,21719,307],{"emptyLinePlaceholder":306},[105,21721,21722,21725,21728,21730,21733],{"class":107,"line":673},[105,21723,21724],{"class":115},"contextBridge.",[105,21726,21727],{"class":241},"exposeInMainWorld",[105,21729,321],{"class":115},[105,21731,21732],{"class":248},"'api'",[105,21734,2799],{"class":115},[105,21736,21737,21740,21742,21745,21747,21749,21752,21755,21757,21759],{"class":107,"line":679},[105,21738,21739],{"class":241},"    readFile",[105,21741,2830],{"class":115},[105,21743,21744],{"class":122},"path",[105,21746,2836],{"class":115},[105,21748,723],{"class":111},[105,21750,21751],{"class":115}," ipcRenderer.",[105,21753,21754],{"class":241},"invoke",[105,21756,321],{"class":115},[105,21758,21623],{"class":248},[105,21760,21761],{"class":115},", path)\n",[105,21763,21764],{"class":107,"line":689},[105,21765,3964],{"class":115},[105,21767,21768],{"class":107,"line":699},[105,21769,307],{"emptyLinePlaceholder":306},[105,21771,21772],{"class":107,"line":704},[105,21773,21774],{"class":630},"\u002F\u002F 渲染进程\n",[105,21776,21777,21779,21781,21783,21785,21788,21790,21792,21795],{"class":107,"line":710},[105,21778,568],{"class":111},[105,21780,21649],{"class":129},[105,21782,574],{"class":111},[105,21784,5410],{"class":111},[105,21786,21787],{"class":115}," window.api.",[105,21789,9184],{"class":241},[105,21791,321],{"class":115},[105,21793,21794],{"class":248},"'\u002Fpath\u002Fto\u002Ffile'",[105,21796,327],{"class":115},[18,21798,21799],{},"它的特点是promise风格，异步有返回值。",[18,21801,21802],{},"另一种是渲染进程->主进程，使用send和on完成，是单向的，渲染进程发，主进程收，但是不返回。",[96,21804,21806],{"className":559,"code":21805,"language":561,"meta":101,"style":101},"\u002F\u002F 主进程\nipcMain.on('log', (event, message) => {\n    console.log('渲染进程说:', message);\n    \u002F\u002F 不返回\n});\n\n\u002F\u002F 预加载\ncontextBridge.exposeInMainWorld('api', {\n    log: (msg) => ipcRenderer.send('log', msg)\n});\n\n\u002F\u002F 渲染进程\nwindow.api.log('hello');\n",[24,21807,21808,21812,21839,21853,21858,21862,21866,21871,21883,21909,21913,21917,21921],{"__ignoreMap":101},[105,21809,21810],{"class":107,"line":108},[105,21811,21583],{"class":630},[105,21813,21814,21816,21819,21821,21824,21826,21828,21830,21833,21835,21837],{"class":107,"line":119},[105,21815,21615],{"class":115},[105,21817,21818],{"class":241},"on",[105,21820,321],{"class":115},[105,21822,21823],{"class":248},"'log'",[105,21825,3294],{"class":115},[105,21827,5592],{"class":122},[105,21829,286],{"class":115},[105,21831,21832],{"class":122},"message",[105,21834,2836],{"class":115},[105,21836,723],{"class":111},[105,21838,116],{"class":115},[105,21840,21841,21843,21845,21847,21850],{"class":107,"line":133},[105,21842,6662],{"class":115},[105,21844,639],{"class":241},[105,21846,321],{"class":115},[105,21848,21849],{"class":248},"'渲染进程说:'",[105,21851,21852],{"class":115},", message);\n",[105,21854,21855],{"class":107,"line":148},[105,21856,21857],{"class":630},"    \u002F\u002F 不返回\n",[105,21859,21860],{"class":107,"line":163},[105,21861,3964],{"class":115},[105,21863,21864],{"class":107,"line":179},[105,21865,307],{"emptyLinePlaceholder":306},[105,21867,21868],{"class":107,"line":194},[105,21869,21870],{"class":630},"\u002F\u002F 预加载\n",[105,21872,21873,21875,21877,21879,21881],{"class":107,"line":209},[105,21874,21724],{"class":115},[105,21876,21727],{"class":241},[105,21878,321],{"class":115},[105,21880,21732],{"class":248},[105,21882,2799],{"class":115},[105,21884,21885,21888,21890,21893,21895,21897,21899,21902,21904,21906],{"class":107,"line":526},[105,21886,21887],{"class":241},"    log",[105,21889,2830],{"class":115},[105,21891,21892],{"class":122},"msg",[105,21894,2836],{"class":115},[105,21896,723],{"class":111},[105,21898,21751],{"class":115},[105,21900,21901],{"class":241},"send",[105,21903,321],{"class":115},[105,21905,21823],{"class":248},[105,21907,21908],{"class":115},", msg)\n",[105,21910,21911],{"class":107,"line":531},[105,21912,3964],{"class":115},[105,21914,21915],{"class":107,"line":668},[105,21916,307],{"emptyLinePlaceholder":306},[105,21918,21919],{"class":107,"line":673},[105,21920,21774],{"class":630},[105,21922,21923,21926,21928,21930,21932],{"class":107,"line":679},[105,21924,21925],{"class":115},"window.api.",[105,21927,639],{"class":241},[105,21929,321],{"class":115},[105,21931,3262],{"class":248},[105,21933,327],{"class":115},[18,21935,21936],{},"主进程单向到渲染进程：",[96,21938,21940],{"className":559,"code":21939,"language":561,"meta":101,"style":101},"\u002F\u002F 主进程\nmainWindow.webContents.send('update', { version: '1.0.0' });\n\u002F\u002F 预加载\ncontextBridge.exposeInMainWorld('api', {\n    onUpdate: (callback) => ipcRenderer.on('update', (event, data) => callback(data))\n});\n\u002F\u002F 渲染进程\nwindow.api.onUpdate((data) => {\n    console.log('收到更新:', data);\n});\n",[24,21941,21942,21946,21965,21969,21981,22021,22025,22029,22046,22060],{"__ignoreMap":101},[105,21943,21944],{"class":107,"line":108},[105,21945,21583],{"class":630},[105,21947,21948,21951,21953,21955,21957,21960,21963],{"class":107,"line":119},[105,21949,21950],{"class":115},"mainWindow.webContents.",[105,21952,21901],{"class":241},[105,21954,321],{"class":115},[105,21956,21248],{"class":248},[105,21958,21959],{"class":115},", { version: ",[105,21961,21962],{"class":248},"'1.0.0'",[105,21964,17559],{"class":115},[105,21966,21967],{"class":107,"line":133},[105,21968,21870],{"class":630},[105,21970,21971,21973,21975,21977,21979],{"class":107,"line":148},[105,21972,21724],{"class":115},[105,21974,21727],{"class":241},[105,21976,321],{"class":115},[105,21978,21732],{"class":248},[105,21980,2799],{"class":115},[105,21982,21983,21986,21988,21991,21993,21995,21997,21999,22001,22003,22005,22007,22009,22011,22013,22015,22018],{"class":107,"line":163},[105,21984,21985],{"class":241},"    onUpdate",[105,21987,2830],{"class":115},[105,21989,21990],{"class":122},"callback",[105,21992,2836],{"class":115},[105,21994,723],{"class":111},[105,21996,21751],{"class":115},[105,21998,21818],{"class":241},[105,22000,321],{"class":115},[105,22002,21248],{"class":248},[105,22004,3294],{"class":115},[105,22006,5592],{"class":122},[105,22008,286],{"class":115},[105,22010,5052],{"class":122},[105,22012,2836],{"class":115},[105,22014,723],{"class":111},[105,22016,22017],{"class":241}," callback",[105,22019,22020],{"class":115},"(data))\n",[105,22022,22023],{"class":107,"line":179},[105,22024,3964],{"class":115},[105,22026,22027],{"class":107,"line":194},[105,22028,21774],{"class":630},[105,22030,22031,22033,22036,22038,22040,22042,22044],{"class":107,"line":209},[105,22032,21925],{"class":115},[105,22034,22035],{"class":241},"onUpdate",[105,22037,7280],{"class":115},[105,22039,5052],{"class":122},[105,22041,2836],{"class":115},[105,22043,723],{"class":111},[105,22045,116],{"class":115},[105,22047,22048,22050,22052,22054,22057],{"class":107,"line":526},[105,22049,6662],{"class":115},[105,22051,639],{"class":241},[105,22053,321],{"class":115},[105,22055,22056],{"class":248},"'收到更新:'",[105,22058,22059],{"class":115},", data);\n",[105,22061,22062],{"class":107,"line":531},[105,22063,3964],{"class":115},[18,22065,22066],{},"ipc的底层是chromium的mojo ipc，mojo是chromium的ipc框架，管理二进制序列化、异步、跨进程，electron封装了mojo，能够提供ipcRenderer、ipcMain。",[18,22068,22069,22070,22073,22074,22077],{},"mojo在win32平台的底层传输是基于命名管道实现的，函数在 Windows 下会调用 ",[24,22071,22072],{},"CreateNamedPipeW"," 来创建管道。一个专门抓取 Chromium IPC 的工具也明确指出，它捕获的就是 ",[21,22075,22076],{},"Named Pipe"," 的流量",[18,22079,22080,22081,22084,22085,22088],{},"值得一提的是，从 Chrome 112 开始，Chromium 逐渐用新的 ",[21,22082,22083],{},"ipcz"," 协议替代了 Mojo Core。ipcz 为了优化性能，在 Windows 上可能会更多地使用",[21,22086,22087],{},"共享内存","来传递数据，而不是完全依赖命名管道，但这属于更底层的优化实现。",[18,22090,22091],{},"安全方面：",[18,22093,22094],{},"contextIsolation是默认开启的，渲染进程和预加载脚本上下文隔离。nodeInteration也是默认关闭的，渲染进程肯定不能访问node。sandbox沙箱默认开启，渲染进程在沙箱里。webSecurit默认开启，同源策略。",[18,22096,22097],{},"生命周期方面：",[18,22099,22100],{},"app.whenReady()是app初始化完成的时候，创建窗口。\nwindow-all-closed是所有窗口都关闭，macos不退出。\nbefore-quit: 退出前，可以拦截\nwill-quit:即将推出\nquit:已退出\nready:app就绪\nsecond-instance:第二个实例启动",[18,22102,22103],{},"窗口管理：",[18,22105,22106,22107,31,22109],{},"BrowserWindow:创建窗口\nwebPreferences:安全配置preload、contextisloation、nodeintergration\nloadURL\u002FloadFile：加载远程URL\u002F本地文件\nready-to-show:窗口准备好显示，避免白屏\nframe:false:无边框窗口，自定义标题栏\nshow:false + read-to-show 先隐藏，准备好在显示，可以消除白屏\ntransparent:true透明窗口\nalwaysOnTop：置顶\nwebContents:窗口的web内容，可以",[24,22108,21901],{},[24,22110,22111],{},"openDevTools",[18,22113,22114,22115,31,22117,22119],{},"preload脚本是渲染进程启动前，DOM加载前执行，能够访问",[24,22116,21698],{},[24,22118,21703],{},"、部分 Node API。",[14,22121,22123],{"id":22122},"emscripten","Emscripten",[18,22125,22126],{},"emscripten可以把c\u002Fc++代码翻译为浏览器和nodejs能直接运行的wasm模块，并附带一个js胶水文件。",[18,22128,22129],{},"emscripten的核心工具是emcc，内部串联了多个工具，分别是前端clang\u002Fllvm，可以将我们的c\u002Fc++代码编译为llvm的ir码，目标是WebAssembly平台。后端llvm wasm backend将llvm ir转换为WebAssembly目标文件.o或者.a。最后链接和优化，wasm-ld链接器将所有目标文件打包成一个.wasm二进制模块，之后Binaryen工具链会对这个模块进行优化，让他的体积更小，运行更快。emcc会生成一个js胶水文件，作用是加载、编译、实例化.wasm文件，并处理c\u002Fc++与js之间的所有通信，比如内存分配，函数调用和文件系统模拟。",[18,22131,22132,22133],{},"C\u002FC++ 代码常需要读写文件，但浏览器出于安全考虑，不允许 JS 直接访问本地文件。Emscripten 的解决方案是",[21,22134,22135],{},"虚拟文件系统",[18,22137,22138,22139,22142,22143,31,22146,22149],{},"胶水层代码是自动生成，但什么一些情况下需要手动补充js：\n比如说调用导出函数，我们在前端要自己调用；字符串和数组之间需要转换，c的 ",[24,22140,22141],{},"char*","和js的string不互通，因此需要手动转换，生成的js提供了 ",[24,22144,22145],{},"stringToNewUTF8",[24,22147,22148],{},"UTF8ToString"," 这些工具，你负责组合；然后C要调js函数比如事件回调，你需要在C侧写",[96,22151,22154],{"className":22152,"code":22153,"language":392},[390],"\u002F\u002F C 侧\ntypedef void (*Callback)(int);\nvoid set_callback(Callback cb);\n\n\u002F\u002F JS 侧\nconst callback = Module.addFunction((value) => {\n    console.log('C 调用了 JS:', value);\n}, 'vi');\nModule._set_callback(callback);\n",[24,22155,22153],{"__ignoreMap":101},[18,22157,22158],{},"所以我们一般分为core、common、platform的实现，core是平台无关核心实现，然后common是包装c abi，最后platform是平台适配。",[10,22160,22161],{"id":22161},"认证和授权",[14,22163,22165],{"id":22164},"oauth","OAuth",[18,22167,22168],{},"Oauth2.0是一套授权框架，目标是解决第三方应用如何在不获取用户密码的前提下，代表用户去访问受保护的资源。oauth体系里面，有四个角色，分别是资源所有者、客户端、授权服务器、资源服务器。",[18,22170,22171],{},"资源所有者一般就是用户，是数据拥有者，有权决定是否授权，客户端是想要访问用户数据的第三方应用，比如有个工具想要读取你gayhub仓库列表的项目管理。授权服务器用于验证身份、征得同意、颁发令牌，gayhub也有oauth登录服务。资源服务用于存储受保护的api服务，如github有restapi，需要令牌，不认密码。",[18,22173,22174],{},"然后是授权模式，首先是授权码模式，这个是最安全、最常用的模式。用户需要发请请求，比如阿紫客户端点击使用github登录，重定向到授权服务器中，客户端把用户的浏览器重定向到github的授权服务器，请求用户授权。用户需要登录并授权，同意授权给客户端相应的权限。完成之后授权服务器把浏览器重定向会客户端，并在URL上带上一个授权码，客户端在后台用授权码+客户端秘钥向授权服务器换取访问令牌，客户端就可以通过访问令牌去资源服务器上请求用户的数据了。",[18,22176,22177],{},"隐式模式不推荐，它是令牌慧姐通过URL返回，容易泄漏，无法刷新。密码模式已禁止，用户需要把密码直接给客户端，从根本上违背了OAuth的设计。",[18,22179,22180],{},"客户端机密模式是服务间调用，用户无参与，客户端会以自己的身份获取令牌，用于访问自己的资源，不涉及用户。",[18,22182,22183],{},"OAuth本身留下了很多安全细节，最新实践RFC 9700明确表示强制使用PKCE，所有的客户端类型，尤其是公开客户端SPA 移动APP都需要使用PKCE来防止授权码被拦截。授权服务器必须对重定向uri进行精确的字符串匹配，不能使用通配符或者模式匹配，防止授权码被重定向到恶意地址。",[18,22185,22186,22187,22190,22191,22194,22195,22198],{},"注意： OAuth只负责授权，不负责认证，使用Google登录这种选项实际上是OAuth2.0 + OIDC的组合。",[21,22188,22189],{},"OIDC"," 是 OAuth 2.0 之上的一个",[21,22192,22193],{},"身份层","，它额外提供了一个 ",[21,22196,22197],{},"ID 令牌 (ID Token)"," ，这是一个包含用户身份信息（如姓名、邮箱）的 JWT，用来证明用户身份，之后会讲。",[18,22200,22201],{},"为什么访问资源需要客户端秘钥，这个是为了证明客户端身份，因为授权服务器需要确认客户端是否真的是我们自己规定的客户端，如果说授权码被拦截也无所谓，有了客户端秘钥才能换令牌。",[14,22203,22189],{"id":22204},"oidc",[18,22206,22207,22208,22211],{},"然后我们来谈谈oidc部分，它是在oauth2之上的身份认证层，核心目标是让客户端能验证用户，它在 OAuth 2.0 的授权流程上，额外返回一个 ",[21,22209,22210],{},"ID Token","（JWT）。",[18,22213,22214],{},"因为oauth2只会给access_token，也就是用于访问资源的访问令牌，oidc额外返回了id_token，id_token是jwt，能够包含用户信息，客户端解 id_token。然后客户端是可以验证id token里面的签名的，需要使用JWKS，我们可以通过一个get请求，去访问公钥集合：",[96,22216,22219],{"className":22217,"code":22218,"language":392},[390],"GET https:\u002F\u002Fauth.example.com\u002F.well-known\u002Fjwks.json\n\n{\n    \"keys\": [\n        {\n            \"kty\": \"RSA\",\n            \"kid\": \"key-1\",\n            \"use\": \"sig\",\n            \"n\": \"...\",\n            \"e\": \"AQAB\"\n        }\n    ]\n}\n",[24,22220,22218],{"__ignoreMap":101},[18,22222,22223,22224,9829],{},"并且scope里面必须包含openid，不然就不是oidc。",[21,22225,22226],{},"OIDC 定义了标准 scope",[835,22228,22229,22241],{},[838,22230,22231],{},[841,22232,22233,22236,22238],{},[844,22234,22235],{},"scope",[844,22237,1169],{},[844,22239,22240],{},"返回的 claim",[854,22242,22243,22261,22282,22299,22313],{},[841,22244,22245,22250,22256],{},[859,22246,22247],{},[24,22248,22249],{},"openid",[859,22251,22252,22255],{},[21,22253,22254],{},"必须","，表示 OIDC",[859,22257,22258],{},[24,22259,22260],{},"sub",[841,22262,22263,22268,22271],{},[859,22264,22265],{},[24,22266,22267],{},"profile",[859,22269,22270],{},"基本资料",[859,22272,22273,31,22275,31,22278,22281],{},[24,22274,4996],{},[24,22276,22277],{},"family_name",[24,22279,22280],{},"picture"," 等",[841,22283,22284,22289,22292],{},[859,22285,22286],{},[24,22287,22288],{},"email",[859,22290,22291],{},"邮箱",[859,22293,22294,31,22296],{},[24,22295,22288],{},[24,22297,22298],{},"email_verified",[841,22300,22301,22306,22309],{},[859,22302,22303],{},[24,22304,22305],{},"address",[859,22307,22308],{},"地址",[859,22310,22311],{},[24,22312,22305],{},[841,22314,22315,22320,22323],{},[859,22316,22317],{},[24,22318,22319],{},"phone",[859,22321,22322],{},"电话",[859,22324,22325,31,22328],{},[24,22326,22327],{},"phone_number",[24,22329,22330],{},"phone_number_verified",[18,22332,22333],{},"oidc的使用场景：",[18,22335,22336],{},"SSO 单点登录，用户登录一次，可以多个应用共享身份，每个应用用oidc来验证用户。比如企业sso，一个idp，多个应用。",[18,22338,22339],{},"社交登录：使用google登录，是使用oauth2.0+oidc，拿id_token->验证用户。",[18,22341,22342],{},"移动App：调用系统浏览器访问授权服务器返回id_token。然后客户端是可以验证id",[10,22344,22345],{"id":22345},"工程",[14,22347,22348],{"id":22348},"微前端",[18,22350,22351],{},"这是一种将大型前端应用拆分成多个小型、独立、可独立开发和部署的子应用的架构模式，核心理念借鉴了后端微服务，可以把一个庞大的单体前端，拆分为多个可以独立开发、测试、部署的小块，最后在运行时或者构建的时候组合为一个完整应用。",[18,22353,22354],{},"这样的优点很明显，子应用独立构建，互不影响。多团队协作，技术栈可以不同，但是技术栈升级困难，发布风险高，耦合严重。",[18,22356,22357],{},"子应用会被作为npm包引入，构建的时候打包在一起。实现方案也有很多种，比如可以用iframe，每个子应用一个，简单但是通信、样式、路由受限。",[18,22359,22360],{},"可以用web component，自定义元素封装子应用，元素隔离，但是生态兼容性有限。",[18,22362,22363],{},"webpack5支持module federation，构建的时候+运行时结合，生态比较成熟。",[18,22365,22366],{},"qiankun是一个微前端实现库，能够无痛构建一个生产可用的微前端框架系统，qiankun原子蚂蚁金融科技基于微前端架构的云产品统一接入平台，现在已经非常完备了。qiankun 对于用户而言只是一个类似 jQuery 的库，你需要调用几个 qiankun 的 API 即可完成应用的微前端改造。同时由于 qiankun 的 HTML entry 及沙箱的设计，使得微应用的接入像使用 iframe 一样简单。",[3615,22368,22369],{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":101,"searchDepth":119,"depth":119,"links":22371},[22372,22377,22380,22387,22398,22419,22424,22435,22439,22443],{"id":12,"depth":119,"text":12,"children":22373},[22374,22375,22376],{"id":16,"depth":133,"text":16},{"id":53,"depth":133,"text":53},{"id":100,"depth":133,"text":830},{"id":2757,"depth":119,"text":2758,"children":22378},[22379],{"id":2913,"depth":133,"text":2914},{"id":227,"depth":119,"text":3012,"children":22381},[22382,22383,22384,22385,22386],{"id":3015,"depth":133,"text":3016},{"id":3129,"depth":133,"text":3129},{"id":3342,"depth":133,"text":3343},{"id":3487,"depth":133,"text":3488},{"id":3634,"depth":133,"text":3634},{"id":4932,"depth":119,"text":4932,"children":22388},[22389,22390,22391,22392,22393,22394,22395,22396,22397],{"id":5298,"depth":133,"text":5299},{"id":5428,"depth":133,"text":5429},{"id":6989,"depth":133,"text":6990},{"id":7572,"depth":133,"text":6723},{"id":7611,"depth":133,"text":7612},{"id":7624,"depth":133,"text":7625},{"id":7637,"depth":133,"text":7637},{"id":7724,"depth":133,"text":7724},{"id":7784,"depth":133,"text":7784},{"id":7802,"depth":119,"text":7803,"children":22399},[22400,22401,22402,22403,22404,22405,22406,22407,22408,22409,22410,22411,22412,22413,22414,22415,22416,22417,22418],{"id":7806,"depth":133,"text":7807},{"id":8029,"depth":133,"text":8029},{"id":8508,"depth":133,"text":8508},{"id":9146,"depth":133,"text":9146},{"id":9671,"depth":133,"text":9672},{"id":10120,"depth":133,"text":10120},{"id":10351,"depth":133,"text":10351},{"id":10370,"depth":133,"text":10370},{"id":8245,"depth":133,"text":8245},{"id":11280,"depth":133,"text":11280},{"id":11474,"depth":133,"text":11474},{"id":13882,"depth":133,"text":13882},{"id":14474,"depth":133,"text":14474},{"id":15242,"depth":133,"text":15242},{"id":15248,"depth":133,"text":15248},{"id":10015,"depth":133,"text":10015},{"id":17835,"depth":133,"text":17836},{"id":17895,"depth":133,"text":17896},{"id":18050,"depth":133,"text":18051},{"id":2947,"depth":119,"text":2947,"children":22420},[22421,22422,22423],{"id":19088,"depth":133,"text":19088},{"id":19930,"depth":133,"text":19930},{"id":20471,"depth":133,"text":20472},{"id":20493,"depth":119,"text":20493,"children":22425},[22426,22427,22428,22429,22430,22431,22432,22433,22434],{"id":20496,"depth":133,"text":20497},{"id":20628,"depth":133,"text":20628},{"id":20743,"depth":133,"text":20743},{"id":20898,"depth":133,"text":20898},{"id":20955,"depth":133,"text":20955},{"id":21007,"depth":133,"text":21007},{"id":21050,"depth":133,"text":21050},{"id":21106,"depth":133,"text":21107},{"id":21147,"depth":133,"text":21148},{"id":21561,"depth":119,"text":21561,"children":22436},[22437,22438],{"id":21564,"depth":133,"text":21564},{"id":22122,"depth":133,"text":22123},{"id":22161,"depth":119,"text":22161,"children":22440},[22441,22442],{"id":22164,"depth":133,"text":22165},{"id":22204,"depth":133,"text":22189},{"id":22345,"depth":119,"text":22345,"children":22444},[22445],{"id":22348,"depth":133,"text":22348},"md",{},"\u002Finterview-notes",{"description":101},"interview-notes","WvwZQrNaC9IzvJPh5L8Wy5mijoRtm_HP31jDaRo9WCc",1791042466554]