[{"data":1,"prerenderedAt":33152},["ShallowReactive",2],{"search-dialog-posts":3,"post-\u002Fposts\u002Fscss-icon-sprite-map":32604,"post-surround-\u002Fposts\u002Fscss-icon-sprite-map":33149},[4,394,2044,4024,4574,5398,6030,6241,6718,7035,8412,10519,10714,10831,10931,11567,11602,11650,12316,12546,12644,12814,12890,13006,13424,13621,13669,14435,14894,15432,15544,15983,16366,17926,18677,19786,21458,21691,21816,22956,23243,23572,23891,24075,24952,25347,25746,27983,30669,31208,31314],{"id":5,"title":6,"body":7,"category":378,"description":379,"draft":380,"extension":381,"image":382,"meta":383,"navigation":102,"path":384,"publishedAt":385,"relatedExperiments":386,"seo":387,"stem":388,"tags":389,"updatedAt":392,"__hash__":393},"posts\u002Fposts\u002F20160919.if-switch-ternary.md","JavaScript 條件判斷：if、switch 與三元運算子",{"type":8,"value":9,"toc":371},"minimark",[10,14,18,21,55,58,61,63,150,154,157,298,301,304,326,329,367],[11,12,13],"p",{},"這篇是當時學 JavaScript 時整理的條件判斷筆記。",[15,16,17],"h2",{"id":17},"if",[11,19,20],{},"基本寫法：",[22,23,28],"pre",{"className":24,"code":25,"language":26,"meta":27,"style":27},"language-js shiki shiki-themes github-light github-dark-default","if (條件) {\n  語句;\n}\n","js","",[29,30,31,43,49],"code",{"__ignoreMap":27},[32,33,36,39],"span",{"class":34,"line":35},"line",1,[32,37,17],{"class":38},"sDgYj",[32,40,42],{"class":41},"szmH1"," (條件) {\n",[32,44,46],{"class":34,"line":45},2,[32,47,48],{"class":41},"  語句;\n",[32,50,52],{"class":34,"line":51},3,[32,53,54],{"class":41},"}\n",[11,56,57],{},"條件成立時，就會執行裡面的語句。",[15,59,60],{"id":60},"switch",[11,62,20],{},[22,64,66],{"className":24,"code":65,"language":26,"meta":27,"style":27},"switch (變數) {\n  case 值1:\n    語句1;\n    break;\n\n  case 值2:\n    語句2;\n    break;\n\n  default:\n    語句n;\n}\n",[29,67,68,75,83,88,97,104,112,118,125,130,139,145],{"__ignoreMap":27},[32,69,70,72],{"class":34,"line":35},[32,71,60],{"class":38},[32,73,74],{"class":41}," (變數) {\n",[32,76,77,80],{"class":34,"line":45},[32,78,79],{"class":38},"  case",[32,81,82],{"class":41}," 值1:\n",[32,84,85],{"class":34,"line":51},[32,86,87],{"class":41},"    語句1;\n",[32,89,91,94],{"class":34,"line":90},4,[32,92,93],{"class":38},"    break",[32,95,96],{"class":41},";\n",[32,98,100],{"class":34,"line":99},5,[32,101,103],{"emptyLinePlaceholder":102},true,"\n",[32,105,107,109],{"class":34,"line":106},6,[32,108,79],{"class":38},[32,110,111],{"class":41}," 值2:\n",[32,113,115],{"class":34,"line":114},7,[32,116,117],{"class":41},"    語句2;\n",[32,119,121,123],{"class":34,"line":120},8,[32,122,93],{"class":38},[32,124,96],{"class":41},[32,126,128],{"class":34,"line":127},9,[32,129,103],{"emptyLinePlaceholder":102},[32,131,133,136],{"class":34,"line":132},10,[32,134,135],{"class":38},"  default",[32,137,138],{"class":41},":\n",[32,140,142],{"class":34,"line":141},11,[32,143,144],{"class":41},"    語句n;\n",[32,146,148],{"class":34,"line":147},12,[32,149,54],{"class":41},[151,152,153],"h3",{"id":153},"判斷男女",[11,155,156],{},"當時寫的一個簡單範例：",[22,158,160],{"className":24,"code":159,"language":26,"meta":27,"style":27},"var name = 'abc';\nvar sex = '';\n\nswitch (sex) {\n  case '男':\n    alert(name + ' sir，您好');\n    break;\n\n  case '女':\n    alert(name + '，您好');\n    break;\n\n  default:\n    alert(name + '，您好');\n}\n",[29,161,162,179,193,197,204,213,231,237,241,250,263,269,273,280,293],{"__ignoreMap":27},[32,163,164,167,170,173,177],{"class":34,"line":35},[32,165,166],{"class":38},"var",[32,168,169],{"class":41}," name ",[32,171,172],{"class":38},"=",[32,174,176],{"class":175},"sNbzA"," 'abc'",[32,178,96],{"class":41},[32,180,181,183,186,188,191],{"class":34,"line":45},[32,182,166],{"class":38},[32,184,185],{"class":41}," sex ",[32,187,172],{"class":38},[32,189,190],{"class":175}," ''",[32,192,96],{"class":41},[32,194,195],{"class":34,"line":51},[32,196,103],{"emptyLinePlaceholder":102},[32,198,199,201],{"class":34,"line":90},[32,200,60],{"class":38},[32,202,203],{"class":41}," (sex) {\n",[32,205,206,208,211],{"class":34,"line":99},[32,207,79],{"class":38},[32,209,210],{"class":175}," '男'",[32,212,138],{"class":41},[32,214,215,219,222,225,228],{"class":34,"line":106},[32,216,218],{"class":217},"sVjZL","    alert",[32,220,221],{"class":41},"(name ",[32,223,224],{"class":38},"+",[32,226,227],{"class":175}," ' sir，您好'",[32,229,230],{"class":41},");\n",[32,232,233,235],{"class":34,"line":114},[32,234,93],{"class":38},[32,236,96],{"class":41},[32,238,239],{"class":34,"line":120},[32,240,103],{"emptyLinePlaceholder":102},[32,242,243,245,248],{"class":34,"line":127},[32,244,79],{"class":38},[32,246,247],{"class":175}," '女'",[32,249,138],{"class":41},[32,251,252,254,256,258,261],{"class":34,"line":132},[32,253,218],{"class":217},[32,255,221],{"class":41},[32,257,224],{"class":38},[32,259,260],{"class":175}," '，您好'",[32,262,230],{"class":41},[32,264,265,267],{"class":34,"line":141},[32,266,93],{"class":38},[32,268,96],{"class":41},[32,270,271],{"class":34,"line":147},[32,272,103],{"emptyLinePlaceholder":102},[32,274,276,278],{"class":34,"line":275},13,[32,277,135],{"class":38},[32,279,138],{"class":41},[32,281,283,285,287,289,291],{"class":34,"line":282},14,[32,284,218],{"class":217},[32,286,221],{"class":41},[32,288,224],{"class":38},[32,290,260],{"class":175},[32,292,230],{"class":41},[32,294,296],{"class":34,"line":295},15,[32,297,54],{"class":41},[15,299,300],{"id":300},"三元運算子",[11,302,303],{},"三元運算子的基本格式：",[22,305,307],{"className":24,"code":306,"language":26,"meta":27,"style":27},"條件 ? 成立時的值 : 不成立時的值\n",[29,308,309],{"__ignoreMap":27},[32,310,311,314,317,320,323],{"class":34,"line":35},[32,312,313],{"class":41},"條件 ",[32,315,316],{"class":38},"?",[32,318,319],{"class":41}," 成立時的值 ",[32,321,322],{"class":38},":",[32,324,325],{"class":41}," 不成立時的值\n",[11,327,328],{},"例如：",[22,330,332],{"className":24,"code":331,"language":26,"meta":27,"style":27},"var result = age >= 18 ? '成年' : '未成年';\n",[29,333,334],{"__ignoreMap":27},[32,335,336,338,341,343,346,349,353,356,359,362,365],{"class":34,"line":35},[32,337,166],{"class":38},[32,339,340],{"class":41}," result ",[32,342,172],{"class":38},[32,344,345],{"class":41}," age ",[32,347,348],{"class":38},">=",[32,350,352],{"class":351},"sPgVT"," 18",[32,354,355],{"class":38}," ?",[32,357,358],{"class":175}," '成年'",[32,360,361],{"class":38}," :",[32,363,364],{"class":175}," '未成年'",[32,366,96],{"class":41},[368,369,370],"style",{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}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 .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}",{"title":27,"searchDepth":51,"depth":51,"links":372},[373,374,377],{"id":17,"depth":45,"text":17},{"id":60,"depth":45,"text":60,"children":375},[376],{"id":153,"depth":51,"text":153},{"id":300,"depth":45,"text":300},"frontend","2016 年整理的 JavaScript 條件判斷筆記，包含 if、switch 與三元運算子的基本寫法。",false,"md",null,{},"\u002Fposts\u002Fif-switch-ternary","2016-09-19",[],{"title":6,"description":379},"posts\u002F20160919.if-switch-ternary",[390,391],"JavaScript","條件判斷","2026-08-05","dPXwwFB6y056Mc9ug83ByikB5NjDO606U7MRXBzIl5Q",{"id":395,"title":396,"body":397,"category":378,"description":2032,"draft":380,"extension":381,"image":382,"meta":2033,"navigation":102,"path":2034,"publishedAt":2035,"relatedExperiments":2036,"seo":2037,"stem":2038,"tags":2039,"updatedAt":392,"__hash__":2043},"posts\u002Fposts\u002F20170126.legacy-browser-detection.md","判斷瀏覽器與手機裝置的方法",{"type":8,"value":398,"toc":2024},[399,402,406,409,418,421,429,432,439,859,863,1208,1212,1452,1456,1992,1995,2001,2005,2008,2011,2018,2021],[11,400,401],{},"這篇是當時整理瀏覽器與手機裝置判斷方式時留下來的筆記。",[15,403,405],{"id":404},"css-hack","CSS Hack",[11,407,408],{},"當時參考：",[11,410,411],{},[412,413,417],"a",{"href":414,"rel":415},"https:\u002F\u002Fwww.w3cplus.com\u002Fcss\u002Fbrowser-hacks.html",[416],"nofollow","W3CPlus：CSS Browser Hacks",[11,419,420],{},"IE6、IE7、IE8、Firefox 的一些辨識符號：",[22,422,427],{"className":423,"code":425,"language":426,"meta":27},[424],"language-text","\\9\n*\n_\n","text",[29,428,425],{"__ignoreMap":27},[15,430,431],{"id":431},"判斷手機裝置",[11,433,434,435,438],{},"透過 ",[29,436,437],{},"navigator.userAgent"," 判斷目前使用的裝置。",[22,440,442],{"className":24,"code":441,"language":26,"meta":27,"style":27},"var uA = navigator.userAgent;\n\nif (\n  uA.match(\u002FiPod\u002F) ||\n  uA.match(\u002FiPad\u002F) ||\n  uA.match(\u002FiPhone\u002F)\n) {\n  $(\"#moLoAppDownload\").attr(\n    \"href\",\n    \"https:\u002F\u002Fitunes.apple.com\u002Fapp\u002Fapple-store\u002Fid919043762\"\n  );\n} else if (\n  uA.match(\u002FAndroid\u002F) ||\n  uA.match(\u002FWindows Phone\u002F)\n) {\n  \u002F* $(\"#moLoAppDownload\").attr(\"href\", getAPK(medium, \"\", \"\", activity)); *\u002F\n\n  $(\"#moLoAppDownload\").attr(\"href\", getAPK(medium));\n\n  $(\"#moLoAppDownload\").click(function () {\n    $(\".teachGroup\").fadeIn();\n  });\n} else {\n  $(\"#moLoAppDownload\").attr(\n    \"href\",\n    getEXE(medium, activity)\n  );\n}\n\n$(\"#closeTeach\").click(function () {\n  $(\".teachGroup\").fadeOut();\n});\n\n$(\"#homeURL\").attr(\n  \"href\",\n  location.protocol +\n    \"\u002F\u002F\" +\n    location.host +\n    \"\u002FmobileHD\u002F\"\n);\n",[29,443,444,456,460,467,487,502,516,521,540,548,553,558,571,586,599,603,610,615,642,647,669,688,694,704,719,726,735,740,745,750,771,787,793,798,814,822,831,840,848,854],{"__ignoreMap":27},[32,445,446,448,451,453],{"class":34,"line":35},[32,447,166],{"class":38},[32,449,450],{"class":41}," uA ",[32,452,172],{"class":38},[32,454,455],{"class":41}," navigator.userAgent;\n",[32,457,458],{"class":34,"line":45},[32,459,103],{"emptyLinePlaceholder":102},[32,461,462,464],{"class":34,"line":51},[32,463,17],{"class":38},[32,465,466],{"class":41}," (\n",[32,468,469,472,475,478,481,484],{"class":34,"line":90},[32,470,471],{"class":41},"  uA.",[32,473,474],{"class":217},"match",[32,476,477],{"class":41},"(",[32,479,480],{"class":175},"\u002FiPod\u002F",[32,482,483],{"class":41},") ",[32,485,486],{"class":38},"||\n",[32,488,489,491,493,495,498,500],{"class":34,"line":99},[32,490,471],{"class":41},[32,492,474],{"class":217},[32,494,477],{"class":41},[32,496,497],{"class":175},"\u002FiPad\u002F",[32,499,483],{"class":41},[32,501,486],{"class":38},[32,503,504,506,508,510,513],{"class":34,"line":106},[32,505,471],{"class":41},[32,507,474],{"class":217},[32,509,477],{"class":41},[32,511,512],{"class":175},"\u002FiPhone\u002F",[32,514,515],{"class":41},")\n",[32,517,518],{"class":34,"line":114},[32,519,520],{"class":41},") {\n",[32,522,523,526,528,531,534,537],{"class":34,"line":120},[32,524,525],{"class":217},"  $",[32,527,477],{"class":41},[32,529,530],{"class":175},"\"#moLoAppDownload\"",[32,532,533],{"class":41},").",[32,535,536],{"class":217},"attr",[32,538,539],{"class":41},"(\n",[32,541,542,545],{"class":34,"line":127},[32,543,544],{"class":175},"    \"href\"",[32,546,547],{"class":41},",\n",[32,549,550],{"class":34,"line":132},[32,551,552],{"class":175},"    \"https:\u002F\u002Fitunes.apple.com\u002Fapp\u002Fapple-store\u002Fid919043762\"\n",[32,554,555],{"class":34,"line":141},[32,556,557],{"class":41},"  );\n",[32,559,560,563,566,569],{"class":34,"line":147},[32,561,562],{"class":41},"} ",[32,564,565],{"class":38},"else",[32,567,568],{"class":38}," if",[32,570,466],{"class":41},[32,572,573,575,577,579,582,584],{"class":34,"line":275},[32,574,471],{"class":41},[32,576,474],{"class":217},[32,578,477],{"class":41},[32,580,581],{"class":175},"\u002FAndroid\u002F",[32,583,483],{"class":41},[32,585,486],{"class":38},[32,587,588,590,592,594,597],{"class":34,"line":282},[32,589,471],{"class":41},[32,591,474],{"class":217},[32,593,477],{"class":41},[32,595,596],{"class":175},"\u002FWindows Phone\u002F",[32,598,515],{"class":41},[32,600,601],{"class":34,"line":295},[32,602,520],{"class":41},[32,604,606],{"class":34,"line":605},16,[32,607,609],{"class":608},"sPyeA","  \u002F* $(\"#moLoAppDownload\").attr(\"href\", getAPK(medium, \"\", \"\", activity)); *\u002F\n",[32,611,613],{"class":34,"line":612},17,[32,614,103],{"emptyLinePlaceholder":102},[32,616,618,620,622,624,626,628,630,633,636,639],{"class":34,"line":617},18,[32,619,525],{"class":217},[32,621,477],{"class":41},[32,623,530],{"class":175},[32,625,533],{"class":41},[32,627,536],{"class":217},[32,629,477],{"class":41},[32,631,632],{"class":175},"\"href\"",[32,634,635],{"class":41},", ",[32,637,638],{"class":217},"getAPK",[32,640,641],{"class":41},"(medium));\n",[32,643,645],{"class":34,"line":644},19,[32,646,103],{"emptyLinePlaceholder":102},[32,648,650,652,654,656,658,661,663,666],{"class":34,"line":649},20,[32,651,525],{"class":217},[32,653,477],{"class":41},[32,655,530],{"class":175},[32,657,533],{"class":41},[32,659,660],{"class":217},"click",[32,662,477],{"class":41},[32,664,665],{"class":38},"function",[32,667,668],{"class":41}," () {\n",[32,670,672,675,677,680,682,685],{"class":34,"line":671},21,[32,673,674],{"class":217},"    $",[32,676,477],{"class":41},[32,678,679],{"class":175},"\".teachGroup\"",[32,681,533],{"class":41},[32,683,684],{"class":217},"fadeIn",[32,686,687],{"class":41},"();\n",[32,689,691],{"class":34,"line":690},22,[32,692,693],{"class":41},"  });\n",[32,695,697,699,701],{"class":34,"line":696},23,[32,698,562],{"class":41},[32,700,565],{"class":38},[32,702,703],{"class":41}," {\n",[32,705,707,709,711,713,715,717],{"class":34,"line":706},24,[32,708,525],{"class":217},[32,710,477],{"class":41},[32,712,530],{"class":175},[32,714,533],{"class":41},[32,716,536],{"class":217},[32,718,539],{"class":41},[32,720,722,724],{"class":34,"line":721},25,[32,723,544],{"class":175},[32,725,547],{"class":41},[32,727,729,732],{"class":34,"line":728},26,[32,730,731],{"class":217},"    getEXE",[32,733,734],{"class":41},"(medium, activity)\n",[32,736,738],{"class":34,"line":737},27,[32,739,557],{"class":41},[32,741,743],{"class":34,"line":742},28,[32,744,54],{"class":41},[32,746,748],{"class":34,"line":747},29,[32,749,103],{"emptyLinePlaceholder":102},[32,751,753,756,758,761,763,765,767,769],{"class":34,"line":752},30,[32,754,755],{"class":217},"$",[32,757,477],{"class":41},[32,759,760],{"class":175},"\"#closeTeach\"",[32,762,533],{"class":41},[32,764,660],{"class":217},[32,766,477],{"class":41},[32,768,665],{"class":38},[32,770,668],{"class":41},[32,772,774,776,778,780,782,785],{"class":34,"line":773},31,[32,775,525],{"class":217},[32,777,477],{"class":41},[32,779,679],{"class":175},[32,781,533],{"class":41},[32,783,784],{"class":217},"fadeOut",[32,786,687],{"class":41},[32,788,790],{"class":34,"line":789},32,[32,791,792],{"class":41},"});\n",[32,794,796],{"class":34,"line":795},33,[32,797,103],{"emptyLinePlaceholder":102},[32,799,801,803,805,808,810,812],{"class":34,"line":800},34,[32,802,755],{"class":217},[32,804,477],{"class":41},[32,806,807],{"class":175},"\"#homeURL\"",[32,809,533],{"class":41},[32,811,536],{"class":217},[32,813,539],{"class":41},[32,815,817,820],{"class":34,"line":816},35,[32,818,819],{"class":175},"  \"href\"",[32,821,547],{"class":41},[32,823,825,828],{"class":34,"line":824},36,[32,826,827],{"class":41},"  location.protocol ",[32,829,830],{"class":38},"+\n",[32,832,834,837],{"class":34,"line":833},37,[32,835,836],{"class":175},"    \"\u002F\u002F\"",[32,838,839],{"class":38}," +\n",[32,841,843,846],{"class":34,"line":842},38,[32,844,845],{"class":41},"    location.host ",[32,847,830],{"class":38},[32,849,851],{"class":34,"line":850},39,[32,852,853],{"class":175},"    \"\u002FmobileHD\u002F\"\n",[32,855,857],{"class":34,"line":856},40,[32,858,230],{"class":41},[15,860,862],{"id":861},"判斷-ie-版本","判斷 IE 版本",[22,864,866],{"className":24,"code":865,"language":26,"meta":27,"style":27},"$(document).ready(function () {\n  var isIE = function (ver) {\n    var b = document.createElement(\"b\");\n\n    b.innerHTML =\n      \"\u003C!--[if IE \" +\n      ver +\n      \"]>\u003Ci>\u003C\u002Fi>\u003C![endif]-->\";\n\n    return (\n      b.getElementsByTagName(\"i\").length === 1\n    );\n  };\n\n  \u002F* 判斷 IE8 \u002F IE9 透明貼圖 *\u002F\n  if (isIE(8) || isIE(9)) {\n    $(\"img\").each(function () {\n      if (\n        $(this)\n          .attr(\"src\")\n          .indexOf(\".png\") != -1\n      ) {\n        $(this).css({\n          filter:\n            'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\"' +\n            $(this).attr(\"src\") +\n            '\", sizingMethod=\"scale\");',\n        });\n      }\n    });\n  }\n});\n",[29,867,868,884,907,930,934,942,949,956,963,967,974,998,1003,1008,1012,1017,1047,1067,1074,1086,1100,1123,1128,1144,1149,1156,1177,1184,1189,1194,1199,1204],{"__ignoreMap":27},[32,869,870,872,875,878,880,882],{"class":34,"line":35},[32,871,755],{"class":217},[32,873,874],{"class":41},"(document).",[32,876,877],{"class":217},"ready",[32,879,477],{"class":41},[32,881,665],{"class":38},[32,883,668],{"class":41},[32,885,886,889,892,895,898,901,905],{"class":34,"line":45},[32,887,888],{"class":38},"  var",[32,890,891],{"class":217}," isIE",[32,893,894],{"class":38}," =",[32,896,897],{"class":38}," function",[32,899,900],{"class":41}," (",[32,902,904],{"class":903},"sjlk-","ver",[32,906,520],{"class":41},[32,908,909,912,915,917,920,923,925,928],{"class":34,"line":51},[32,910,911],{"class":38},"    var",[32,913,914],{"class":41}," b ",[32,916,172],{"class":38},[32,918,919],{"class":41}," document.",[32,921,922],{"class":217},"createElement",[32,924,477],{"class":41},[32,926,927],{"class":175},"\"b\"",[32,929,230],{"class":41},[32,931,932],{"class":34,"line":90},[32,933,103],{"emptyLinePlaceholder":102},[32,935,936,939],{"class":34,"line":99},[32,937,938],{"class":41},"    b.innerHTML ",[32,940,941],{"class":38},"=\n",[32,943,944,947],{"class":34,"line":106},[32,945,946],{"class":175},"      \"\u003C!--[if IE \"",[32,948,839],{"class":38},[32,950,951,954],{"class":34,"line":114},[32,952,953],{"class":41},"      ver ",[32,955,830],{"class":38},[32,957,958,961],{"class":34,"line":120},[32,959,960],{"class":175},"      \"]>\u003Ci>\u003C\u002Fi>\u003C![endif]-->\"",[32,962,96],{"class":41},[32,964,965],{"class":34,"line":127},[32,966,103],{"emptyLinePlaceholder":102},[32,968,969,972],{"class":34,"line":132},[32,970,971],{"class":38},"    return",[32,973,466],{"class":41},[32,975,976,979,982,984,987,989,992,995],{"class":34,"line":141},[32,977,978],{"class":41},"      b.",[32,980,981],{"class":217},"getElementsByTagName",[32,983,477],{"class":41},[32,985,986],{"class":175},"\"i\"",[32,988,533],{"class":41},[32,990,991],{"class":351},"length",[32,993,994],{"class":38}," ===",[32,996,997],{"class":351}," 1\n",[32,999,1000],{"class":34,"line":147},[32,1001,1002],{"class":41},"    );\n",[32,1004,1005],{"class":34,"line":275},[32,1006,1007],{"class":41},"  };\n",[32,1009,1010],{"class":34,"line":282},[32,1011,103],{"emptyLinePlaceholder":102},[32,1013,1014],{"class":34,"line":295},[32,1015,1016],{"class":608},"  \u002F* 判斷 IE8 \u002F IE9 透明貼圖 *\u002F\n",[32,1018,1019,1022,1024,1027,1029,1032,1034,1037,1039,1041,1044],{"class":34,"line":605},[32,1020,1021],{"class":38},"  if",[32,1023,900],{"class":41},[32,1025,1026],{"class":217},"isIE",[32,1028,477],{"class":41},[32,1030,1031],{"class":351},"8",[32,1033,483],{"class":41},[32,1035,1036],{"class":38},"||",[32,1038,891],{"class":217},[32,1040,477],{"class":41},[32,1042,1043],{"class":351},"9",[32,1045,1046],{"class":41},")) {\n",[32,1048,1049,1051,1053,1056,1058,1061,1063,1065],{"class":34,"line":612},[32,1050,674],{"class":217},[32,1052,477],{"class":41},[32,1054,1055],{"class":175},"\"img\"",[32,1057,533],{"class":41},[32,1059,1060],{"class":217},"each",[32,1062,477],{"class":41},[32,1064,665],{"class":38},[32,1066,668],{"class":41},[32,1068,1069,1072],{"class":34,"line":617},[32,1070,1071],{"class":38},"      if",[32,1073,466],{"class":41},[32,1075,1076,1079,1081,1084],{"class":34,"line":644},[32,1077,1078],{"class":217},"        $",[32,1080,477],{"class":41},[32,1082,1083],{"class":351},"this",[32,1085,515],{"class":41},[32,1087,1088,1091,1093,1095,1098],{"class":34,"line":649},[32,1089,1090],{"class":41},"          .",[32,1092,536],{"class":217},[32,1094,477],{"class":41},[32,1096,1097],{"class":175},"\"src\"",[32,1099,515],{"class":41},[32,1101,1102,1104,1107,1109,1112,1114,1117,1120],{"class":34,"line":671},[32,1103,1090],{"class":41},[32,1105,1106],{"class":217},"indexOf",[32,1108,477],{"class":41},[32,1110,1111],{"class":175},"\".png\"",[32,1113,483],{"class":41},[32,1115,1116],{"class":38},"!=",[32,1118,1119],{"class":38}," -",[32,1121,1122],{"class":351},"1\n",[32,1124,1125],{"class":34,"line":690},[32,1126,1127],{"class":41},"      ) {\n",[32,1129,1130,1132,1134,1136,1138,1141],{"class":34,"line":696},[32,1131,1078],{"class":217},[32,1133,477],{"class":41},[32,1135,1083],{"class":351},[32,1137,533],{"class":41},[32,1139,1140],{"class":217},"css",[32,1142,1143],{"class":41},"({\n",[32,1145,1146],{"class":34,"line":706},[32,1147,1148],{"class":41},"          filter:\n",[32,1150,1151,1154],{"class":34,"line":721},[32,1152,1153],{"class":175},"            'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\"'",[32,1155,839],{"class":38},[32,1157,1158,1161,1163,1165,1167,1169,1171,1173,1175],{"class":34,"line":728},[32,1159,1160],{"class":217},"            $",[32,1162,477],{"class":41},[32,1164,1083],{"class":351},[32,1166,533],{"class":41},[32,1168,536],{"class":217},[32,1170,477],{"class":41},[32,1172,1097],{"class":175},[32,1174,483],{"class":41},[32,1176,830],{"class":38},[32,1178,1179,1182],{"class":34,"line":737},[32,1180,1181],{"class":175},"            '\", sizingMethod=\"scale\");'",[32,1183,547],{"class":41},[32,1185,1186],{"class":34,"line":742},[32,1187,1188],{"class":41},"        });\n",[32,1190,1191],{"class":34,"line":747},[32,1192,1193],{"class":41},"      }\n",[32,1195,1196],{"class":34,"line":752},[32,1197,1198],{"class":41},"    });\n",[32,1200,1201],{"class":34,"line":773},[32,1202,1203],{"class":41},"  }\n",[32,1205,1206],{"class":34,"line":789},[32,1207,792],{"class":41},[15,1209,1211],{"id":1210},"javascript-判斷瀏覽器版本-1","JavaScript 判斷瀏覽器：版本 1",[22,1213,1215],{"className":24,"code":1214,"language":26,"meta":27,"style":27},"var isFirefox =\n  navigator.userAgent.match(\"Firefox\");\n\u002F\u002F 判斷是否為 Firefox\n\nvar isIE =\n  \u002FTrident\\\u002F7\\.\u002F.test(navigator.userAgent);\n\nvar isOpera =\n  navigator.userAgent.match(\"Opera\");\n\u002F\u002F 判斷是否為 Opera\n\nvar isSafari =\n  navigator.userAgent.match(\"Safari\");\n\u002F\u002F 判斷是否為 Safari 或 Google Chrome\n\nif (isIE) {\n  alert(\"你使用的是 IE\");\n}\n\nif (isFirefox) {\n  alert(\"你使用的是 FireFox\");\n}\n\nif (isOpera) {\n  alert(\"你使用的是 Opera\");\n}\n\nif (isSafari) {\n  alert(\"你使用的是 Chrome 或 Safari\");\n}\n",[29,1216,1217,1226,1240,1245,1249,1258,1285,1289,1298,1311,1316,1320,1329,1342,1347,1351,1358,1370,1374,1378,1385,1396,1400,1404,1411,1422,1426,1430,1437,1448],{"__ignoreMap":27},[32,1218,1219,1221,1224],{"class":34,"line":35},[32,1220,166],{"class":38},[32,1222,1223],{"class":41}," isFirefox ",[32,1225,941],{"class":38},[32,1227,1228,1231,1233,1235,1238],{"class":34,"line":45},[32,1229,1230],{"class":41},"  navigator.userAgent.",[32,1232,474],{"class":217},[32,1234,477],{"class":41},[32,1236,1237],{"class":175},"\"Firefox\"",[32,1239,230],{"class":41},[32,1241,1242],{"class":34,"line":51},[32,1243,1244],{"class":608},"\u002F\u002F 判斷是否為 Firefox\n",[32,1246,1247],{"class":34,"line":90},[32,1248,103],{"emptyLinePlaceholder":102},[32,1250,1251,1253,1256],{"class":34,"line":99},[32,1252,166],{"class":38},[32,1254,1255],{"class":41}," isIE ",[32,1257,941],{"class":38},[32,1259,1260,1263,1267,1270,1273,1276,1279,1282],{"class":34,"line":106},[32,1261,1262],{"class":175},"  \u002FTrident",[32,1264,1266],{"class":1265},"sh9l7","\\\u002F",[32,1268,1269],{"class":175},"7",[32,1271,1272],{"class":1265},"\\.",[32,1274,1275],{"class":175},"\u002F",[32,1277,1278],{"class":41},".",[32,1280,1281],{"class":217},"test",[32,1283,1284],{"class":41},"(navigator.userAgent);\n",[32,1286,1287],{"class":34,"line":114},[32,1288,103],{"emptyLinePlaceholder":102},[32,1290,1291,1293,1296],{"class":34,"line":120},[32,1292,166],{"class":38},[32,1294,1295],{"class":41}," isOpera ",[32,1297,941],{"class":38},[32,1299,1300,1302,1304,1306,1309],{"class":34,"line":127},[32,1301,1230],{"class":41},[32,1303,474],{"class":217},[32,1305,477],{"class":41},[32,1307,1308],{"class":175},"\"Opera\"",[32,1310,230],{"class":41},[32,1312,1313],{"class":34,"line":132},[32,1314,1315],{"class":608},"\u002F\u002F 判斷是否為 Opera\n",[32,1317,1318],{"class":34,"line":141},[32,1319,103],{"emptyLinePlaceholder":102},[32,1321,1322,1324,1327],{"class":34,"line":147},[32,1323,166],{"class":38},[32,1325,1326],{"class":41}," isSafari ",[32,1328,941],{"class":38},[32,1330,1331,1333,1335,1337,1340],{"class":34,"line":275},[32,1332,1230],{"class":41},[32,1334,474],{"class":217},[32,1336,477],{"class":41},[32,1338,1339],{"class":175},"\"Safari\"",[32,1341,230],{"class":41},[32,1343,1344],{"class":34,"line":282},[32,1345,1346],{"class":608},"\u002F\u002F 判斷是否為 Safari 或 Google Chrome\n",[32,1348,1349],{"class":34,"line":295},[32,1350,103],{"emptyLinePlaceholder":102},[32,1352,1353,1355],{"class":34,"line":605},[32,1354,17],{"class":38},[32,1356,1357],{"class":41}," (isIE) {\n",[32,1359,1360,1363,1365,1368],{"class":34,"line":612},[32,1361,1362],{"class":217},"  alert",[32,1364,477],{"class":41},[32,1366,1367],{"class":175},"\"你使用的是 IE\"",[32,1369,230],{"class":41},[32,1371,1372],{"class":34,"line":617},[32,1373,54],{"class":41},[32,1375,1376],{"class":34,"line":644},[32,1377,103],{"emptyLinePlaceholder":102},[32,1379,1380,1382],{"class":34,"line":649},[32,1381,17],{"class":38},[32,1383,1384],{"class":41}," (isFirefox) {\n",[32,1386,1387,1389,1391,1394],{"class":34,"line":671},[32,1388,1362],{"class":217},[32,1390,477],{"class":41},[32,1392,1393],{"class":175},"\"你使用的是 FireFox\"",[32,1395,230],{"class":41},[32,1397,1398],{"class":34,"line":690},[32,1399,54],{"class":41},[32,1401,1402],{"class":34,"line":696},[32,1403,103],{"emptyLinePlaceholder":102},[32,1405,1406,1408],{"class":34,"line":706},[32,1407,17],{"class":38},[32,1409,1410],{"class":41}," (isOpera) {\n",[32,1412,1413,1415,1417,1420],{"class":34,"line":721},[32,1414,1362],{"class":217},[32,1416,477],{"class":41},[32,1418,1419],{"class":175},"\"你使用的是 Opera\"",[32,1421,230],{"class":41},[32,1423,1424],{"class":34,"line":728},[32,1425,54],{"class":41},[32,1427,1428],{"class":34,"line":737},[32,1429,103],{"emptyLinePlaceholder":102},[32,1431,1432,1434],{"class":34,"line":742},[32,1433,17],{"class":38},[32,1435,1436],{"class":41}," (isSafari) {\n",[32,1438,1439,1441,1443,1446],{"class":34,"line":747},[32,1440,1362],{"class":217},[32,1442,477],{"class":41},[32,1444,1445],{"class":175},"\"你使用的是 Chrome 或 Safari\"",[32,1447,230],{"class":41},[32,1449,1450],{"class":34,"line":752},[32,1451,54],{"class":41},[15,1453,1455],{"id":1454},"javascript-判斷瀏覽器版本-2","JavaScript 判斷瀏覽器：版本 2",[22,1457,1459],{"className":24,"code":1458,"language":26,"meta":27,"style":27},"\u002F\u002F Opera 8.0+\nvar isOpera =\n  (!!window.opr && !!opr.addons) ||\n  !!window.opera ||\n  navigator.userAgent.indexOf(\" OPR\u002F\") >= 0;\n\n\u002F\u002F Firefox 1.0+\nvar isFirefox =\n  typeof InstallTrigger !== \"undefined\";\n\n\u002F\u002F At least Safari 3+\nvar isSafari =\n  Object.prototype.toString\n    .call(window.HTMLElement)\n    .indexOf(\"Constructor\") > 0;\n\n\u002F\u002F Internet Explorer 6-11\nvar isIE =\n  false || !!document.documentMode;\n\n\u002F\u002F Edge 20+\nvar isEdge =\n  !isIE && !!window.StyleMedia;\n\n\u002F\u002F Chrome 1+\nvar isChrome =\n  !!window.chrome &&\n  !!window.chrome.webstore;\n\n\u002F\u002F Blink engine detection\nvar isBlink =\n  (isChrome || isOpera) &&\n  !!window.CSS;\n\nvar output =\n  \"Detecting browsers by ducktyping:\u003Chr>\";\n\noutput +=\n  \"isFirefox: \" + isFirefox + \"\u003Cbr>\";\n\noutput +=\n  \"isChrome: \" + isChrome + \"\u003Cbr>\";\n\noutput +=\n  \"isSafari: \" + isSafari + \"\u003Cbr>\";\n\noutput +=\n  \"isOpera: \" + isOpera + \"\u003Cbr>\";\n\noutput +=\n  \"isIE: \" + isIE + \"\u003Cbr>\";\n\noutput +=\n  \"isEdge: \" + isEdge + \"\u003Cbr>\";\n\noutput +=\n  \"isBlink: \" + isBlink + \"\u003Cbr>\";\n\ndocument.body.innerHTML = output;\n",[29,1460,1461,1466,1474,1496,1506,1526,1530,1535,1543,1559,1563,1568,1576,1589,1600,1620,1624,1629,1637,1650,1654,1659,1668,1683,1687,1692,1701,1711,1718,1722,1727,1736,1748,1760,1764,1773,1780,1784,1792,1809,1813,1820,1836,1841,1848,1864,1869,1876,1892,1897,1904,1920,1925,1932,1948,1953,1960,1976,1981],{"__ignoreMap":27},[32,1462,1463],{"class":34,"line":35},[32,1464,1465],{"class":608},"\u002F\u002F Opera 8.0+\n",[32,1467,1468,1470,1472],{"class":34,"line":45},[32,1469,166],{"class":38},[32,1471,1295],{"class":41},[32,1473,941],{"class":38},[32,1475,1476,1479,1482,1485,1488,1491,1494],{"class":34,"line":51},[32,1477,1478],{"class":41},"  (",[32,1480,1481],{"class":38},"!!",[32,1483,1484],{"class":41},"window.opr ",[32,1486,1487],{"class":38},"&&",[32,1489,1490],{"class":38}," !!",[32,1492,1493],{"class":41},"opr.addons) ",[32,1495,486],{"class":38},[32,1497,1498,1501,1504],{"class":34,"line":90},[32,1499,1500],{"class":38},"  !!",[32,1502,1503],{"class":41},"window.opera ",[32,1505,486],{"class":38},[32,1507,1508,1510,1512,1514,1517,1519,1521,1524],{"class":34,"line":99},[32,1509,1230],{"class":41},[32,1511,1106],{"class":217},[32,1513,477],{"class":41},[32,1515,1516],{"class":175},"\" OPR\u002F\"",[32,1518,483],{"class":41},[32,1520,348],{"class":38},[32,1522,1523],{"class":351}," 0",[32,1525,96],{"class":41},[32,1527,1528],{"class":34,"line":106},[32,1529,103],{"emptyLinePlaceholder":102},[32,1531,1532],{"class":34,"line":114},[32,1533,1534],{"class":608},"\u002F\u002F Firefox 1.0+\n",[32,1536,1537,1539,1541],{"class":34,"line":120},[32,1538,166],{"class":38},[32,1540,1223],{"class":41},[32,1542,941],{"class":38},[32,1544,1545,1548,1551,1554,1557],{"class":34,"line":127},[32,1546,1547],{"class":38},"  typeof",[32,1549,1550],{"class":41}," InstallTrigger ",[32,1552,1553],{"class":38},"!==",[32,1555,1556],{"class":175}," \"undefined\"",[32,1558,96],{"class":41},[32,1560,1561],{"class":34,"line":132},[32,1562,103],{"emptyLinePlaceholder":102},[32,1564,1565],{"class":34,"line":141},[32,1566,1567],{"class":608},"\u002F\u002F At least Safari 3+\n",[32,1569,1570,1572,1574],{"class":34,"line":147},[32,1571,166],{"class":38},[32,1573,1326],{"class":41},[32,1575,941],{"class":38},[32,1577,1578,1581,1583,1586],{"class":34,"line":275},[32,1579,1580],{"class":351},"  Object",[32,1582,1278],{"class":41},[32,1584,1585],{"class":351},"prototype",[32,1587,1588],{"class":41},".toString\n",[32,1590,1591,1594,1597],{"class":34,"line":282},[32,1592,1593],{"class":41},"    .",[32,1595,1596],{"class":217},"call",[32,1598,1599],{"class":41},"(window.HTMLElement)\n",[32,1601,1602,1604,1606,1608,1611,1613,1616,1618],{"class":34,"line":295},[32,1603,1593],{"class":41},[32,1605,1106],{"class":217},[32,1607,477],{"class":41},[32,1609,1610],{"class":175},"\"Constructor\"",[32,1612,483],{"class":41},[32,1614,1615],{"class":38},">",[32,1617,1523],{"class":351},[32,1619,96],{"class":41},[32,1621,1622],{"class":34,"line":605},[32,1623,103],{"emptyLinePlaceholder":102},[32,1625,1626],{"class":34,"line":612},[32,1627,1628],{"class":608},"\u002F\u002F Internet Explorer 6-11\n",[32,1630,1631,1633,1635],{"class":34,"line":617},[32,1632,166],{"class":38},[32,1634,1255],{"class":41},[32,1636,941],{"class":38},[32,1638,1639,1642,1645,1647],{"class":34,"line":644},[32,1640,1641],{"class":351},"  false",[32,1643,1644],{"class":38}," ||",[32,1646,1490],{"class":38},[32,1648,1649],{"class":41},"document.documentMode;\n",[32,1651,1652],{"class":34,"line":649},[32,1653,103],{"emptyLinePlaceholder":102},[32,1655,1656],{"class":34,"line":671},[32,1657,1658],{"class":608},"\u002F\u002F Edge 20+\n",[32,1660,1661,1663,1666],{"class":34,"line":690},[32,1662,166],{"class":38},[32,1664,1665],{"class":41}," isEdge ",[32,1667,941],{"class":38},[32,1669,1670,1673,1676,1678,1680],{"class":34,"line":696},[32,1671,1672],{"class":38},"  !",[32,1674,1675],{"class":41},"isIE ",[32,1677,1487],{"class":38},[32,1679,1490],{"class":38},[32,1681,1682],{"class":41},"window.StyleMedia;\n",[32,1684,1685],{"class":34,"line":706},[32,1686,103],{"emptyLinePlaceholder":102},[32,1688,1689],{"class":34,"line":721},[32,1690,1691],{"class":608},"\u002F\u002F Chrome 1+\n",[32,1693,1694,1696,1699],{"class":34,"line":728},[32,1695,166],{"class":38},[32,1697,1698],{"class":41}," isChrome ",[32,1700,941],{"class":38},[32,1702,1703,1705,1708],{"class":34,"line":737},[32,1704,1500],{"class":38},[32,1706,1707],{"class":41},"window.chrome ",[32,1709,1710],{"class":38},"&&\n",[32,1712,1713,1715],{"class":34,"line":742},[32,1714,1500],{"class":38},[32,1716,1717],{"class":41},"window.chrome.webstore;\n",[32,1719,1720],{"class":34,"line":747},[32,1721,103],{"emptyLinePlaceholder":102},[32,1723,1724],{"class":34,"line":752},[32,1725,1726],{"class":608},"\u002F\u002F Blink engine detection\n",[32,1728,1729,1731,1734],{"class":34,"line":773},[32,1730,166],{"class":38},[32,1732,1733],{"class":41}," isBlink ",[32,1735,941],{"class":38},[32,1737,1738,1741,1743,1746],{"class":34,"line":789},[32,1739,1740],{"class":41},"  (isChrome ",[32,1742,1036],{"class":38},[32,1744,1745],{"class":41}," isOpera) ",[32,1747,1710],{"class":38},[32,1749,1750,1752,1755,1758],{"class":34,"line":795},[32,1751,1500],{"class":38},[32,1753,1754],{"class":41},"window.",[32,1756,1757],{"class":351},"CSS",[32,1759,96],{"class":41},[32,1761,1762],{"class":34,"line":800},[32,1763,103],{"emptyLinePlaceholder":102},[32,1765,1766,1768,1771],{"class":34,"line":816},[32,1767,166],{"class":38},[32,1769,1770],{"class":41}," output ",[32,1772,941],{"class":38},[32,1774,1775,1778],{"class":34,"line":824},[32,1776,1777],{"class":175},"  \"Detecting browsers by ducktyping:\u003Chr>\"",[32,1779,96],{"class":41},[32,1781,1782],{"class":34,"line":833},[32,1783,103],{"emptyLinePlaceholder":102},[32,1785,1786,1789],{"class":34,"line":842},[32,1787,1788],{"class":41},"output ",[32,1790,1791],{"class":38},"+=\n",[32,1793,1794,1797,1800,1802,1804,1807],{"class":34,"line":850},[32,1795,1796],{"class":175},"  \"isFirefox: \"",[32,1798,1799],{"class":38}," +",[32,1801,1223],{"class":41},[32,1803,224],{"class":38},[32,1805,1806],{"class":175}," \"\u003Cbr>\"",[32,1808,96],{"class":41},[32,1810,1811],{"class":34,"line":856},[32,1812,103],{"emptyLinePlaceholder":102},[32,1814,1816,1818],{"class":34,"line":1815},41,[32,1817,1788],{"class":41},[32,1819,1791],{"class":38},[32,1821,1823,1826,1828,1830,1832,1834],{"class":34,"line":1822},42,[32,1824,1825],{"class":175},"  \"isChrome: \"",[32,1827,1799],{"class":38},[32,1829,1698],{"class":41},[32,1831,224],{"class":38},[32,1833,1806],{"class":175},[32,1835,96],{"class":41},[32,1837,1839],{"class":34,"line":1838},43,[32,1840,103],{"emptyLinePlaceholder":102},[32,1842,1844,1846],{"class":34,"line":1843},44,[32,1845,1788],{"class":41},[32,1847,1791],{"class":38},[32,1849,1851,1854,1856,1858,1860,1862],{"class":34,"line":1850},45,[32,1852,1853],{"class":175},"  \"isSafari: \"",[32,1855,1799],{"class":38},[32,1857,1326],{"class":41},[32,1859,224],{"class":38},[32,1861,1806],{"class":175},[32,1863,96],{"class":41},[32,1865,1867],{"class":34,"line":1866},46,[32,1868,103],{"emptyLinePlaceholder":102},[32,1870,1872,1874],{"class":34,"line":1871},47,[32,1873,1788],{"class":41},[32,1875,1791],{"class":38},[32,1877,1879,1882,1884,1886,1888,1890],{"class":34,"line":1878},48,[32,1880,1881],{"class":175},"  \"isOpera: \"",[32,1883,1799],{"class":38},[32,1885,1295],{"class":41},[32,1887,224],{"class":38},[32,1889,1806],{"class":175},[32,1891,96],{"class":41},[32,1893,1895],{"class":34,"line":1894},49,[32,1896,103],{"emptyLinePlaceholder":102},[32,1898,1900,1902],{"class":34,"line":1899},50,[32,1901,1788],{"class":41},[32,1903,1791],{"class":38},[32,1905,1907,1910,1912,1914,1916,1918],{"class":34,"line":1906},51,[32,1908,1909],{"class":175},"  \"isIE: \"",[32,1911,1799],{"class":38},[32,1913,1255],{"class":41},[32,1915,224],{"class":38},[32,1917,1806],{"class":175},[32,1919,96],{"class":41},[32,1921,1923],{"class":34,"line":1922},52,[32,1924,103],{"emptyLinePlaceholder":102},[32,1926,1928,1930],{"class":34,"line":1927},53,[32,1929,1788],{"class":41},[32,1931,1791],{"class":38},[32,1933,1935,1938,1940,1942,1944,1946],{"class":34,"line":1934},54,[32,1936,1937],{"class":175},"  \"isEdge: \"",[32,1939,1799],{"class":38},[32,1941,1665],{"class":41},[32,1943,224],{"class":38},[32,1945,1806],{"class":175},[32,1947,96],{"class":41},[32,1949,1951],{"class":34,"line":1950},55,[32,1952,103],{"emptyLinePlaceholder":102},[32,1954,1956,1958],{"class":34,"line":1955},56,[32,1957,1788],{"class":41},[32,1959,1791],{"class":38},[32,1961,1963,1966,1968,1970,1972,1974],{"class":34,"line":1962},57,[32,1964,1965],{"class":175},"  \"isBlink: \"",[32,1967,1799],{"class":38},[32,1969,1733],{"class":41},[32,1971,224],{"class":38},[32,1973,1806],{"class":175},[32,1975,96],{"class":41},[32,1977,1979],{"class":34,"line":1978},58,[32,1980,103],{"emptyLinePlaceholder":102},[32,1982,1984,1987,1989],{"class":34,"line":1983},59,[32,1985,1986],{"class":41},"document.body.innerHTML ",[32,1988,172],{"class":38},[32,1990,1991],{"class":41}," output;\n",[11,1993,1994],{},"當時在 Chrome 上測試的輸出：",[22,1996,1999],{"className":1997,"code":1998,"language":426,"meta":27},[424],"isFirefox: false\nisChrome: true\nisSafari: false\nisOpera: false\nisIE: false\nisEdge: false\nisBlink: true\n",[29,2000,1998],{"__ignoreMap":27},[15,2002,2004],{"id":2003},"_2026-年後記","2026 年後記",[11,2006,2007],{},"這篇是 2017 年留下來的瀏覽器相容性筆記，所以保留了當時使用 User Agent、IE 條件註解、jQuery 與瀏覽器特徵判斷的寫法。",[11,2009,2010],{},"現在瀏覽器環境已經和當時差很多，像是 IE、舊版 Edge、Windows Phone，以及程式碼裡拿來判斷瀏覽器的部分 API，都已經不適合直接套用到現在的新專案。",[11,2012,2013,2014,2017],{},"原文中的 ",[29,2015,2016],{},"AlphaImageLoader"," 也是早期 Internet Explorer 使用的專有濾鏡。這段程式碼保留下來，是作為當時處理瀏覽器相容性問題的紀錄，不建議拿來當現在的實作方式。",[11,2019,2020],{},"至於現在如果需要判斷裝置或瀏覽器能力，我會另外整理新的做法；這篇就先保留當年的版本。",[368,2022,2023],{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}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 .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sh9l7, html code.shiki .sh9l7{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#7EE787;--shiki-dark-font-weight:bold}",{"title":27,"searchDepth":51,"depth":51,"links":2025},[2026,2027,2028,2029,2030,2031],{"id":404,"depth":45,"text":405},{"id":431,"depth":45,"text":431},{"id":861,"depth":45,"text":862},{"id":1210,"depth":45,"text":1211},{"id":1454,"depth":45,"text":1455},{"id":2003,"depth":45,"text":2004},"2017 年整理的瀏覽器與手機裝置判斷筆記，包含 User Agent、IE 判斷、CSS Hack 與不同瀏覽器的判斷方式。",{},"\u002Fposts\u002Flegacy-browser-detection","2017-01-26",[],{"title":396,"description":2032},"posts\u002F20170126.legacy-browser-detection",[390,2040,2041,2042],"jQuery","IE","瀏覽器偵測","upOlKnPsbMT6pXq5GRVGBk0NuzYSLqmR31NLIB357l4",{"id":2045,"title":2046,"body":2047,"category":4012,"description":4013,"draft":380,"extension":381,"image":382,"meta":4014,"navigation":102,"path":4015,"publishedAt":4016,"relatedExperiments":4017,"seo":4018,"stem":4019,"tags":4020,"updatedAt":392,"__hash__":4023},"posts\u002Fposts\u002F20180105.git-commands-cheatsheet.md","Git 常用指令與終端機筆記",{"type":8,"value":2048,"toc":3946},[2049,2052,2055,2067,2070,2083,2086,2098,2101,2114,2117,2130,2133,2142,2145,2148,2161,2164,2177,2180,2183,2193,2196,2209,2212,2218,2221,2239,2242,2254,2257,2273,2276,2291,2294,2312,2315,2327,2330,2345,2352,2367,2370,2375,2379,2391,2394,2398,2401,2407,2410,2413,2425,2428,2434,2437,2440,2455,2458,2461,2464,2470,2473,2476,2479,2482,2497,2500,2514,2517,2520,2538,2544,2560,2563,2569,2572,2584,2587,2590,2596,2599,2617,2620,2637,2640,2642,2648,2651,2654,2673,2677,2696,2700,2717,2721,2724,2749,2752,2770,2773,2776,2779,2785,2789,2792,2821,2824,2830,2833,2839,2852,2855,2861,2864,2869,2872,2875,2889,2892,2897,2900,2903,2909,2912,2926,2929,2932,2946,2949,2965,2969,2972,2984,2986,3001,3007,3009,3023,3026,3040,3043,3047,3064,3067,3071,3087,3090,3097,3101,3117,3120,3123,3127,3139,3142,3145,3148,3154,3157,3163,3166,3169,3181,3184,3199,3202,3218,3221,3224,3237,3240,3256,3259,3261,3267,3270,3276,3279,3282,3284,3290,3293,3296,3310,3313,3327,3330,3333,3339,3343,3346,3363,3366,3369,3374,3376,3380,3383,3398,3401,3407,3411,3414,3429,3432,3466,3469,3499,3502,3505,3511,3515,3518,3520,3539,3543,3546,3565,3568,3580,3583,3594,3597,3642,3645,3671,3675,3678,3684,3687,3693,3696,3702,3705,3710,3714,3717,3723,3726,3739,3742,3744,3755,3758,3765,3776,3779,3791,3794,3808,3811,3823,3826,3840,3846,3852,3855,3867,3870,3876,3879,3886,3898,3901,3904,3907,3913,3924,3930,3940,3943],[11,2050,2051],{},"這篇是當時學 Git 時整理下來的指令筆記，除了 Git 本身，也一起記了一些終端機、SourceTree 和 Vim 的操作。",[15,2053,2054],{"id":2054},"基本終端機指令",[22,2056,2060],{"className":2057,"code":2058,"language":2059,"meta":27,"style":27},"language-bash shiki shiki-themes github-light github-dark-default","ls\n","bash",[29,2061,2062],{"__ignoreMap":27},[32,2063,2064],{"class":34,"line":35},[32,2065,2058],{"class":2066},"sYRkA",[11,2068,2069],{},"可以看到目前目錄下的檔案。",[22,2071,2073],{"className":2057,"code":2072,"language":2059,"meta":27,"style":27},"ls -a\n",[29,2074,2075],{"__ignoreMap":27},[32,2076,2077,2080],{"class":34,"line":35},[32,2078,2079],{"class":2066},"ls",[32,2081,2082],{"class":351}," -a\n",[11,2084,2085],{},"顯示包含隱藏檔案在內的內容。",[22,2087,2089],{"className":2057,"code":2088,"language":2059,"meta":27,"style":27},"ls -al\n",[29,2090,2091],{"__ignoreMap":27},[32,2092,2093,2095],{"class":34,"line":35},[32,2094,2079],{"class":2066},[32,2096,2097],{"class":351}," -al\n",[11,2099,2100],{},"顯示比較完整的檔案資訊。",[22,2102,2104],{"className":2057,"code":2103,"language":2059,"meta":27,"style":27},"which git\n",[29,2105,2106],{"__ignoreMap":27},[32,2107,2108,2111],{"class":34,"line":35},[32,2109,2110],{"class":351},"which",[32,2112,2113],{"class":175}," git\n",[11,2115,2116],{},"查看 Git 安裝在哪裡。",[22,2118,2120],{"className":2057,"code":2119,"language":2059,"meta":27,"style":27},"git --version\n",[29,2121,2122],{"__ignoreMap":27},[32,2123,2124,2127],{"class":34,"line":35},[32,2125,2126],{"class":2066},"git",[32,2128,2129],{"class":351}," --version\n",[11,2131,2132],{},"查看目前 Git 版本。",[22,2134,2136],{"className":2057,"code":2135,"language":2059,"meta":27,"style":27},"pwd\n",[29,2137,2138],{"__ignoreMap":27},[32,2139,2140],{"class":34,"line":35},[32,2141,2135],{"class":351},[11,2143,2144],{},"查看現在所在的位置。",[151,2146,2147],{"id":2147},"建立檔案與資料夾",[22,2149,2151],{"className":2057,"code":2150,"language":2059,"meta":27,"style":27},"touch git.html\n",[29,2152,2153],{"__ignoreMap":27},[32,2154,2155,2158],{"class":34,"line":35},[32,2156,2157],{"class":2066},"touch",[32,2159,2160],{"class":175}," git.html\n",[11,2162,2163],{},"建立檔案。",[22,2165,2167],{"className":2057,"code":2166,"language":2059,"meta":27,"style":27},"mkdir test\n",[29,2168,2169],{"__ignoreMap":27},[32,2170,2171,2174],{"class":34,"line":35},[32,2172,2173],{"class":2066},"mkdir",[32,2175,2176],{"class":175}," test\n",[11,2178,2179],{},"建立新的資料夾。",[11,2181,2182],{},"當時有特別記：",[2184,2185,2186],"blockquote",{},[11,2187,2188,2189,2192],{},"建立空的資料夾時 Git 不會感應到，要在資料夾裡面放入檔案後，",[29,2190,2191],{},"git status"," 才會出現。\n空的目錄本身無法提交到 Git。",[151,2194,2195],{"id":2195},"查看與操作檔案",[22,2197,2199],{"className":2057,"code":2198,"language":2059,"meta":27,"style":27},"echo \"hello\"\n",[29,2200,2201],{"__ignoreMap":27},[32,2202,2203,2206],{"class":34,"line":35},[32,2204,2205],{"class":351},"echo",[32,2207,2208],{"class":175}," \"hello\"\n",[11,2210,2211],{},"列印出：",[22,2213,2216],{"className":2214,"code":2215,"language":426,"meta":27},[424],"hello\n",[29,2217,2215],{"__ignoreMap":27},[11,2219,2220],{},"把內容寫入檔案：",[22,2222,2224],{"className":2057,"code":2223,"language":2059,"meta":27,"style":27},"echo \"hello\" > world.html\n",[29,2225,2226],{"__ignoreMap":27},[32,2227,2228,2230,2233,2236],{"class":34,"line":35},[32,2229,2205],{"class":351},[32,2231,2232],{"class":175}," \"hello\"",[32,2234,2235],{"class":38}," >",[32,2237,2238],{"class":175}," world.html\n",[11,2240,2241],{},"查看檔案內容：",[22,2243,2245],{"className":2057,"code":2244,"language":2059,"meta":27,"style":27},"cat world.html\n",[29,2246,2247],{"__ignoreMap":27},[32,2248,2249,2252],{"class":34,"line":35},[32,2250,2251],{"class":2066},"cat",[32,2253,2238],{"class":175},[11,2255,2256],{},"複製檔案：",[22,2258,2260],{"className":2057,"code":2259,"language":2059,"meta":27,"style":27},"cp test.html asd.html\n",[29,2261,2262],{"__ignoreMap":27},[32,2263,2264,2267,2270],{"class":34,"line":35},[32,2265,2266],{"class":2066},"cp",[32,2268,2269],{"class":175}," test.html",[32,2271,2272],{"class":175}," asd.html\n",[11,2274,2275],{},"移動或重新命名：",[22,2277,2279],{"className":2057,"code":2278,"language":2059,"meta":27,"style":27},"mv test.html cde.html\n",[29,2280,2281],{"__ignoreMap":27},[32,2282,2283,2286,2288],{"class":34,"line":35},[32,2284,2285],{"class":2066},"mv",[32,2287,2269],{"class":175},[32,2289,2290],{"class":175}," cde.html\n",[11,2292,2293],{},"也可以直接用：",[22,2295,2297],{"className":2057,"code":2296,"language":2059,"meta":27,"style":27},"git mv test2.html ruby.html\n",[29,2298,2299],{"__ignoreMap":27},[32,2300,2301,2303,2306,2309],{"class":34,"line":35},[32,2302,2126],{"class":2066},[32,2304,2305],{"class":175}," mv",[32,2307,2308],{"class":175}," test2.html",[32,2310,2311],{"class":175}," ruby.html\n",[151,2313,2314],{"id":2314},"刪除檔案",[22,2316,2318],{"className":2057,"code":2317,"language":2059,"meta":27,"style":27},"rm cde.html\n",[29,2319,2320],{"__ignoreMap":27},[32,2321,2322,2325],{"class":34,"line":35},[32,2323,2324],{"class":2066},"rm",[32,2326,2290],{"class":175},[11,2328,2329],{},"直接刪除檔案。",[22,2331,2333],{"className":2057,"code":2332,"language":2059,"meta":27,"style":27},"rm *.html\n",[29,2334,2335],{"__ignoreMap":27},[32,2336,2337,2339,2342],{"class":34,"line":35},[32,2338,2324],{"class":2066},[32,2340,2341],{"class":351}," *",[32,2343,2344],{"class":175},".html\n",[11,2346,2347,2348,2351],{},"刪除目前目錄下所有 ",[29,2349,2350],{},".html"," 檔案。",[22,2353,2355],{"className":2057,"code":2354,"language":2059,"meta":27,"style":27},"rm -rf .git\n",[29,2356,2357],{"__ignoreMap":27},[32,2358,2359,2361,2364],{"class":34,"line":35},[32,2360,2324],{"class":2066},[32,2362,2363],{"class":351}," -rf",[32,2365,2366],{"class":175}," .git\n",[11,2368,2369],{},"刪除目前專案的 Git 版控資料。",[2184,2371,2372],{},[11,2373,2374],{},"這個指令要非常小心。",[15,2376,2378],{"id":2377},"git-初始化","Git 初始化",[22,2380,2382],{"className":2057,"code":2381,"language":2059,"meta":27,"style":27},"git init\n",[29,2383,2384],{"__ignoreMap":27},[32,2385,2386,2388],{"class":34,"line":35},[32,2387,2126],{"class":2066},[32,2389,2390],{"class":175}," init\n",[11,2392,2393],{},"初始化 Git，讓目前這個目錄開始由 Git 控管。",[151,2395,2397],{"id":2396},"sourcetree","SourceTree",[11,2399,2400],{},"當時使用 SourceTree 的方式：",[22,2402,2405],{"className":2403,"code":2404,"language":426,"meta":27},[424],"New \u002F Create Local Repository\n",[29,2406,2404],{"__ignoreMap":27},[11,2408,2409],{},"建立新的本機 Repository。",[15,2411,2412],{"id":2412},"查看目前狀態",[22,2414,2416],{"className":2057,"code":2415,"language":2059,"meta":27,"style":27},"git status\n",[29,2417,2418],{"__ignoreMap":27},[32,2419,2420,2422],{"class":34,"line":35},[32,2421,2126],{"class":2066},[32,2423,2424],{"class":175}," status\n",[11,2426,2427],{},"如果看到：",[22,2429,2432],{"className":2430,"code":2431,"language":426,"meta":27},[424],"nothing to commit\n",[29,2433,2431],{"__ignoreMap":27},[11,2435,2436],{},"代表目前沒有新的變更需要提交。",[15,2438,2439],{"id":2439},"查詢檔案作者",[22,2441,2443],{"className":2057,"code":2442,"language":2059,"meta":27,"style":27},"git blame index.html\n",[29,2444,2445],{"__ignoreMap":27},[32,2446,2447,2449,2452],{"class":34,"line":35},[32,2448,2126],{"class":2066},[32,2450,2451],{"class":175}," blame",[32,2453,2454],{"class":175}," index.html\n",[11,2456,2457],{},"查看檔案每一行最後是由誰修改。",[151,2459,2397],{"id":2460},"sourcetree-1",[11,2462,2463],{},"對檔案按右鍵：",[22,2465,2468],{"className":2466,"code":2467,"language":426,"meta":27},[424],"Blame Selected\n",[29,2469,2467],{"__ignoreMap":27},[11,2471,2472],{},"也可以查看每一行的修改紀錄。",[15,2474,2475],{"id":2475},"加入暫存區",[11,2477,2478],{},"工作目錄 → 暫存區。",[11,2480,2481],{},"加入單一檔案：",[22,2483,2485],{"className":2057,"code":2484,"language":2059,"meta":27,"style":27},"git add welcome.html\n",[29,2486,2487],{"__ignoreMap":27},[32,2488,2489,2491,2494],{"class":34,"line":35},[32,2490,2126],{"class":2066},[32,2492,2493],{"class":175}," add",[32,2495,2496],{"class":175}," welcome.html\n",[11,2498,2499],{},"全部加入：",[22,2501,2503],{"className":2057,"code":2502,"language":2059,"meta":27,"style":27},"git add --all\n",[29,2504,2505],{"__ignoreMap":27},[32,2506,2507,2509,2511],{"class":34,"line":35},[32,2508,2126],{"class":2066},[32,2510,2493],{"class":175},[32,2512,2513],{"class":351}," --all\n",[15,2515,2516],{"id":2516},"提交",[11,2518,2519],{},"暫存區 → Git 紀錄。",[22,2521,2523],{"className":2057,"code":2522,"language":2059,"meta":27,"style":27},"git commit -m \"init commit\"\n",[29,2524,2525],{"__ignoreMap":27},[32,2526,2527,2529,2532,2535],{"class":34,"line":35},[32,2528,2126],{"class":2066},[32,2530,2531],{"class":175}," commit",[32,2533,2534],{"class":351}," -m",[32,2536,2537],{"class":175}," \"init commit\"\n",[11,2539,2540,2543],{},[29,2541,2542],{},"-m"," 後面接這次提交的訊息，例如：",[22,2545,2547],{"className":2057,"code":2546,"language":2059,"meta":27,"style":27},"git commit -m \"#34 bug fix\"\n",[29,2548,2549],{"__ignoreMap":27},[32,2550,2551,2553,2555,2557],{"class":34,"line":35},[32,2552,2126],{"class":2066},[32,2554,2531],{"class":175},[32,2556,2534],{"class":351},[32,2558,2559],{"class":175}," \"#34 bug fix\"\n",[11,2561,2562],{},"當時記的基本操作順序：",[22,2564,2567],{"className":2565,"code":2566,"language":426,"meta":27},[424],"git status\n↓\ngit add 檔案\n↓\ngit commit -m \"訊息\"\n",[29,2568,2566],{"__ignoreMap":27},[15,2570,2571],{"id":2571},"查詢歷史紀錄",[22,2573,2575],{"className":2057,"code":2574,"language":2059,"meta":27,"style":27},"git log\n",[29,2576,2577],{"__ignoreMap":27},[32,2578,2579,2581],{"class":34,"line":35},[32,2580,2126],{"class":2066},[32,2582,2583],{"class":175}," log\n",[11,2585,2586],{},"查看提交歷史。",[11,2588,2589],{},"每一筆 commit 都會有自己的 SHA：",[22,2591,2594],{"className":2592,"code":2593,"language":426,"meta":27},[424],"commit b11569e1ef6b1c873aa184350e07e490f78d3f50\n",[29,2595,2593],{"__ignoreMap":27},[11,2597,2598],{},"想看得簡單一點：",[22,2600,2602],{"className":2057,"code":2601,"language":2059,"meta":27,"style":27},"git log --oneline --graph\n",[29,2603,2604],{"__ignoreMap":27},[32,2605,2606,2608,2611,2614],{"class":34,"line":35},[32,2607,2126],{"class":2066},[32,2609,2610],{"class":175}," log",[32,2612,2613],{"class":351}," --oneline",[32,2615,2616],{"class":351}," --graph\n",[151,2618,2619],{"id":2619},"查詢單一檔案紀錄",[22,2621,2623],{"className":2057,"code":2622,"language":2059,"meta":27,"style":27},"git log -p test.html\n",[29,2624,2625],{"__ignoreMap":27},[32,2626,2627,2629,2631,2634],{"class":34,"line":35},[32,2628,2126],{"class":2066},[32,2630,2610],{"class":175},[32,2632,2633],{"class":351}," -p",[32,2635,2636],{"class":175}," test.html\n",[151,2638,2397],{"id":2639},"sourcetree-2",[11,2641,2463],{},[22,2643,2646],{"className":2644,"code":2645,"language":426,"meta":27},[424],"Log Selected\n",[29,2647,2645],{"__ignoreMap":27},[11,2649,2650],{},"可以查看這個檔案的歷史紀錄。",[151,2652,2653],{"id":2653},"查詢作者",[22,2655,2657],{"className":2057,"code":2656,"language":2059,"meta":27,"style":27},"git log --oneline --author=\"XXX\"\n",[29,2658,2659],{"__ignoreMap":27},[32,2660,2661,2663,2665,2667,2670],{"class":34,"line":35},[32,2662,2126],{"class":2066},[32,2664,2610],{"class":175},[32,2666,2613],{"class":351},[32,2668,2669],{"class":351}," --author=",[32,2671,2672],{"class":175},"\"XXX\"\n",[151,2674,2676],{"id":2675},"查詢-commit-訊息","查詢 Commit 訊息",[22,2678,2680],{"className":2057,"code":2679,"language":2059,"meta":27,"style":27},"git log --oneline --grep=\"wtf\"\n",[29,2681,2682],{"__ignoreMap":27},[32,2683,2684,2686,2688,2690,2693],{"class":34,"line":35},[32,2685,2126],{"class":2066},[32,2687,2610],{"class":175},[32,2689,2613],{"class":351},[32,2691,2692],{"class":351}," --grep=",[32,2694,2695],{"class":175},"\"wtf\"\n",[151,2697,2699],{"id":2698},"查詢曾經加入或刪除某段文字的-commit","查詢曾經加入或刪除某段文字的 Commit",[22,2701,2703],{"className":2057,"code":2702,"language":2059,"meta":27,"style":27},"git log -S \"xxx\"\n",[29,2704,2705],{"__ignoreMap":27},[32,2706,2707,2709,2711,2714],{"class":34,"line":35},[32,2708,2126],{"class":2066},[32,2710,2610],{"class":175},[32,2712,2713],{"class":351}," -S",[32,2715,2716],{"class":175}," \"xxx\"\n",[151,2718,2720],{"id":2719},"查詢一段時間內的-commit","查詢一段時間內的 Commit",[11,2722,2723],{},"例如查今天早上的紀錄：",[22,2725,2727],{"className":2057,"code":2726,"language":2059,"meta":27,"style":27},"git log --oneline --since=\"9am\" --until=\"12pm\"\n",[29,2728,2729],{"__ignoreMap":27},[32,2730,2731,2733,2735,2737,2740,2743,2746],{"class":34,"line":35},[32,2732,2126],{"class":2066},[32,2734,2610],{"class":175},[32,2736,2613],{"class":351},[32,2738,2739],{"class":351}," --since=",[32,2741,2742],{"class":175},"\"9am\"",[32,2744,2745],{"class":351}," --until=",[32,2747,2748],{"class":175},"\"12pm\"\n",[15,2750,2751],{"id":2751},"從暫存區移除",[22,2753,2755],{"className":2057,"code":2754,"language":2059,"meta":27,"style":27},"git rm --cached xxx.html\n",[29,2756,2757],{"__ignoreMap":27},[32,2758,2759,2761,2764,2767],{"class":34,"line":35},[32,2760,2126],{"class":2066},[32,2762,2763],{"class":175}," rm",[32,2765,2766],{"class":351}," --cached",[32,2768,2769],{"class":175}," xxx.html\n",[11,2771,2772],{},"不是刪除檔案，而是停止追蹤這個檔案。",[151,2774,2397],{"id":2775},"sourcetree-3",[11,2777,2778],{},"在暫存區對檔案按右鍵：",[22,2780,2783],{"className":2781,"code":2782,"language":426,"meta":27},[424],"Stop Tracking\n",[29,2784,2782],{"__ignoreMap":27},[15,2786,2788],{"id":2787},"修改最後一次-commit","修改最後一次 Commit",[11,2790,2791],{},"如果想把新的檔案合併到上一筆 Git 紀錄：",[22,2793,2795],{"className":2057,"code":2794,"language":2059,"meta":27,"style":27},"touch cat.html\ngit add cat.html\ngit commit --amend\n",[29,2796,2797,2804,2812],{"__ignoreMap":27},[32,2798,2799,2801],{"class":34,"line":35},[32,2800,2157],{"class":2066},[32,2802,2803],{"class":175}," cat.html\n",[32,2805,2806,2808,2810],{"class":34,"line":45},[32,2807,2126],{"class":2066},[32,2809,2493],{"class":175},[32,2811,2803],{"class":175},[32,2813,2814,2816,2818],{"class":34,"line":51},[32,2815,2126],{"class":2066},[32,2817,2531],{"class":175},[32,2819,2820],{"class":351}," --amend\n",[11,2822,2823],{},"進入 Vim 後：",[22,2825,2828],{"className":2826,"code":2827,"language":426,"meta":27},[424],":wq\n",[29,2829,2827],{"__ignoreMap":27},[11,2831,2832],{},"存檔並離開。",[11,2834,2835,2838],{},[29,2836,2837],{},"git commit --amend"," 也可以用來修改最後一次 Commit 的訊息：",[22,2840,2842],{"className":2057,"code":2841,"language":2059,"meta":27,"style":27},"git commit --amend\n",[29,2843,2844],{"__ignoreMap":27},[32,2845,2846,2848,2850],{"class":34,"line":35},[32,2847,2126],{"class":2066},[32,2849,2531],{"class":175},[32,2851,2820],{"class":351},[11,2853,2854],{},"進入編輯畫面後：",[22,2856,2859],{"className":2857,"code":2858,"language":426,"meta":27},[424],"i\n",[29,2860,2858],{"__ignoreMap":27},[11,2862,2863],{},"進入輸入模式，修改訊息後：",[22,2865,2867],{"className":2866,"code":2827,"language":426,"meta":27},[424],[29,2868,2827],{"__ignoreMap":27},[11,2870,2871],{},"存檔離開。",[11,2873,2874],{},"再查看：",[22,2876,2878],{"className":2057,"code":2877,"language":2059,"meta":27,"style":27},"git log --oneline\n",[29,2879,2880],{"__ignoreMap":27},[32,2881,2882,2884,2886],{"class":34,"line":35},[32,2883,2126],{"class":2066},[32,2885,2610],{"class":175},[32,2887,2888],{"class":351}," --oneline\n",[11,2890,2891],{},"當時也有記到一件事：",[2184,2893,2894],{},[11,2895,2896],{},"Commit 經過 amend 後，SHA 值也會跟著改變。",[151,2898,2397],{"id":2899},"sourcetree-4",[11,2901,2902],{},"當時的操作路徑：",[22,2904,2907],{"className":2905,"code":2906,"language":426,"meta":27},[424],"Working Copy\n→ Commit Options\n→ Amend Last Commit\n→ 修改訊息\n→ Commit\n",[29,2908,2906],{"__ignoreMap":27},[15,2910,2911],{"id":2911},"還原檔案",[22,2913,2915],{"className":2057,"code":2914,"language":2059,"meta":27,"style":27},"git checkout test.html\n",[29,2916,2917],{"__ignoreMap":27},[32,2918,2919,2921,2924],{"class":34,"line":35},[32,2920,2126],{"class":2066},[32,2922,2923],{"class":175}," checkout",[32,2925,2636],{"class":175},[11,2927,2928],{},"把尚未提交的檔案變更還原。",[11,2930,2931],{},"全部還原：",[22,2933,2935],{"className":2057,"code":2934,"language":2059,"meta":27,"style":27},"git checkout .\n",[29,2936,2937],{"__ignoreMap":27},[32,2938,2939,2941,2943],{"class":34,"line":35},[32,2940,2126],{"class":2066},[32,2942,2923],{"class":175},[32,2944,2945],{"class":175}," .\n",[11,2947,2948],{},"拿兩個版本之前的檔案內容：",[22,2950,2952],{"className":2057,"code":2951,"language":2059,"meta":27,"style":27},"git checkout HEAD~2 test.html\n",[29,2953,2954],{"__ignoreMap":27},[32,2955,2956,2958,2960,2963],{"class":34,"line":35},[32,2957,2126],{"class":2066},[32,2959,2923],{"class":175},[32,2961,2962],{"class":175}," HEAD~2",[32,2964,2636],{"class":175},[15,2966,2968],{"id":2967},"reset拆掉-commit-重做","Reset：拆掉 Commit 重做",[11,2970,2971],{},"先查看歷史：",[22,2973,2974],{"className":2057,"code":2877,"language":2059,"meta":27,"style":27},[29,2975,2976],{"__ignoreMap":27},[32,2977,2978,2980,2982],{"class":34,"line":35},[32,2979,2126],{"class":2066},[32,2981,2610],{"class":175},[32,2983,2888],{"class":351},[11,2985,328],{},[22,2987,2989],{"className":2057,"code":2988,"language":2059,"meta":27,"style":27},"git reset ae8a3ab^\n",[29,2990,2991],{"__ignoreMap":27},[32,2992,2993,2995,2998],{"class":34,"line":35},[32,2994,2126],{"class":2066},[32,2996,2997],{"class":175}," reset",[32,2999,3000],{"class":175}," ae8a3ab^\n",[11,3002,3003,3006],{},[29,3004,3005],{},"^"," 可以理解成往前一個 Commit。",[11,3008,328],{},[22,3010,3012],{"className":2057,"code":3011,"language":2059,"meta":27,"style":27},"git reset HEAD^\n",[29,3013,3014],{"__ignoreMap":27},[32,3015,3016,3018,3020],{"class":34,"line":35},[32,3017,2126],{"class":2066},[32,3019,2997],{"class":175},[32,3021,3022],{"class":175}," HEAD^\n",[11,3024,3025],{},"往前兩個：",[22,3027,3029],{"className":2057,"code":3028,"language":2059,"meta":27,"style":27},"git reset HEAD~2\n",[29,3030,3031],{"__ignoreMap":27},[32,3032,3033,3035,3037],{"class":34,"line":35},[32,3034,2126],{"class":2066},[32,3036,2997],{"class":175},[32,3038,3039],{"class":175}," HEAD~2\n",[11,3041,3042],{},"當時整理的三種模式：",[151,3044,3046],{"id":3045},"soft","Soft",[22,3048,3050],{"className":2057,"code":3049,"language":2059,"meta":27,"style":27},"git reset --soft 版號\n",[29,3051,3052],{"__ignoreMap":27},[32,3053,3054,3056,3058,3061],{"class":34,"line":35},[32,3055,2126],{"class":2066},[32,3057,2997],{"class":175},[32,3059,3060],{"class":351}," --soft",[32,3062,3063],{"class":175}," 版號\n",[11,3065,3066],{},"拆出來的檔案會保留在暫存區。",[151,3068,3070],{"id":3069},"mixed","Mixed",[22,3072,3074],{"className":2057,"code":3073,"language":2059,"meta":27,"style":27},"git reset --mixed 版號\n",[29,3075,3076],{"__ignoreMap":27},[32,3077,3078,3080,3082,3085],{"class":34,"line":35},[32,3079,2126],{"class":2066},[32,3081,2997],{"class":175},[32,3083,3084],{"class":351}," --mixed",[32,3086,3063],{"class":175},[11,3088,3089],{},"檔案會保留在工作目錄。",[11,3091,3092,3093,3096],{},"這也是 ",[29,3094,3095],{},"git reset"," 預設的模式。",[151,3098,3100],{"id":3099},"hard","Hard",[22,3102,3104],{"className":2057,"code":3103,"language":2059,"meta":27,"style":27},"git reset --hard 版號\n",[29,3105,3106],{"__ignoreMap":27},[32,3107,3108,3110,3112,3115],{"class":34,"line":35},[32,3109,2126],{"class":2066},[32,3111,2997],{"class":175},[32,3113,3114],{"class":351}," --hard",[32,3116,3063],{"class":175},[11,3118,3119],{},"工作目錄的變更也一起丟掉。",[11,3121,3122],{},"這個要特別小心。",[151,3124,3126],{"id":3125},"reflog","Reflog",[22,3128,3130],{"className":2057,"code":3129,"language":2059,"meta":27,"style":27},"git reflog\n",[29,3131,3132],{"__ignoreMap":27},[32,3133,3134,3136],{"class":34,"line":35},[32,3135,2126],{"class":2066},[32,3137,3138],{"class":175}," reflog\n",[11,3140,3141],{},"可以查看 Git 的 HEAD \u002F 分支移動紀錄。",[151,3143,2397],{"id":3144},"sourcetree-5",[11,3146,3147],{},"對某個 Commit 按右鍵：",[22,3149,3152],{"className":3150,"code":3151,"language":426,"meta":27},[424],"Reset master to this commit\n",[29,3153,3151],{"__ignoreMap":27},[11,3155,3156],{},"再選擇：",[22,3158,3161],{"className":3159,"code":3160,"language":426,"meta":27},[424],"Soft\nMixed\nHard\n",[29,3162,3160],{"__ignoreMap":27},[15,3164,3165],{"id":3165},"分支",[11,3167,3168],{},"查看目前分支：",[22,3170,3172],{"className":2057,"code":3171,"language":2059,"meta":27,"style":27},"git branch\n",[29,3173,3174],{"__ignoreMap":27},[32,3175,3176,3178],{"class":34,"line":35},[32,3177,2126],{"class":2066},[32,3179,3180],{"class":175}," branch\n",[11,3182,3183],{},"建立分支：",[22,3185,3187],{"className":2057,"code":3186,"language":2059,"meta":27,"style":27},"git branch xxx\n",[29,3188,3189],{"__ignoreMap":27},[32,3190,3191,3193,3196],{"class":34,"line":35},[32,3192,2126],{"class":2066},[32,3194,3195],{"class":175}," branch",[32,3197,3198],{"class":175}," xxx\n",[11,3200,3201],{},"刪除分支：",[22,3203,3205],{"className":2057,"code":3204,"language":2059,"meta":27,"style":27},"git branch -d xxx\n",[29,3206,3207],{"__ignoreMap":27},[32,3208,3209,3211,3213,3216],{"class":34,"line":35},[32,3210,2126],{"class":2066},[32,3212,3195],{"class":175},[32,3214,3215],{"class":351}," -d",[32,3217,3198],{"class":175},[11,3219,3220],{},"不能刪除目前正在使用的分支，需要先切換到其他分支。",[11,3222,3223],{},"切換分支：",[22,3225,3227],{"className":2057,"code":3226,"language":2059,"meta":27,"style":27},"git checkout xxx\n",[29,3228,3229],{"__ignoreMap":27},[32,3230,3231,3233,3235],{"class":34,"line":35},[32,3232,2126],{"class":2066},[32,3234,2923],{"class":175},[32,3236,3198],{"class":175},[11,3238,3239],{},"建立並直接切換：",[22,3241,3243],{"className":2057,"code":3242,"language":2059,"meta":27,"style":27},"git checkout -b xxx\n",[29,3244,3245],{"__ignoreMap":27},[32,3246,3247,3249,3251,3254],{"class":34,"line":35},[32,3248,2126],{"class":2066},[32,3250,2923],{"class":175},[32,3252,3253],{"class":351}," -b",[32,3255,3198],{"class":175},[151,3257,2397],{"id":3258},"sourcetree-6",[11,3260,3183],{},[22,3262,3265],{"className":3263,"code":3264,"language":426,"meta":27},[424],"Branch\n",[29,3266,3264],{"__ignoreMap":27},[11,3268,3269],{},"如果勾選：",[22,3271,3274],{"className":3272,"code":3273,"language":426,"meta":27},[424],"Checkout New Branch\n",[29,3275,3273],{"__ignoreMap":27},[11,3277,3278],{},"建立完成後會直接切換過去。",[11,3280,3281],{},"切換分支時，也可以直接對分支點兩下。",[11,3283,3201],{},[22,3285,3288],{"className":3286,"code":3287,"language":426,"meta":27},[424],"右鍵分支\n→ Delete xxx\n",[29,3289,3287],{"__ignoreMap":27},[15,3291,3292],{"id":3292},"合併分支",[11,3294,3295],{},"先切換回主線：",[22,3297,3299],{"className":2057,"code":3298,"language":2059,"meta":27,"style":27},"git checkout master\n",[29,3300,3301],{"__ignoreMap":27},[32,3302,3303,3305,3307],{"class":34,"line":35},[32,3304,2126],{"class":2066},[32,3306,2923],{"class":175},[32,3308,3309],{"class":175}," master\n",[11,3311,3312],{},"再把其他分支合併進來：",[22,3314,3316],{"className":2057,"code":3315,"language":2059,"meta":27,"style":27},"git merge xxx\n",[29,3317,3318],{"__ignoreMap":27},[32,3319,3320,3322,3325],{"class":34,"line":35},[32,3321,2126],{"class":2066},[32,3323,3324],{"class":175}," merge",[32,3326,3198],{"class":175},[151,3328,2397],{"id":3329},"sourcetree-7",[11,3331,3332],{},"對想合併的分支按右鍵：",[22,3334,3337],{"className":3335,"code":3336,"language":426,"meta":27},[424],"Merge xxx into current branch\n",[29,3338,3336],{"__ignoreMap":27},[15,3340,3342],{"id":3341},"不使用-fast-forward-合併","不使用 Fast-forward 合併",[11,3344,3345],{},"當時把這個畫面記成「小耳朵」。",[22,3347,3349],{"className":2057,"code":3348,"language":2059,"meta":27,"style":27},"git merge cat --no-ff\n",[29,3350,3351],{"__ignoreMap":27},[32,3352,3353,3355,3357,3360],{"class":34,"line":35},[32,3354,2126],{"class":2066},[32,3356,3324],{"class":175},[32,3358,3359],{"class":175}," cat",[32,3361,3362],{"class":351}," --no-ff\n",[11,3364,3365],{},"會保留一筆 Merge Commit。",[11,3367,3368],{},"如果進入 Vim：",[22,3370,3372],{"className":3371,"code":2827,"language":426,"meta":27},[424],[29,3373,2827],{"__ignoreMap":27},[11,3375,2871],{},[15,3377,3379],{"id":3378},"clone-專案","Clone 專案",[11,3381,3382],{},"把專案下載到本機：",[22,3384,3386],{"className":2057,"code":3385,"language":2059,"meta":27,"style":27},"git clone xxxxxxxxxxxxx\n",[29,3387,3388],{"__ignoreMap":27},[32,3389,3390,3392,3395],{"class":34,"line":35},[32,3391,2126],{"class":2066},[32,3393,3394],{"class":175}," clone",[32,3396,3397],{"class":175}," xxxxxxxxxxxxx\n",[151,3399,2397],{"id":3400},"sourcetree-8",[22,3402,3405],{"className":3403,"code":3404,"language":426,"meta":27},[424],"File\n→ New\n→ Clone Repository\n→ 貼上 Repository 資訊\n",[29,3406,3404],{"__ignoreMap":27},[15,3408,3410],{"id":3409},"git-config","Git Config",[11,3412,3413],{},"查看目前設定：",[22,3415,3417],{"className":2057,"code":3416,"language":2059,"meta":27,"style":27},"git config --list\n",[29,3418,3419],{"__ignoreMap":27},[32,3420,3421,3423,3426],{"class":34,"line":35},[32,3422,2126],{"class":2066},[32,3424,3425],{"class":175}," config",[32,3427,3428],{"class":351}," --list\n",[151,3430,3431],{"id":3431},"全域設定",[22,3433,3435],{"className":2057,"code":3434,"language":2059,"meta":27,"style":27},"git config --global user.name \"XXX\"\ngit config --global user.email \"XXX@xxx.xx\"\n",[29,3436,3437,3452],{"__ignoreMap":27},[32,3438,3439,3441,3443,3446,3449],{"class":34,"line":35},[32,3440,2126],{"class":2066},[32,3442,3425],{"class":175},[32,3444,3445],{"class":351}," --global",[32,3447,3448],{"class":175}," user.name",[32,3450,3451],{"class":175}," \"XXX\"\n",[32,3453,3454,3456,3458,3460,3463],{"class":34,"line":45},[32,3455,2126],{"class":2066},[32,3457,3425],{"class":175},[32,3459,3445],{"class":351},[32,3461,3462],{"class":175}," user.email",[32,3464,3465],{"class":175}," \"XXX@xxx.xx\"\n",[151,3467,3468],{"id":3468},"專案專屬設定",[22,3470,3472],{"className":2057,"code":3471,"language":2059,"meta":27,"style":27},"git config --local user.name \"XXX\"\ngit config --local user.email \"XXX@xxx.xx\"\n",[29,3473,3474,3487],{"__ignoreMap":27},[32,3475,3476,3478,3480,3483,3485],{"class":34,"line":35},[32,3477,2126],{"class":2066},[32,3479,3425],{"class":175},[32,3481,3482],{"class":351}," --local",[32,3484,3448],{"class":175},[32,3486,3451],{"class":175},[32,3488,3489,3491,3493,3495,3497],{"class":34,"line":45},[32,3490,2126],{"class":2066},[32,3492,3425],{"class":175},[32,3494,3482],{"class":351},[32,3496,3462],{"class":175},[32,3498,3465],{"class":175},[151,3500,2397],{"id":3501},"sourcetree-9",[11,3503,3504],{},"當時的設定位置：",[22,3506,3509],{"className":3507,"code":3508,"language":426,"meta":27},[424],"Repository Settings\n→ User Information\n",[29,3510,3508],{"__ignoreMap":27},[15,3512,3514],{"id":3513},"修改-git-編輯器","修改 Git 編輯器",[11,3516,3517],{},"當時 Git 預設會進 Vim，也可以換成其他編輯器。",[11,3519,328],{},[22,3521,3523],{"className":2057,"code":3522,"language":2059,"meta":27,"style":27},"git config --global core.editor emacs\n",[29,3524,3525],{"__ignoreMap":27},[32,3526,3527,3529,3531,3533,3536],{"class":34,"line":35},[32,3528,2126],{"class":2066},[32,3530,3425],{"class":175},[32,3532,3445],{"class":351},[32,3534,3535],{"class":175}," core.editor",[32,3537,3538],{"class":175}," emacs\n",[15,3540,3542],{"id":3541},"git-alias","Git Alias",[11,3544,3545],{},"可以幫常用指令設定縮寫。",[22,3547,3549],{"className":2057,"code":3548,"language":2059,"meta":27,"style":27},"git config --global alias.co checkout\n",[29,3550,3551],{"__ignoreMap":27},[32,3552,3553,3555,3557,3559,3562],{"class":34,"line":35},[32,3554,2126],{"class":2066},[32,3556,3425],{"class":175},[32,3558,3445],{"class":351},[32,3560,3561],{"class":175}," alias.co",[32,3563,3564],{"class":175}," checkout\n",[11,3566,3567],{},"之後：",[22,3569,3571],{"className":2057,"code":3570,"language":2059,"meta":27,"style":27},"git co\n",[29,3572,3573],{"__ignoreMap":27},[32,3574,3575,3577],{"class":34,"line":35},[32,3576,2126],{"class":2066},[32,3578,3579],{"class":175}," co\n",[11,3581,3582],{},"等同：",[22,3584,3586],{"className":2057,"code":3585,"language":2059,"meta":27,"style":27},"git checkout\n",[29,3587,3588],{"__ignoreMap":27},[32,3589,3590,3592],{"class":34,"line":35},[32,3591,2126],{"class":2066},[32,3593,3564],{"class":175},[11,3595,3596],{},"其他：",[22,3598,3600],{"className":2057,"code":3599,"language":2059,"meta":27,"style":27},"git config --global alias.br branch\ngit config --global alias.st status\ngit config --global alias.l \"log --oneline --graph\"\n",[29,3601,3602,3615,3628],{"__ignoreMap":27},[32,3603,3604,3606,3608,3610,3613],{"class":34,"line":35},[32,3605,2126],{"class":2066},[32,3607,3425],{"class":175},[32,3609,3445],{"class":351},[32,3611,3612],{"class":175}," alias.br",[32,3614,3180],{"class":175},[32,3616,3617,3619,3621,3623,3626],{"class":34,"line":45},[32,3618,2126],{"class":2066},[32,3620,3425],{"class":175},[32,3622,3445],{"class":351},[32,3624,3625],{"class":175}," alias.st",[32,3627,2424],{"class":175},[32,3629,3630,3632,3634,3636,3639],{"class":34,"line":51},[32,3631,2126],{"class":2066},[32,3633,3425],{"class":175},[32,3635,3445],{"class":351},[32,3637,3638],{"class":175}," alias.l",[32,3640,3641],{"class":175}," \"log --oneline --graph\"\n",[11,3643,3644],{},"之後就可以使用：",[22,3646,3648],{"className":2057,"code":3647,"language":2059,"meta":27,"style":27},"git br\ngit st\ngit l\n",[29,3649,3650,3657,3664],{"__ignoreMap":27},[32,3651,3652,3654],{"class":34,"line":35},[32,3653,2126],{"class":2066},[32,3655,3656],{"class":175}," br\n",[32,3658,3659,3661],{"class":34,"line":45},[32,3660,2126],{"class":2066},[32,3662,3663],{"class":175}," st\n",[32,3665,3666,3668],{"class":34,"line":51},[32,3667,2126],{"class":2066},[32,3669,3670],{"class":175}," l\n",[15,3672,3674],{"id":3673},"vim-基本操作","Vim 基本操作",[11,3676,3677],{},"當時順便記了一些 Vim 操作。",[22,3679,3682],{"className":3680,"code":3681,"language":426,"meta":27},[424],"i = insert\na = append\no = new line\n",[29,3683,3681],{"__ignoreMap":27},[11,3685,3686],{},"存檔：",[22,3688,3691],{"className":3689,"code":3690,"language":426,"meta":27},[424],":w\n",[29,3692,3690],{"__ignoreMap":27},[11,3694,3695],{},"離開：",[22,3697,3700],{"className":3698,"code":3699,"language":426,"meta":27},[424],":q\n",[29,3701,3699],{"__ignoreMap":27},[11,3703,3704],{},"存檔並離開：",[22,3706,3708],{"className":3707,"code":2827,"language":426,"meta":27},[424],[29,3709,2827],{"__ignoreMap":27},[15,3711,3713],{"id":3712},"windows-指令筆記","Windows 指令筆記",[11,3715,3716],{},"當時也一起記了一些 Windows 終端機常用指令：",[22,3718,3721],{"className":3719,"code":3720,"language":426,"meta":27},[424],"cd        切換目錄\ndir       列出目前檔案\nmkdir     建立新的資料夾\ncopy      複製檔案\nmove      移動檔案\ndel       刪除檔案\ncls       清除畫面內容\ncd ..     回到上一層\n",[29,3722,3720],{"__ignoreMap":27},[11,3724,3725],{},"另外在 Unix \u002F Git Bash 類型的終端機中：",[22,3727,3729],{"className":2057,"code":3728,"language":2059,"meta":27,"style":27},"cd ~\n",[29,3730,3731],{"__ignoreMap":27},[32,3732,3733,3736],{"class":34,"line":35},[32,3734,3735],{"class":351},"cd",[32,3737,3738],{"class":175}," ~\n",[11,3740,3741],{},"可以回到目前使用者的 Home 目錄。",[15,3743,2004],{"id":2003},[11,3745,3746,3747,3750,3751,3754],{},"這篇是 2018 年學 Git 時留下來的筆記，所以保留了當時使用 ",[29,3748,3749],{},"git checkout","、",[29,3752,3753],{},"master","、SourceTree 和 Vim 的操作方式。",[11,3756,3757],{},"現在回頭看，有幾個地方需要特別補充：",[151,3759,3761,3764],{"id":3760},"checkout-現在有更明確的替代指令",[29,3762,3763],{},"checkout"," 現在有更明確的替代指令",[11,3766,3767,3768,3771,3772,3775],{},"Git 2.23 之後新增了 ",[29,3769,3770],{},"git switch"," 和 ",[29,3773,3774],{},"git restore","。",[11,3777,3778],{},"以前切換分支：",[22,3780,3781],{"className":2057,"code":3226,"language":2059,"meta":27,"style":27},[29,3782,3783],{"__ignoreMap":27},[32,3784,3785,3787,3789],{"class":34,"line":35},[32,3786,2126],{"class":2066},[32,3788,2923],{"class":175},[32,3790,3198],{"class":175},[11,3792,3793],{},"現在也可以寫：",[22,3795,3797],{"className":2057,"code":3796,"language":2059,"meta":27,"style":27},"git switch xxx\n",[29,3798,3799],{"__ignoreMap":27},[32,3800,3801,3803,3806],{"class":34,"line":35},[32,3802,2126],{"class":2066},[32,3804,3805],{"class":175}," switch",[32,3807,3198],{"class":175},[11,3809,3810],{},"以前還原工作目錄的檔案：",[22,3812,3813],{"className":2057,"code":2914,"language":2059,"meta":27,"style":27},[29,3814,3815],{"__ignoreMap":27},[32,3816,3817,3819,3821],{"class":34,"line":35},[32,3818,2126],{"class":2066},[32,3820,2923],{"class":175},[32,3822,2636],{"class":175},[11,3824,3825],{},"現在也可以使用：",[22,3827,3829],{"className":2057,"code":3828,"language":2059,"meta":27,"style":27},"git restore test.html\n",[29,3830,3831],{"__ignoreMap":27},[32,3832,3833,3835,3838],{"class":34,"line":35},[32,3834,2126],{"class":2066},[32,3836,3837],{"class":175}," restore",[32,3839,2636],{"class":175},[11,3841,3842,3843,3845],{},"原本的 ",[29,3844,3749],{}," 並沒有不能用，只是現在多了語意比較清楚的寫法。",[151,3847,3849,3850],{"id":3848},"主分支名稱不一定是-master","主分支名稱不一定是 ",[29,3851,3753],{},[11,3853,3854],{},"這篇當年使用：",[22,3856,3857],{"className":2057,"code":3298,"language":2059,"meta":27,"style":27},[29,3858,3859],{"__ignoreMap":27},[32,3860,3861,3863,3865],{"class":34,"line":35},[32,3862,2126],{"class":2066},[32,3864,2923],{"class":175},[32,3866,3309],{"class":175},[11,3868,3869],{},"現在很多新的 Repository 預設主分支會叫做：",[22,3871,3874],{"className":3872,"code":3873,"language":426,"meta":27},[424],"main\n",[29,3875,3873],{"__ignoreMap":27},[11,3877,3878],{},"實際名稱還是要看專案本身。",[151,3880,3882,3883],{"id":3881},"刪除-git","刪除 ",[29,3884,3885],{},".git",[22,3887,3888],{"className":2057,"code":2354,"language":2059,"meta":27,"style":27},[29,3889,3890],{"__ignoreMap":27},[32,3891,3892,3894,3896],{"class":34,"line":35},[32,3893,2324],{"class":2066},[32,3895,2363],{"class":351},[32,3897,2366],{"class":175},[11,3899,3900],{},"會刪除目前目錄的 Git Repository 資訊，但不會把工作目錄裡的程式碼一起刪掉。",[11,3902,3903],{},"如果專案已經有推送到 GitHub 或其他遠端 Repository，歷史紀錄也不一定真的就此消失。",[11,3905,3906],{},"不過這仍然是一個要非常小心使用的指令。",[151,3908,3910],{"id":3909},"git-reset-hard",[29,3911,3912],{},"git reset --hard",[11,3914,3915,3916,3919,3920,3923],{},"這篇以前把 ",[29,3917,3918],{},"git reflog"," 記成誤用 ",[29,3921,3922],{},"--hard"," 後的救援方式，但它並不是所有情況都能救。",[11,3925,3926,3927,3929],{},"如果丟掉的是曾經 Commit 過、Git 還保有紀錄的內容，可能可以透過 ",[29,3928,3125],{}," 找回。",[11,3931,3932,3933,3936,3937,3939],{},"但如果只是工作目錄裡從來沒有 Commit 過的修改，被 ",[29,3934,3935],{},"reset --hard"," 丟掉之後，就不能把 ",[29,3938,3125],{}," 當成一定救得回來的方法。",[11,3941,3942],{},"這篇其他內容就先保留當年的學習紀錄。",[368,3944,3945],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}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 .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}",{"title":27,"searchDepth":51,"depth":51,"links":3947},[3948,3953,3956,3957,3960,3961,3962,3970,3973,3976,3977,3984,3987,3990,3991,3994,3999,4000,4001,4002,4003],{"id":2054,"depth":45,"text":2054,"children":3949},[3950,3951,3952],{"id":2147,"depth":51,"text":2147},{"id":2195,"depth":51,"text":2195},{"id":2314,"depth":51,"text":2314},{"id":2377,"depth":45,"text":2378,"children":3954},[3955],{"id":2396,"depth":51,"text":2397},{"id":2412,"depth":45,"text":2412},{"id":2439,"depth":45,"text":2439,"children":3958},[3959],{"id":2460,"depth":51,"text":2397},{"id":2475,"depth":45,"text":2475},{"id":2516,"depth":45,"text":2516},{"id":2571,"depth":45,"text":2571,"children":3963},[3964,3965,3966,3967,3968,3969],{"id":2619,"depth":51,"text":2619},{"id":2639,"depth":51,"text":2397},{"id":2653,"depth":51,"text":2653},{"id":2675,"depth":51,"text":2676},{"id":2698,"depth":51,"text":2699},{"id":2719,"depth":51,"text":2720},{"id":2751,"depth":45,"text":2751,"children":3971},[3972],{"id":2775,"depth":51,"text":2397},{"id":2787,"depth":45,"text":2788,"children":3974},[3975],{"id":2899,"depth":51,"text":2397},{"id":2911,"depth":45,"text":2911},{"id":2967,"depth":45,"text":2968,"children":3978},[3979,3980,3981,3982,3983],{"id":3045,"depth":51,"text":3046},{"id":3069,"depth":51,"text":3070},{"id":3099,"depth":51,"text":3100},{"id":3125,"depth":51,"text":3126},{"id":3144,"depth":51,"text":2397},{"id":3165,"depth":45,"text":3165,"children":3985},[3986],{"id":3258,"depth":51,"text":2397},{"id":3292,"depth":45,"text":3292,"children":3988},[3989],{"id":3329,"depth":51,"text":2397},{"id":3341,"depth":45,"text":3342},{"id":3378,"depth":45,"text":3379,"children":3992},[3993],{"id":3400,"depth":51,"text":2397},{"id":3409,"depth":45,"text":3410,"children":3995},[3996,3997,3998],{"id":3431,"depth":51,"text":3431},{"id":3468,"depth":51,"text":3468},{"id":3501,"depth":51,"text":2397},{"id":3513,"depth":45,"text":3514},{"id":3541,"depth":45,"text":3542},{"id":3673,"depth":45,"text":3674},{"id":3712,"depth":45,"text":3713},{"id":2003,"depth":45,"text":2004,"children":4004},[4005,4007,4009,4011],{"id":3760,"depth":51,"text":4006},"checkout 現在有更明確的替代指令",{"id":3848,"depth":51,"text":4008},"主分支名稱不一定是 master",{"id":3881,"depth":51,"text":4010},"刪除 .git",{"id":3909,"depth":51,"text":3912},"notes","2018 年整理的 Git 學習筆記，包含基本終端機指令、Git 操作、分支、reset、SourceTree、Vim 與一些常用設定。",{},"\u002Fposts\u002Fgit-commands-cheatsheet","2018-01-05",[],{"title":2046,"description":4013},"posts\u002F20180105.git-commands-cheatsheet",[4021,4022],"Git","版本控制","uJ6Iyc7HRET6uc4zL9_isq3AGn9CO29Q9ojLjegfmFs",{"id":4025,"title":4026,"body":4027,"category":378,"description":4564,"draft":380,"extension":381,"image":382,"meta":4565,"navigation":102,"path":4566,"publishedAt":4567,"relatedExperiments":4568,"seo":4569,"stem":4570,"tags":4571,"updatedAt":392,"__hash__":4573},"posts\u002Fposts\u002F20180108.vue2-password-strength.md","用 Vue 寫一個密碼強度計",{"type":8,"value":4028,"toc":4558},[4029,4032,4036,4110,4114,4517,4519,4534,4537,4555],[11,4030,4031],{},"這篇記錄用 Vue 做一個簡單的密碼強度判斷。",[15,4033,4035],{"id":4034},"html","HTML",[22,4037,4040],{"className":4038,"code":4039,"language":4034,"meta":27,"style":27},"language-html shiki shiki-themes github-light github-dark-default","\u003Cdiv id=\"app\">\n  \u003Cinput type=\"password\" v-model=\"password\">\n  \u003Cp>{{ strength }}\u003C\u002Fp>\n\u003C\u002Fdiv>\n",[29,4041,4042,4063,4088,4101],{"__ignoreMap":27},[32,4043,4044,4047,4051,4055,4057,4060],{"class":34,"line":35},[32,4045,4046],{"class":41},"\u003C",[32,4048,4050],{"class":4049},"sXQ1o","div",[32,4052,4054],{"class":4053},"syv4q"," id",[32,4056,172],{"class":41},[32,4058,4059],{"class":175},"\"app\"",[32,4061,4062],{"class":41},">\n",[32,4064,4065,4068,4071,4074,4076,4079,4082,4084,4086],{"class":34,"line":45},[32,4066,4067],{"class":41},"  \u003C",[32,4069,4070],{"class":4049},"input",[32,4072,4073],{"class":4053}," type",[32,4075,172],{"class":41},[32,4077,4078],{"class":175},"\"password\"",[32,4080,4081],{"class":4053}," v-model",[32,4083,172],{"class":41},[32,4085,4078],{"class":175},[32,4087,4062],{"class":41},[32,4089,4090,4092,4094,4097,4099],{"class":34,"line":51},[32,4091,4067],{"class":41},[32,4093,11],{"class":4049},[32,4095,4096],{"class":41},">{{ strength }}\u003C\u002F",[32,4098,11],{"class":4049},[32,4100,4062],{"class":41},[32,4102,4103,4106,4108],{"class":34,"line":90},[32,4104,4105],{"class":41},"\u003C\u002F",[32,4107,4050],{"class":4049},[32,4109,4062],{"class":41},[15,4111,4113],{"id":4112},"vue","Vue",[22,4115,4117],{"className":4038,"code":4116,"language":4034,"meta":27,"style":27},"\u003Cscript src=\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fvue@2\">\u003C\u002Fscript>\n\n\u003Cscript>\n  const vm = new Vue({\n    el: '#app',\n\n    data: {\n      password: '',\n    },\n\n    computed: {\n      strength() {\n        let score = this.password.length;\n\n        \u002F\u002F 包含大寫 A-Z\n        if (\u002F[A-Z]\u002F.test(this.password)) score *= 1.25;\n\n        \u002F\u002F 包含小寫 a-z\n        if (\u002F[a-z]\u002F.test(this.password)) score *= 1.25;\n\n        \u002F\u002F 包含數字 0-9\n        if (\u002F[0-9]\u002F.test(this.password)) score *= 1.25;\n\n        \u002F\u002F 包含不是大寫 A-Z、小寫 a-z、數字 0-9 的字元\n        if (\u002F[^A-Za-z0-9]\u002F.test(this.password)) score *= 1.25;\n\n        \u002F\u002F 密碼夠長夠完美\n        if (score > 40) return 'Perfect';\n\n        if (score > 20) return 'Great';\n\n        if (score > 10) return 'Good';\n\n        return 'weak';\n      },\n    },\n  });\n\u003C\u002Fscript>\n",[29,4118,4119,4141,4145,4153,4171,4181,4185,4190,4200,4205,4209,4214,4222,4242,4246,4251,4284,4288,4293,4322,4326,4331,4360,4364,4369,4403,4407,4412,4434,4438,4458,4462,4482,4486,4496,4501,4505,4509],{"__ignoreMap":27},[32,4120,4121,4123,4126,4129,4131,4134,4137,4139],{"class":34,"line":35},[32,4122,4046],{"class":41},[32,4124,4125],{"class":4049},"script",[32,4127,4128],{"class":4053}," src",[32,4130,172],{"class":41},[32,4132,4133],{"class":175},"\"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Fvue@2\"",[32,4135,4136],{"class":41},">\u003C\u002F",[32,4138,4125],{"class":4049},[32,4140,4062],{"class":41},[32,4142,4143],{"class":34,"line":45},[32,4144,103],{"emptyLinePlaceholder":102},[32,4146,4147,4149,4151],{"class":34,"line":51},[32,4148,4046],{"class":41},[32,4150,4125],{"class":4049},[32,4152,4062],{"class":41},[32,4154,4155,4158,4161,4163,4166,4169],{"class":34,"line":90},[32,4156,4157],{"class":38},"  const",[32,4159,4160],{"class":351}," vm",[32,4162,894],{"class":38},[32,4164,4165],{"class":38}," new",[32,4167,4168],{"class":217}," Vue",[32,4170,1143],{"class":41},[32,4172,4173,4176,4179],{"class":34,"line":99},[32,4174,4175],{"class":41},"    el: ",[32,4177,4178],{"class":175},"'#app'",[32,4180,547],{"class":41},[32,4182,4183],{"class":34,"line":106},[32,4184,103],{"emptyLinePlaceholder":102},[32,4186,4187],{"class":34,"line":114},[32,4188,4189],{"class":41},"    data: {\n",[32,4191,4192,4195,4198],{"class":34,"line":120},[32,4193,4194],{"class":41},"      password: ",[32,4196,4197],{"class":175},"''",[32,4199,547],{"class":41},[32,4201,4202],{"class":34,"line":127},[32,4203,4204],{"class":41},"    },\n",[32,4206,4207],{"class":34,"line":132},[32,4208,103],{"emptyLinePlaceholder":102},[32,4210,4211],{"class":34,"line":141},[32,4212,4213],{"class":41},"    computed: {\n",[32,4215,4216,4219],{"class":34,"line":147},[32,4217,4218],{"class":217},"      strength",[32,4220,4221],{"class":41},"() {\n",[32,4223,4224,4227,4230,4232,4235,4238,4240],{"class":34,"line":275},[32,4225,4226],{"class":38},"        let",[32,4228,4229],{"class":41}," score ",[32,4231,172],{"class":38},[32,4233,4234],{"class":351}," this",[32,4236,4237],{"class":41},".password.",[32,4239,991],{"class":351},[32,4241,96],{"class":41},[32,4243,4244],{"class":34,"line":282},[32,4245,103],{"emptyLinePlaceholder":102},[32,4247,4248],{"class":34,"line":295},[32,4249,4250],{"class":608},"        \u002F\u002F 包含大寫 A-Z\n",[32,4252,4253,4256,4258,4260,4263,4265,4267,4269,4271,4273,4276,4279,4282],{"class":34,"line":605},[32,4254,4255],{"class":38},"        if",[32,4257,900],{"class":41},[32,4259,1275],{"class":175},[32,4261,4262],{"class":351},"[A-Z]",[32,4264,1275],{"class":175},[32,4266,1278],{"class":41},[32,4268,1281],{"class":217},[32,4270,477],{"class":41},[32,4272,1083],{"class":351},[32,4274,4275],{"class":41},".password)) score ",[32,4277,4278],{"class":38},"*=",[32,4280,4281],{"class":351}," 1.25",[32,4283,96],{"class":41},[32,4285,4286],{"class":34,"line":612},[32,4287,103],{"emptyLinePlaceholder":102},[32,4289,4290],{"class":34,"line":617},[32,4291,4292],{"class":608},"        \u002F\u002F 包含小寫 a-z\n",[32,4294,4295,4297,4299,4301,4304,4306,4308,4310,4312,4314,4316,4318,4320],{"class":34,"line":644},[32,4296,4255],{"class":38},[32,4298,900],{"class":41},[32,4300,1275],{"class":175},[32,4302,4303],{"class":351},"[a-z]",[32,4305,1275],{"class":175},[32,4307,1278],{"class":41},[32,4309,1281],{"class":217},[32,4311,477],{"class":41},[32,4313,1083],{"class":351},[32,4315,4275],{"class":41},[32,4317,4278],{"class":38},[32,4319,4281],{"class":351},[32,4321,96],{"class":41},[32,4323,4324],{"class":34,"line":649},[32,4325,103],{"emptyLinePlaceholder":102},[32,4327,4328],{"class":34,"line":671},[32,4329,4330],{"class":608},"        \u002F\u002F 包含數字 0-9\n",[32,4332,4333,4335,4337,4339,4342,4344,4346,4348,4350,4352,4354,4356,4358],{"class":34,"line":690},[32,4334,4255],{"class":38},[32,4336,900],{"class":41},[32,4338,1275],{"class":175},[32,4340,4341],{"class":351},"[0-9]",[32,4343,1275],{"class":175},[32,4345,1278],{"class":41},[32,4347,1281],{"class":217},[32,4349,477],{"class":41},[32,4351,1083],{"class":351},[32,4353,4275],{"class":41},[32,4355,4278],{"class":38},[32,4357,4281],{"class":351},[32,4359,96],{"class":41},[32,4361,4362],{"class":34,"line":696},[32,4363,103],{"emptyLinePlaceholder":102},[32,4365,4366],{"class":34,"line":706},[32,4367,4368],{"class":608},"        \u002F\u002F 包含不是大寫 A-Z、小寫 a-z、數字 0-9 的字元\n",[32,4370,4371,4373,4375,4377,4380,4382,4385,4387,4389,4391,4393,4395,4397,4399,4401],{"class":34,"line":721},[32,4372,4255],{"class":38},[32,4374,900],{"class":41},[32,4376,1275],{"class":175},[32,4378,4379],{"class":351},"[",[32,4381,3005],{"class":38},[32,4383,4384],{"class":351},"A-Za-z0-9]",[32,4386,1275],{"class":175},[32,4388,1278],{"class":41},[32,4390,1281],{"class":217},[32,4392,477],{"class":41},[32,4394,1083],{"class":351},[32,4396,4275],{"class":41},[32,4398,4278],{"class":38},[32,4400,4281],{"class":351},[32,4402,96],{"class":41},[32,4404,4405],{"class":34,"line":728},[32,4406,103],{"emptyLinePlaceholder":102},[32,4408,4409],{"class":34,"line":737},[32,4410,4411],{"class":608},"        \u002F\u002F 密碼夠長夠完美\n",[32,4413,4414,4416,4419,4421,4424,4426,4429,4432],{"class":34,"line":742},[32,4415,4255],{"class":38},[32,4417,4418],{"class":41}," (score ",[32,4420,1615],{"class":38},[32,4422,4423],{"class":351}," 40",[32,4425,483],{"class":41},[32,4427,4428],{"class":38},"return",[32,4430,4431],{"class":175}," 'Perfect'",[32,4433,96],{"class":41},[32,4435,4436],{"class":34,"line":747},[32,4437,103],{"emptyLinePlaceholder":102},[32,4439,4440,4442,4444,4446,4449,4451,4453,4456],{"class":34,"line":752},[32,4441,4255],{"class":38},[32,4443,4418],{"class":41},[32,4445,1615],{"class":38},[32,4447,4448],{"class":351}," 20",[32,4450,483],{"class":41},[32,4452,4428],{"class":38},[32,4454,4455],{"class":175}," 'Great'",[32,4457,96],{"class":41},[32,4459,4460],{"class":34,"line":773},[32,4461,103],{"emptyLinePlaceholder":102},[32,4463,4464,4466,4468,4470,4473,4475,4477,4480],{"class":34,"line":789},[32,4465,4255],{"class":38},[32,4467,4418],{"class":41},[32,4469,1615],{"class":38},[32,4471,4472],{"class":351}," 10",[32,4474,483],{"class":41},[32,4476,4428],{"class":38},[32,4478,4479],{"class":175}," 'Good'",[32,4481,96],{"class":41},[32,4483,4484],{"class":34,"line":795},[32,4485,103],{"emptyLinePlaceholder":102},[32,4487,4488,4491,4494],{"class":34,"line":800},[32,4489,4490],{"class":38},"        return",[32,4492,4493],{"class":175}," 'weak'",[32,4495,96],{"class":41},[32,4497,4498],{"class":34,"line":816},[32,4499,4500],{"class":41},"      },\n",[32,4502,4503],{"class":34,"line":824},[32,4504,4204],{"class":41},[32,4506,4507],{"class":34,"line":833},[32,4508,693],{"class":41},[32,4510,4511,4513,4515],{"class":34,"line":842},[32,4512,4105],{"class":41},[32,4514,4125],{"class":4049},[32,4516,4062],{"class":41},[15,4518,2004],{"id":2003},[11,4520,4521,4522,4525,4526,4529,4530,4533],{},"這篇是 2018 年留下來的 Vue 練習，當時是直接透過 CDN 引入 Vue 2，再使用 ",[29,4523,4524],{},"new Vue()"," 搭配 ",[29,4527,4528],{},"el"," 掛載到頁面上。\n現在 Vue 專案的寫法已經和當年不同，Vue 3 也改成使用 ",[29,4531,4532],{},"createApp()"," 建立應用程式。\n另外，這裡的密碼強度計分方式只是當時練習使用的簡單判斷，並不適合直接作為正式服務的密碼安全標準。\n這篇就保留當年的寫法，作為以前的技術紀錄。",[15,4535,4536],{"id":4536},"參考資料",[4538,4539,4540,4548],"ul",{},[4541,4542,4543],"li",{},[412,4544,4547],{"href":4545,"rel":4546},"https:\u002F\u002Fv2.vuejs.org\u002Fv2\u002Fguide\u002F",[416],"Vue 2 官方文件（封存版）",[4541,4549,4550],{},[412,4551,4554],{"href":4552,"rel":4553},"https:\u002F\u002Fv2.vuejs.org\u002Fv2\u002Fguide\u002Fcomputed.html",[416],"Vue 2 computed 屬性",[368,4556,4557],{},"html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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 .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}",{"title":27,"searchDepth":51,"depth":51,"links":4559},[4560,4561,4562,4563],{"id":4034,"depth":45,"text":4035},{"id":4112,"depth":45,"text":4113},{"id":2003,"depth":45,"text":2004},{"id":4536,"depth":45,"text":4536},"使用 Vue 的 computed 製作一個簡單的密碼強度判斷。",{},"\u002Fposts\u002Fvue2-password-strength","2018-01-08",[],{"title":4026,"description":4564},"posts\u002F20180108.vue2-password-strength",[4113,390,4572],"表單驗證","IxRQGjJZoHSSvXZgZiycMAIsFzwL7N7YT7gQtY3ihXI",{"id":4575,"title":4576,"body":4577,"category":378,"description":5388,"draft":380,"extension":381,"image":382,"meta":5389,"navigation":102,"path":5390,"publishedAt":5391,"relatedExperiments":5392,"seo":5393,"stem":5394,"tags":5395,"updatedAt":392,"__hash__":5397},"posts\u002Fposts\u002F20180813.vue-cli3-project-setup.md","Vue CLI 3.0 安裝與建立專案筆記",{"type":8,"value":4578,"toc":5355},[4579,4582,4586,4590,4593,4606,4609,4613,4616,4635,4639,4657,4661,4672,4680,4683,4695,4698,4701,4704,4707,4710,4713,4716,4719,4726,4729,4732,4756,4759,4762,4765,4773,4776,4779,4785,4788,4803,4806,4810,4813,4819,4822,4825,4831,4834,4837,4860,4863,4867,4870,4873,4879,4882,4893,4896,4902,4905,4908,4914,4917,4920,4923,4929,4932,4934,4940,4946,4949,4952,4965,4968,4971,4984,4987,4990,4994,4997,5000,5006,5009,5012,5015,5018,5022,5029,5120,5127,5130,5143,5146,5149,5155,5158,5175,5178,5210,5213,5220,5226,5229,5235,5241,5293,5299,5303,5306,5320,5322,5325,5328,5338,5341,5343,5352],[11,4580,4581],{},"這篇是當時學 Vue CLI 3.0 時留下來的安裝與操作筆記。",[15,4583,4585],{"id":4584},"安裝-vue-cli-30","安裝 Vue CLI 3.0",[151,4587,4589],{"id":4588},"_1-確認-nodejs-版本","1. 確認 Node.js 版本",[11,4591,4592],{},"Node.js 需要 8.9 以上。",[22,4594,4596],{"className":2057,"code":4595,"language":2059,"meta":27,"style":27},"node -v\n",[29,4597,4598],{"__ignoreMap":27},[32,4599,4600,4603],{"class":34,"line":35},[32,4601,4602],{"class":2066},"node",[32,4604,4605],{"class":351}," -v\n",[11,4607,4608],{},"查看目前的 Node.js 版本。",[151,4610,4612],{"id":4611},"_2-移除舊版-vue-cli","2. 移除舊版 Vue CLI",[11,4614,4615],{},"如果之前有安裝 Vue CLI 1.x 或 2.x：",[22,4617,4619],{"className":2057,"code":4618,"language":2059,"meta":27,"style":27},"npm uninstall vue-cli -g\n",[29,4620,4621],{"__ignoreMap":27},[32,4622,4623,4626,4629,4632],{"class":34,"line":35},[32,4624,4625],{"class":2066},"npm",[32,4627,4628],{"class":175}," uninstall",[32,4630,4631],{"class":175}," vue-cli",[32,4633,4634],{"class":351}," -g\n",[151,4636,4638],{"id":4637},"_3-安裝-vue-cli-30","3. 安裝 Vue CLI 3.0",[22,4640,4642],{"className":2057,"code":4641,"language":2059,"meta":27,"style":27},"npm install -g @vue\u002Fcli\n",[29,4643,4644],{"__ignoreMap":27},[32,4645,4646,4648,4651,4654],{"class":34,"line":35},[32,4647,4625],{"class":2066},[32,4649,4650],{"class":175}," install",[32,4652,4653],{"class":351}," -g",[32,4655,4656],{"class":175}," @vue\u002Fcli\n",[151,4658,4660],{"id":4659},"_4-確認是否安裝成功","4. 確認是否安裝成功",[22,4662,4664],{"className":2057,"code":4663,"language":2059,"meta":27,"style":27},"vue --version\n",[29,4665,4666],{"__ignoreMap":27},[32,4667,4668,4670],{"class":34,"line":35},[32,4669,4112],{"class":2066},[32,4671,2129],{"class":351},[15,4673,4675,4676,4679],{"id":4674},"使用-vue-ui-建立專案","使用 ",[29,4677,4678],{},"vue ui"," 建立專案",[11,4681,4682],{},"在終端機輸入：",[22,4684,4686],{"className":2057,"code":4685,"language":2059,"meta":27,"style":27},"vue ui\n",[29,4687,4688],{"__ignoreMap":27},[32,4689,4690,4692],{"class":34,"line":35},[32,4691,4112],{"class":2066},[32,4693,4694],{"class":175}," ui\n",[11,4696,4697],{},"會開啟 Vue CLI 的圖形化管理介面，可以從裡面建立與管理專案。",[11,4699,4700],{},"專案建立完成後，可以在後台看到幾個功能。",[151,4702,4703],{"id":4703},"插件",[11,4705,4706],{},"查看目前已經安裝的插件。",[151,4708,4709],{"id":4709},"套件",[11,4711,4712],{},"可以新增專案需要使用的套件。",[151,4714,4715],{"id":4715},"設定",[11,4717,4718],{},"可以調整專案相關設定。",[11,4720,4721,4722,4725],{},"當時有記到 ",[29,4723,4724],{},"Base URL","，用來設定 build 後資源的路徑。",[151,4727,4728],{"id":4728},"任務",[11,4730,4731],{},"可以看到：",[22,4733,4735],{"className":2057,"code":4734,"language":2059,"meta":27,"style":27},"npm run serve\nnpm run build\n",[29,4736,4737,4747],{"__ignoreMap":27},[32,4738,4739,4741,4744],{"class":34,"line":35},[32,4740,4625],{"class":2066},[32,4742,4743],{"class":175}," run",[32,4745,4746],{"class":175}," serve\n",[32,4748,4749,4751,4753],{"class":34,"line":45},[32,4750,4625],{"class":2066},[32,4752,4743],{"class":175},[32,4754,4755],{"class":175}," build\n",[11,4757,4758],{},"等專案任務。",[11,4760,4761],{},"點選執行可以啟動專案，也可以從 Output 查看指令執行的訊息。",[11,4763,4764],{},"當時 Vue UI 裡還有 Dashboard 與分析功能，可以查看專案執行與打包後的資訊，例如：",[4538,4766,4767,4770],{},[4541,4768,4769],{},"Speed Stats：查看載入速度",[4541,4771,4772],{},"Assets：查看各個檔案的大小",[11,4774,4775],{},"Build 任務則可以用來編譯專案。",[11,4777,4778],{},"如果原本已經用指令建立專案，也可以透過 Vue UI 的匯入功能，把專案加入管理介面。",[15,4780,4675,4782,4679],{"id":4781},"使用-vue-create-建立專案",[29,4783,4784],{},"vue create",[11,4786,4787],{},"建立專案：",[22,4789,4791],{"className":2057,"code":4790,"language":2059,"meta":27,"style":27},"vue create vueclidemo\n",[29,4792,4793],{"__ignoreMap":27},[32,4794,4795,4797,4800],{"class":34,"line":35},[32,4796,4112],{"class":2066},[32,4798,4799],{"class":175}," create",[32,4801,4802],{"class":175}," vueclidemo\n",[11,4804,4805],{},"建立過程中會詢問要使用哪一種設定。",[151,4807,4809],{"id":4808},"preset","Preset",[11,4811,4812],{},"可以選：",[22,4814,4817],{"className":4815,"code":4816,"language":426,"meta":27},[424],"default\n",[29,4818,4816],{"__ignoreMap":27},[11,4820,4821],{},"使用預設的 Babel、ESLint。",[11,4823,4824],{},"或選：",[22,4826,4829],{"className":4827,"code":4828,"language":426,"meta":27},[424],"Manually select features\n",[29,4830,4828],{"__ignoreMap":27},[11,4832,4833],{},"自己選擇需要的功能。",[11,4835,4836],{},"當時筆記裡有記到：",[4538,4838,4839,4842,4845,4848,4851,4854,4857],{},[4541,4840,4841],{},"Vue Router",[4541,4843,4844],{},"Vuex",[4541,4846,4847],{},"Sass",[4541,4849,4850],{},"Babel",[4541,4852,4853],{},"ESLint",[4541,4855,4856],{},"Unit Testing",[4541,4858,4859],{},"E2E Testing",[11,4861,4862],{},"也可以把選好的設定儲存成 Preset，之後建立其他專案時繼續使用。",[15,4864,4866],{"id":4865},"linter-formatter","Linter \u002F Formatter",[11,4868,4869],{},"建立專案時可以選擇 JavaScript 的程式碼規範。",[11,4871,4872],{},"當時的選項包含：",[22,4874,4877],{"className":4875,"code":4876,"language":426,"meta":27},[424],"TSLint\nESLint with error prevention only\nESLint + Airbnb config\nESLint + Standard config\nESLint + Prettier\n",[29,4878,4876],{"__ignoreMap":27},[11,4880,4881],{},"當時自己的筆記是：",[4538,4883,4884,4887,4890],{},[4541,4885,4886],{},"Airbnb：規則比較嚴格",[4541,4888,4889],{},"Standard：相對適合剛開始使用",[4541,4891,4892],{},"初學時如果還不熟 Linter，也可以先不要選太複雜的設定",[11,4894,4895],{},"接著會詢問什麼時候執行 Lint：",[22,4897,4900],{"className":4898,"code":4899,"language":426,"meta":27},[424],"Lint on save\n",[29,4901,4899],{"__ignoreMap":27},[11,4903,4904],{},"存檔時檢查。",[11,4906,4907],{},"或：",[22,4909,4912],{"className":4910,"code":4911,"language":426,"meta":27},[424],"Lint and fix on commit\n",[29,4913,4911],{"__ignoreMap":27},[11,4915,4916],{},"Commit 時進行檢查與修正。",[15,4918,4919],{"id":4919},"設定檔放哪裡",[11,4921,4922],{},"建立專案時還會詢問設定要放在哪裡。",[22,4924,4927],{"className":4925,"code":4926,"language":426,"meta":27},[424],"In dedicated config files\n",[29,4928,4926],{"__ignoreMap":27},[11,4930,4931],{},"使用獨立的設定檔。",[11,4933,4907],{},[22,4935,4938],{"className":4936,"code":4937,"language":426,"meta":27},[424],"In package.json\n",[29,4939,4937],{"__ignoreMap":27},[11,4941,4942,4943,3775],{},"把設定放進 ",[29,4944,4945],{},"package.json",[15,4947,4948],{"id":4948},"啟動專案",[11,4950,4951],{},"安裝完成後，就可以執行：",[22,4953,4955],{"className":2057,"code":4954,"language":2059,"meta":27,"style":27},"npm run serve\n",[29,4956,4957],{"__ignoreMap":27},[32,4958,4959,4961,4963],{"class":34,"line":35},[32,4960,4625],{"class":2066},[32,4962,4743],{"class":175},[32,4964,4746],{"class":175},[11,4966,4967],{},"啟動開發環境。",[11,4969,4970],{},"Build：",[22,4972,4974],{"className":2057,"code":4973,"language":2059,"meta":27,"style":27},"npm run build\n",[29,4975,4976],{"__ignoreMap":27},[32,4977,4978,4980,4982],{"class":34,"line":35},[32,4979,4625],{"class":2066},[32,4981,4743],{"class":175},[32,4983,4755],{"class":175},[11,4985,4986],{},"編譯正式環境使用的檔案。",[11,4988,4989],{},"開發伺服器啟動後，當時會看到 Local 與 Network 的網址。",[15,4991,4993],{"id":4992},"vue-cli-30-專案結構","Vue CLI 3.0 專案結構",[11,4995,4996],{},"Vue CLI 3.0 和 Vue CLI 2.0 的專案結構不太一樣。",[11,4998,4999],{},"當時主要記了：",[22,5001,5004],{"className":5002,"code":5003,"language":426,"meta":27},[424],"src\u002F\n├── components\u002F\n├── views\u002F\n├── router.js\n├── App.vue\n└── main.js\n",[29,5005,5003],{"__ignoreMap":27},[151,5007,5008],{"id":5008},"components",[11,5010,5011],{},"放置專案內使用的元件。",[151,5013,5014],{"id":5014},"views",[11,5016,5017],{},"放置 Router 切換的頁面元件。",[151,5019,5021],{"id":5020},"routerjs","router.js",[11,5023,5024,5025,5028],{},"當時可以直接使用動態 ",[29,5026,5027],{},"import()"," 載入頁面：",[22,5030,5032],{"className":24,"code":5031,"language":26,"meta":27,"style":27},"export default new Router({\n  routes: [\n    {\n      path: '\u002F',\n      name: 'home',\n      component: () => import('.\u002Fviews\u002FHome.vue'),\n    },\n  ],\n});\n",[29,5033,5034,5050,5055,5060,5070,5080,5102,5106,5113],{"__ignoreMap":27},[32,5035,5036,5039,5042,5044,5047],{"class":34,"line":35},[32,5037,5038],{"class":38},"export",[32,5040,5041],{"class":38}," default",[32,5043,4165],{"class":38},[32,5045,5046],{"class":217}," Router",[32,5048,1143],{"class":5049},"sfpNf",[32,5051,5052],{"class":34,"line":45},[32,5053,5054],{"class":41},"  routes: [\n",[32,5056,5057],{"class":34,"line":51},[32,5058,5059],{"class":41},"    {\n",[32,5061,5062,5065,5068],{"class":34,"line":90},[32,5063,5064],{"class":41},"      path: ",[32,5066,5067],{"class":175},"'\u002F'",[32,5069,547],{"class":41},[32,5071,5072,5075,5078],{"class":34,"line":99},[32,5073,5074],{"class":41},"      name: ",[32,5076,5077],{"class":175},"'home'",[32,5079,547],{"class":41},[32,5081,5082,5085,5088,5091,5094,5096,5099],{"class":34,"line":106},[32,5083,5084],{"class":217},"      component",[32,5086,5087],{"class":41},": () ",[32,5089,5090],{"class":38},"=>",[32,5092,5093],{"class":38}," import",[32,5095,477],{"class":41},[32,5097,5098],{"class":175},"'.\u002Fviews\u002FHome.vue'",[32,5100,5101],{"class":41},"),\n",[32,5103,5104],{"class":34,"line":114},[32,5105,4204],{"class":41},[32,5107,5108,5111],{"class":34,"line":120},[32,5109,5110],{"class":41},"  ]",[32,5112,547],{"class":5049},[32,5114,5115,5118],{"class":34,"line":127},[32,5116,5117],{"class":5049},"})",[32,5119,96],{"class":41},[11,5121,5122,5123,5126],{},"不用先在檔案上方把 ",[29,5124,5125],{},"Home.vue"," import 進來。",[11,5128,5129],{},"當時另外有記到：",[2184,5131,5132],{},[11,5133,5134,5135,5138,5139,5142],{},"在 ",[29,5136,5137],{},"main.js"," 引入 ",[29,5140,5141],{},".vue"," 檔案時，要注意檔案名稱與副檔名。",[15,5144,5145],{"id":5145},"環境變數",[11,5147,5148],{},"先建立：",[22,5150,5153],{"className":5151,"code":5152,"language":426,"meta":27},[424],".env\n",[29,5154,5152],{"__ignoreMap":27},[11,5156,5157],{},"當時的設定：",[22,5159,5163],{"className":5160,"code":5161,"language":5162,"meta":27,"style":27},"language-env shiki shiki-themes github-light github-dark-default","VUE_APP_APIPATH=https:\u002F\u002Fvue-course-api.hexschool.io\nVUE_APP_CUSTOMPATH=rockayumitw\n","env",[29,5164,5165,5170],{"__ignoreMap":27},[32,5166,5167],{"class":34,"line":35},[32,5168,5169],{},"VUE_APP_APIPATH=https:\u002F\u002Fvue-course-api.hexschool.io\n",[32,5171,5172],{"class":34,"line":45},[32,5173,5174],{},"VUE_APP_CUSTOMPATH=rockayumitw\n",[11,5176,5177],{},"在程式裡可以透過：",[22,5179,5181],{"className":24,"code":5180,"language":26,"meta":27,"style":27},"created() {\n  console.log(process.env.VUE_APP_APIPATH);\n}\n",[29,5182,5183,5190,5206],{"__ignoreMap":27},[32,5184,5185,5188],{"class":34,"line":35},[32,5186,5187],{"class":217},"created",[32,5189,4221],{"class":41},[32,5191,5192,5195,5198,5201,5204],{"class":34,"line":45},[32,5193,5194],{"class":41},"  console.",[32,5196,5197],{"class":217},"log",[32,5199,5200],{"class":41},"(process.env.",[32,5202,5203],{"class":351},"VUE_APP_APIPATH",[32,5205,230],{"class":41},[32,5207,5208],{"class":34,"line":51},[32,5209,54],{"class":41},[11,5211,5212],{},"取得環境變數。",[11,5214,5215,5216,5219],{},"修改 ",[29,5217,5218],{},".env"," 後，需要重新啟動開發環境。",[15,5221,5223,5224],{"id":5222},"不同環境的-env","不同環境的 ",[29,5225,5218],{},[11,5227,5228],{},"也可以依照不同環境建立：",[22,5230,5233],{"className":5231,"code":5232,"language":426,"meta":27},[424],".env.development\n.env.production\n",[29,5234,5232],{"__ignoreMap":27},[11,5236,5237,5238,5240],{},"例如 ",[29,5239,4945],{},"：",[22,5242,5246],{"className":5243,"code":5244,"language":5245,"meta":27,"style":27},"language-json shiki shiki-themes github-light github-dark-default","{\n  \"scripts\": {\n    \"serve\": \"vue-cli-service serve --mode development\",\n    \"build\": \"vue-cli-service build\"\n  }\n}\n","json",[29,5247,5248,5253,5262,5275,5285,5289],{"__ignoreMap":27},[32,5249,5250],{"class":34,"line":35},[32,5251,5252],{"class":41},"{\n",[32,5254,5255,5259],{"class":34,"line":45},[32,5256,5258],{"class":5257},"swgif","  \"scripts\"",[32,5260,5261],{"class":41},": {\n",[32,5263,5264,5267,5270,5273],{"class":34,"line":51},[32,5265,5266],{"class":5257},"    \"serve\"",[32,5268,5269],{"class":41},": ",[32,5271,5272],{"class":175},"\"vue-cli-service serve --mode development\"",[32,5274,547],{"class":41},[32,5276,5277,5280,5282],{"class":34,"line":90},[32,5278,5279],{"class":5257},"    \"build\"",[32,5281,5269],{"class":41},[32,5283,5284],{"class":175},"\"vue-cli-service build\"\n",[32,5286,5287],{"class":34,"line":99},[32,5288,1203],{"class":41},[32,5290,5291],{"class":34,"line":106},[32,5292,54],{"class":41},[11,5294,5295,5296,5298],{},"當時的筆記是：如果不同 ",[29,5297,5218],{}," 中有相同名稱的變數，會依照目前執行的 mode 載入對應設定。",[15,5300,5302],{"id":5301},"移除-vue-cli","移除 Vue CLI",[11,5304,5305],{},"如果需要移除舊版 Vue CLI：",[22,5307,5308],{"className":2057,"code":4618,"language":2059,"meta":27,"style":27},[29,5309,5310],{"__ignoreMap":27},[32,5311,5312,5314,5316,5318],{"class":34,"line":35},[32,5313,4625],{"class":2066},[32,5315,4628],{"class":175},[32,5317,4631],{"class":175},[32,5319,4634],{"class":351},[15,5321,2004],{"id":2003},[11,5323,5324],{},"這篇是 2018 年 Vue CLI 3.0 時期留下來的學習筆記，所以介面、建立流程、套件選項與專案結構都是當時的環境。",[11,5326,5327],{},"現在建立新的 Vue 專案已經不會以 Vue CLI 3.0 作為主要方式，因此這篇不適合直接拿來照著建立新的專案。",[11,5329,5330,5331,5333,5334,5337],{},"不過 ",[29,5332,4678],{},"、Vue CLI 的 Preset、",[29,5335,5336],{},"VUE_APP_"," 環境變數，以及當時 Vue CLI 專案的結構，還是很常在舊專案裡看到。",[11,5339,5340],{},"這篇就保留當時的安裝與操作方式，作為 Vue CLI 時期的學習紀錄。",[15,5342,4536],{"id":4536},[4538,5344,5345],{},[4541,5346,5347],{},[412,5348,5351],{"href":5349,"rel":5350},"https:\u002F\u002Fcli.vuejs.org\u002Fzh\u002F",[416],"Vue CLI 官方文件（封存版）",[368,5353,5354],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}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 .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .swgif, html code.shiki .swgif{--shiki-default:#005CC5;--shiki-dark:#7EE787}",{"title":27,"searchDepth":51,"depth":51,"links":5356},[5357,5363,5370,5374,5375,5376,5377,5382,5383,5385,5386,5387],{"id":4584,"depth":45,"text":4585,"children":5358},[5359,5360,5361,5362],{"id":4588,"depth":51,"text":4589},{"id":4611,"depth":51,"text":4612},{"id":4637,"depth":51,"text":4638},{"id":4659,"depth":51,"text":4660},{"id":4674,"depth":45,"text":5364,"children":5365},"使用 vue ui 建立專案",[5366,5367,5368,5369],{"id":4703,"depth":51,"text":4703},{"id":4709,"depth":51,"text":4709},{"id":4715,"depth":51,"text":4715},{"id":4728,"depth":51,"text":4728},{"id":4781,"depth":45,"text":5371,"children":5372},"使用 vue create 建立專案",[5373],{"id":4808,"depth":51,"text":4809},{"id":4865,"depth":45,"text":4866},{"id":4919,"depth":45,"text":4919},{"id":4948,"depth":45,"text":4948},{"id":4992,"depth":45,"text":4993,"children":5378},[5379,5380,5381],{"id":5008,"depth":51,"text":5008},{"id":5014,"depth":51,"text":5014},{"id":5020,"depth":51,"text":5021},{"id":5145,"depth":45,"text":5145},{"id":5222,"depth":45,"text":5384},"不同環境的 .env",{"id":5301,"depth":45,"text":5302},{"id":2003,"depth":45,"text":2004},{"id":4536,"depth":45,"text":4536},"2018 年整理的 Vue CLI 3.0 學習筆記，包含安裝、vue ui、vue create、專案結構與環境變數設定。",{},"\u002Fposts\u002Fvue-cli3-project-setup","2018-08-13",[],{"title":4576,"description":5388},"posts\u002F20180813.vue-cli3-project-setup",[4113,5396,4625],"Vue CLI","0wsdoWyq32GjCD8Yd8_29zfDppMa4jhYXEVvf8xssnQ",{"id":5399,"title":5400,"body":5401,"category":378,"description":6019,"draft":380,"extension":381,"image":382,"meta":6020,"navigation":102,"path":6021,"publishedAt":6022,"relatedExperiments":6023,"seo":6024,"stem":6025,"tags":6026,"updatedAt":392,"__hash__":6029},"posts\u002Fposts\u002F20180814.legacy-screen-orientation.md","移動裝置螢幕旋轉判斷與鎖定",{"type":8,"value":5402,"toc":6009},[5403,5406,5410,5480,5484,5506,5650,5654,5684,5688,5749,5753,5831,5835,5962,5965,5988,5990,6000,6006],[11,5404,5405],{},"這篇是 2018 年整理移動裝置螢幕方向判斷與鎖定方式時留下來的筆記。",[15,5407,5409],{"id":5408},"css-判斷","CSS 判斷",[22,5411,5414],{"className":5412,"code":5413,"language":1140,"meta":27,"style":27},"language-css shiki shiki-themes github-light github-dark-default","@media screen and (orientation: portrait) {\n  \u002F* 針對縱向 CSS 撰寫樣式 *\u002F\n}\n\n@media screen and (orientation: landscape) {\n  \u002F* 針對橫向 CSS 撰寫樣式 *\u002F\n}\n",[29,5415,5416,5439,5444,5448,5452,5471,5476],{"__ignoreMap":27},[32,5417,5418,5421,5424,5427,5429,5432,5434,5437],{"class":34,"line":35},[32,5419,5420],{"class":38},"@media",[32,5422,5423],{"class":351}," screen",[32,5425,5426],{"class":38}," and",[32,5428,900],{"class":41},[32,5430,5431],{"class":351},"orientation",[32,5433,5269],{"class":41},[32,5435,5436],{"class":351},"portrait",[32,5438,520],{"class":41},[32,5440,5441],{"class":34,"line":45},[32,5442,5443],{"class":608},"  \u002F* 針對縱向 CSS 撰寫樣式 *\u002F\n",[32,5445,5446],{"class":34,"line":51},[32,5447,54],{"class":41},[32,5449,5450],{"class":34,"line":90},[32,5451,103],{"emptyLinePlaceholder":102},[32,5453,5454,5456,5458,5460,5462,5464,5466,5469],{"class":34,"line":99},[32,5455,5420],{"class":38},[32,5457,5423],{"class":351},[32,5459,5426],{"class":38},[32,5461,900],{"class":41},[32,5463,5431],{"class":351},[32,5465,5269],{"class":41},[32,5467,5468],{"class":351},"landscape",[32,5470,520],{"class":41},[32,5472,5473],{"class":34,"line":106},[32,5474,5475],{"class":608},"  \u002F* 針對橫向 CSS 撰寫樣式 *\u002F\n",[32,5477,5478],{"class":34,"line":114},[32,5479,54],{"class":41},[15,5481,5483],{"id":5482},"orientationchange-事件","orientationchange 事件",[22,5485,5487],{"className":4038,"code":5486,"language":4034,"meta":27,"style":27},"\u003Cbody onorientationchange=\"updateOrientation();\">\n",[29,5488,5489],{"__ignoreMap":27},[32,5490,5491,5493,5496,5499,5501,5504],{"class":34,"line":35},[32,5492,4046],{"class":41},[32,5494,5495],{"class":4049},"body",[32,5497,5498],{"class":4053}," onorientationchange",[32,5500,172],{"class":41},[32,5502,5503],{"class":175},"\"updateOrientation();\"",[32,5505,4062],{"class":41},[22,5507,5509],{"className":24,"code":5508,"language":26,"meta":27,"style":27},"window.addEventListener(\n  \"onorientationchange\" in window ? \"orientationchange\" : \"resize\",\n  function () {\n    if (window.orientation === 180 || window.orientation === 0) {\n      alert(\"目前您的螢幕為縱向！\");\n    }\n\n    if (window.orientation === 90 || window.orientation === -90) {\n      alert(\"目前您的螢幕為橫向！\");\n    }\n  },\n  false,\n);\n",[29,5510,5511,5520,5543,5550,5575,5587,5592,5596,5620,5631,5635,5640,5646],{"__ignoreMap":27},[32,5512,5513,5515,5518],{"class":34,"line":35},[32,5514,1754],{"class":41},[32,5516,5517],{"class":217},"addEventListener",[32,5519,539],{"class":41},[32,5521,5522,5525,5528,5531,5533,5536,5538,5541],{"class":34,"line":45},[32,5523,5524],{"class":175},"  \"onorientationchange\"",[32,5526,5527],{"class":38}," in",[32,5529,5530],{"class":41}," window ",[32,5532,316],{"class":38},[32,5534,5535],{"class":175}," \"orientationchange\"",[32,5537,361],{"class":38},[32,5539,5540],{"class":175}," \"resize\"",[32,5542,547],{"class":41},[32,5544,5545,5548],{"class":34,"line":51},[32,5546,5547],{"class":38},"  function",[32,5549,668],{"class":41},[32,5551,5552,5555,5558,5561,5564,5566,5569,5571,5573],{"class":34,"line":90},[32,5553,5554],{"class":38},"    if",[32,5556,5557],{"class":41}," (window.orientation ",[32,5559,5560],{"class":38},"===",[32,5562,5563],{"class":351}," 180",[32,5565,1644],{"class":38},[32,5567,5568],{"class":41}," window.orientation ",[32,5570,5560],{"class":38},[32,5572,1523],{"class":351},[32,5574,520],{"class":41},[32,5576,5577,5580,5582,5585],{"class":34,"line":99},[32,5578,5579],{"class":217},"      alert",[32,5581,477],{"class":41},[32,5583,5584],{"class":175},"\"目前您的螢幕為縱向！\"",[32,5586,230],{"class":41},[32,5588,5589],{"class":34,"line":106},[32,5590,5591],{"class":41},"    }\n",[32,5593,5594],{"class":34,"line":114},[32,5595,103],{"emptyLinePlaceholder":102},[32,5597,5598,5600,5602,5604,5607,5609,5611,5613,5615,5618],{"class":34,"line":120},[32,5599,5554],{"class":38},[32,5601,5557],{"class":41},[32,5603,5560],{"class":38},[32,5605,5606],{"class":351}," 90",[32,5608,1644],{"class":38},[32,5610,5568],{"class":41},[32,5612,5560],{"class":38},[32,5614,1119],{"class":38},[32,5616,5617],{"class":351},"90",[32,5619,520],{"class":41},[32,5621,5622,5624,5626,5629],{"class":34,"line":127},[32,5623,5579],{"class":217},[32,5625,477],{"class":41},[32,5627,5628],{"class":175},"\"目前您的螢幕為橫向！\"",[32,5630,230],{"class":41},[32,5632,5633],{"class":34,"line":132},[32,5634,5591],{"class":41},[32,5636,5637],{"class":34,"line":141},[32,5638,5639],{"class":41},"  },\n",[32,5641,5642,5644],{"class":34,"line":147},[32,5643,1641],{"class":351},[32,5645,547],{"class":41},[32,5647,5648],{"class":34,"line":275},[32,5649,230],{"class":41},[15,5651,5653],{"id":5652},"meta-判斷","meta 判斷",[22,5655,5657],{"className":4038,"code":5656,"language":4034,"meta":27,"style":27},"\u003Cmeta name=\"x5-orientation\" content=\"portrait\">\n",[29,5658,5659],{"__ignoreMap":27},[32,5660,5661,5663,5666,5669,5671,5674,5677,5679,5682],{"class":34,"line":35},[32,5662,4046],{"class":41},[32,5664,5665],{"class":4049},"meta",[32,5667,5668],{"class":4053}," name",[32,5670,172],{"class":41},[32,5672,5673],{"class":175},"\"x5-orientation\"",[32,5675,5676],{"class":4053}," content",[32,5678,172],{"class":41},[32,5680,5681],{"class":175},"\"portrait\"",[32,5683,4062],{"class":41},[15,5685,5687],{"id":5686},"api","API",[22,5689,5691],{"className":24,"code":5690,"language":26,"meta":27,"style":27},"screen.addEventListener(\"orientationchange\", function () {\n  console.log(\"The orientation of the screen is: \" + screen.orientation);\n});\n\nscreen.lockOrientation(\"landscape\");\n",[29,5692,5693,5711,5727,5731,5735],{"__ignoreMap":27},[32,5694,5695,5698,5700,5702,5705,5707,5709],{"class":34,"line":35},[32,5696,5697],{"class":41},"screen.",[32,5699,5517],{"class":217},[32,5701,477],{"class":41},[32,5703,5704],{"class":175},"\"orientationchange\"",[32,5706,635],{"class":41},[32,5708,665],{"class":38},[32,5710,668],{"class":41},[32,5712,5713,5715,5717,5719,5722,5724],{"class":34,"line":45},[32,5714,5194],{"class":41},[32,5716,5197],{"class":217},[32,5718,477],{"class":41},[32,5720,5721],{"class":175},"\"The orientation of the screen is: \"",[32,5723,1799],{"class":38},[32,5725,5726],{"class":41}," screen.orientation);\n",[32,5728,5729],{"class":34,"line":51},[32,5730,792],{"class":41},[32,5732,5733],{"class":34,"line":90},[32,5734,103],{"emptyLinePlaceholder":102},[32,5736,5737,5739,5742,5744,5747],{"class":34,"line":99},[32,5738,5697],{"class":41},[32,5740,5741],{"class":217},"lockOrientation",[32,5743,477],{"class":41},[32,5745,5746],{"class":175},"\"landscape\"",[32,5748,230],{"class":41},[15,5750,5752],{"id":5751},"css-強制旋轉","CSS 強制旋轉",[22,5754,5756],{"className":5412,"code":5755,"language":1140,"meta":27,"style":27},"transform: rotate(90deg);\ntransform-origin: left bottom;\n-ms-transform: rotate(90deg); \u002F* Internet Explorer 9 *\u002F\n-ms-transform-origin: left bottom; \u002F* Internet Explorer 9 *\u002F\n-moz-transform: rotate(90deg); \u002F* Firefox *\u002F\n-moz-transform-origin: left bottom; \u002F* Firefox *\u002F\n-webkit-transform: rotate(90deg); \u002F* Safari & Chrome *\u002F\n-webkit-transform-origin: left bottom; \u002F* Safari & Chrome *\u002F\n-o-transform: rotate(90deg); \u002F* Opera *\u002F\n-o-transform-origin: left bottom; \u002F* Opera *\u002F\n",[29,5757,5758,5763,5771,5779,5786,5794,5801,5809,5816,5824],{"__ignoreMap":27},[32,5759,5760],{"class":34,"line":35},[32,5761,5762],{"class":41},"transform: rotate(90deg);\n",[32,5764,5765,5768],{"class":34,"line":45},[32,5766,5767],{"class":4049},"transform-origin",[32,5769,5770],{"class":41},": left bottom;\n",[32,5772,5773,5776],{"class":34,"line":51},[32,5774,5775],{"class":41},"-ms-transform: rotate(90deg); ",[32,5777,5778],{"class":608},"\u002F* Internet Explorer 9 *\u002F\n",[32,5780,5781,5784],{"class":34,"line":90},[32,5782,5783],{"class":41},"-ms-transform-origin: left bottom; ",[32,5785,5778],{"class":608},[32,5787,5788,5791],{"class":34,"line":99},[32,5789,5790],{"class":41},"-moz-transform: rotate(90deg); ",[32,5792,5793],{"class":608},"\u002F* Firefox *\u002F\n",[32,5795,5796,5799],{"class":34,"line":106},[32,5797,5798],{"class":41},"-moz-transform-origin: left bottom; ",[32,5800,5793],{"class":608},[32,5802,5803,5806],{"class":34,"line":114},[32,5804,5805],{"class":41},"-webkit-transform: rotate(90deg); ",[32,5807,5808],{"class":608},"\u002F* Safari & Chrome *\u002F\n",[32,5810,5811,5814],{"class":34,"line":120},[32,5812,5813],{"class":41},"-webkit-transform-origin: left bottom; ",[32,5815,5808],{"class":608},[32,5817,5818,5821],{"class":34,"line":127},[32,5819,5820],{"class":41},"-o-transform: rotate(90deg); ",[32,5822,5823],{"class":608},"\u002F* Opera *\u002F\n",[32,5825,5826,5829],{"class":34,"line":132},[32,5827,5828],{"class":41},"-o-transform-origin: left bottom; ",[32,5830,5823],{"class":608},[15,5832,5834],{"id":5833},"jquery-寫法","jQuery 寫法",[22,5836,5838],{"className":24,"code":5837,"language":26,"meta":27,"style":27},"$(document).bind(\"orientationchange\", function () {\n  if (window.orientation == 90 || window.orientation == -90) {\n    $(\".bottom_fix\").css(\"position\", \"static\");\n  } else {\n    $(\".bottom_fix\").css({\n      position: \"fixed\",\n      bottom: \"0\",\n    });\n  }\n});\n",[29,5839,5840,5859,5882,5907,5916,5930,5940,5950,5954,5958],{"__ignoreMap":27},[32,5841,5842,5844,5846,5849,5851,5853,5855,5857],{"class":34,"line":35},[32,5843,755],{"class":217},[32,5845,874],{"class":41},[32,5847,5848],{"class":217},"bind",[32,5850,477],{"class":41},[32,5852,5704],{"class":175},[32,5854,635],{"class":41},[32,5856,665],{"class":38},[32,5858,668],{"class":41},[32,5860,5861,5863,5865,5868,5870,5872,5874,5876,5878,5880],{"class":34,"line":45},[32,5862,1021],{"class":38},[32,5864,5557],{"class":41},[32,5866,5867],{"class":38},"==",[32,5869,5606],{"class":351},[32,5871,1644],{"class":38},[32,5873,5568],{"class":41},[32,5875,5867],{"class":38},[32,5877,1119],{"class":38},[32,5879,5617],{"class":351},[32,5881,520],{"class":41},[32,5883,5884,5886,5888,5891,5893,5895,5897,5900,5902,5905],{"class":34,"line":51},[32,5885,674],{"class":217},[32,5887,477],{"class":41},[32,5889,5890],{"class":175},"\".bottom_fix\"",[32,5892,533],{"class":41},[32,5894,1140],{"class":217},[32,5896,477],{"class":41},[32,5898,5899],{"class":175},"\"position\"",[32,5901,635],{"class":41},[32,5903,5904],{"class":175},"\"static\"",[32,5906,230],{"class":41},[32,5908,5909,5912,5914],{"class":34,"line":90},[32,5910,5911],{"class":41},"  } ",[32,5913,565],{"class":38},[32,5915,703],{"class":41},[32,5917,5918,5920,5922,5924,5926,5928],{"class":34,"line":99},[32,5919,674],{"class":217},[32,5921,477],{"class":41},[32,5923,5890],{"class":175},[32,5925,533],{"class":41},[32,5927,1140],{"class":217},[32,5929,1143],{"class":41},[32,5931,5932,5935,5938],{"class":34,"line":106},[32,5933,5934],{"class":41},"      position: ",[32,5936,5937],{"class":175},"\"fixed\"",[32,5939,547],{"class":41},[32,5941,5942,5945,5948],{"class":34,"line":114},[32,5943,5944],{"class":41},"      bottom: ",[32,5946,5947],{"class":175},"\"0\"",[32,5949,547],{"class":41},[32,5951,5952],{"class":34,"line":120},[32,5953,1198],{"class":41},[32,5955,5956],{"class":34,"line":127},[32,5957,1203],{"class":41},[32,5959,5960],{"class":34,"line":132},[32,5961,792],{"class":41},[15,5963,5964],{"id":5964},"原文參考連結",[4538,5966,5967,5974,5981],{},[4541,5968,5969],{},[412,5970,5973],{"href":5971,"rel":5972},"http:\u002F\u002Fsrdesign.com.tw\u002Fdesign\u002Fthread-517-1-1.html",[416],"srdesign.com.tw",[4541,5975,5976],{},[412,5977,5980],{"href":5978,"rel":5979},"https:\u002F\u002Fhk.saowen.com\u002Fa\u002Fd98d575aeda7cd3e201f60c3cde03cb7050119380497b75d1dab3cc382ec340e",[416],"hk.saowen.com",[4541,5982,5983],{},[412,5984,5987],{"href":5985,"rel":5986},"http:\u002F\u002Fwww.runoob.com\u002Fjquerymobile\u002Fjquerymobile-events-orientation.html",[416],"菜鳥教程：jQuery Mobile orientationchange 事件",[15,5989,2004],{"id":2003},[11,5991,5992,5993,3750,5996,5999],{},"這篇保留的是 2018 年使用 ",[29,5994,5995],{},"window.orientation",[29,5997,5998],{},"screen.lockOrientation()"," 與瀏覽器專屬 meta 標籤的寫法。前兩者已經棄用或不屬於現行標準，不建議直接用在新專案。",[11,6001,6002,6003,6005],{},"現在若要用 JavaScript 取得或鎖定螢幕方向，可參考《screen.orientation：現在判斷與鎖定螢幕方向的做法》。其中方向鎖定仍受瀏覽器與執行情境限制；單純依橫直向調整版面時，CSS 的 ",[29,6004,5431],{}," 媒體查詢仍然適用。",[368,6007,6008],{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}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 .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}",{"title":27,"searchDepth":51,"depth":51,"links":6010},[6011,6012,6013,6014,6015,6016,6017,6018],{"id":5408,"depth":45,"text":5409},{"id":5482,"depth":45,"text":5483},{"id":5652,"depth":45,"text":5653},{"id":5686,"depth":45,"text":5687},{"id":5751,"depth":45,"text":5752},{"id":5833,"depth":45,"text":5834},{"id":5964,"depth":45,"text":5964},{"id":2003,"depth":45,"text":2004},"2018 年整理的移動裝置螢幕方向筆記，包含 CSS 媒體查詢、window.orientation、舊版方向鎖定 API 與 jQuery 範例。",{},"\u002Fposts\u002Flegacy-screen-orientation","2018-08-14",[],{"title":5400,"description":6019},"posts\u002F20180814.legacy-screen-orientation",[390,2040,6027,6028],"RWD","螢幕方向","DHrrss7fhuuHCDYXLC39zyDTt4Hu8Ib2EcNeVqLs_8s",{"id":6031,"title":6032,"body":6033,"category":378,"description":6232,"draft":380,"extension":381,"image":382,"meta":6233,"navigation":102,"path":6234,"publishedAt":6235,"relatedExperiments":6236,"seo":6237,"stem":6238,"tags":6239,"updatedAt":392,"__hash__":6240},"posts\u002Fposts\u002F20180822.dynamic-link-stylesheet.md","用 JavaScript 動態建立與停用 CSS 檔案",{"type":8,"value":6034,"toc":6227},[6035,6038,6042,6174,6178,6209,6211,6218,6224],[11,6036,6037],{},"這篇是 2018 年整理用 JavaScript 動態建立與停用 CSS 檔案時留下來的筆記。",[15,6039,6041],{"id":6040},"動態建立-css-檔案","動態建立 CSS 檔案",[22,6043,6045],{"className":24,"code":6044,"language":26,"meta":27,"style":27},"\u002F\u002F CSS 亂數\nvar styleRandom = Math.floor(Math.random() * 1000);\n\n\u002F\u002F 建立 CSS 檔案\nvar styleDocument = document.createElement(\"link\");\n\n\u002F\u002F 設定 CSS 路徑\nstyleDocument.rel = \"StyleSheet\";\nstyleDocument.href = \"css\u002F01.css\" + \"?\" + styleRandom;\n\n\u002F\u002F 將 CSS 文件加入 head\ndocument.head.append(styleDocument);\n",[29,6046,6047,6052,6084,6088,6093,6113,6117,6122,6134,6154,6158,6163],{"__ignoreMap":27},[32,6048,6049],{"class":34,"line":35},[32,6050,6051],{"class":608},"\u002F\u002F CSS 亂數\n",[32,6053,6054,6056,6059,6061,6064,6067,6070,6073,6076,6079,6082],{"class":34,"line":45},[32,6055,166],{"class":38},[32,6057,6058],{"class":41}," styleRandom ",[32,6060,172],{"class":38},[32,6062,6063],{"class":41}," Math.",[32,6065,6066],{"class":217},"floor",[32,6068,6069],{"class":41},"(Math.",[32,6071,6072],{"class":217},"random",[32,6074,6075],{"class":41},"() ",[32,6077,6078],{"class":38},"*",[32,6080,6081],{"class":351}," 1000",[32,6083,230],{"class":41},[32,6085,6086],{"class":34,"line":51},[32,6087,103],{"emptyLinePlaceholder":102},[32,6089,6090],{"class":34,"line":90},[32,6091,6092],{"class":608},"\u002F\u002F 建立 CSS 檔案\n",[32,6094,6095,6097,6100,6102,6104,6106,6108,6111],{"class":34,"line":99},[32,6096,166],{"class":38},[32,6098,6099],{"class":41}," styleDocument ",[32,6101,172],{"class":38},[32,6103,919],{"class":41},[32,6105,922],{"class":217},[32,6107,477],{"class":41},[32,6109,6110],{"class":175},"\"link\"",[32,6112,230],{"class":41},[32,6114,6115],{"class":34,"line":106},[32,6116,103],{"emptyLinePlaceholder":102},[32,6118,6119],{"class":34,"line":114},[32,6120,6121],{"class":608},"\u002F\u002F 設定 CSS 路徑\n",[32,6123,6124,6127,6129,6132],{"class":34,"line":120},[32,6125,6126],{"class":41},"styleDocument.rel ",[32,6128,172],{"class":38},[32,6130,6131],{"class":175}," \"StyleSheet\"",[32,6133,96],{"class":41},[32,6135,6136,6139,6141,6144,6146,6149,6151],{"class":34,"line":127},[32,6137,6138],{"class":41},"styleDocument.href ",[32,6140,172],{"class":38},[32,6142,6143],{"class":175}," \"css\u002F01.css\"",[32,6145,1799],{"class":38},[32,6147,6148],{"class":175}," \"?\"",[32,6150,1799],{"class":38},[32,6152,6153],{"class":41}," styleRandom;\n",[32,6155,6156],{"class":34,"line":132},[32,6157,103],{"emptyLinePlaceholder":102},[32,6159,6160],{"class":34,"line":141},[32,6161,6162],{"class":608},"\u002F\u002F 將 CSS 文件加入 head\n",[32,6164,6165,6168,6171],{"class":34,"line":147},[32,6166,6167],{"class":41},"document.head.",[32,6169,6170],{"class":217},"append",[32,6172,6173],{"class":41},"(styleDocument);\n",[15,6175,6177],{"id":6176},"停用-css","停用 CSS",[22,6179,6181],{"className":24,"code":6180,"language":26,"meta":27,"style":27},"$(\"link[href='_css\u002FMobile_landscape.css']\").prop(\"disabled\", true);\n",[29,6182,6183],{"__ignoreMap":27},[32,6184,6185,6187,6189,6192,6194,6197,6199,6202,6204,6207],{"class":34,"line":35},[32,6186,755],{"class":217},[32,6188,477],{"class":41},[32,6190,6191],{"class":175},"\"link[href='_css\u002FMobile_landscape.css']\"",[32,6193,533],{"class":41},[32,6195,6196],{"class":217},"prop",[32,6198,477],{"class":41},[32,6200,6201],{"class":175},"\"disabled\"",[32,6203,635],{"class":41},[32,6205,6206],{"class":351},"true",[32,6208,230],{"class":41},[15,6210,2004],{"id":2003},[11,6212,6213,6214,6217],{},"動態建立 ",[29,6215,6216],{},"\u003Clink>"," 與停用樣式表的做法現在仍可使用；原文在網址加亂數是為了避開瀏覽器快取，但也會讓每次載入都使用不同網址。現在的建置工具通常會以檔名雜湊處理靜態資源快取。",[11,6219,6220,6221,6223],{},"如果需求是直接用 JavaScript 建立並套用樣式表，也可以參考《document.adoptedStyleSheets：不用 link 元素也能動態套用樣式》。這裡保留原本的 ",[29,6222,6216],{}," 寫法作為當時的紀錄。",[368,6225,6226],{},"html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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);}",{"title":27,"searchDepth":51,"depth":51,"links":6228},[6229,6230,6231],{"id":6040,"depth":45,"text":6041},{"id":6176,"depth":45,"text":6177},{"id":2003,"depth":45,"text":2004},"2018 年整理的動態 CSS 筆記，包含建立 link 元素、以亂數參數避開快取，以及用 jQuery 停用樣式表。",{},"\u002Fposts\u002Fdynamic-link-stylesheet","2018-08-22",[],{"title":6032,"description":6232},"posts\u002F20180822.dynamic-link-stylesheet",[390,2040,1757],"WJki7QRO4-w1nU3GxXrk7vRMlw2cHKmjk88Fvpc8G5s",{"id":6242,"title":6243,"body":6244,"category":378,"description":6706,"draft":380,"extension":381,"image":382,"meta":6707,"navigation":102,"path":6708,"publishedAt":6709,"relatedExperiments":6710,"seo":6711,"stem":6712,"tags":6713,"updatedAt":6716,"__hash__":6717},"posts\u002Fposts\u002F20180923.tweenmax-install-notes.md","TweenMax 安裝與使用筆記",{"type":8,"value":6245,"toc":6693},[6246,6250,6253,6272,6313,6342,6345,6355,6358,6364,6368,6371,6391,6395,6414,6420,6425,6437,6443,6448,6454,6457,6480,6483,6489,6494,6562,6565,6569,6575,6578,6581,6599,6602,6608,6632,6635,6661,6669,6672,6690],[15,6247,6249],{"id":6248},"使用-nvm-管理-nodejs","使用 NVM 管理 Node.js",[11,6251,6252],{},"如果直接從官網下載 Node.js，只會安裝固定版本；NVM 可以用來安裝及切換不同版本。",[4538,6254,6255,6263,6269],{},[4541,6256,6257,6258],{},"下載：",[412,6259,6262],{"href":6260,"rel":6261},"https:\u002F\u002Fgithub.com\u002Fcoreybutler\u002Fnvm-windows\u002Freleases",[416],"nvm-windows",[4541,6264,6265,6266],{},"點選 ",[29,6267,6268],{},"nvm-setup.zip",[4541,6270,6271],{},"開啟命令提示字元安裝 Node.js",[22,6273,6275],{"className":2057,"code":6274,"language":2059,"meta":27,"style":27},"nvm list\nnvm list available\nnvm install v8.12.0\nnvm use v8.12.0\n",[29,6276,6277,6285,6295,6304],{"__ignoreMap":27},[32,6278,6279,6282],{"class":34,"line":35},[32,6280,6281],{"class":2066},"nvm",[32,6283,6284],{"class":175}," list\n",[32,6286,6287,6289,6292],{"class":34,"line":45},[32,6288,6281],{"class":2066},[32,6290,6291],{"class":175}," list",[32,6293,6294],{"class":175}," available\n",[32,6296,6297,6299,6301],{"class":34,"line":51},[32,6298,6281],{"class":2066},[32,6300,4650],{"class":175},[32,6302,6303],{"class":175}," v8.12.0\n",[32,6305,6306,6308,6311],{"class":34,"line":90},[32,6307,6281],{"class":2066},[32,6309,6310],{"class":175}," use",[32,6312,6303],{"class":175},[4538,6314,6315,6321,6327,6333,6339],{},[4541,6316,6317,6320],{},[29,6318,6319],{},"nvm list","：查看已安裝的版本",[4541,6322,6323,6326],{},[29,6324,6325],{},"nvm list available","：查看可以安裝的 Node.js 版本",[4541,6328,6329,6332],{},[29,6330,6331],{},"nvm install v8.12.0","：安裝指定版本",[4541,6334,6335,6338],{},[29,6336,6337],{},"nvm use v8.12.0","：切換到指定版本",[4541,6340,6341],{},"LTS：穩定版本",[11,6343,6344],{},"確認是否安裝完成：",[22,6346,6347],{"className":2057,"code":4595,"language":2059,"meta":27,"style":27},[29,6348,6349],{"__ignoreMap":27},[32,6350,6351,6353],{"class":34,"line":35},[32,6352,4602],{"class":2066},[32,6354,4605],{"class":351},[11,6356,6357],{},"安裝的版本是：",[22,6359,6362],{"className":6360,"code":6361,"language":426,"meta":27},[424],"v8.12.0\n",[29,6363,6361],{"__ignoreMap":27},[15,6365,6367],{"id":6366},"vs-code-套件","VS Code 套件",[11,6369,6370],{},"這裡也順便分享常用的VSCode套件:",[4538,6372,6373,6376,6379,6382,6385,6388],{},[4541,6374,6375],{},"Monokai Dark Soda：編輯器配色",[4541,6377,6378],{},"Live Server：模擬 server 環境並開啟網頁",[4541,6380,6381],{},"colorize：顯示顏色標籤",[4541,6383,6384],{},"AutoFileName：協助尋找路徑",[4541,6386,6387],{},"vscode-icons：顯示專案資料夾圖示",[4541,6389,6390],{},"Copy filename：在右鍵選單直接複製檔名",[15,6392,6394],{"id":6393},"tweenmax","TweenMax",[4538,6396,6397,6406],{},[4541,6398,6399,6400,6405],{},"安裝：",[412,6401,6404],{"href":6402,"rel":6403},"https:\u002F\u002Fgreensock.com\u002Ftweenmax",[416],"TweenMax 下載頁","（右上角下載）",[4541,6407,6408,6409],{},"動畫曲線：",[412,6410,6413],{"href":6411,"rel":6412},"https:\u002F\u002Fgreensock.com\u002Fease-visualizer",[416],"Ease Visualizer",[151,6415,6417],{"id":6416},"to",[29,6418,6419],{},"to()",[11,6421,6422,6424],{},[29,6423,6416],{}," 表示動畫要到哪裡，可以帶三個參數：",[6426,6427,6428,6431,6434],"ol",{},[4541,6429,6430],{},"要改變哪個物件，例如 ID 或 class。",[4541,6432,6433],{},"動畫要在幾秒內完成。",[4541,6435,6436],{},"要改變的屬性、函式與其他選項。",[151,6438,6440],{"id":6439},"transformorigin",[29,6441,6442],{},"transformOrigin",[11,6444,6445,6447],{},[29,6446,6442],{}," 是中心點定位屬性：",[22,6449,6452],{"className":6450,"code":6451,"language":426,"meta":27},[424],"(0,0)   (50,0)   (100,0)\n(0,50)  (50,50)  (100,50)\n(0,100) (50,100) (100,100)\n",[29,6453,6451],{"__ignoreMap":27},[151,6455,6456],{"id":6456},"回呼函式",[4538,6458,6459,6465,6471],{},[4541,6460,6461,6464],{},[29,6462,6463],{},"onStart","：動畫開始以前執行，只會執行一次。",[4541,6466,6467,6470],{},[29,6468,6469],{},"onUpdate","：動畫移動過程中不斷執行。",[4541,6472,6473,6476,6477,3775],{},[29,6474,6475],{},"onComplete","：動畫結束時呼叫，只會執行一次，例如 ",[29,6478,6479],{},"onComplete: move",[11,6481,6482],{},"這些回呼可用來做動畫生命週期的階段性偵測。",[151,6484,6486],{"id":6485},"staggerto",[29,6487,6488],{},"staggerTo",[11,6490,6491,6493],{},[29,6492,6488],{}," 會依序處理元素：",[22,6495,6497],{"className":24,"code":6496,"language":26,"meta":27,"style":27},"TweenMax.staggerTo(\n  '.pic',\n  0.5,\n  {\n    transform: 'translateY(0px)',\n    opacity: 1,\n  },\n  0.2,\n);\n",[29,6498,6499,6508,6515,6522,6527,6537,6547,6551,6558],{"__ignoreMap":27},[32,6500,6501,6504,6506],{"class":34,"line":35},[32,6502,6503],{"class":41},"TweenMax.",[32,6505,6488],{"class":217},[32,6507,539],{"class":41},[32,6509,6510,6513],{"class":34,"line":45},[32,6511,6512],{"class":175},"  '.pic'",[32,6514,547],{"class":41},[32,6516,6517,6520],{"class":34,"line":51},[32,6518,6519],{"class":351},"  0.5",[32,6521,547],{"class":41},[32,6523,6524],{"class":34,"line":90},[32,6525,6526],{"class":41},"  {\n",[32,6528,6529,6532,6535],{"class":34,"line":99},[32,6530,6531],{"class":41},"    transform: ",[32,6533,6534],{"class":175},"'translateY(0px)'",[32,6536,547],{"class":41},[32,6538,6539,6542,6545],{"class":34,"line":106},[32,6540,6541],{"class":41},"    opacity: ",[32,6543,6544],{"class":351},"1",[32,6546,547],{"class":41},[32,6548,6549],{"class":34,"line":114},[32,6550,5639],{"class":41},[32,6552,6553,6556],{"class":34,"line":120},[32,6554,6555],{"class":351},"  0.2",[32,6557,547],{"class":41},[32,6559,6560],{"class":34,"line":127},[32,6561,230],{"class":41},[11,6563,6564],{},"所有物件會間隔 0.2 秒出現。",[151,6566,6568],{"id":6567},"scrolltoplugin","ScrollToPlugin",[11,6570,6571,6572,3775],{},"捲軸處理使用 ScrollToPlugin，使用方式可查官方文件裡的 ",[29,6573,6574],{},"scrollTo",[15,6576,6577],{"id":6577},"其他動畫筆記",[11,6579,6580],{},"水平置中的 CSS：",[22,6582,6584],{"className":5412,"code":6583,"language":1140,"meta":27,"style":27},"display: flex;\njustify-content: center;\n",[29,6585,6586,6591],{"__ignoreMap":27},[32,6587,6588],{"class":34,"line":35},[32,6589,6590],{"class":41},"display: flex;\n",[32,6592,6593,6596],{"class":34,"line":45},[32,6594,6595],{"class":4049},"justify-content",[32,6597,6598],{"class":41},": center;\n",[11,6600,6601],{},"製作動畫時，先想好最後要呈現的樣子，再做初始化動作。",[11,6603,6604,6605,5240],{},"取得瀏覽器高度時，要扣掉物件本身的高度。物件的起始位置是左上角 ",[29,6606,6607],{},"(0, 0)",[22,6609,6611],{"className":24,"code":6610,"language":26,"meta":27,"style":27},"const availableHeight = window.innerHeight - elementHeight;\n",[29,6612,6613],{"__ignoreMap":27},[32,6614,6615,6618,6621,6623,6626,6629],{"class":34,"line":35},[32,6616,6617],{"class":38},"const",[32,6619,6620],{"class":351}," availableHeight",[32,6622,894],{"class":38},[32,6624,6625],{"class":41}," window.innerHeight ",[32,6627,6628],{"class":38},"-",[32,6630,6631],{"class":41}," elementHeight;\n",[11,6633,6634],{},"無限重複旋轉與運動速率：",[22,6636,6638],{"className":24,"code":6637,"language":26,"meta":27,"style":27},"repeat: -1;\nease: Linear.easeInOut;\n",[29,6639,6640,6653],{"__ignoreMap":27},[32,6641,6642,6645,6647,6649,6651],{"class":34,"line":35},[32,6643,6644],{"class":2066},"repeat",[32,6646,5269],{"class":41},[32,6648,6628],{"class":38},[32,6650,6544],{"class":351},[32,6652,96],{"class":41},[32,6654,6655,6658],{"class":34,"line":45},[32,6656,6657],{"class":2066},"ease",[32,6659,6660],{"class":41},": Linear.easeInOut;\n",[11,6662,6663,6664,3775],{},"各瀏覽器滑鼠滾動事件相容性可參考：",[412,6665,6668],{"href":6666,"rel":6667},"https:\u002F\u002Fwww.zhangxinxu.com\u002Fstudy\u002F201304\u002Fmousewheel-dommousescroll-same-different.html",[416],"滑鼠滾輪事件的相容性整理",[15,6670,6671],{"id":6671},"快捷鍵",[4538,6673,6674,6680],{},[4541,6675,6676,6679],{},[29,6677,6678],{},"Shift"," + 滑鼠右鍵：開啟命令視窗",[4541,6681,6682,6685,6686,6689],{},[29,6683,6684],{},"Ctrl+P","：輸入 ",[29,6687,6688],{},">icon"," 更換 VS Code 圖示",[368,6691,6692],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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 .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}",{"title":27,"searchDepth":51,"depth":51,"links":6694},[6695,6696,6697,6704,6705],{"id":6248,"depth":45,"text":6249},{"id":6366,"depth":45,"text":6367},{"id":6393,"depth":45,"text":6394,"children":6698},[6699,6700,6701,6702,6703],{"id":6416,"depth":51,"text":6419},{"id":6439,"depth":51,"text":6442},{"id":6456,"depth":51,"text":6456},{"id":6485,"depth":51,"text":6488},{"id":6567,"depth":51,"text":6568},{"id":6577,"depth":45,"text":6577},{"id":6671,"depth":45,"text":6671},"2018 年整理的 Node.js、VS Code 與 TweenMax 安裝及動畫操作筆記。",{},"\u002Fposts\u002Ftweenmax-install-notes","2018-09-23",[],{"title":6243,"description":6706},"posts\u002F20180923.tweenmax-install-notes",[6714,6394,6715,390],"GSAP","動畫","2026-08-24","bKt6DRP6TnBxibZGme3mfoXPifvC_27ObmOt0Rs1rrQ",{"id":6719,"title":6720,"body":6721,"category":378,"description":7025,"draft":380,"extension":381,"image":382,"meta":7026,"navigation":102,"path":7027,"publishedAt":7028,"relatedExperiments":7029,"seo":7030,"stem":7031,"tags":7032,"updatedAt":392,"__hash__":7034},"posts\u002Fposts\u002F20180926.legacy-virtual-keyboard-detection.md","JavaScript 虛擬鍵盤高度偵測筆記",{"type":8,"value":6722,"toc":7023},[6723,6730,7020],[11,6724,6725,6726,6729],{},"這段程式會記錄頁面原本的高度，並在 ",[29,6727,6728],{},"resize"," 時比較目前高度，判斷手機虛擬鍵盤是否彈出。",[22,6731,6733],{"className":24,"code":6732,"language":26,"meta":27,"style":27},"$(function () {\n  var winHeight = $(window).height();\n  $('.num').append('原高度:' + winHeight);\n\n  $(window).resize(function () {\n    var thisHeight = $(this).height();\n    $('.num').append('此高度:' + thisHeight);\n\n    if (winHeight - thisHeight > 50) {\n      \u002F\u002F 當軟鍵盤彈出，在這裡操作\n      $('body').css('height', winHeight + 'px');\n      $('body').css('background', 'red');\n      $('.num').append('鍵盤彈出後高度:' + winHeight);\n    } else {\n      \u002F\u002F 當軟鍵盤收起，在這裡操作\n      $('body').css('height', '100%');\n      $('.num').append('鍵盤收起:' + winHeight);\n    }\n  });\n});\n",[29,6734,6735,6745,6765,6788,6792,6806,6827,6849,6853,6871,6876,6905,6929,6950,6959,6964,6987,7008,7012,7016],{"__ignoreMap":27},[32,6736,6737,6739,6741,6743],{"class":34,"line":35},[32,6738,755],{"class":217},[32,6740,477],{"class":41},[32,6742,665],{"class":38},[32,6744,668],{"class":41},[32,6746,6747,6749,6752,6754,6757,6760,6763],{"class":34,"line":45},[32,6748,888],{"class":38},[32,6750,6751],{"class":41}," winHeight ",[32,6753,172],{"class":38},[32,6755,6756],{"class":217}," $",[32,6758,6759],{"class":41},"(window).",[32,6761,6762],{"class":217},"height",[32,6764,687],{"class":41},[32,6766,6767,6769,6771,6774,6776,6778,6780,6783,6785],{"class":34,"line":51},[32,6768,525],{"class":217},[32,6770,477],{"class":41},[32,6772,6773],{"class":175},"'.num'",[32,6775,533],{"class":41},[32,6777,6170],{"class":217},[32,6779,477],{"class":41},[32,6781,6782],{"class":175},"'原高度:'",[32,6784,1799],{"class":38},[32,6786,6787],{"class":41}," winHeight);\n",[32,6789,6790],{"class":34,"line":90},[32,6791,103],{"emptyLinePlaceholder":102},[32,6793,6794,6796,6798,6800,6802,6804],{"class":34,"line":99},[32,6795,525],{"class":217},[32,6797,6759],{"class":41},[32,6799,6728],{"class":217},[32,6801,477],{"class":41},[32,6803,665],{"class":38},[32,6805,668],{"class":41},[32,6807,6808,6810,6813,6815,6817,6819,6821,6823,6825],{"class":34,"line":106},[32,6809,911],{"class":38},[32,6811,6812],{"class":41}," thisHeight ",[32,6814,172],{"class":38},[32,6816,6756],{"class":217},[32,6818,477],{"class":41},[32,6820,1083],{"class":351},[32,6822,533],{"class":41},[32,6824,6762],{"class":217},[32,6826,687],{"class":41},[32,6828,6829,6831,6833,6835,6837,6839,6841,6844,6846],{"class":34,"line":114},[32,6830,674],{"class":217},[32,6832,477],{"class":41},[32,6834,6773],{"class":175},[32,6836,533],{"class":41},[32,6838,6170],{"class":217},[32,6840,477],{"class":41},[32,6842,6843],{"class":175},"'此高度:'",[32,6845,1799],{"class":38},[32,6847,6848],{"class":41}," thisHeight);\n",[32,6850,6851],{"class":34,"line":120},[32,6852,103],{"emptyLinePlaceholder":102},[32,6854,6855,6857,6860,6862,6864,6866,6869],{"class":34,"line":127},[32,6856,5554],{"class":38},[32,6858,6859],{"class":41}," (winHeight ",[32,6861,6628],{"class":38},[32,6863,6812],{"class":41},[32,6865,1615],{"class":38},[32,6867,6868],{"class":351}," 50",[32,6870,520],{"class":41},[32,6872,6873],{"class":34,"line":132},[32,6874,6875],{"class":608},"      \u002F\u002F 當軟鍵盤彈出，在這裡操作\n",[32,6877,6878,6881,6883,6886,6888,6890,6892,6895,6898,6900,6903],{"class":34,"line":141},[32,6879,6880],{"class":217},"      $",[32,6882,477],{"class":41},[32,6884,6885],{"class":175},"'body'",[32,6887,533],{"class":41},[32,6889,1140],{"class":217},[32,6891,477],{"class":41},[32,6893,6894],{"class":175},"'height'",[32,6896,6897],{"class":41},", winHeight ",[32,6899,224],{"class":38},[32,6901,6902],{"class":175}," 'px'",[32,6904,230],{"class":41},[32,6906,6907,6909,6911,6913,6915,6917,6919,6922,6924,6927],{"class":34,"line":147},[32,6908,6880],{"class":217},[32,6910,477],{"class":41},[32,6912,6885],{"class":175},[32,6914,533],{"class":41},[32,6916,1140],{"class":217},[32,6918,477],{"class":41},[32,6920,6921],{"class":175},"'background'",[32,6923,635],{"class":41},[32,6925,6926],{"class":175},"'red'",[32,6928,230],{"class":41},[32,6930,6931,6933,6935,6937,6939,6941,6943,6946,6948],{"class":34,"line":275},[32,6932,6880],{"class":217},[32,6934,477],{"class":41},[32,6936,6773],{"class":175},[32,6938,533],{"class":41},[32,6940,6170],{"class":217},[32,6942,477],{"class":41},[32,6944,6945],{"class":175},"'鍵盤彈出後高度:'",[32,6947,1799],{"class":38},[32,6949,6787],{"class":41},[32,6951,6952,6955,6957],{"class":34,"line":282},[32,6953,6954],{"class":41},"    } ",[32,6956,565],{"class":38},[32,6958,703],{"class":41},[32,6960,6961],{"class":34,"line":295},[32,6962,6963],{"class":608},"      \u002F\u002F 當軟鍵盤收起，在這裡操作\n",[32,6965,6966,6968,6970,6972,6974,6976,6978,6980,6982,6985],{"class":34,"line":605},[32,6967,6880],{"class":217},[32,6969,477],{"class":41},[32,6971,6885],{"class":175},[32,6973,533],{"class":41},[32,6975,1140],{"class":217},[32,6977,477],{"class":41},[32,6979,6894],{"class":175},[32,6981,635],{"class":41},[32,6983,6984],{"class":175},"'100%'",[32,6986,230],{"class":41},[32,6988,6989,6991,6993,6995,6997,6999,7001,7004,7006],{"class":34,"line":612},[32,6990,6880],{"class":217},[32,6992,477],{"class":41},[32,6994,6773],{"class":175},[32,6996,533],{"class":41},[32,6998,6170],{"class":217},[32,7000,477],{"class":41},[32,7002,7003],{"class":175},"'鍵盤收起:'",[32,7005,1799],{"class":38},[32,7007,6787],{"class":41},[32,7009,7010],{"class":34,"line":617},[32,7011,5591],{"class":41},[32,7013,7014],{"class":34,"line":644},[32,7015,693],{"class":41},[32,7017,7018],{"class":34,"line":649},[32,7019,792],{"class":41},[368,7021,7022],{},"html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}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);}",{"title":27,"searchDepth":51,"depth":51,"links":7024},[],"2018 年透過 resize 前後的視窗高度差，判斷手機虛擬鍵盤是否彈出的筆記。",{},"\u002Fposts\u002Flegacy-virtual-keyboard-detection","2018-09-26",[],{"title":6720,"description":7025},"posts\u002F20180926.legacy-virtual-keyboard-detection",[390,2040,6027,7033],"虛擬鍵盤","8Urv9Xsdipg5PNg_ssEXmNCca4KT7IS9UI8ifjm87SA",{"id":7036,"title":7037,"body":7038,"category":378,"description":8402,"draft":380,"extension":381,"image":382,"meta":8403,"navigation":102,"path":8404,"publishedAt":8405,"relatedExperiments":8406,"seo":8407,"stem":8408,"tags":8409,"updatedAt":392,"__hash__":8411},"posts\u002Fposts\u002F20181024.webpack4-config-notes.md","Webpack 4 設定筆記",{"type":8,"value":7039,"toc":8394},[7040,7044,7047,7067,7073,7084,7094,7098,7190,7214,7226,7386,7389,7401,7404,7410,7420,7442,7455,7476,7489,7568,7571,7583,7587,7595,7730,7733,7747,7751,7760,7772,7784,7966,7985,7988,7992,7995,8017,8037,8041,8240,8245,8329,8332,8344,8356,8363,8366,8368,8391],[15,7041,7043],{"id":7042},"webpack-一開始的配置","Webpack 一開始的配置",[11,7045,7046],{},"建立環境：",[22,7048,7050],{"className":2057,"code":7049,"language":2059,"meta":27,"style":27},"npm install webpack webpack-cli --save-dev\n",[29,7051,7052],{"__ignoreMap":27},[32,7053,7054,7056,7058,7061,7064],{"class":34,"line":35},[32,7055,4625],{"class":2066},[32,7057,4650],{"class":175},[32,7059,7060],{"class":175}," webpack",[32,7062,7063],{"class":175}," webpack-cli",[32,7065,7066],{"class":351}," --save-dev\n",[11,7068,7069,7070,7072],{},"用 npm 建立 ",[29,7071,4945],{},"。設定檔資訊可以先按 Enter 跳過，之後再補：",[22,7074,7076],{"className":2057,"code":7075,"language":2059,"meta":27,"style":27},"npm init\n",[29,7077,7078],{"__ignoreMap":27},[32,7079,7080,7082],{"class":34,"line":35},[32,7081,4625],{"class":2066},[32,7083,2390],{"class":175},[11,7085,7086,7087,7090,7091,3775],{},"建立 ",[29,7088,7089],{},"webpack.config.js"," 與 ",[29,7092,7093],{},"index.js",[11,7095,7096,5240],{},[29,7097,7089],{},[22,7099,7101],{"className":24,"code":7100,"language":26,"meta":27,"style":27},"const path = require('path');\n\nmodule.exports = {\n  entry: '.\u002Findex.js',\n  output: {\n    filename: 'index.bundle.js',\n    path: path.resolve(__dirname, '.\u002F'),\n  },\n};\n",[29,7102,7103,7122,7126,7140,7150,7155,7165,7181,7185],{"__ignoreMap":27},[32,7104,7105,7107,7110,7112,7115,7117,7120],{"class":34,"line":35},[32,7106,6617],{"class":38},[32,7108,7109],{"class":351}," path",[32,7111,894],{"class":38},[32,7113,7114],{"class":217}," require",[32,7116,477],{"class":41},[32,7118,7119],{"class":175},"'path'",[32,7121,230],{"class":41},[32,7123,7124],{"class":34,"line":45},[32,7125,103],{"emptyLinePlaceholder":102},[32,7127,7128,7131,7133,7136,7138],{"class":34,"line":51},[32,7129,7130],{"class":351},"module",[32,7132,1278],{"class":41},[32,7134,7135],{"class":351},"exports",[32,7137,894],{"class":38},[32,7139,703],{"class":41},[32,7141,7142,7145,7148],{"class":34,"line":90},[32,7143,7144],{"class":41},"  entry: ",[32,7146,7147],{"class":175},"'.\u002Findex.js'",[32,7149,547],{"class":41},[32,7151,7152],{"class":34,"line":99},[32,7153,7154],{"class":41},"  output: {\n",[32,7156,7157,7160,7163],{"class":34,"line":106},[32,7158,7159],{"class":41},"    filename: ",[32,7161,7162],{"class":175},"'index.bundle.js'",[32,7164,547],{"class":41},[32,7166,7167,7170,7173,7176,7179],{"class":34,"line":114},[32,7168,7169],{"class":41},"    path: path.",[32,7171,7172],{"class":217},"resolve",[32,7174,7175],{"class":41},"(__dirname, ",[32,7177,7178],{"class":175},"'.\u002F'",[32,7180,5101],{"class":41},[32,7182,7183],{"class":34,"line":120},[32,7184,5639],{"class":41},[32,7186,7187],{"class":34,"line":127},[32,7188,7189],{"class":41},"};\n",[4538,7191,7192,7200],{},[4541,7193,7194,7197,7198,3775],{},[29,7195,7196],{},"entry","：進入點，這裡指定為 ",[29,7199,7093],{},[4541,7201,7202,7205,7206,7209,7210,7213],{},[29,7203,7204],{},"output","：打包後的輸出檔案。檔名在 ",[29,7207,7208],{},"index"," 後面加上 ",[29,7211,7212],{},"bundle","，用來辨認這是打包後的檔案。",[11,7215,5134,7216,7218,7219,7222,7223,5240],{},[29,7217,4945],{}," 的 ",[29,7220,7221],{},"scripts"," 加入 ",[29,7224,7225],{},"build",[22,7227,7229],{"className":5243,"code":7228,"language":5245,"meta":27,"style":27},"{\n  \"name\": \"webpack-test\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\",\n    \"build\": \"webpack\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"webpack\": \"^4.6.0\",\n    \"webpack-cli\": \"^2.0.15\"\n  }\n}\n",[29,7230,7231,7235,7247,7259,7271,7283,7289,7313,7322,7326,7337,7349,7356,7368,7378,7382],{"__ignoreMap":27},[32,7232,7233],{"class":34,"line":35},[32,7234,5252],{"class":41},[32,7236,7237,7240,7242,7245],{"class":34,"line":45},[32,7238,7239],{"class":5257},"  \"name\"",[32,7241,5269],{"class":41},[32,7243,7244],{"class":175},"\"webpack-test\"",[32,7246,547],{"class":41},[32,7248,7249,7252,7254,7257],{"class":34,"line":51},[32,7250,7251],{"class":5257},"  \"version\"",[32,7253,5269],{"class":41},[32,7255,7256],{"class":175},"\"1.0.0\"",[32,7258,547],{"class":41},[32,7260,7261,7264,7266,7269],{"class":34,"line":90},[32,7262,7263],{"class":5257},"  \"description\"",[32,7265,5269],{"class":41},[32,7267,7268],{"class":175},"\"\"",[32,7270,547],{"class":41},[32,7272,7273,7276,7278,7281],{"class":34,"line":99},[32,7274,7275],{"class":5257},"  \"main\"",[32,7277,5269],{"class":41},[32,7279,7280],{"class":175},"\"index.js\"",[32,7282,547],{"class":41},[32,7284,7285,7287],{"class":34,"line":106},[32,7286,5258],{"class":5257},[32,7288,5261],{"class":41},[32,7290,7291,7294,7296,7299,7303,7306,7308,7311],{"class":34,"line":114},[32,7292,7293],{"class":5257},"    \"test\"",[32,7295,5269],{"class":41},[32,7297,7298],{"class":175},"\"echo ",[32,7300,7302],{"class":7301},"sw7DY","\\\"",[32,7304,7305],{"class":175},"Error: no test specified",[32,7307,7302],{"class":7301},[32,7309,7310],{"class":175}," && exit 1\"",[32,7312,547],{"class":41},[32,7314,7315,7317,7319],{"class":34,"line":120},[32,7316,5279],{"class":5257},[32,7318,5269],{"class":41},[32,7320,7321],{"class":175},"\"webpack\"\n",[32,7323,7324],{"class":34,"line":127},[32,7325,5639],{"class":41},[32,7327,7328,7331,7333,7335],{"class":34,"line":132},[32,7329,7330],{"class":5257},"  \"author\"",[32,7332,5269],{"class":41},[32,7334,7268],{"class":175},[32,7336,547],{"class":41},[32,7338,7339,7342,7344,7347],{"class":34,"line":141},[32,7340,7341],{"class":5257},"  \"license\"",[32,7343,5269],{"class":41},[32,7345,7346],{"class":175},"\"ISC\"",[32,7348,547],{"class":41},[32,7350,7351,7354],{"class":34,"line":147},[32,7352,7353],{"class":5257},"  \"devDependencies\"",[32,7355,5261],{"class":41},[32,7357,7358,7361,7363,7366],{"class":34,"line":275},[32,7359,7360],{"class":5257},"    \"webpack\"",[32,7362,5269],{"class":41},[32,7364,7365],{"class":175},"\"^4.6.0\"",[32,7367,547],{"class":41},[32,7369,7370,7373,7375],{"class":34,"line":282},[32,7371,7372],{"class":5257},"    \"webpack-cli\"",[32,7374,5269],{"class":41},[32,7376,7377],{"class":175},"\"^2.0.15\"\n",[32,7379,7380],{"class":34,"line":295},[32,7381,1203],{"class":41},[32,7383,7384],{"class":34,"line":605},[32,7385,54],{"class":41},[11,7387,7388],{},"開啟終端機並執行打包：",[22,7390,7391],{"className":2057,"code":4973,"language":2059,"meta":27,"style":27},[29,7392,7393],{"__ignoreMap":27},[32,7394,7395,7397,7399],{"class":34,"line":35},[32,7396,4625],{"class":2066},[32,7398,4743],{"class":175},[32,7400,4755],{"class":175},[15,7402,7403],{"id":7403},"檔案路徑設定",[22,7405,7408],{"className":7406,"code":7407,"language":426,"meta":27},[424],"src\u002F  開發中的檔案\ndist\u002F 輸出編譯後的檔案\n",[29,7409,7407],{"__ignoreMap":27},[11,7411,7412,7413,7416,7417,7419],{},"Node.js 搬移檔案需要使用 ",[29,7414,7415],{},"path"," API，因此先在 ",[29,7418,7089],{}," 引入：",[22,7421,7423],{"className":24,"code":7422,"language":26,"meta":27,"style":27},"var path = require('path');\n",[29,7424,7425],{"__ignoreMap":27},[32,7426,7427,7429,7432,7434,7436,7438,7440],{"class":34,"line":35},[32,7428,166],{"class":38},[32,7430,7431],{"class":41}," path ",[32,7433,172],{"class":38},[32,7435,7114],{"class":217},[32,7437,477],{"class":41},[32,7439,7119],{"class":175},[32,7441,230],{"class":41},[11,7443,5134,7444,7222,7447,7450,7451,7454],{},[29,7445,7446],{},"module.exports",[29,7448,7449],{},"context","，用 ",[29,7452,7453],{},"path.resolve"," 指定起始路徑：",[22,7456,7458],{"className":24,"code":7457,"language":26,"meta":27,"style":27},"context: path.resolve(__dirname, 'src'),\n",[29,7459,7460],{"__ignoreMap":27},[32,7461,7462,7464,7467,7469,7471,7474],{"class":34,"line":35},[32,7463,7449],{"class":2066},[32,7465,7466],{"class":41},": path.",[32,7468,7172],{"class":217},[32,7470,7175],{"class":41},[32,7472,7473],{"class":175},"'src'",[32,7475,5101],{"class":41},[11,7477,7478,7481,7482,7484,7485,7488],{},[29,7479,7480],{},"__dirname"," 是目前執行檔案所在目錄的完整位置。第一個參數使用 ",[29,7483,7480],{},"，第二個參數指定從 ",[29,7486,7487],{},"src"," 資料夾開始找檔案。",[22,7490,7492],{"className":24,"code":7491,"language":26,"meta":27,"style":27},"var path = require('path');\n\nmodule.exports = {\n  context: path.resolve(__dirname, 'src'),\n  entry: '.\u002Findex.js',\n  output: {\n    filename: 'index-bundle.js',\n  },\n};\n",[29,7493,7494,7510,7514,7526,7539,7547,7551,7560,7564],{"__ignoreMap":27},[32,7495,7496,7498,7500,7502,7504,7506,7508],{"class":34,"line":35},[32,7497,166],{"class":38},[32,7499,7431],{"class":41},[32,7501,172],{"class":38},[32,7503,7114],{"class":217},[32,7505,477],{"class":41},[32,7507,7119],{"class":175},[32,7509,230],{"class":41},[32,7511,7512],{"class":34,"line":45},[32,7513,103],{"emptyLinePlaceholder":102},[32,7515,7516,7518,7520,7522,7524],{"class":34,"line":51},[32,7517,7130],{"class":351},[32,7519,1278],{"class":41},[32,7521,7135],{"class":351},[32,7523,894],{"class":38},[32,7525,703],{"class":41},[32,7527,7528,7531,7533,7535,7537],{"class":34,"line":90},[32,7529,7530],{"class":41},"  context: path.",[32,7532,7172],{"class":217},[32,7534,7175],{"class":41},[32,7536,7473],{"class":175},[32,7538,5101],{"class":41},[32,7540,7541,7543,7545],{"class":34,"line":99},[32,7542,7144],{"class":41},[32,7544,7147],{"class":175},[32,7546,547],{"class":41},[32,7548,7549],{"class":34,"line":106},[32,7550,7154],{"class":41},[32,7552,7553,7555,7558],{"class":34,"line":114},[32,7554,7159],{"class":41},[32,7556,7557],{"class":175},"'index-bundle.js'",[32,7559,547],{"class":41},[32,7561,7562],{"class":34,"line":120},[32,7563,5639],{"class":41},[32,7565,7566],{"class":34,"line":127},[32,7567,7189],{"class":41},[11,7569,7570],{},"設定路徑後執行：",[22,7572,7573],{"className":2057,"code":4973,"language":2059,"meta":27,"style":27},[29,7574,7575],{"__ignoreMap":27},[32,7576,7577,7579,7581],{"class":34,"line":35},[32,7578,4625],{"class":2066},[32,7580,4743],{"class":175},[32,7582,4755],{"class":175},[15,7584,7586],{"id":7585},"watch-設定","watch 設定",[11,7588,5134,7589,7222,7591,7594],{},[29,7590,4945],{},[29,7592,7593],{},"watch","，監控 JavaScript 檔案；檔案變動後會自動執行編譯。",[22,7596,7598],{"className":5243,"code":7597,"language":5245,"meta":27,"style":27},"{\n  \"name\": \"watch\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"webpack.config.js\",\n  \"scripts\": {\n    \"watch\": \"webpack --watch\",\n    \"build\": \"webpack\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"webpack\": \"^4.16.5\",\n    \"webpack-cli\": \"^3.1.0\"\n  }\n}\n",[29,7599,7600,7604,7615,7625,7635,7646,7652,7664,7672,7676,7686,7696,7702,7713,7722,7726],{"__ignoreMap":27},[32,7601,7602],{"class":34,"line":35},[32,7603,5252],{"class":41},[32,7605,7606,7608,7610,7613],{"class":34,"line":45},[32,7607,7239],{"class":5257},[32,7609,5269],{"class":41},[32,7611,7612],{"class":175},"\"watch\"",[32,7614,547],{"class":41},[32,7616,7617,7619,7621,7623],{"class":34,"line":51},[32,7618,7251],{"class":5257},[32,7620,5269],{"class":41},[32,7622,7256],{"class":175},[32,7624,547],{"class":41},[32,7626,7627,7629,7631,7633],{"class":34,"line":90},[32,7628,7263],{"class":5257},[32,7630,5269],{"class":41},[32,7632,7268],{"class":175},[32,7634,547],{"class":41},[32,7636,7637,7639,7641,7644],{"class":34,"line":99},[32,7638,7275],{"class":5257},[32,7640,5269],{"class":41},[32,7642,7643],{"class":175},"\"webpack.config.js\"",[32,7645,547],{"class":41},[32,7647,7648,7650],{"class":34,"line":106},[32,7649,5258],{"class":5257},[32,7651,5261],{"class":41},[32,7653,7654,7657,7659,7662],{"class":34,"line":114},[32,7655,7656],{"class":5257},"    \"watch\"",[32,7658,5269],{"class":41},[32,7660,7661],{"class":175},"\"webpack --watch\"",[32,7663,547],{"class":41},[32,7665,7666,7668,7670],{"class":34,"line":120},[32,7667,5279],{"class":5257},[32,7669,5269],{"class":41},[32,7671,7321],{"class":175},[32,7673,7674],{"class":34,"line":127},[32,7675,5639],{"class":41},[32,7677,7678,7680,7682,7684],{"class":34,"line":132},[32,7679,7330],{"class":5257},[32,7681,5269],{"class":41},[32,7683,7268],{"class":175},[32,7685,547],{"class":41},[32,7687,7688,7690,7692,7694],{"class":34,"line":141},[32,7689,7341],{"class":5257},[32,7691,5269],{"class":41},[32,7693,7346],{"class":175},[32,7695,547],{"class":41},[32,7697,7698,7700],{"class":34,"line":147},[32,7699,7353],{"class":5257},[32,7701,5261],{"class":41},[32,7703,7704,7706,7708,7711],{"class":34,"line":275},[32,7705,7360],{"class":5257},[32,7707,5269],{"class":41},[32,7709,7710],{"class":175},"\"^4.16.5\"",[32,7712,547],{"class":41},[32,7714,7715,7717,7719],{"class":34,"line":282},[32,7716,7372],{"class":5257},[32,7718,5269],{"class":41},[32,7720,7721],{"class":175},"\"^3.1.0\"\n",[32,7723,7724],{"class":34,"line":295},[32,7725,1203],{"class":41},[32,7727,7728],{"class":34,"line":605},[32,7729,54],{"class":41},[11,7731,7732],{},"設定完成後執行：",[22,7734,7736],{"className":2057,"code":7735,"language":2059,"meta":27,"style":27},"npm run watch\n",[29,7737,7738],{"__ignoreMap":27},[32,7739,7740,7742,7744],{"class":34,"line":35},[32,7741,4625],{"class":2066},[32,7743,4743],{"class":175},[32,7745,7746],{"class":175}," watch\n",[15,7748,7750],{"id":7749},"mode-設定","mode 設定",[11,7752,7753,7754,7090,7757,5240],{},"Webpack 編譯分為 ",[29,7755,7756],{},"production",[29,7758,7759],{},"development",[4538,7761,7762,7767],{},[4541,7763,7764,7766],{},[29,7765,7756],{},"：上線版本，會壓縮與最佳化檔案。",[4541,7768,7769,7771],{},[29,7770,7759],{},"：開發中，只編譯。",[11,7773,7774,7775,7777,7778,7781,7782,5240],{},"Webpack 4 預設是 ",[29,7776,7756],{},"。如果沒有設定 ",[29,7779,7780],{},"mode","，執行 watch 時也會壓縮與最佳化，比較消耗資源，因此在 npm script 加上 ",[29,7783,7759],{},[22,7785,7787],{"className":5243,"code":7786,"language":5245,"meta":27,"style":27},"{\n  \"name\": \"Babel\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"webpack.config.js\",\n  \"scripts\": {\n    \"watch\": \"webpack --mode development --watch\",\n    \"start\": \"webpack --mode development\",\n    \"deploy\": \"webpack --mode production\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"devDependencies\": {\n    \"@babel\u002Fcore\": \"^7.0.0\",\n    \"@babel\u002Fpreset-env\": \"^7.0.0\",\n    \"babel-loader\": \"^8.0.2\",\n    \"webpack\": \"^4.21.0\",\n    \"webpack-cli\": \"^3.1.2\"\n  }\n}\n",[29,7788,7789,7793,7804,7814,7824,7834,7840,7851,7863,7873,7877,7887,7897,7903,7915,7926,7938,7949,7958,7962],{"__ignoreMap":27},[32,7790,7791],{"class":34,"line":35},[32,7792,5252],{"class":41},[32,7794,7795,7797,7799,7802],{"class":34,"line":45},[32,7796,7239],{"class":5257},[32,7798,5269],{"class":41},[32,7800,7801],{"class":175},"\"Babel\"",[32,7803,547],{"class":41},[32,7805,7806,7808,7810,7812],{"class":34,"line":51},[32,7807,7251],{"class":5257},[32,7809,5269],{"class":41},[32,7811,7256],{"class":175},[32,7813,547],{"class":41},[32,7815,7816,7818,7820,7822],{"class":34,"line":90},[32,7817,7263],{"class":5257},[32,7819,5269],{"class":41},[32,7821,7268],{"class":175},[32,7823,547],{"class":41},[32,7825,7826,7828,7830,7832],{"class":34,"line":99},[32,7827,7275],{"class":5257},[32,7829,5269],{"class":41},[32,7831,7643],{"class":175},[32,7833,547],{"class":41},[32,7835,7836,7838],{"class":34,"line":106},[32,7837,5258],{"class":5257},[32,7839,5261],{"class":41},[32,7841,7842,7844,7846,7849],{"class":34,"line":114},[32,7843,7656],{"class":5257},[32,7845,5269],{"class":41},[32,7847,7848],{"class":175},"\"webpack --mode development --watch\"",[32,7850,547],{"class":41},[32,7852,7853,7856,7858,7861],{"class":34,"line":120},[32,7854,7855],{"class":5257},"    \"start\"",[32,7857,5269],{"class":41},[32,7859,7860],{"class":175},"\"webpack --mode development\"",[32,7862,547],{"class":41},[32,7864,7865,7868,7870],{"class":34,"line":127},[32,7866,7867],{"class":5257},"    \"deploy\"",[32,7869,5269],{"class":41},[32,7871,7872],{"class":175},"\"webpack --mode production\"\n",[32,7874,7875],{"class":34,"line":132},[32,7876,5639],{"class":41},[32,7878,7879,7881,7883,7885],{"class":34,"line":141},[32,7880,7330],{"class":5257},[32,7882,5269],{"class":41},[32,7884,7268],{"class":175},[32,7886,547],{"class":41},[32,7888,7889,7891,7893,7895],{"class":34,"line":147},[32,7890,7341],{"class":5257},[32,7892,5269],{"class":41},[32,7894,7346],{"class":175},[32,7896,547],{"class":41},[32,7898,7899,7901],{"class":34,"line":275},[32,7900,7353],{"class":5257},[32,7902,5261],{"class":41},[32,7904,7905,7908,7910,7913],{"class":34,"line":282},[32,7906,7907],{"class":5257},"    \"@babel\u002Fcore\"",[32,7909,5269],{"class":41},[32,7911,7912],{"class":175},"\"^7.0.0\"",[32,7914,547],{"class":41},[32,7916,7917,7920,7922,7924],{"class":34,"line":295},[32,7918,7919],{"class":5257},"    \"@babel\u002Fpreset-env\"",[32,7921,5269],{"class":41},[32,7923,7912],{"class":175},[32,7925,547],{"class":41},[32,7927,7928,7931,7933,7936],{"class":34,"line":605},[32,7929,7930],{"class":5257},"    \"babel-loader\"",[32,7932,5269],{"class":41},[32,7934,7935],{"class":175},"\"^8.0.2\"",[32,7937,547],{"class":41},[32,7939,7940,7942,7944,7947],{"class":34,"line":612},[32,7941,7360],{"class":5257},[32,7943,5269],{"class":41},[32,7945,7946],{"class":175},"\"^4.21.0\"",[32,7948,547],{"class":41},[32,7950,7951,7953,7955],{"class":34,"line":617},[32,7952,7372],{"class":5257},[32,7954,5269],{"class":41},[32,7956,7957],{"class":175},"\"^3.1.2\"\n",[32,7959,7960],{"class":34,"line":644},[32,7961,1203],{"class":41},[32,7963,7964],{"class":34,"line":649},[32,7965,54],{"class":41},[4538,7967,7968,7973,7979],{},[4541,7969,7970,7972],{},[29,7971,7593],{},"：持續編譯不中斷，使用 development 版本。",[4541,7974,7975,7978],{},[29,7976,7977],{},"start","：執行一次編譯，使用 development 版本。",[4541,7980,7981,7984],{},[29,7982,7983],{},"deploy","：執行一次編譯，使用 production 版本。",[11,7986,7987],{},"Development 的程式碼不會被壓縮。",[15,7989,7991],{"id":7990},"javascript-與-babel","JavaScript 與 Babel",[11,7993,7994],{},"安裝 Babel：",[22,7996,7998],{"className":2057,"code":7997,"language":2059,"meta":27,"style":27},"npm install babel-loader @babel\u002Fcore @babel\u002Fpreset-env --save-dev\n",[29,7999,8000],{"__ignoreMap":27},[32,8001,8002,8004,8006,8009,8012,8015],{"class":34,"line":35},[32,8003,4625],{"class":2066},[32,8005,4650],{"class":175},[32,8007,8008],{"class":175}," babel-loader",[32,8010,8011],{"class":175}," @babel\u002Fcore",[32,8013,8014],{"class":175}," @babel\u002Fpreset-env",[32,8016,7066],{"class":351},[6426,8018,8019,8025,8031],{},[4541,8020,8021,8024],{},[29,8022,8023],{},"babel-loader","：讓 Webpack 判讀並轉換檔案。",[4541,8026,8027,8030],{},[29,8028,8029],{},"@babel\u002Fcore","：提供 Babel 編譯用的 API。",[4541,8032,8033,8036],{},[29,8034,8035],{},"@babel\u002Fpreset-env","：使用新版 JavaScript 語法時，不必逐一設定需要轉換的語法。",[11,8038,8039,5240],{},[29,8040,7089],{},[22,8042,8044],{"className":24,"code":8043,"language":26,"meta":27,"style":27},"var path = require('path');\n\nmodule.exports = {\n  context: path.resolve(__dirname, 'js'),\n  entry: {\n    index: '.\u002Findex.js',\n  },\n  output: {\n    path: path.resolve(__dirname, 'js'),\n    filename: '.\u002Findex.bundle.js',\n  },\n  module: {\n    rules: [\n      {\n        test: \u002F\\.(js)$\u002F,\n        exclude: \u002F(node_modules)\u002F,\n        use: {\n          loader: 'babel-loader',\n          options: {\n            presets: ['@babel\u002Fpreset-env'],\n          },\n        },\n      },\n    ],\n  },\n};\n",[29,8045,8046,8062,8066,8078,8091,8096,8105,8109,8113,8125,8134,8138,8143,8148,8153,8172,8182,8187,8197,8202,8213,8218,8223,8227,8232,8236],{"__ignoreMap":27},[32,8047,8048,8050,8052,8054,8056,8058,8060],{"class":34,"line":35},[32,8049,166],{"class":38},[32,8051,7431],{"class":41},[32,8053,172],{"class":38},[32,8055,7114],{"class":217},[32,8057,477],{"class":41},[32,8059,7119],{"class":175},[32,8061,230],{"class":41},[32,8063,8064],{"class":34,"line":45},[32,8065,103],{"emptyLinePlaceholder":102},[32,8067,8068,8070,8072,8074,8076],{"class":34,"line":51},[32,8069,7130],{"class":351},[32,8071,1278],{"class":41},[32,8073,7135],{"class":351},[32,8075,894],{"class":38},[32,8077,703],{"class":41},[32,8079,8080,8082,8084,8086,8089],{"class":34,"line":90},[32,8081,7530],{"class":41},[32,8083,7172],{"class":217},[32,8085,7175],{"class":41},[32,8087,8088],{"class":175},"'js'",[32,8090,5101],{"class":41},[32,8092,8093],{"class":34,"line":99},[32,8094,8095],{"class":41},"  entry: {\n",[32,8097,8098,8101,8103],{"class":34,"line":106},[32,8099,8100],{"class":41},"    index: ",[32,8102,7147],{"class":175},[32,8104,547],{"class":41},[32,8106,8107],{"class":34,"line":114},[32,8108,5639],{"class":41},[32,8110,8111],{"class":34,"line":120},[32,8112,7154],{"class":41},[32,8114,8115,8117,8119,8121,8123],{"class":34,"line":127},[32,8116,7169],{"class":41},[32,8118,7172],{"class":217},[32,8120,7175],{"class":41},[32,8122,8088],{"class":175},[32,8124,5101],{"class":41},[32,8126,8127,8129,8132],{"class":34,"line":132},[32,8128,7159],{"class":41},[32,8130,8131],{"class":175},"'.\u002Findex.bundle.js'",[32,8133,547],{"class":41},[32,8135,8136],{"class":34,"line":141},[32,8137,5639],{"class":41},[32,8139,8140],{"class":34,"line":147},[32,8141,8142],{"class":41},"  module: {\n",[32,8144,8145],{"class":34,"line":275},[32,8146,8147],{"class":41},"    rules: [\n",[32,8149,8150],{"class":34,"line":282},[32,8151,8152],{"class":41},"      {\n",[32,8154,8155,8158,8161,8163,8166,8168,8170],{"class":34,"line":295},[32,8156,8157],{"class":41},"        test:",[32,8159,8160],{"class":175}," \u002F",[32,8162,1272],{"class":1265},[32,8164,8165],{"class":175},"(js)",[32,8167,755],{"class":38},[32,8169,1275],{"class":175},[32,8171,547],{"class":41},[32,8173,8174,8177,8180],{"class":34,"line":605},[32,8175,8176],{"class":41},"        exclude:",[32,8178,8179],{"class":175}," \u002F(node_modules)\u002F",[32,8181,547],{"class":41},[32,8183,8184],{"class":34,"line":612},[32,8185,8186],{"class":41},"        use: {\n",[32,8188,8189,8192,8195],{"class":34,"line":617},[32,8190,8191],{"class":41},"          loader: ",[32,8193,8194],{"class":175},"'babel-loader'",[32,8196,547],{"class":41},[32,8198,8199],{"class":34,"line":644},[32,8200,8201],{"class":41},"          options: {\n",[32,8203,8204,8207,8210],{"class":34,"line":649},[32,8205,8206],{"class":41},"            presets: [",[32,8208,8209],{"class":175},"'@babel\u002Fpreset-env'",[32,8211,8212],{"class":41},"],\n",[32,8214,8215],{"class":34,"line":671},[32,8216,8217],{"class":41},"          },\n",[32,8219,8220],{"class":34,"line":690},[32,8221,8222],{"class":41},"        },\n",[32,8224,8225],{"class":34,"line":696},[32,8226,4500],{"class":41},[32,8228,8229],{"class":34,"line":706},[32,8230,8231],{"class":41},"    ],\n",[32,8233,8234],{"class":34,"line":721},[32,8235,5639],{"class":41},[32,8237,8238],{"class":34,"line":728},[32,8239,7189],{"class":41},[11,8241,5134,8242,8244],{},[29,8243,7093],{}," 使用 ES6 語法測試：",[22,8246,8248],{"className":24,"code":8247,"language":26,"meta":27,"style":27},"const Name = ['Mike', 'Jacky', 'Andy', 'Scars'];\nName.forEach((obj, idx) => console.log(`${idx} => ${obj}`));\n",[29,8249,8250,8283],{"__ignoreMap":27},[32,8251,8252,8254,8257,8259,8262,8265,8267,8270,8272,8275,8277,8280],{"class":34,"line":35},[32,8253,6617],{"class":38},[32,8255,8256],{"class":351}," Name",[32,8258,894],{"class":38},[32,8260,8261],{"class":41}," [",[32,8263,8264],{"class":175},"'Mike'",[32,8266,635],{"class":41},[32,8268,8269],{"class":175},"'Jacky'",[32,8271,635],{"class":41},[32,8273,8274],{"class":175},"'Andy'",[32,8276,635],{"class":41},[32,8278,8279],{"class":175},"'Scars'",[32,8281,8282],{"class":41},"];\n",[32,8284,8285,8288,8291,8294,8297,8299,8302,8304,8306,8309,8311,8313,8316,8318,8321,8323,8326],{"class":34,"line":45},[32,8286,8287],{"class":41},"Name.",[32,8289,8290],{"class":217},"forEach",[32,8292,8293],{"class":41},"((",[32,8295,8296],{"class":903},"obj",[32,8298,635],{"class":41},[32,8300,8301],{"class":903},"idx",[32,8303,483],{"class":41},[32,8305,5090],{"class":38},[32,8307,8308],{"class":41}," console.",[32,8310,5197],{"class":217},[32,8312,477],{"class":41},[32,8314,8315],{"class":175},"`${",[32,8317,8301],{"class":41},[32,8319,8320],{"class":175},"} => ${",[32,8322,8296],{"class":41},[32,8324,8325],{"class":175},"}`",[32,8327,8328],{"class":41},"));\n",[11,8330,8331],{},"執行：",[22,8333,8334],{"className":2057,"code":7735,"language":2059,"meta":27,"style":27},[29,8335,8336],{"__ignoreMap":27},[32,8337,8338,8340,8342],{"class":34,"line":35},[32,8339,4625],{"class":2066},[32,8341,4743],{"class":175},[32,8343,7746],{"class":175},[11,8345,8346,8347,8349,8350,8352,8353,8355],{},"編譯結果會把 ",[29,8348,6617],{}," 轉成 ",[29,8351,166],{},"、箭頭函式轉成 ",[29,8354,665],{},"，字串模板也會組回一般字串，代表程式已編譯成 ES5。",[11,8357,8358,8359,8362],{},"正式上專案前要執行 ",[29,8360,8361],{},"npm run deploy","，讓 production 編譯壓縮及最佳化程式碼。",[11,8364,8365],{},"Babel 不只轉換新版 JavaScript，也可以轉換 React 的 JSX 與 TypeScript 等語法。",[15,8367,4536],{"id":4536},[4538,8369,8370,8377,8384],{},[4541,8371,8372],{},[412,8373,8376],{"href":8374,"rel":8375},"https:\u002F\u002Fwww.cnblogs.com\u002Fzaking\u002Fp\u002F8586458.html",[416],"Webpack 配置參考",[4541,8378,8379],{},[412,8380,8383],{"href":8381,"rel":8382},"https:\u002F\u002Ffrontenddev.org\u002Farticle\u002Fcoolie-pk-battle-with-webpack-1-js-file-consolidation-and-compression.html",[416],"JavaScript 檔案合併與壓縮",[4541,8385,8386],{},[412,8387,8390],{"href":8388,"rel":8389},"https:\u002F\u002Fmedium.com\u002F@Mike_Cheng1208\u002F%E4%BB%80%E9%BA%BC%E6%98%AFwebpack-%E4%BD%A0%E9%9C%80%E8%A6%81webpack%E5%97%8E-2d8f9658241d",[416],"什麼是 webpack？你需要 webpack 嗎",[368,8392,8393],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}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 .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .swgif, html code.shiki .swgif{--shiki-default:#005CC5;--shiki-dark:#7EE787}html pre.shiki code .sw7DY, html code.shiki .sw7DY{--shiki-default:#005CC5;--shiki-dark:#FF7B72}html pre.shiki code .sh9l7, html code.shiki .sh9l7{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#7EE787;--shiki-dark-font-weight:bold}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}",{"title":27,"searchDepth":51,"depth":51,"links":8395},[8396,8397,8398,8399,8400,8401],{"id":7042,"depth":45,"text":7043},{"id":7403,"depth":45,"text":7403},{"id":7585,"depth":45,"text":7586},{"id":7749,"depth":45,"text":7750},{"id":7990,"depth":45,"text":7991},{"id":4536,"depth":45,"text":4536},"2018 年整理的 Webpack 4 基本配置、路徑、watch、mode 與 Babel 設定。",{},"\u002Fposts\u002Fwebpack4-config-notes","2018-10-24",[],{"title":7037,"description":8402},"posts\u002F20181024.webpack4-config-notes",[8410,4850,390],"Webpack","uL2rRwQqtYziOo7WpITS_ymdjiSa8cXB_x7hz13upWk",{"id":8413,"title":8414,"body":8415,"category":378,"description":10509,"draft":380,"extension":381,"image":382,"meta":10510,"navigation":102,"path":10511,"publishedAt":10512,"relatedExperiments":10513,"seo":10514,"stem":10515,"tags":10516,"updatedAt":392,"__hash__":10518},"posts\u002Fposts\u002F20181029.vue2-project-kit.md","Vue CLI、Bootstrap 與常用套件安裝筆記",{"type":8,"value":8416,"toc":10488},[8417,8420,8422,8424,8437,8471,8491,8495,8498,8515,8521,8524,8535,8538,8567,8570,8598,8600,8612,8616,8660,8663,8730,8733,8750,8753,8780,8785,8809,8813,8832,8836,8899,8905,9019,9023,9039,9043,9052,9106,9109,9142,9145,9148,9165,9169,9234,9239,9347,9354,9357,9363,9366,9533,9536,9546,9549,9565,9612,9776,9780,9794,9798,9830,9833,9918,9924,9927,10021,10024,10030,10069,10072,10183,10186,10193,10209,10213,10245,10248,10280,10284,10290,10295,10371,10376,10398,10405,10409,10415,10418,10439,10442,10485],[11,8418,8419],{},"這邊記錄手動安裝vuecli，\n第三方套件要往上放，先安裝 Node.js。",[15,8421,6249],{"id":6248},[11,8423,6252],{},[4538,8425,8426,8431,8435],{},[4541,8427,6257,8428],{},[412,8429,6262],{"href":6260,"rel":8430},[416],[4541,8432,6265,8433],{},[29,8434,6268],{},[4541,8436,6271],{},[22,8438,8439],{"className":2057,"code":6274,"language":2059,"meta":27,"style":27},[29,8440,8441,8447,8455,8463],{"__ignoreMap":27},[32,8442,8443,8445],{"class":34,"line":35},[32,8444,6281],{"class":2066},[32,8446,6284],{"class":175},[32,8448,8449,8451,8453],{"class":34,"line":45},[32,8450,6281],{"class":2066},[32,8452,6291],{"class":175},[32,8454,6294],{"class":175},[32,8456,8457,8459,8461],{"class":34,"line":51},[32,8458,6281],{"class":2066},[32,8460,4650],{"class":175},[32,8462,6303],{"class":175},[32,8464,8465,8467,8469],{"class":34,"line":90},[32,8466,6281],{"class":2066},[32,8468,6310],{"class":175},[32,8470,6303],{"class":175},[4538,8472,8473,8477,8481,8485,8489],{},[4541,8474,8475,6320],{},[29,8476,6319],{},[4541,8478,8479,6326],{},[29,8480,6325],{},[4541,8482,8483,6332],{},[29,8484,6331],{},[4541,8486,8487,6338],{},[29,8488,6337],{},[4541,8490,6341],{},[15,8492,8494],{"id":8493},"安裝-vue-cli","安裝 Vue CLI",[11,8496,8497],{},"全域安裝一次：",[22,8499,8501],{"className":2057,"code":8500,"language":2059,"meta":27,"style":27},"npm i -g vue-cli\n",[29,8502,8503],{"__ignoreMap":27},[32,8504,8505,8507,8510,8512],{"class":34,"line":35},[32,8506,4625],{"class":2066},[32,8508,8509],{"class":175}," i",[32,8511,4653],{"class":351},[32,8513,8514],{"class":175}," vue-cli\n",[11,8516,8517,8520],{},[29,8518,8519],{},"-g"," 代表全域安裝。",[11,8522,8523],{},"查看模板清單：",[22,8525,8527],{"className":2057,"code":8526,"language":2059,"meta":27,"style":27},"vue list\n",[29,8528,8529],{"__ignoreMap":27},[32,8530,8531,8533],{"class":34,"line":35},[32,8532,4112],{"class":2066},[32,8534,6284],{"class":175},[11,8536,8537],{},"空資料夾可以選擇下列模板：",[4538,8539,8540,8545,8550,8555,8561],{},[4541,8541,8542],{},[29,8543,8544],{},"browserify",[4541,8546,8547],{},[29,8548,8549],{},"browserify-simple",[4541,8551,8552],{},[29,8553,8554],{},"pwa",[4541,8556,8557,8560],{},[29,8558,8559],{},"webpack","：主要模板",[4541,8562,8563,8566],{},[29,8564,8565],{},"webpack-simple","：基本架構",[11,8568,8569],{},"如果要使用 SCSS，設定過程要選擇 Yes。",[22,8571,8573],{"className":2057,"code":8572,"language":2059,"meta":27,"style":27},"vue init webpack 資料夾名稱\nvue init webpack-simple 資料夾名稱\n",[29,8574,8575,8587],{"__ignoreMap":27},[32,8576,8577,8579,8582,8584],{"class":34,"line":35},[32,8578,4112],{"class":2066},[32,8580,8581],{"class":175}," init",[32,8583,7060],{"class":175},[32,8585,8586],{"class":175}," 資料夾名稱\n",[32,8588,8589,8591,8593,8596],{"class":34,"line":45},[32,8590,4112],{"class":2066},[32,8592,8581],{"class":175},[32,8594,8595],{"class":175}," webpack-simple",[32,8597,8586],{"class":175},[11,8599,6344],{},[22,8601,8603],{"className":2057,"code":8602,"language":2059,"meta":27,"style":27},"vue -V\n",[29,8604,8605],{"__ignoreMap":27},[32,8606,8607,8609],{"class":34,"line":35},[32,8608,4112],{"class":2066},[32,8610,8611],{"class":351}," -V\n",[15,8613,8615],{"id":8614},"安裝-sass-與-bootstrap","安裝 Sass 與 Bootstrap",[22,8617,8619],{"className":2057,"code":8618,"language":2059,"meta":27,"style":27},"npm install bootstrap node-sass sass-loader --save\nnpm install --save jquery\nnpm install popper.js\n",[29,8620,8621,8639,8651],{"__ignoreMap":27},[32,8622,8623,8625,8627,8630,8633,8636],{"class":34,"line":35},[32,8624,4625],{"class":2066},[32,8626,4650],{"class":175},[32,8628,8629],{"class":175}," bootstrap",[32,8631,8632],{"class":175}," node-sass",[32,8634,8635],{"class":175}," sass-loader",[32,8637,8638],{"class":351}," --save\n",[32,8640,8641,8643,8645,8648],{"class":34,"line":45},[32,8642,4625],{"class":2066},[32,8644,4650],{"class":175},[32,8646,8647],{"class":351}," --save",[32,8649,8650],{"class":175}," jquery\n",[32,8652,8653,8655,8657],{"class":34,"line":51},[32,8654,4625],{"class":2066},[32,8656,4650],{"class":175},[32,8658,8659],{"class":175}," popper.js\n",[11,8661,8662],{},"測試 SCSS：",[22,8664,8667],{"className":8665,"code":8666,"language":4112,"meta":27,"style":27},"language-vue shiki shiki-themes github-light github-dark-default","\u003Cstyle lang=\"scss\">\n$color: #000;\n\nbody {\n  background-color: $color;\n}\n\u003C\u002Fstyle>\n",[29,8668,8669,8685,8697,8701,8707,8718,8722],{"__ignoreMap":27},[32,8670,8671,8673,8675,8678,8680,8683],{"class":34,"line":35},[32,8672,4046],{"class":41},[32,8674,368],{"class":4049},[32,8676,8677],{"class":4053}," lang",[32,8679,172],{"class":41},[32,8681,8682],{"class":175},"\"scss\"",[32,8684,4062],{"class":41},[32,8686,8687,8690,8692,8695],{"class":34,"line":45},[32,8688,8689],{"class":903},"$color",[32,8691,5269],{"class":5049},[32,8693,8694],{"class":351},"#000",[32,8696,96],{"class":41},[32,8698,8699],{"class":34,"line":51},[32,8700,103],{"emptyLinePlaceholder":102},[32,8702,8703,8705],{"class":34,"line":90},[32,8704,5495],{"class":4049},[32,8706,703],{"class":41},[32,8708,8709,8712,8714,8716],{"class":34,"line":99},[32,8710,8711],{"class":351},"  background-color",[32,8713,5269],{"class":41},[32,8715,8689],{"class":903},[32,8717,96],{"class":41},[32,8719,8720],{"class":34,"line":106},[32,8721,54],{"class":41},[32,8723,8724,8726,8728],{"class":34,"line":114},[32,8725,4105],{"class":41},[32,8727,368],{"class":4049},[32,8729,4062],{"class":41},[11,8731,8732],{},"引入 Bootstrap：",[22,8734,8738],{"className":8735,"code":8736,"language":8737,"meta":27,"style":27},"language-scss shiki shiki-themes github-light github-dark-default","@import '~bootstrap\u002Fscss\u002Fbootstrap';\n","scss",[29,8739,8740],{"__ignoreMap":27},[32,8741,8742,8745,8748],{"class":34,"line":35},[32,8743,8744],{"class":38},"@import",[32,8746,8747],{"class":175}," '~bootstrap\u002Fscss\u002Fbootstrap'",[32,8749,96],{"class":41},[11,8751,8752],{},"如果要指定當時使用的 Bootstrap 4 版本：",[22,8754,8756],{"className":2057,"code":8755,"language":2059,"meta":27,"style":27},"npm install --save jquery popper.js\nnpm install bootstrap@4.0.0-beta.3\n",[29,8757,8758,8771],{"__ignoreMap":27},[32,8759,8760,8762,8764,8766,8769],{"class":34,"line":35},[32,8761,4625],{"class":2066},[32,8763,4650],{"class":175},[32,8765,8647],{"class":351},[32,8767,8768],{"class":175}," jquery",[32,8770,8659],{"class":175},[32,8772,8773,8775,8777],{"class":34,"line":45},[32,8774,4625],{"class":2066},[32,8776,4650],{"class":175},[32,8778,8779],{"class":175}," bootstrap@4.0.0-beta.3\n",[11,8781,5134,8782,8784],{},[29,8783,5137],{}," 引入 Bootstrap 的 SCSS 與 JavaScript 模組：",[22,8786,8788],{"className":24,"code":8787,"language":26,"meta":27,"style":27},"import 'bootstrap\u002Fscss\u002Fbootstrap.scss';\nimport 'bootstrap';\n",[29,8789,8790,8800],{"__ignoreMap":27},[32,8791,8792,8795,8798],{"class":34,"line":35},[32,8793,8794],{"class":38},"import",[32,8796,8797],{"class":175}," 'bootstrap\u002Fscss\u002Fbootstrap.scss'",[32,8799,96],{"class":41},[32,8801,8802,8804,8807],{"class":34,"line":45},[32,8803,8794],{"class":38},[32,8805,8806],{"class":175}," 'bootstrap'",[32,8808,96],{"class":41},[15,8810,8812],{"id":8811},"axios-與-vue-axios","axios 與 vue-axios",[22,8814,8816],{"className":2057,"code":8815,"language":2059,"meta":27,"style":27},"npm install --save axios vue-axios\n",[29,8817,8818],{"__ignoreMap":27},[32,8819,8820,8822,8824,8826,8829],{"class":34,"line":35},[32,8821,4625],{"class":2066},[32,8823,4650],{"class":175},[32,8825,8647],{"class":351},[32,8827,8828],{"class":175}," axios",[32,8830,8831],{"class":175}," vue-axios\n",[11,8833,5134,8834,7419],{},[29,8835,5137],{},[22,8837,8839],{"className":24,"code":8838,"language":26,"meta":27,"style":27},"import Vue from 'vue';\nimport axios from 'axios';\nimport VueAxios from 'vue-axios';\n\nVue.use(VueAxios, axios);\n",[29,8840,8841,8856,8870,8884,8888],{"__ignoreMap":27},[32,8842,8843,8845,8848,8851,8854],{"class":34,"line":35},[32,8844,8794],{"class":38},[32,8846,8847],{"class":41}," Vue ",[32,8849,8850],{"class":38},"from",[32,8852,8853],{"class":175}," 'vue'",[32,8855,96],{"class":41},[32,8857,8858,8860,8863,8865,8868],{"class":34,"line":45},[32,8859,8794],{"class":38},[32,8861,8862],{"class":41}," axios ",[32,8864,8850],{"class":38},[32,8866,8867],{"class":175}," 'axios'",[32,8869,96],{"class":41},[32,8871,8872,8874,8877,8879,8882],{"class":34,"line":51},[32,8873,8794],{"class":38},[32,8875,8876],{"class":41}," VueAxios ",[32,8878,8850],{"class":38},[32,8880,8881],{"class":175}," 'vue-axios'",[32,8883,96],{"class":41},[32,8885,8886],{"class":34,"line":90},[32,8887,103],{"emptyLinePlaceholder":102},[32,8889,8890,8893,8896],{"class":34,"line":99},[32,8891,8892],{"class":41},"Vue.",[32,8894,8895],{"class":217},"use",[32,8897,8898],{"class":41},"(VueAxios, axios);\n",[11,8900,8901,8902,5240],{},"測試資料可使用 ",[29,8903,8904],{},"https:\u002F\u002Frandomuser.me\u002Fapi\u002F",[22,8906,8908],{"className":24,"code":8907,"language":26,"meta":27,"style":27},"created() {\n  const url = 'https:\u002F\u002Frandomuser.me\u002Fapi\u002F';\n\n  this.axios.get(url).then((response) => {\n    console.log(response.data);\n  });\n\n  this.$http.get(url).then((response) => {\n    console.log(response.data);\n  });\n}\n",[29,8909,8910,8916,8930,8934,8962,8972,8976,8980,9003,9011,9015],{"__ignoreMap":27},[32,8911,8912,8914],{"class":34,"line":35},[32,8913,5187],{"class":217},[32,8915,4221],{"class":41},[32,8917,8918,8920,8923,8925,8928],{"class":34,"line":45},[32,8919,4157],{"class":38},[32,8921,8922],{"class":351}," url",[32,8924,894],{"class":38},[32,8926,8927],{"class":175}," 'https:\u002F\u002Frandomuser.me\u002Fapi\u002F'",[32,8929,96],{"class":41},[32,8931,8932],{"class":34,"line":51},[32,8933,103],{"emptyLinePlaceholder":102},[32,8935,8936,8939,8942,8945,8948,8951,8953,8956,8958,8960],{"class":34,"line":90},[32,8937,8938],{"class":351},"  this",[32,8940,8941],{"class":41},".axios.",[32,8943,8944],{"class":217},"get",[32,8946,8947],{"class":41},"(url).",[32,8949,8950],{"class":217},"then",[32,8952,8293],{"class":41},[32,8954,8955],{"class":903},"response",[32,8957,483],{"class":41},[32,8959,5090],{"class":38},[32,8961,703],{"class":41},[32,8963,8964,8967,8969],{"class":34,"line":99},[32,8965,8966],{"class":41},"    console.",[32,8968,5197],{"class":217},[32,8970,8971],{"class":41},"(response.data);\n",[32,8973,8974],{"class":34,"line":106},[32,8975,693],{"class":41},[32,8977,8978],{"class":34,"line":114},[32,8979,103],{"emptyLinePlaceholder":102},[32,8981,8982,8984,8987,8989,8991,8993,8995,8997,8999,9001],{"class":34,"line":120},[32,8983,8938],{"class":351},[32,8985,8986],{"class":41},".$http.",[32,8988,8944],{"class":217},[32,8990,8947],{"class":41},[32,8992,8950],{"class":217},[32,8994,8293],{"class":41},[32,8996,8955],{"class":903},[32,8998,483],{"class":41},[32,9000,5090],{"class":38},[32,9002,703],{"class":41},[32,9004,9005,9007,9009],{"class":34,"line":127},[32,9006,8966],{"class":41},[32,9008,5197],{"class":217},[32,9010,8971],{"class":41},[32,9012,9013],{"class":34,"line":132},[32,9014,693],{"class":41},[32,9016,9017],{"class":34,"line":141},[32,9018,54],{"class":41},[151,9020,9022],{"id":9021},"相關連結參考","相關連結參考：",[4538,9024,9025,9032],{},[4541,9026,9027],{},[412,9028,9031],{"href":9029,"rel":9030},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fvue-axios",[416],"vue-axios",[4541,9033,9034,3775],{},[412,9035,9038],{"href":9036,"rel":9037},"https:\u002F\u002Fithelp.ithome.com.tw\u002Farticles\u002F10188510",[416],"iT 邦幫忙文章",[15,9040,9042],{"id":9041},"設定-api-路徑","設定 API 路徑",[11,9044,9045,9046,7090,9049,5240],{},"設定 ",[29,9047,9048],{},"config\u002Fdev.env.js",[29,9050,9051],{},"config\u002Fprod.env.js",[22,9053,9055],{"className":24,"code":9054,"language":26,"meta":27,"style":27},"module.exports = merge(prodEnv, {\n  NODE_ENV: '\"development\"',\n  APIPATH: '\"https:\u002F\u002Fxxx.xxx.xxx\u002F\"',\n  CUSTOMPATH: '\"adminUser\"',\n});\n",[29,9056,9057,9072,9082,9092,9102],{"__ignoreMap":27},[32,9058,9059,9061,9063,9065,9067,9069],{"class":34,"line":35},[32,9060,7130],{"class":351},[32,9062,1278],{"class":41},[32,9064,7135],{"class":351},[32,9066,894],{"class":38},[32,9068,3324],{"class":217},[32,9070,9071],{"class":41},"(prodEnv, {\n",[32,9073,9074,9077,9080],{"class":34,"line":45},[32,9075,9076],{"class":41},"  NODE_ENV: ",[32,9078,9079],{"class":175},"'\"development\"'",[32,9081,547],{"class":41},[32,9083,9084,9087,9090],{"class":34,"line":51},[32,9085,9086],{"class":41},"  APIPATH: ",[32,9088,9089],{"class":175},"'\"https:\u002F\u002Fxxx.xxx.xxx\u002F\"'",[32,9091,547],{"class":41},[32,9093,9094,9097,9100],{"class":34,"line":90},[32,9095,9096],{"class":41},"  CUSTOMPATH: ",[32,9098,9099],{"class":175},"'\"adminUser\"'",[32,9101,547],{"class":41},[32,9103,9104],{"class":34,"line":99},[32,9105,792],{"class":41},[11,9107,9108],{},"設定完成後要重啟 Vue CLI。在需要使用的頁面輸入：",[22,9110,9112],{"className":24,"code":9111,"language":26,"meta":27,"style":27},"const url = `${process.env.APIPATH}\u002Fadmin\u002Fsignin`;\n",[29,9113,9114],{"__ignoreMap":27},[32,9115,9116,9118,9120,9122,9125,9128,9130,9132,9134,9137,9140],{"class":34,"line":35},[32,9117,6617],{"class":38},[32,9119,8922],{"class":351},[32,9121,894],{"class":38},[32,9123,9124],{"class":175}," `${",[32,9126,9127],{"class":41},"process",[32,9129,1278],{"class":175},[32,9131,5162],{"class":41},[32,9133,1278],{"class":175},[32,9135,9136],{"class":351},"APIPATH",[32,9138,9139],{"class":175},"}\u002Fadmin\u002Fsignin`",[32,9141,96],{"class":41},[15,9143,4841],{"id":9144},"vue-router",[11,9146,9147],{},"如果沒有安裝 Vue Router：",[22,9149,9151],{"className":2057,"code":9150,"language":2059,"meta":27,"style":27},"npm i -s vue-router\n",[29,9152,9153],{"__ignoreMap":27},[32,9154,9155,9157,9159,9162],{"class":34,"line":35},[32,9156,4625],{"class":2066},[32,9158,8509],{"class":175},[32,9160,9161],{"class":351}," -s",[32,9163,9164],{"class":175}," vue-router\n",[11,9166,9167,5240],{},[29,9168,5137],{},[22,9170,9172],{"className":24,"code":9171,"language":26,"meta":27,"style":27},"import router from '.\u002Frouter';\n\nnew Vue({\n  el: '#app',\n  router,\n  components: { App },\n  template: '\u003CApp\u002F>',\n});\n",[29,9173,9174,9188,9192,9201,9210,9215,9220,9230],{"__ignoreMap":27},[32,9175,9176,9178,9181,9183,9186],{"class":34,"line":35},[32,9177,8794],{"class":38},[32,9179,9180],{"class":41}," router ",[32,9182,8850],{"class":38},[32,9184,9185],{"class":175}," '.\u002Frouter'",[32,9187,96],{"class":41},[32,9189,9190],{"class":34,"line":45},[32,9191,103],{"emptyLinePlaceholder":102},[32,9193,9194,9197,9199],{"class":34,"line":51},[32,9195,9196],{"class":38},"new",[32,9198,4168],{"class":217},[32,9200,1143],{"class":41},[32,9202,9203,9206,9208],{"class":34,"line":90},[32,9204,9205],{"class":41},"  el: ",[32,9207,4178],{"class":175},[32,9209,547],{"class":41},[32,9211,9212],{"class":34,"line":99},[32,9213,9214],{"class":41},"  router,\n",[32,9216,9217],{"class":34,"line":106},[32,9218,9219],{"class":41},"  components: { App },\n",[32,9221,9222,9225,9228],{"class":34,"line":114},[32,9223,9224],{"class":41},"  template: ",[32,9226,9227],{"class":175},"'\u003CApp\u002F>'",[32,9229,547],{"class":41},[32,9231,9232],{"class":34,"line":120},[32,9233,792],{"class":41},[11,9235,9236,5240],{},[29,9237,9238],{},"router\u002Findex.js",[22,9240,9242],{"className":24,"code":9241,"language":26,"meta":27,"style":27},"import Router from 'vue-router';\nimport login from '@\u002Fcomponents\u002Fpages\u002Flogin';\n\nVue.use(Router);\n\nexport default new Router({\n  routes: [\n    {\n      path: '\u002F',\n      name: 'xxx',\n      component: xxx,\n    },\n  ],\n});\n",[29,9243,9244,9258,9272,9276,9285,9289,9301,9305,9309,9317,9326,9331,9335,9341],{"__ignoreMap":27},[32,9245,9246,9248,9251,9253,9256],{"class":34,"line":35},[32,9247,8794],{"class":38},[32,9249,9250],{"class":41}," Router ",[32,9252,8850],{"class":38},[32,9254,9255],{"class":175}," 'vue-router'",[32,9257,96],{"class":41},[32,9259,9260,9262,9265,9267,9270],{"class":34,"line":45},[32,9261,8794],{"class":38},[32,9263,9264],{"class":41}," login ",[32,9266,8850],{"class":38},[32,9268,9269],{"class":175}," '@\u002Fcomponents\u002Fpages\u002Flogin'",[32,9271,96],{"class":41},[32,9273,9274],{"class":34,"line":51},[32,9275,103],{"emptyLinePlaceholder":102},[32,9277,9278,9280,9282],{"class":34,"line":90},[32,9279,8892],{"class":41},[32,9281,8895],{"class":217},[32,9283,9284],{"class":41},"(Router);\n",[32,9286,9287],{"class":34,"line":99},[32,9288,103],{"emptyLinePlaceholder":102},[32,9290,9291,9293,9295,9297,9299],{"class":34,"line":106},[32,9292,5038],{"class":38},[32,9294,5041],{"class":38},[32,9296,4165],{"class":38},[32,9298,5046],{"class":217},[32,9300,1143],{"class":5049},[32,9302,9303],{"class":34,"line":114},[32,9304,5054],{"class":41},[32,9306,9307],{"class":34,"line":120},[32,9308,5059],{"class":41},[32,9310,9311,9313,9315],{"class":34,"line":127},[32,9312,5064],{"class":41},[32,9314,5067],{"class":175},[32,9316,547],{"class":41},[32,9318,9319,9321,9324],{"class":34,"line":132},[32,9320,5074],{"class":41},[32,9322,9323],{"class":175},"'xxx'",[32,9325,547],{"class":41},[32,9327,9328],{"class":34,"line":141},[32,9329,9330],{"class":41},"      component: xxx,\n",[32,9332,9333],{"class":34,"line":147},[32,9334,4204],{"class":41},[32,9336,9337,9339],{"class":34,"line":275},[32,9338,5110],{"class":41},[32,9340,547],{"class":5049},[32,9342,9343,9345],{"class":34,"line":282},[32,9344,5117],{"class":5049},[32,9346,96],{"class":41},[11,9348,9349,9350,9353],{},"元件使用 ",[29,9351,9352],{},"\u003Crouter-view\u002F>"," 顯示路由內容。",[151,9355,9356],{"id":9356},"導航守衛",[11,9358,9359,9360,3775],{},"導航守衛用來驗證使用者是否需要登入，在路由加入 ",[29,9361,9362],{},"meta: { requiresAuth: true }",[11,9364,9365],{},"檢查權限：",[22,9367,9369],{"className":24,"code":9368,"language":26,"meta":27,"style":27},"router.beforeEach((to, from, next) => {\n  if (to.meta.requiresAuth) {\n    const api = `${process.env.APIPATH}\u002Fapi\u002Fuser\u002Fcheck`;\n\n    axios.post(api).then((response) => {\n      if (response.data.success) {\n        next();\n      } else {\n        next({\n          path: '\u002Flogin',\n        });\n      }\n    });\n  } else {\n    next();\n  }\n});\n",[29,9370,9371,9398,9405,9432,9436,9459,9466,9473,9482,9488,9498,9502,9506,9510,9518,9525,9529],{"__ignoreMap":27},[32,9372,9373,9376,9379,9381,9383,9385,9387,9389,9392,9394,9396],{"class":34,"line":35},[32,9374,9375],{"class":41},"router.",[32,9377,9378],{"class":217},"beforeEach",[32,9380,8293],{"class":41},[32,9382,6416],{"class":903},[32,9384,635],{"class":41},[32,9386,8850],{"class":903},[32,9388,635],{"class":41},[32,9390,9391],{"class":903},"next",[32,9393,483],{"class":41},[32,9395,5090],{"class":38},[32,9397,703],{"class":41},[32,9399,9400,9402],{"class":34,"line":45},[32,9401,1021],{"class":38},[32,9403,9404],{"class":41}," (to.meta.requiresAuth) {\n",[32,9406,9407,9410,9413,9415,9417,9419,9421,9423,9425,9427,9430],{"class":34,"line":51},[32,9408,9409],{"class":38},"    const",[32,9411,9412],{"class":351}," api",[32,9414,894],{"class":38},[32,9416,9124],{"class":175},[32,9418,9127],{"class":41},[32,9420,1278],{"class":175},[32,9422,5162],{"class":41},[32,9424,1278],{"class":175},[32,9426,9136],{"class":351},[32,9428,9429],{"class":175},"}\u002Fapi\u002Fuser\u002Fcheck`",[32,9431,96],{"class":41},[32,9433,9434],{"class":34,"line":90},[32,9435,103],{"emptyLinePlaceholder":102},[32,9437,9438,9441,9444,9447,9449,9451,9453,9455,9457],{"class":34,"line":99},[32,9439,9440],{"class":41},"    axios.",[32,9442,9443],{"class":217},"post",[32,9445,9446],{"class":41},"(api).",[32,9448,8950],{"class":217},[32,9450,8293],{"class":41},[32,9452,8955],{"class":903},[32,9454,483],{"class":41},[32,9456,5090],{"class":38},[32,9458,703],{"class":41},[32,9460,9461,9463],{"class":34,"line":106},[32,9462,1071],{"class":38},[32,9464,9465],{"class":41}," (response.data.success) {\n",[32,9467,9468,9471],{"class":34,"line":114},[32,9469,9470],{"class":217},"        next",[32,9472,687],{"class":41},[32,9474,9475,9478,9480],{"class":34,"line":120},[32,9476,9477],{"class":41},"      } ",[32,9479,565],{"class":38},[32,9481,703],{"class":41},[32,9483,9484,9486],{"class":34,"line":127},[32,9485,9470],{"class":217},[32,9487,1143],{"class":41},[32,9489,9490,9493,9496],{"class":34,"line":132},[32,9491,9492],{"class":41},"          path: ",[32,9494,9495],{"class":175},"'\u002Flogin'",[32,9497,547],{"class":41},[32,9499,9500],{"class":34,"line":141},[32,9501,1188],{"class":41},[32,9503,9504],{"class":34,"line":147},[32,9505,1193],{"class":41},[32,9507,9508],{"class":34,"line":275},[32,9509,1198],{"class":41},[32,9511,9512,9514,9516],{"class":34,"line":282},[32,9513,5911],{"class":41},[32,9515,565],{"class":38},[32,9517,703],{"class":41},[32,9519,9520,9523],{"class":34,"line":295},[32,9521,9522],{"class":217},"    next",[32,9524,687],{"class":41},[32,9526,9527],{"class":34,"line":605},[32,9528,1203],{"class":41},[32,9530,9531],{"class":34,"line":612},[32,9532,792],{"class":41},[11,9534,9535],{},"當時的筆記是：頁面第一次無法觸發時，必須點選下一頁才會執行。",[11,9537,9538,9541,9542,9545],{},[29,9539,9540],{},"axios"," 可以直接使用；",[29,9543,9544],{},"this.$http"," 要在 Vue 元件內使用。",[15,9547,9548],{"id":9548},"vue-loading-overlay",[22,9550,9552],{"className":2057,"code":9551,"language":2059,"meta":27,"style":27},"npm install vue-loading-overlay --save\n",[29,9553,9554],{"__ignoreMap":27},[32,9555,9556,9558,9560,9563],{"class":34,"line":35},[32,9557,4625],{"class":2066},[32,9559,4650],{"class":175},[32,9561,9562],{"class":175}," vue-loading-overlay",[32,9564,8638],{"class":351},[22,9566,9568],{"className":24,"code":9567,"language":26,"meta":27,"style":27},"import Loading from 'vue-loading-overlay';\nimport 'vue-loading-overlay\u002Fdist\u002Fvue-loading.min.css';\n\nVue.component('Loading', Loading);\n",[29,9569,9570,9584,9593,9597],{"__ignoreMap":27},[32,9571,9572,9574,9577,9579,9582],{"class":34,"line":35},[32,9573,8794],{"class":38},[32,9575,9576],{"class":41}," Loading ",[32,9578,8850],{"class":38},[32,9580,9581],{"class":175}," 'vue-loading-overlay'",[32,9583,96],{"class":41},[32,9585,9586,9588,9591],{"class":34,"line":45},[32,9587,8794],{"class":38},[32,9589,9590],{"class":175}," 'vue-loading-overlay\u002Fdist\u002Fvue-loading.min.css'",[32,9592,96],{"class":41},[32,9594,9595],{"class":34,"line":51},[32,9596,103],{"emptyLinePlaceholder":102},[32,9598,9599,9601,9604,9606,9609],{"class":34,"line":90},[32,9600,8892],{"class":41},[32,9602,9603],{"class":217},"component",[32,9605,477],{"class":41},[32,9607,9608],{"class":175},"'Loading'",[32,9610,9611],{"class":41},", Loading);\n",[22,9613,9615],{"className":8665,"code":9614,"language":4112,"meta":27,"style":27},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cloading\n      :active.sync=\"isLoading\"\n      :can-cancel=\"true\"\n      :on-cancel=\"onCancel\"\n      :is-full-page=\"fullPage\"\n    \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  data() {\n    return {\n      isLoading: false,\n    };\n  },\n};\n\u003C\u002Fscript>\n",[29,9616,9617,9626,9634,9642,9652,9662,9672,9682,9687,9696,9704,9708,9716,9724,9733,9739,9749,9754,9761,9768],{"__ignoreMap":27},[32,9618,9619,9621,9624],{"class":34,"line":35},[32,9620,4046],{"class":41},[32,9622,9623],{"class":4049},"template",[32,9625,4062],{"class":41},[32,9627,9628,9630,9632],{"class":34,"line":45},[32,9629,4067],{"class":41},[32,9631,4050],{"class":4049},[32,9633,4062],{"class":41},[32,9635,9636,9639],{"class":34,"line":51},[32,9637,9638],{"class":41},"    \u003C",[32,9640,9641],{"class":4049},"loading\n",[32,9643,9644,9647,9649],{"class":34,"line":90},[32,9645,9646],{"class":4053},"      :active.sync",[32,9648,172],{"class":41},[32,9650,9651],{"class":175},"\"isLoading\"\n",[32,9653,9654,9657,9659],{"class":34,"line":99},[32,9655,9656],{"class":4053},"      :can-cancel",[32,9658,172],{"class":41},[32,9660,9661],{"class":175},"\"true\"\n",[32,9663,9664,9667,9669],{"class":34,"line":106},[32,9665,9666],{"class":4053},"      :on-cancel",[32,9668,172],{"class":41},[32,9670,9671],{"class":175},"\"onCancel\"\n",[32,9673,9674,9677,9679],{"class":34,"line":114},[32,9675,9676],{"class":4053},"      :is-full-page",[32,9678,172],{"class":41},[32,9680,9681],{"class":175},"\"fullPage\"\n",[32,9683,9684],{"class":34,"line":120},[32,9685,9686],{"class":41},"    \u002F>\n",[32,9688,9689,9692,9694],{"class":34,"line":127},[32,9690,9691],{"class":41},"  \u003C\u002F",[32,9693,4050],{"class":4049},[32,9695,4062],{"class":41},[32,9697,9698,9700,9702],{"class":34,"line":132},[32,9699,4105],{"class":41},[32,9701,9623],{"class":4049},[32,9703,4062],{"class":41},[32,9705,9706],{"class":34,"line":141},[32,9707,103],{"emptyLinePlaceholder":102},[32,9709,9710,9712,9714],{"class":34,"line":147},[32,9711,4046],{"class":41},[32,9713,4125],{"class":4049},[32,9715,4062],{"class":41},[32,9717,9718,9720,9722],{"class":34,"line":275},[32,9719,5038],{"class":38},[32,9721,5041],{"class":38},[32,9723,703],{"class":5049},[32,9725,9726,9729,9731],{"class":34,"line":282},[32,9727,9728],{"class":217},"  data",[32,9730,6075],{"class":5049},[32,9732,5252],{"class":41},[32,9734,9735,9737],{"class":34,"line":295},[32,9736,971],{"class":38},[32,9738,703],{"class":41},[32,9740,9741,9744,9747],{"class":34,"line":605},[32,9742,9743],{"class":41},"      isLoading: ",[32,9745,9746],{"class":351},"false",[32,9748,547],{"class":41},[32,9750,9751],{"class":34,"line":612},[32,9752,9753],{"class":41},"    };\n",[32,9755,9756,9759],{"class":34,"line":617},[32,9757,9758],{"class":41},"  }",[32,9760,547],{"class":5049},[32,9762,9763,9766],{"class":34,"line":644},[32,9764,9765],{"class":5049},"}",[32,9767,96],{"class":41},[32,9769,9770,9772,9774],{"class":34,"line":649},[32,9771,4105],{"class":41},[32,9773,4125],{"class":4049},[32,9775,4062],{"class":41},[15,9777,9779],{"id":9778},"vee-validate","Vee Validate",[22,9781,9783],{"className":2057,"code":9782,"language":2059,"meta":27,"style":27},"npm install vee-validate\n",[29,9784,9785],{"__ignoreMap":27},[32,9786,9787,9789,9791],{"class":34,"line":35},[32,9788,4625],{"class":2066},[32,9790,4650],{"class":175},[32,9792,9793],{"class":175}," vee-validate\n",[11,9795,5134,9796,7419],{},[29,9797,5137],{},[22,9799,9801],{"className":24,"code":9800,"language":26,"meta":27,"style":27},"import VeeValidate from 'vee-validate';\n\nVue.use(VeeValidate);\n",[29,9802,9803,9817,9821],{"__ignoreMap":27},[32,9804,9805,9807,9810,9812,9815],{"class":34,"line":35},[32,9806,8794],{"class":38},[32,9808,9809],{"class":41}," VeeValidate ",[32,9811,8850],{"class":38},[32,9813,9814],{"class":175}," 'vee-validate'",[32,9816,96],{"class":41},[32,9818,9819],{"class":34,"line":45},[32,9820,103],{"emptyLinePlaceholder":102},[32,9822,9823,9825,9827],{"class":34,"line":51},[32,9824,8892],{"class":41},[32,9826,8895],{"class":217},[32,9828,9829],{"class":41},"(VeeValidate);\n",[11,9831,9832],{},"姓名欄位判斷：",[22,9834,9836],{"className":4038,"code":9835,"language":4034,"meta":27,"style":27},"\u003Cinput\n  type=\"text\"\n  name=\"name\"\n  :class=\"{ 'is-invalid': errors.has('name') }\"\n  v-validate=\"'required'\"\n>\n\u003Cspan class=\"text-danger\" v-if=\"errors.has('name')\">姓名必須輸入\u003C\u002Fspan>\n",[29,9837,9838,9845,9855,9865,9875,9885,9889],{"__ignoreMap":27},[32,9839,9840,9842],{"class":34,"line":35},[32,9841,4046],{"class":41},[32,9843,9844],{"class":4049},"input\n",[32,9846,9847,9850,9852],{"class":34,"line":45},[32,9848,9849],{"class":4053},"  type",[32,9851,172],{"class":41},[32,9853,9854],{"class":175},"\"text\"\n",[32,9856,9857,9860,9862],{"class":34,"line":51},[32,9858,9859],{"class":4053},"  name",[32,9861,172],{"class":41},[32,9863,9864],{"class":175},"\"name\"\n",[32,9866,9867,9870,9872],{"class":34,"line":90},[32,9868,9869],{"class":4053},"  :class",[32,9871,172],{"class":41},[32,9873,9874],{"class":175},"\"{ 'is-invalid': errors.has('name') }\"\n",[32,9876,9877,9880,9882],{"class":34,"line":99},[32,9878,9879],{"class":4053},"  v-validate",[32,9881,172],{"class":41},[32,9883,9884],{"class":175},"\"'required'\"\n",[32,9886,9887],{"class":34,"line":106},[32,9888,4062],{"class":41},[32,9890,9891,9893,9895,9898,9900,9903,9906,9908,9911,9914,9916],{"class":34,"line":114},[32,9892,4046],{"class":41},[32,9894,32],{"class":4049},[32,9896,9897],{"class":4053}," class",[32,9899,172],{"class":41},[32,9901,9902],{"class":175},"\"text-danger\"",[32,9904,9905],{"class":4053}," v-if",[32,9907,172],{"class":41},[32,9909,9910],{"class":175},"\"errors.has('name')\"",[32,9912,9913],{"class":41},">姓名必須輸入\u003C\u002F",[32,9915,32],{"class":4049},[32,9917,4062],{"class":41},[11,9919,9920,9923],{},[29,9921,9922],{},"v-validate=\"'required'\""," 代表必須輸入。欄位點選後沒有輸入，再移出欄位時會顯示錯誤。",[11,9925,9926],{},"Email 欄位判斷：",[22,9928,9930],{"className":4038,"code":9929,"language":4034,"meta":27,"style":27},"\u003Cinput\n  type=\"text\"\n  name=\"email\"\n  v-model=\"form.user.email\"\n  :class=\"{ 'is-invalid': errors.has('email') }\"\n  v-validate=\"'required|email'\"\n>\n\u003Cspan class=\"text-danger\" v-if=\"errors.has('email')\">\n  {{ errors.first('email') }}\n\u003C\u002Fspan>\n",[29,9931,9932,9938,9946,9955,9965,9974,9983,9987,10008,10013],{"__ignoreMap":27},[32,9933,9934,9936],{"class":34,"line":35},[32,9935,4046],{"class":41},[32,9937,9844],{"class":4049},[32,9939,9940,9942,9944],{"class":34,"line":45},[32,9941,9849],{"class":4053},[32,9943,172],{"class":41},[32,9945,9854],{"class":175},[32,9947,9948,9950,9952],{"class":34,"line":51},[32,9949,9859],{"class":4053},[32,9951,172],{"class":41},[32,9953,9954],{"class":175},"\"email\"\n",[32,9956,9957,9960,9962],{"class":34,"line":90},[32,9958,9959],{"class":4053},"  v-model",[32,9961,172],{"class":41},[32,9963,9964],{"class":175},"\"form.user.email\"\n",[32,9966,9967,9969,9971],{"class":34,"line":99},[32,9968,9869],{"class":4053},[32,9970,172],{"class":41},[32,9972,9973],{"class":175},"\"{ 'is-invalid': errors.has('email') }\"\n",[32,9975,9976,9978,9980],{"class":34,"line":106},[32,9977,9879],{"class":4053},[32,9979,172],{"class":41},[32,9981,9982],{"class":175},"\"'required|email'\"\n",[32,9984,9985],{"class":34,"line":114},[32,9986,4062],{"class":41},[32,9988,9989,9991,9993,9995,9997,9999,10001,10003,10006],{"class":34,"line":120},[32,9990,4046],{"class":41},[32,9992,32],{"class":4049},[32,9994,9897],{"class":4053},[32,9996,172],{"class":41},[32,9998,9902],{"class":175},[32,10000,9905],{"class":4053},[32,10002,172],{"class":41},[32,10004,10005],{"class":175},"\"errors.has('email')\"",[32,10007,4062],{"class":41},[32,10009,10010],{"class":34,"line":127},[32,10011,10012],{"class":41},"  {{ errors.first('email') }}\n",[32,10014,10015,10017,10019],{"class":34,"line":132},[32,10016,4105],{"class":41},[32,10018,32],{"class":4049},[32,10020,4062],{"class":41},[151,10022,10023],{"id":10023},"驗證語系",[11,10025,10026,10027,3775],{},"語系路徑：",[29,10028,10029],{},"node_modules\u002Fvee-validate\u002Fdist\u002Flocale\u002F",[22,10031,10033],{"className":24,"code":10032,"language":26,"meta":27,"style":27},"import zhTWValidate from 'vee-validate\u002Fdist\u002Flocale\u002Fzh_TW';\n\nVeeValidate.Validator.localize('zh_TW', zhTWValidate);\n",[29,10034,10035,10049,10053],{"__ignoreMap":27},[32,10036,10037,10039,10042,10044,10047],{"class":34,"line":35},[32,10038,8794],{"class":38},[32,10040,10041],{"class":41}," zhTWValidate ",[32,10043,8850],{"class":38},[32,10045,10046],{"class":175}," 'vee-validate\u002Fdist\u002Flocale\u002Fzh_TW'",[32,10048,96],{"class":41},[32,10050,10051],{"class":34,"line":45},[32,10052,103],{"emptyLinePlaceholder":102},[32,10054,10055,10058,10061,10063,10066],{"class":34,"line":51},[32,10056,10057],{"class":41},"VeeValidate.Validator.",[32,10059,10060],{"class":217},"localize",[32,10062,477],{"class":41},[32,10064,10065],{"class":175},"'zh_TW'",[32,10067,10068],{"class":41},", zhTWValidate);\n",[151,10070,10071],{"id":10071},"表單送出前驗證",[22,10073,10075],{"className":24,"code":10074,"language":26,"meta":27,"style":27},"this.$validator.validate().then((result) => {\n  if (result) {\n    this.$http.post(url, { data: order }).then((res) => {\n      console.log('訂單已建立', res);\n    });\n  } else {\n    console.log('未完成');\n  }\n});\n",[29,10076,10077,10103,10110,10135,10150,10154,10162,10175,10179],{"__ignoreMap":27},[32,10078,10079,10081,10084,10087,10090,10092,10094,10097,10099,10101],{"class":34,"line":35},[32,10080,1083],{"class":351},[32,10082,10083],{"class":41},".$validator.",[32,10085,10086],{"class":217},"validate",[32,10088,10089],{"class":41},"().",[32,10091,8950],{"class":217},[32,10093,8293],{"class":41},[32,10095,10096],{"class":903},"result",[32,10098,483],{"class":41},[32,10100,5090],{"class":38},[32,10102,703],{"class":41},[32,10104,10105,10107],{"class":34,"line":45},[32,10106,1021],{"class":38},[32,10108,10109],{"class":41}," (result) {\n",[32,10111,10112,10115,10117,10119,10122,10124,10126,10129,10131,10133],{"class":34,"line":51},[32,10113,10114],{"class":351},"    this",[32,10116,8986],{"class":41},[32,10118,9443],{"class":217},[32,10120,10121],{"class":41},"(url, { data: order }).",[32,10123,8950],{"class":217},[32,10125,8293],{"class":41},[32,10127,10128],{"class":903},"res",[32,10130,483],{"class":41},[32,10132,5090],{"class":38},[32,10134,703],{"class":41},[32,10136,10137,10140,10142,10144,10147],{"class":34,"line":90},[32,10138,10139],{"class":41},"      console.",[32,10141,5197],{"class":217},[32,10143,477],{"class":41},[32,10145,10146],{"class":175},"'訂單已建立'",[32,10148,10149],{"class":41},", res);\n",[32,10151,10152],{"class":34,"line":99},[32,10153,1198],{"class":41},[32,10155,10156,10158,10160],{"class":34,"line":106},[32,10157,5911],{"class":41},[32,10159,565],{"class":38},[32,10161,703],{"class":41},[32,10163,10164,10166,10168,10170,10173],{"class":34,"line":114},[32,10165,8966],{"class":41},[32,10167,5197],{"class":217},[32,10169,477],{"class":41},[32,10171,10172],{"class":175},"'未完成'",[32,10174,230],{"class":41},[32,10176,10177],{"class":34,"line":120},[32,10178,1203],{"class":41},[32,10180,10181],{"class":34,"line":127},[32,10182,792],{"class":41},[15,10184,4844],{"id":10185},"vuex",[11,10187,10188,10189,10192],{},"先新增 ",[29,10190,10191],{},"store.js","，再安裝 Vuex：",[22,10194,10196],{"className":2057,"code":10195,"language":2059,"meta":27,"style":27},"npm i -s vuex\n",[29,10197,10198],{"__ignoreMap":27},[32,10199,10200,10202,10204,10206],{"class":34,"line":35},[32,10201,4625],{"class":2066},[32,10203,8509],{"class":175},[32,10205,9161],{"class":351},[32,10207,10208],{"class":175}," vuex\n",[11,10210,10211,5240],{},[29,10212,5137],{},[22,10214,10216],{"className":24,"code":10215,"language":26,"meta":27,"style":27},"import Vuex, { Store } from 'vuex';\n\nVue.use(Vuex);\n",[29,10217,10218,10232,10236],{"__ignoreMap":27},[32,10219,10220,10222,10225,10227,10230],{"class":34,"line":35},[32,10221,8794],{"class":38},[32,10223,10224],{"class":41}," Vuex, { Store } ",[32,10226,8850],{"class":38},[32,10228,10229],{"class":175}," 'vuex'",[32,10231,96],{"class":41},[32,10233,10234],{"class":34,"line":45},[32,10235,103],{"emptyLinePlaceholder":102},[32,10237,10238,10240,10242],{"class":34,"line":51},[32,10239,8892],{"class":41},[32,10241,8895],{"class":217},[32,10243,10244],{"class":41},"(Vuex);\n",[11,10246,10247],{},"元件：",[22,10249,10251],{"className":24,"code":10250,"language":26,"meta":27,"style":27},"import store from '.\u002Fstore.js';\nimport { mapState } from 'vuex';\n",[29,10252,10253,10267],{"__ignoreMap":27},[32,10254,10255,10257,10260,10262,10265],{"class":34,"line":35},[32,10256,8794],{"class":38},[32,10258,10259],{"class":41}," store ",[32,10261,8850],{"class":38},[32,10263,10264],{"class":175}," '.\u002Fstore.js'",[32,10266,96],{"class":41},[32,10268,10269,10271,10274,10276,10278],{"class":34,"line":45},[32,10270,8794],{"class":38},[32,10272,10273],{"class":41}," { mapState } ",[32,10275,8850],{"class":38},[32,10277,10229],{"class":175},[32,10279,96],{"class":41},[15,10281,10283],{"id":10282},"scss-架構","SCSS 架構",[22,10285,10288],{"className":10286,"code":10287,"language":426,"meta":27},[424],"app.scss              統一引入自訂樣式與 Bootstrap\n_customBootstrap.scss 統一調整 Bootstrap 風格\n",[29,10289,10287],{"__ignoreMap":27},[11,10291,10292,5240],{},[29,10293,10294],{},"app.scss",[22,10296,10298],{"className":8735,"code":10297,"language":8737,"meta":27,"style":27},"@import 'variable';\n@import 'mixin';\n@import 'reset';\n@import 'extend';\n@import 'layout';\n\n\u002F\u002F third-party\n@import 'customBootstrap';\n@import '~bootstrap\u002Fscss\u002Fbootstrap';\n",[29,10299,10300,10309,10318,10327,10336,10345,10349,10354,10363],{"__ignoreMap":27},[32,10301,10302,10304,10307],{"class":34,"line":35},[32,10303,8744],{"class":38},[32,10305,10306],{"class":175}," 'variable'",[32,10308,96],{"class":41},[32,10310,10311,10313,10316],{"class":34,"line":45},[32,10312,8744],{"class":38},[32,10314,10315],{"class":175}," 'mixin'",[32,10317,96],{"class":41},[32,10319,10320,10322,10325],{"class":34,"line":51},[32,10321,8744],{"class":38},[32,10323,10324],{"class":175}," 'reset'",[32,10326,96],{"class":41},[32,10328,10329,10331,10334],{"class":34,"line":90},[32,10330,8744],{"class":38},[32,10332,10333],{"class":175}," 'extend'",[32,10335,96],{"class":41},[32,10337,10338,10340,10343],{"class":34,"line":99},[32,10339,8744],{"class":38},[32,10341,10342],{"class":175}," 'layout'",[32,10344,96],{"class":41},[32,10346,10347],{"class":34,"line":106},[32,10348,103],{"emptyLinePlaceholder":102},[32,10350,10351],{"class":34,"line":114},[32,10352,10353],{"class":608},"\u002F\u002F third-party\n",[32,10355,10356,10358,10361],{"class":34,"line":120},[32,10357,8744],{"class":38},[32,10359,10360],{"class":175}," 'customBootstrap'",[32,10362,96],{"class":41},[32,10364,10365,10367,10369],{"class":34,"line":127},[32,10366,8744],{"class":38},[32,10368,8747],{"class":175},[32,10370,96],{"class":41},[11,10372,10373,10374,5240],{},"程式進入點只要引入 Bootstrap 與 ",[29,10375,10294],{},[22,10377,10379],{"className":24,"code":10378,"language":26,"meta":27,"style":27},"import 'bootstrap';\nimport '.\u002Fassets\u002Fcss\u002Fapp.scss';\n",[29,10380,10381,10389],{"__ignoreMap":27},[32,10382,10383,10385,10387],{"class":34,"line":35},[32,10384,8794],{"class":38},[32,10386,8806],{"class":175},[32,10388,96],{"class":41},[32,10390,10391,10393,10396],{"class":34,"line":45},[32,10392,8794],{"class":38},[32,10394,10395],{"class":175}," '.\u002Fassets\u002Fcss\u002Fapp.scss'",[32,10397,96],{"class":41},[11,10399,10400,10401,3775],{},"參考：",[412,10402,10283],{"href":10403,"rel":10404},"https:\u002F\u002Fithelp.ithome.com.tw\u002Farticles\u002F10186760",[416],[15,10406,10408],{"id":10407},"vue-cli-專案結構","Vue CLI 專案結構",[22,10410,10413],{"className":10411,"code":10412,"language":426,"meta":27},[424],"main.js         程式進入點\nrouter\u002Findex.js Router 設定檔\n*.vue           分頁與元件\n",[29,10414,10412],{"__ignoreMap":27},[11,10416,10417],{},"安裝完成後：",[22,10419,10421],{"className":2057,"code":10420,"language":2059,"meta":27,"style":27},"npm i\nnpm run dev\n",[29,10422,10423,10430],{"__ignoreMap":27},[32,10424,10425,10427],{"class":34,"line":35},[32,10426,4625],{"class":2066},[32,10428,10429],{"class":175}," i\n",[32,10431,10432,10434,10436],{"class":34,"line":45},[32,10433,4625],{"class":2066},[32,10435,4743],{"class":175},[32,10437,10438],{"class":175}," dev\n",[15,10440,10441],{"id":10441},"其他筆記",[4538,10443,10444,10454,10458,10464,10473,10476],{},[4541,10445,10446,10447,10450,10451],{},"終止命令提示字元：",[29,10448,10449],{},"Ctrl+C"," 或 ",[29,10452,10453],{},"Ctrl+\u002F",[4541,10455,10456,6679],{},[29,10457,6678],{},[4541,10459,10460,10461],{},"開啟 VS Code 終端機：",[29,10462,10463],{},"Ctrl+`",[4541,10465,10466,10467,10469,10470],{},"Webpack 的 ",[29,10468,7172],{}," 可以省略副檔名，路徑可以使用 ",[29,10471,10472],{},"@\u002F",[4541,10474,10475],{},"圖片小於 10KB 會壓縮成 Base64，大於 10KB 則以圖檔顯示",[4541,10477,10478,10480,10481,10484],{},[29,10479,7093],{}," 依執行環境設定，",[29,10482,10483],{},"host"," 可以替換成要運行的 IP",[368,10486,10487],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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 .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}",{"title":27,"searchDepth":51,"depth":51,"links":10489},[10490,10491,10492,10493,10496,10497,10500,10501,10505,10506,10507,10508],{"id":6248,"depth":45,"text":6249},{"id":8493,"depth":45,"text":8494},{"id":8614,"depth":45,"text":8615},{"id":8811,"depth":45,"text":8812,"children":10494},[10495],{"id":9021,"depth":51,"text":9022},{"id":9041,"depth":45,"text":9042},{"id":9144,"depth":45,"text":4841,"children":10498},[10499],{"id":9356,"depth":51,"text":9356},{"id":9548,"depth":45,"text":9548},{"id":9778,"depth":45,"text":9779,"children":10502},[10503,10504],{"id":10023,"depth":51,"text":10023},{"id":10071,"depth":51,"text":10071},{"id":10185,"depth":45,"text":4844},{"id":10282,"depth":45,"text":10283},{"id":10407,"depth":45,"text":10408},{"id":10441,"depth":45,"text":10441},"2018 年整理的 Vue CLI、Vue Router、Vuex、axios、Bootstrap 4 與表單驗證套件安裝筆記。",{},"\u002Fposts\u002Fvue2-project-kit","2018-10-29",[],{"title":8414,"description":10509},"posts\u002F20181029.vue2-project-kit",[4113,4844,9540,10517,4572],"Bootstrap","ukHYg73OuRjyb1jgyStTmLmg0B_Vkx6i9OCkWEzX5H4",{"id":10520,"title":10521,"body":10522,"category":378,"description":10704,"draft":380,"extension":381,"image":382,"meta":10705,"navigation":102,"path":10706,"publishedAt":10707,"relatedExperiments":10708,"seo":10709,"stem":10710,"tags":10711,"updatedAt":392,"__hash__":10713},"posts\u002Fposts\u002F20190315.legacy-url-parsing.md","使用 location.href 取得網址片段",{"type":8,"value":10523,"toc":10702},[10524,10527,10537,10692,10699],[10525,10526],"hr",{},[11,10528,10529,10530,10533,10534,10536],{},"先透過 ",[29,10531,10532],{},"location.href"," 取得目前頁面的完整網址，再使用 ",[29,10535,1275],{}," 分割字串，取得指定位置的內容：",[22,10538,10540],{"className":24,"code":10539,"language":26,"meta":27,"style":27},"var url = location.href;\n\nif (url.indexOf('\u002F') != -1) {\n  var ary1 = url.split('\u002F');\n\n  ary1[5] == undefined ? (num = 'u001') : (num = ary1[5].split('='));\n\n  var ary2 = num.toString();\n  num = ary2;\n}\n",[29,10541,10542,10554,10558,10581,10602,10606,10657,10661,10678,10688],{"__ignoreMap":27},[32,10543,10544,10546,10549,10551],{"class":34,"line":35},[32,10545,166],{"class":38},[32,10547,10548],{"class":41}," url ",[32,10550,172],{"class":38},[32,10552,10553],{"class":41}," location.href;\n",[32,10555,10556],{"class":34,"line":45},[32,10557,103],{"emptyLinePlaceholder":102},[32,10559,10560,10562,10565,10567,10569,10571,10573,10575,10577,10579],{"class":34,"line":51},[32,10561,17],{"class":38},[32,10563,10564],{"class":41}," (url.",[32,10566,1106],{"class":217},[32,10568,477],{"class":41},[32,10570,5067],{"class":175},[32,10572,483],{"class":41},[32,10574,1116],{"class":38},[32,10576,1119],{"class":38},[32,10578,6544],{"class":351},[32,10580,520],{"class":41},[32,10582,10583,10585,10588,10590,10593,10596,10598,10600],{"class":34,"line":90},[32,10584,888],{"class":38},[32,10586,10587],{"class":41}," ary1 ",[32,10589,172],{"class":38},[32,10591,10592],{"class":41}," url.",[32,10594,10595],{"class":217},"split",[32,10597,477],{"class":41},[32,10599,5067],{"class":175},[32,10601,230],{"class":41},[32,10603,10604],{"class":34,"line":99},[32,10605,103],{"emptyLinePlaceholder":102},[32,10607,10608,10611,10614,10617,10619,10622,10624,10627,10629,10632,10634,10636,10638,10640,10643,10645,10648,10650,10652,10655],{"class":34,"line":106},[32,10609,10610],{"class":41},"  ary1[",[32,10612,10613],{"class":351},"5",[32,10615,10616],{"class":41},"] ",[32,10618,5867],{"class":38},[32,10620,10621],{"class":351}," undefined",[32,10623,355],{"class":38},[32,10625,10626],{"class":41}," (num ",[32,10628,172],{"class":38},[32,10630,10631],{"class":175}," 'u001'",[32,10633,483],{"class":41},[32,10635,322],{"class":38},[32,10637,10626],{"class":41},[32,10639,172],{"class":38},[32,10641,10642],{"class":41}," ary1[",[32,10644,10613],{"class":351},[32,10646,10647],{"class":41},"].",[32,10649,10595],{"class":217},[32,10651,477],{"class":41},[32,10653,10654],{"class":175},"'='",[32,10656,8328],{"class":41},[32,10658,10659],{"class":34,"line":114},[32,10660,103],{"emptyLinePlaceholder":102},[32,10662,10663,10665,10668,10670,10673,10676],{"class":34,"line":120},[32,10664,888],{"class":38},[32,10666,10667],{"class":41}," ary2 ",[32,10669,172],{"class":38},[32,10671,10672],{"class":41}," num.",[32,10674,10675],{"class":217},"toString",[32,10677,687],{"class":41},[32,10679,10680,10683,10685],{"class":34,"line":127},[32,10681,10682],{"class":41},"  num ",[32,10684,172],{"class":38},[32,10686,10687],{"class":41}," ary2;\n",[32,10689,10690],{"class":34,"line":132},[32,10691,54],{"class":41},[11,10693,10694,10695,10698],{},"這種寫法會依賴網址分割後的固定位置，例如 ",[29,10696,10697],{},"ary1[5]","。\n如果網址結構改變，就可能取得錯誤的內容。",[368,10700,10701],{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}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);}",{"title":27,"searchDepth":51,"depth":51,"links":10703},[],"2019 年使用 location.href 搭配 split() 取得網址片段的程式碼筆記，並補充較新的寫法。",{},"\u002Fposts\u002Flegacy-url-parsing","2019-03-15",[],{"title":10521,"description":10704},"posts\u002F20190315.legacy-url-parsing",[390,10712],"URL","cmNtF4IogsayA72J8LWjxUPCgBJZ72VPUplK0nhEYcI",{"id":10715,"title":10716,"body":10717,"category":378,"description":10819,"draft":380,"extension":381,"image":382,"meta":10820,"navigation":102,"path":10821,"publishedAt":10822,"relatedExperiments":10823,"seo":10824,"stem":10825,"tags":10826,"updatedAt":10829,"__hash__":10830},"posts\u002Fposts\u002F20200203.es6-var-let-const.md","JavaScript ES6：var、let 與 const",{"type":8,"value":10718,"toc":10813},[10719,10730,10734,10739,10746,10750,10758,10764,10772,10778,10782,10787,10793,10796,10802,10805],[11,10720,10721,10722,3750,10724,7090,10727,10729],{},"這邊記錄 ",[29,10723,166],{},[29,10725,10726],{},"let",[29,10728,6617],{}," 變數之間差異。",[15,10731,10733],{"id":10732},"var-宣告","var 宣告",[11,10735,4675,10736,10738],{},[29,10737,166],{}," 宣告變數時，會先在記憶體裡面存取空間。",[11,10740,10741],{},[10742,10743],"img",{"alt":10744,"src":10745},"開發者工具使用 var 宣告 test 變數，宣告結果顯示 undefined","\u002Fimages\u002Fposts\u002Fes6-var-let-const\u002F01.webp",[15,10747,10749],{"id":10748},"var-與-let-的作用域","var 與 let 的作用域",[11,10751,10752,10754,10755,10757],{},[29,10753,166],{}," 在 function 裡宣告，作用域是 function scope；",[29,10756,10726],{}," 的作用域是 block。",[11,10759,10760],{},[10742,10761],{"alt":10762,"src":10763},"var 與 let 在 if 區塊中的作用域比較，var 會影響外層變數，let 不會","\u002Fimages\u002Fposts\u002Fes6-var-let-const\u002F02.webp",[11,10765,10766,10767,7090,10769,10771],{},"下面比較 ",[29,10768,166],{},[29,10770,10726],{}," 在迴圈裡的差異。",[11,10773,10774],{},[10742,10775],{"alt":10776,"src":10777},"for 迴圈分別使用 var 與 let，var 成為全域變數並輸出 10，let 依序輸出 0 到 9","\u002Fimages\u002Fposts\u002Fes6-var-let-const\u002F03.webp",[15,10779,10781],{"id":10780},"const-常數","const 常數",[11,10783,10784,10786],{},[29,10785,6617],{}," 宣告的變數不能被更換。",[11,10788,10789],{},[10742,10790],{"alt":10791,"src":10792},"重新指定 const 變數時出現 Assignment to constant variable 錯誤","\u002Fimages\u002Fposts\u002Fes6-var-let-const\u002F04.webp",[11,10794,10795],{},"物件本身是傳參考而不是傳值，因此物件內的屬性仍然可以修改。",[11,10797,10798],{},[10742,10799],{"alt":10800,"src":10801},"const 宣告物件後修改 a2 屬性，物件內容由 456 變成 222","\u002Fimages\u002Fposts\u002Fes6-var-let-const\u002F05.webp",[15,10803,10804],{"id":10804},"結論",[11,10806,10807,10808,10810,10811,3775],{},"作用域預設使用 ",[29,10809,6617],{},"，需要重新賦值時才使用 ",[29,10812,10726],{},{"title":27,"searchDepth":51,"depth":51,"links":10814},[10815,10816,10817,10818],{"id":10732,"depth":45,"text":10733},{"id":10748,"depth":45,"text":10749},{"id":10780,"depth":45,"text":10781},{"id":10804,"depth":45,"text":10804},"2020 年整理的 var、let 與 const 變數宣告及作用域筆記。",{},"\u002Fposts\u002Fes6-var-let-const","2020-02-03",[],{"title":10716,"description":10819},"posts\u002F20200203.es6-var-let-const",[390,10827,10828],"ES6","變數宣告","2026-08-06","3yvA0FjmicBouy6o7wc7PCuHBY_Eba7xxCZizBvAL8g",{"id":10832,"title":10833,"body":10834,"category":378,"description":10921,"draft":380,"extension":381,"image":382,"meta":10922,"navigation":102,"path":10923,"publishedAt":10924,"relatedExperiments":10925,"seo":10926,"stem":10927,"tags":10928,"updatedAt":10829,"__hash__":10930},"posts\u002Fposts\u002F20200205.es6-destructuring.md","JavaScript ES6：解構賦值",{"type":8,"value":10835,"toc":10914},[10836,10839,10842,10845,10851,10854,10857,10863,10866,10872,10875,10878,10884,10887,10893,10896,10899,10905,10908],[11,10837,10838],{},"解構賦值是將右邊的值賦予給左邊的變數，有點像鏡射的概念。\n如果左右兩邊數量不同，右邊多餘的值不會進行賦值。",[15,10840,10841],{"id":10841},"陣列解構",[11,10843,10844],{},"範例：將陣列裡的值賦予變數。逗號之間沒有變數時，會跳過該位置。",[11,10846,10847],{},[10742,10848],{"alt":10849,"src":10850},"陣列解構依序取得值、只取前兩個值，以及用空位跳過中間元素","\u002Fimages\u002Fposts\u002Fes6-destructuring\u002F01.webp",[15,10852,10853],{"id":10853},"交換變數",[11,10855,10856],{},"解構賦值可以直接交換變數，不需要第三個暫存變數。",[11,10858,10859],{},[10742,10860],{"alt":10861,"src":10862},"使用陣列解構交換 aa 與 bb，輸出結果由 aa bb 變成 bb aa","\u002Fimages\u002Fposts\u002Fes6-destructuring\u002F02.webp",[15,10864,10865],{"id":10865},"拆解字串",[11,10867,10868],{},[10742,10869],{"alt":10870,"src":10871},"將 hello 字串解構成五個字元並逐一輸出","\u002Fimages\u002Fposts\u002Fes6-destructuring\u002F03.webp",[15,10873,10874],{"id":10874},"物件解構",[11,10876,10877],{},"可以取出物件其中一個值，也可以指定新的變數名稱。",[11,10879,10880],{},[10742,10881],{"alt":10882,"src":10883},"從物件取出 aa 屬性，並示範將 aa 改名成 gg","\u002Fimages\u002Fposts\u002Fes6-destructuring\u002F04.webp",[11,10885,10886],{},"物件與陣列也能一起解構。",[11,10888,10889],{},[10742,10890],{"alt":10891,"src":10892},"同時解構物件的 ll 屬性與 tt 陣列第二個元素，輸出 test 與 bb","\u002Fimages\u002Fposts\u002Fes6-destructuring\u002F05.webp",[15,10894,10895],{"id":10895},"預設值",[11,10897,10898],{},"陣列解構可以設定預設值。",[11,10900,10901],{},[10742,10902],{"alt":10903,"src":10904},"陣列只有 cc 一個值時，testa 取得 cc，testb 使用預設值 bb","\u002Fimages\u002Fposts\u002Fes6-destructuring\u002F06.webp",[11,10906,10907],{},"物件解構改名時也能設定預設值。",[11,10909,10910],{},[10742,10911],{"alt":10912,"src":10913},"從空物件解構 testc 並改名為 ming，最後使用預設值 aa","\u002Fimages\u002Fposts\u002Fes6-destructuring\u002F07.webp",{"title":27,"searchDepth":51,"depth":51,"links":10915},[10916,10917,10918,10919,10920],{"id":10841,"depth":45,"text":10841},{"id":10853,"depth":45,"text":10853},{"id":10865,"depth":45,"text":10865},{"id":10874,"depth":45,"text":10874},{"id":10895,"depth":45,"text":10895},"2020 年整理的陣列、字串與物件解構賦值筆記。",{},"\u002Fposts\u002Fes6-destructuring","2020-02-05",[],{"title":10833,"description":10921},"posts\u002F20200205.es6-destructuring",[390,10827,10929],"解構賦值","3eZqGwKLbvymn70nkehs7tumY0sH9ooYT1dyONm1nDA",{"id":10932,"title":10933,"body":10934,"category":378,"description":11557,"draft":380,"extension":381,"image":382,"meta":11558,"navigation":102,"path":11559,"publishedAt":11560,"relatedExperiments":11561,"seo":11562,"stem":11563,"tags":11564,"updatedAt":10829,"__hash__":11566},"posts\u002Fposts\u002F20200206.es6-arrow-functions.md","JavaScript ES6：箭頭函式",{"type":8,"value":10935,"toc":11547},[10936,10939,10947,11054,11057,11110,11113,11196,11199,11232,11235,11241,11278,11282,11288,11291,11298,11365,11368,11371,11475,11479,11482,11515,11533,11535,11544],[15,10937,10938],{"id":10938},"語法簡寫",[4538,10940,10941,10944],{},[4541,10942,10943],{},"參數只有一個時可以省略小括號。",[4541,10945,10946],{},"內容只有一行並回傳時，可以簡寫成一行。",[22,10948,10950],{"className":24,"code":10949,"language":26,"meta":27,"style":27},"var double = function (x) {\n  return x * 2;\n};\n\nconst double = (x) => {\n  return x * 2;\n};\n\nconst shortDouble = x => x * 2;\n",[29,10951,10952,10970,10985,10989,10993,11011,11023,11027,11031],{"__ignoreMap":27},[32,10953,10954,10956,10959,10961,10963,10965,10968],{"class":34,"line":35},[32,10955,166],{"class":38},[32,10957,10958],{"class":217}," double",[32,10960,894],{"class":38},[32,10962,897],{"class":38},[32,10964,900],{"class":41},[32,10966,10967],{"class":903},"x",[32,10969,520],{"class":41},[32,10971,10972,10975,10978,10980,10983],{"class":34,"line":45},[32,10973,10974],{"class":38},"  return",[32,10976,10977],{"class":41}," x ",[32,10979,6078],{"class":38},[32,10981,10982],{"class":351}," 2",[32,10984,96],{"class":41},[32,10986,10987],{"class":34,"line":51},[32,10988,7189],{"class":41},[32,10990,10991],{"class":34,"line":90},[32,10992,103],{"emptyLinePlaceholder":102},[32,10994,10995,10997,10999,11001,11003,11005,11007,11009],{"class":34,"line":99},[32,10996,6617],{"class":38},[32,10998,10958],{"class":217},[32,11000,894],{"class":38},[32,11002,900],{"class":41},[32,11004,10967],{"class":903},[32,11006,483],{"class":41},[32,11008,5090],{"class":38},[32,11010,703],{"class":41},[32,11012,11013,11015,11017,11019,11021],{"class":34,"line":106},[32,11014,10974],{"class":38},[32,11016,10977],{"class":41},[32,11018,6078],{"class":38},[32,11020,10982],{"class":351},[32,11022,96],{"class":41},[32,11024,11025],{"class":34,"line":114},[32,11026,7189],{"class":41},[32,11028,11029],{"class":34,"line":120},[32,11030,103],{"emptyLinePlaceholder":102},[32,11032,11033,11035,11038,11040,11043,11046,11048,11050,11052],{"class":34,"line":127},[32,11034,6617],{"class":38},[32,11036,11037],{"class":217}," shortDouble",[32,11039,894],{"class":38},[32,11041,11042],{"class":903}," x",[32,11044,11045],{"class":38}," =>",[32,11047,10977],{"class":41},[32,11049,6078],{"class":38},[32,11051,10982],{"class":351},[32,11053,96],{"class":41},[11,11055,11056],{},"只有一個參數時可以省略小括號；有兩個參數時要保留：",[22,11058,11060],{"className":24,"code":11059,"language":26,"meta":27,"style":27},"const double = x => x * 2;\nconst add = (x, y) => x + y;\n",[29,11061,11062,11082],{"__ignoreMap":27},[32,11063,11064,11066,11068,11070,11072,11074,11076,11078,11080],{"class":34,"line":35},[32,11065,6617],{"class":38},[32,11067,10958],{"class":217},[32,11069,894],{"class":38},[32,11071,11042],{"class":903},[32,11073,11045],{"class":38},[32,11075,10977],{"class":41},[32,11077,6078],{"class":38},[32,11079,10982],{"class":351},[32,11081,96],{"class":41},[32,11083,11084,11086,11088,11090,11092,11094,11096,11099,11101,11103,11105,11107],{"class":34,"line":45},[32,11085,6617],{"class":38},[32,11087,2493],{"class":217},[32,11089,894],{"class":38},[32,11091,900],{"class":41},[32,11093,10967],{"class":903},[32,11095,635],{"class":41},[32,11097,11098],{"class":903},"y",[32,11100,483],{"class":41},[32,11102,5090],{"class":38},[32,11104,10977],{"class":41},[32,11106,224],{"class":38},[32,11108,11109],{"class":41}," y;\n",[11,11111,11112],{},"陣列方法的 ES5 與 ES6 寫法：",[22,11114,11116],{"className":24,"code":11115,"language":26,"meta":27,"style":27},"\u002F\u002F ES5\narr.map(function (elm, idx) {\n  return elm + idx;\n});\n\n\u002F\u002F ES6\narr.map((elm, idx) => elm + idx);\n",[29,11117,11118,11123,11146,11158,11162,11166,11171],{"__ignoreMap":27},[32,11119,11120],{"class":34,"line":35},[32,11121,11122],{"class":608},"\u002F\u002F ES5\n",[32,11124,11125,11128,11131,11133,11135,11137,11140,11142,11144],{"class":34,"line":45},[32,11126,11127],{"class":41},"arr.",[32,11129,11130],{"class":217},"map",[32,11132,477],{"class":41},[32,11134,665],{"class":38},[32,11136,900],{"class":41},[32,11138,11139],{"class":903},"elm",[32,11141,635],{"class":41},[32,11143,8301],{"class":903},[32,11145,520],{"class":41},[32,11147,11148,11150,11153,11155],{"class":34,"line":51},[32,11149,10974],{"class":38},[32,11151,11152],{"class":41}," elm ",[32,11154,224],{"class":38},[32,11156,11157],{"class":41}," idx;\n",[32,11159,11160],{"class":34,"line":90},[32,11161,792],{"class":41},[32,11163,11164],{"class":34,"line":99},[32,11165,103],{"emptyLinePlaceholder":102},[32,11167,11168],{"class":34,"line":106},[32,11169,11170],{"class":608},"\u002F\u002F ES6\n",[32,11172,11173,11175,11177,11179,11181,11183,11185,11187,11189,11191,11193],{"class":34,"line":114},[32,11174,11127],{"class":41},[32,11176,11130],{"class":217},[32,11178,8293],{"class":41},[32,11180,11139],{"class":903},[32,11182,635],{"class":41},[32,11184,8301],{"class":903},[32,11186,483],{"class":41},[32,11188,5090],{"class":38},[32,11190,11152],{"class":41},[32,11192,224],{"class":38},[32,11194,11195],{"class":41}," idx);\n",[11,11197,11198],{},"事件監聽：",[22,11200,11202],{"className":24,"code":11201,"language":26,"meta":27,"style":27},"btn.addEventListener('click', () => console.log(10));\n",[29,11203,11204],{"__ignoreMap":27},[32,11205,11206,11209,11211,11213,11216,11219,11221,11223,11225,11227,11230],{"class":34,"line":35},[32,11207,11208],{"class":41},"btn.",[32,11210,5517],{"class":217},[32,11212,477],{"class":41},[32,11214,11215],{"class":175},"'click'",[32,11217,11218],{"class":41},", () ",[32,11220,5090],{"class":38},[32,11222,8308],{"class":41},[32,11224,5197],{"class":217},[32,11226,477],{"class":41},[32,11228,11229],{"class":351},"10",[32,11231,8328],{"class":41},[15,11233,11234],{"id":11234},"自動綁定",[11,11236,11237,11238,11240],{},"箭頭函式內部的 ",[29,11239,1083],{}," 與外部相同。",[22,11242,11244],{"className":24,"code":11243,"language":26,"meta":27,"style":27},"const a = () => {\n  console.log(this);\n};\n",[29,11245,11246,11262,11274],{"__ignoreMap":27},[32,11247,11248,11250,11253,11255,11258,11260],{"class":34,"line":35},[32,11249,6617],{"class":38},[32,11251,11252],{"class":217}," a",[32,11254,894],{"class":38},[32,11256,11257],{"class":41}," () ",[32,11259,5090],{"class":38},[32,11261,703],{"class":41},[32,11263,11264,11266,11268,11270,11272],{"class":34,"line":45},[32,11265,5194],{"class":41},[32,11267,5197],{"class":217},[32,11269,477],{"class":41},[32,11271,1083],{"class":351},[32,11273,230],{"class":41},[32,11275,11276],{"class":34,"line":51},[32,11277,7189],{"class":41},[15,11279,11281],{"id":11280},"一般函式的-this","一般函式的 this",[11,11283,11284,11285,11287],{},"一般函式的 ",[29,11286,1083],{}," 由函式呼叫方式決定。",[151,11289,11290],{"id":11290},"直接執行",[11,11292,11293,11294,11297],{},"直接執行時指向 ",[29,11295,11296],{},"window","（global）：",[22,11299,11301],{"className":24,"code":11300,"language":26,"meta":27,"style":27},"var name = 'hello';\n\nvar sayMyName = function () {\n  console.log(this.name);\n};\n\nsayMyName(); \u002F\u002F global.hello\n",[29,11302,11303,11316,11320,11333,11346,11350,11354],{"__ignoreMap":27},[32,11304,11305,11307,11309,11311,11314],{"class":34,"line":35},[32,11306,166],{"class":38},[32,11308,169],{"class":41},[32,11310,172],{"class":38},[32,11312,11313],{"class":175}," 'hello'",[32,11315,96],{"class":41},[32,11317,11318],{"class":34,"line":45},[32,11319,103],{"emptyLinePlaceholder":102},[32,11321,11322,11324,11327,11329,11331],{"class":34,"line":51},[32,11323,166],{"class":38},[32,11325,11326],{"class":217}," sayMyName",[32,11328,894],{"class":38},[32,11330,897],{"class":38},[32,11332,668],{"class":41},[32,11334,11335,11337,11339,11341,11343],{"class":34,"line":90},[32,11336,5194],{"class":41},[32,11338,5197],{"class":217},[32,11340,477],{"class":41},[32,11342,1083],{"class":351},[32,11344,11345],{"class":41},".name);\n",[32,11347,11348],{"class":34,"line":99},[32,11349,7189],{"class":41},[32,11351,11352],{"class":34,"line":106},[32,11353,103],{"emptyLinePlaceholder":102},[32,11355,11356,11359,11362],{"class":34,"line":114},[32,11357,11358],{"class":217},"sayMyName",[32,11360,11361],{"class":41},"(); ",[32,11363,11364],{"class":608},"\u002F\u002F global.hello\n",[151,11366,11367],{"id":11367},"作為物件的成員函式",[11,11369,11370],{},"作為物件的成員函式執行時，指向該物件：",[22,11372,11374],{"className":24,"code":11373,"language":26,"meta":27,"style":27},"var name = 'hello';\n\nvar sayMyName = function () {\n  console.log(this.name);\n};\n\nvar teacher = {\n  name: 'white',\n};\n\nteacher.sayMyName = sayMyName;\nteacher.sayMyName(); \u002F\u002F white\n",[29,11375,11376,11388,11392,11404,11416,11420,11424,11435,11445,11449,11453,11463],{"__ignoreMap":27},[32,11377,11378,11380,11382,11384,11386],{"class":34,"line":35},[32,11379,166],{"class":38},[32,11381,169],{"class":41},[32,11383,172],{"class":38},[32,11385,11313],{"class":175},[32,11387,96],{"class":41},[32,11389,11390],{"class":34,"line":45},[32,11391,103],{"emptyLinePlaceholder":102},[32,11393,11394,11396,11398,11400,11402],{"class":34,"line":51},[32,11395,166],{"class":38},[32,11397,11326],{"class":217},[32,11399,894],{"class":38},[32,11401,897],{"class":38},[32,11403,668],{"class":41},[32,11405,11406,11408,11410,11412,11414],{"class":34,"line":90},[32,11407,5194],{"class":41},[32,11409,5197],{"class":217},[32,11411,477],{"class":41},[32,11413,1083],{"class":351},[32,11415,11345],{"class":41},[32,11417,11418],{"class":34,"line":99},[32,11419,7189],{"class":41},[32,11421,11422],{"class":34,"line":106},[32,11423,103],{"emptyLinePlaceholder":102},[32,11425,11426,11428,11431,11433],{"class":34,"line":114},[32,11427,166],{"class":38},[32,11429,11430],{"class":41}," teacher ",[32,11432,172],{"class":38},[32,11434,703],{"class":41},[32,11436,11437,11440,11443],{"class":34,"line":120},[32,11438,11439],{"class":41},"  name: ",[32,11441,11442],{"class":175},"'white'",[32,11444,547],{"class":41},[32,11446,11447],{"class":34,"line":127},[32,11448,7189],{"class":41},[32,11450,11451],{"class":34,"line":132},[32,11452,103],{"emptyLinePlaceholder":102},[32,11454,11455,11458,11460],{"class":34,"line":141},[32,11456,11457],{"class":41},"teacher.sayMyName ",[32,11459,172],{"class":38},[32,11461,11462],{"class":41}," sayMyName;\n",[32,11464,11465,11468,11470,11472],{"class":34,"line":147},[32,11466,11467],{"class":41},"teacher.",[32,11469,11358],{"class":217},[32,11471,11361],{"class":41},[32,11473,11474],{"class":608},"\u002F\u002F white\n",[151,11476,11478],{"id":11477},"作為-dom-的監聽函式","作為 DOM 的監聽函式",[11,11480,11481],{},"作為 DOM 的監聽函式執行時，指向該 DOM：",[22,11483,11485],{"className":4038,"code":11484,"language":4034,"meta":27,"style":27},"\u003Cbutton id=\"btn\" name=\"God damn right\">Response\u003C\u002Fbutton>\n",[29,11486,11487],{"__ignoreMap":27},[32,11488,11489,11491,11494,11496,11498,11501,11503,11505,11508,11511,11513],{"class":34,"line":35},[32,11490,4046],{"class":41},[32,11492,11493],{"class":4049},"button",[32,11495,4054],{"class":4053},[32,11497,172],{"class":41},[32,11499,11500],{"class":175},"\"btn\"",[32,11502,5668],{"class":4053},[32,11504,172],{"class":41},[32,11506,11507],{"class":175},"\"God damn right\"",[32,11509,11510],{"class":41},">Response\u003C\u002F",[32,11512,11493],{"class":4049},[32,11514,4062],{"class":41},[22,11516,11518],{"className":24,"code":11517,"language":26,"meta":27,"style":27},"btn.addEventListener('click', sayMyName);\n",[29,11519,11520],{"__ignoreMap":27},[32,11521,11522,11524,11526,11528,11530],{"class":34,"line":35},[32,11523,11208],{"class":41},[32,11525,5517],{"class":217},[32,11527,477],{"class":41},[32,11529,11215],{"class":175},[32,11531,11532],{"class":41},", sayMyName);\n",[15,11534,2004],{"id":2003},[11,11536,11537,11538,11540,11541,11543],{},"箭頭函式的語法與 ",[29,11539,1083],{}," 規則沒有改變。需要依呼叫者取得 ",[29,11542,1083],{}," 的物件方法或 DOM 監聽器，仍要留意是否適合使用一般函式。",[368,11545,11546],{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}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 .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}",{"title":27,"searchDepth":51,"depth":51,"links":11548},[11549,11550,11551,11556],{"id":10938,"depth":45,"text":10938},{"id":11234,"depth":45,"text":11234},{"id":11280,"depth":45,"text":11281,"children":11552},[11553,11554,11555],{"id":11290,"depth":51,"text":11290},{"id":11367,"depth":51,"text":11367},{"id":11477,"depth":51,"text":11478},{"id":2003,"depth":45,"text":2004},"2020 年整理的箭頭函式簡寫方式與 this 綁定筆記。",{},"\u002Fposts\u002Fes6-arrow-functions","2020-02-06",[],{"title":10933,"description":11557},"posts\u002F20200206.es6-arrow-functions",[390,10827,11565,1083],"箭頭函式","ET-V7Wwr3RvHTnz33yXdaif4tVHMmUow5CFJvL-td8Y",{"id":11568,"title":11569,"body":11570,"category":378,"description":11592,"draft":380,"extension":381,"image":382,"meta":11593,"navigation":102,"path":11594,"publishedAt":11595,"relatedExperiments":11596,"seo":11597,"stem":11598,"tags":11599,"updatedAt":6716,"__hash__":11601},"posts\u002Fposts\u002F20200228.google-fonts-embed.md","Google Fonts 引用筆記",{"type":8,"value":11571,"toc":11589},[11572,11575,11584,11586],[11,11573,11574],{},"這邊記錄網頁需要字型時，可以從哪邊引用：",[4538,11576,11577],{},[4541,11578,11579],{},[412,11580,11583],{"href":11581,"rel":11582},"https:\u002F\u002Ffonts.google.com\u002F",[416],"Google Fonts",[15,11585,2004],{"id":2003},[11,11587,11588],{},"Google Fonts 現在仍可使用，但若在意載入效能與訪客請求會連到第三方網域，可以改成自架字型。相關做法整理在《現在引用網頁字型的做法》。",{"title":27,"searchDepth":51,"depth":51,"links":11590},[11591],{"id":2003,"depth":45,"text":2004},"2020 年記錄網頁需要字型時，可以從 Google Fonts 取得引用方式。",{},"\u002Fposts\u002Fgoogle-fonts-embed","2020-02-28",[],{"title":11569,"description":11592},"posts\u002F20200228.google-fonts-embed",[1757,11583,11600],"字型","vo42ZtA9vW9kyb7P771zblsxG2jjuM6gtyGqjWX-8Rw",{"id":11603,"title":11604,"body":11605,"category":378,"description":11641,"draft":380,"extension":381,"image":382,"meta":11642,"navigation":102,"path":11643,"publishedAt":11644,"relatedExperiments":11645,"seo":11646,"stem":11647,"tags":11648,"updatedAt":10829,"__hash__":11649},"posts\u002Fposts\u002F20200310.box-sizing-border-box.md","CSS box-sizing 筆記",{"type":8,"value":11606,"toc":11638},[11607,11612,11625,11631,11633],[11,11608,10721,11609,3775],{},[29,11610,11611],{},"box-sizing",[11,11613,11614,11617,11618,11621,11622,11624],{},[29,11615,11616],{},"box-sizing: border-box"," 可以把 padding 與 border 計入設定的 ",[29,11619,11620],{},"width","／",[29,11623,6762],{},"，避免元素的實際尺寸向外增加。",[11,11626,11627],{},[10742,11628],{"alt":11629,"src":11630},"兩個相同寬高、邊框與內距的方塊，未設定 box-sizing 的方塊較大，設定 border-box 的方塊維持 50px","\u002Fimages\u002Fposts\u002Fbox-sizing-border-box\u002F01.webp",[15,11632,2004],{"id":2003},[11,11634,11635,11637],{},[29,11636,11616],{}," 現在仍普遍使用，通常會連同偽元素一起設為全域規則。",{"title":27,"searchDepth":51,"depth":51,"links":11639},[11640],{"id":2003,"depth":45,"text":2004},"2020 年記錄 box-sizing 如何讓 padding 與 border 計入元素設定的寬高。",{},"\u002Fposts\u002Fbox-sizing-border-box","2020-03-10",[],{"title":11604,"description":11641},"posts\u002F20200310.box-sizing-border-box",[1757],"Hsa5G1waJHX12ORw72prX8uMREyZxjDlCeZ-YBv8OMM",{"id":11651,"title":11652,"body":11653,"category":378,"description":12307,"draft":380,"extension":381,"image":382,"meta":12308,"navigation":102,"path":12309,"publishedAt":11644,"relatedExperiments":12310,"seo":12311,"stem":12312,"tags":12313,"updatedAt":10829,"__hash__":12315},"posts\u002Fposts\u002F20200310.scss-icon-sprite-map.md","用 SCSS 的 Map 與 @each 統一管理背景圖示",{"type":8,"value":11654,"toc":12299},[11655,11662,11666,11799,11807,11811,11996,12016,12019,12033,12235,12243,12246,12253,12259,12261,12278,12280,12296],[11,11656,11657,11658,11661],{},"網站圖示如果是一批各自獨立的 PNG 檔案（而不是圖示字型或 SVG），每個圖示又要處理 hover／normal 兩種狀態，手動幫每一個圖示各寫一段 CSS 很容易漏東漏西。\n這篇整理當時用 SCSS 的 map 加 ",[29,11659,11660],{},"@each"," 迴圈批次產生的做法。",[15,11663,11665],{"id":11664},"用-map-存圖示路徑","用 map 存圖示路徑",[22,11667,11669],{"className":8735,"code":11668,"language":8737,"meta":27,"style":27},"$icon-name: 'bookmark', 'avatar', 'chat', 'page', 'stars', 'focus';\n\n$iconImageGroup: (\n  'bookmark-hover': '..\u002F..\u002Fassets\u002F_img\u002F_icon\u002Ficon-bookmark-hover.png',\n  'bookmark-normal': '..\u002F..\u002Fassets\u002F_img\u002F_icon\u002Ficon-bookmark-normal.png',\n  'stars-hover': '..\u002F..\u002Fassets\u002F_img\u002F_icon\u002Ficon-stars-hover.png',\n  'stars-normal': '..\u002F..\u002Fassets\u002F_img\u002F_icon\u002Ficon-stars-normal.png',\n  'focus-hover': '\u002F_img\u002F_icon\u002Ficon_focus_hover.png',\n  'focus-normal': '\u002F_img\u002F_icon\u002Ficon_focus_normal.png',\n);\n",[29,11670,11671,11708,11712,11720,11732,11744,11756,11768,11780,11792],{"__ignoreMap":27},[32,11672,11673,11676,11678,11681,11683,11686,11688,11691,11693,11696,11698,11701,11703,11706],{"class":34,"line":35},[32,11674,11675],{"class":903},"$icon-name",[32,11677,5269],{"class":5049},[32,11679,11680],{"class":175},"'bookmark'",[32,11682,635],{"class":5049},[32,11684,11685],{"class":175},"'avatar'",[32,11687,635],{"class":5049},[32,11689,11690],{"class":175},"'chat'",[32,11692,635],{"class":5049},[32,11694,11695],{"class":175},"'page'",[32,11697,635],{"class":5049},[32,11699,11700],{"class":175},"'stars'",[32,11702,635],{"class":5049},[32,11704,11705],{"class":175},"'focus'",[32,11707,96],{"class":41},[32,11709,11710],{"class":34,"line":45},[32,11711,103],{"emptyLinePlaceholder":102},[32,11713,11714,11717],{"class":34,"line":51},[32,11715,11716],{"class":903},"$iconImageGroup",[32,11718,11719],{"class":5049},": (\n",[32,11721,11722,11725,11727,11730],{"class":34,"line":90},[32,11723,11724],{"class":175},"  'bookmark-hover'",[32,11726,5269],{"class":5049},[32,11728,11729],{"class":175},"'..\u002F..\u002Fassets\u002F_img\u002F_icon\u002Ficon-bookmark-hover.png'",[32,11731,547],{"class":5049},[32,11733,11734,11737,11739,11742],{"class":34,"line":99},[32,11735,11736],{"class":175},"  'bookmark-normal'",[32,11738,5269],{"class":5049},[32,11740,11741],{"class":175},"'..\u002F..\u002Fassets\u002F_img\u002F_icon\u002Ficon-bookmark-normal.png'",[32,11743,547],{"class":5049},[32,11745,11746,11749,11751,11754],{"class":34,"line":106},[32,11747,11748],{"class":175},"  'stars-hover'",[32,11750,5269],{"class":5049},[32,11752,11753],{"class":175},"'..\u002F..\u002Fassets\u002F_img\u002F_icon\u002Ficon-stars-hover.png'",[32,11755,547],{"class":5049},[32,11757,11758,11761,11763,11766],{"class":34,"line":114},[32,11759,11760],{"class":175},"  'stars-normal'",[32,11762,5269],{"class":5049},[32,11764,11765],{"class":175},"'..\u002F..\u002Fassets\u002F_img\u002F_icon\u002Ficon-stars-normal.png'",[32,11767,547],{"class":5049},[32,11769,11770,11773,11775,11778],{"class":34,"line":120},[32,11771,11772],{"class":175},"  'focus-hover'",[32,11774,5269],{"class":5049},[32,11776,11777],{"class":175},"'\u002F_img\u002F_icon\u002Ficon_focus_hover.png'",[32,11779,547],{"class":5049},[32,11781,11782,11785,11787,11790],{"class":34,"line":127},[32,11783,11784],{"class":175},"  'focus-normal'",[32,11786,5269],{"class":5049},[32,11788,11789],{"class":175},"'\u002F_img\u002F_icon\u002Ficon_focus_normal.png'",[32,11791,547],{"class":5049},[32,11793,11794,11797],{"class":34,"line":132},[32,11795,11796],{"class":5049},")",[32,11798,96],{"class":41},[11,11800,11801,11803,11804,11806],{},[29,11802,11675],{}," 是一份圖示名稱清單，",[29,11805,11716],{}," 是一個 map（SCSS 的鍵值對結構），把每個圖示 hover／normal 兩種狀態各自對應到一個圖檔路徑。",[15,11808,11810],{"id":11809},"用-each-批次產生-class","用 @each 批次產生 class",[22,11812,11814],{"className":8735,"code":11813,"language":8737,"meta":27,"style":27},"@each $name in $icon-name {\n  &.icon-#{$name} {\n    width: 26px;\n    height: 24px;\n    padding: 0px !important;\n    background: url(map-get($iconImageGroup, $name + '-normal'));\n    background-repeat: no-repeat;\n    background-position: center center;\n    transition: 0.3s;\n    margin-right: 2px;\n    display: inline-block;\n  }\n}\n",[29,11815,11816,11830,11843,11858,11872,11889,11920,11932,11947,11962,11976,11988,11992],{"__ignoreMap":27},[32,11817,11818,11820,11823,11825,11828],{"class":34,"line":35},[32,11819,11660],{"class":38},[32,11821,11822],{"class":903}," $name",[32,11824,5527],{"class":38},[32,11826,11827],{"class":903}," $icon-name",[32,11829,703],{"class":41},[32,11831,11832,11835,11838,11841],{"class":34,"line":45},[32,11833,11834],{"class":4049},"  &",[32,11836,11837],{"class":4053},".icon-",[32,11839,11840],{"class":903},"#{$name}",[32,11842,703],{"class":41},[32,11844,11845,11848,11850,11853,11856],{"class":34,"line":51},[32,11846,11847],{"class":351},"    width",[32,11849,5269],{"class":41},[32,11851,11852],{"class":351},"26",[32,11854,11855],{"class":38},"px",[32,11857,96],{"class":41},[32,11859,11860,11863,11865,11868,11870],{"class":34,"line":90},[32,11861,11862],{"class":351},"    height",[32,11864,5269],{"class":41},[32,11866,11867],{"class":351},"24",[32,11869,11855],{"class":38},[32,11871,96],{"class":41},[32,11873,11874,11877,11879,11882,11884,11887],{"class":34,"line":99},[32,11875,11876],{"class":351},"    padding",[32,11878,5269],{"class":41},[32,11880,11881],{"class":351},"0",[32,11883,11855],{"class":38},[32,11885,11886],{"class":38}," !important",[32,11888,96],{"class":41},[32,11890,11891,11894,11896,11899,11901,11904,11906,11908,11910,11913,11915,11918],{"class":34,"line":106},[32,11892,11893],{"class":351},"    background",[32,11895,5269],{"class":41},[32,11897,11898],{"class":351},"url",[32,11900,477],{"class":41},[32,11902,11903],{"class":351},"map-get",[32,11905,477],{"class":41},[32,11907,11716],{"class":903},[32,11909,635],{"class":41},[32,11911,11912],{"class":903},"$name",[32,11914,1799],{"class":38},[32,11916,11917],{"class":175}," '-normal'",[32,11919,8328],{"class":41},[32,11921,11922,11925,11927,11930],{"class":34,"line":114},[32,11923,11924],{"class":351},"    background-repeat",[32,11926,5269],{"class":41},[32,11928,11929],{"class":351},"no-repeat",[32,11931,96],{"class":41},[32,11933,11934,11937,11939,11942,11945],{"class":34,"line":120},[32,11935,11936],{"class":351},"    background-position",[32,11938,5269],{"class":41},[32,11940,11941],{"class":351},"center",[32,11943,11944],{"class":351}," center",[32,11946,96],{"class":41},[32,11948,11949,11952,11954,11957,11960],{"class":34,"line":127},[32,11950,11951],{"class":351},"    transition",[32,11953,5269],{"class":41},[32,11955,11956],{"class":351},"0.3",[32,11958,11959],{"class":38},"s",[32,11961,96],{"class":41},[32,11963,11964,11967,11969,11972,11974],{"class":34,"line":132},[32,11965,11966],{"class":351},"    margin-right",[32,11968,5269],{"class":41},[32,11970,11971],{"class":351},"2",[32,11973,11855],{"class":38},[32,11975,96],{"class":41},[32,11977,11978,11981,11983,11986],{"class":34,"line":141},[32,11979,11980],{"class":351},"    display",[32,11982,5269],{"class":41},[32,11984,11985],{"class":351},"inline-block",[32,11987,96],{"class":41},[32,11989,11990],{"class":34,"line":147},[32,11991,1203],{"class":41},[32,11993,11994],{"class":34,"line":275},[32,11995,54],{"class":41},[11,11997,11998,12001,12002,12004,12005,3750,12008,12011,12012,12015],{},[29,11999,12000],{},"@each $name in $icon-name"," 會把清單裡的每個名稱輪流帶入迴圈，",[29,12003,11840],{}," 是字串插值，讓 class 名稱依名稱動態產生（",[29,12006,12007],{},".icon-bookmark",[29,12009,12010],{},".icon-avatar","……），",[29,12013,12014],{},"map-get()"," 則依名稱去 map 裡查對應的圖檔路徑。\n六個圖示的樣式規則就這樣一次產生完，之後要加第七個圖示，只需要在兩份清單裡各加一筆。",[15,12017,12018],{"id":12018},"依名稱做例外處理",[11,12020,12021,12022,11621,12025,12028,12029,12032],{},"不是每個圖示都遵循同一套規則，某些圖示需要額外的狀態 class（例如 ",[29,12023,12024],{},"bookmark",[29,12026,12027],{},"unbookmark"," 兩種狀態各自的樣式），可以用 ",[29,12030,12031],{},"@if"," 針對特定名稱加規則：",[22,12034,12036],{"className":8735,"code":12035,"language":8737,"meta":27,"style":27},"@each $name in $icon-name {\n  &.icon-#{$name} {\n    \u002F\u002F 通用規則...\n\n    @if ($name == 'bookmark') {\n      &.bookmark {\n        cursor: pointer;\n        background: url(map-get($iconImageGroup, 'bookmark-hover'));\n        width: 22px;\n        height: 32px;\n      }\n      &.unbookmark {\n        width: 22px;\n        height: 32px;\n        background: url(map-get($iconImageGroup, 'bookmark-normal'));\n      }\n    }\n  }\n}\n",[29,12037,12038,12050,12060,12065,12069,12085,12095,12107,12131,12145,12159,12163,12172,12184,12196,12219,12223,12227,12231],{"__ignoreMap":27},[32,12039,12040,12042,12044,12046,12048],{"class":34,"line":35},[32,12041,11660],{"class":38},[32,12043,11822],{"class":903},[32,12045,5527],{"class":38},[32,12047,11827],{"class":903},[32,12049,703],{"class":41},[32,12051,12052,12054,12056,12058],{"class":34,"line":45},[32,12053,11834],{"class":4049},[32,12055,11837],{"class":4053},[32,12057,11840],{"class":903},[32,12059,703],{"class":41},[32,12061,12062],{"class":34,"line":51},[32,12063,12064],{"class":608},"    \u002F\u002F 通用規則...\n",[32,12066,12067],{"class":34,"line":90},[32,12068,103],{"emptyLinePlaceholder":102},[32,12070,12071,12074,12076,12078,12081,12083],{"class":34,"line":99},[32,12072,12073],{"class":38},"    @if",[32,12075,900],{"class":41},[32,12077,11912],{"class":903},[32,12079,12080],{"class":41}," == ",[32,12082,11680],{"class":175},[32,12084,520],{"class":41},[32,12086,12087,12090,12093],{"class":34,"line":106},[32,12088,12089],{"class":4049},"      &",[32,12091,12092],{"class":4053},".bookmark",[32,12094,703],{"class":41},[32,12096,12097,12100,12102,12105],{"class":34,"line":114},[32,12098,12099],{"class":4049},"        cursor",[32,12101,5269],{"class":41},[32,12103,12104],{"class":351},"pointer",[32,12106,96],{"class":41},[32,12108,12109,12112,12114,12116,12118,12120,12122,12124,12126,12129],{"class":34,"line":120},[32,12110,12111],{"class":351},"        background",[32,12113,5269],{"class":41},[32,12115,11898],{"class":351},[32,12117,477],{"class":41},[32,12119,11903],{"class":351},[32,12121,477],{"class":41},[32,12123,11716],{"class":903},[32,12125,635],{"class":41},[32,12127,12128],{"class":175},"'bookmark-hover'",[32,12130,8328],{"class":41},[32,12132,12133,12136,12138,12141,12143],{"class":34,"line":127},[32,12134,12135],{"class":351},"        width",[32,12137,5269],{"class":41},[32,12139,12140],{"class":351},"22",[32,12142,11855],{"class":38},[32,12144,96],{"class":41},[32,12146,12147,12150,12152,12155,12157],{"class":34,"line":132},[32,12148,12149],{"class":351},"        height",[32,12151,5269],{"class":41},[32,12153,12154],{"class":351},"32",[32,12156,11855],{"class":38},[32,12158,96],{"class":41},[32,12160,12161],{"class":34,"line":141},[32,12162,1193],{"class":41},[32,12164,12165,12167,12170],{"class":34,"line":147},[32,12166,12089],{"class":4049},[32,12168,12169],{"class":4053},".unbookmark",[32,12171,703],{"class":41},[32,12173,12174,12176,12178,12180,12182],{"class":34,"line":275},[32,12175,12135],{"class":351},[32,12177,5269],{"class":41},[32,12179,12140],{"class":351},[32,12181,11855],{"class":38},[32,12183,96],{"class":41},[32,12185,12186,12188,12190,12192,12194],{"class":34,"line":282},[32,12187,12149],{"class":351},[32,12189,5269],{"class":41},[32,12191,12154],{"class":351},[32,12193,11855],{"class":38},[32,12195,96],{"class":41},[32,12197,12198,12200,12202,12204,12206,12208,12210,12212,12214,12217],{"class":34,"line":295},[32,12199,12111],{"class":351},[32,12201,5269],{"class":41},[32,12203,11898],{"class":351},[32,12205,477],{"class":41},[32,12207,11903],{"class":351},[32,12209,477],{"class":41},[32,12211,11716],{"class":903},[32,12213,635],{"class":41},[32,12215,12216],{"class":175},"'bookmark-normal'",[32,12218,8328],{"class":41},[32,12220,12221],{"class":34,"line":605},[32,12222,1193],{"class":41},[32,12224,12225],{"class":34,"line":612},[32,12226,5591],{"class":41},[32,12228,12229],{"class":34,"line":617},[32,12230,1203],{"class":41},[32,12232,12233],{"class":34,"line":644},[32,12234,54],{"class":41},[11,12236,12237,12239,12240,12242],{},[29,12238,12031],{}," 在迴圈內部依當下的 ",[29,12241,11912],{}," 值判斷，只有名稱符合條件時才會加上額外的巢狀規則，其餘圖示不受影響。",[15,12244,12245],{"id":12245},"這個做法的代價",[11,12247,12248,12252],{},[12249,12250,12251],"strong",{},"每個圖示、每種狀態都是一個獨立的圖檔請求","。hover 狀態要切換圖片，代表瀏覽器要先把 hover 版本的圖也下載下來（或是遇到滑鼠移上去才臨時載入，反而有延遲閃爍的問題），圖示數量一多，請求數跟著往上疊。",[11,12254,12255,12258],{},[12249,12256,12257],{},"顏色調整要重新出圖","。網站要換色系、或同一個圖示要在不同背景色上呈現不同顏色，PNG 圖示沒辦法用 CSS 調色，只能請設計重新出一版圖檔。",[15,12260,2004],{"id":2003},[11,12262,12263,12266,12267,12269,12270,12273,12274,12277],{},[12249,12264,12265],{},"這套「用資料結構驅動批次產生樣式」的思路本身沒有過時","，SCSS 的 map／",[29,12268,11660],{}," 現在同樣好用。真正被取代的是",[12249,12271,12272],{},"用一堆獨立 PNG 檔案管理圖示","這件事——現在多半用 SVG 圖示元件，一份圖示可以用 ",[29,12275,12276],{},"currentColor"," 直接吃 CSS 的文字顏色，不需要為了換色重新出圖，也不用為了 hover 狀態多準備一張圖。整理在《SVG 圖示元件：取代背景圖示與圖示字型》。",[15,12279,4536],{"id":4536},[4538,12281,12282,12289],{},[4541,12283,12284],{},[412,12285,12288],{"href":12286,"rel":12287},"https:\u002F\u002Fsass-lang.com\u002Fdocumentation\u002Fvalues\u002Fmaps\u002F",[416],"Sass 官方文件：Maps",[4541,12290,12291],{},[412,12292,12295],{"href":12293,"rel":12294},"https:\u002F\u002Fsass-lang.com\u002Fdocumentation\u002Fat-rules\u002Fcontrol\u002Feach\u002F",[416],"Sass 官方文件：@each",[368,12297,12298],{},"html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}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 .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}",{"title":27,"searchDepth":51,"depth":51,"links":12300},[12301,12302,12303,12304,12305,12306],{"id":11664,"depth":45,"text":11665},{"id":11809,"depth":45,"text":11810},{"id":12018,"depth":45,"text":12018},{"id":12245,"depth":45,"text":12245},{"id":2003,"depth":45,"text":2004},{"id":4536,"depth":45,"text":4536},"整理用 SCSS 的 map 資料結構搭配 @each 迴圈，批次產生一組背景圖示 class 的做法，同一份清單同時管理 hover／normal 兩種狀態各自對應的圖檔路徑。這個做法現在多半被 SVG 圖示元件取代，整理在新的一篇。",{},"\u002Fposts\u002Fscss-icon-sprite-map",[],{"title":11652,"description":12307},"posts\u002F20200310.scss-icon-sprite-map",[12314,1757,4847],"SCSS","qNGNDDRHUx-7vcZ7tskko8SwRuReYeSbdJwM-WwT7Vk",{"id":12317,"title":12318,"body":12319,"category":378,"description":12537,"draft":380,"extension":381,"image":382,"meta":12538,"navigation":102,"path":12539,"publishedAt":12540,"relatedExperiments":12541,"seo":12542,"stem":12543,"tags":12544,"updatedAt":10829,"__hash__":12545},"posts\u002Fposts\u002F20200311.html5-meta-basics.md","HTML5 基本 meta 資訊筆記",{"type":8,"value":12320,"toc":12534},[12321,12516,12518,12531],[22,12322,12324],{"className":4038,"code":12323,"language":4034,"meta":27,"style":27},"\u003C!DOCTYPE html>\n\u003Chtml lang=\"en\">\n\u003Chead>\n  \u003Cmeta charset=\"UTF-8\">\n  \u003Cmeta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"> \u003C!-- 行動裝置必須加 -->\n  \u003Cmeta http-equiv=\"X-UA-Compatible\" content=\"ie=edge\"> \u003C!-- 將瀏覽器更新到 IE Edge -->\n  \u003Ctitle>Yahoo 新聞\u003C\u002Ftitle>\n\u003C\u002Fhead>\n\u003Cbody>\n  \u003Ch1>標題\u003C\u002Fh1>\n  \u003Cp>段落段落\u003C\u002Fp>\n  \u003Cp>段落段落\u003C\u002Fp>\n\u003C\u002Fbody>\n\u003C\u002Fhtml>\n",[29,12325,12326,12339,12354,12363,12379,12405,12431,12445,12453,12461,12475,12488,12500,12508],{"__ignoreMap":27},[32,12327,12328,12331,12334,12337],{"class":34,"line":35},[32,12329,12330],{"class":41},"\u003C!",[32,12332,12333],{"class":4049},"DOCTYPE",[32,12335,12336],{"class":4053}," html",[32,12338,4062],{"class":41},[32,12340,12341,12343,12345,12347,12349,12352],{"class":34,"line":45},[32,12342,4046],{"class":41},[32,12344,4034],{"class":4049},[32,12346,8677],{"class":4053},[32,12348,172],{"class":41},[32,12350,12351],{"class":175},"\"en\"",[32,12353,4062],{"class":41},[32,12355,12356,12358,12361],{"class":34,"line":51},[32,12357,4046],{"class":41},[32,12359,12360],{"class":4049},"head",[32,12362,4062],{"class":41},[32,12364,12365,12367,12369,12372,12374,12377],{"class":34,"line":90},[32,12366,4067],{"class":41},[32,12368,5665],{"class":4049},[32,12370,12371],{"class":4053}," charset",[32,12373,172],{"class":41},[32,12375,12376],{"class":175},"\"UTF-8\"",[32,12378,4062],{"class":41},[32,12380,12381,12383,12385,12387,12389,12392,12394,12396,12399,12402],{"class":34,"line":99},[32,12382,4067],{"class":41},[32,12384,5665],{"class":4049},[32,12386,5668],{"class":4053},[32,12388,172],{"class":41},[32,12390,12391],{"class":175},"\"viewport\"",[32,12393,5676],{"class":4053},[32,12395,172],{"class":41},[32,12397,12398],{"class":175},"\"width=device-width,initial-scale=1.0\"",[32,12400,12401],{"class":41},"> ",[32,12403,12404],{"class":608},"\u003C!-- 行動裝置必須加 -->\n",[32,12406,12407,12409,12411,12414,12416,12419,12421,12423,12426,12428],{"class":34,"line":106},[32,12408,4067],{"class":41},[32,12410,5665],{"class":4049},[32,12412,12413],{"class":4053}," http-equiv",[32,12415,172],{"class":41},[32,12417,12418],{"class":175},"\"X-UA-Compatible\"",[32,12420,5676],{"class":4053},[32,12422,172],{"class":41},[32,12424,12425],{"class":175},"\"ie=edge\"",[32,12427,12401],{"class":41},[32,12429,12430],{"class":608},"\u003C!-- 將瀏覽器更新到 IE Edge -->\n",[32,12432,12433,12435,12438,12441,12443],{"class":34,"line":114},[32,12434,4067],{"class":41},[32,12436,12437],{"class":4049},"title",[32,12439,12440],{"class":41},">Yahoo 新聞\u003C\u002F",[32,12442,12437],{"class":4049},[32,12444,4062],{"class":41},[32,12446,12447,12449,12451],{"class":34,"line":120},[32,12448,4105],{"class":41},[32,12450,12360],{"class":4049},[32,12452,4062],{"class":41},[32,12454,12455,12457,12459],{"class":34,"line":127},[32,12456,4046],{"class":41},[32,12458,5495],{"class":4049},[32,12460,4062],{"class":41},[32,12462,12463,12465,12468,12471,12473],{"class":34,"line":132},[32,12464,4067],{"class":41},[32,12466,12467],{"class":4049},"h1",[32,12469,12470],{"class":41},">標題\u003C\u002F",[32,12472,12467],{"class":4049},[32,12474,4062],{"class":41},[32,12476,12477,12479,12481,12484,12486],{"class":34,"line":141},[32,12478,4067],{"class":41},[32,12480,11],{"class":4049},[32,12482,12483],{"class":41},">段落段落\u003C\u002F",[32,12485,11],{"class":4049},[32,12487,4062],{"class":41},[32,12489,12490,12492,12494,12496,12498],{"class":34,"line":147},[32,12491,4067],{"class":41},[32,12493,11],{"class":4049},[32,12495,12483],{"class":41},[32,12497,11],{"class":4049},[32,12499,4062],{"class":41},[32,12501,12502,12504,12506],{"class":34,"line":275},[32,12503,4105],{"class":41},[32,12505,5495],{"class":4049},[32,12507,4062],{"class":41},[32,12509,12510,12512,12514],{"class":34,"line":282},[32,12511,4105],{"class":41},[32,12513,4034],{"class":4049},[32,12515,4062],{"class":41},[15,12517,2004],{"id":2003},[11,12519,12520,7090,12523,12526,12527,12530],{},[29,12521,12522],{},"charset",[29,12524,12525],{},"viewport"," 現在仍是基本設定；",[29,12528,12529],{},"X-UA-Compatible"," 是給 Internet Explorer 的舊設定，新專案已不需要。現行頁面資訊可參考《現在的 HTML meta 標籤怎麼寫》。",[368,12532,12533],{},"html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}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);}",{"title":27,"searchDepth":51,"depth":51,"links":12535},[12536],{"id":2003,"depth":45,"text":2004},"2020 年留下的 HTML5 頁面骨架與基本 meta 標籤。",{},"\u002Fposts\u002Fhtml5-meta-basics","2020-03-11",[],{"title":12318,"description":12537},"posts\u002F20200311.html5-meta-basics",[4035,5665],"TcHYP9NY4iyRlNma-aQwG2M9mErLhsU1swF2Gz9k80w",{"id":12547,"title":12548,"body":12549,"category":378,"description":12634,"draft":380,"extension":381,"image":382,"meta":12635,"navigation":102,"path":12636,"publishedAt":12637,"relatedExperiments":12638,"seo":12639,"stem":12640,"tags":12641,"updatedAt":10829,"__hash__":12643},"posts\u002Fposts\u002F20200313.bootstrap-modal-scrollbar-fix.md","Bootstrap Modal 捲軸變動修正",{"type":8,"value":12550,"toc":12631},[12551,12558,12561,12619,12621,12628],[11,12552,12553,12554,12557],{},"使用 Bootstrap 的 Modal 元件時，如果頁面 Header 設為 ",[29,12555,12556],{},"position: fixed","，會發現開啟 Modal 時捲軸與版面發生變動。",[11,12559,12560],{},"當時使用以下 CSS 修正：",[22,12562,12564],{"className":5412,"code":12563,"language":1140,"meta":27,"style":27},"body,\nbody.swal2-shown,\nbody.modal-open {\n  overflow: inherit;\n  padding-right: inherit !important;\n}\n",[29,12565,12566,12572,12581,12590,12602,12615],{"__ignoreMap":27},[32,12567,12568,12570],{"class":34,"line":35},[32,12569,5495],{"class":4049},[32,12571,547],{"class":41},[32,12573,12574,12576,12579],{"class":34,"line":45},[32,12575,5495],{"class":4049},[32,12577,12578],{"class":4053},".swal2-shown",[32,12580,547],{"class":41},[32,12582,12583,12585,12588],{"class":34,"line":51},[32,12584,5495],{"class":4049},[32,12586,12587],{"class":4053},".modal-open",[32,12589,703],{"class":41},[32,12591,12592,12595,12597,12600],{"class":34,"line":90},[32,12593,12594],{"class":351},"  overflow",[32,12596,5269],{"class":41},[32,12598,12599],{"class":351},"inherit",[32,12601,96],{"class":41},[32,12603,12604,12607,12609,12611,12613],{"class":34,"line":99},[32,12605,12606],{"class":351},"  padding-right",[32,12608,5269],{"class":41},[32,12610,12599],{"class":351},[32,12612,11886],{"class":38},[32,12614,96],{"class":41},[32,12616,12617],{"class":34,"line":106},[32,12618,54],{"class":41},[15,12620,2004],{"id":2003},[11,12622,12623,12624,12627],{},"這段覆蓋規則會取消套件原本的捲動鎖定與捲軸寬度補償，只適合當時的版面情境。新專案若不依賴 Bootstrap Modal，也可以考慮瀏覽器原生的 ",[29,12625,12626],{},"dialog"," 元素。",[368,12629,12630],{},"html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}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);}",{"title":27,"searchDepth":51,"depth":51,"links":12632},[12633],{"id":2003,"depth":45,"text":2004},"2020 年記錄 Bootstrap Modal 與 fixed Header 一起使用時的捲軸修正方式。",{},"\u002Fposts\u002Fbootstrap-modal-scrollbar-fix","2020-03-13",[],{"title":12548,"description":12634},"posts\u002F20200313.bootstrap-modal-scrollbar-fix",[10517,1757,12642],"Modal","EVd-qtpdp1Yhv-cXjxVDZ4ceK7-IiK6TUgid-9-YIgU",{"id":12645,"title":12646,"body":12647,"category":4012,"description":12803,"draft":380,"extension":381,"image":382,"meta":12804,"navigation":102,"path":12805,"publishedAt":12806,"relatedExperiments":12807,"seo":12808,"stem":12809,"tags":12810,"updatedAt":10829,"__hash__":12813},"posts\u002Fposts\u002F20200315.github-first-push.md","專案上傳 GitHub 前的初始化流程",{"type":8,"value":12648,"toc":12797},[12649,12652,12656,12659,12665,12668,12674,12680,12683,12686,12692,12695,12705,12711,12714,12734,12737,12743,12747,12750,12756,12759,12765,12772,12778,12780,12794],[11,12650,12651],{},"這邊記錄專案上傳 GitHub 前會先做的動作。",[15,12653,12655],{"id":12654},"在-github-建立專案","在 GitHub 建立專案",[11,12657,12658],{},"進到 GitHub，點選右上角新增專案。",[11,12660,12661],{},[10742,12662],{"alt":12663,"src":12664},"GitHub 右上角的新增選單，New repository 選項用紅框標示","\u002Fimages\u002Fposts\u002Fgithub-first-push\u002F01.png",[11,12666,12667],{},"填寫專案名稱並選擇是否公開。",[11,12669,12670],{},[10742,12671],{"alt":12672,"src":12673},"GitHub 的 Create a new repository 表單，可填寫專案名稱並選擇 Public 或 Private","\u002Fimages\u002Fposts\u002Fgithub-first-push\u002F02.png",[11,12675,12676],{},[10742,12677],{"alt":12678,"src":12679},"GitHub 建立空倉庫後顯示的 command line 初始化指令","\u002Fimages\u002Fposts\u002Fgithub-first-push\u002F03.png",[15,12681,12682],{"id":12682},"本機初始化",[11,12684,12685],{},"開啟 PowerShell，將路徑切換到要上傳的專案並查看檔案清單。",[11,12687,12688],{},[10742,12689],{"alt":12690,"src":12691},"PowerShell 使用 ls 顯示專案資料夾中的檔案與目錄","\u002Fimages\u002Fposts\u002Fgithub-first-push\u002F04.png",[11,12693,12694],{},"初始化 Git：",[22,12696,12697],{"className":2057,"code":2381,"language":2059,"meta":27,"style":27},[29,12698,12699],{"__ignoreMap":27},[32,12700,12701,12703],{"class":34,"line":35},[32,12702,2126],{"class":2066},[32,12704,2390],{"class":175},[11,12706,12707],{},[10742,12708],{"alt":12709,"src":12710},"PowerShell 執行 git init，顯示 Initialized empty Git repository","\u002Fimages\u002Fposts\u002Fgithub-first-push\u002F05.png",[11,12712,12713],{},"告訴本機資料夾要連到哪個 GitHub 倉庫：",[22,12715,12717],{"className":2057,"code":12716,"language":2059,"meta":27,"style":27},"git remote add origin https:\u002F\u002Fgithub.com\u002F帳號\u002F專案名稱.git\n",[29,12718,12719],{"__ignoreMap":27},[32,12720,12721,12723,12726,12728,12731],{"class":34,"line":35},[32,12722,2126],{"class":2066},[32,12724,12725],{"class":175}," remote",[32,12727,2493],{"class":175},[32,12729,12730],{"class":175}," origin",[32,12732,12733],{"class":175}," https:\u002F\u002Fgithub.com\u002F帳號\u002F專案名稱.git\n",[11,12735,12736],{},"沒有任何錯誤或其他訊息出現，就代表設定成功。",[11,12738,12739],{},[10742,12740],{"alt":12741,"src":12742},"PowerShell 執行 git remote add origin，成功時沒有其他訊息","\u002Fimages\u002Fposts\u002Fgithub-first-push\u002F06.png",[15,12744,12746],{"id":12745},"使用-github-desktop","使用 GitHub Desktop",[11,12748,12749],{},"開啟 GitHub Desktop。",[11,12751,12752],{},[10742,12753],{"alt":12754,"src":12755},"GitHub Desktop 的 File 選單，Add local repository 選項被選取","\u002Fimages\u002Fposts\u002Fgithub-first-push\u002F07.png",[11,12757,12758],{},"選擇已建立、準備上傳的專案。",[11,12760,12761],{},[10742,12762],{"alt":12763,"src":12764},"GitHub Desktop 的 Add local repository 視窗，可選擇本機專案路徑","\u002Fimages\u002Fposts\u002Fgithub-first-push\u002F08.png",[11,12766,12767,12768,12771],{},"新增 ",[29,12769,12770],{},"CHANGELOG.md","，填寫 commit 訊息後提交並 push。",[11,12773,12774],{},[10742,12775],{"alt":12776,"src":12777},"GitHub Desktop 顯示專案變更，提交訊息欄位填寫初始化 Laravel 專案","\u002Fimages\u002Fposts\u002Fgithub-first-push\u002F09.png",[15,12779,2004],{"id":2003},[11,12781,12782,12783,12786,12787,12789,12790,12793],{},"初始化、設定 remote、commit 與 push 的流程沒有改變；現在 GitHub 新倉庫的預設分支通常是 ",[29,12784,12785],{},"main","，舊截圖則仍顯示 ",[29,12788,3753],{},"。常用指令可參考《",[412,12791,12792],{"href":4015},"Git 常用指令整理","》。",[368,12795,12796],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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);}",{"title":27,"searchDepth":51,"depth":51,"links":12798},[12799,12800,12801,12802],{"id":12654,"depth":45,"text":12655},{"id":12682,"depth":45,"text":12682},{"id":12745,"depth":45,"text":12746},{"id":2003,"depth":45,"text":2004},"2020 年記錄建立 GitHub 倉庫、本機初始化並用 GitHub Desktop 完成第一次提交的流程。",{},"\u002Fposts\u002Fgithub-first-push","2020-03-15",[],{"title":12646,"description":12803},"posts\u002F20200315.github-first-push",[4021,12811,12812],"GitHub","GitHub Desktop","w1rx37B0l7tYN3oI2p6GNhHrDvSJgFmqR0LK532n4fk",{"id":12815,"title":12816,"body":12817,"category":378,"description":12881,"draft":380,"extension":381,"image":382,"meta":12882,"navigation":102,"path":12883,"publishedAt":12884,"relatedExperiments":12885,"seo":12886,"stem":12887,"tags":12888,"updatedAt":10829,"__hash__":12889},"posts\u002Fposts\u002F20200316.css-reset-normalize.md","CSS Reset 與 Normalize.css 筆記",{"type":8,"value":12818,"toc":12875},[12819,12822,12826,12838,12842,12851,12854,12861,12867,12869],[11,12820,12821],{},"開始製作專案時，習慣先將瀏覽器的預設樣式清空，以免撰寫 CSS 時出現不一致。\n清空方式很多，也可以自己手寫，這邊記錄當時常用的兩種選擇。",[15,12823,12825],{"id":12824},"reset-css","Reset CSS",[4538,12827,12828,12831],{},[4541,12829,12830],{},"會將不同瀏覽器的預設樣式清空。",[4541,12832,12833],{},[412,12834,12837],{"href":12835,"rel":12836},"https:\u002F\u002Fmeyerweb.com\u002Feric\u002Ftools\u002Fcss\u002Freset\u002F",[416],"Eric Meyer 的 Reset CSS",[15,12839,12841],{"id":12840},"normalizecss","Normalize.css",[4538,12843,12844],{},[4541,12845,12846,12847],{},"會保留部分預設樣式，並清除瀏覽器之間多餘或不一致的設定。當時較常使用這一套，Bootstrap 也有採用。\n",[412,12848,12841],{"href":12849,"rel":12850},"https:\u002F\u002Fnecolas.github.io\u002Fnormalize.css\u002F",[416],[15,12852,12853],{"id":12853},"使用方式",[11,12855,12856,12857,12860],{},"選擇一套習慣的方案，下載後可以直接把 CSS 檔改成 ",[29,12858,12859],{},"_xxx.scss","，再匯入主要的 SCSS 檔案。",[11,12862,12863],{},[10742,12864],{"alt":12865,"src":12866},"SCSS 入口檔使用 import 引入 normalize、animate、變數與 mixin 等檔案","\u002Fimages\u002Fposts\u002Fcss-reset-normalize\u002F01.webp",[15,12868,2004],{"id":2003},[11,12870,12871,12872,12874],{},"這兩套方案仍可使用；現代瀏覽器也可以搭配更精簡的 reset，整理在《現在的 CSS Reset》。Sass 的 ",[29,12873,8744],{}," 已有新的模組寫法，這篇保留當時的專案結構。",{"title":27,"searchDepth":51,"depth":51,"links":12876},[12877,12878,12879,12880],{"id":12824,"depth":45,"text":12825},{"id":12840,"depth":45,"text":12841},{"id":12853,"depth":45,"text":12853},{"id":2003,"depth":45,"text":2004},"2020 年整理的 CSS Reset、Normalize.css 與 Sass 引入方式。",{},"\u002Fposts\u002Fcss-reset-normalize","2020-03-16",[],{"title":12816,"description":12881},"posts\u002F20200316.css-reset-normalize",[1757,12314,12841],"2tIswfoUUD1KKmVrMk9YL5eph33bYXiugPlZQdwWGdI",{"id":12891,"title":12892,"body":12893,"category":378,"description":12997,"draft":380,"extension":381,"image":382,"meta":12998,"navigation":102,"path":12999,"publishedAt":13000,"relatedExperiments":13001,"seo":13002,"stem":13003,"tags":13004,"updatedAt":10829,"__hash__":13005},"posts\u002Fposts\u002F20200317.sass-scss-syntax-basics.md","關於 Sass／SCSS：兩種語法與專案架構",{"type":8,"value":12894,"toc":12991},[12895,12898,12902,12905,12908,12914,12920,12924,12927,12933,12939,12942,12948,12954,12960,12973,12975,12988],[11,12896,12897],{},"Sass／SCSS 是用來管理 CSS 的工具與程式語言，透過編譯工具轉成瀏覽器看得懂的 CSS。SCSS 是 Sass 3 引入的新語法，兩種寫法有些不同，但執行順序都是由上而下。",[15,12899,12901],{"id":12900},"sass-與-scss-寫法","Sass 與 SCSS 寫法",[11,12903,12904],{},"兩者最大的表面差異，是 Sass 省略了分號與大括號。Sass 寫起來較簡潔，但內容很多時只靠縮排會比較難分辨段落。",[11,12906,12907],{},"下面的 SCSS 與 Sass 寫法沒有功能差異，會編譯成相同的 CSS。",[11,12909,12910],{},[10742,12911],{"alt":12912,"src":12913},"SCSS 使用大括號與分號撰寫巢狀 nav，右側顯示編譯後 CSS","\u002Fimages\u002Fposts\u002Fsass-scss-syntax-basics\u002F01.webp",[11,12915,12916],{},[10742,12917],{"alt":12918,"src":12919},"Sass 使用縮排撰寫相同的 nav 樣式，右側顯示相同的編譯結果","\u002Fimages\u002Fposts\u002Fsass-scss-syntax-basics\u002F02.webp",[15,12921,12923],{"id":12922},"mixin-寫法","mixin 寫法",[11,12925,12926],{},"Mixin 在 SCSS 與 Sass 中的寫法不同。",[11,12928,12929],{},[10742,12930],{"alt":12931,"src":12932},"SCSS 使用 @mixin 與 @include 定義 transform，右側顯示編譯後 CSS","\u002Fimages\u002Fposts\u002Fsass-scss-syntax-basics\u002F03.webp",[11,12934,12935],{},[10742,12936],{"alt":12937,"src":12938},"Sass 使用等號與加號定義及呼叫 transform mixin，右側顯示編譯後 CSS","\u002Fimages\u002Fposts\u002Fsass-scss-syntax-basics\u002F04.webp",[15,12940,12941],{"id":12941},"專案架構",[11,12943,12944,12945,12947],{},"當時用 ",[29,12946,8744],{}," 組成的簡單架構：",[22,12949,12952],{"className":12950,"code":12951,"language":426,"meta":27},[424],"_variable.scss   \u002F\u002F 變數\n_normalize.scss  \u002F\u002F 保持乾淨的網站\n_mixin.scss      \u002F\u002F mixin\n_header.scss     \u002F\u002F header、footer 等\n_page1.scss      \u002F\u002F 分頁\n_page2.scss\n_all.scss        \u002F\u002F 所有檔案 import 到這裡\n",[29,12953,12951],{"__ignoreMap":27},[11,12955,12956,12959],{},[29,12957,12958],{},"_all.scss"," 內的寫法：",[22,12961,12963],{"className":8735,"code":12962,"language":8737,"meta":27,"style":27},"@import 'variable';\n",[29,12964,12965],{"__ignoreMap":27},[32,12966,12967,12969,12971],{"class":34,"line":35},[32,12968,8744],{"class":38},[32,12970,10306],{"class":175},[32,12972,96],{"class":41},[15,12974,2004],{"id":2003},[11,12976,12977,12978,7090,12981,12984,12985,12987],{},"SCSS 仍是常見語法，但 Sass 官方已用 ",[29,12979,12980],{},"@use",[29,12982,12983],{},"@forward"," 取代舊式 ",[29,12986,8744],{}," 架構；現行模組方式整理在《Sass 的 @use 與 @forward》。",[368,12989,12990],{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}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);}",{"title":27,"searchDepth":51,"depth":51,"links":12992},[12993,12994,12995,12996],{"id":12900,"depth":45,"text":12901},{"id":12922,"depth":45,"text":12923},{"id":12941,"depth":45,"text":12941},{"id":2003,"depth":45,"text":2004},"2020 年整理的 Sass、SCSS 語法差異、mixin 與 import 專案結構。",{},"\u002Fposts\u002Fsass-scss-syntax-basics","2020-03-17",[],{"title":12892,"description":12997},"posts\u002F20200317.sass-scss-syntax-basics",[4847,12314],"j87ORCDJiqkqsXGX6jbLhz0Q_PZRE4jOpg2Kq20V0ck",{"id":13007,"title":13008,"body":13009,"category":378,"description":13415,"draft":380,"extension":381,"image":382,"meta":13416,"navigation":102,"path":13417,"publishedAt":13418,"relatedExperiments":13419,"seo":13420,"stem":13421,"tags":13422,"updatedAt":10829,"__hash__":13423},"posts\u002Fposts\u002F20200318.css-methodologies-smacss-oocss-bem.md","CSS 架構方法論：SMACSS、OOCSS、BEM",{"type":8,"value":13010,"toc":13402},[13011,13014,13018,13021,13033,13036,13115,13124,13190,13197,13201,13204,13212,13232,13256,13260,13264,13275,13282,13293,13300,13314,13317,13388,13394,13396,13399],[11,13012,13013],{},"Modular CSS 的目的是讓程式碼易懂、可重用，進而有效率地開發及維護。",[15,13015,13017],{"id":13016},"smacss","SMACSS",[11,13019,13020],{},"SMACSS（Scalable and Modular Architecture for CSS）具有結構規範與命名規則，將 CSS 結構分類，並讓 CSS 與 HTML 分離。",[4538,13022,13023,13026],{},[4541,13024,13025],{},"作者：Jonathan Snook",[4541,13027,13028,13029],{},"網站：",[412,13030,13017],{"href":13031,"rel":13032},"http:\u002F\u002Fsmacss.com\u002F",[416],[11,13034,13035],{},"分類方式：",[4538,13037,13038,13058,13075,13092,13109],{},[4541,13039,13040,13043,13044],{},[12249,13041,13042],{},"Base","：基底\n",[4538,13045,13046,13049,13052],{},[4541,13047,13048],{},"CSS Reset",[4541,13050,13051],{},"CSS Normalize",[4541,13053,13054,13055],{},"不應該使用 ",[29,13056,13057],{},"!important",[4541,13059,13060,13063,13064],{},[12249,13061,13062],{},"Layout","：圖層\n",[4538,13065,13066,13069,13072],{},[4541,13067,13068],{},"Header",[4541,13070,13071],{},"Sidebar",[4541,13073,13074],{},"Content",[4541,13076,13077,13080,13081],{},[12249,13078,13079],{},"Module","：模組\n",[4538,13082,13083,13086,13089],{},[4541,13084,13085],{},"Tabs",[4541,13087,13088],{},"Button",[4541,13090,13091],{},"List",[4541,13093,13094,13097,13098],{},[12249,13095,13096],{},"State","：狀態\n",[4538,13099,13100,13103,13106],{},[4541,13101,13102],{},"Disabled State",[4541,13104,13105],{},"Default State",[4541,13107,13108],{},"Active State",[4541,13110,13111,13114],{},[12249,13112,13113],{},"Theme","：頁面上可單獨存在並可重複使用的元件",[11,13116,13117,13118,3750,13121,3775],{},"定義 Module 時，應避免使用 ID 或標籤名稱作為選擇器。子模組以原模組名稱加連字號作為名稱，例如 ",[29,13119,13120],{},".mod-header",[29,13122,13123],{},".mod-body",[22,13125,13127],{"className":4038,"code":13126,"language":4034,"meta":27,"style":27},"\u003Cdiv class=\"fld\">\n  \u003Cp class=\"fld-name\">\u003C\u002Fp>\n  \u003Cp class=\"fld-item\">\u003C\u002Fp>\n\u003C\u002Fdiv>\n",[29,13128,13129,13144,13163,13182],{"__ignoreMap":27},[32,13130,13131,13133,13135,13137,13139,13142],{"class":34,"line":35},[32,13132,4046],{"class":41},[32,13134,4050],{"class":4049},[32,13136,9897],{"class":4053},[32,13138,172],{"class":41},[32,13140,13141],{"class":175},"\"fld\"",[32,13143,4062],{"class":41},[32,13145,13146,13148,13150,13152,13154,13157,13159,13161],{"class":34,"line":45},[32,13147,4067],{"class":41},[32,13149,11],{"class":4049},[32,13151,9897],{"class":4053},[32,13153,172],{"class":41},[32,13155,13156],{"class":175},"\"fld-name\"",[32,13158,4136],{"class":41},[32,13160,11],{"class":4049},[32,13162,4062],{"class":41},[32,13164,13165,13167,13169,13171,13173,13176,13178,13180],{"class":34,"line":51},[32,13166,4067],{"class":41},[32,13168,11],{"class":4049},[32,13170,9897],{"class":4053},[32,13172,172],{"class":41},[32,13174,13175],{"class":175},"\"fld-item\"",[32,13177,4136],{"class":41},[32,13179,11],{"class":4049},[32,13181,4062],{"class":41},[32,13183,13184,13186,13188],{"class":34,"line":90},[32,13185,4105],{"class":41},[32,13187,4050],{"class":4049},[32,13189,4062],{"class":41},[11,13191,10400,13192],{},[412,13193,13196],{"href":13194,"rel":13195},"https:\u002F\u002Fwww.slideshare.net\u002Fkurotanshi\u002Fcss-oocss-smacss-bem",[416],"CSS：OOCSS、SMACSS、BEM",[15,13198,13200],{"id":13199},"oocss","OOCSS",[11,13202,13203],{},"OOCSS（Object Oriented CSS）最典型的框架例子是 Bootstrap。",[4538,13205,13206,13209],{},[4541,13207,13208],{},"結構與外觀分離：與樣式有關的部分要獨立出來。",[4541,13210,13211],{},"容器與內容分離，例如格線系統 Grid System。",[22,13213,13215],{"className":5412,"code":13214,"language":1140,"meta":27,"style":27},".container .grid_1\n.container .grid_2\n",[29,13216,13217,13225],{"__ignoreMap":27},[32,13218,13219,13222],{"class":34,"line":35},[32,13220,13221],{"class":4053},".container",[32,13223,13224],{"class":4053}," .grid_1\n",[32,13226,13227,13229],{"class":34,"line":45},[32,13228,13221],{"class":4053},[32,13230,13231],{"class":4053}," .grid_2\n",[22,13233,13235],{"className":4038,"code":13234,"language":4034,"meta":27,"style":27},"\u003Cbutton class=\"btn btn-small btn-primary\">\u003C\u002Fbutton>\n",[29,13236,13237],{"__ignoreMap":27},[32,13238,13239,13241,13243,13245,13247,13250,13252,13254],{"class":34,"line":35},[32,13240,4046],{"class":41},[32,13242,11493],{"class":4049},[32,13244,9897],{"class":4053},[32,13246,172],{"class":41},[32,13248,13249],{"class":175},"\"btn btn-small btn-primary\"",[32,13251,4136],{"class":41},[32,13253,11493],{"class":4049},[32,13255,4062],{"class":41},[15,13257,13259],{"id":13258},"bem","BEM",[151,13261,13263],{"id":13262},"block區塊","Block（區塊）",[4538,13265,13266,13269,13272],{},[4541,13267,13268],{},"在頁面上獨立存在並可重複使用的元件。",[4541,13270,13271],{},"如同 SMACSS 的 Module、Layout。",[4541,13273,13274],{},"每個 Block 都是獨立存在。",[151,13276,13278,13279],{"id":13277},"element元素__","Element（元素）",[29,13280,13281],{},"__",[4538,13283,13284,13287,13290],{},[4541,13285,13286],{},"Block 的一部分，也就是子元件。",[4541,13288,13289],{},"無法獨立於 Block 之外。",[4541,13291,13292],{},"有些 Block 可能沒有 Element。",[151,13294,13296,13297],{"id":13295},"modifier修飾符","Modifier（修飾符）",[29,13298,13299],{},"--",[4538,13301,13302,13305,13308,13311],{},[4541,13303,13304],{},"用來修改樣式。",[4541,13306,13307],{},"定義 Block 或 Element 的狀態或屬性。",[4541,13309,13310],{},"類似 SMACSS 的 State。",[4541,13312,13313],{},"同一個 Block 或 Element 可以同時存在多組 Modifier。",[15,13315,13316],{"id":13316},"命名筆記",[4538,13318,13319,13322,13346,13361,13370,13376,13379,13382,13385],{},[4541,13320,13321],{},"階層不要超過五層。",[4541,13323,13324,13325,3750,13328,3750,13331,3750,13334,3750,13337,3750,13340,3750,13343,3775],{},"前綴詞：",[29,13326,13327],{},".l-layout",[29,13329,13330],{},".e-element",[29,13332,13333],{},".p-page",[29,13335,13336],{},".h-helper",[29,13338,13339],{},".col-",[29,13341,13342],{},".fas",[29,13344,13345],{},".fa-icon",[4541,13347,13348,13349,3750,13352,3750,13355,3750,13358,3775],{},"Element：",[29,13350,13351],{},".button__icon",[29,13353,13354],{},".text-field__label",[29,13356,13357],{},".heading__title",[29,13359,13360],{},".menu__item",[4541,13362,13363,13364,3750,13367,3775],{},"Modifier：",[29,13365,13366],{},".button_active",[29,13368,13369],{},".text-field_editable",[4541,13371,13372,13373,3775],{},"當時 CSS 通常使用小駝峰寫法，例如 ",[29,13374,13375],{},".bookList",[4541,13377,13378],{},"依照開發規範選擇命名方式，重點是保持一致。",[4541,13380,13381],{},"可以使用 BEM 設計模式與命名規範。",[4541,13383,13384],{},"拋棄語意命名方式，改以工具性質命名（SMACSS）。",[4541,13386,13387],{},"多參考其他框架，了解不同命名方式。",[11,13389,13390,13391,3775],{},"良好的 CSS 架構：",[12249,13392,13393],{},"可預測 → 可複用 → 可維護 → 可延展",[15,13395,2004],{"id":2003},[11,13397,13398],{},"這些方法論仍可用在全域 CSS；元件 scoped style、CSS Modules 與 utility-first CSS 則提供了不同的作用域與命名方式，整理在《Scoped Style 與 Utility-First CSS》。",[368,13400,13401],{},"html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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);}",{"title":27,"searchDepth":51,"depth":51,"links":13403},[13404,13405,13406,13413,13414],{"id":13016,"depth":45,"text":13017},{"id":13199,"depth":45,"text":13200},{"id":13258,"depth":45,"text":13259,"children":13407},[13408,13409,13411],{"id":13262,"depth":51,"text":13263},{"id":13277,"depth":51,"text":13410},"Element（元素）__",{"id":13295,"depth":51,"text":13412},"Modifier（修飾符）--",{"id":13316,"depth":45,"text":13316},{"id":2003,"depth":45,"text":2004},"2020 年整理的 SMACSS、OOCSS、BEM 分類與命名筆記。",{},"\u002Fposts\u002Fcss-methodologies-smacss-oocss-bem","2020-03-18",[],{"title":13008,"description":13415},"posts\u002F20200318.css-methodologies-smacss-oocss-bem",[1757,13259,13017,13200],"x5xxmylpKKc2860imGPWbwo0ArnzP-siL-1cFC3Rhqk",{"id":13425,"title":13426,"body":13427,"category":378,"description":13611,"draft":380,"extension":381,"image":382,"meta":13612,"navigation":102,"path":13613,"publishedAt":13614,"relatedExperiments":13615,"seo":13616,"stem":13617,"tags":13618,"updatedAt":10829,"__hash__":13620},"posts\u002Fposts\u002F20200319.css-scss-team-standard.md","2020 年 CSS／Sass 規範與檢核表",{"type":8,"value":13428,"toc":13606},[13429,13432,13533,13536,13598,13600],[15,13430,13431],{"id":13431},"規範",[13433,13434,13435,13448],"table",{},[13436,13437,13438],"thead",{},[13439,13440,13441,13445],"tr",{},[13442,13443,13444],"th",{},"項次",[13442,13446,13447],{},"規範敘述",[13449,13450,13451,13459,13466,13474,13482,13489,13501,13508,13519,13526],"tbody",{},[13439,13452,13453,13456],{},[13454,13455,6544],"td",{},[13454,13457,13458],{},"類別階層以不超過四層為原則",[13439,13460,13461,13463],{},[13454,13462,11971],{},[13454,13464,13465],{},"不使用 ID，一律使用 class；ID 通常只用於唯一值與錨點定位",[13439,13467,13468,13471],{},[13454,13469,13470],{},"3",[13454,13472,13473],{},"使用 OOCSS 建立類別工具庫",[13439,13475,13476,13479],{},[13454,13477,13478],{},"4",[13454,13480,13481],{},"排版結構以 BEM 開發",[13439,13483,13484,13486],{},[13454,13485,10613],{},[13454,13487,13488],{},"導入 Bootstrap 4 的 Grid System",[13439,13490,13491,13494],{},[13454,13492,13493],{},"6",[13454,13495,13496,13497,13500],{},"使用 Sass ",[29,13498,13499],{},"for"," 管理擴充元件",[13439,13502,13503,13505],{},[13454,13504,1269],{},[13454,13506,13507],{},"一律採用小駝峰式命名",[13439,13509,13510,13512],{},[13454,13511,1031],{},[13454,13513,13514,13515,13518],{},"第三方插件在 Sass 的 ",[29,13516,13517],{},"vendor"," 分類中引入",[13439,13520,13521,13523],{},[13454,13522,1043],{},[13454,13524,13525],{},"使用 REM 或 vmin 單位",[13439,13527,13528,13530],{},[13454,13529,11229],{},[13454,13531,13532],{},"使用 flexbox 進行元件布局",[15,13534,13535],{"id":13535},"檢核表",[13433,13537,13538,13548],{},[13436,13539,13540],{},[13439,13541,13542,13545],{},[13442,13543,13544],{},"項目",[13442,13546,13547],{},"敘述",[13449,13549,13550,13558,13566,13574,13582,13590],{},[13439,13551,13552,13555],{},[13454,13553,13554],{},"網頁相容性",[13454,13556,13557],{},"檢查各大瀏覽器版本，例如 Chrome、Firefox、Edge",[13439,13559,13560,13563],{},[13454,13561,13562],{},"螢幕解析度",[13454,13564,13565],{},"可自行設定 1920、1440、1280、1024、768、414、370、320 等尺寸；使用 Bootstrap 時改用 BS4 的 breakpoints",[13439,13567,13568,13571],{},[13454,13569,13570],{},"可讀性",[13454,13572,13573],{},"class 的連字號、底線與駝峰寫法要有一致規則，並統一縮排與註解",[13439,13575,13576,13579],{},[13454,13577,13578],{},"可擴充性",[13454,13580,13581],{},"具備元件化設計觀念（BEM 或自訂規則），不輕易影響主體架構與 JS hooks",[13439,13583,13584,13587],{},[13454,13585,13586],{},"可重用性",[13454,13588,13589],{},"自訂 class 可在多處使用（OOCSS），不重複套用多餘 CSS",[13439,13591,13592,13595],{},[13454,13593,13594],{},"可維護性",[13454,13596,13597],{},"Sass import 結構清晰易懂，整體風格一致",[15,13599,2004],{"id":2003},[11,13601,13602,13603,13605],{},"這份規範保留的是當時的團隊約定，其中 Bootstrap 4 Grid、Sass ",[29,13604,8744],{}," 與命名方式不必直接套用到新專案。現代元件樣式與 utility-first 的取捨可參考《Scoped Style 與 Utility-First CSS》。",{"title":27,"searchDepth":51,"depth":51,"links":13607},[13608,13609,13610],{"id":13431,"depth":45,"text":13431},{"id":13535,"depth":45,"text":13535},{"id":2003,"depth":45,"text":2004},"2020 年留下的 CSS／Sass 團隊規範、相容性與維護性檢核表。",{},"\u002Fposts\u002Fcss-scss-team-standard","2020-03-19",[],{"title":13426,"description":13611},"posts\u002F20200319.css-scss-team-standard",[1757,4847,13619],"團隊規範","68E2Iy1aYISP0Sw0Ue3P9da2cVL8hpuctfU36c2zjsc",{"id":13622,"title":13623,"body":13624,"category":378,"description":13658,"draft":380,"extension":381,"image":382,"meta":13659,"navigation":102,"path":13660,"publishedAt":13661,"relatedExperiments":13662,"seo":13663,"stem":13664,"tags":13665,"updatedAt":10829,"__hash__":13668},"posts\u002Fposts\u002F20200320.ios-momentum-scrolling.md","iOS 捲動順暢設定：-webkit-overflow-scrolling",{"type":8,"value":13625,"toc":13655},[13626,13629,13644,13646,13652],[11,13627,13628],{},"若要在 iOS 讓區塊的捲軸滾動順暢，可以使用以下語法：",[22,13630,13632],{"className":5412,"code":13631,"language":1140,"meta":27,"style":27},"overflow: auto;\n-webkit-overflow-scrolling: touch;\n",[29,13633,13634,13639],{"__ignoreMap":27},[32,13635,13636],{"class":34,"line":35},[32,13637,13638],{"class":41},"overflow: auto;\n",[32,13640,13641],{"class":34,"line":45},[32,13642,13643],{"class":41},"-webkit-overflow-scrolling: touch;\n",[15,13645,2004],{"id":2003},[11,13647,13648,13649,3775],{},"這是早期 iOS Safari 的非標準設定。新版 Safari 已預設提供慣性捲動，新專案通常不需要再加入 ",[29,13650,13651],{},"-webkit-overflow-scrolling: touch",[368,13653,13654],{},"html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}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);}",{"title":27,"searchDepth":51,"depth":51,"links":13656},[13657],{"id":2003,"depth":45,"text":2004},"2020 年記錄 iOS 可捲動區塊啟用慣性捲動的 CSS。",{},"\u002Fposts\u002Fios-momentum-scrolling","2020-03-20",[],{"title":13623,"description":13658},"posts\u002F20200320.ios-momentum-scrolling",[1757,13666,13667],"iOS","Safari","dHIBq5EBemZqS3COAUSR8IkMlYhqdXZWNmbLJCzZeSc",{"id":13670,"title":13671,"body":13672,"category":378,"description":14424,"draft":380,"extension":381,"image":382,"meta":14425,"navigation":102,"path":14426,"publishedAt":14427,"relatedExperiments":14428,"seo":14429,"stem":14430,"tags":14431,"updatedAt":14433,"__hash__":14434},"posts\u002Fposts\u002F20200323.scss-color-map.md","用 SCSS 的 Map 建立一份共用色票",{"type":8,"value":13673,"toc":14416},[13674,13681,13684,13782,13795,13799,13910,13923,13927,13933,14087,14090,14242,14245,14369,14381,14384,14398,14400,14413],[11,13675,13676,13677,13680],{},"顏色散落在樣式表各處直接寫死色碼（",[29,13678,13679],{},"#FECC00","），改配色時要一個個找出來改，還很容易漏掉。這篇整理當時用 SCSS 的 map 建立一份共用色票的做法。",[15,13682,13683],{"id":13683},"定義基礎色票",[22,13685,13687],{"className":8735,"code":13686,"language":8737,"meta":27,"style":27},"\u002F\u002F 共用 color\n\u002F\u002F example: bg-color | text-color | border-color\n$colorGroup: (\n  'supernova': #FECC00,\n  'fiord': #4C596C,\n  'black': #000,\n  'alto': #DBDBDB,\n  'dustyGray': #999,\n  'gray': #7F7F7F,\n);\n",[29,13688,13689,13694,13699,13706,13717,13729,13740,13752,13764,13776],{"__ignoreMap":27},[32,13690,13691],{"class":34,"line":35},[32,13692,13693],{"class":608},"\u002F\u002F 共用 color\n",[32,13695,13696],{"class":34,"line":45},[32,13697,13698],{"class":608},"\u002F\u002F example: bg-color | text-color | border-color\n",[32,13700,13701,13704],{"class":34,"line":51},[32,13702,13703],{"class":903},"$colorGroup",[32,13705,11719],{"class":5049},[32,13707,13708,13711,13713,13715],{"class":34,"line":90},[32,13709,13710],{"class":175},"  'supernova'",[32,13712,5269],{"class":5049},[32,13714,13679],{"class":351},[32,13716,547],{"class":5049},[32,13718,13719,13722,13724,13727],{"class":34,"line":99},[32,13720,13721],{"class":175},"  'fiord'",[32,13723,5269],{"class":5049},[32,13725,13726],{"class":351},"#4C596C",[32,13728,547],{"class":5049},[32,13730,13731,13734,13736,13738],{"class":34,"line":106},[32,13732,13733],{"class":175},"  'black'",[32,13735,5269],{"class":5049},[32,13737,8694],{"class":351},[32,13739,547],{"class":5049},[32,13741,13742,13745,13747,13750],{"class":34,"line":114},[32,13743,13744],{"class":175},"  'alto'",[32,13746,5269],{"class":5049},[32,13748,13749],{"class":351},"#DBDBDB",[32,13751,547],{"class":5049},[32,13753,13754,13757,13759,13762],{"class":34,"line":120},[32,13755,13756],{"class":175},"  'dustyGray'",[32,13758,5269],{"class":5049},[32,13760,13761],{"class":351},"#999",[32,13763,547],{"class":5049},[32,13765,13766,13769,13771,13774],{"class":34,"line":127},[32,13767,13768],{"class":175},"  'gray'",[32,13770,5269],{"class":5049},[32,13772,13773],{"class":351},"#7F7F7F",[32,13775,547],{"class":5049},[32,13777,13778,13780],{"class":34,"line":132},[32,13779,11796],{"class":5049},[32,13781,96],{"class":41},[11,13783,13784,13785,13790,13791,13794],{},"顏色的命名不是憑感覺亂取，而是先把色碼貼到 ",[412,13786,13789],{"href":13787,"rel":13788},"http:\u002F\u002Fchir.ag\u002Fprojects\u002Fname-that-color\u002F",[416],"name that color"," 這類工具查出對應的顏色名稱，統一用查出來的名稱當 key，之後不管是自己還是接手的人，看到 ",[29,13792,13793],{},"fiord"," 都知道對應的是哪個藍灰色，不用每次都去猜色碼。",[15,13796,13798],{"id":13797},"用-each-批次產生工具類別","用 @each 批次產生工具類別",[22,13800,13802],{"className":8735,"code":13801,"language":8737,"meta":27,"style":27},"\u002F\u002F example: bg-color | text-color | border-color\n@each $name, $color in $colorGroup {\n  .bg-#{$name} {\n    background: $color;\n  }\n  .text-#{$name} {\n    color: $color !important;\n  }\n  .border-#{$name} {\n    border: 1px solid $color;\n  }\n}\n",[29,13803,13804,13808,13825,13834,13844,13848,13857,13870,13874,13883,13902,13906],{"__ignoreMap":27},[32,13805,13806],{"class":34,"line":35},[32,13807,13698],{"class":608},[32,13809,13810,13812,13814,13816,13818,13820,13823],{"class":34,"line":45},[32,13811,11660],{"class":38},[32,13813,11822],{"class":903},[32,13815,635],{"class":41},[32,13817,8689],{"class":903},[32,13819,5527],{"class":38},[32,13821,13822],{"class":903}," $colorGroup",[32,13824,703],{"class":41},[32,13826,13827,13830,13832],{"class":34,"line":51},[32,13828,13829],{"class":4053},"  .bg-",[32,13831,11840],{"class":903},[32,13833,703],{"class":41},[32,13835,13836,13838,13840,13842],{"class":34,"line":90},[32,13837,11893],{"class":351},[32,13839,5269],{"class":41},[32,13841,8689],{"class":903},[32,13843,96],{"class":41},[32,13845,13846],{"class":34,"line":99},[32,13847,1203],{"class":41},[32,13849,13850,13853,13855],{"class":34,"line":106},[32,13851,13852],{"class":4053},"  .text-",[32,13854,11840],{"class":903},[32,13856,703],{"class":41},[32,13858,13859,13862,13864,13866,13868],{"class":34,"line":114},[32,13860,13861],{"class":351},"    color",[32,13863,5269],{"class":41},[32,13865,8689],{"class":903},[32,13867,11886],{"class":38},[32,13869,96],{"class":41},[32,13871,13872],{"class":34,"line":120},[32,13873,1203],{"class":41},[32,13875,13876,13879,13881],{"class":34,"line":127},[32,13877,13878],{"class":4053},"  .border-",[32,13880,11840],{"class":903},[32,13882,703],{"class":41},[32,13884,13885,13888,13890,13892,13894,13897,13900],{"class":34,"line":132},[32,13886,13887],{"class":351},"    border",[32,13889,5269],{"class":41},[32,13891,6544],{"class":351},[32,13893,11855],{"class":38},[32,13895,13896],{"class":351}," solid",[32,13898,13899],{"class":903}," $color",[32,13901,96],{"class":41},[32,13903,13904],{"class":34,"line":141},[32,13905,1203],{"class":41},[32,13907,13908],{"class":34,"line":147},[32,13909,54],{"class":41},[11,13911,13912,13913,3750,13916,3750,13919,13922],{},"六個顏色，一次產生十八個工具類別（",[29,13914,13915],{},"bg-supernova",[29,13917,13918],{},"text-supernova",[29,13920,13921],{},"border-supernova","……），之後要用某個顏色當背景、文字或邊框，直接在 HTML 上疊 class，不用再手寫一次色碼。",[15,13924,13926],{"id":13925},"區塊各自的顏色需求另建一份-map引用同一份色票","區塊各自的顏色需求：另建一份 map，引用同一份色票",[11,13928,13929,13930,13932],{},"不同區塊要用到的顏色不會剛好都落在基礎色票的命名裡，這時候另外建一份「語意化」的 map，用 ",[29,13931,12014],{}," 去引用基礎色票裡的顏色：",[22,13934,13936],{"className":8735,"code":13935,"language":8737,"meta":27,"style":27},"\u002F\u002F footer 選單字體與背景顏色\n$footerGroup: (\n  'menu-title-color': map-get($colorGroup, 'nobel'),\n  'menu-normal-color': map-get($colorGroup, 'doveGray'),\n  'menu-hover-color': map-get($colorGroup, 'treePoppy'),\n  'bottom-bg-color': map-get($colorGroup, 'shark'),\n  'privacy-text-color': map-get($colorGroup, 'doveGray'),\n  'border-color': map-get($colorGroup, 'alto'),\n  'footer-bg': white,\n);\n",[29,13937,13938,13943,13950,13970,13990,14010,14030,14049,14069,14081],{"__ignoreMap":27},[32,13939,13940],{"class":34,"line":35},[32,13941,13942],{"class":608},"\u002F\u002F footer 選單字體與背景顏色\n",[32,13944,13945,13948],{"class":34,"line":45},[32,13946,13947],{"class":903},"$footerGroup",[32,13949,11719],{"class":5049},[32,13951,13952,13955,13957,13959,13961,13963,13965,13968],{"class":34,"line":51},[32,13953,13954],{"class":175},"  'menu-title-color'",[32,13956,5269],{"class":5049},[32,13958,11903],{"class":351},[32,13960,477],{"class":5049},[32,13962,13703],{"class":903},[32,13964,635],{"class":5049},[32,13966,13967],{"class":175},"'nobel'",[32,13969,5101],{"class":5049},[32,13971,13972,13975,13977,13979,13981,13983,13985,13988],{"class":34,"line":90},[32,13973,13974],{"class":175},"  'menu-normal-color'",[32,13976,5269],{"class":5049},[32,13978,11903],{"class":351},[32,13980,477],{"class":5049},[32,13982,13703],{"class":903},[32,13984,635],{"class":5049},[32,13986,13987],{"class":175},"'doveGray'",[32,13989,5101],{"class":5049},[32,13991,13992,13995,13997,13999,14001,14003,14005,14008],{"class":34,"line":99},[32,13993,13994],{"class":175},"  'menu-hover-color'",[32,13996,5269],{"class":5049},[32,13998,11903],{"class":351},[32,14000,477],{"class":5049},[32,14002,13703],{"class":903},[32,14004,635],{"class":5049},[32,14006,14007],{"class":175},"'treePoppy'",[32,14009,5101],{"class":5049},[32,14011,14012,14015,14017,14019,14021,14023,14025,14028],{"class":34,"line":106},[32,14013,14014],{"class":175},"  'bottom-bg-color'",[32,14016,5269],{"class":5049},[32,14018,11903],{"class":351},[32,14020,477],{"class":5049},[32,14022,13703],{"class":903},[32,14024,635],{"class":5049},[32,14026,14027],{"class":175},"'shark'",[32,14029,5101],{"class":5049},[32,14031,14032,14035,14037,14039,14041,14043,14045,14047],{"class":34,"line":114},[32,14033,14034],{"class":175},"  'privacy-text-color'",[32,14036,5269],{"class":5049},[32,14038,11903],{"class":351},[32,14040,477],{"class":5049},[32,14042,13703],{"class":903},[32,14044,635],{"class":5049},[32,14046,13987],{"class":175},[32,14048,5101],{"class":5049},[32,14050,14051,14054,14056,14058,14060,14062,14064,14067],{"class":34,"line":120},[32,14052,14053],{"class":175},"  'border-color'",[32,14055,5269],{"class":5049},[32,14057,11903],{"class":351},[32,14059,477],{"class":5049},[32,14061,13703],{"class":903},[32,14063,635],{"class":5049},[32,14065,14066],{"class":175},"'alto'",[32,14068,5101],{"class":5049},[32,14070,14071,14074,14076,14079],{"class":34,"line":127},[32,14072,14073],{"class":175},"  'footer-bg'",[32,14075,5269],{"class":5049},[32,14077,14078],{"class":351},"white",[32,14080,547],{"class":5049},[32,14082,14083,14085],{"class":34,"line":132},[32,14084,11796],{"class":5049},[32,14086,96],{"class":41},[11,14088,14089],{},"社群圖示這類需要品牌色的區塊，也能混用漸層值：",[22,14091,14093],{"className":8735,"code":14092,"language":8737,"meta":27,"style":27},"\u002F\u002F socialIcon\n$socialGroup: (\n  'users': #2ab1f3,\n  'fb': #3b5998,\n  'twitter': #1DA1F2,\n  'ig': linear-gradient(45deg, #f09433 0%, #e6683c 25%, #dc2743 50%, #cc2366 75%, #bc1888 100%),\n  'telegram': #0088cc,\n  'youtube': #cd201f,\n);\n",[29,14094,14095,14100,14107,14119,14131,14143,14212,14224,14236],{"__ignoreMap":27},[32,14096,14097],{"class":34,"line":35},[32,14098,14099],{"class":608},"\u002F\u002F socialIcon\n",[32,14101,14102,14105],{"class":34,"line":45},[32,14103,14104],{"class":903},"$socialGroup",[32,14106,11719],{"class":5049},[32,14108,14109,14112,14114,14117],{"class":34,"line":51},[32,14110,14111],{"class":175},"  'users'",[32,14113,5269],{"class":5049},[32,14115,14116],{"class":351},"#2ab1f3",[32,14118,547],{"class":5049},[32,14120,14121,14124,14126,14129],{"class":34,"line":90},[32,14122,14123],{"class":175},"  'fb'",[32,14125,5269],{"class":5049},[32,14127,14128],{"class":351},"#3b5998",[32,14130,547],{"class":5049},[32,14132,14133,14136,14138,14141],{"class":34,"line":99},[32,14134,14135],{"class":175},"  'twitter'",[32,14137,5269],{"class":5049},[32,14139,14140],{"class":351},"#1DA1F2",[32,14142,547],{"class":5049},[32,14144,14145,14148,14150,14153,14155,14158,14161,14163,14166,14168,14171,14173,14176,14179,14181,14183,14186,14188,14190,14192,14195,14198,14200,14202,14205,14208,14210],{"class":34,"line":106},[32,14146,14147],{"class":175},"  'ig'",[32,14149,5269],{"class":5049},[32,14151,14152],{"class":351},"linear-gradient",[32,14154,477],{"class":5049},[32,14156,14157],{"class":351},"45",[32,14159,14160],{"class":38},"deg",[32,14162,635],{"class":5049},[32,14164,14165],{"class":351},"#f09433",[32,14167,1523],{"class":351},[32,14169,14170],{"class":38},"%",[32,14172,635],{"class":5049},[32,14174,14175],{"class":351},"#e6683c",[32,14177,14178],{"class":351}," 25",[32,14180,14170],{"class":38},[32,14182,635],{"class":5049},[32,14184,14185],{"class":351},"#dc2743",[32,14187,6868],{"class":351},[32,14189,14170],{"class":38},[32,14191,635],{"class":5049},[32,14193,14194],{"class":351},"#cc2366",[32,14196,14197],{"class":351}," 75",[32,14199,14170],{"class":38},[32,14201,635],{"class":5049},[32,14203,14204],{"class":351},"#bc1888",[32,14206,14207],{"class":351}," 100",[32,14209,14170],{"class":38},[32,14211,5101],{"class":5049},[32,14213,14214,14217,14219,14222],{"class":34,"line":114},[32,14215,14216],{"class":175},"  'telegram'",[32,14218,5269],{"class":5049},[32,14220,14221],{"class":351},"#0088cc",[32,14223,547],{"class":5049},[32,14225,14226,14229,14231,14234],{"class":34,"line":120},[32,14227,14228],{"class":175},"  'youtube'",[32,14230,5269],{"class":5049},[32,14232,14233],{"class":351},"#cd201f",[32,14235,547],{"class":5049},[32,14237,14238,14240],{"class":34,"line":127},[32,14239,11796],{"class":5049},[32,14241,96],{"class":41},[15,14243,14244],{"id":14244},"調用方式",[22,14246,14248],{"className":8735,"code":14247,"language":8737,"meta":27,"style":27},"footer {\n  @include z-index(footer);\n\n  .footer-area {\n    letter-spacing: 1px;\n    background: map-get($footerGroup, 'footer-bg');\n    @include relative();\n\n    .footer-box {\n      \u002F\u002F ...\n    }\n  }\n\n  .footer-privacy {\n    \u002F\u002F ...\n  }\n}\n",[29,14249,14250,14257,14268,14272,14279,14292,14311,14321,14325,14332,14337,14341,14345,14349,14356,14361,14365],{"__ignoreMap":27},[32,14251,14252,14255],{"class":34,"line":35},[32,14253,14254],{"class":4049},"footer",[32,14256,703],{"class":41},[32,14258,14259,14262,14265],{"class":34,"line":45},[32,14260,14261],{"class":38},"  @include",[32,14263,14264],{"class":217}," z-index",[32,14266,14267],{"class":41},"(footer);\n",[32,14269,14270],{"class":34,"line":51},[32,14271,103],{"emptyLinePlaceholder":102},[32,14273,14274,14277],{"class":34,"line":90},[32,14275,14276],{"class":4053},"  .footer-area",[32,14278,703],{"class":41},[32,14280,14281,14284,14286,14288,14290],{"class":34,"line":99},[32,14282,14283],{"class":351},"    letter-spacing",[32,14285,5269],{"class":41},[32,14287,6544],{"class":351},[32,14289,11855],{"class":38},[32,14291,96],{"class":41},[32,14293,14294,14296,14298,14300,14302,14304,14306,14309],{"class":34,"line":106},[32,14295,11893],{"class":351},[32,14297,5269],{"class":41},[32,14299,11903],{"class":351},[32,14301,477],{"class":41},[32,14303,13947],{"class":903},[32,14305,635],{"class":41},[32,14307,14308],{"class":175},"'footer-bg'",[32,14310,230],{"class":41},[32,14312,14313,14316,14319],{"class":34,"line":114},[32,14314,14315],{"class":38},"    @include",[32,14317,14318],{"class":217}," relative",[32,14320,687],{"class":41},[32,14322,14323],{"class":34,"line":120},[32,14324,103],{"emptyLinePlaceholder":102},[32,14326,14327,14330],{"class":34,"line":127},[32,14328,14329],{"class":4053},"    .footer-box",[32,14331,703],{"class":41},[32,14333,14334],{"class":34,"line":132},[32,14335,14336],{"class":608},"      \u002F\u002F ...\n",[32,14338,14339],{"class":34,"line":141},[32,14340,5591],{"class":41},[32,14342,14343],{"class":34,"line":147},[32,14344,1203],{"class":41},[32,14346,14347],{"class":34,"line":275},[32,14348,103],{"emptyLinePlaceholder":102},[32,14350,14351,14354],{"class":34,"line":282},[32,14352,14353],{"class":4053},"  .footer-privacy",[32,14355,703],{"class":41},[32,14357,14358],{"class":34,"line":295},[32,14359,14360],{"class":608},"    \u002F\u002F ...\n",[32,14362,14363],{"class":34,"line":605},[32,14364,1203],{"class":41},[32,14366,14367],{"class":34,"line":612},[32,14368,54],{"class":41},[11,14370,14371,14374,14375,14377,14378,14380],{},[29,14372,14373],{},"map-get($footerGroup, 'footer-bg')"," 一路往下追，最終取到的還是 ",[29,14376,13703],{}," 裡定義的那個色碼——改配色時只要動 ",[29,14379,13703],{}," 這一份，所有引用到它的地方全部跟著換。",[15,14382,14383],{"id":14383},"現在還用得到嗎",[11,14385,14386,14389,14390,14393,14394,14397],{},[12249,14387,14388],{},"用一份共用來源管理顏色的思路完全沒過時","，這正是現在「設計代幣」（design tokens）概念在做的事。真正被取代的是",[12249,14391,14392],{},"實作方式","：SCSS 的 map 只在編譯時期存在，瀏覽器執行時完全看不到這些變數，深色模式這類需要",[12249,14395,14396],{},"執行期動態換色","的需求，SCSS map 處理不了（只能編譯出兩份完全獨立的 CSS，切換時整份替換）。現在的做法多半改用 CSS 自訂屬性（CSS Custom Properties），整理在《CSS 自訂屬性與設計代幣：取代 SCSS 的顏色 map》。",[15,14399,4536],{"id":4536},[4538,14401,14402,14408],{},[4541,14403,14404],{},[412,14405,14407],{"href":13787,"rel":14406},[416],"name that color：顏色命名工具",[4541,14409,14410],{},[412,14411,12288],{"href":12286,"rel":14412},[416],[368,14414,14415],{},"html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}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 .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}",{"title":27,"searchDepth":51,"depth":51,"links":14417},[14418,14419,14420,14421,14422,14423],{"id":13683,"depth":45,"text":13683},{"id":13797,"depth":45,"text":13798},{"id":13925,"depth":45,"text":13926},{"id":14244,"depth":45,"text":14244},{"id":14383,"depth":45,"text":14383},{"id":4536,"depth":45,"text":4536},"整理用 SCSS 的 map 定義一份基礎色票，搭配 @each 批次產生 bg-／text-／border- 開頭的顏色工具類別，再用 map-get 讓區塊樣式（footer、社群圖示）引用同一份色票命名的做法，並附上顏色命名工具的參考連結。",{},"\u002Fposts\u002Fscss-color-map","2020-03-23",[],{"title":13671,"description":14424},"posts\u002F20200323.scss-color-map",[12314,1757,14432],"色彩系統","2026-08-19","d3xTyVCDznGNI98mpvixpi4eWK3ctmPYkg3kDhJ-ZfE",{"id":14436,"title":14437,"body":14438,"category":378,"description":14884,"draft":380,"extension":381,"image":382,"meta":14885,"navigation":102,"path":14886,"publishedAt":14887,"relatedExperiments":14888,"seo":14889,"stem":14890,"tags":14891,"updatedAt":14433,"__hash__":14893},"posts\u002Fposts\u002F20200328.webkit-scrollbar-styling.md","::-webkit-scrollbar 捲軸樣式調整整理",{"type":8,"value":14439,"toc":14876},[14440,14447,14450,14453,14537,14540,14543,14646,14649,14718,14721,14830,14834,14847,14849,14862,14864,14873],[11,14441,14442,14443,14446],{},"調整捲軸的過程中發現對這塊了解不夠，也發現一個奇怪的現象：Android App 內嵌 WebView 時，單純用 ",[29,14444,14445],{},"overflow: scroll","，捲軸反而會消失不見，得額外處理。爬了一輪文整理如下。",[15,14448,14449],{"id":14449},"捲軸的各個部位",[11,14451,14452],{},"WebKit 系瀏覽器把捲軸拆成好幾個可以個別選取的偽元素：",[13433,14454,14455,14465],{},[13436,14456,14457],{},[13439,14458,14459,14462],{},[13442,14460,14461],{},"偽元素",[13442,14463,14464],{},"對應部位",[13449,14466,14467,14477,14487,14497,14507,14517,14527],{},[13439,14468,14469,14474],{},[13454,14470,14471],{},[29,14472,14473],{},"::-webkit-scrollbar",[13454,14475,14476],{},"捲軸整體",[13439,14478,14479,14484],{},[13454,14480,14481],{},[29,14482,14483],{},"::-webkit-scrollbar-thumb",[13454,14485,14486],{},"捲軸裡可以拖動的小方塊（滑塊）",[13439,14488,14489,14494],{},[13454,14490,14491],{},[29,14492,14493],{},"::-webkit-scrollbar-track",[13454,14495,14496],{},"捲軸的軌道，滑塊在裡面移動",[13439,14498,14499,14504],{},[13454,14500,14501],{},[29,14502,14503],{},"::-webkit-scrollbar-button",[13454,14505,14506],{},"軌道兩端的按鈕，點一下可以微調滑塊位置",[13439,14508,14509,14514],{},[13454,14510,14511],{},[29,14512,14513],{},"::-webkit-scrollbar-track-piece",[13454,14515,14516],{},"軌道扣掉滑塊之後剩下的部分",[13439,14518,14519,14524],{},[13454,14520,14521],{},[29,14522,14523],{},"::-webkit-scrollbar-corner",[13454,14525,14526],{},"水平與垂直捲軸交會處的邊角",[13439,14528,14529,14534],{},[13454,14530,14531],{},[29,14532,14533],{},"::-webkit-resizer",[13454,14535,14536],{},"邊角處可拖曳調整元素大小的控制元件",[15,14538,14539],{"id":14539},"常用的偽類別",[11,14541,14542],{},"這些偽元素還能再搭配偽類別，選取更細的狀態：",[22,14544,14546],{"className":5412,"code":14545,"language":1140,"meta":27,"style":27},":horizontal   \u002F* 水平方向的捲軸 *\u002F\n:vertical     \u002F* 垂直方向的捲軸 *\u002F\n:decrement    \u002F* 讓捲軸往上或往左移動的按鈕／軌道碎片 *\u002F\n:increment    \u002F* 讓捲軸往下或往右移動的按鈕／軌道碎片 *\u002F\n:start        \u002F* 物件位於滑塊之前 *\u002F\n:end          \u002F* 物件位於滑塊之後 *\u002F\n:double-button \u002F* 軌道結束處是一對按鈕 *\u002F\n:single-button \u002F* 軌道結束處是單一按鈕 *\u002F\n:no-button    \u002F* 軌道結束處沒有按鈕 *\u002F\n:corner-present \u002F* 邊角是否存在 *\u002F\n:window-inactive \u002F* 包含捲軸的區域失去焦點時 *\u002F\n",[29,14547,14548,14556,14564,14572,14580,14588,14596,14606,14616,14626,14636],{"__ignoreMap":27},[32,14549,14550,14553],{"class":34,"line":35},[32,14551,14552],{"class":41},":horizontal   ",[32,14554,14555],{"class":608},"\u002F* 水平方向的捲軸 *\u002F\n",[32,14557,14558,14561],{"class":34,"line":45},[32,14559,14560],{"class":41},":vertical     ",[32,14562,14563],{"class":608},"\u002F* 垂直方向的捲軸 *\u002F\n",[32,14565,14566,14569],{"class":34,"line":51},[32,14567,14568],{"class":41},":decrement    ",[32,14570,14571],{"class":608},"\u002F* 讓捲軸往上或往左移動的按鈕／軌道碎片 *\u002F\n",[32,14573,14574,14577],{"class":34,"line":90},[32,14575,14576],{"class":41},":increment    ",[32,14578,14579],{"class":608},"\u002F* 讓捲軸往下或往右移動的按鈕／軌道碎片 *\u002F\n",[32,14581,14582,14585],{"class":34,"line":99},[32,14583,14584],{"class":41},":start        ",[32,14586,14587],{"class":608},"\u002F* 物件位於滑塊之前 *\u002F\n",[32,14589,14590,14593],{"class":34,"line":106},[32,14591,14592],{"class":41},":end          ",[32,14594,14595],{"class":608},"\u002F* 物件位於滑塊之後 *\u002F\n",[32,14597,14598,14600,14603],{"class":34,"line":114},[32,14599,322],{"class":41},[32,14601,14602],{"class":4049},"double-button",[32,14604,14605],{"class":608}," \u002F* 軌道結束處是一對按鈕 *\u002F\n",[32,14607,14608,14610,14613],{"class":34,"line":120},[32,14609,322],{"class":41},[32,14611,14612],{"class":4049},"single-button",[32,14614,14615],{"class":608}," \u002F* 軌道結束處是單一按鈕 *\u002F\n",[32,14617,14618,14620,14623],{"class":34,"line":127},[32,14619,322],{"class":41},[32,14621,14622],{"class":4049},"no-button",[32,14624,14625],{"class":608},"    \u002F* 軌道結束處沒有按鈕 *\u002F\n",[32,14627,14628,14630,14633],{"class":34,"line":132},[32,14629,322],{"class":41},[32,14631,14632],{"class":4049},"corner-present",[32,14634,14635],{"class":608}," \u002F* 邊角是否存在 *\u002F\n",[32,14637,14638,14640,14643],{"class":34,"line":141},[32,14639,322],{"class":41},[32,14641,14642],{"class":4049},"window-inactive",[32,14644,14645],{"class":608}," \u002F* 包含捲軸的區域失去焦點時 *\u002F\n",[11,14647,14648],{},"實際範例：",[22,14650,14652],{"className":5412,"code":14651,"language":1140,"meta":27,"style":27},"::-webkit-scrollbar-track-piece:start {\n  \u002F* 捲軸上半邊或左半邊的軌道 *\u002F\n}\n\n::-webkit-scrollbar-thumb:window-inactive {\n  \u002F* 焦點不在這個區域時，滑塊的狀態 *\u002F\n}\n\n::-webkit-scrollbar-button:horizontal:decrement:hover {\n  \u002F* 滑鼠停在水平捲軸下方按鈕上的狀態 *\u002F\n}\n",[29,14653,14654,14661,14666,14670,14674,14684,14689,14693,14697,14709,14714],{"__ignoreMap":27},[32,14655,14656,14658],{"class":34,"line":35},[32,14657,14513],{"class":4053},[32,14659,14660],{"class":41},":start {\n",[32,14662,14663],{"class":34,"line":45},[32,14664,14665],{"class":608},"  \u002F* 捲軸上半邊或左半邊的軌道 *\u002F\n",[32,14667,14668],{"class":34,"line":51},[32,14669,54],{"class":41},[32,14671,14672],{"class":34,"line":90},[32,14673,103],{"emptyLinePlaceholder":102},[32,14675,14676,14678,14680,14682],{"class":34,"line":99},[32,14677,14483],{"class":4053},[32,14679,322],{"class":41},[32,14681,14642],{"class":4049},[32,14683,703],{"class":41},[32,14685,14686],{"class":34,"line":106},[32,14687,14688],{"class":608},"  \u002F* 焦點不在這個區域時，滑塊的狀態 *\u002F\n",[32,14690,14691],{"class":34,"line":114},[32,14692,54],{"class":41},[32,14694,14695],{"class":34,"line":120},[32,14696,103],{"emptyLinePlaceholder":102},[32,14698,14699,14701,14704,14707],{"class":34,"line":127},[32,14700,14503],{"class":4053},[32,14702,14703],{"class":41},":horizontal:decrement",[32,14705,14706],{"class":4053},":hover",[32,14708,703],{"class":41},[32,14710,14711],{"class":34,"line":132},[32,14712,14713],{"class":608},"  \u002F* 滑鼠停在水平捲軸下方按鈕上的狀態 *\u002F\n",[32,14715,14716],{"class":34,"line":141},[32,14717,54],{"class":41},[15,14719,14720],{"id":14720},"常見自訂寫法",[22,14722,14724],{"className":5412,"code":14723,"language":1140,"meta":27,"style":27},".scroll-box::-webkit-scrollbar {\n  width: 8px;\n}\n.scroll-box::-webkit-scrollbar-track {\n  background: #f1f1f1;\n}\n.scroll-box::-webkit-scrollbar-thumb {\n  background: #888;\n  border-radius: 4px;\n}\n.scroll-box::-webkit-scrollbar-thumb:hover {\n  background: #555;\n}\n",[29,14725,14726,14733,14746,14750,14757,14769,14773,14780,14791,14804,14808,14815,14826],{"__ignoreMap":27},[32,14727,14728,14731],{"class":34,"line":35},[32,14729,14730],{"class":4053},".scroll-box::-webkit-scrollbar",[32,14732,703],{"class":41},[32,14734,14735,14738,14740,14742,14744],{"class":34,"line":45},[32,14736,14737],{"class":351},"  width",[32,14739,5269],{"class":41},[32,14741,1031],{"class":351},[32,14743,11855],{"class":38},[32,14745,96],{"class":41},[32,14747,14748],{"class":34,"line":51},[32,14749,54],{"class":41},[32,14751,14752,14755],{"class":34,"line":90},[32,14753,14754],{"class":4053},".scroll-box::-webkit-scrollbar-track",[32,14756,703],{"class":41},[32,14758,14759,14762,14764,14767],{"class":34,"line":99},[32,14760,14761],{"class":351},"  background",[32,14763,5269],{"class":41},[32,14765,14766],{"class":351},"#f1f1f1",[32,14768,96],{"class":41},[32,14770,14771],{"class":34,"line":106},[32,14772,54],{"class":41},[32,14774,14775,14778],{"class":34,"line":114},[32,14776,14777],{"class":4053},".scroll-box::-webkit-scrollbar-thumb",[32,14779,703],{"class":41},[32,14781,14782,14784,14786,14789],{"class":34,"line":120},[32,14783,14761],{"class":351},[32,14785,5269],{"class":41},[32,14787,14788],{"class":351},"#888",[32,14790,96],{"class":41},[32,14792,14793,14796,14798,14800,14802],{"class":34,"line":127},[32,14794,14795],{"class":351},"  border-radius",[32,14797,5269],{"class":41},[32,14799,13478],{"class":351},[32,14801,11855],{"class":38},[32,14803,96],{"class":41},[32,14805,14806],{"class":34,"line":132},[32,14807,54],{"class":41},[32,14809,14810,14813],{"class":34,"line":141},[32,14811,14812],{"class":4053},".scroll-box::-webkit-scrollbar-thumb:hover",[32,14814,703],{"class":41},[32,14816,14817,14819,14821,14824],{"class":34,"line":147},[32,14818,14761],{"class":351},[32,14820,5269],{"class":41},[32,14822,14823],{"class":351},"#555",[32,14825,96],{"class":41},[32,14827,14828],{"class":34,"line":275},[32,14829,54],{"class":41},[15,14831,14833],{"id":14832},"android-webview-裡捲軸消失的踩坑","Android WebView 裡捲軸消失的踩坑",[11,14835,14836,14837,14839,14840,14843,14844,14846],{},"單純用 ",[29,14838,14445],{}," 讓一個區塊可以捲動，一般瀏覽器沒問題，但",[12249,14841,14842],{},"嵌在某些 Android App 的 WebView 裡，捲軸會直接不見","——不是捲動功能壞掉，是捲軸本身沒有畫出來，使用者滑得動但視覺上沒有任何捲軸提示。這是特定 WebView 實作的顯示行為問題，解法通常是額外用上面這組 ",[29,14845,14473],{}," 系列自己畫一份捲軸樣式，而不是依賴系統預設的捲軸外觀。",[15,14848,14383],{"id":14383},[11,14850,14851,14854,14855,11621,14858,14861],{},[12249,14852,14853],{},"這整組偽元素還能用，Chromium 系瀏覽器都支援","，但它們從來不是任何標準規格的一部分，Firefox 完全不認得。想要精細控制捲軸每個部位（滑塊圓角、軌道背景、hover 狀態）依然要靠這一組。如果需求只是「细一點的捲軸」「換個顏色」這種基本調整，現在有標準化的 ",[29,14856,14857],{},"scrollbar-width",[29,14859,14860],{},"scrollbar-color"," 屬性，主流瀏覽器引擎都已支援，不需要再依賴 WebKit 專屬寫法，整理在《標準化的捲軸樣式：scrollbar-width 與 scrollbar-color》。",[15,14863,4536],{"id":4536},[4538,14865,14866],{},[4541,14867,14868],{},[412,14869,14872],{"href":14870,"rel":14871},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FCSS\u002F::-webkit-scrollbar",[416],"MDN：::-webkit-scrollbar（非標準）",[368,14874,14875],{},"html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}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 .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}",{"title":27,"searchDepth":51,"depth":51,"links":14877},[14878,14879,14880,14881,14882,14883],{"id":14449,"depth":45,"text":14449},{"id":14539,"depth":45,"text":14539},{"id":14720,"depth":45,"text":14720},{"id":14832,"depth":45,"text":14833},{"id":14383,"depth":45,"text":14383},{"id":4536,"depth":45,"text":4536},"整理 WebKit 專屬的一整組捲軸偽元素與偽類別——thumb、track、button、corner 各自對應捲軸的哪個部位，並記錄 Android WebView 內嵌頁面單純用 overflow scroll 捲軸會直接消失、需要額外處理的踩坑。現在標準化的 scrollbar-width／scrollbar-color 屬性整理在新的一篇。",{},"\u002Fposts\u002Fwebkit-scrollbar-styling","2020-03-28",[],{"title":14437,"description":14884},"posts\u002F20200328.webkit-scrollbar-styling",[1757,14892],"捲軸","3sNk8CbTCwdW50uQxgc7dHQlZUWUQ6jGRneKube0SM4",{"id":14895,"title":14896,"body":14897,"category":378,"description":15422,"draft":380,"extension":381,"image":382,"meta":15423,"navigation":102,"path":15424,"publishedAt":15425,"relatedExperiments":15426,"seo":15427,"stem":15428,"tags":15429,"updatedAt":14433,"__hash__":15431},"posts\u002Fposts\u002F20200329.jquery-scroll-animation.md","jQuery 版本的捲動進場動畫：手動比對 offset 與 scrollTop",{"type":8,"value":14898,"toc":15414},[14899,14902,14906,14997,15018,15022,15167,15186,15190,15348,15364,15366,15384,15386,15395,15397,15411],[11,14900,14901],{},"練習做活動頁（EDM）常常需要「捲到某個區塊時觸發進場動畫」的效果，每次都要重新想邏輯、翻舊 code，這篇整理一次常用的寫法。",[15,14903,14905],{"id":14904},"監聽-resize-與-scroll","監聽 resize 與 scroll",[22,14907,14909],{"className":24,"code":14908,"language":26,"meta":27,"style":27},"$(window)\n  .resize(function () {\n    $(window)\n      .on('scroll', function () {\n        \u002F\u002F 捲動時要做的事...\n      })\n      .trigger('scroll');\n  })\n  .trigger('resize');\n",[29,14910,14911,14918,14931,14937,14956,14961,14966,14979,14984],{"__ignoreMap":27},[32,14912,14913,14915],{"class":34,"line":35},[32,14914,755],{"class":217},[32,14916,14917],{"class":41},"(window)\n",[32,14919,14920,14923,14925,14927,14929],{"class":34,"line":45},[32,14921,14922],{"class":41},"  .",[32,14924,6728],{"class":217},[32,14926,477],{"class":41},[32,14928,665],{"class":38},[32,14930,668],{"class":41},[32,14932,14933,14935],{"class":34,"line":51},[32,14934,674],{"class":217},[32,14936,14917],{"class":41},[32,14938,14939,14942,14945,14947,14950,14952,14954],{"class":34,"line":90},[32,14940,14941],{"class":41},"      .",[32,14943,14944],{"class":217},"on",[32,14946,477],{"class":41},[32,14948,14949],{"class":175},"'scroll'",[32,14951,635],{"class":41},[32,14953,665],{"class":38},[32,14955,668],{"class":41},[32,14957,14958],{"class":34,"line":99},[32,14959,14960],{"class":608},"        \u002F\u002F 捲動時要做的事...\n",[32,14962,14963],{"class":34,"line":106},[32,14964,14965],{"class":41},"      })\n",[32,14967,14968,14970,14973,14975,14977],{"class":34,"line":114},[32,14969,14941],{"class":41},[32,14971,14972],{"class":217},"trigger",[32,14974,477],{"class":41},[32,14976,14949],{"class":175},[32,14978,230],{"class":41},[32,14980,14981],{"class":34,"line":120},[32,14982,14983],{"class":41},"  })\n",[32,14985,14986,14988,14990,14992,14995],{"class":34,"line":127},[32,14987,14922],{"class":41},[32,14989,14972],{"class":217},[32,14991,477],{"class":41},[32,14993,14994],{"class":175},"'resize'",[32,14996,230],{"class":41},[11,14998,14999,15000,15002,15003,15006,15007,11621,15010,15013,15014,15017],{},"外層先綁 ",[29,15001,6728],{},"，裡面才綁 ",[29,15004,15005],{},"scroll","，並且用 ",[29,15008,15009],{},".trigger('resize')",[29,15011,15012],{},".trigger('scroll')"," 手動觸發一次——這是為了確保",[12249,15015,15016],{},"頁面剛載入、使用者還沒有真的捲動或縮放視窗之前","，也能先執行一次判斷邏輯，不然要等使用者第一次動手捲動或縮放，畫面才會有反應。",[15,15019,15021],{"id":15020},"捲軸判斷只觸發一次的進場動畫","捲軸判斷：只觸發一次的進場動畫",[22,15023,15025],{"className":24,"code":15024,"language":26,"meta":27,"style":27},"var item = $(this);\nvar windowTop = $(window).scrollTop() + $(window).height();\nvar pageTop = item.offset().top;\n\nif (windowTop >= pageTop) {\n  if (item.hasClass('no-gutters')) {\n    item.addClass('animate__animated animate__bounceInRight');\n  } else {\n    item.addClass('animate__animated animate__bounceInLeft');\n  }\n}\n",[29,15026,15027,15044,15072,15090,15094,15106,15123,15138,15146,15159,15163],{"__ignoreMap":27},[32,15028,15029,15031,15034,15036,15038,15040,15042],{"class":34,"line":35},[32,15030,166],{"class":38},[32,15032,15033],{"class":41}," item ",[32,15035,172],{"class":38},[32,15037,6756],{"class":217},[32,15039,477],{"class":41},[32,15041,1083],{"class":351},[32,15043,230],{"class":41},[32,15045,15046,15048,15051,15053,15055,15057,15060,15062,15064,15066,15068,15070],{"class":34,"line":45},[32,15047,166],{"class":38},[32,15049,15050],{"class":41}," windowTop ",[32,15052,172],{"class":38},[32,15054,6756],{"class":217},[32,15056,6759],{"class":41},[32,15058,15059],{"class":217},"scrollTop",[32,15061,6075],{"class":41},[32,15063,224],{"class":38},[32,15065,6756],{"class":217},[32,15067,6759],{"class":41},[32,15069,6762],{"class":217},[32,15071,687],{"class":41},[32,15073,15074,15076,15079,15081,15084,15087],{"class":34,"line":51},[32,15075,166],{"class":38},[32,15077,15078],{"class":41}," pageTop ",[32,15080,172],{"class":38},[32,15082,15083],{"class":41}," item.",[32,15085,15086],{"class":217},"offset",[32,15088,15089],{"class":41},"().top;\n",[32,15091,15092],{"class":34,"line":90},[32,15093,103],{"emptyLinePlaceholder":102},[32,15095,15096,15098,15101,15103],{"class":34,"line":99},[32,15097,17],{"class":38},[32,15099,15100],{"class":41}," (windowTop ",[32,15102,348],{"class":38},[32,15104,15105],{"class":41}," pageTop) {\n",[32,15107,15108,15110,15113,15116,15118,15121],{"class":34,"line":106},[32,15109,1021],{"class":38},[32,15111,15112],{"class":41}," (item.",[32,15114,15115],{"class":217},"hasClass",[32,15117,477],{"class":41},[32,15119,15120],{"class":175},"'no-gutters'",[32,15122,1046],{"class":41},[32,15124,15125,15128,15131,15133,15136],{"class":34,"line":114},[32,15126,15127],{"class":41},"    item.",[32,15129,15130],{"class":217},"addClass",[32,15132,477],{"class":41},[32,15134,15135],{"class":175},"'animate__animated animate__bounceInRight'",[32,15137,230],{"class":41},[32,15139,15140,15142,15144],{"class":34,"line":120},[32,15141,5911],{"class":41},[32,15143,565],{"class":38},[32,15145,703],{"class":41},[32,15147,15148,15150,15152,15154,15157],{"class":34,"line":127},[32,15149,15127],{"class":41},[32,15151,15130],{"class":217},[32,15153,477],{"class":41},[32,15155,15156],{"class":175},"'animate__animated animate__bounceInLeft'",[32,15158,230],{"class":41},[32,15160,15161],{"class":34,"line":132},[32,15162,1203],{"class":41},[32,15164,15165],{"class":34,"line":141},[32,15166,54],{"class":41},[11,15168,15169,15172,15173,15176,15177,15182,15183,15185],{},[29,15170,15171],{},"windowTop"," 是「目前捲動位置 + 可視窗口高度」，等於可視範圍的最底部在整個頁面上的絕對位置；",[29,15174,15175],{},"pageTop"," 是目標元素頂部的絕對位置。當可視範圍的底部已經超過元素頂部，就代表元素進入畫面了，加上對應的 ",[412,15178,15181],{"href":15179,"rel":15180},"https:\u002F\u002Fanimate.style\u002F",[416],"animate.css"," 動畫 class。因為只是 ",[29,15184,15130],{},"，動畫只會播放一次，之後捲上捲下都不會重播。",[15,15187,15189],{"id":15188},"捲動動畫進入與消失都反應","捲動動畫：進入與消失都反應",[22,15191,15193],{"className":24,"code":15192,"language":26,"meta":27,"style":27},"$(select).each(function (i) {\n  var bottomOfObject = $(this).offset().top + $(this).outerHeight() \u002F 8;\n  var bottomOfWindow = $(window).scrollTop() + $(window).height();\n\n  bottomOfWindow > bottomOfObject\n    ? $(this).addClass('active')\n    : $(this).removeClass('active');\n});\n",[29,15194,15195,15215,15259,15286,15290,15300,15322,15344],{"__ignoreMap":27},[32,15196,15197,15199,15202,15204,15206,15208,15210,15213],{"class":34,"line":35},[32,15198,755],{"class":217},[32,15200,15201],{"class":41},"(select).",[32,15203,1060],{"class":217},[32,15205,477],{"class":41},[32,15207,665],{"class":38},[32,15209,900],{"class":41},[32,15211,15212],{"class":903},"i",[32,15214,520],{"class":41},[32,15216,15217,15219,15222,15224,15226,15228,15230,15232,15234,15237,15239,15241,15243,15245,15247,15250,15252,15254,15257],{"class":34,"line":45},[32,15218,888],{"class":38},[32,15220,15221],{"class":41}," bottomOfObject ",[32,15223,172],{"class":38},[32,15225,6756],{"class":217},[32,15227,477],{"class":41},[32,15229,1083],{"class":351},[32,15231,533],{"class":41},[32,15233,15086],{"class":217},[32,15235,15236],{"class":41},"().top ",[32,15238,224],{"class":38},[32,15240,6756],{"class":217},[32,15242,477],{"class":41},[32,15244,1083],{"class":351},[32,15246,533],{"class":41},[32,15248,15249],{"class":217},"outerHeight",[32,15251,6075],{"class":41},[32,15253,1275],{"class":38},[32,15255,15256],{"class":351}," 8",[32,15258,96],{"class":41},[32,15260,15261,15263,15266,15268,15270,15272,15274,15276,15278,15280,15282,15284],{"class":34,"line":51},[32,15262,888],{"class":38},[32,15264,15265],{"class":41}," bottomOfWindow ",[32,15267,172],{"class":38},[32,15269,6756],{"class":217},[32,15271,6759],{"class":41},[32,15273,15059],{"class":217},[32,15275,6075],{"class":41},[32,15277,224],{"class":38},[32,15279,6756],{"class":217},[32,15281,6759],{"class":41},[32,15283,6762],{"class":217},[32,15285,687],{"class":41},[32,15287,15288],{"class":34,"line":90},[32,15289,103],{"emptyLinePlaceholder":102},[32,15291,15292,15295,15297],{"class":34,"line":99},[32,15293,15294],{"class":41},"  bottomOfWindow ",[32,15296,1615],{"class":38},[32,15298,15299],{"class":41}," bottomOfObject\n",[32,15301,15302,15305,15307,15309,15311,15313,15315,15317,15320],{"class":34,"line":106},[32,15303,15304],{"class":38},"    ?",[32,15306,6756],{"class":217},[32,15308,477],{"class":41},[32,15310,1083],{"class":351},[32,15312,533],{"class":41},[32,15314,15130],{"class":217},[32,15316,477],{"class":41},[32,15318,15319],{"class":175},"'active'",[32,15321,515],{"class":41},[32,15323,15324,15327,15329,15331,15333,15335,15338,15340,15342],{"class":34,"line":114},[32,15325,15326],{"class":38},"    :",[32,15328,6756],{"class":217},[32,15330,477],{"class":41},[32,15332,1083],{"class":351},[32,15334,533],{"class":41},[32,15336,15337],{"class":217},"removeClass",[32,15339,477],{"class":41},[32,15341,15319],{"class":175},[32,15343,230],{"class":41},[32,15345,15346],{"class":34,"line":120},[32,15347,792],{"class":41},[11,15349,15350,15351,11621,15353,15355,15356,15359,15360,15363],{},"這版邏輯差不多，但用 ",[29,15352,15130],{},[29,15354,15337],{}," 交替，元素進入畫面加上 ",[29,15357,15358],{},"active","、離開畫面又拿掉，捲上捲下都會反應，適合搭配 CSS ",[29,15361,15362],{},"transition"," 做那種「捲到才淡入，捲走就淡出」的效果。",[15,15365,12245],{"id":12245},[11,15367,15368,15373,15374,3750,15377,3750,15380,15383],{},[12249,15369,15370,15372],{},[29,15371,15005],{}," 事件觸發得非常頻繁","，使用者捲動的每一影格都可能觸發一次，裡面又跑了 ",[29,15375,15376],{},".offset()",[29,15378,15379],{},".scrollTop()",[29,15381,15382],{},".height()"," 這些會強制瀏覽器重新計算版面（reflow）的操作，元素一多，捲動時很容易感覺到卡頓。實務上還需要另外加節流（throttle）或去抖（debounce）處理，這篇筆記裡的版本沒有做這件事。",[15,15385,14383],{"id":14383},[11,15387,15388,15391,15392,15394],{},[12249,15389,15390],{},"這套「算位置、比大小、加 class」的邏輯本身沒有錯，但瀏覽器現在有專門為這個情境設計的 API","——IntersectionObserver 不需要監聽 ",[29,15393,15005],{}," 事件、不需要自己計算 offset，效能上也好上一截，因為它不是每一影格都在算，而是瀏覽器只在元素真正進出可視範圍的交界點才通知你。整理在《IntersectionObserver：取代手動計算捲動位置》。",[15,15396,4536],{"id":4536},[4538,15398,15399,15404],{},[4541,15400,15401],{},[412,15402,15181],{"href":15179,"rel":15403},[416],[4541,15405,15406],{},[412,15407,15410],{"href":15408,"rel":15409},"https:\u002F\u002Fapi.jquery.com\u002Fscroll\u002F",[416],"jQuery：.scroll()",[368,15412,15413],{},"html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}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 .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}",{"title":27,"searchDepth":51,"depth":51,"links":15415},[15416,15417,15418,15419,15420,15421],{"id":14904,"depth":45,"text":14905},{"id":15020,"depth":45,"text":15021},{"id":15188,"depth":45,"text":15189},{"id":12245,"depth":45,"text":12245},{"id":14383,"depth":45,"text":14383},{"id":4536,"depth":45,"text":4536},"整理用 jQuery 監聽 resize 與 scroll 事件、手動計算元素位置與可視範圍邊界，判斷元素是否進入畫面並加上 animate.css 動畫 class 的做法，包含只觸發一次的進場動畫與會反覆進出的捲動動畫兩種版本。現在的做法是 IntersectionObserver，整理在新的一篇。",{},"\u002Fposts\u002Fjquery-scroll-animation","2020-03-29",[],{"title":14896,"description":15422},"posts\u002F20200329.jquery-scroll-animation",[2040,390,15430],"捲動動畫","EhK8pGYcG3cKRS2BW9pGheloavrMbkFC8fgZ25uI2rA",{"id":15433,"title":15434,"body":15435,"category":378,"description":15535,"draft":380,"extension":381,"image":382,"meta":15536,"navigation":102,"path":15537,"publishedAt":15538,"relatedExperiments":15539,"seo":15540,"stem":15541,"tags":15542,"updatedAt":14433,"__hash__":15543},"posts\u002Fposts\u002F20200408.node-sass-sass-loader.md","安裝 node-sass 與 sass-loader",{"type":8,"value":15436,"toc":15530},[15437,15444,15447,15466,15480,15489,15491,15513,15516,15518,15527],[11,15438,15439,15440,15443],{},"專案要處理 ",[29,15441,15442],{},".scss"," 檔案，webpack 得先裝對應的工具。",[15,15445,15446],{"id":15446},"安裝指令",[22,15448,15450],{"className":2057,"code":15449,"language":2059,"meta":27,"style":27},"npm install --save-dev node-sass sass-loader\n",[29,15451,15452],{"__ignoreMap":27},[32,15453,15454,15456,15458,15461,15463],{"class":34,"line":35},[32,15455,4625],{"class":2066},[32,15457,4650],{"class":175},[32,15459,15460],{"class":351}," --save-dev",[32,15462,8632],{"class":175},[32,15464,15465],{"class":175}," sass-loader\n",[4538,15467,15468,15474],{},[4541,15469,15470,15473],{},[12249,15471,15472],{},"node-sass","：Sass 官方 C++ 版本編譯器（LibSass）的 Node.js 綁定，負責把 SCSS 編譯成 CSS",[4541,15475,15476,15479],{},[12249,15477,15478],{},"sass-loader","：webpack 的 loader，負責在打包流程裡呼叫 node-sass 做轉換，串接 webpack 跟 Sass 編譯器",[11,15481,15482,15483,15485,15486,15488],{},"兩個套件缺一不可：",[29,15484,15478],{}," 只是轉接層，實際編譯工作是 ",[29,15487,15472],{}," 在做。",[15,15490,14383],{"id":14383},[11,15492,15493,15496,15497,15499,15500,15502,15503,15506,15507,15509,15510,15512],{},[12249,15494,15495],{},"不建議繼續用了。"," ",[29,15498,15472],{}," 底層依賴的 LibSass（C++ 版 Sass 編譯器）已經在 2020 年被 Sass 官方團隊正式棄用並停止維護，",[29,15501,15472],{}," 本身也因此停止更新。它最大的痛點是",[12249,15504,15505],{},"跟 Node.js 版本強綁定","——",[29,15508,15472],{}," 需要編譯原生二進位檔，Node 版本一升級，往往要跟著等 ",[29,15511,15472],{}," 出新版才能繼續裝，團隊裡 Node 版本沒對齊時常常一個人裝得起來、另一個人裝不起來。",[11,15514,15515],{},"現在的做法整理在《Dart Sass 遷移筆記：node-sass 之後怎麼裝 Sass》。",[15,15517,4536],{"id":4536},[4538,15519,15520],{},[4541,15521,15522],{},[412,15523,15526],{"href":15524,"rel":15525},"https:\u002F\u002Fsass-lang.com\u002Fblog\u002Flibsass-is-deprecated",[416],"Sass 官方公告：LibSass 棄用",[368,15528,15529],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}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);}",{"title":27,"searchDepth":51,"depth":51,"links":15531},[15532,15533,15534],{"id":15446,"depth":45,"text":15446},{"id":14383,"depth":45,"text":14383},{"id":4536,"depth":45,"text":4536},"記錄當時在 webpack 專案裡安裝 node-sass 與 sass-loader，讓建置工具能處理 .scss 檔案的做法。node-sass 已於 2020 年被 Sass 團隊正式棄用，現在改用 Dart Sass，整理在新的一篇。",{},"\u002Fposts\u002Fnode-sass-sass-loader","2020-04-08",[],{"title":15434,"description":15535},"posts\u002F20200408.node-sass-sass-loader",[4847,8410],"PxtJXRq5fUJaB3q7Vqw4x2BKNmJetviOXuFAQK-655w",{"id":15545,"title":15546,"body":15547,"category":378,"description":15973,"draft":380,"extension":381,"image":382,"meta":15974,"navigation":102,"path":15975,"publishedAt":15976,"relatedExperiments":15977,"seo":15978,"stem":15979,"tags":15980,"updatedAt":14433,"__hash__":15982},"posts\u002Fposts\u002F20200410.nuxt-layout-usage.md","Nuxt Layout 使用方式（Nuxt 2）",{"type":8,"value":15548,"toc":15968},[15549,15552,15556,15568,15675,15684,15688,15918,15934,15954,15956,15965],[11,15550,15551],{},"Nuxt 2 的 Layout（版面）機制讓不同頁面套用不同的外層骨架（例如某些頁面有側邊欄、某些頁面全螢幕沒有導覽列），這篇記使用方式。",[15,15553,15555],{"id":15554},"新增自訂-layout","新增自訂 Layout",[11,15557,5134,15558,15561,15562,15564,15565,5240],{},[29,15559,15560],{},"layouts\u002F"," 資料夾新增一個 ",[29,15563,5141],{}," 檔案，結構可以參照專案本來就有的 ",[29,15566,15567],{},"default.vue",[22,15569,15571],{"className":8665,"code":15570,"language":4112,"meta":27,"style":27},"\u003C!-- layouts\u002Fanimation.vue -->\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cnuxt \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  data() {\n    return {};\n  },\n};\n\u003C\u002Fscript>\n",[29,15572,15573,15578,15586,15594,15604,15612,15620,15624,15632,15640,15648,15655,15661,15667],{"__ignoreMap":27},[32,15574,15575],{"class":34,"line":35},[32,15576,15577],{"class":608},"\u003C!-- layouts\u002Fanimation.vue -->\n",[32,15579,15580,15582,15584],{"class":34,"line":45},[32,15581,4046],{"class":41},[32,15583,9623],{"class":4049},[32,15585,4062],{"class":41},[32,15587,15588,15590,15592],{"class":34,"line":51},[32,15589,4067],{"class":41},[32,15591,4050],{"class":4049},[32,15593,4062],{"class":41},[32,15595,15596,15598,15601],{"class":34,"line":90},[32,15597,9638],{"class":41},[32,15599,15600],{"class":4049},"nuxt",[32,15602,15603],{"class":41}," \u002F>\n",[32,15605,15606,15608,15610],{"class":34,"line":99},[32,15607,9691],{"class":41},[32,15609,4050],{"class":4049},[32,15611,4062],{"class":41},[32,15613,15614,15616,15618],{"class":34,"line":106},[32,15615,4105],{"class":41},[32,15617,9623],{"class":4049},[32,15619,4062],{"class":41},[32,15621,15622],{"class":34,"line":114},[32,15623,103],{"emptyLinePlaceholder":102},[32,15625,15626,15628,15630],{"class":34,"line":120},[32,15627,4046],{"class":41},[32,15629,4125],{"class":4049},[32,15631,4062],{"class":41},[32,15633,15634,15636,15638],{"class":34,"line":127},[32,15635,5038],{"class":38},[32,15637,5041],{"class":38},[32,15639,703],{"class":5049},[32,15641,15642,15644,15646],{"class":34,"line":132},[32,15643,9728],{"class":217},[32,15645,6075],{"class":5049},[32,15647,5252],{"class":41},[32,15649,15650,15652],{"class":34,"line":141},[32,15651,971],{"class":38},[32,15653,15654],{"class":41}," {};\n",[32,15656,15657,15659],{"class":34,"line":147},[32,15658,9758],{"class":41},[32,15660,547],{"class":5049},[32,15662,15663,15665],{"class":34,"line":275},[32,15664,9765],{"class":5049},[32,15666,96],{"class":41},[32,15668,15669,15671,15673],{"class":34,"line":282},[32,15670,4105],{"class":41},[32,15672,4125],{"class":4049},[32,15674,4062],{"class":41},[11,15676,15677,15680,15681,15683],{},[29,15678,15679],{},"\u003Cnuxt \u002F>"," 是 Nuxt 2 的固定寫法，代表「目前這個頁面的內容要渲染在這裡」，版面裡其他部分（導覽列、頁尾、動畫背景……）就自由加在 ",[29,15682,15679],{}," 的周圍。",[15,15685,15687],{"id":15686},"在頁面裡指定要用哪個-layout","在頁面裡指定要用哪個 Layout",[22,15689,15691],{"className":8665,"code":15690,"language":4112,"meta":27,"style":27},"\u003C!-- pages\u002Fanimation-page.vue -->\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003CSquareMonster \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { FontAwesomeIcon } from '@fortawesome\u002Fvue-fontawesome';\nimport { gsap } from 'gsap\u002Fdist\u002Fgsap';\nimport { ScrollToPlugin } from 'gsap\u002Fdist\u002FScrollToPlugin';\nimport CardAnimation from '~\u002Fcomponents\u002FCardAnimation';\nimport SquareMonster from '~\u002Fcomponents\u002FSquareMonster';\n\ngsap.registerPlugin(ScrollToPlugin);\n\nexport default {\n  layout: 'animation',\n  components: { CardAnimation, SquareMonster },\n  data() {\n    return {};\n  },\n  mounted() {},\n  methods: {},\n};\n\u003C\u002Fscript>\n",[29,15692,15693,15698,15706,15714,15723,15731,15739,15743,15751,15765,15779,15793,15807,15821,15825,15836,15840,15848,15858,15865,15873,15879,15885,15897,15904,15910],{"__ignoreMap":27},[32,15694,15695],{"class":34,"line":35},[32,15696,15697],{"class":608},"\u003C!-- pages\u002Fanimation-page.vue -->\n",[32,15699,15700,15702,15704],{"class":34,"line":45},[32,15701,4046],{"class":41},[32,15703,9623],{"class":4049},[32,15705,4062],{"class":41},[32,15707,15708,15710,15712],{"class":34,"line":51},[32,15709,4067],{"class":41},[32,15711,4050],{"class":4049},[32,15713,4062],{"class":41},[32,15715,15716,15718,15721],{"class":34,"line":90},[32,15717,9638],{"class":41},[32,15719,15720],{"class":4049},"SquareMonster",[32,15722,15603],{"class":41},[32,15724,15725,15727,15729],{"class":34,"line":99},[32,15726,9691],{"class":41},[32,15728,4050],{"class":4049},[32,15730,4062],{"class":41},[32,15732,15733,15735,15737],{"class":34,"line":106},[32,15734,4105],{"class":41},[32,15736,9623],{"class":4049},[32,15738,4062],{"class":41},[32,15740,15741],{"class":34,"line":114},[32,15742,103],{"emptyLinePlaceholder":102},[32,15744,15745,15747,15749],{"class":34,"line":120},[32,15746,4046],{"class":41},[32,15748,4125],{"class":4049},[32,15750,4062],{"class":41},[32,15752,15753,15755,15758,15760,15763],{"class":34,"line":127},[32,15754,8794],{"class":38},[32,15756,15757],{"class":41}," { FontAwesomeIcon } ",[32,15759,8850],{"class":38},[32,15761,15762],{"class":175}," '@fortawesome\u002Fvue-fontawesome'",[32,15764,96],{"class":41},[32,15766,15767,15769,15772,15774,15777],{"class":34,"line":132},[32,15768,8794],{"class":38},[32,15770,15771],{"class":41}," { gsap } ",[32,15773,8850],{"class":38},[32,15775,15776],{"class":175}," 'gsap\u002Fdist\u002Fgsap'",[32,15778,96],{"class":41},[32,15780,15781,15783,15786,15788,15791],{"class":34,"line":141},[32,15782,8794],{"class":38},[32,15784,15785],{"class":41}," { ScrollToPlugin } ",[32,15787,8850],{"class":38},[32,15789,15790],{"class":175}," 'gsap\u002Fdist\u002FScrollToPlugin'",[32,15792,96],{"class":41},[32,15794,15795,15797,15800,15802,15805],{"class":34,"line":147},[32,15796,8794],{"class":38},[32,15798,15799],{"class":41}," CardAnimation ",[32,15801,8850],{"class":38},[32,15803,15804],{"class":175}," '~\u002Fcomponents\u002FCardAnimation'",[32,15806,96],{"class":41},[32,15808,15809,15811,15814,15816,15819],{"class":34,"line":275},[32,15810,8794],{"class":38},[32,15812,15813],{"class":41}," SquareMonster ",[32,15815,8850],{"class":38},[32,15817,15818],{"class":175}," '~\u002Fcomponents\u002FSquareMonster'",[32,15820,96],{"class":41},[32,15822,15823],{"class":34,"line":282},[32,15824,103],{"emptyLinePlaceholder":102},[32,15826,15827,15830,15833],{"class":34,"line":295},[32,15828,15829],{"class":41},"gsap.",[32,15831,15832],{"class":217},"registerPlugin",[32,15834,15835],{"class":41},"(ScrollToPlugin);\n",[32,15837,15838],{"class":34,"line":605},[32,15839,103],{"emptyLinePlaceholder":102},[32,15841,15842,15844,15846],{"class":34,"line":612},[32,15843,5038],{"class":38},[32,15845,5041],{"class":38},[32,15847,703],{"class":5049},[32,15849,15850,15853,15856],{"class":34,"line":617},[32,15851,15852],{"class":41},"  layout: ",[32,15854,15855],{"class":175},"'animation'",[32,15857,547],{"class":5049},[32,15859,15860,15863],{"class":34,"line":644},[32,15861,15862],{"class":41},"  components: { CardAnimation, SquareMonster }",[32,15864,547],{"class":5049},[32,15866,15867,15869,15871],{"class":34,"line":649},[32,15868,9728],{"class":217},[32,15870,6075],{"class":5049},[32,15872,5252],{"class":41},[32,15874,15875,15877],{"class":34,"line":671},[32,15876,971],{"class":38},[32,15878,15654],{"class":41},[32,15880,15881,15883],{"class":34,"line":690},[32,15882,9758],{"class":41},[32,15884,547],{"class":5049},[32,15886,15887,15890,15892,15895],{"class":34,"line":696},[32,15888,15889],{"class":217},"  mounted",[32,15891,6075],{"class":5049},[32,15893,15894],{"class":41},"{}",[32,15896,547],{"class":5049},[32,15898,15899,15902],{"class":34,"line":706},[32,15900,15901],{"class":41},"  methods: {}",[32,15903,547],{"class":5049},[32,15905,15906,15908],{"class":34,"line":721},[32,15907,9765],{"class":5049},[32,15909,96],{"class":41},[32,15911,15912,15914,15916],{"class":34,"line":728},[32,15913,4105],{"class":41},[32,15915,4125],{"class":4049},[32,15917,4062],{"class":41},[11,15919,15920,15923,15924,15927,15928,15931,15932,3775],{},[29,15921,15922],{},"export default"," 裡的 ",[29,15925,15926],{},"layout: 'animation'"," 這個屬性，值對應到 ",[29,15929,15930],{},"layouts\u002Fanimation.vue"," 的檔名（不含副檔名）——這樣這個頁面就會套用剛剛新建的版面，而不是預設的 ",[29,15933,15567],{},[11,15935,15936,15937,15940,15941,15944,15945,15949,15950,15953],{},"後來 Nuxt 指定 Layout 的方式改了，",[29,15938,15939],{},"layout"," 屬性換成 ",[29,15942,15943],{},"definePageMeta","，完整寫法整理在《",[412,15946,15948],{"href":15947},"\u002Fposts\u002Fnuxt3-setup-notes","Nuxt 3 專案建立筆記","》的 Layout 段落。範例裡用到的 ",[29,15951,15952],{},"gsap.registerPlugin(ScrollToPlugin)"," 這行，是 GSAP 3 之後的外掛註冊方式，完整寫法整理在《GSAP 3 遷移筆記》。",[15,15955,4536],{"id":4536},[4538,15957,15958],{},[4541,15959,15960],{},[412,15961,15964],{"href":15962,"rel":15963},"https:\u002F\u002Fv2.nuxt.com\u002Fdocs\u002Fdirectory-structure\u002Flayouts",[416],"Nuxt 2：Layouts（封存版文件）",[368,15966,15967],{},"html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}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 .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}",{"title":27,"searchDepth":51,"depth":51,"links":15969},[15970,15971,15972],{"id":15554,"depth":45,"text":15555},{"id":15686,"depth":45,"text":15687},{"id":4536,"depth":45,"text":4536},"記錄在 Nuxt 2 的 layouts 資料夾新增一份自訂版面、頁面用 layout 屬性指定要套用哪個版面的做法，範例是一個包含 GSAP 動畫套件與 Font Awesome 圖示的版面。Nuxt 3 之後改用 definePageMeta 指定 layout，寫法不同。",{},"\u002Fposts\u002Fnuxt-layout-usage","2020-04-10",[],{"title":15546,"description":15973},"posts\u002F20200410.nuxt-layout-usage",[15981,4113],"Nuxt","m7tdZq-4t5GhGNeSnWdkADk2ISRi0lwoRfiqlDINONE",{"id":15984,"title":15985,"body":15986,"category":378,"description":16356,"draft":380,"extension":381,"image":382,"meta":16357,"navigation":102,"path":16358,"publishedAt":16359,"relatedExperiments":16360,"seo":16361,"stem":16362,"tags":16363,"updatedAt":14433,"__hash__":16365},"posts\u002Fposts\u002F20200413.js-sort-filter-arrow.md","JavaScript sort 與 filter 搭配箭頭函式的範例",{"type":8,"value":15987,"toc":16352},[15988,15991,16144,16179,16183,16268,16284,16287,16328,16331,16333,16349],[11,15989,15990],{},"用箭頭函式簡化陣列方法的寫法，一份商品清單當範例：",[22,15992,15994],{"className":24,"code":15993,"language":26,"meta":27,"style":27},"const shopData = [\n  { name: '美商博帝 120G SSD', price: 638 },\n  { name: 'SanDisk 120G SSD', price: 599 },\n  { name: '美光 MX500 250G SSD', price: 1499 },\n];\n\n\u002F\u002F 依價格排序\nconst sorted = shopData.sort((a, b) => a.price - b.price);\n\n\u002F\u002F 過濾出價格低於 600 的商品\nconst cheap = shopData.filter((shop) => shop.price \u003C 600);\n",[29,15995,15996,16008,16025,16039,16053,16057,16061,16066,16102,16106,16111],{"__ignoreMap":27},[32,15997,15998,16000,16003,16005],{"class":34,"line":35},[32,15999,6617],{"class":38},[32,16001,16002],{"class":351}," shopData",[32,16004,894],{"class":38},[32,16006,16007],{"class":41}," [\n",[32,16009,16010,16013,16016,16019,16022],{"class":34,"line":45},[32,16011,16012],{"class":41},"  { name: ",[32,16014,16015],{"class":175},"'美商博帝 120G SSD'",[32,16017,16018],{"class":41},", price: ",[32,16020,16021],{"class":351},"638",[32,16023,16024],{"class":41}," },\n",[32,16026,16027,16029,16032,16034,16037],{"class":34,"line":51},[32,16028,16012],{"class":41},[32,16030,16031],{"class":175},"'SanDisk 120G SSD'",[32,16033,16018],{"class":41},[32,16035,16036],{"class":351},"599",[32,16038,16024],{"class":41},[32,16040,16041,16043,16046,16048,16051],{"class":34,"line":90},[32,16042,16012],{"class":41},[32,16044,16045],{"class":175},"'美光 MX500 250G SSD'",[32,16047,16018],{"class":41},[32,16049,16050],{"class":351},"1499",[32,16052,16024],{"class":41},[32,16054,16055],{"class":34,"line":99},[32,16056,8282],{"class":41},[32,16058,16059],{"class":34,"line":106},[32,16060,103],{"emptyLinePlaceholder":102},[32,16062,16063],{"class":34,"line":114},[32,16064,16065],{"class":608},"\u002F\u002F 依價格排序\n",[32,16067,16068,16070,16073,16075,16078,16081,16083,16085,16087,16090,16092,16094,16097,16099],{"class":34,"line":120},[32,16069,6617],{"class":38},[32,16071,16072],{"class":351}," sorted",[32,16074,894],{"class":38},[32,16076,16077],{"class":41}," shopData.",[32,16079,16080],{"class":217},"sort",[32,16082,8293],{"class":41},[32,16084,412],{"class":903},[32,16086,635],{"class":41},[32,16088,16089],{"class":903},"b",[32,16091,483],{"class":41},[32,16093,5090],{"class":38},[32,16095,16096],{"class":41}," a.price ",[32,16098,6628],{"class":38},[32,16100,16101],{"class":41}," b.price);\n",[32,16103,16104],{"class":34,"line":127},[32,16105,103],{"emptyLinePlaceholder":102},[32,16107,16108],{"class":34,"line":132},[32,16109,16110],{"class":608},"\u002F\u002F 過濾出價格低於 600 的商品\n",[32,16112,16113,16115,16118,16120,16122,16125,16127,16130,16132,16134,16137,16139,16142],{"class":34,"line":141},[32,16114,6617],{"class":38},[32,16116,16117],{"class":351}," cheap",[32,16119,894],{"class":38},[32,16121,16077],{"class":41},[32,16123,16124],{"class":217},"filter",[32,16126,8293],{"class":41},[32,16128,16129],{"class":903},"shop",[32,16131,483],{"class":41},[32,16133,5090],{"class":38},[32,16135,16136],{"class":41}," shop.price ",[32,16138,4046],{"class":38},[32,16140,16141],{"class":351}," 600",[32,16143,230],{"class":41},[11,16145,16146,16149,16150,16152,16153,16155,16156,16158,16159,16162,16163,16165,16166,16168,16169,16172,16173,16175,16176,16178],{},[29,16147,16148],{},"sort()"," 的比較函式回傳負數代表 ",[29,16151,412],{}," 排在 ",[29,16154,16089],{}," 前面，回傳正數代表 ",[29,16157,16089],{}," 排在前面，",[29,16160,16161],{},"a.price - b.price"," 是「數字由小到大排序」最簡短的寫法——差值是負的（",[29,16164,412],{}," 比較小）就排前面，符合 ",[29,16167,16080],{}," 的規則。",[29,16170,16171],{},"filter()"," 則是回傳一個布林值，",[29,16174,6206],{}," 的項目留下、",[29,16177,9746],{}," 的項目濾掉。",[15,16180,16182],{"id":16181},"容易忽略的地方sort-會直接修改原陣列","容易忽略的地方：sort 會直接修改原陣列",[22,16184,16186],{"className":24,"code":16185,"language":26,"meta":27,"style":27},"const shopData = [{ price: 3 }, { price: 1 }, { price: 2 }];\nconst sorted = shopData.sort((a, b) => a.price - b.price);\n\nsorted === shopData; \u002F\u002F true，是同一個陣列\nshopData; \u002F\u002F 也被排序過了，不是原本的順序\n",[29,16187,16188,16213,16243,16247,16260],{"__ignoreMap":27},[32,16189,16190,16192,16194,16196,16199,16201,16204,16206,16208,16210],{"class":34,"line":35},[32,16191,6617],{"class":38},[32,16193,16002],{"class":351},[32,16195,894],{"class":38},[32,16197,16198],{"class":41}," [{ price: ",[32,16200,13470],{"class":351},[32,16202,16203],{"class":41}," }, { price: ",[32,16205,6544],{"class":351},[32,16207,16203],{"class":41},[32,16209,11971],{"class":351},[32,16211,16212],{"class":41}," }];\n",[32,16214,16215,16217,16219,16221,16223,16225,16227,16229,16231,16233,16235,16237,16239,16241],{"class":34,"line":45},[32,16216,6617],{"class":38},[32,16218,16072],{"class":351},[32,16220,894],{"class":38},[32,16222,16077],{"class":41},[32,16224,16080],{"class":217},[32,16226,8293],{"class":41},[32,16228,412],{"class":903},[32,16230,635],{"class":41},[32,16232,16089],{"class":903},[32,16234,483],{"class":41},[32,16236,5090],{"class":38},[32,16238,16096],{"class":41},[32,16240,6628],{"class":38},[32,16242,16101],{"class":41},[32,16244,16245],{"class":34,"line":51},[32,16246,103],{"emptyLinePlaceholder":102},[32,16248,16249,16252,16254,16257],{"class":34,"line":90},[32,16250,16251],{"class":41},"sorted ",[32,16253,5560],{"class":38},[32,16255,16256],{"class":41}," shopData; ",[32,16258,16259],{"class":608},"\u002F\u002F true，是同一個陣列\n",[32,16261,16262,16265],{"class":34,"line":99},[32,16263,16264],{"class":41},"shopData; ",[32,16266,16267],{"class":608},"\u002F\u002F 也被排序過了，不是原本的順序\n",[11,16269,16270,16272,16273,16276,16277,16280,16281,16283],{},[29,16271,16148],{}," 是",[12249,16274,16275],{},"原地排序","（in-place），不會回傳一份新陣列，回傳值就是排序後的原陣列本身。如果程式的其他地方還依賴 ",[29,16278,16279],{},"shopData"," 原本的順序，這個副作用很容易造成意料之外的 bug——",[29,16282,16171],{}," 不會有這個問題，它一律回傳新陣列，不動原本的資料。",[11,16285,16286],{},"需要保留原陣列不變時，排序前先複製一份：",[22,16288,16290],{"className":24,"code":16289,"language":26,"meta":27,"style":27},"const sorted = [...shopData].sort((a, b) => a.price - b.price);\n",[29,16291,16292],{"__ignoreMap":27},[32,16293,16294,16296,16298,16300,16302,16305,16308,16310,16312,16314,16316,16318,16320,16322,16324,16326],{"class":34,"line":35},[32,16295,6617],{"class":38},[32,16297,16072],{"class":351},[32,16299,894],{"class":38},[32,16301,8261],{"class":41},[32,16303,16304],{"class":38},"...",[32,16306,16307],{"class":41},"shopData].",[32,16309,16080],{"class":217},[32,16311,8293],{"class":41},[32,16313,412],{"class":903},[32,16315,635],{"class":41},[32,16317,16089],{"class":903},[32,16319,483],{"class":41},[32,16321,5090],{"class":38},[32,16323,16096],{"class":41},[32,16325,6628],{"class":38},[32,16327,16101],{"class":41},[11,16329,16330],{},"後來陣列方法又多了幾個好用的新成員，整理在《後來新增的陣列方法：at、findLast、toSorted 這一批》。",[15,16332,4536],{"id":4536},[4538,16334,16335,16342],{},[4541,16336,16337],{},[412,16338,16341],{"href":16339,"rel":16340},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-TW\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FArray\u002Fsort",[416],"MDN：Array.prototype.sort()",[4541,16343,16344],{},[412,16345,16348],{"href":16346,"rel":16347},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-TW\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FArray\u002Ffilter",[416],"MDN：Array.prototype.filter()",[368,16350,16351],{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}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);}",{"title":27,"searchDepth":51,"depth":51,"links":16353},[16354,16355],{"id":16181,"depth":45,"text":16182},{"id":4536,"depth":45,"text":4536},"用一份商品清單示範 sort 依價格排序、filter 過濾出低於門檻的商品，並提醒 sort 會直接修改原陣列這個容易忽略的副作用，排序前要先複製一份才能保留原始資料。",{},"\u002Fposts\u002Fjs-sort-filter-arrow","2020-04-13",[],{"title":15985,"description":16356},"posts\u002F20200413.js-sort-filter-arrow",[390,16364],"陣列方法","K2aqwXA86uQzUKR3Nku9vdXFn725WV7Wc1LY3vTd2E0",{"id":16367,"title":16368,"body":16369,"category":378,"description":17916,"draft":380,"extension":381,"image":382,"meta":17917,"navigation":102,"path":17918,"publishedAt":17919,"relatedExperiments":17920,"seo":17921,"stem":17922,"tags":17923,"updatedAt":14433,"__hash__":17925},"posts\u002Fposts\u002F20200419.js-prototype-inheritance.md","JavaScript 物件導向：建構函式、prototype 與繼承三步驟",{"type":8,"value":16370,"toc":17898},[16371,16374,16377,16391,16394,16398,16461,16464,16678,16687,16721,16725,16745,16749,16821,16847,16851,16854,16860,16866,16869,16873,16992,17022,17026,17075,17106,17127,17131,17152,17180,17184,17517,17535,17539,17542,17546,17549,17636,17640,17643,17751,17755,17761,17844,17847,17860,17877,17879,17895],[11,16372,16373],{},"物件導向的核心想法很單純：把情境裡的東西包裝成物件。步驟大致是：把情境描述成物件、定義這些物件的屬性跟行為、讓物件之間互動。",[11,16375,16376],{},"用「人」跟「香蕉」這兩個物件當例子：",[4538,16378,16379,16385],{},[4541,16380,16381,16384],{},[12249,16382,16383],{},"人","：屬性是名字、年齡、有的物品；行為是打招呼、走路、接收物品",[4541,16386,16387,16390],{},[12249,16388,16389],{},"香蕉","：屬性是名字、價格、味道；行為是剝皮、攻擊別人（比喻用，實際不會真的攻擊）",[11,16392,16393],{},"這篇整理 ES5 時代用建構函式手刻這套邏輯的完整流程。",[15,16395,16397],{"id":16396},"封裝把屬性跟方法包進一個建構函式","封裝：把屬性跟方法包進一個建構函式",[22,16399,16401],{"className":24,"code":16400,"language":26,"meta":27,"style":27},"var Ticket = function (初始值) {\n  this.屬性 = 初始值;\n  this.方法 = function () {\n    \u002F\u002F 這裡面可以用 this.屬性 跟 this.方法\n  };\n};\n",[29,16402,16403,16421,16433,16448,16453,16457],{"__ignoreMap":27},[32,16404,16405,16407,16410,16412,16414,16416,16419],{"class":34,"line":35},[32,16406,166],{"class":38},[32,16408,16409],{"class":217}," Ticket",[32,16411,894],{"class":38},[32,16413,897],{"class":38},[32,16415,900],{"class":41},[32,16417,16418],{"class":903},"初始值",[32,16420,520],{"class":41},[32,16422,16423,16425,16428,16430],{"class":34,"line":45},[32,16424,8938],{"class":351},[32,16426,16427],{"class":41},".屬性 ",[32,16429,172],{"class":38},[32,16431,16432],{"class":41}," 初始值;\n",[32,16434,16435,16437,16439,16442,16444,16446],{"class":34,"line":51},[32,16436,8938],{"class":351},[32,16438,1278],{"class":41},[32,16440,16441],{"class":217},"方法",[32,16443,894],{"class":38},[32,16445,897],{"class":38},[32,16447,668],{"class":41},[32,16449,16450],{"class":34,"line":90},[32,16451,16452],{"class":608},"    \u002F\u002F 這裡面可以用 this.屬性 跟 this.方法\n",[32,16454,16455],{"class":34,"line":99},[32,16456,1007],{"class":41},[32,16458,16459],{"class":34,"line":106},[32,16460,7189],{"class":41},[11,16462,16463],{},"用一個實際例子——一張票券，有價格、折扣、固定手續費，還有算總價跟印出描述的方法：",[22,16465,16467],{"className":24,"code":16466,"language":26,"meta":27,"style":27},"var Ticket = function (init_price, init_discount) {\n  \u002F\u002F 初始化\n  this.price = init_price;\n  this.type = 'student';\n  this.discount = init_discount;\n  this.fee = 30;\n\n  this.get_price = function () {\n    \u002F\u002F this 參照物件本體的資訊\n    return this.price * this.discount + this.fee;\n  };\n\n  this.get_description = function () {\n    console.log('這張票是' + this.type + '票，有' + this.discount * 10 + '折扣，共' + this.get_price() + '元');\n  };\n};\n",[29,16468,16469,16491,16496,16508,16522,16534,16548,16552,16567,16572,16593,16597,16601,16616,16670,16674],{"__ignoreMap":27},[32,16470,16471,16473,16475,16477,16479,16481,16484,16486,16489],{"class":34,"line":35},[32,16472,166],{"class":38},[32,16474,16409],{"class":217},[32,16476,894],{"class":38},[32,16478,897],{"class":38},[32,16480,900],{"class":41},[32,16482,16483],{"class":903},"init_price",[32,16485,635],{"class":41},[32,16487,16488],{"class":903},"init_discount",[32,16490,520],{"class":41},[32,16492,16493],{"class":34,"line":45},[32,16494,16495],{"class":608},"  \u002F\u002F 初始化\n",[32,16497,16498,16500,16503,16505],{"class":34,"line":51},[32,16499,8938],{"class":351},[32,16501,16502],{"class":41},".price ",[32,16504,172],{"class":38},[32,16506,16507],{"class":41}," init_price;\n",[32,16509,16510,16512,16515,16517,16520],{"class":34,"line":90},[32,16511,8938],{"class":351},[32,16513,16514],{"class":41},".type ",[32,16516,172],{"class":38},[32,16518,16519],{"class":175}," 'student'",[32,16521,96],{"class":41},[32,16523,16524,16526,16529,16531],{"class":34,"line":99},[32,16525,8938],{"class":351},[32,16527,16528],{"class":41},".discount ",[32,16530,172],{"class":38},[32,16532,16533],{"class":41}," init_discount;\n",[32,16535,16536,16538,16541,16543,16546],{"class":34,"line":106},[32,16537,8938],{"class":351},[32,16539,16540],{"class":41},".fee ",[32,16542,172],{"class":38},[32,16544,16545],{"class":351}," 30",[32,16547,96],{"class":41},[32,16549,16550],{"class":34,"line":114},[32,16551,103],{"emptyLinePlaceholder":102},[32,16553,16554,16556,16558,16561,16563,16565],{"class":34,"line":120},[32,16555,8938],{"class":351},[32,16557,1278],{"class":41},[32,16559,16560],{"class":217},"get_price",[32,16562,894],{"class":38},[32,16564,897],{"class":38},[32,16566,668],{"class":41},[32,16568,16569],{"class":34,"line":127},[32,16570,16571],{"class":608},"    \u002F\u002F this 參照物件本體的資訊\n",[32,16573,16574,16576,16578,16580,16582,16584,16586,16588,16590],{"class":34,"line":132},[32,16575,971],{"class":38},[32,16577,4234],{"class":351},[32,16579,16502],{"class":41},[32,16581,6078],{"class":38},[32,16583,4234],{"class":351},[32,16585,16528],{"class":41},[32,16587,224],{"class":38},[32,16589,4234],{"class":351},[32,16591,16592],{"class":41},".fee;\n",[32,16594,16595],{"class":34,"line":141},[32,16596,1007],{"class":41},[32,16598,16599],{"class":34,"line":147},[32,16600,103],{"emptyLinePlaceholder":102},[32,16602,16603,16605,16607,16610,16612,16614],{"class":34,"line":275},[32,16604,8938],{"class":351},[32,16606,1278],{"class":41},[32,16608,16609],{"class":217},"get_description",[32,16611,894],{"class":38},[32,16613,897],{"class":38},[32,16615,668],{"class":41},[32,16617,16618,16620,16622,16624,16627,16629,16631,16633,16635,16638,16640,16642,16644,16646,16648,16650,16653,16655,16657,16659,16661,16663,16665,16668],{"class":34,"line":282},[32,16619,8966],{"class":41},[32,16621,5197],{"class":217},[32,16623,477],{"class":41},[32,16625,16626],{"class":175},"'這張票是'",[32,16628,1799],{"class":38},[32,16630,4234],{"class":351},[32,16632,16514],{"class":41},[32,16634,224],{"class":38},[32,16636,16637],{"class":175}," '票，有'",[32,16639,1799],{"class":38},[32,16641,4234],{"class":351},[32,16643,16528],{"class":41},[32,16645,6078],{"class":38},[32,16647,4472],{"class":351},[32,16649,1799],{"class":38},[32,16651,16652],{"class":175}," '折扣，共'",[32,16654,1799],{"class":38},[32,16656,4234],{"class":351},[32,16658,1278],{"class":41},[32,16660,16560],{"class":217},[32,16662,6075],{"class":41},[32,16664,224],{"class":38},[32,16666,16667],{"class":175}," '元'",[32,16669,230],{"class":41},[32,16671,16672],{"class":34,"line":295},[32,16673,1007],{"class":41},[32,16675,16676],{"class":34,"line":605},[32,16677,7189],{"class":41},[11,16679,16680,16683,16684,16686],{},[12249,16681,16682],{},"建立實體物件","時用 ",[29,16685,9196],{},"，傳進去的參數對應建構函式裡的初始值：",[22,16688,16690],{"className":24,"code":16689,"language":26,"meta":27,"style":27},"var myticket = new Ticket(500, 0.5); \u002F\u002F 產生時，傳進物件價格跟折扣\n",[29,16691,16692],{"__ignoreMap":27},[32,16693,16694,16696,16699,16701,16703,16705,16707,16710,16712,16715,16718],{"class":34,"line":35},[32,16695,166],{"class":38},[32,16697,16698],{"class":41}," myticket ",[32,16700,172],{"class":38},[32,16702,4165],{"class":38},[32,16704,16409],{"class":217},[32,16706,477],{"class":41},[32,16708,16709],{"class":351},"500",[32,16711,635],{"class":41},[32,16713,16714],{"class":351},"0.5",[32,16716,16717],{"class":41},"); ",[32,16719,16720],{"class":608},"\u002F\u002F 產生時，傳進物件價格跟折扣\n",[15,16722,16724],{"id":16723},"這樣寫的問題每個實體都各自複製一份方法","這樣寫的問題：每個實體都各自複製一份方法",[11,16726,16727,16728,3750,16730,16732,16733,16736,16737,16740,16741,16744],{},"上面的寫法裡，",[29,16729,16560],{},[29,16731,16609],{}," 這兩個方法是寫在建構函式",[12249,16734,16735],{},"內部","的，代表每次 ",[29,16738,16739],{},"new Ticket(...)","，這兩個方法都會被",[12249,16742,16743],{},"重新建立一次、各自佔一份記憶體","——就算兩張票券的邏輯完全一樣，也是兩份獨立的函式物件。",[15,16746,16748],{"id":16747},"共用函式掛在-prototype-上","共用函式：掛在 prototype 上",[22,16750,16752],{"className":24,"code":16751,"language":26,"meta":27,"style":27},"var Ticket = function (初始值) {\n  this.屬性 = 初始值;\n};\n\n\u002F\u002F 共用函數\nTicket.prototype.方法 = function () {\n  \u002F\u002F 其中可使用 this.屬性 跟 this.方法\n};\n",[29,16753,16754,16770,16780,16784,16788,16793,16812,16817],{"__ignoreMap":27},[32,16755,16756,16758,16760,16762,16764,16766,16768],{"class":34,"line":35},[32,16757,166],{"class":38},[32,16759,16409],{"class":217},[32,16761,894],{"class":38},[32,16763,897],{"class":38},[32,16765,900],{"class":41},[32,16767,16418],{"class":903},[32,16769,520],{"class":41},[32,16771,16772,16774,16776,16778],{"class":34,"line":45},[32,16773,8938],{"class":351},[32,16775,16427],{"class":41},[32,16777,172],{"class":38},[32,16779,16432],{"class":41},[32,16781,16782],{"class":34,"line":51},[32,16783,7189],{"class":41},[32,16785,16786],{"class":34,"line":90},[32,16787,103],{"emptyLinePlaceholder":102},[32,16789,16790],{"class":34,"line":99},[32,16791,16792],{"class":608},"\u002F\u002F 共用函數\n",[32,16794,16795,16798,16800,16802,16804,16806,16808,16810],{"class":34,"line":106},[32,16796,16797],{"class":351},"Ticket",[32,16799,1278],{"class":41},[32,16801,1585],{"class":351},[32,16803,1278],{"class":41},[32,16805,16441],{"class":217},[32,16807,894],{"class":38},[32,16809,897],{"class":38},[32,16811,668],{"class":41},[32,16813,16814],{"class":34,"line":114},[32,16815,16816],{"class":608},"  \u002F\u002F 其中可使用 this.屬性 跟 this.方法\n",[32,16818,16819],{"class":34,"line":120},[32,16820,7189],{"class":41},[11,16822,16823,16824,16826,16827,16830,16831,16833,16834,16836,16837,16839,16840,16842,16843,16846],{},"放在 ",[29,16825,1585],{}," 裡的東西",[12249,16828,16829],{},"不會被複製進每一個物件","，物件本身只會有一個參考連到 ",[29,16832,1585],{},"；存取屬性或方法時，先在物件自己身上找，找不到才會往上到 ",[29,16835,1585],{}," 找。這代表不管 ",[29,16838,9196],{}," 出幾個實體，",[29,16841,1585],{}," 上的方法永遠只有",[12249,16844,16845],{},"一份","，多個實體共用同一份參考。",[15,16848,16850],{"id":16849},"繼承類別之間的延續","繼承：類別之間的延續",[11,16852,16853],{},"先定義一個比較上層、比較抽象的類別「生物」，再讓「狗」繼承它：",[22,16855,16858],{"className":16856,"code":16857,"language":426,"meta":27},[424],"生物 (Creature)：名字、生命長度、移動、吃東西\n狗 (Dog)：品種、喜歡吃的東西、吠叫、攻擊\n",[29,16859,16857],{"__ignoreMap":27},[11,16861,16862,16863,3775],{},"「狗」除了自己的專屬屬性（品種、吠叫），也應該擁有「生物」共通的那些（名字、吃東西）——這就是繼承要解決的問題：",[12249,16864,16865],{},"避免每個子類別都要重新定義一次共通的部分",[11,16867,16868],{},"繼承的完整步驟分三步：",[151,16870,16872],{"id":16871},"步驟一建立時呼叫母類別的建構子","步驟一：建立時呼叫母類別的建構子",[22,16874,16876],{"className":24,"code":16875,"language":26,"meta":27,"style":27},"var Creature = function (name, lifespan) {\n  this.name = name;\n  this.lifespan = lifespan;\n};\n\nvar Dog = function (name, lifespan, breed) {\n  Creature.call(this, name, lifespan); \u002F\u002F 呼叫母類別，針對母類別進行初始化\n  this.breed = breed;\n};\n",[29,16877,16878,16901,16913,16925,16929,16933,16959,16976,16988],{"__ignoreMap":27},[32,16879,16880,16882,16885,16887,16889,16891,16894,16896,16899],{"class":34,"line":35},[32,16881,166],{"class":38},[32,16883,16884],{"class":217}," Creature",[32,16886,894],{"class":38},[32,16888,897],{"class":38},[32,16890,900],{"class":41},[32,16892,16893],{"class":903},"name",[32,16895,635],{"class":41},[32,16897,16898],{"class":903},"lifespan",[32,16900,520],{"class":41},[32,16902,16903,16905,16908,16910],{"class":34,"line":45},[32,16904,8938],{"class":351},[32,16906,16907],{"class":41},".name ",[32,16909,172],{"class":38},[32,16911,16912],{"class":41}," name;\n",[32,16914,16915,16917,16920,16922],{"class":34,"line":51},[32,16916,8938],{"class":351},[32,16918,16919],{"class":41},".lifespan ",[32,16921,172],{"class":38},[32,16923,16924],{"class":41}," lifespan;\n",[32,16926,16927],{"class":34,"line":90},[32,16928,7189],{"class":41},[32,16930,16931],{"class":34,"line":99},[32,16932,103],{"emptyLinePlaceholder":102},[32,16934,16935,16937,16940,16942,16944,16946,16948,16950,16952,16954,16957],{"class":34,"line":106},[32,16936,166],{"class":38},[32,16938,16939],{"class":217}," Dog",[32,16941,894],{"class":38},[32,16943,897],{"class":38},[32,16945,900],{"class":41},[32,16947,16893],{"class":903},[32,16949,635],{"class":41},[32,16951,16898],{"class":903},[32,16953,635],{"class":41},[32,16955,16956],{"class":903},"breed",[32,16958,520],{"class":41},[32,16960,16961,16964,16966,16968,16970,16973],{"class":34,"line":114},[32,16962,16963],{"class":41},"  Creature.",[32,16965,1596],{"class":217},[32,16967,477],{"class":41},[32,16969,1083],{"class":351},[32,16971,16972],{"class":41},", name, lifespan); ",[32,16974,16975],{"class":608},"\u002F\u002F 呼叫母類別，針對母類別進行初始化\n",[32,16977,16978,16980,16983,16985],{"class":34,"line":120},[32,16979,8938],{"class":351},[32,16981,16982],{"class":41},".breed ",[32,16984,172],{"class":38},[32,16986,16987],{"class":41}," breed;\n",[32,16989,16990],{"class":34,"line":127},[32,16991,7189],{"class":41},[11,16993,16994,16997,16998,17001,17002,17004,17005,17008,17009,17012,17013,17015,17016,17018,17019,17021],{},[29,16995,16996],{},"Creature.call(this, name, lifespan)"," 的意思是「借用 ",[29,16999,17000],{},"Creature"," 這個建構函式，但讓它的 ",[29,17003,1083],{}," 指向現在正在建立的 ",[29,17006,17007],{},"Dog"," 實體」——這一步只處理",[12249,17010,17011],{},"屬性","的繼承，",[29,17014,17000],{}," 建構子裡對 ",[29,17017,1083],{}," 做的初始化，會套用在正在建立的這個 ",[29,17020,17007],{}," 物件上。",[151,17023,17025],{"id":17024},"步驟二讓子類別的-prototype-接上母類別的-prototype","步驟二：讓子類別的 prototype 接上母類別的 prototype",[22,17027,17029],{"className":24,"code":17028,"language":26,"meta":27,"style":27},"Dog.prototype = Object.create(Creature.prototype);\nDog.prototype.constructor = Dog;\n",[29,17030,17031,17057],{"__ignoreMap":27},[32,17032,17033,17035,17037,17039,17041,17044,17047,17049,17051,17053,17055],{"class":34,"line":35},[32,17034,17007],{"class":351},[32,17036,1278],{"class":41},[32,17038,1585],{"class":351},[32,17040,894],{"class":38},[32,17042,17043],{"class":41}," Object.",[32,17045,17046],{"class":217},"create",[32,17048,477],{"class":41},[32,17050,17000],{"class":351},[32,17052,1278],{"class":41},[32,17054,1585],{"class":351},[32,17056,230],{"class":41},[32,17058,17059,17061,17063,17065,17067,17070,17072],{"class":34,"line":45},[32,17060,17007],{"class":351},[32,17062,1278],{"class":41},[32,17064,1585],{"class":351},[32,17066,1278],{"class":41},[32,17068,17069],{"class":351},"constructor",[32,17071,894],{"class":38},[32,17073,17074],{"class":41}," Dog;\n",[11,17076,17077,17080,17081,17084,17085,17088,17089,17092,17093,17095,17096,17098,17099,17102,17103,17105],{},[29,17078,17079],{},"Object.create(Creature.prototype)"," 建立一個",[12249,17082,17083],{},"空的中繼物件","，這個物件的原型鏈往上連到 ",[29,17086,17087],{},"Creature.prototype","，再把它指派給 ",[29,17090,17091],{},"Dog.prototype","。這一步做完之後，",[29,17094,17007],{}," 的實體如果在自己身上跟 ",[29,17097,17091],{}," 都找不到某個方法，會",[12249,17100,17101],{},"再往上一層","去 ",[29,17104,17087],{}," 找。",[11,17107,17108,17111,17112,17115,17116,17118,17119,17122,17123,17126],{},[12249,17109,17110],{},"只做步驟一不夠","：步驟一只繼承了「屬性」（建構子裡 ",[29,17113,17114],{},"this.xxx = xxx"," 那些），母類別掛在 ",[29,17117,1585],{}," 上的",[12249,17120,17121],{},"共用方法","沒有被繼承到，因為步驟一的 ",[29,17124,17125],{},"call()"," 只是借用建構函式執行了一次，不會動到原型鏈的連結，這正是需要步驟二的原因。",[151,17128,17130],{"id":17129},"步驟三把建構函式指回自己","步驟三：把建構函式指回自己",[22,17132,17134],{"className":24,"code":17133,"language":26,"meta":27,"style":27},"Dog.prototype.constructor = Dog;\n",[29,17135,17136],{"__ignoreMap":27},[32,17137,17138,17140,17142,17144,17146,17148,17150],{"class":34,"line":35},[32,17139,17007],{"class":351},[32,17141,1278],{"class":41},[32,17143,1585],{"class":351},[32,17145,1278],{"class":41},[32,17147,17069],{"class":351},[32,17149,894],{"class":38},[32,17151,17074],{"class":41},[11,17153,17154,17155,17158,17159,17161,17162,17164,17165,17167,17168,17170,17171,17174,17175,17177,17178,3775],{},"步驟二用 ",[29,17156,17157],{},"Object.create()"," 整個換掉了 ",[29,17160,17091],{},"，這個新物件的 ",[29,17163,17069],{}," 屬性預設會指向 ",[29,17166,17000],{},"（因為它是從 ",[29,17169,17087],{}," 複製結構過來的），不修正的話，",[29,17172,17173],{},"someDogInstance.constructor"," 會誤指到 ",[29,17176,17000],{},"，這一步負責把它導正回 ",[29,17179,17007],{},[15,17181,17183],{"id":17182},"完整範例人的繼承","完整範例：人的繼承",[22,17185,17187],{"className":24,"code":17186,"language":26,"meta":27,"style":27},"\u002F\u002F 母類別\nvar Person = function (name, age) {\n  this.name = name;\n  this.age = age;\n};\n\nPerson.prototype.sayHello = function () {\n  console.log('hello i am ' + this.name);\n};\n\n\u002F\u002F 子類別：Worker 繼承 Person\nvar Worker = function (name, age, job) {\n  Person.call(this, name, age); \u002F\u002F 步驟一：呼叫母類別初始化\n  this.job = job; \u002F\u002F 設定自己的額外屬性\n};\n\nWorker.prototype = Object.create(Person.prototype); \u002F\u002F 步驟二：接上原型鏈\nWorker.prototype.constructor = Worker; \u002F\u002F 步驟三：建構函式指回自己\n\nWorker.prototype.tellJob = function () {\n  \u002F\u002F Worker 自己的共用方法\n  console.log('I am a ' + this.job);\n};\n\nvar henry = new Worker('Henry', 30, 'Teacher');\nhenry.sayHello(); \u002F\u002F hello i am Henry ← 繼承自 Person.prototype\nhenry.tellJob(); \u002F\u002F I am a Teacher   ← Worker 自己的方法\n",[29,17188,17189,17194,17216,17226,17238,17242,17246,17266,17283,17287,17291,17296,17322,17339,17354,17358,17362,17390,17410,17414,17433,17438,17456,17460,17464,17494,17506],{"__ignoreMap":27},[32,17190,17191],{"class":34,"line":35},[32,17192,17193],{"class":608},"\u002F\u002F 母類別\n",[32,17195,17196,17198,17201,17203,17205,17207,17209,17211,17214],{"class":34,"line":45},[32,17197,166],{"class":38},[32,17199,17200],{"class":217}," Person",[32,17202,894],{"class":38},[32,17204,897],{"class":38},[32,17206,900],{"class":41},[32,17208,16893],{"class":903},[32,17210,635],{"class":41},[32,17212,17213],{"class":903},"age",[32,17215,520],{"class":41},[32,17217,17218,17220,17222,17224],{"class":34,"line":51},[32,17219,8938],{"class":351},[32,17221,16907],{"class":41},[32,17223,172],{"class":38},[32,17225,16912],{"class":41},[32,17227,17228,17230,17233,17235],{"class":34,"line":90},[32,17229,8938],{"class":351},[32,17231,17232],{"class":41},".age ",[32,17234,172],{"class":38},[32,17236,17237],{"class":41}," age;\n",[32,17239,17240],{"class":34,"line":99},[32,17241,7189],{"class":41},[32,17243,17244],{"class":34,"line":106},[32,17245,103],{"emptyLinePlaceholder":102},[32,17247,17248,17251,17253,17255,17257,17260,17262,17264],{"class":34,"line":114},[32,17249,17250],{"class":351},"Person",[32,17252,1278],{"class":41},[32,17254,1585],{"class":351},[32,17256,1278],{"class":41},[32,17258,17259],{"class":217},"sayHello",[32,17261,894],{"class":38},[32,17263,897],{"class":38},[32,17265,668],{"class":41},[32,17267,17268,17270,17272,17274,17277,17279,17281],{"class":34,"line":120},[32,17269,5194],{"class":41},[32,17271,5197],{"class":217},[32,17273,477],{"class":41},[32,17275,17276],{"class":175},"'hello i am '",[32,17278,1799],{"class":38},[32,17280,4234],{"class":351},[32,17282,11345],{"class":41},[32,17284,17285],{"class":34,"line":127},[32,17286,7189],{"class":41},[32,17288,17289],{"class":34,"line":132},[32,17290,103],{"emptyLinePlaceholder":102},[32,17292,17293],{"class":34,"line":141},[32,17294,17295],{"class":608},"\u002F\u002F 子類別：Worker 繼承 Person\n",[32,17297,17298,17300,17303,17305,17307,17309,17311,17313,17315,17317,17320],{"class":34,"line":147},[32,17299,166],{"class":38},[32,17301,17302],{"class":217}," Worker",[32,17304,894],{"class":38},[32,17306,897],{"class":38},[32,17308,900],{"class":41},[32,17310,16893],{"class":903},[32,17312,635],{"class":41},[32,17314,17213],{"class":903},[32,17316,635],{"class":41},[32,17318,17319],{"class":903},"job",[32,17321,520],{"class":41},[32,17323,17324,17327,17329,17331,17333,17336],{"class":34,"line":275},[32,17325,17326],{"class":41},"  Person.",[32,17328,1596],{"class":217},[32,17330,477],{"class":41},[32,17332,1083],{"class":351},[32,17334,17335],{"class":41},", name, age); ",[32,17337,17338],{"class":608},"\u002F\u002F 步驟一：呼叫母類別初始化\n",[32,17340,17341,17343,17346,17348,17351],{"class":34,"line":282},[32,17342,8938],{"class":351},[32,17344,17345],{"class":41},".job ",[32,17347,172],{"class":38},[32,17349,17350],{"class":41}," job; ",[32,17352,17353],{"class":608},"\u002F\u002F 設定自己的額外屬性\n",[32,17355,17356],{"class":34,"line":295},[32,17357,7189],{"class":41},[32,17359,17360],{"class":34,"line":605},[32,17361,103],{"emptyLinePlaceholder":102},[32,17363,17364,17367,17369,17371,17373,17375,17377,17379,17381,17383,17385,17387],{"class":34,"line":612},[32,17365,17366],{"class":351},"Worker",[32,17368,1278],{"class":41},[32,17370,1585],{"class":351},[32,17372,894],{"class":38},[32,17374,17043],{"class":41},[32,17376,17046],{"class":217},[32,17378,477],{"class":41},[32,17380,17250],{"class":351},[32,17382,1278],{"class":41},[32,17384,1585],{"class":351},[32,17386,16717],{"class":41},[32,17388,17389],{"class":608},"\u002F\u002F 步驟二：接上原型鏈\n",[32,17391,17392,17394,17396,17398,17400,17402,17404,17407],{"class":34,"line":617},[32,17393,17366],{"class":351},[32,17395,1278],{"class":41},[32,17397,1585],{"class":351},[32,17399,1278],{"class":41},[32,17401,17069],{"class":351},[32,17403,894],{"class":38},[32,17405,17406],{"class":41}," Worker; ",[32,17408,17409],{"class":608},"\u002F\u002F 步驟三：建構函式指回自己\n",[32,17411,17412],{"class":34,"line":644},[32,17413,103],{"emptyLinePlaceholder":102},[32,17415,17416,17418,17420,17422,17424,17427,17429,17431],{"class":34,"line":649},[32,17417,17366],{"class":351},[32,17419,1278],{"class":41},[32,17421,1585],{"class":351},[32,17423,1278],{"class":41},[32,17425,17426],{"class":217},"tellJob",[32,17428,894],{"class":38},[32,17430,897],{"class":38},[32,17432,668],{"class":41},[32,17434,17435],{"class":34,"line":671},[32,17436,17437],{"class":608},"  \u002F\u002F Worker 自己的共用方法\n",[32,17439,17440,17442,17444,17446,17449,17451,17453],{"class":34,"line":690},[32,17441,5194],{"class":41},[32,17443,5197],{"class":217},[32,17445,477],{"class":41},[32,17447,17448],{"class":175},"'I am a '",[32,17450,1799],{"class":38},[32,17452,4234],{"class":351},[32,17454,17455],{"class":41},".job);\n",[32,17457,17458],{"class":34,"line":696},[32,17459,7189],{"class":41},[32,17461,17462],{"class":34,"line":706},[32,17463,103],{"emptyLinePlaceholder":102},[32,17465,17466,17468,17471,17473,17475,17477,17479,17482,17484,17487,17489,17492],{"class":34,"line":721},[32,17467,166],{"class":38},[32,17469,17470],{"class":41}," henry ",[32,17472,172],{"class":38},[32,17474,4165],{"class":38},[32,17476,17302],{"class":217},[32,17478,477],{"class":41},[32,17480,17481],{"class":175},"'Henry'",[32,17483,635],{"class":41},[32,17485,17486],{"class":351},"30",[32,17488,635],{"class":41},[32,17490,17491],{"class":175},"'Teacher'",[32,17493,230],{"class":41},[32,17495,17496,17499,17501,17503],{"class":34,"line":728},[32,17497,17498],{"class":41},"henry.",[32,17500,17259],{"class":217},[32,17502,11361],{"class":41},[32,17504,17505],{"class":608},"\u002F\u002F hello i am Henry ← 繼承自 Person.prototype\n",[32,17507,17508,17510,17512,17514],{"class":34,"line":737},[32,17509,17498],{"class":41},[32,17511,17426],{"class":217},[32,17513,11361],{"class":41},[32,17515,17516],{"class":608},"\u002F\u002F I am a Teacher   ← Worker 自己的方法\n",[11,17518,17519,17522,17523,17526,17527,17530,17531,17534],{},[29,17520,17521],{},"henry.sayHello()"," 這個方法沒有寫在 ",[29,17524,17525],{},"Worker.prototype"," 上，",[29,17528,17529],{},"henry"," 自己身上也沒有，會一路往上找到 ",[29,17532,17533],{},"Person.prototype"," 才找到——這就是原型鏈繼承實際運作的樣子。",[15,17536,17538],{"id":17537},"順便記的-es6-物件寫法","順便記的 ES6 物件寫法",[11,17540,17541],{},"練習物件導向的過程中，也整理了幾個 ES6 讓物件字面量寫起來更精簡的語法，跟上面的繼承主題不算同一件事，但當時是一起記的。",[151,17543,17545],{"id":17544},"物件縮寫object-shorthand","物件縮寫（Object Shorthand）",[11,17547,17548],{},"屬性值剛好跟一個同名變數對應時，可以省略冒號跟重複的名稱：",[22,17550,17552],{"className":24,"code":17551,"language":26,"meta":27,"style":27},"\u002F\u002F 完整寫法\nfunction makePoint(x, y) {\n  return {\n    x: x,\n    y: y,\n  };\n}\n\n\u002F\u002F 縮寫\nfunction makePoint(x, y) {\n  return { x, y };\n}\n",[29,17553,17554,17559,17576,17582,17587,17592,17596,17600,17604,17609,17625,17632],{"__ignoreMap":27},[32,17555,17556],{"class":34,"line":35},[32,17557,17558],{"class":608},"\u002F\u002F 完整寫法\n",[32,17560,17561,17563,17566,17568,17570,17572,17574],{"class":34,"line":45},[32,17562,665],{"class":38},[32,17564,17565],{"class":217}," makePoint",[32,17567,477],{"class":41},[32,17569,10967],{"class":903},[32,17571,635],{"class":41},[32,17573,11098],{"class":903},[32,17575,520],{"class":41},[32,17577,17578,17580],{"class":34,"line":51},[32,17579,10974],{"class":38},[32,17581,703],{"class":41},[32,17583,17584],{"class":34,"line":90},[32,17585,17586],{"class":41},"    x: x,\n",[32,17588,17589],{"class":34,"line":99},[32,17590,17591],{"class":41},"    y: y,\n",[32,17593,17594],{"class":34,"line":106},[32,17595,1007],{"class":41},[32,17597,17598],{"class":34,"line":114},[32,17599,54],{"class":41},[32,17601,17602],{"class":34,"line":120},[32,17603,103],{"emptyLinePlaceholder":102},[32,17605,17606],{"class":34,"line":127},[32,17607,17608],{"class":608},"\u002F\u002F 縮寫\n",[32,17610,17611,17613,17615,17617,17619,17621,17623],{"class":34,"line":132},[32,17612,665],{"class":38},[32,17614,17565],{"class":217},[32,17616,477],{"class":41},[32,17618,10967],{"class":903},[32,17620,635],{"class":41},[32,17622,11098],{"class":903},[32,17624,520],{"class":41},[32,17626,17627,17629],{"class":34,"line":141},[32,17628,10974],{"class":38},[32,17630,17631],{"class":41}," { x, y };\n",[32,17633,17634],{"class":34,"line":147},[32,17635,54],{"class":41},[151,17637,17639],{"id":17638},"計算屬性computed-property-names","計算屬性（Computed Property Names）",[11,17641,17642],{},"屬性名稱需要動態決定時，用中括號包住運算式：",[22,17644,17646],{"className":24,"code":17645,"language":26,"meta":27,"style":27},"function createObject(key, value) {\n  const obj = {\n    [key + '1']: value, \u002F\u002F 動態決定屬性名稱\n  };\n  return obj;\n}\n\ncreateObject('name', 'John'); \u002F\u002F { name1: 'John' }\ncreateObject('legs', 4); \u002F\u002F { legs1: 4 }\n",[29,17647,17648,17667,17678,17694,17698,17705,17709,17713,17733],{"__ignoreMap":27},[32,17649,17650,17652,17655,17657,17660,17662,17665],{"class":34,"line":35},[32,17651,665],{"class":38},[32,17653,17654],{"class":217}," createObject",[32,17656,477],{"class":41},[32,17658,17659],{"class":903},"key",[32,17661,635],{"class":41},[32,17663,17664],{"class":903},"value",[32,17666,520],{"class":41},[32,17668,17669,17671,17674,17676],{"class":34,"line":45},[32,17670,4157],{"class":38},[32,17672,17673],{"class":351}," obj",[32,17675,894],{"class":38},[32,17677,703],{"class":41},[32,17679,17680,17683,17685,17688,17691],{"class":34,"line":51},[32,17681,17682],{"class":41},"    [key ",[32,17684,224],{"class":38},[32,17686,17687],{"class":175}," '1'",[32,17689,17690],{"class":41},"]: value, ",[32,17692,17693],{"class":608},"\u002F\u002F 動態決定屬性名稱\n",[32,17695,17696],{"class":34,"line":90},[32,17697,1007],{"class":41},[32,17699,17700,17702],{"class":34,"line":99},[32,17701,10974],{"class":38},[32,17703,17704],{"class":41}," obj;\n",[32,17706,17707],{"class":34,"line":106},[32,17708,54],{"class":41},[32,17710,17711],{"class":34,"line":114},[32,17712,103],{"emptyLinePlaceholder":102},[32,17714,17715,17718,17720,17723,17725,17728,17730],{"class":34,"line":120},[32,17716,17717],{"class":217},"createObject",[32,17719,477],{"class":41},[32,17721,17722],{"class":175},"'name'",[32,17724,635],{"class":41},[32,17726,17727],{"class":175},"'John'",[32,17729,16717],{"class":41},[32,17731,17732],{"class":608},"\u002F\u002F { name1: 'John' }\n",[32,17734,17735,17737,17739,17742,17744,17746,17748],{"class":34,"line":127},[32,17736,17717],{"class":217},[32,17738,477],{"class":41},[32,17740,17741],{"class":175},"'legs'",[32,17743,635],{"class":41},[32,17745,13478],{"class":351},[32,17747,16717],{"class":41},[32,17749,17750],{"class":608},"\u002F\u002F { legs1: 4 }\n",[151,17752,17754],{"id":17753},"函式縮寫method-shorthand","函式縮寫（Method Shorthand）",[11,17756,17757,17758,5240],{},"物件裡的方法不用再寫 ",[29,17759,17760],{},"key: function () {}",[22,17762,17764],{"className":24,"code":17763,"language":26,"meta":27,"style":27},"\u002F\u002F 完整寫法\nconst options = {\n  name: 'Options',\n  created: function () {},\n};\n\n\u002F\u002F ES6 縮寫\nconst options = {\n  name: 'Options',\n  created() {},\n};\n",[29,17765,17766,17770,17781,17790,17802,17806,17810,17815,17825,17833,17840],{"__ignoreMap":27},[32,17767,17768],{"class":34,"line":35},[32,17769,17558],{"class":608},[32,17771,17772,17774,17777,17779],{"class":34,"line":45},[32,17773,6617],{"class":38},[32,17775,17776],{"class":351}," options",[32,17778,894],{"class":38},[32,17780,703],{"class":41},[32,17782,17783,17785,17788],{"class":34,"line":51},[32,17784,11439],{"class":41},[32,17786,17787],{"class":175},"'Options'",[32,17789,547],{"class":41},[32,17791,17792,17795,17797,17799],{"class":34,"line":90},[32,17793,17794],{"class":217},"  created",[32,17796,5269],{"class":41},[32,17798,665],{"class":38},[32,17800,17801],{"class":41}," () {},\n",[32,17803,17804],{"class":34,"line":99},[32,17805,7189],{"class":41},[32,17807,17808],{"class":34,"line":106},[32,17809,103],{"emptyLinePlaceholder":102},[32,17811,17812],{"class":34,"line":114},[32,17813,17814],{"class":608},"\u002F\u002F ES6 縮寫\n",[32,17816,17817,17819,17821,17823],{"class":34,"line":120},[32,17818,6617],{"class":38},[32,17820,17776],{"class":351},[32,17822,894],{"class":38},[32,17824,703],{"class":41},[32,17826,17827,17829,17831],{"class":34,"line":127},[32,17828,11439],{"class":41},[32,17830,17787],{"class":175},[32,17832,547],{"class":41},[32,17834,17835,17837],{"class":34,"line":132},[32,17836,17794],{"class":217},[32,17838,17839],{"class":41},"() {},\n",[32,17841,17842],{"class":34,"line":141},[32,17843,7189],{"class":41},[15,17845,17846],{"id":17846},"現在還適用嗎",[11,17848,17849,17852,17853,17856,17857,17859],{},[12249,17850,17851],{},"原型鏈這套底層機制到現在完全沒變","，",[29,17854,17855],{},"class"," 語法（ES6 就有）背後運作的原理就是這篇整理的 ",[29,17858,1585],{}," 鏈接，不是另一套全新的東西。物件縮寫、計算屬性、函式縮寫這幾個 ES6 寫法現在也還是日常寫法，沒有過時。",[11,17861,17862,17863,17866,17867,17869,17870,17872,17873,17876],{},"真正變的是",[12249,17864,17865],{},"手刻繼承這件事的人","——現在幾乎沒有人會再手寫 ",[29,17868,17157],{}," 加修正 ",[29,17871,17069],{}," 這三步驟，",[29,17874,17875],{},"class extends"," 把這一整套樣板程式碼包裝起來，寫法更接近其他物件導向語言、也更不容易漏掉某一步。整理在《ES6 Class：prototype 繼承的語法糖》。",[15,17878,4536],{"id":4536},[4538,17880,17881,17888],{},[4541,17882,17883],{},[412,17884,17887],{"href":17885,"rel":17886},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-TW\u002Fdocs\u002FWeb\u002FJavaScript\u002FInheritance_and_the_prototype_chain",[416],"MDN：繼承與原型鏈",[4541,17889,17890],{},[412,17891,17894],{"href":17892,"rel":17893},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-TW\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FObject\u002Fcreate",[416],"MDN：Object.create()",[368,17896,17897],{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}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 .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}",{"title":27,"searchDepth":51,"depth":51,"links":17899},[17900,17901,17902,17903,17908,17909,17914,17915],{"id":16396,"depth":45,"text":16397},{"id":16723,"depth":45,"text":16724},{"id":16747,"depth":45,"text":16748},{"id":16849,"depth":45,"text":16850,"children":17904},[17905,17906,17907],{"id":16871,"depth":51,"text":16872},{"id":17024,"depth":51,"text":17025},{"id":17129,"depth":51,"text":17130},{"id":17182,"depth":45,"text":17183},{"id":17537,"depth":45,"text":17538,"children":17910},[17911,17912,17913],{"id":17544,"depth":51,"text":17545},{"id":17638,"depth":51,"text":17639},{"id":17753,"depth":51,"text":17754},{"id":17846,"depth":45,"text":17846},{"id":4536,"depth":45,"text":4536},"整理 ES5 時代用建構函式加 prototype 手刻物件導向的完整流程——封裝屬性與方法、共用函式為什麼要掛在 prototype 上、以及繼承的三個步驟（call 借用母類別建構子、Object.create 接上原型鏈、修正 constructor 指向），並附上物件縮寫與計算屬性這兩個順便學到的 ES6 寫法。",{},"\u002Fposts\u002Fjs-prototype-inheritance","2020-04-19",[],{"title":16368,"description":17916},"posts\u002F20200419.js-prototype-inheritance",[390,17924,1585],"物件導向","I__C0RwVSthzWrbGPTNo4Q9ErBM_ZjfZSQQKSCNHGdI",{"id":17927,"title":17928,"body":17929,"category":4012,"description":18668,"draft":380,"extension":381,"image":382,"meta":18669,"navigation":102,"path":18670,"publishedAt":18671,"relatedExperiments":18672,"seo":18673,"stem":18674,"tags":18675,"updatedAt":14433,"__hash__":18676},"posts\u002Fposts\u002F20200420.vuex-todos-example.md","Vuex 上課筆記：用一個 todos 範例串起 state、mutation、action",{"type":8,"value":17930,"toc":18662},[17931,17939,17943,18102,18121,18124,18366,18396,18399,18621,18644,18646,18659],[11,17932,17933,17934,17938],{},"Vuex 的核心概念——state（狀態）、mutation（提交更改）、action（觸發非同步動作）、getter（取得狀態）——已經整理在《",[412,17935,17937],{"href":17936},"\u002Fposts\u002Fpinia-vuex-state-management","Pinia 與 Vuex：差在哪、怎麼選、怎麼寫","》，這篇是上課時做的練習筆記，用一個實際會動的 todos 範例，把那幾個概念串成一個完整流程，留著當個練習紀錄。",[15,17940,17942],{"id":17941},"store-設定","Store 設定",[22,17944,17946],{"className":24,"code":17945,"language":26,"meta":27,"style":27},"export default new Vuex.Store({\n  state: {\n    todos: [],\n  },\n  mutations: {\n    setTodos(state, value) {\n      state.todos = value;\n    },\n  },\n  actions: {\n    loadTodos(context) {\n      axios.get('https:\u002F\u002Fjsonplaceholder.typicode.com\u002Ftodos').then((res) => {\n        context.commit('setTodos', res.data);\n      });\n    },\n  },\n});\n",[29,17947,17948,17966,17971,17976,17982,17987,18003,18013,18017,18023,18028,18039,18065,18081,18086,18090,18096],{"__ignoreMap":27},[32,17949,17950,17952,17954,17956,17959,17961,17964],{"class":34,"line":35},[32,17951,5038],{"class":38},[32,17953,5041],{"class":38},[32,17955,4165],{"class":38},[32,17957,17958],{"class":41}," Vuex",[32,17960,1278],{"class":5049},[32,17962,17963],{"class":217},"Store",[32,17965,1143],{"class":5049},[32,17967,17968],{"class":34,"line":45},[32,17969,17970],{"class":41},"  state: {\n",[32,17972,17973],{"class":34,"line":51},[32,17974,17975],{"class":41},"    todos: [],\n",[32,17977,17978,17980],{"class":34,"line":90},[32,17979,9758],{"class":41},[32,17981,547],{"class":5049},[32,17983,17984],{"class":34,"line":99},[32,17985,17986],{"class":41},"  mutations: {\n",[32,17988,17989,17992,17994,17997,17999,18001],{"class":34,"line":106},[32,17990,17991],{"class":217},"    setTodos",[32,17993,477],{"class":41},[32,17995,17996],{"class":903},"state",[32,17998,635],{"class":41},[32,18000,17664],{"class":903},[32,18002,520],{"class":41},[32,18004,18005,18008,18010],{"class":34,"line":114},[32,18006,18007],{"class":41},"      state.todos ",[32,18009,172],{"class":38},[32,18011,18012],{"class":41}," value;\n",[32,18014,18015],{"class":34,"line":120},[32,18016,4204],{"class":41},[32,18018,18019,18021],{"class":34,"line":127},[32,18020,9758],{"class":41},[32,18022,547],{"class":5049},[32,18024,18025],{"class":34,"line":132},[32,18026,18027],{"class":41},"  actions: {\n",[32,18029,18030,18033,18035,18037],{"class":34,"line":141},[32,18031,18032],{"class":217},"    loadTodos",[32,18034,477],{"class":41},[32,18036,7449],{"class":903},[32,18038,520],{"class":41},[32,18040,18041,18044,18046,18048,18051,18053,18055,18057,18059,18061,18063],{"class":34,"line":147},[32,18042,18043],{"class":41},"      axios.",[32,18045,8944],{"class":217},[32,18047,477],{"class":41},[32,18049,18050],{"class":175},"'https:\u002F\u002Fjsonplaceholder.typicode.com\u002Ftodos'",[32,18052,533],{"class":41},[32,18054,8950],{"class":217},[32,18056,8293],{"class":41},[32,18058,10128],{"class":903},[32,18060,483],{"class":41},[32,18062,5090],{"class":38},[32,18064,703],{"class":41},[32,18066,18067,18070,18073,18075,18078],{"class":34,"line":275},[32,18068,18069],{"class":41},"        context.",[32,18071,18072],{"class":217},"commit",[32,18074,477],{"class":41},[32,18076,18077],{"class":175},"'setTodos'",[32,18079,18080],{"class":41},", res.data);\n",[32,18082,18083],{"class":34,"line":282},[32,18084,18085],{"class":41},"      });\n",[32,18087,18088],{"class":34,"line":295},[32,18089,4204],{"class":41},[32,18091,18092,18094],{"class":34,"line":605},[32,18093,9758],{"class":41},[32,18095,547],{"class":5049},[32,18097,18098,18100],{"class":34,"line":612},[32,18099,5117],{"class":5049},[32,18101,96],{"class":41},[11,18103,18104,18105,18108,18109,18111,18112,17852,18115,18117,18118,18120],{},"流程是：",[29,18106,18107],{},"action"," 負責打 API（非同步動作），拿到資料後呼叫 ",[29,18110,18072],{}," 觸發 ",[29,18113,18114],{},"mutation",[29,18116,18114],{}," 才是真正改動 ",[29,18119,17996],{}," 的地方——這正是 Vuex 規定「非同步的事情放 action、真正改狀態的動作放 mutation」的實際運作樣子。",[15,18122,18123],{"id":18123},"元件裡使用",[22,18125,18127],{"className":8665,"code":18126,"language":4112,"meta":27,"style":27},"\u003Cscript>\nimport { mapState, mapMutations, mapActions } from 'vuex';\n\nexport default {\n  computed: {\n    ...mapState(['todos']),\n  },\n  mounted() {\n    this.loadTodos(); \u002F\u002F 一開始先呼叫載入\n  },\n  methods: {\n    ...mapMutations(['setTodos']),\n    ...mapActions(['loadTodos']),\n  },\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch2>{{ $store.state.todos }}\u003C\u002Fh2>\n    \u003Cul>\n      \u003Cli v-for=\"post in todos\" :key=\"post.id\">{{ post.id }}\u003C\u002Fli>\n    \u003C\u002Ful>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[29,18128,18129,18137,18150,18154,18162,18167,18184,18190,18198,18212,18218,18223,18236,18250,18256,18262,18270,18274,18282,18290,18303,18311,18341,18350,18358],{"__ignoreMap":27},[32,18130,18131,18133,18135],{"class":34,"line":35},[32,18132,4046],{"class":41},[32,18134,4125],{"class":4049},[32,18136,4062],{"class":41},[32,18138,18139,18141,18144,18146,18148],{"class":34,"line":45},[32,18140,8794],{"class":38},[32,18142,18143],{"class":41}," { mapState, mapMutations, mapActions } ",[32,18145,8850],{"class":38},[32,18147,10229],{"class":175},[32,18149,96],{"class":41},[32,18151,18152],{"class":34,"line":51},[32,18153,103],{"emptyLinePlaceholder":102},[32,18155,18156,18158,18160],{"class":34,"line":90},[32,18157,5038],{"class":38},[32,18159,5041],{"class":38},[32,18161,703],{"class":5049},[32,18163,18164],{"class":34,"line":99},[32,18165,18166],{"class":41},"  computed: {\n",[32,18168,18169,18172,18175,18178,18181],{"class":34,"line":106},[32,18170,18171],{"class":38},"    ...",[32,18173,18174],{"class":217},"mapState",[32,18176,18177],{"class":41},"([",[32,18179,18180],{"class":175},"'todos'",[32,18182,18183],{"class":41},"]),\n",[32,18185,18186,18188],{"class":34,"line":114},[32,18187,9758],{"class":41},[32,18189,547],{"class":5049},[32,18191,18192,18194,18196],{"class":34,"line":120},[32,18193,15889],{"class":217},[32,18195,6075],{"class":5049},[32,18197,5252],{"class":41},[32,18199,18200,18202,18204,18207,18209],{"class":34,"line":127},[32,18201,10114],{"class":351},[32,18203,1278],{"class":41},[32,18205,18206],{"class":217},"loadTodos",[32,18208,11361],{"class":41},[32,18210,18211],{"class":608},"\u002F\u002F 一開始先呼叫載入\n",[32,18213,18214,18216],{"class":34,"line":132},[32,18215,9758],{"class":41},[32,18217,547],{"class":5049},[32,18219,18220],{"class":34,"line":141},[32,18221,18222],{"class":41},"  methods: {\n",[32,18224,18225,18227,18230,18232,18234],{"class":34,"line":147},[32,18226,18171],{"class":38},[32,18228,18229],{"class":217},"mapMutations",[32,18231,18177],{"class":41},[32,18233,18077],{"class":175},[32,18235,18183],{"class":41},[32,18237,18238,18240,18243,18245,18248],{"class":34,"line":275},[32,18239,18171],{"class":38},[32,18241,18242],{"class":217},"mapActions",[32,18244,18177],{"class":41},[32,18246,18247],{"class":175},"'loadTodos'",[32,18249,18183],{"class":41},[32,18251,18252,18254],{"class":34,"line":282},[32,18253,9758],{"class":41},[32,18255,547],{"class":5049},[32,18257,18258,18260],{"class":34,"line":295},[32,18259,9765],{"class":5049},[32,18261,96],{"class":41},[32,18263,18264,18266,18268],{"class":34,"line":605},[32,18265,4105],{"class":41},[32,18267,4125],{"class":4049},[32,18269,4062],{"class":41},[32,18271,18272],{"class":34,"line":612},[32,18273,103],{"emptyLinePlaceholder":102},[32,18275,18276,18278,18280],{"class":34,"line":617},[32,18277,4046],{"class":41},[32,18279,9623],{"class":4049},[32,18281,4062],{"class":41},[32,18283,18284,18286,18288],{"class":34,"line":644},[32,18285,4067],{"class":41},[32,18287,4050],{"class":4049},[32,18289,4062],{"class":41},[32,18291,18292,18294,18296,18299,18301],{"class":34,"line":649},[32,18293,9638],{"class":41},[32,18295,15],{"class":4049},[32,18297,18298],{"class":41},">{{ $store.state.todos }}\u003C\u002F",[32,18300,15],{"class":4049},[32,18302,4062],{"class":41},[32,18304,18305,18307,18309],{"class":34,"line":671},[32,18306,9638],{"class":41},[32,18308,4538],{"class":4049},[32,18310,4062],{"class":41},[32,18312,18313,18316,18318,18321,18323,18326,18329,18331,18334,18337,18339],{"class":34,"line":690},[32,18314,18315],{"class":41},"      \u003C",[32,18317,4541],{"class":4049},[32,18319,18320],{"class":4053}," v-for",[32,18322,172],{"class":41},[32,18324,18325],{"class":175},"\"post in todos\"",[32,18327,18328],{"class":4053}," :key",[32,18330,172],{"class":41},[32,18332,18333],{"class":175},"\"post.id\"",[32,18335,18336],{"class":41},">{{ post.id }}\u003C\u002F",[32,18338,4541],{"class":4049},[32,18340,4062],{"class":41},[32,18342,18343,18346,18348],{"class":34,"line":696},[32,18344,18345],{"class":41},"    \u003C\u002F",[32,18347,4538],{"class":4049},[32,18349,4062],{"class":41},[32,18351,18352,18354,18356],{"class":34,"line":706},[32,18353,9691],{"class":41},[32,18355,4050],{"class":4049},[32,18357,4062],{"class":41},[32,18359,18360,18362,18364],{"class":34,"line":721},[32,18361,4105],{"class":41},[32,18363,9623],{"class":4049},[32,18365,4062],{"class":41},[11,18367,18368,18371,18372,18375,18376,18379,18380,18383,18384,11621,18386,11621,18388,18390,18391,11621,18393,3775],{},[29,18369,18370],{},"{{ $store.state.todos }}"," 是直接存取 store 的寫法，",[29,18373,18374],{},"...mapState(['todos'])"," 展開後則變成 ",[29,18377,18378],{},"computed"," 屬性可以直接用 ",[29,18381,18382],{},"todos"," 這個名字取用——兩種寫法效果一樣，",[29,18385,18174],{},[29,18387,18229],{},[29,18389,18242],{}," 這三個輔助函式的用途，就是把 store 的東西映射到元件裡，用起來像一般的 ",[29,18392,18378],{},[29,18394,18395],{},"methods",[15,18397,18398],{"id":18398},"搭配路由參數做篩選",[22,18400,18402],{"className":8665,"code":18401,"language":4112,"meta":27,"style":27},"\u003Cscript>\nimport { mapState, mapMutations, mapActions } from 'vuex';\n\nexport default {\n  computed: {\n    ...mapState(['todos']),\n    todoId() {\n      return this.todos.filter((todo) => todo.userId == this.$route.params.num);\n    },\n  },\n  mounted() {\n    this.loadTodos();\n  },\n  methods: {\n    ...mapMutations(['setTodos']),\n    ...mapActions(['loadTodos']),\n  },\n};\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp>you enter by id todos {{ todoId }}\u003C\u002Fp>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[29,18403,18404,18412,18424,18428,18436,18440,18452,18459,18490,18494,18500,18508,18518,18524,18528,18540,18552,18558,18564,18572,18576,18584,18592,18605,18613],{"__ignoreMap":27},[32,18405,18406,18408,18410],{"class":34,"line":35},[32,18407,4046],{"class":41},[32,18409,4125],{"class":4049},[32,18411,4062],{"class":41},[32,18413,18414,18416,18418,18420,18422],{"class":34,"line":45},[32,18415,8794],{"class":38},[32,18417,18143],{"class":41},[32,18419,8850],{"class":38},[32,18421,10229],{"class":175},[32,18423,96],{"class":41},[32,18425,18426],{"class":34,"line":51},[32,18427,103],{"emptyLinePlaceholder":102},[32,18429,18430,18432,18434],{"class":34,"line":90},[32,18431,5038],{"class":38},[32,18433,5041],{"class":38},[32,18435,703],{"class":5049},[32,18437,18438],{"class":34,"line":99},[32,18439,18166],{"class":41},[32,18441,18442,18444,18446,18448,18450],{"class":34,"line":106},[32,18443,18171],{"class":38},[32,18445,18174],{"class":217},[32,18447,18177],{"class":41},[32,18449,18180],{"class":175},[32,18451,18183],{"class":41},[32,18453,18454,18457],{"class":34,"line":114},[32,18455,18456],{"class":217},"    todoId",[32,18458,4221],{"class":41},[32,18460,18461,18464,18466,18469,18471,18473,18476,18478,18480,18483,18485,18487],{"class":34,"line":120},[32,18462,18463],{"class":38},"      return",[32,18465,4234],{"class":351},[32,18467,18468],{"class":41},".todos.",[32,18470,16124],{"class":217},[32,18472,8293],{"class":41},[32,18474,18475],{"class":903},"todo",[32,18477,483],{"class":41},[32,18479,5090],{"class":38},[32,18481,18482],{"class":41}," todo.userId ",[32,18484,5867],{"class":38},[32,18486,4234],{"class":351},[32,18488,18489],{"class":41},".$route.params.num);\n",[32,18491,18492],{"class":34,"line":127},[32,18493,4204],{"class":41},[32,18495,18496,18498],{"class":34,"line":132},[32,18497,9758],{"class":41},[32,18499,547],{"class":5049},[32,18501,18502,18504,18506],{"class":34,"line":141},[32,18503,15889],{"class":217},[32,18505,6075],{"class":5049},[32,18507,5252],{"class":41},[32,18509,18510,18512,18514,18516],{"class":34,"line":147},[32,18511,10114],{"class":351},[32,18513,1278],{"class":41},[32,18515,18206],{"class":217},[32,18517,687],{"class":41},[32,18519,18520,18522],{"class":34,"line":275},[32,18521,9758],{"class":41},[32,18523,547],{"class":5049},[32,18525,18526],{"class":34,"line":282},[32,18527,18222],{"class":41},[32,18529,18530,18532,18534,18536,18538],{"class":34,"line":295},[32,18531,18171],{"class":38},[32,18533,18229],{"class":217},[32,18535,18177],{"class":41},[32,18537,18077],{"class":175},[32,18539,18183],{"class":41},[32,18541,18542,18544,18546,18548,18550],{"class":34,"line":605},[32,18543,18171],{"class":38},[32,18545,18242],{"class":217},[32,18547,18177],{"class":41},[32,18549,18247],{"class":175},[32,18551,18183],{"class":41},[32,18553,18554,18556],{"class":34,"line":612},[32,18555,9758],{"class":41},[32,18557,547],{"class":5049},[32,18559,18560,18562],{"class":34,"line":617},[32,18561,9765],{"class":5049},[32,18563,96],{"class":41},[32,18565,18566,18568,18570],{"class":34,"line":644},[32,18567,4105],{"class":41},[32,18569,4125],{"class":4049},[32,18571,4062],{"class":41},[32,18573,18574],{"class":34,"line":649},[32,18575,103],{"emptyLinePlaceholder":102},[32,18577,18578,18580,18582],{"class":34,"line":671},[32,18579,4046],{"class":41},[32,18581,9623],{"class":4049},[32,18583,4062],{"class":41},[32,18585,18586,18588,18590],{"class":34,"line":690},[32,18587,4067],{"class":41},[32,18589,4050],{"class":4049},[32,18591,4062],{"class":41},[32,18593,18594,18596,18598,18601,18603],{"class":34,"line":696},[32,18595,9638],{"class":41},[32,18597,11],{"class":4049},[32,18599,18600],{"class":41},">you enter by id todos {{ todoId }}\u003C\u002F",[32,18602,11],{"class":4049},[32,18604,4062],{"class":41},[32,18606,18607,18609,18611],{"class":34,"line":706},[32,18608,9691],{"class":41},[32,18610,4050],{"class":4049},[32,18612,4062],{"class":41},[32,18614,18615,18617,18619],{"class":34,"line":721},[32,18616,4105],{"class":41},[32,18618,9623],{"class":4049},[32,18620,4062],{"class":41},[11,18622,18623,18626,18627,18629,18630,18632,18633,18636,18637,18639,18640,18643],{},[29,18624,18625],{},"todoId"," 是一個 ",[29,18628,18378],{},"，依賴 ",[29,18631,18382],{},"（store 的狀態）跟 ",[29,18634,18635],{},"$route.params.num","（路由參數），任何一個變動都會重新計算——這就是為什麼「取得符合某個條件的資料」通常會寫成 ",[29,18638,18378],{}," 而不是在 ",[29,18641,18642],{},"mounted"," 裡算一次就結束，資料來源一變，畫面才會自動跟著更新。",[15,18645,4536],{"id":4536},[4538,18647,18648,18652],{},[4541,18649,18650],{},[412,18651,17937],{"href":17936},[4541,18653,18654],{},[412,18655,18658],{"href":18656,"rel":18657},"https:\u002F\u002Fvuex.vuejs.org\u002Fzh\u002F",[416],"Vuex 官方文件（封存版）",[368,18660,18661],{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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 .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}",{"title":27,"searchDepth":51,"depth":51,"links":18663},[18664,18665,18666,18667],{"id":17941,"depth":45,"text":17942},{"id":18123,"depth":45,"text":18123},{"id":18398,"depth":45,"text":18398},{"id":4536,"depth":45,"text":4536},"上課時做的 Vuex 練習筆記，用一個從 API 載入 todo 清單、存進 store、在元件裡用 mapState 與 mapActions 取用的完整範例，把 state／mutation／action 三個概念串成一個真的能動的流程。完整的 Vuex 概念解說與 Pinia 比較已經整理在另一篇。",{},"\u002Fposts\u002Fvuex-todos-example","2020-04-20",[],{"title":17928,"description":18668},"posts\u002F20200420.vuex-todos-example",[4844,4113],"ubT8EGFoAwEg0581ZETZzsQtHPnw99Z05LwRCPrNPO8",{"id":18678,"title":18679,"body":18680,"category":378,"description":19774,"draft":380,"extension":381,"image":382,"meta":19775,"navigation":102,"path":19776,"publishedAt":19777,"relatedExperiments":19778,"seo":19779,"stem":19780,"tags":19781,"updatedAt":14433,"__hash__":19785},"posts\u002Fposts\u002F20200421.threejs-basics.md","Three.js 基礎入門：場景、光源、材質、攝影機",{"type":8,"value":18681,"toc":19760},[18682,18685,18688,18714,18717,18838,18842,18933,18950,18953,18956,19017,19088,19091,19116,19129,19132,19225,19228,19231,19270,19280,19283,19341,19358,19361,19492,19507,19511,19518,19712,19722,19724,19738,19740,19757],[11,18683,18684],{},"Three.js 把「畫一個 3D 場景」拆解成幾個固定角色，這篇整理這套概念跟基本 API。",[15,18686,18687],{"id":18687},"場景的四大主角",[4538,18689,18690,18696,18702,18708],{},[4541,18691,18692,18695],{},[12249,18693,18694],{},"物件（object）","：實際的物體，畫面上看得到的東西",[4541,18697,18698,18701],{},[12249,18699,18700],{},"光源（light）","：照亮物體，沒有光源，物件會是全黑的",[4541,18703,18704,18707],{},[12249,18705,18706],{},"材質（material）","：決定每個面怎麼渲染——顏色、貼圖、反光程度",[4541,18709,18710,18713],{},[12249,18711,18712],{},"攝影機（camera）","：決定視角、視野跟渲染的範圍",[15,18715,18716],{"id":18716},"建立場景與渲染器",[22,18718,18720],{"className":24,"code":18719,"language":26,"meta":27,"style":27},"\u002F\u002F 攝影機 \u002F 場景 \u002F 渲染器\nvar camera, scene, renderer;\n\u002F\u002F 形狀 \u002F 材質 \u002F 物件\nvar geometry, material, mesh;\n\nfunction init() {\n  scene = new THREE.Scene();\n  renderer = new THREE.WebGLRenderer({ antialias: true });\n  renderer.setSize(window.innerWidth, window.innerHeight);\n  document.body.appendChild(renderer.domElement);\n}\n\ninit();\n",[29,18721,18722,18727,18734,18739,18746,18750,18758,18777,18801,18812,18823,18827,18831],{"__ignoreMap":27},[32,18723,18724],{"class":34,"line":35},[32,18725,18726],{"class":608},"\u002F\u002F 攝影機 \u002F 場景 \u002F 渲染器\n",[32,18728,18729,18731],{"class":34,"line":45},[32,18730,166],{"class":38},[32,18732,18733],{"class":41}," camera, scene, renderer;\n",[32,18735,18736],{"class":34,"line":51},[32,18737,18738],{"class":608},"\u002F\u002F 形狀 \u002F 材質 \u002F 物件\n",[32,18740,18741,18743],{"class":34,"line":90},[32,18742,166],{"class":38},[32,18744,18745],{"class":41}," geometry, material, mesh;\n",[32,18747,18748],{"class":34,"line":99},[32,18749,103],{"emptyLinePlaceholder":102},[32,18751,18752,18754,18756],{"class":34,"line":106},[32,18753,665],{"class":38},[32,18755,8581],{"class":217},[32,18757,4221],{"class":41},[32,18759,18760,18763,18765,18767,18770,18772,18775],{"class":34,"line":114},[32,18761,18762],{"class":41},"  scene ",[32,18764,172],{"class":38},[32,18766,4165],{"class":38},[32,18768,18769],{"class":351}," THREE",[32,18771,1278],{"class":41},[32,18773,18774],{"class":217},"Scene",[32,18776,687],{"class":41},[32,18778,18779,18782,18784,18786,18788,18790,18793,18796,18798],{"class":34,"line":120},[32,18780,18781],{"class":41},"  renderer ",[32,18783,172],{"class":38},[32,18785,4165],{"class":38},[32,18787,18769],{"class":351},[32,18789,1278],{"class":41},[32,18791,18792],{"class":217},"WebGLRenderer",[32,18794,18795],{"class":41},"({ antialias: ",[32,18797,6206],{"class":351},[32,18799,18800],{"class":41}," });\n",[32,18802,18803,18806,18809],{"class":34,"line":127},[32,18804,18805],{"class":41},"  renderer.",[32,18807,18808],{"class":217},"setSize",[32,18810,18811],{"class":41},"(window.innerWidth, window.innerHeight);\n",[32,18813,18814,18817,18820],{"class":34,"line":132},[32,18815,18816],{"class":41},"  document.body.",[32,18818,18819],{"class":217},"appendChild",[32,18821,18822],{"class":41},"(renderer.domElement);\n",[32,18824,18825],{"class":34,"line":141},[32,18826,54],{"class":41},[32,18828,18829],{"class":34,"line":147},[32,18830,103],{"emptyLinePlaceholder":102},[32,18832,18833,18836],{"class":34,"line":275},[32,18834,18835],{"class":217},"init",[32,18837,687],{"class":41},[15,18839,18841],{"id":18840},"建立物件形狀-材質","建立物件：形狀 + 材質",[22,18843,18845],{"className":24,"code":18844,"language":26,"meta":27,"style":27},"geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);\nmaterial = new THREE.MeshNormalMaterial();\n\nmesh = new THREE.Mesh(geometry, material); \u002F\u002F 組合成物件\nscene.add(mesh);\n",[29,18846,18847,18878,18896,18900,18922],{"__ignoreMap":27},[32,18848,18849,18852,18854,18856,18858,18860,18863,18865,18868,18870,18872,18874,18876],{"class":34,"line":35},[32,18850,18851],{"class":41},"geometry ",[32,18853,172],{"class":38},[32,18855,4165],{"class":38},[32,18857,18769],{"class":351},[32,18859,1278],{"class":41},[32,18861,18862],{"class":217},"BoxGeometry",[32,18864,477],{"class":41},[32,18866,18867],{"class":351},"0.2",[32,18869,635],{"class":41},[32,18871,18867],{"class":351},[32,18873,635],{"class":41},[32,18875,18867],{"class":351},[32,18877,230],{"class":41},[32,18879,18880,18883,18885,18887,18889,18891,18894],{"class":34,"line":45},[32,18881,18882],{"class":41},"material ",[32,18884,172],{"class":38},[32,18886,4165],{"class":38},[32,18888,18769],{"class":351},[32,18890,1278],{"class":41},[32,18892,18893],{"class":217},"MeshNormalMaterial",[32,18895,687],{"class":41},[32,18897,18898],{"class":34,"line":51},[32,18899,103],{"emptyLinePlaceholder":102},[32,18901,18902,18905,18907,18909,18911,18913,18916,18919],{"class":34,"line":90},[32,18903,18904],{"class":41},"mesh ",[32,18906,172],{"class":38},[32,18908,4165],{"class":38},[32,18910,18769],{"class":351},[32,18912,1278],{"class":41},[32,18914,18915],{"class":217},"Mesh",[32,18917,18918],{"class":41},"(geometry, material); ",[32,18920,18921],{"class":608},"\u002F\u002F 組合成物件\n",[32,18923,18924,18927,18930],{"class":34,"line":99},[32,18925,18926],{"class":41},"scene.",[32,18928,18929],{"class":217},"add",[32,18931,18932],{"class":41},"(mesh);\n",[11,18934,18935,15506,18938,18941,18942,18945,18946,18949],{},[12249,18936,18937],{},"物件＝形狀（geometry）＋材質（material）",[29,18939,18940],{},"geometry"," 只定義形狀的頂點資料，實際看起來是什麼顏色、什麼質感，由 ",[29,18943,18944],{},"material"," 決定，兩者透過 ",[29,18947,18948],{},"THREE.Mesh"," 組合成一個能被加進場景的物件。",[15,18951,18952],{"id":18952},"建立光源",[11,18954,18955],{},"Three.js 提供幾種常見光源，各自模擬不同的照明情境：",[13433,18957,18958,18971],{},[13436,18959,18960],{},[13439,18961,18962,18965,18968],{},[13442,18963,18964],{},"光源",[13442,18966,18967],{},"特性",[13442,18969,18970],{},"例子",[13449,18972,18973,18984,18995,19006],{},[13439,18974,18975,18978,18981],{},[13454,18976,18977],{},"點光源（PointLight）",[13454,18979,18980],{},"控制位置、有衰弱（falloff），從一個點往外放射，離光源越遠越暗",[13454,18982,18983],{},"燈泡",[13439,18985,18986,18989,18992],{},[13454,18987,18988],{},"聚光燈（SpotLight）",[13454,18990,18991],{},"控制位置、光束角度、有衰弱",[13454,18993,18994],{},"手電筒",[13439,18996,18997,19000,19003],{},[13454,18998,18999],{},"平行光（DirectionalLight）",[13454,19001,19002],{},"光線互相平行，沒有位置衰減",[13454,19004,19005],{},"日照",[13439,19007,19008,19011,19014],{},[13454,19009,19010],{},"環境光源（AmbientLight）",[13454,19012,19013],{},"均勻照亮整個場景，沒有方向性，通常當基礎補光",[13454,19015,19016],{},"—",[22,19018,19020],{"className":24,"code":19019,"language":26,"meta":27,"style":27},"var light = new THREE.PointLight(0xff0000, 1, 100);\nlight.position.set(50, 50, 50);\nscene.add(light);\n",[29,19021,19022,19056,19079],{"__ignoreMap":27},[32,19023,19024,19026,19029,19031,19033,19035,19037,19040,19042,19045,19047,19049,19051,19054],{"class":34,"line":35},[32,19025,166],{"class":38},[32,19027,19028],{"class":41}," light ",[32,19030,172],{"class":38},[32,19032,4165],{"class":38},[32,19034,18769],{"class":351},[32,19036,1278],{"class":41},[32,19038,19039],{"class":217},"PointLight",[32,19041,477],{"class":41},[32,19043,19044],{"class":351},"0xff0000",[32,19046,635],{"class":41},[32,19048,6544],{"class":351},[32,19050,635],{"class":41},[32,19052,19053],{"class":351},"100",[32,19055,230],{"class":41},[32,19057,19058,19061,19064,19066,19069,19071,19073,19075,19077],{"class":34,"line":45},[32,19059,19060],{"class":41},"light.position.",[32,19062,19063],{"class":217},"set",[32,19065,477],{"class":41},[32,19067,19068],{"class":351},"50",[32,19070,635],{"class":41},[32,19072,19068],{"class":351},[32,19074,635],{"class":41},[32,19076,19068],{"class":351},[32,19078,230],{"class":41},[32,19080,19081,19083,19085],{"class":34,"line":51},[32,19082,18926],{"class":41},[32,19084,18929],{"class":217},[32,19086,19087],{"class":41},"(light);\n",[15,19089,19090],{"id":19090},"建立材質",[22,19092,19094],{"className":24,"code":19093,"language":26,"meta":27,"style":27},"var sphereMaterial = new THREE.MeshLambertMaterial();\n",[29,19095,19096],{"__ignoreMap":27},[32,19097,19098,19100,19103,19105,19107,19109,19111,19114],{"class":34,"line":35},[32,19099,166],{"class":38},[32,19101,19102],{"class":41}," sphereMaterial ",[32,19104,172],{"class":38},[32,19106,4165],{"class":38},[32,19108,18769],{"class":351},[32,19110,1278],{"class":41},[32,19112,19113],{"class":217},"MeshLambertMaterial",[32,19115,687],{"class":41},[11,19117,19118,19119,3750,19122,3750,19125,19128],{},"材質的幾個重點參數：",[12249,19120,19121],{},"反光（reflect）",[12249,19123,19124],{},"散射（diffuse）",[12249,19126,19127],{},"凹凸（bump）","——反光越強烈，看起來越像金屬；凹凸貼圖能用一張平面圖模擬出表面的立體起伏，不需要真的建出凹凸的幾何形狀，是節省計算資源的常見做法。",[15,19130,19131],{"id":19131},"建立攝影機",[22,19133,19135],{"className":24,"code":19134,"language":26,"meta":27,"style":27},"camera = new THREE.PerspectiveCamera(\n  70, \u002F\u002F 視野角度（FOV）\n  window.innerWidth \u002F window.innerHeight, \u002F\u002F 長寬比\n  0.01, \u002F\u002F 近截面\n  10, \u002F\u002F 遠截面\n);\ncamera.position.z = 1;\ncamera.lookAt(scene.position);\n",[29,19136,19137,19155,19165,19178,19188,19198,19202,19214],{"__ignoreMap":27},[32,19138,19139,19142,19144,19146,19148,19150,19153],{"class":34,"line":35},[32,19140,19141],{"class":41},"camera ",[32,19143,172],{"class":38},[32,19145,4165],{"class":38},[32,19147,18769],{"class":351},[32,19149,1278],{"class":41},[32,19151,19152],{"class":217},"PerspectiveCamera",[32,19154,539],{"class":41},[32,19156,19157,19160,19162],{"class":34,"line":45},[32,19158,19159],{"class":351},"  70",[32,19161,635],{"class":41},[32,19163,19164],{"class":608},"\u002F\u002F 視野角度（FOV）\n",[32,19166,19167,19170,19172,19175],{"class":34,"line":51},[32,19168,19169],{"class":41},"  window.innerWidth ",[32,19171,1275],{"class":38},[32,19173,19174],{"class":41}," window.innerHeight, ",[32,19176,19177],{"class":608},"\u002F\u002F 長寬比\n",[32,19179,19180,19183,19185],{"class":34,"line":90},[32,19181,19182],{"class":351},"  0.01",[32,19184,635],{"class":41},[32,19186,19187],{"class":608},"\u002F\u002F 近截面\n",[32,19189,19190,19193,19195],{"class":34,"line":99},[32,19191,19192],{"class":351},"  10",[32,19194,635],{"class":41},[32,19196,19197],{"class":608},"\u002F\u002F 遠截面\n",[32,19199,19200],{"class":34,"line":106},[32,19201,230],{"class":41},[32,19203,19204,19207,19209,19212],{"class":34,"line":114},[32,19205,19206],{"class":41},"camera.position.z ",[32,19208,172],{"class":38},[32,19210,19211],{"class":351}," 1",[32,19213,96],{"class":41},[32,19215,19216,19219,19222],{"class":34,"line":120},[32,19217,19218],{"class":41},"camera.",[32,19220,19221],{"class":217},"lookAt",[32,19223,19224],{"class":41},"(scene.position);\n",[11,19226,19227],{},"**視野（FOV，Field of View）**控制攝影機能看到多大的範圍——角度越大，看到的範圍越廣，但邊緣的透視變形也越明顯，跟廣角鏡頭是同樣的道理。渲染器（renderer）除了這篇用的 WebGL，還有 Canvas、CSS、SVG 這幾種選擇，各自的效能與相容性不同，Three.js 官方最推薦、支援度也最完整的是 WebGL 渲染器。",[15,19229,19230],{"id":19230},"渲染場景",[22,19232,19234],{"className":24,"code":19233,"language":26,"meta":27,"style":27},"function render() {\n  renderer.render(scene, camera);\n  requestAnimationFrame(render); \u002F\u002F 呼叫下一個渲染\n}\n",[29,19235,19236,19245,19255,19266],{"__ignoreMap":27},[32,19237,19238,19240,19243],{"class":34,"line":35},[32,19239,665],{"class":38},[32,19241,19242],{"class":217}," render",[32,19244,4221],{"class":41},[32,19246,19247,19249,19252],{"class":34,"line":45},[32,19248,18805],{"class":41},[32,19250,19251],{"class":217},"render",[32,19253,19254],{"class":41},"(scene, camera);\n",[32,19256,19257,19260,19263],{"class":34,"line":51},[32,19258,19259],{"class":217},"  requestAnimationFrame",[32,19261,19262],{"class":41},"(render); ",[32,19264,19265],{"class":608},"\u002F\u002F 呼叫下一個渲染\n",[32,19267,19268],{"class":34,"line":90},[32,19269,54],{"class":41},[11,19271,19272,19275,19276,19279],{},[29,19273,19274],{},"requestAnimationFrame"," 讓瀏覽器在下一次重繪之前呼叫這個函式，形成一個持續更新畫面的渲染迴圈，比用 ",[29,19277,19278],{},"setInterval"," 手動控制更新頻率更順暢，也更省電（分頁不在前景時瀏覽器會自動降低頻率）。",[15,19281,19282],{"id":19282},"移動物件",[22,19284,19286],{"className":24,"code":19285,"language":26,"meta":27,"style":27},"function animate() {\n  mesh.rotation.x += 0.01;\n  mesh.rotation.y += 0.02;\n  renderer.render(scene, camera);\n  requestAnimationFrame(animate);\n}\n",[29,19287,19288,19297,19310,19322,19330,19337],{"__ignoreMap":27},[32,19289,19290,19292,19295],{"class":34,"line":35},[32,19291,665],{"class":38},[32,19293,19294],{"class":217}," animate",[32,19296,4221],{"class":41},[32,19298,19299,19302,19305,19308],{"class":34,"line":45},[32,19300,19301],{"class":41},"  mesh.rotation.x ",[32,19303,19304],{"class":38},"+=",[32,19306,19307],{"class":351}," 0.01",[32,19309,96],{"class":41},[32,19311,19312,19315,19317,19320],{"class":34,"line":51},[32,19313,19314],{"class":41},"  mesh.rotation.y ",[32,19316,19304],{"class":38},[32,19318,19319],{"class":351}," 0.02",[32,19321,96],{"class":41},[32,19323,19324,19326,19328],{"class":34,"line":90},[32,19325,18805],{"class":41},[32,19327,19251],{"class":217},[32,19329,19254],{"class":41},[32,19331,19332,19334],{"class":34,"line":99},[32,19333,19259],{"class":217},[32,19335,19336],{"class":41},"(animate);\n",[32,19338,19339],{"class":34,"line":106},[32,19340,54],{"class":41},[11,19342,19343,11621,19346,19349,19350,11621,19352,11621,19354,19357],{},[29,19344,19345],{},"position",[29,19347,19348],{},"rotation"," 都是三維向量，各自有 ",[29,19351,10967],{},[29,19353,11098],{},[29,19355,19356],{},"z"," 三個分量，每一影格微調數值就形成動畫。",[15,19359,19360],{"id":19360},"物件群組",[22,19362,19364],{"className":24,"code":19363,"language":26,"meta":27,"style":27},"var sphereGeometry = new THREE.SphereGeometry(5, 60, 60);\nvar sphereMaterial = new THREE.MeshLambertMaterial();\nvar sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);\n\nvar group = new THREE.Object3D(); \u002F\u002F 空的物件，作為群組轉動的容器\ngroup.add(sphere);\nscene.add(group);\n\n\u002F\u002F group 的 position 跟 rotation 可以一起操控底下所有子物件\n",[29,19365,19366,19399,19417,19437,19441,19464,19474,19483,19487],{"__ignoreMap":27},[32,19367,19368,19370,19373,19375,19377,19379,19381,19384,19386,19388,19390,19393,19395,19397],{"class":34,"line":35},[32,19369,166],{"class":38},[32,19371,19372],{"class":41}," sphereGeometry ",[32,19374,172],{"class":38},[32,19376,4165],{"class":38},[32,19378,18769],{"class":351},[32,19380,1278],{"class":41},[32,19382,19383],{"class":217},"SphereGeometry",[32,19385,477],{"class":41},[32,19387,10613],{"class":351},[32,19389,635],{"class":41},[32,19391,19392],{"class":351},"60",[32,19394,635],{"class":41},[32,19396,19392],{"class":351},[32,19398,230],{"class":41},[32,19400,19401,19403,19405,19407,19409,19411,19413,19415],{"class":34,"line":45},[32,19402,166],{"class":38},[32,19404,19102],{"class":41},[32,19406,172],{"class":38},[32,19408,4165],{"class":38},[32,19410,18769],{"class":351},[32,19412,1278],{"class":41},[32,19414,19113],{"class":217},[32,19416,687],{"class":41},[32,19418,19419,19421,19424,19426,19428,19430,19432,19434],{"class":34,"line":51},[32,19420,166],{"class":38},[32,19422,19423],{"class":41}," sphere ",[32,19425,172],{"class":38},[32,19427,4165],{"class":38},[32,19429,18769],{"class":351},[32,19431,1278],{"class":41},[32,19433,18915],{"class":217},[32,19435,19436],{"class":41},"(sphereGeometry, sphereMaterial);\n",[32,19438,19439],{"class":34,"line":90},[32,19440,103],{"emptyLinePlaceholder":102},[32,19442,19443,19445,19448,19450,19452,19454,19456,19459,19461],{"class":34,"line":99},[32,19444,166],{"class":38},[32,19446,19447],{"class":41}," group ",[32,19449,172],{"class":38},[32,19451,4165],{"class":38},[32,19453,18769],{"class":351},[32,19455,1278],{"class":41},[32,19457,19458],{"class":217},"Object3D",[32,19460,11361],{"class":41},[32,19462,19463],{"class":608},"\u002F\u002F 空的物件，作為群組轉動的容器\n",[32,19465,19466,19469,19471],{"class":34,"line":106},[32,19467,19468],{"class":41},"group.",[32,19470,18929],{"class":217},[32,19472,19473],{"class":41},"(sphere);\n",[32,19475,19476,19478,19480],{"class":34,"line":114},[32,19477,18926],{"class":41},[32,19479,18929],{"class":217},[32,19481,19482],{"class":41},"(group);\n",[32,19484,19485],{"class":34,"line":120},[32,19486,103],{"emptyLinePlaceholder":102},[32,19488,19489],{"class":34,"line":127},[32,19490,19491],{"class":608},"\u002F\u002F group 的 position 跟 rotation 可以一起操控底下所有子物件\n",[11,19493,19494,19497,19498,19500,19501,19503,19504,19506],{},[29,19495,19496],{},"group"," 是一個空的 3D 物件，本身不會被畫出來，純粹當容器用——把多個物件都 ",[29,19499,18929],{}," 進同一個 ",[29,19502,19496],{},"，之後移動或旋轉這個 ",[29,19505,19496],{},"，底下所有子物件會一起跟著動，不需要對每個子物件各自套用同樣的變換。",[15,19508,19510],{"id":19509},"滑鼠定位raycaster","滑鼠定位：Raycaster",[11,19512,19513,19514,19517],{},"要判斷滑鼠點到了場景裡的哪個物件，靠的是",[12249,19515,19516],{},"射線偵測","：從攝影機（或滑鼠位置）往場景裡射出一條看不見的射線，偵測這條射線跟哪些物件有交集。",[22,19519,19521],{"className":24,"code":19520,"language":26,"meta":27,"style":27},"var raycaster = new THREE.Raycaster();\nvar mouse = new THREE.Vector2();\n\nfunction onMouseClick(event) {\n  mouse.x = (event.clientX \u002F window.innerWidth) * 2 - 1;\n  mouse.y = -(event.clientY \u002F window.innerHeight) * 2 + 1;\n\n  raycaster.setFromCamera(mouse, camera);\n  var intersects = raycaster.intersectObjects(scene.children);\n\n  if (intersects.length > 0) {\n    console.log('點到了', intersects[0].object);\n  }\n}\n",[29,19522,19523,19543,19563,19567,19581,19606,19633,19637,19648,19666,19670,19685,19704,19708],{"__ignoreMap":27},[32,19524,19525,19527,19530,19532,19534,19536,19538,19541],{"class":34,"line":35},[32,19526,166],{"class":38},[32,19528,19529],{"class":41}," raycaster ",[32,19531,172],{"class":38},[32,19533,4165],{"class":38},[32,19535,18769],{"class":351},[32,19537,1278],{"class":41},[32,19539,19540],{"class":217},"Raycaster",[32,19542,687],{"class":41},[32,19544,19545,19547,19550,19552,19554,19556,19558,19561],{"class":34,"line":45},[32,19546,166],{"class":38},[32,19548,19549],{"class":41}," mouse ",[32,19551,172],{"class":38},[32,19553,4165],{"class":38},[32,19555,18769],{"class":351},[32,19557,1278],{"class":41},[32,19559,19560],{"class":217},"Vector2",[32,19562,687],{"class":41},[32,19564,19565],{"class":34,"line":51},[32,19566,103],{"emptyLinePlaceholder":102},[32,19568,19569,19571,19574,19576,19579],{"class":34,"line":90},[32,19570,665],{"class":38},[32,19572,19573],{"class":217}," onMouseClick",[32,19575,477],{"class":41},[32,19577,19578],{"class":903},"event",[32,19580,520],{"class":41},[32,19582,19583,19586,19588,19591,19593,19596,19598,19600,19602,19604],{"class":34,"line":99},[32,19584,19585],{"class":41},"  mouse.x ",[32,19587,172],{"class":38},[32,19589,19590],{"class":41}," (event.clientX ",[32,19592,1275],{"class":38},[32,19594,19595],{"class":41}," window.innerWidth) ",[32,19597,6078],{"class":38},[32,19599,10982],{"class":351},[32,19601,1119],{"class":38},[32,19603,19211],{"class":351},[32,19605,96],{"class":41},[32,19607,19608,19611,19613,19615,19618,19620,19623,19625,19627,19629,19631],{"class":34,"line":106},[32,19609,19610],{"class":41},"  mouse.y ",[32,19612,172],{"class":38},[32,19614,1119],{"class":38},[32,19616,19617],{"class":41},"(event.clientY ",[32,19619,1275],{"class":38},[32,19621,19622],{"class":41}," window.innerHeight) ",[32,19624,6078],{"class":38},[32,19626,10982],{"class":351},[32,19628,1799],{"class":38},[32,19630,19211],{"class":351},[32,19632,96],{"class":41},[32,19634,19635],{"class":34,"line":114},[32,19636,103],{"emptyLinePlaceholder":102},[32,19638,19639,19642,19645],{"class":34,"line":120},[32,19640,19641],{"class":41},"  raycaster.",[32,19643,19644],{"class":217},"setFromCamera",[32,19646,19647],{"class":41},"(mouse, camera);\n",[32,19649,19650,19652,19655,19657,19660,19663],{"class":34,"line":127},[32,19651,888],{"class":38},[32,19653,19654],{"class":41}," intersects ",[32,19656,172],{"class":38},[32,19658,19659],{"class":41}," raycaster.",[32,19661,19662],{"class":217},"intersectObjects",[32,19664,19665],{"class":41},"(scene.children);\n",[32,19667,19668],{"class":34,"line":132},[32,19669,103],{"emptyLinePlaceholder":102},[32,19671,19672,19674,19677,19679,19681,19683],{"class":34,"line":141},[32,19673,1021],{"class":38},[32,19675,19676],{"class":41}," (intersects.",[32,19678,991],{"class":351},[32,19680,2235],{"class":38},[32,19682,1523],{"class":351},[32,19684,520],{"class":41},[32,19686,19687,19689,19691,19693,19696,19699,19701],{"class":34,"line":147},[32,19688,8966],{"class":41},[32,19690,5197],{"class":217},[32,19692,477],{"class":41},[32,19694,19695],{"class":175},"'點到了'",[32,19697,19698],{"class":41},", intersects[",[32,19700,11881],{"class":351},[32,19702,19703],{"class":41},"].object);\n",[32,19705,19706],{"class":34,"line":275},[32,19707,1203],{"class":41},[32,19709,19710],{"class":34,"line":282},[32,19711,54],{"class":41},[11,19713,19714,19717,19718,19721],{},[29,19715,19716],{},"intersectObjects()"," 回傳跟射線相交的所有物件，依距離攝影機由近到遠排序，",[29,19719,19720],{},"intersects[0]"," 就是滑鼠實際點到、離攝影機最近的那個物件。",[15,19723,17846],{"id":17846},[11,19725,19726,19729,19730,19733,19734,19737],{},[12249,19727,19728],{},"場景四大主角、光源類型、渲染迴圈這些核心概念完全沒變","，是 Three.js 從以前到現在的基礎骨架。改變的主要是",[12249,19731,19732],{},"安裝與引入方式","——這篇用的是直接在瀏覽器全域掛一個 ",[29,19735,19736],{},"THREE"," 物件的 CDN 引入寫法，現在的專案幾乎都改用 npm 安裝加 ES Module 引入，整理在《Three.js 現在怎麼裝：從全域 THREE 到 ES Module》。",[15,19739,4536],{"id":4536},[4538,19741,19742,19750],{},[4541,19743,19744,19749],{},[412,19745,19748],{"href":19746,"rel":19747},"https:\u002F\u002Fthreejs.org\u002F",[416],"Three.js 官方網站","（一直持續在改版）",[4541,19751,19752],{},[412,19753,19756],{"href":19754,"rel":19755},"https:\u002F\u002Fthreejs.org\u002Fexamples\u002F?q=raycas",[416],"Three.js：Raycaster 範例",[368,19758,19759],{},"html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}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 .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}",{"title":27,"searchDepth":51,"depth":51,"links":19761},[19762,19763,19764,19765,19766,19767,19768,19769,19770,19771,19772,19773],{"id":18687,"depth":45,"text":18687},{"id":18716,"depth":45,"text":18716},{"id":18840,"depth":45,"text":18841},{"id":18952,"depth":45,"text":18952},{"id":19090,"depth":45,"text":19090},{"id":19131,"depth":45,"text":19131},{"id":19230,"depth":45,"text":19230},{"id":19282,"depth":45,"text":19282},{"id":19360,"depth":45,"text":19360},{"id":19509,"depth":45,"text":19510},{"id":17846,"depth":45,"text":17846},{"id":4536,"depth":45,"text":4536},"整理 Three.js 的核心概念——3D 場景四大主角（物件、光源、材質、攝影機）、幾種光源與渲染器類型、用 requestAnimationFrame 跑渲染迴圈、用 Raycaster 做滑鼠選取物件，這篇用 2020 年當時的全域 THREE 寫法示範。ESM 安裝方式整理在新的一篇。",{},"\u002Fposts\u002Fthreejs-basics","2020-04-21",[],{"title":18679,"description":19774},"posts\u002F20200421.threejs-basics",[19782,19783,19784],"Three.js","WebGL","3D","o0R5VNq5Mw9eDFn5nJTavu9dInxq6ErnBef3QZASCuo",{"id":19787,"title":19788,"body":19789,"category":4012,"description":21450,"draft":380,"extension":381,"image":382,"meta":21451,"navigation":102,"path":21452,"publishedAt":19777,"relatedExperiments":21453,"seo":21454,"stem":21455,"tags":21456,"updatedAt":14433,"__hash__":21457},"posts\u002Fposts\u002F20200421.vuex-syntax-cheatsheet.md","Vuex API 語法速查：安裝、state、mutations、actions、getters、modules",{"type":8,"value":19790,"toc":21438},[19791,19803,19806,19822,19869,19875,19923,19928,19967,19983,19987,20005,20009,20018,20052,20059,20070,20109,20159,20163,20200,20206,20212,20306,20314,20394,20398,20470,20479,20485,20561,20568,20576,20637,20643,20712,20761,20765,20916,20930,20933,20972,21037,21043,21063,21067,21197,21254,21257,21272,21276,21295,21370,21390,21399,21401,21419,21421,21435],[11,19792,19793,19794,19800,19801,12793],{},"這篇是上課期間陸續做的 Vuex 練習筆記合輯，涵蓋安裝到 state／mutations／actions／getters／modules 每一塊的具體語法。",[12249,19795,19796,19797,19799],{},"核心概念的說明、Vuex 遇到的瓶頸、跟 Pinia 的完整比較已經整理在《",[412,19798,17937],{"href":17936},"》","，這篇不重複講「為什麼」，純粹當語法查詢用；一個從頭到尾能動的練習範例見《",[412,19802,17928],{"href":18670},[15,19804,19805],{"id":19805},"安裝與注入",[22,19807,19809],{"className":2057,"code":19808,"language":2059,"meta":27,"style":27},"npm install vuex --save\n",[29,19810,19811],{"__ignoreMap":27},[32,19812,19813,19815,19817,19820],{"class":34,"line":35},[32,19814,4625],{"class":2066},[32,19816,4650],{"class":175},[32,19818,19819],{"class":175}," vuex",[32,19821,8638],{"class":351},[22,19823,19825],{"className":24,"code":19824,"language":26,"meta":27,"style":27},"\u002F\u002F main.js\nimport Vue from 'vue';\nimport Vuex from 'vuex';\n\nVue.use(Vuex);\n",[29,19826,19827,19832,19844,19857,19861],{"__ignoreMap":27},[32,19828,19829],{"class":34,"line":35},[32,19830,19831],{"class":608},"\u002F\u002F main.js\n",[32,19833,19834,19836,19838,19840,19842],{"class":34,"line":45},[32,19835,8794],{"class":38},[32,19837,8847],{"class":41},[32,19839,8850],{"class":38},[32,19841,8853],{"class":175},[32,19843,96],{"class":41},[32,19845,19846,19848,19851,19853,19855],{"class":34,"line":51},[32,19847,8794],{"class":38},[32,19849,19850],{"class":41}," Vuex ",[32,19852,8850],{"class":38},[32,19854,10229],{"class":175},[32,19856,96],{"class":41},[32,19858,19859],{"class":34,"line":90},[32,19860,103],{"emptyLinePlaceholder":102},[32,19862,19863,19865,19867],{"class":34,"line":99},[32,19864,8892],{"class":41},[32,19866,8895],{"class":217},[32,19868,10244],{"class":41},[11,19870,19871,19874],{},[29,19872,19873],{},"store\u002Findex.js","（彙整 Vuex 所有行為的地方）：",[22,19876,19878],{"className":24,"code":19877,"language":26,"meta":27,"style":27},"export default new Vuex.Store({\n  state: {},\n  mutations: {},\n  actions: {},\n});\n",[29,19879,19880,19896,19903,19910,19917],{"__ignoreMap":27},[32,19881,19882,19884,19886,19888,19890,19892,19894],{"class":34,"line":35},[32,19883,5038],{"class":38},[32,19885,5041],{"class":38},[32,19887,4165],{"class":38},[32,19889,17958],{"class":41},[32,19891,1278],{"class":5049},[32,19893,17963],{"class":217},[32,19895,1143],{"class":5049},[32,19897,19898,19901],{"class":34,"line":45},[32,19899,19900],{"class":41},"  state: {}",[32,19902,547],{"class":5049},[32,19904,19905,19908],{"class":34,"line":51},[32,19906,19907],{"class":41},"  mutations: {}",[32,19909,547],{"class":5049},[32,19911,19912,19915],{"class":34,"line":90},[32,19913,19914],{"class":41},"  actions: {}",[32,19916,547],{"class":5049},[32,19918,19919,19921],{"class":34,"line":99},[32,19920,5117],{"class":5049},[32,19922,96],{"class":41},[11,19924,19925,19927],{},[29,19926,5137],{}," 裡注入：",[22,19929,19931],{"className":24,"code":19930,"language":26,"meta":27,"style":27},"import store from '.\u002Fstore';\n\nnew Vue({\n  store,\n});\n",[29,19932,19933,19946,19950,19958,19963],{"__ignoreMap":27},[32,19934,19935,19937,19939,19941,19944],{"class":34,"line":35},[32,19936,8794],{"class":38},[32,19938,10259],{"class":41},[32,19940,8850],{"class":38},[32,19942,19943],{"class":175}," '.\u002Fstore'",[32,19945,96],{"class":41},[32,19947,19948],{"class":34,"line":45},[32,19949,103],{"emptyLinePlaceholder":102},[32,19951,19952,19954,19956],{"class":34,"line":51},[32,19953,9196],{"class":38},[32,19955,4168],{"class":217},[32,19957,1143],{"class":41},[32,19959,19960],{"class":34,"line":90},[32,19961,19962],{"class":41},"  store,\n",[32,19964,19965],{"class":34,"line":99},[32,19966,792],{"class":41},[11,19968,19969,19975,19976,11621,19978,11621,19980,19982],{},[12249,19970,19971,19972,19974],{},"一定要在最上層（",[29,19973,5137],{},"）注入 store","，之後元件裡用 ",[29,19977,18174],{},[29,19979,18229],{},[29,19981,18242],{}," 這些輔助函式才會生效，沒注入的話這些輔助函式拿不到 store 實例。",[15,19984,19986],{"id":19985},"store-三大規則","Store 三大規則",[6426,19988,19989,19995,20000],{},[4541,19990,19991,19994],{},[12249,19992,19993],{},"app 層級的狀態集中放進 store","——例如會員登入狀態、跨元件都會用到的商品列表",[4541,19996,19997],{},[12249,19998,19999],{},"mutations 是改變 state 的唯一方法，且必須是同步的",[4541,20001,20002],{},[12249,20003,20004],{},"非同步操作都放在 actions 裡",[15,20006,20008],{"id":20007},"state儲存狀態","state：儲存狀態",[11,20010,20011,20013,20014,20017],{},[29,20012,17996],{}," 裡可以放任何東西，但",[12249,20015,20016],{},"要事先定義好欄位","，就算給空值也沒關係：",[22,20019,20021],{"className":24,"code":20020,"language":26,"meta":27,"style":27},"state: {\n  loading: false,\n  todos: [],\n}\n",[29,20022,20023,20029,20040,20048],{"__ignoreMap":27},[32,20024,20025,20027],{"class":34,"line":35},[32,20026,17996],{"class":2066},[32,20028,5261],{"class":41},[32,20030,20031,20034,20036,20038],{"class":34,"line":45},[32,20032,20033],{"class":2066},"  loading",[32,20035,5269],{"class":41},[32,20037,9746],{"class":351},[32,20039,547],{"class":41},[32,20041,20042,20045],{"class":34,"line":51},[32,20043,20044],{"class":2066},"  todos",[32,20046,20047],{"class":41},": [],\n",[32,20049,20050],{"class":34,"line":90},[32,20051,54],{"class":41},[11,20053,20054,20055,20058],{},"沒有事先定義、想動態加欄位的話要用 ",[29,20056,20057],{},"Vue.set(state, 'loading', false)","，但這不是建議的做法——事先定義好結構，可讀性跟可維護性都比較好。",[11,20060,20061,20066,20067,20069],{},[12249,20062,20063,20064],{},"取用 state 一定要透過 ",[29,20065,18378],{},"（不要在 ",[29,20068,18395],{}," 或直接在模板裡運算），這樣資料變動時畫面才會自動反應：",[22,20071,20073],{"className":24,"code":20072,"language":26,"meta":27,"style":27},"\u002F\u002F 第一種：直接用 $store\ncomputed: {\n  loading() {\n    return this.$store.state.loading;\n  },\n}\n",[29,20074,20075,20080,20086,20092,20101,20105],{"__ignoreMap":27},[32,20076,20077],{"class":34,"line":35},[32,20078,20079],{"class":608},"\u002F\u002F 第一種：直接用 $store\n",[32,20081,20082,20084],{"class":34,"line":45},[32,20083,18378],{"class":2066},[32,20085,5261],{"class":41},[32,20087,20088,20090],{"class":34,"line":51},[32,20089,20033],{"class":217},[32,20091,4221],{"class":41},[32,20093,20094,20096,20098],{"class":34,"line":90},[32,20095,971],{"class":38},[32,20097,4234],{"class":351},[32,20099,20100],{"class":41},".$store.state.loading;\n",[32,20102,20103],{"class":34,"line":99},[32,20104,5639],{"class":41},[32,20106,20107],{"class":34,"line":106},[32,20108,54],{"class":41},[22,20110,20112],{"className":24,"code":20111,"language":26,"meta":27,"style":27},"\u002F\u002F 第二種：用 mapState\nimport { mapState } from 'vuex';\n\ncomputed: {\n  ...mapState(['loading']),\n}\n",[29,20113,20114,20119,20131,20135,20141,20155],{"__ignoreMap":27},[32,20115,20116],{"class":34,"line":35},[32,20117,20118],{"class":608},"\u002F\u002F 第二種：用 mapState\n",[32,20120,20121,20123,20125,20127,20129],{"class":34,"line":45},[32,20122,8794],{"class":38},[32,20124,10273],{"class":41},[32,20126,8850],{"class":38},[32,20128,10229],{"class":175},[32,20130,96],{"class":41},[32,20132,20133],{"class":34,"line":51},[32,20134,103],{"emptyLinePlaceholder":102},[32,20136,20137,20139],{"class":34,"line":90},[32,20138,18378],{"class":2066},[32,20140,5261],{"class":41},[32,20142,20143,20146,20148,20150,20153],{"class":34,"line":99},[32,20144,20145],{"class":38},"  ...",[32,20147,18174],{"class":217},[32,20149,18177],{"class":41},[32,20151,20152],{"class":175},"'loading'",[32,20154,18183],{"class":41},[32,20156,20157],{"class":34,"line":106},[32,20158,54],{"class":41},[15,20160,20162],{"id":20161},"mutations唯一能改變-state-的地方","mutations：唯一能改變 state 的地方",[22,20164,20166],{"className":24,"code":20165,"language":26,"meta":27,"style":27},"mutations: {\n  SET_LOADING(state, value) {\n    state.loading = value;\n  },\n}\n",[29,20167,20168,20175,20183,20192,20196],{"__ignoreMap":27},[32,20169,20170,20173],{"class":34,"line":35},[32,20171,20172],{"class":2066},"mutations",[32,20174,5261],{"class":41},[32,20176,20177,20180],{"class":34,"line":45},[32,20178,20179],{"class":217},"  SET_LOADING",[32,20181,20182],{"class":41},"(state, value) {\n",[32,20184,20185,20188,20190],{"class":34,"line":51},[32,20186,20187],{"class":41},"    state.loading ",[32,20189,172],{"class":38},[32,20191,18012],{"class":41},[32,20193,20194],{"class":34,"line":90},[32,20195,5639],{"class":41},[32,20197,20198],{"class":34,"line":99},[32,20199,54],{"class":41},[11,20201,20202,20205],{},[12249,20203,20204],{},"必須是同步操作，不能在 mutation 裡直接呼叫 API","——因為 Vuex 的 devtools 是靠追蹤每一次 mutation 來記錄狀態變化的時間點，非同步操作會讓這個追蹤失去意義（沒辦法確定狀態是在哪個時間點被改變的）。命名習慣上，mutation 常用全大寫命名，跟一般方法區分開來。",[11,20207,20208,20209,20211],{},"在元件裡觸發 mutation 用 ",[29,20210,18072],{},"，有三種傳參方式：",[22,20213,20215],{"className":24,"code":20214,"language":26,"meta":27,"style":27},"\u002F\u002F 傳入字串（不帶參數）\nthis.$store.commit('SET_LOADING');\n\n\u002F\u002F 傳入參數（payload）\nthis.$store.commit('SET_LOADING', true);\n\n\u002F\u002F 傳入物件（type 欄位指定要觸發哪個 mutation）\nthis.$store.commit({\n  type: 'SET_LOADING',\n  value: true,\n});\n",[29,20216,20217,20222,20238,20242,20247,20265,20269,20274,20284,20293,20302],{"__ignoreMap":27},[32,20218,20219],{"class":34,"line":35},[32,20220,20221],{"class":608},"\u002F\u002F 傳入字串（不帶參數）\n",[32,20223,20224,20226,20229,20231,20233,20236],{"class":34,"line":45},[32,20225,1083],{"class":351},[32,20227,20228],{"class":41},".$store.",[32,20230,18072],{"class":217},[32,20232,477],{"class":41},[32,20234,20235],{"class":175},"'SET_LOADING'",[32,20237,230],{"class":41},[32,20239,20240],{"class":34,"line":51},[32,20241,103],{"emptyLinePlaceholder":102},[32,20243,20244],{"class":34,"line":90},[32,20245,20246],{"class":608},"\u002F\u002F 傳入參數（payload）\n",[32,20248,20249,20251,20253,20255,20257,20259,20261,20263],{"class":34,"line":99},[32,20250,1083],{"class":351},[32,20252,20228],{"class":41},[32,20254,18072],{"class":217},[32,20256,477],{"class":41},[32,20258,20235],{"class":175},[32,20260,635],{"class":41},[32,20262,6206],{"class":351},[32,20264,230],{"class":41},[32,20266,20267],{"class":34,"line":106},[32,20268,103],{"emptyLinePlaceholder":102},[32,20270,20271],{"class":34,"line":114},[32,20272,20273],{"class":608},"\u002F\u002F 傳入物件（type 欄位指定要觸發哪個 mutation）\n",[32,20275,20276,20278,20280,20282],{"class":34,"line":120},[32,20277,1083],{"class":351},[32,20279,20228],{"class":41},[32,20281,18072],{"class":217},[32,20283,1143],{"class":41},[32,20285,20286,20289,20291],{"class":34,"line":127},[32,20287,20288],{"class":41},"  type: ",[32,20290,20235],{"class":175},[32,20292,547],{"class":41},[32,20294,20295,20298,20300],{"class":34,"line":132},[32,20296,20297],{"class":41},"  value: ",[32,20299,6206],{"class":351},[32,20301,547],{"class":41},[32,20303,20304],{"class":34,"line":141},[32,20305,792],{"class":41},[11,20307,20308,20309,20311,20312,5240],{},"用 ",[29,20310,18229],{}," 映射到 ",[29,20313,18395],{},[22,20315,20317],{"className":24,"code":20316,"language":26,"meta":27,"style":27},"import { mapMutations } from 'vuex';\n\nmethods: {\n  \u002F\u002F 字串陣列方式\n  ...mapMutations(['SET_LOADING']),\n\n  \u002F\u002F 物件別名方式（本地方法名稱不同於 mutation 名稱時）\n  ...mapMutations({\n    setLoading: 'SET_LOADING',\n  }),\n}\n",[29,20318,20319,20332,20336,20342,20347,20359,20363,20368,20376,20385,20390],{"__ignoreMap":27},[32,20320,20321,20323,20326,20328,20330],{"class":34,"line":35},[32,20322,8794],{"class":38},[32,20324,20325],{"class":41}," { mapMutations } ",[32,20327,8850],{"class":38},[32,20329,10229],{"class":175},[32,20331,96],{"class":41},[32,20333,20334],{"class":34,"line":45},[32,20335,103],{"emptyLinePlaceholder":102},[32,20337,20338,20340],{"class":34,"line":51},[32,20339,18395],{"class":2066},[32,20341,5261],{"class":41},[32,20343,20344],{"class":34,"line":90},[32,20345,20346],{"class":608},"  \u002F\u002F 字串陣列方式\n",[32,20348,20349,20351,20353,20355,20357],{"class":34,"line":99},[32,20350,20145],{"class":38},[32,20352,18229],{"class":217},[32,20354,18177],{"class":41},[32,20356,20235],{"class":175},[32,20358,18183],{"class":41},[32,20360,20361],{"class":34,"line":106},[32,20362,103],{"emptyLinePlaceholder":102},[32,20364,20365],{"class":34,"line":114},[32,20366,20367],{"class":608},"  \u002F\u002F 物件別名方式（本地方法名稱不同於 mutation 名稱時）\n",[32,20369,20370,20372,20374],{"class":34,"line":120},[32,20371,20145],{"class":38},[32,20373,18229],{"class":217},[32,20375,1143],{"class":41},[32,20377,20378,20381,20383],{"class":34,"line":127},[32,20379,20380],{"class":41},"    setLoading: ",[32,20382,20235],{"class":175},[32,20384,547],{"class":41},[32,20386,20387],{"class":34,"line":132},[32,20388,20389],{"class":41},"  }),\n",[32,20391,20392],{"class":34,"line":141},[32,20393,54],{"class":41},[15,20395,20397],{"id":20396},"actions非同步操作","actions：非同步操作",[22,20399,20401],{"className":24,"code":20400,"language":26,"meta":27,"style":27},"actions: {\n  async loadTodos(context) {\n    const res = await axios.get('\u002Fapi\u002Ftodos');\n    context.commit('SET_TODOS', res.data); \u002F\u002F 非同步完成後才 commit\n  },\n}\n",[29,20402,20403,20410,20420,20444,20462,20466],{"__ignoreMap":27},[32,20404,20405,20408],{"class":34,"line":35},[32,20406,20407],{"class":2066},"actions",[32,20409,5261],{"class":41},[32,20411,20412,20415,20417],{"class":34,"line":45},[32,20413,20414],{"class":41},"  async ",[32,20416,18206],{"class":217},[32,20418,20419],{"class":41},"(context) {\n",[32,20421,20422,20424,20427,20429,20432,20435,20437,20439,20442],{"class":34,"line":51},[32,20423,9409],{"class":38},[32,20425,20426],{"class":351}," res",[32,20428,894],{"class":38},[32,20430,20431],{"class":38}," await",[32,20433,20434],{"class":41}," axios.",[32,20436,8944],{"class":217},[32,20438,477],{"class":41},[32,20440,20441],{"class":175},"'\u002Fapi\u002Ftodos'",[32,20443,230],{"class":41},[32,20445,20446,20449,20451,20453,20456,20459],{"class":34,"line":90},[32,20447,20448],{"class":41},"    context.",[32,20450,18072],{"class":217},[32,20452,477],{"class":41},[32,20454,20455],{"class":175},"'SET_TODOS'",[32,20457,20458],{"class":41},", res.data); ",[32,20460,20461],{"class":608},"\u002F\u002F 非同步完成後才 commit\n",[32,20463,20464],{"class":34,"line":99},[32,20465,5639],{"class":41},[32,20467,20468],{"class":34,"line":106},[32,20469,54],{"class":41},[11,20471,20472,20475,20476,3775],{},[12249,20473,20474],{},"action 不能直接修改 state，一定要透過 commit 觸發 mutation","——這是 Vuex 架構上的硬性規則，就算 action 裡已經非同步取得資料，也不能跳過 mutation 直接 ",[29,20477,20478],{},"state.todos = res.data",[11,20480,20481,20482,5240],{},"在元件裡觸發 action 用 ",[29,20483,20484],{},"dispatch",[22,20486,20488],{"className":24,"code":20487,"language":26,"meta":27,"style":27},"this.$store.dispatch('loadTodos');\n\n\u002F\u002F 帶參數\nthis.$store.dispatch('loadTodos', { page: 2 });\n\n\u002F\u002F 物件別名寫法\nthis.$store.dispatch({ type: 'loadTodos', page: 2 });\n",[29,20489,20490,20504,20508,20513,20532,20536,20541],{"__ignoreMap":27},[32,20491,20492,20494,20496,20498,20500,20502],{"class":34,"line":35},[32,20493,1083],{"class":351},[32,20495,20228],{"class":41},[32,20497,20484],{"class":217},[32,20499,477],{"class":41},[32,20501,18247],{"class":175},[32,20503,230],{"class":41},[32,20505,20506],{"class":34,"line":45},[32,20507,103],{"emptyLinePlaceholder":102},[32,20509,20510],{"class":34,"line":51},[32,20511,20512],{"class":608},"\u002F\u002F 帶參數\n",[32,20514,20515,20517,20519,20521,20523,20525,20528,20530],{"class":34,"line":90},[32,20516,1083],{"class":351},[32,20518,20228],{"class":41},[32,20520,20484],{"class":217},[32,20522,477],{"class":41},[32,20524,18247],{"class":175},[32,20526,20527],{"class":41},", { page: ",[32,20529,11971],{"class":351},[32,20531,18800],{"class":41},[32,20533,20534],{"class":34,"line":99},[32,20535,103],{"emptyLinePlaceholder":102},[32,20537,20538],{"class":34,"line":106},[32,20539,20540],{"class":608},"\u002F\u002F 物件別名寫法\n",[32,20542,20543,20545,20547,20549,20552,20554,20557,20559],{"class":34,"line":114},[32,20544,1083],{"class":351},[32,20546,20228],{"class":41},[32,20548,20484],{"class":217},[32,20550,20551],{"class":41},"({ type: ",[32,20553,18247],{"class":175},[32,20555,20556],{"class":41},", page: ",[32,20558,11971],{"class":351},[32,20560,18800],{"class":41},[11,20562,20563,20564,20567],{},"action 裡面",[12249,20565,20566],{},"做多次 commit 是同步依序執行的","（一行一行往下跑，不會因為前一個 commit 是同步操作而互相打斷）。",[11,20569,20570,20572,20573,20575],{},[29,20571,18242],{}," 用法跟 ",[29,20574,18229],{}," 對稱：",[22,20577,20579],{"className":24,"code":20578,"language":26,"meta":27,"style":27},"import { mapActions } from 'vuex';\n\nmethods: {\n  ...mapActions(['loadTodos']),\n  ...mapActions({ loadMyTodos: 'loadTodos' }), \u002F\u002F 物件別名\n}\n",[29,20580,20581,20594,20598,20604,20616,20633],{"__ignoreMap":27},[32,20582,20583,20585,20588,20590,20592],{"class":34,"line":35},[32,20584,8794],{"class":38},[32,20586,20587],{"class":41}," { mapActions } ",[32,20589,8850],{"class":38},[32,20591,10229],{"class":175},[32,20593,96],{"class":41},[32,20595,20596],{"class":34,"line":45},[32,20597,103],{"emptyLinePlaceholder":102},[32,20599,20600,20602],{"class":34,"line":51},[32,20601,18395],{"class":2066},[32,20603,5261],{"class":41},[32,20605,20606,20608,20610,20612,20614],{"class":34,"line":90},[32,20607,20145],{"class":38},[32,20609,18242],{"class":217},[32,20611,18177],{"class":41},[32,20613,18247],{"class":175},[32,20615,18183],{"class":41},[32,20617,20618,20620,20622,20625,20627,20630],{"class":34,"line":99},[32,20619,20145],{"class":38},[32,20621,18242],{"class":217},[32,20623,20624],{"class":41},"({ loadMyTodos: ",[32,20626,18247],{"class":175},[32,20628,20629],{"class":41}," }), ",[32,20631,20632],{"class":608},"\u002F\u002F 物件別名\n",[32,20634,20635],{"class":34,"line":106},[32,20636,54],{"class":41},[11,20638,20639,20642],{},[12249,20640,20641],{},"action 完成後呼叫另一個 action","、或需要等待非同步結果，靠回傳 Promise 串接：",[22,20644,20646],{"className":24,"code":20645,"language":26,"meta":27,"style":27},"actions: {\n  async loadTodos(context) {\n    const res = await axios.get('\u002Fapi\u002Ftodos');\n    context.commit('SET_TODOS', res.data);\n    return res.data; \u002F\u002F 回傳值，讓外部可以用 .then() 接\n  },\n}\n",[29,20647,20648,20654,20662,20682,20694,20704,20708],{"__ignoreMap":27},[32,20649,20650,20652],{"class":34,"line":35},[32,20651,20407],{"class":2066},[32,20653,5261],{"class":41},[32,20655,20656,20658,20660],{"class":34,"line":45},[32,20657,20414],{"class":41},[32,20659,18206],{"class":217},[32,20661,20419],{"class":41},[32,20663,20664,20666,20668,20670,20672,20674,20676,20678,20680],{"class":34,"line":51},[32,20665,9409],{"class":38},[32,20667,20426],{"class":351},[32,20669,894],{"class":38},[32,20671,20431],{"class":38},[32,20673,20434],{"class":41},[32,20675,8944],{"class":217},[32,20677,477],{"class":41},[32,20679,20441],{"class":175},[32,20681,230],{"class":41},[32,20683,20684,20686,20688,20690,20692],{"class":34,"line":90},[32,20685,20448],{"class":41},[32,20687,18072],{"class":217},[32,20689,477],{"class":41},[32,20691,20455],{"class":175},[32,20693,18080],{"class":41},[32,20695,20696,20698,20701],{"class":34,"line":99},[32,20697,971],{"class":38},[32,20699,20700],{"class":41}," res.data; ",[32,20702,20703],{"class":608},"\u002F\u002F 回傳值，讓外部可以用 .then() 接\n",[32,20705,20706],{"class":34,"line":106},[32,20707,5639],{"class":41},[32,20709,20710],{"class":34,"line":114},[32,20711,54],{"class":41},[22,20713,20715],{"className":24,"code":20714,"language":26,"meta":27,"style":27},"this.$store.dispatch('loadTodos').then((todos) => {\n  console.log('載入完成', todos);\n});\n",[29,20716,20717,20743,20757],{"__ignoreMap":27},[32,20718,20719,20721,20723,20725,20727,20729,20731,20733,20735,20737,20739,20741],{"class":34,"line":35},[32,20720,1083],{"class":351},[32,20722,20228],{"class":41},[32,20724,20484],{"class":217},[32,20726,477],{"class":41},[32,20728,18247],{"class":175},[32,20730,533],{"class":41},[32,20732,8950],{"class":217},[32,20734,8293],{"class":41},[32,20736,18382],{"class":903},[32,20738,483],{"class":41},[32,20740,5090],{"class":38},[32,20742,703],{"class":41},[32,20744,20745,20747,20749,20751,20754],{"class":34,"line":45},[32,20746,5194],{"class":41},[32,20748,5197],{"class":217},[32,20750,477],{"class":41},[32,20752,20753],{"class":175},"'載入完成'",[32,20755,20756],{"class":41},", todos);\n",[32,20758,20759],{"class":34,"line":51},[32,20760,792],{"class":41},[15,20762,20764],{"id":20763},"gettersstore-層級的-computed","getters：store 層級的 computed",[22,20766,20768],{"className":24,"code":20767,"language":26,"meta":27,"style":27},"getters: {\n  \u002F\u002F 直接依 state 回傳值\n  todoCount(state) {\n    return state.todos.length;\n  },\n\n  \u002F\u002F 回傳參數（依賴其他 getter）\n  doneTodoCount(state, getters) {\n    return getters.todoCount - state.todos.filter((t) => !t.done).length;\n  },\n\n  \u002F\u002F 回傳一個函式，讓呼叫端自己帶參數\n  getTodoById(state) {\n    return (id) => state.todos.find((t) => t.id === id);\n  },\n}\n",[29,20769,20770,20777,20782,20790,20801,20805,20809,20814,20822,20854,20858,20862,20867,20874,20908,20912],{"__ignoreMap":27},[32,20771,20772,20775],{"class":34,"line":35},[32,20773,20774],{"class":2066},"getters",[32,20776,5261],{"class":41},[32,20778,20779],{"class":34,"line":45},[32,20780,20781],{"class":608},"  \u002F\u002F 直接依 state 回傳值\n",[32,20783,20784,20787],{"class":34,"line":51},[32,20785,20786],{"class":217},"  todoCount",[32,20788,20789],{"class":41},"(state) {\n",[32,20791,20792,20794,20797,20799],{"class":34,"line":90},[32,20793,971],{"class":38},[32,20795,20796],{"class":41}," state.todos.",[32,20798,991],{"class":351},[32,20800,96],{"class":41},[32,20802,20803],{"class":34,"line":99},[32,20804,5639],{"class":41},[32,20806,20807],{"class":34,"line":106},[32,20808,103],{"emptyLinePlaceholder":102},[32,20810,20811],{"class":34,"line":114},[32,20812,20813],{"class":608},"  \u002F\u002F 回傳參數（依賴其他 getter）\n",[32,20815,20816,20819],{"class":34,"line":120},[32,20817,20818],{"class":217},"  doneTodoCount",[32,20820,20821],{"class":41},"(state, getters) {\n",[32,20823,20824,20826,20829,20831,20833,20835,20837,20840,20842,20844,20847,20850,20852],{"class":34,"line":127},[32,20825,971],{"class":38},[32,20827,20828],{"class":41}," getters.todoCount ",[32,20830,6628],{"class":38},[32,20832,20796],{"class":41},[32,20834,16124],{"class":217},[32,20836,8293],{"class":41},[32,20838,20839],{"class":903},"t",[32,20841,483],{"class":41},[32,20843,5090],{"class":38},[32,20845,20846],{"class":38}," !",[32,20848,20849],{"class":41},"t.done).",[32,20851,991],{"class":351},[32,20853,96],{"class":41},[32,20855,20856],{"class":34,"line":132},[32,20857,5639],{"class":41},[32,20859,20860],{"class":34,"line":141},[32,20861,103],{"emptyLinePlaceholder":102},[32,20863,20864],{"class":34,"line":147},[32,20865,20866],{"class":608},"  \u002F\u002F 回傳一個函式，讓呼叫端自己帶參數\n",[32,20868,20869,20872],{"class":34,"line":275},[32,20870,20871],{"class":217},"  getTodoById",[32,20873,20789],{"class":41},[32,20875,20876,20878,20880,20883,20885,20887,20889,20892,20894,20896,20898,20900,20903,20905],{"class":34,"line":282},[32,20877,971],{"class":38},[32,20879,900],{"class":41},[32,20881,20882],{"class":903},"id",[32,20884,483],{"class":41},[32,20886,5090],{"class":38},[32,20888,20796],{"class":41},[32,20890,20891],{"class":217},"find",[32,20893,8293],{"class":41},[32,20895,20839],{"class":903},[32,20897,483],{"class":41},[32,20899,5090],{"class":38},[32,20901,20902],{"class":41}," t.id ",[32,20904,5560],{"class":38},[32,20906,20907],{"class":41}," id);\n",[32,20909,20910],{"class":34,"line":295},[32,20911,5639],{"class":41},[32,20913,20914],{"class":34,"line":605},[32,20915,54],{"class":41},[11,20917,20918,20920,20921,20923,20924,3775,20926,20929],{},[29,20919,20774],{}," 一定是吃 ",[29,20922,17996],{}," 當第一個參數，第二個參數可以拿到其他 ",[29,20925,20774],{},[12249,20927,20928],{},"回傳函式","這個型態很實用——一般的 getter 結果會被快取（依賴不變就不重新計算），但回傳函式的寫法每次呼叫都會重新執行，適合需要外部傳參數查詢的情境（例如「依 ID 查某一筆」）。",[11,20931,20932],{},"在元件裡使用：",[22,20934,20936],{"className":24,"code":20935,"language":26,"meta":27,"style":27},"\u002F\u002F 直接用 $store.getters\ncomputed: {\n  todoCount() {\n    return this.$store.getters.todoCount;\n  },\n}\n",[29,20937,20938,20943,20949,20955,20964,20968],{"__ignoreMap":27},[32,20939,20940],{"class":34,"line":35},[32,20941,20942],{"class":608},"\u002F\u002F 直接用 $store.getters\n",[32,20944,20945,20947],{"class":34,"line":45},[32,20946,18378],{"class":2066},[32,20948,5261],{"class":41},[32,20950,20951,20953],{"class":34,"line":51},[32,20952,20786],{"class":217},[32,20954,4221],{"class":41},[32,20956,20957,20959,20961],{"class":34,"line":90},[32,20958,971],{"class":38},[32,20960,4234],{"class":351},[32,20962,20963],{"class":41},".$store.getters.todoCount;\n",[32,20965,20966],{"class":34,"line":99},[32,20967,5639],{"class":41},[32,20969,20970],{"class":34,"line":106},[32,20971,54],{"class":41},[22,20973,20975],{"className":24,"code":20974,"language":26,"meta":27,"style":27},"\u002F\u002F mapGetters，也支援物件別名\nimport { mapGetters } from 'vuex';\n\ncomputed: {\n  ...mapGetters(['todoCount']),\n  ...mapGetters({ count: 'todoCount' }),\n}\n",[29,20976,20977,20982,20995,20999,21005,21019,21033],{"__ignoreMap":27},[32,20978,20979],{"class":34,"line":35},[32,20980,20981],{"class":608},"\u002F\u002F mapGetters，也支援物件別名\n",[32,20983,20984,20986,20989,20991,20993],{"class":34,"line":45},[32,20985,8794],{"class":38},[32,20987,20988],{"class":41}," { mapGetters } ",[32,20990,8850],{"class":38},[32,20992,10229],{"class":175},[32,20994,96],{"class":41},[32,20996,20997],{"class":34,"line":51},[32,20998,103],{"emptyLinePlaceholder":102},[32,21000,21001,21003],{"class":34,"line":90},[32,21002,18378],{"class":2066},[32,21004,5261],{"class":41},[32,21006,21007,21009,21012,21014,21017],{"class":34,"line":99},[32,21008,20145],{"class":38},[32,21010,21011],{"class":217},"mapGetters",[32,21013,18177],{"class":41},[32,21015,21016],{"class":175},"'todoCount'",[32,21018,18183],{"class":41},[32,21020,21021,21023,21025,21028,21030],{"class":34,"line":106},[32,21022,20145],{"class":38},[32,21024,21011],{"class":217},[32,21026,21027],{"class":41},"({ count: ",[32,21029,21016],{"class":175},[32,21031,21032],{"class":41}," }),\n",[32,21034,21035],{"class":34,"line":114},[32,21036,54],{"class":41},[11,21038,21039,21042],{},[29,21040,21041],{},"getTodoById"," 這種回傳函式的 getter，在元件裡當函式呼叫：",[22,21044,21046],{"className":24,"code":21045,"language":26,"meta":27,"style":27},"this.$store.getters.getTodoById(3);\n",[29,21047,21048],{"__ignoreMap":27},[32,21049,21050,21052,21055,21057,21059,21061],{"class":34,"line":35},[32,21051,1083],{"class":351},[32,21053,21054],{"class":41},".$store.getters.",[32,21056,21041],{"class":217},[32,21058,477],{"class":41},[32,21060,13470],{"class":351},[32,21062,230],{"class":41},[15,21064,21066],{"id":21065},"modules拆分大型-store","modules：拆分大型 store",[22,21068,21070],{"className":24,"code":21069,"language":26,"meta":27,"style":27},"\u002F\u002F store\u002Fmodules\u002Ftodos.js\nexport default {\n  state: { todos: [] },\n  mutations: {\n    SET_TODOS(state, value) {\n      state.todos = value;\n    },\n  },\n  actions: {\n    async loadTodos({ commit }) {\n      const res = await axios.get('\u002Fapi\u002Ftodos');\n      commit('SET_TODOS', res.data);\n    },\n  },\n};\n",[29,21071,21072,21077,21085,21092,21096,21111,21119,21123,21129,21133,21149,21170,21181,21185,21191],{"__ignoreMap":27},[32,21073,21074],{"class":34,"line":35},[32,21075,21076],{"class":608},"\u002F\u002F store\u002Fmodules\u002Ftodos.js\n",[32,21078,21079,21081,21083],{"class":34,"line":45},[32,21080,5038],{"class":38},[32,21082,5041],{"class":38},[32,21084,703],{"class":5049},[32,21086,21087,21090],{"class":34,"line":51},[32,21088,21089],{"class":41},"  state: { todos: [] }",[32,21091,547],{"class":5049},[32,21093,21094],{"class":34,"line":90},[32,21095,17986],{"class":41},[32,21097,21098,21101,21103,21105,21107,21109],{"class":34,"line":99},[32,21099,21100],{"class":217},"    SET_TODOS",[32,21102,477],{"class":41},[32,21104,17996],{"class":903},[32,21106,635],{"class":41},[32,21108,17664],{"class":903},[32,21110,520],{"class":41},[32,21112,21113,21115,21117],{"class":34,"line":106},[32,21114,18007],{"class":41},[32,21116,172],{"class":38},[32,21118,18012],{"class":41},[32,21120,21121],{"class":34,"line":114},[32,21122,4204],{"class":41},[32,21124,21125,21127],{"class":34,"line":120},[32,21126,9758],{"class":41},[32,21128,547],{"class":5049},[32,21130,21131],{"class":34,"line":127},[32,21132,18027],{"class":41},[32,21134,21135,21138,21141,21144,21146],{"class":34,"line":132},[32,21136,21137],{"class":38},"    async",[32,21139,21140],{"class":217}," loadTodos",[32,21142,21143],{"class":41},"({ ",[32,21145,18072],{"class":903},[32,21147,21148],{"class":41}," }) {\n",[32,21150,21151,21154,21156,21158,21160,21162,21164,21166,21168],{"class":34,"line":141},[32,21152,21153],{"class":38},"      const",[32,21155,20426],{"class":351},[32,21157,894],{"class":38},[32,21159,20431],{"class":38},[32,21161,20434],{"class":41},[32,21163,8944],{"class":217},[32,21165,477],{"class":41},[32,21167,20441],{"class":175},[32,21169,230],{"class":41},[32,21171,21172,21175,21177,21179],{"class":34,"line":147},[32,21173,21174],{"class":217},"      commit",[32,21176,477],{"class":41},[32,21178,20455],{"class":175},[32,21180,18080],{"class":41},[32,21182,21183],{"class":34,"line":275},[32,21184,4204],{"class":41},[32,21186,21187,21189],{"class":34,"line":282},[32,21188,9758],{"class":41},[32,21190,547],{"class":5049},[32,21192,21193,21195],{"class":34,"line":295},[32,21194,9765],{"class":5049},[32,21196,96],{"class":41},[22,21198,21200],{"className":24,"code":21199,"language":26,"meta":27,"style":27},"\u002F\u002F store\u002Findex.js\nimport todos from '.\u002Fmodules\u002Ftodos';\n\nexport default new Vuex.Store({\n  modules: { todos },\n});\n",[29,21201,21202,21207,21221,21225,21241,21248],{"__ignoreMap":27},[32,21203,21204],{"class":34,"line":35},[32,21205,21206],{"class":608},"\u002F\u002F store\u002Findex.js\n",[32,21208,21209,21211,21214,21216,21219],{"class":34,"line":45},[32,21210,8794],{"class":38},[32,21212,21213],{"class":41}," todos ",[32,21215,8850],{"class":38},[32,21217,21218],{"class":175}," '.\u002Fmodules\u002Ftodos'",[32,21220,96],{"class":41},[32,21222,21223],{"class":34,"line":51},[32,21224,103],{"emptyLinePlaceholder":102},[32,21226,21227,21229,21231,21233,21235,21237,21239],{"class":34,"line":90},[32,21228,5038],{"class":38},[32,21230,5041],{"class":38},[32,21232,4165],{"class":38},[32,21234,17958],{"class":41},[32,21236,1278],{"class":5049},[32,21238,17963],{"class":217},[32,21240,1143],{"class":5049},[32,21242,21243,21246],{"class":34,"line":99},[32,21244,21245],{"class":41},"  modules: { todos }",[32,21247,547],{"class":5049},[32,21249,21250,21252],{"class":34,"line":106},[32,21251,5117],{"class":5049},[32,21253,96],{"class":41},[11,21255,21256],{},"拆成 module 之後，state 的存取路徑要多一層模組名稱：",[22,21258,21260],{"className":24,"code":21259,"language":26,"meta":27,"style":27},"this.$store.state.todos.todos; \u002F\u002F 模組名稱.state 欄位名稱\n",[29,21261,21262],{"__ignoreMap":27},[32,21263,21264,21266,21269],{"class":34,"line":35},[32,21265,1083],{"class":351},[32,21267,21268],{"class":41},".$store.state.todos.todos; ",[32,21270,21271],{"class":608},"\u002F\u002F 模組名稱.state 欄位名稱\n",[15,21273,21275],{"id":21274},"v-model-搭配-store-狀態gettersetter-computed","v-model 搭配 store 狀態：getter\u002Fsetter computed",[11,21277,21278,21281,21282,21285,21286,21288,21289,11621,21291,7218,21293,5240],{},[29,21279,21280],{},"v-model"," 需要同時讀取跟寫入，直接綁 ",[29,21283,21284],{},"$store.state.xxx"," 沒辦法寫入（",[29,21287,17996],{}," 不能被直接賦值），要包一層帶 ",[29,21290,8944],{},[29,21292,19063],{},[29,21294,18378],{},[22,21296,21298],{"className":24,"code":21297,"language":26,"meta":27,"style":27},"computed: {\n  keyword: {\n    get() {\n      return this.$store.state.keyword;\n    },\n    set(value) {\n      this.$store.commit('SET_KEYWORD', value);\n    },\n  },\n}\n",[29,21299,21300,21306,21313,21320,21329,21333,21341,21358,21362,21366],{"__ignoreMap":27},[32,21301,21302,21304],{"class":34,"line":35},[32,21303,18378],{"class":2066},[32,21305,5261],{"class":41},[32,21307,21308,21311],{"class":34,"line":45},[32,21309,21310],{"class":2066},"  keyword",[32,21312,5261],{"class":41},[32,21314,21315,21318],{"class":34,"line":51},[32,21316,21317],{"class":217},"    get",[32,21319,4221],{"class":41},[32,21321,21322,21324,21326],{"class":34,"line":90},[32,21323,18463],{"class":38},[32,21325,4234],{"class":351},[32,21327,21328],{"class":41},".$store.state.keyword;\n",[32,21330,21331],{"class":34,"line":99},[32,21332,4204],{"class":41},[32,21334,21335,21338],{"class":34,"line":106},[32,21336,21337],{"class":217},"    set",[32,21339,21340],{"class":41},"(value) {\n",[32,21342,21343,21346,21348,21350,21352,21355],{"class":34,"line":114},[32,21344,21345],{"class":351},"      this",[32,21347,20228],{"class":41},[32,21349,18072],{"class":217},[32,21351,477],{"class":41},[32,21353,21354],{"class":175},"'SET_KEYWORD'",[32,21356,21357],{"class":41},", value);\n",[32,21359,21360],{"class":34,"line":120},[32,21361,4204],{"class":41},[32,21363,21364],{"class":34,"line":127},[32,21365,5639],{"class":41},[32,21367,21368],{"class":34,"line":132},[32,21369,54],{"class":41},[22,21371,21373],{"className":4038,"code":21372,"language":4034,"meta":27,"style":27},"\u003Cinput v-model=\"keyword\">\n",[29,21374,21375],{"__ignoreMap":27},[32,21376,21377,21379,21381,21383,21385,21388],{"class":34,"line":35},[32,21378,4046],{"class":41},[32,21380,4070],{"class":4049},[32,21382,4081],{"class":4053},[32,21384,172],{"class":41},[32,21386,21387],{"class":175},"\"keyword\"",[32,21389,4062],{"class":41},[11,21391,21392,21393,21395,21396,21398],{},"畫面上看起來就是普通的雙向綁定，實際上讀取跟寫入分別走 ",[29,21394,17996],{}," 讀取與 ",[29,21397,18072],{}," 觸發 mutation 兩條路徑，符合「只有 mutation 能改 state」的規則。",[15,21400,17846],{"id":17846},[11,21402,21403,21406,21407,21410,21411,21413,21414,21416,21417,12793],{},[12249,21404,21405],{},"這篇整理的全部語法對既有的 Vuex 專案依然完全有效","，Vuex 沒有被移除、也還在維護。但新專案的建議已經是 Pinia——",[29,21408,21409],{},"getter\u002Fsetter computed"," 這種為了配合 ",[29,21412,21280],{}," 而繞的寫法，在 Pinia 裡不需要，直接讀寫 state 就好；",[29,21415,20172],{}," 這一整層在 Pinia 裡也被拿掉了。完整比較看《",[412,21418,17937],{"href":17936},[15,21420,4536],{"id":4536},[4538,21422,21423,21428],{},[4541,21424,21425],{},[412,21426,18658],{"href":18656,"rel":21427},[416],[4541,21429,21430],{},[412,21431,21434],{"href":21432,"rel":21433},"https:\u002F\u002Fvuex.vuejs.org\u002Fzh\u002Fguide\u002Fmutations.html",[416],"Vuex：Mutations",[368,21436,21437],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}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 .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}",{"title":27,"searchDepth":51,"depth":51,"links":21439},[21440,21441,21442,21443,21444,21445,21446,21447,21448,21449],{"id":19805,"depth":45,"text":19805},{"id":19985,"depth":45,"text":19986},{"id":20007,"depth":45,"text":20008},{"id":20161,"depth":45,"text":20162},{"id":20396,"depth":45,"text":20397},{"id":20763,"depth":45,"text":20764},{"id":21065,"depth":45,"text":21066},{"id":21274,"depth":45,"text":21275},{"id":17846,"depth":45,"text":17846},{"id":4536,"depth":45,"text":4536},"整理當時上課做的一系列 Vuex 練習筆記——安裝與注入 store、state 的兩種取用方式、mutations 的三種 commit 傳參方式、actions 裡串接多個非同步動作、getters 回傳值／參數／函式三種型態、modules 拆分，以及 v-model 搭配 store 狀態的 get\u002Fset 寫法。核心概念與 Pinia 比較另外整理在別篇，這篇純粹是語法速查。",{},"\u002Fposts\u002Fvuex-syntax-cheatsheet",[],{"title":19788,"description":21450},"posts\u002F20200421.vuex-syntax-cheatsheet",[4844,4113],"-nV30uDKMAqY4tFwd_L4GAuYuQAYdSZ2Iki6mNkeJkg",{"id":21459,"title":21460,"body":21461,"category":378,"description":21682,"draft":380,"extension":381,"image":382,"meta":21683,"navigation":102,"path":21684,"publishedAt":21685,"relatedExperiments":21686,"seo":21687,"stem":21688,"tags":21689,"updatedAt":14433,"__hash__":21690},"posts\u002Fposts\u002F20200429.nuxt-axios-module.md","Nuxt 安裝 @nuxtjs\u002Faxios 模組",{"type":8,"value":21462,"toc":21677},[21463,21466,21480,21486,21539,21549,21623,21639,21641,21663,21665,21674],[15,21464,21465],{"id":21465},"安裝",[22,21467,21469],{"className":2057,"code":21468,"language":2059,"meta":27,"style":27},"npm install @nuxtjs\u002Faxios\n",[29,21470,21471],{"__ignoreMap":27},[32,21472,21473,21475,21477],{"class":34,"line":35},[32,21474,4625],{"class":2066},[32,21476,4650],{"class":175},[32,21478,21479],{"class":175}," @nuxtjs\u002Faxios\n",[11,21481,21482,21485],{},[29,21483,21484],{},"nuxt.config.js"," 註冊模組：",[22,21487,21489],{"className":24,"code":21488,"language":26,"meta":27,"style":27},"export default {\n  modules: ['@nuxtjs\u002Faxios'],\n  axios: {\n    baseURL: 'https:\u002F\u002Fapi.example.com',\n  },\n};\n",[29,21490,21491,21499,21512,21517,21527,21533],{"__ignoreMap":27},[32,21492,21493,21495,21497],{"class":34,"line":35},[32,21494,5038],{"class":38},[32,21496,5041],{"class":38},[32,21498,703],{"class":5049},[32,21500,21501,21504,21507,21510],{"class":34,"line":45},[32,21502,21503],{"class":41},"  modules: [",[32,21505,21506],{"class":175},"'@nuxtjs\u002Faxios'",[32,21508,21509],{"class":41},"]",[32,21511,547],{"class":5049},[32,21513,21514],{"class":34,"line":51},[32,21515,21516],{"class":41},"  axios: {\n",[32,21518,21519,21522,21525],{"class":34,"line":90},[32,21520,21521],{"class":41},"    baseURL: ",[32,21523,21524],{"class":175},"'https:\u002F\u002Fapi.example.com'",[32,21526,547],{"class":41},[32,21528,21529,21531],{"class":34,"line":99},[32,21530,9758],{"class":41},[32,21532,547],{"class":5049},[32,21534,21535,21537],{"class":34,"line":106},[32,21536,9765],{"class":5049},[32,21538,96],{"class":41},[11,21540,21541,21542,21545,21546,5240],{},"註冊完之後，任何元件裡都能直接用 ",[29,21543,21544],{},"this.$axios"," 發請求，不需要自己在每個檔案裡 ",[29,21547,21548],{},"import axios",[22,21550,21552],{"className":24,"code":21551,"language":26,"meta":27,"style":27},"export default {\n  async asyncData({ $axios }) {\n    const posts = await $axios.$get('\u002Fposts');\n    return { posts };\n  },\n};\n",[29,21553,21554,21562,21580,21604,21611,21617],{"__ignoreMap":27},[32,21555,21556,21558,21560],{"class":34,"line":35},[32,21557,5038],{"class":38},[32,21559,5041],{"class":38},[32,21561,703],{"class":5049},[32,21563,21564,21567,21570,21572,21575,21578],{"class":34,"line":45},[32,21565,21566],{"class":38},"  async",[32,21568,21569],{"class":217}," asyncData",[32,21571,21143],{"class":5049},[32,21573,21574],{"class":903},"$axios",[32,21576,21577],{"class":5049}," }) ",[32,21579,5252],{"class":41},[32,21581,21582,21584,21587,21589,21591,21594,21597,21599,21602],{"class":34,"line":51},[32,21583,9409],{"class":38},[32,21585,21586],{"class":351}," posts",[32,21588,894],{"class":38},[32,21590,20431],{"class":38},[32,21592,21593],{"class":41}," $axios.",[32,21595,21596],{"class":217},"$get",[32,21598,477],{"class":41},[32,21600,21601],{"class":175},"'\u002Fposts'",[32,21603,230],{"class":41},[32,21605,21606,21608],{"class":34,"line":90},[32,21607,971],{"class":38},[32,21609,21610],{"class":41}," { posts };\n",[32,21612,21613,21615],{"class":34,"line":99},[32,21614,9758],{"class":41},[32,21616,547],{"class":5049},[32,21618,21619,21621],{"class":34,"line":106},[32,21620,9765],{"class":5049},[32,21622,96],{"class":41},[11,21624,21625,21628,21629,21631,21632,21635,21636,3775],{},[29,21626,21627],{},"$axios.$get()","（前面多一個 ",[29,21630,755],{},"）是這個模組提供的簡化方法，直接回傳 ",[29,21633,21634],{},"response.data","，不用自己再多寫一層 ",[29,21637,21638],{},".then(res => res.data)",[15,21640,17846],{"id":17846},[11,21642,21643,21649,21650,11621,21653,21656,21657,21662],{},[12249,21644,21645,21648],{},[29,21646,21647],{},"@nuxtjs\u002Faxios"," 已經停止維護","，Nuxt 官方在 Nuxt 3 之後不再推薦它，改成推內建的 ",[29,21651,21652],{},"useFetch",[29,21654,21655],{},"$fetch","（底層是 ",[412,21658,21661],{"href":21659,"rel":21660},"https:\u002F\u002Fgithub.com\u002Funjs\u002Fofetch",[416],"ofetch","），不需要額外安裝任何模組。整理在《Nuxt 內建的 useFetch 與 $fetch》。",[15,21664,4536],{"id":4536},[4538,21666,21667],{},[4541,21668,21669],{},[412,21670,21673],{"href":21671,"rel":21672},"https:\u002F\u002Faxios.nuxtjs.org\u002F",[416],"@nuxtjs\u002Faxios（已停止維護）",[368,21675,21676],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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 .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}",{"title":27,"searchDepth":51,"depth":51,"links":21678},[21679,21680,21681],{"id":21465,"depth":45,"text":21465},{"id":17846,"depth":45,"text":17846},{"id":4536,"depth":45,"text":4536},"記錄在 Nuxt 2 專案安裝 @nuxtjs\u002Faxios 模組，讓元件裡能直接用 this.$axios 發請求的做法。這個模組已經停止維護，Nuxt 3 之後改用內建的 useFetch／$fetch，整理在新的一篇。",{},"\u002Fposts\u002Fnuxt-axios-module","2020-04-29",[],{"title":21460,"description":21682},"posts\u002F20200429.nuxt-axios-module",[15981,9540],"Bj9DBkmUbopmf1A0aSmt4WjvVxQSoTXfwpTluCx7C8g",{"id":21692,"title":21693,"body":21694,"category":378,"description":21806,"draft":380,"extension":381,"image":382,"meta":21807,"navigation":102,"path":21808,"publishedAt":21809,"relatedExperiments":21810,"seo":21811,"stem":21812,"tags":21813,"updatedAt":14433,"__hash__":21815},"posts\u002Fposts\u002F20200729.chrome-focus-outline.md","消除 Chrome 按鈕的黑色外框",{"type":8,"value":21695,"toc":21800},[21696,21699,21702,21748,21755,21758,21768,21777,21780,21787,21789,21798],[11,21697,21698],{},"Chrome 更新後，點選按鈕會出現一圈黑色外框，跟原本設計稿的樣子不一樣。",[15,21700,21701],{"id":21701},"當時的做法",[22,21703,21705],{"className":5412,"code":21704,"language":1140,"meta":27,"style":27},"*,\n*:focus,\n*:hover {\n  outline: none !important;\n}\n",[29,21706,21707,21713,21722,21730,21744],{"__ignoreMap":27},[32,21708,21709,21711],{"class":34,"line":35},[32,21710,6078],{"class":4049},[32,21712,547],{"class":41},[32,21714,21715,21717,21720],{"class":34,"line":45},[32,21716,6078],{"class":4049},[32,21718,21719],{"class":4053},":focus",[32,21721,547],{"class":41},[32,21723,21724,21726,21728],{"class":34,"line":51},[32,21725,6078],{"class":4049},[32,21727,14706],{"class":4053},[32,21729,703],{"class":41},[32,21731,21732,21735,21737,21740,21742],{"class":34,"line":90},[32,21733,21734],{"class":351},"  outline",[32,21736,5269],{"class":41},[32,21738,21739],{"class":351},"none",[32,21741,11886],{"class":38},[32,21743,96],{"class":41},[32,21745,21746],{"class":34,"line":99},[32,21747,54],{"class":41},[11,21749,21750,21751,21754],{},"全域把所有元素的 ",[29,21752,21753],{},"outline"," 關掉，外框確實消失了。",[15,21756,21757],{"id":21757},"這個外框其實是什麼",[11,21759,21760,21763,21764,21767],{},[12249,21761,21762],{},"這圈外框不是 Chrome 的 bug，是瀏覽器的無障礙焦點指示器（focus indicator）。"," 元素被「聚焦」（focus）時——不管是滑鼠點擊，還是",[12249,21765,21766],{},"用 Tab 鍵在頁面上切換","——瀏覽器會畫出這圈外框，讓使用者知道「目前操作的對象是這個元素」。這對只靠鍵盤操作的使用者（螢幕閱讀器使用者、行動不便無法精準操作滑鼠的使用者，也包含單純不想用滑鼠、圖快用鍵盤操作表單的人）是必要的視覺線索——沒有它，Tab 鍵按到第幾個欄位、目前選到哪個按鈕，完全無從得知。",[11,21769,21770,21776],{},[12249,21771,21772,21775],{},[29,21773,21774],{},"*, *:focus, *:hover { outline: none !important; }"," 把這個指示器整個關掉，代價是所有人都看不到焦點在哪，包含真正需要它的鍵盤使用者。"," 這是網頁無障礙裡一個很常見、也很容易在不知情的狀況下犯的錯誤——單純覺得「這圈框很醜」就整個關掉，卻沒意識到它承擔的是可用性上的責任，不是裝飾。",[15,21778,21779],{"id":21779},"現在的做法",[11,21781,21782,21783,21786],{},"只想拿掉滑鼠點擊時出現的框、但保留鍵盤導覽時的框，現在有專門為這個情境設計的 CSS 偽類別 ",[29,21784,21785],{},":focus-visible","，整理在《:focus-visible：只在鍵盤導覽時顯示焦點框》。",[15,21788,4536],{"id":4536},[4538,21790,21791],{},[4541,21792,21793],{},[412,21794,21797],{"href":21795,"rel":21796},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-TW\u002Fdocs\u002FWeb\u002FCSS\u002Foutline",[416],"MDN：outline",[368,21799,12630],{},{"title":27,"searchDepth":51,"depth":51,"links":21801},[21802,21803,21804,21805],{"id":21701,"depth":45,"text":21701},{"id":21757,"depth":45,"text":21757},{"id":21779,"depth":45,"text":21779},{"id":4536,"depth":45,"text":4536},"記錄 Chrome 更新後點擊按鈕會出現黑色外框，當時用全域 outline none 消除它的做法。這個外框其實是瀏覽器的無障礙焦點指示器，全域關掉會讓鍵盤使用者完全看不到目前的焦點在哪，是應該避免的做法，現在的替代方案整理在新的一篇。",{},"\u002Fposts\u002Fchrome-focus-outline","2020-07-29",[],{"title":21693,"description":21806},"posts\u002F20200729.chrome-focus-outline",[1757,21814],"Chrome","wxNMvWSxbTjddwFmXy6DBxbsGuQeraufXLe5nC2wATw",{"id":21817,"title":21818,"body":21819,"category":378,"description":22947,"draft":380,"extension":381,"image":382,"meta":22948,"navigation":102,"path":22949,"publishedAt":22950,"relatedExperiments":22951,"seo":22952,"stem":22953,"tags":22954,"updatedAt":14433,"__hash__":22955},"posts\u002Fposts\u002F20200820.js-array-methods.md","JavaScript 陣列方法整理：forEach、map、filter、find、some、every、sort、reduce",{"type":8,"value":21820,"toc":22935},[21821,21824,21902,21906,21960,21971,21975,22075,22081,22131,22135,22220,22229,22233,22328,22342,22346,22435,22444,22448,22537,22547,22551,22626,22662,22675,22679,22764,22790,22793,22906,22909,22911,22932],[11,21822,21823],{},"這幾個陣列方法名字很像，但回傳的東西、會不會動到原始資料完全不同，整理一份對照，用同一份商品清單示範。",[22,21825,21827],{"className":24,"code":21826,"language":26,"meta":27,"style":27},"const books = [\n  { name: '這樣思考，人生就不一樣', price: 320, discount: 0.9 },\n  { name: '原子習慣', price: 360, discount: 0.85 },\n  { name: '被討厭的勇氣', price: 300, discount: 0.79 },\n];\n",[29,21828,21829,21840,21860,21879,21898],{"__ignoreMap":27},[32,21830,21831,21833,21836,21838],{"class":34,"line":35},[32,21832,6617],{"class":38},[32,21834,21835],{"class":351}," books",[32,21837,894],{"class":38},[32,21839,16007],{"class":41},[32,21841,21842,21844,21847,21849,21852,21855,21858],{"class":34,"line":45},[32,21843,16012],{"class":41},[32,21845,21846],{"class":175},"'這樣思考，人生就不一樣'",[32,21848,16018],{"class":41},[32,21850,21851],{"class":351},"320",[32,21853,21854],{"class":41},", discount: ",[32,21856,21857],{"class":351},"0.9",[32,21859,16024],{"class":41},[32,21861,21862,21864,21867,21869,21872,21874,21877],{"class":34,"line":51},[32,21863,16012],{"class":41},[32,21865,21866],{"class":175},"'原子習慣'",[32,21868,16018],{"class":41},[32,21870,21871],{"class":351},"360",[32,21873,21854],{"class":41},[32,21875,21876],{"class":351},"0.85",[32,21878,16024],{"class":41},[32,21880,21881,21883,21886,21888,21891,21893,21896],{"class":34,"line":90},[32,21882,16012],{"class":41},[32,21884,21885],{"class":175},"'被討厭的勇氣'",[32,21887,16018],{"class":41},[32,21889,21890],{"class":351},"300",[32,21892,21854],{"class":41},[32,21894,21895],{"class":351},"0.79",[32,21897,16024],{"class":41},[32,21899,21900],{"class":34,"line":99},[32,21901,8282],{"class":41},[15,21903,21905],{"id":21904},"foreach對每個元素執行動作","forEach：對每個元素執行動作",[22,21907,21909],{"className":24,"code":21908,"language":26,"meta":27,"style":27},"books.forEach((book) => {\n  book.total = book.price * book.discount; \u002F\u002F 直接改變原本的物件\n  console.log(book.total);\n});\n",[29,21910,21911,21929,21947,21956],{"__ignoreMap":27},[32,21912,21913,21916,21918,21920,21923,21925,21927],{"class":34,"line":35},[32,21914,21915],{"class":41},"books.",[32,21917,8290],{"class":217},[32,21919,8293],{"class":41},[32,21921,21922],{"class":903},"book",[32,21924,483],{"class":41},[32,21926,5090],{"class":38},[32,21928,703],{"class":41},[32,21930,21931,21934,21936,21939,21941,21944],{"class":34,"line":45},[32,21932,21933],{"class":41},"  book.total ",[32,21935,172],{"class":38},[32,21937,21938],{"class":41}," book.price ",[32,21940,6078],{"class":38},[32,21942,21943],{"class":41}," book.discount; ",[32,21945,21946],{"class":608},"\u002F\u002F 直接改變原本的物件\n",[32,21948,21949,21951,21953],{"class":34,"line":51},[32,21950,5194],{"class":41},[32,21952,5197],{"class":217},[32,21954,21955],{"class":41},"(book.total);\n",[32,21957,21958],{"class":34,"line":90},[32,21959,792],{"class":41},[11,21961,21962,21963,21966,21967,21970],{},"單純跑一次迴圈，",[12249,21964,21965],{},"不會回傳新陣列","（回傳值是 ",[29,21968,21969],{},"undefined","），如果在迴圈裡改動了元素本身的屬性（元素是物件時），會直接影響原陣列——因為陣列裡存的是物件的參考，改物件屬性等於改到原本那份資料。",[15,21972,21974],{"id":21973},"map執行結果存到新陣列","map：執行結果存到新陣列",[22,21976,21978],{"className":24,"code":21977,"language":26,"meta":27,"style":27},"const numbers = [1, 2, 3, 4, 5, 6];\n\nconst squared = numbers.map((num) => num * num);\nconsole.log(squared); \u002F\u002F [1, 4, 9, 16, 25, 36]\nconsole.log(numbers); \u002F\u002F [1, 2, 3, 4, 5, 6] ← 原陣列不變\n",[29,21979,21980,22015,22019,22050,22063],{"__ignoreMap":27},[32,21981,21982,21984,21987,21989,21991,21993,21995,21997,21999,22001,22003,22005,22007,22009,22011,22013],{"class":34,"line":35},[32,21983,6617],{"class":38},[32,21985,21986],{"class":351}," numbers",[32,21988,894],{"class":38},[32,21990,8261],{"class":41},[32,21992,6544],{"class":351},[32,21994,635],{"class":41},[32,21996,11971],{"class":351},[32,21998,635],{"class":41},[32,22000,13470],{"class":351},[32,22002,635],{"class":41},[32,22004,13478],{"class":351},[32,22006,635],{"class":41},[32,22008,10613],{"class":351},[32,22010,635],{"class":41},[32,22012,13493],{"class":351},[32,22014,8282],{"class":41},[32,22016,22017],{"class":34,"line":45},[32,22018,103],{"emptyLinePlaceholder":102},[32,22020,22021,22023,22026,22028,22031,22033,22035,22038,22040,22042,22045,22047],{"class":34,"line":51},[32,22022,6617],{"class":38},[32,22024,22025],{"class":351}," squared",[32,22027,894],{"class":38},[32,22029,22030],{"class":41}," numbers.",[32,22032,11130],{"class":217},[32,22034,8293],{"class":41},[32,22036,22037],{"class":903},"num",[32,22039,483],{"class":41},[32,22041,5090],{"class":38},[32,22043,22044],{"class":41}," num ",[32,22046,6078],{"class":38},[32,22048,22049],{"class":41}," num);\n",[32,22051,22052,22055,22057,22060],{"class":34,"line":90},[32,22053,22054],{"class":41},"console.",[32,22056,5197],{"class":217},[32,22058,22059],{"class":41},"(squared); ",[32,22061,22062],{"class":608},"\u002F\u002F [1, 4, 9, 16, 25, 36]\n",[32,22064,22065,22067,22069,22072],{"class":34,"line":99},[32,22066,22054],{"class":41},[32,22068,5197],{"class":217},[32,22070,22071],{"class":41},"(numbers); ",[32,22073,22074],{"class":608},"\u002F\u002F [1, 2, 3, 4, 5, 6] ← 原陣列不變\n",[11,22076,22077,22080],{},[12249,22078,22079],{},"保留原陣列，回傳一份新陣列","，常用在把一批物件轉換、抽取、計算成另一種形狀：",[22,22082,22084],{"className":24,"code":22083,"language":26,"meta":27,"style":27},"const summary = books.map((book) => ({\n  name: book.name,\n  total: book.price * book.discount,\n}));\n",[29,22085,22086,22111,22116,22126],{"__ignoreMap":27},[32,22087,22088,22090,22093,22095,22098,22100,22102,22104,22106,22108],{"class":34,"line":35},[32,22089,6617],{"class":38},[32,22091,22092],{"class":351}," summary",[32,22094,894],{"class":38},[32,22096,22097],{"class":41}," books.",[32,22099,11130],{"class":217},[32,22101,8293],{"class":41},[32,22103,21922],{"class":903},[32,22105,483],{"class":41},[32,22107,5090],{"class":38},[32,22109,22110],{"class":41}," ({\n",[32,22112,22113],{"class":34,"line":45},[32,22114,22115],{"class":41},"  name: book.name,\n",[32,22117,22118,22121,22123],{"class":34,"line":51},[32,22119,22120],{"class":41},"  total: book.price ",[32,22122,6078],{"class":38},[32,22124,22125],{"class":41}," book.discount,\n",[32,22127,22128],{"class":34,"line":90},[32,22129,22130],{"class":41},"}));\n",[15,22132,22134],{"id":22133},"filter留下符合條件的元素","filter：留下符合條件的元素",[22,22136,22138],{"className":24,"code":22137,"language":26,"meta":27,"style":27},"const numbers = [1, 2, 3, 4, 5, 6];\n\nconst bigNumbers = numbers.filter((num) => num > 3);\nconsole.log(bigNumbers); \u002F\u002F [4, 5, 6]\n",[29,22139,22140,22174,22178,22208],{"__ignoreMap":27},[32,22141,22142,22144,22146,22148,22150,22152,22154,22156,22158,22160,22162,22164,22166,22168,22170,22172],{"class":34,"line":35},[32,22143,6617],{"class":38},[32,22145,21986],{"class":351},[32,22147,894],{"class":38},[32,22149,8261],{"class":41},[32,22151,6544],{"class":351},[32,22153,635],{"class":41},[32,22155,11971],{"class":351},[32,22157,635],{"class":41},[32,22159,13470],{"class":351},[32,22161,635],{"class":41},[32,22163,13478],{"class":351},[32,22165,635],{"class":41},[32,22167,10613],{"class":351},[32,22169,635],{"class":41},[32,22171,13493],{"class":351},[32,22173,8282],{"class":41},[32,22175,22176],{"class":34,"line":45},[32,22177,103],{"emptyLinePlaceholder":102},[32,22179,22180,22182,22185,22187,22189,22191,22193,22195,22197,22199,22201,22203,22206],{"class":34,"line":51},[32,22181,6617],{"class":38},[32,22183,22184],{"class":351}," bigNumbers",[32,22186,894],{"class":38},[32,22188,22030],{"class":41},[32,22190,16124],{"class":217},[32,22192,8293],{"class":41},[32,22194,22037],{"class":903},[32,22196,483],{"class":41},[32,22198,5090],{"class":38},[32,22200,22044],{"class":41},[32,22202,1615],{"class":38},[32,22204,22205],{"class":351}," 3",[32,22207,230],{"class":41},[32,22209,22210,22212,22214,22217],{"class":34,"line":90},[32,22211,22054],{"class":41},[32,22213,5197],{"class":217},[32,22215,22216],{"class":41},"(bigNumbers); ",[32,22218,22219],{"class":608},"\u002F\u002F [4, 5, 6]\n",[11,22221,22222,22223,22225,22226,22228],{},"依函式回傳的布林值決定要不要把元素留下來，",[12249,22224,22079],{},"，扮演「篩選閘門」的角色——回傳 ",[29,22227,6206],{}," 的元素才會出現在結果裡。",[15,22230,22232],{"id":22231},"find找到第一個符合條件的元素","find：找到第一個符合條件的元素",[22,22234,22236],{"className":24,"code":22235,"language":26,"meta":27,"style":27},"const dnaList = ['ATC', 'AGC', 'AAA', 'TTG'];\n\nconst found = dnaList.find((dna) => dna.indexOf('TG') > -1);\nconsole.log(found); \u002F\u002F 'TTG'\n",[29,22237,22238,22269,22273,22316],{"__ignoreMap":27},[32,22239,22240,22242,22245,22247,22249,22252,22254,22257,22259,22262,22264,22267],{"class":34,"line":35},[32,22241,6617],{"class":38},[32,22243,22244],{"class":351}," dnaList",[32,22246,894],{"class":38},[32,22248,8261],{"class":41},[32,22250,22251],{"class":175},"'ATC'",[32,22253,635],{"class":41},[32,22255,22256],{"class":175},"'AGC'",[32,22258,635],{"class":41},[32,22260,22261],{"class":175},"'AAA'",[32,22263,635],{"class":41},[32,22265,22266],{"class":175},"'TTG'",[32,22268,8282],{"class":41},[32,22270,22271],{"class":34,"line":45},[32,22272,103],{"emptyLinePlaceholder":102},[32,22274,22275,22277,22280,22282,22285,22287,22289,22292,22294,22296,22299,22301,22303,22306,22308,22310,22312,22314],{"class":34,"line":51},[32,22276,6617],{"class":38},[32,22278,22279],{"class":351}," found",[32,22281,894],{"class":38},[32,22283,22284],{"class":41}," dnaList.",[32,22286,20891],{"class":217},[32,22288,8293],{"class":41},[32,22290,22291],{"class":903},"dna",[32,22293,483],{"class":41},[32,22295,5090],{"class":38},[32,22297,22298],{"class":41}," dna.",[32,22300,1106],{"class":217},[32,22302,477],{"class":41},[32,22304,22305],{"class":175},"'TG'",[32,22307,483],{"class":41},[32,22309,1615],{"class":38},[32,22311,1119],{"class":38},[32,22313,6544],{"class":351},[32,22315,230],{"class":41},[32,22317,22318,22320,22322,22325],{"class":34,"line":90},[32,22319,22054],{"class":41},[32,22321,5197],{"class":217},[32,22323,22324],{"class":41},"(found); ",[32,22326,22327],{"class":608},"\u002F\u002F 'TTG'\n",[11,22329,22330,22331,22333,22334,22339,22340,3775],{},"跟 ",[29,22332,16124],{}," 的差別：",[12249,22335,22336,22338],{},[29,22337,20891],{}," 回傳的是單一元素本身，不是陣列","，而且只回傳第一個符合條件的，找到就停止繼續找，沒有符合條件的元素則回傳 ",[29,22341,21969],{},[15,22343,22345],{"id":22344},"some判斷是否有元素符合條件","some：判斷是否「有」元素符合條件",[22,22347,22349],{"className":24,"code":22348,"language":26,"meta":27,"style":27},"const numbers = [1, 2, 3, 4, 5, 6];\n\nconst hasEven = numbers.some((num) => num % 2 === 0);\nconsole.log(hasEven); \u002F\u002F true，只要有一個是偶數就成立\n",[29,22350,22351,22385,22389,22423],{"__ignoreMap":27},[32,22352,22353,22355,22357,22359,22361,22363,22365,22367,22369,22371,22373,22375,22377,22379,22381,22383],{"class":34,"line":35},[32,22354,6617],{"class":38},[32,22356,21986],{"class":351},[32,22358,894],{"class":38},[32,22360,8261],{"class":41},[32,22362,6544],{"class":351},[32,22364,635],{"class":41},[32,22366,11971],{"class":351},[32,22368,635],{"class":41},[32,22370,13470],{"class":351},[32,22372,635],{"class":41},[32,22374,13478],{"class":351},[32,22376,635],{"class":41},[32,22378,10613],{"class":351},[32,22380,635],{"class":41},[32,22382,13493],{"class":351},[32,22384,8282],{"class":41},[32,22386,22387],{"class":34,"line":45},[32,22388,103],{"emptyLinePlaceholder":102},[32,22390,22391,22393,22396,22398,22400,22403,22405,22407,22409,22411,22413,22415,22417,22419,22421],{"class":34,"line":51},[32,22392,6617],{"class":38},[32,22394,22395],{"class":351}," hasEven",[32,22397,894],{"class":38},[32,22399,22030],{"class":41},[32,22401,22402],{"class":217},"some",[32,22404,8293],{"class":41},[32,22406,22037],{"class":903},[32,22408,483],{"class":41},[32,22410,5090],{"class":38},[32,22412,22044],{"class":41},[32,22414,14170],{"class":38},[32,22416,10982],{"class":351},[32,22418,994],{"class":38},[32,22420,1523],{"class":351},[32,22422,230],{"class":41},[32,22424,22425,22427,22429,22432],{"class":34,"line":90},[32,22426,22054],{"class":41},[32,22428,5197],{"class":217},[32,22430,22431],{"class":41},"(hasEven); ",[32,22433,22434],{"class":608},"\u002F\u002F true，只要有一個是偶數就成立\n",[11,22436,22437,22438,22443],{},"回傳布林值，",[12249,22439,22440,22441],{},"只要有一個元素符合條件就回傳 ",[29,22442,6206],{},"，適合「大概知道有沒有，不需要知道確切是哪一筆」的情境——例如「這些文章裡有沒有含某個關鍵字」「會員裡有沒有人不符合資格」。",[15,22445,22447],{"id":22446},"every判斷是否全部元素都符合條件","every：判斷是否「全部」元素都符合條件",[22,22449,22451],{"className":24,"code":22450,"language":26,"meta":27,"style":27},"const numbers = [1, 2, 3, 4, 5, 6];\n\nconst allEven = numbers.every((num) => num % 2 === 0);\nconsole.log(allEven); \u002F\u002F false，因為 1、3、5 不是偶數\n",[29,22452,22453,22487,22491,22525],{"__ignoreMap":27},[32,22454,22455,22457,22459,22461,22463,22465,22467,22469,22471,22473,22475,22477,22479,22481,22483,22485],{"class":34,"line":35},[32,22456,6617],{"class":38},[32,22458,21986],{"class":351},[32,22460,894],{"class":38},[32,22462,8261],{"class":41},[32,22464,6544],{"class":351},[32,22466,635],{"class":41},[32,22468,11971],{"class":351},[32,22470,635],{"class":41},[32,22472,13470],{"class":351},[32,22474,635],{"class":41},[32,22476,13478],{"class":351},[32,22478,635],{"class":41},[32,22480,10613],{"class":351},[32,22482,635],{"class":41},[32,22484,13493],{"class":351},[32,22486,8282],{"class":41},[32,22488,22489],{"class":34,"line":45},[32,22490,103],{"emptyLinePlaceholder":102},[32,22492,22493,22495,22498,22500,22502,22505,22507,22509,22511,22513,22515,22517,22519,22521,22523],{"class":34,"line":51},[32,22494,6617],{"class":38},[32,22496,22497],{"class":351}," allEven",[32,22499,894],{"class":38},[32,22501,22030],{"class":41},[32,22503,22504],{"class":217},"every",[32,22506,8293],{"class":41},[32,22508,22037],{"class":903},[32,22510,483],{"class":41},[32,22512,5090],{"class":38},[32,22514,22044],{"class":41},[32,22516,14170],{"class":38},[32,22518,10982],{"class":351},[32,22520,994],{"class":38},[32,22522,1523],{"class":351},[32,22524,230],{"class":41},[32,22526,22527,22529,22531,22534],{"class":34,"line":90},[32,22528,22054],{"class":41},[32,22530,5197],{"class":217},[32,22532,22533],{"class":41},"(allEven); ",[32,22535,22536],{"class":608},"\u002F\u002F false，因為 1、3、5 不是偶數\n",[11,22538,22539,22544,22545,3775],{},[12249,22540,22541,22542],{},"每一個元素都符合條件才回傳 ",[29,22543,6206],{},"，任何一個不符合就整體回傳 ",[29,22546,9746],{},[15,22548,22550],{"id":22549},"sort依比較函式排序","sort：依比較函式排序",[22,22552,22554],{"className":24,"code":22553,"language":26,"meta":27,"style":27},"const members = [{ year: 27 }, { year: 14 }, { year: 50 }];\n\nmembers.sort((a, b) => a.year - b.year);\nconsole.log(members); \u002F\u002F [{year:14}, {year:27}, {year:50}]\n",[29,22555,22556,22583,22587,22614],{"__ignoreMap":27},[32,22557,22558,22560,22563,22565,22568,22571,22574,22577,22579,22581],{"class":34,"line":35},[32,22559,6617],{"class":38},[32,22561,22562],{"class":351}," members",[32,22564,894],{"class":38},[32,22566,22567],{"class":41}," [{ year: ",[32,22569,22570],{"class":351},"27",[32,22572,22573],{"class":41}," }, { year: ",[32,22575,22576],{"class":351},"14",[32,22578,22573],{"class":41},[32,22580,19068],{"class":351},[32,22582,16212],{"class":41},[32,22584,22585],{"class":34,"line":45},[32,22586,103],{"emptyLinePlaceholder":102},[32,22588,22589,22592,22594,22596,22598,22600,22602,22604,22606,22609,22611],{"class":34,"line":51},[32,22590,22591],{"class":41},"members.",[32,22593,16080],{"class":217},[32,22595,8293],{"class":41},[32,22597,412],{"class":903},[32,22599,635],{"class":41},[32,22601,16089],{"class":903},[32,22603,483],{"class":41},[32,22605,5090],{"class":38},[32,22607,22608],{"class":41}," a.year ",[32,22610,6628],{"class":38},[32,22612,22613],{"class":41}," b.year);\n",[32,22615,22616,22618,22620,22623],{"class":34,"line":90},[32,22617,22054],{"class":41},[32,22619,5197],{"class":217},[32,22621,22622],{"class":41},"(members); ",[32,22624,22625],{"class":608},"\u002F\u002F [{year:14}, {year:27}, {year:50}]\n",[11,22627,22628,22631,22632,22635,22636,11621,22638,22640,22641,22643,22644,3750,22646,22643,22648,22650,22651,5240,22654,22657,22658,22661],{},[12249,22629,22630],{},"比較函式一定要回傳數字，不能回傳布林值","——這是最容易寫錯的地方：",[29,22633,22634],{},"(a, b) => a.year > b.year"," 回傳的是 ",[29,22637,6206],{},[29,22639,9746],{},"，雖然多數瀏覽器引擎會把 ",[29,22642,6206],{}," 當成 ",[29,22645,6544],{},[29,22647,9746],{},[29,22649,11881],{}," 處理、勉強排得出一個看起來正確的順序，但這不是規格保證的行為，遇到需要穩定排序（相同值的元素要保持原本的相對順序）或某些特殊資料分佈時可能出錯。正確的寫法是回傳兩者的",[12249,22652,22653],{},"差值",[29,22655,22656],{},"a - b"," 是由小到大，",[29,22659,22660],{},"b - a"," 是由大到小。",[11,22663,22664,22666,22667,22670,22671,22674],{},[29,22665,16148],{}," 也要記得它是",[12249,22668,22669],{},"原地排序，會直接修改原陣列","，這點在",[412,22672,22673],{"href":16358},"之前寫過的 sort\u002Ffilter 筆記","裡有更完整的說明。",[15,22676,22678],{"id":22677},"reduce把整個陣列縮減成一個值","reduce：把整個陣列縮減成一個值",[22,22680,22682],{"className":24,"code":22681,"language":26,"meta":27,"style":27},"const books2 = [{ price: 10 }, { price: 20 }, { price: 30 }];\n\nconst total = books2.reduce((sum, book) => sum + book.price, 0);\nconsole.log(total); \u002F\u002F 60\n",[29,22683,22684,22708,22712,22752],{"__ignoreMap":27},[32,22685,22686,22688,22691,22693,22695,22697,22699,22702,22704,22706],{"class":34,"line":35},[32,22687,6617],{"class":38},[32,22689,22690],{"class":351}," books2",[32,22692,894],{"class":38},[32,22694,16198],{"class":41},[32,22696,11229],{"class":351},[32,22698,16203],{"class":41},[32,22700,22701],{"class":351},"20",[32,22703,16203],{"class":41},[32,22705,17486],{"class":351},[32,22707,16212],{"class":41},[32,22709,22710],{"class":34,"line":45},[32,22711,103],{"emptyLinePlaceholder":102},[32,22713,22714,22716,22719,22721,22724,22727,22729,22732,22734,22736,22738,22740,22743,22745,22748,22750],{"class":34,"line":51},[32,22715,6617],{"class":38},[32,22717,22718],{"class":351}," total",[32,22720,894],{"class":38},[32,22722,22723],{"class":41}," books2.",[32,22725,22726],{"class":217},"reduce",[32,22728,8293],{"class":41},[32,22730,22731],{"class":903},"sum",[32,22733,635],{"class":41},[32,22735,21922],{"class":903},[32,22737,483],{"class":41},[32,22739,5090],{"class":38},[32,22741,22742],{"class":41}," sum ",[32,22744,224],{"class":38},[32,22746,22747],{"class":41}," book.price, ",[32,22749,11881],{"class":351},[32,22751,230],{"class":41},[32,22753,22754,22756,22758,22761],{"class":34,"line":90},[32,22755,22054],{"class":41},[32,22757,5197],{"class":217},[32,22759,22760],{"class":41},"(total); ",[32,22762,22763],{"class":608},"\u002F\u002F 60\n",[11,22765,22766,22768,22769,22772,22773,22775,22776,22778,22779,22782,22783,22786,22787,22789],{},[29,22767,22726],{}," 吃兩個參數：",[12249,22770,22771],{},"處理函式","跟",[12249,22774,16418],{},"（範例裡的 ",[29,22777,11881],{},"）。處理函式本身也吃兩個參數：",[12249,22780,22781],{},"累加器","（上一輪執行完的結果，第一輪則是初始值）跟",[12249,22784,22785],{},"目前的元素","。可以想成「帶了記憶（暫存器）的 ",[29,22788,8290],{},"」，除了加總，也常用在分類歸群、把陣列接成字串這類「多筆資料合併成一個結果」的情境。",[15,22791,22792],{"id":22792},"對照表",[13433,22794,22795,22807],{},[13436,22796,22797],{},[13439,22798,22799,22801,22804],{},[13442,22800,16441],{},[13442,22802,22803],{},"回傳什麼",[13442,22805,22806],{},"動到原陣列嗎",[13449,22808,22809,22822,22834,22845,22859,22870,22880,22895],{},[13439,22810,22811,22815,22819],{},[13454,22812,22813],{},[29,22814,8290],{},[13454,22816,22817],{},[29,22818,21969],{},[13454,22820,22821],{},"元素是物件時，改屬性會動到原資料",[13439,22823,22824,22828,22831],{},[13454,22825,22826],{},[29,22827,11130],{},[13454,22829,22830],{},"新陣列（一對一轉換）",[13454,22832,22833],{},"不會",[13439,22835,22836,22840,22843],{},[13454,22837,22838],{},[29,22839,16124],{},[13454,22841,22842],{},"新陣列（篩選後的子集）",[13454,22844,22833],{},[13439,22846,22847,22851,22857],{},[13454,22848,22849],{},[29,22850,20891],{},[13454,22852,22853,22854,22856],{},"單一元素（或 ",[29,22855,21969],{},"）",[13454,22858,22833],{},[13439,22860,22861,22865,22868],{},[13454,22862,22863],{},[29,22864,22402],{},[13454,22866,22867],{},"布林值",[13454,22869,22833],{},[13439,22871,22872,22876,22878],{},[13454,22873,22874],{},[29,22875,22504],{},[13454,22877,22867],{},[13454,22879,22833],{},[13439,22881,22882,22886,22889],{},[13454,22883,22884],{},[29,22885,16080],{},[13454,22887,22888],{},"排序後的原陣列",[13454,22890,22891,22894],{},[12249,22892,22893],{},"會","，原地排序",[13439,22896,22897,22901,22904],{},[13454,22898,22899],{},[29,22900,22726],{},[13454,22902,22903],{},"累加後的單一結果",[13454,22905,22833],{},[11,22907,22908],{},"後來又多了幾個好用的陣列方法，整理在《後來新增的陣列方法：at、findLast、toSorted 這一批》。",[15,22910,4536],{"id":4536},[4538,22912,22913,22920,22925],{},[4541,22914,22915],{},[412,22916,22919],{"href":22917,"rel":22918},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-TW\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FArray#%E5%AF%A6%E4%BE%8B%E6%96%B9%E6%B3%95",[416],"MDN：Array 方法總覽",[4541,22921,22922],{},[412,22923,16341],{"href":16339,"rel":22924},[416],[4541,22926,22927],{},[412,22928,22931],{"href":22929,"rel":22930},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-TW\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FArray\u002Freduce",[416],"MDN：Array.prototype.reduce()",[368,22933,22934],{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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 .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}",{"title":27,"searchDepth":51,"depth":51,"links":22936},[22937,22938,22939,22940,22941,22942,22943,22944,22945,22946],{"id":21904,"depth":45,"text":21905},{"id":21973,"depth":45,"text":21974},{"id":22133,"depth":45,"text":22134},{"id":22231,"depth":45,"text":22232},{"id":22344,"depth":45,"text":22345},{"id":22446,"depth":45,"text":22447},{"id":22549,"depth":45,"text":22550},{"id":22677,"depth":45,"text":22678},{"id":22792,"depth":45,"text":22792},{"id":4536,"depth":45,"text":4536},"整理八個最常用的陣列方法差異——會不會產生新陣列、會不會動到原始資料、回傳的是單一元素還是整個陣列還是布林值。用同一份商品清單示範，並修正原始筆記裡 sort 比較函式回傳布林值而不是數字的常見錯誤。",{},"\u002Fposts\u002Fjs-array-methods","2020-08-20",[],{"title":21818,"description":22947},"posts\u002F20200820.js-array-methods",[390,16364],"cX6pIvqYGxg9p17attYDRbnQbUsv8Tl03NUZ3p8s3hI",{"id":22957,"title":22958,"body":22959,"category":4012,"description":23232,"draft":380,"extension":381,"image":382,"meta":23233,"navigation":102,"path":23234,"publishedAt":23235,"relatedExperiments":23236,"seo":23237,"stem":23238,"tags":23239,"updatedAt":14433,"__hash__":23242},"posts\u002Fposts\u002F20200827.http-status-codes.md","HTTP 狀態碼整理：開發時真正會用到的那幾個",{"type":8,"value":22960,"toc":23223},[22961,22970,22974,22981,22985,23023,23027,23041,23051,23061,23074,23078,23138,23144,23148,23191,23194,23196,23206,23208],[11,22962,22963,22964,22969],{},"HTTP 狀態碼分成五大類，這篇整理開發時真正常用到的部分，不是每一個細碼都列——完整清單查",[412,22965,22968],{"href":22966,"rel":22967},"https:\u002F\u002Fzh.wikipedia.org\u002Fwiki\u002FHTTP%E7%8A%B6%E6%80%81%E7%A0%81",[416],"維基百科","更全。",[15,22971,22973],{"id":22972},"_1xx參考資訊","1xx：參考資訊",[11,22975,22976,22977,22980],{},"代表伺服器先暫時回應一個狀態，正式回應前用戶端要準備接收一個或多個 1xx 回應。實務上很少直接接觸到，",[29,22978,22979],{},"101","（切換通訊協定）在 WebSocket 的握手過程會用到。",[15,22982,22984],{"id":22983},"_2xx成功","2xx：成功",[13433,22986,22987,22997],{},[13436,22988,22989],{},[13439,22990,22991,22994],{},[13442,22992,22993],{},"狀態碼",[13442,22995,22996],{},"意思",[13449,22998,22999,23007,23015],{},[13439,23000,23001,23004],{},[13454,23002,23003],{},"200",[13454,23005,23006],{},"請求成功",[13439,23008,23009,23012],{},[13454,23010,23011],{},"201",[13454,23013,23014],{},"已建立（常見於 POST 建立新資源後）",[13439,23016,23017,23020],{},[13454,23018,23019],{},"204",[13454,23021,23022],{},"無內容（成功但沒有要回傳的內容，常見於 DELETE）",[15,23024,23026],{"id":23025},"_3xx重新導向容易搞混的幾個","3xx：重新導向，容易搞混的幾個",[11,23028,23029,23032,23033,23036,23037,23040],{},[12249,23030,23031],{},"301 vs 302","：都是「這個網址的內容在別的地方」，差在",[12249,23034,23035],{},"永久","還是",[12249,23038,23039],{},"暫時","。301 會被瀏覽器與搜尋引擎快取記住，之後同樣的請求會直接跳過原網址；302 每次都要重新問一次原網址。網域搬家、網址結構永久改版用 301；短期活動頁、A\u002FB 測試這類暫時性轉址用 302。",[11,23042,23043,23046,23047,23050],{},[12249,23044,23045],{},"303","：通知用戶端改用 ",[12249,23048,23049],{},"GET"," 方法連到另一個網址查看結果，常見於「表單送出用 POST，處理完後轉址到結果頁」這種情境，避免使用者重新整理結果頁時又送出一次表單。",[11,23052,23053,23056,23057,23060],{},[12249,23054,23055],{},"307","：跟 302 一樣是暫時重新導向，但",[12249,23058,23059],{},"明確要求用戶端不能把方法從 POST 改成 GET","——302 在部分舊瀏覽器的實作上會把後續請求方法改成 GET，307 是為了消除這個歧義而生的版本，語意更精確。",[11,23062,23063,23066,23067,11621,23070,23073],{},[12249,23064,23065],{},"304","：用戶端請求的頁面內容沒有變更，直接沿用本地快取的版本，不用重新下載——這是瀏覽器快取機制運作的核心狀態碼，搭配 ",[29,23068,23069],{},"ETag",[29,23071,23072],{},"Last-Modified"," 這類標頭一起看。",[15,23075,23077],{"id":23076},"_4xx用戶端錯誤","4xx：用戶端錯誤",[13433,23079,23080,23088],{},[13436,23081,23082],{},[13439,23083,23084,23086],{},[13442,23085,22993],{},[13442,23087,22996],{},[13449,23089,23090,23098,23106,23114,23122,23130],{},[13439,23091,23092,23095],{},[13454,23093,23094],{},"400",[13454,23096,23097],{},"請求格式錯誤，伺服器看不懂",[13439,23099,23100,23103],{},[13454,23101,23102],{},"401",[13454,23104,23105],{},"未經授權（沒登入，或憑證無效）",[13439,23107,23108,23111],{},[13454,23109,23110],{},"403",[13454,23112,23113],{},"禁止存取（已知道你是誰，但沒有權限）",[13439,23115,23116,23119],{},[13454,23117,23118],{},"404",[13454,23120,23121],{},"找不到資源",[13439,23123,23124,23127],{},[13454,23125,23126],{},"405",[13454,23128,23129],{},"方法不被允許（例如對一個只接受 GET 的路徑發 POST）",[13439,23131,23132,23135],{},[13454,23133,23134],{},"429",[13454,23136,23137],{},"請求過於頻繁（觸發 rate limit）",[11,23139,23140,23143],{},[12249,23141,23142],{},"401 跟 403 常被搞混","：401 是「你還沒證明自己是誰」（該去登入），403 是「知道你是誰了，但你沒有權限做這件事」——一個是身分問題，一個是權限問題。",[15,23145,23147],{"id":23146},"_5xx伺服器錯誤","5xx：伺服器錯誤",[13433,23149,23150,23158],{},[13436,23151,23152],{},[13439,23153,23154,23156],{},[13442,23155,22993],{},[13442,23157,22996],{},[13449,23159,23160,23167,23175,23183],{},[13439,23161,23162,23164],{},[13454,23163,16709],{},[13454,23165,23166],{},"內部伺服器錯誤（籠統的「後端壞了」）",[13439,23168,23169,23172],{},[13454,23170,23171],{},"502",[13454,23173,23174],{},"閘道收到無效回應（反向代理或負載平衡器後面的伺服器沒有正常回應）",[13439,23176,23177,23180],{},[13454,23178,23179],{},"503",[13454,23181,23182],{},"服務無法使用（伺服器過載或維護中）",[13439,23184,23185,23188],{},[13454,23186,23187],{},"504",[13454,23189,23190],{},"閘道逾時（後端伺服器處理太久沒回應）",[11,23192,23193],{},"看到 4xx 不要先急著懷疑自己的程式碼寫錯——先確認是不是請求本身的問題（網址、方法、授權標頭）；看到 5xx 通常代表問題出在對方伺服器，前端能做的有限，通常是加上重試機制或錯誤提示。",[15,23195,17846],{"id":17846},[11,23197,23198,23201,23202,23205],{},[12249,23199,23200],{},"HTTP 狀態碼的定義本身是穩定的規範，這幾年沒有本質上的變化","，429（Too Many Requests）是相對後來才廣泛使用的狀態碼，API 限流的情境現在很常見。這篇原始筆記裡列的許多細碼（像 401.1～401.7、403.1～403.20 這類）是 ",[12249,23203,23204],{},"IIS 專屬的擴充子狀態碼","，只在 IIS 的錯誤紀錄裡看得到，不是 HTTP 規格本身定義的一部分，一般前端開發不太需要背這些，這裡就不逐條列出。",[15,23207,4536],{"id":4536},[4538,23209,23210,23216],{},[4541,23211,23212],{},[412,23213,23215],{"href":22966,"rel":23214},[416],"維基百科：HTTP 狀態碼",[4541,23217,23218],{},[412,23219,23222],{"href":23220,"rel":23221},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-TW\u002Fdocs\u002FWeb\u002FHTTP\u002FStatus",[416],"MDN：HTTP 回應狀態碼",{"title":27,"searchDepth":51,"depth":51,"links":23224},[23225,23226,23227,23228,23229,23230,23231],{"id":22972,"depth":45,"text":22973},{"id":22983,"depth":45,"text":22984},{"id":23025,"depth":45,"text":23026},{"id":23076,"depth":45,"text":23077},{"id":23146,"depth":45,"text":23147},{"id":17846,"depth":45,"text":17846},{"id":4536,"depth":45,"text":4536},"整理 1xx 到 5xx 五大類狀態碼裡，前端與後端開發時真正常碰到的項目，包含 301／302／303／307 這幾個重新導向狀態碼的語意差異、304 快取的運作方式，以及常見的 4xx／5xx 錯誤代表什麼。",{},"\u002Fposts\u002Fhttp-status-codes","2020-08-27",[],{"title":22958,"description":23232},"posts\u002F20200827.http-status-codes",[23240,23241],"HTTP","網路","EWjD8_Vst-A2lfs5o0JEgfvpEKqkRf01diaKzD5A9gg",{"id":23244,"title":23245,"body":23246,"category":378,"description":23563,"draft":380,"extension":381,"image":382,"meta":23564,"navigation":102,"path":23565,"publishedAt":23235,"relatedExperiments":23566,"seo":23567,"stem":23568,"tags":23569,"updatedAt":14433,"__hash__":23571},"posts\u002Fposts\u002F20200827.nuxt-middleware-auth.md","Nuxt 2 Middleware：進頁面前做登入驗證",{"type":8,"value":23247,"toc":23556},[23248,23251,23255,23270,23274,23279,23396,23401,23404,23496,23512,23531,23533,23542,23544,23553],[11,23249,23250],{},"Nuxt 的 middleware（中介軟體）在頁面真正渲染之前先執行，可以用來做「進這個頁面之前，先檢查有沒有權限」這類需要提前攔截的邏輯。",[15,23252,23254],{"id":23253},"順帶一提vue-router-在-nuxt-裡不是直接可用的","順帶一提：vue-router 在 Nuxt 裡不是直接可用的",[11,23256,23257,23258,23261,23262,23265,23266,23269],{},"Nuxt 底層雖然用 vue-router，但",[12249,23259,23260],{},"預設不會讓你直接操作 vue-router 的 API","（例如直接寫 ",[29,23263,23264],{},"router.beforeEach","），Nuxt 把路由設定包裝在自己的檔案系統路由與 middleware 機制裡。真的需要直接控制 vue-router 的底層行為，可以另外安裝 ",[29,23267,23268],{},"@nuxtjs\u002Frouter","，但多數需求（包含這篇的登入驗證）用 middleware 就能處理。",[15,23271,23273],{"id":23272},"新增-middleware","新增 middleware",[11,23275,23276,5240],{},[29,23277,23278],{},"middleware\u002Fauth.js",[22,23280,23282],{"className":24,"code":23281,"language":26,"meta":27,"style":27},"export default ({ route, redirect }) => {\n  if (route.meta[0].requiresAuth) {\n    console.log('需要驗證');\n    const authState = false; \u002F\u002F 實際專案會讀 store 或 cookie 判斷\n\n    if (!authState) {\n      \u002F\u002F 若沒有登入就導回登入頁\n      return redirect('\u002F');\n    }\n  }\n};\n",[29,23283,23284,23307,23319,23332,23350,23354,23366,23371,23384,23388,23392],{"__ignoreMap":27},[32,23285,23286,23288,23290,23293,23296,23298,23301,23303,23305],{"class":34,"line":35},[32,23287,5038],{"class":38},[32,23289,5041],{"class":38},[32,23291,23292],{"class":5049}," ({ ",[32,23294,23295],{"class":903},"route",[32,23297,635],{"class":5049},[32,23299,23300],{"class":903},"redirect",[32,23302,21577],{"class":5049},[32,23304,5090],{"class":38},[32,23306,703],{"class":41},[32,23308,23309,23311,23314,23316],{"class":34,"line":45},[32,23310,1021],{"class":38},[32,23312,23313],{"class":41}," (route.meta[",[32,23315,11881],{"class":351},[32,23317,23318],{"class":41},"].requiresAuth) {\n",[32,23320,23321,23323,23325,23327,23330],{"class":34,"line":51},[32,23322,8966],{"class":41},[32,23324,5197],{"class":217},[32,23326,477],{"class":41},[32,23328,23329],{"class":175},"'需要驗證'",[32,23331,230],{"class":41},[32,23333,23334,23336,23339,23341,23344,23347],{"class":34,"line":90},[32,23335,9409],{"class":38},[32,23337,23338],{"class":351}," authState",[32,23340,894],{"class":38},[32,23342,23343],{"class":351}," false",[32,23345,23346],{"class":41},"; ",[32,23348,23349],{"class":608},"\u002F\u002F 實際專案會讀 store 或 cookie 判斷\n",[32,23351,23352],{"class":34,"line":99},[32,23353,103],{"emptyLinePlaceholder":102},[32,23355,23356,23358,23360,23363],{"class":34,"line":106},[32,23357,5554],{"class":38},[32,23359,900],{"class":41},[32,23361,23362],{"class":38},"!",[32,23364,23365],{"class":41},"authState) {\n",[32,23367,23368],{"class":34,"line":114},[32,23369,23370],{"class":608},"      \u002F\u002F 若沒有登入就導回登入頁\n",[32,23372,23373,23375,23378,23380,23382],{"class":34,"line":120},[32,23374,18463],{"class":38},[32,23376,23377],{"class":217}," redirect",[32,23379,477],{"class":41},[32,23381,5067],{"class":175},[32,23383,230],{"class":41},[32,23385,23386],{"class":34,"line":127},[32,23387,5591],{"class":41},[32,23389,23390],{"class":34,"line":132},[32,23391,1203],{"class":41},[32,23393,23394],{"class":34,"line":141},[32,23395,7189],{"class":41},[11,23397,23398,23400],{},[29,23399,23300],{}," 是 Nuxt 的 middleware context 提供的方法，呼叫它會中斷原本的頁面渲染，改導向指定的路徑。",[15,23402,23403],{"id":23403},"在頁面裡套用",[22,23405,23407],{"className":8665,"code":23406,"language":4112,"meta":27,"style":27},"\u003Cscript>\nexport default {\n  layout: 'login',\n  middleware: 'auth',\n  meta: {\n    requiresAuth: true,\n  },\n  data() {\n    \u002F\u002F ...\n  },\n};\n\u003C\u002Fscript>\n",[29,23408,23409,23417,23425,23434,23444,23449,23458,23464,23472,23476,23482,23488],{"__ignoreMap":27},[32,23410,23411,23413,23415],{"class":34,"line":35},[32,23412,4046],{"class":41},[32,23414,4125],{"class":4049},[32,23416,4062],{"class":41},[32,23418,23419,23421,23423],{"class":34,"line":45},[32,23420,5038],{"class":38},[32,23422,5041],{"class":38},[32,23424,703],{"class":5049},[32,23426,23427,23429,23432],{"class":34,"line":51},[32,23428,15852],{"class":41},[32,23430,23431],{"class":175},"'login'",[32,23433,547],{"class":5049},[32,23435,23436,23439,23442],{"class":34,"line":90},[32,23437,23438],{"class":41},"  middleware: ",[32,23440,23441],{"class":175},"'auth'",[32,23443,547],{"class":5049},[32,23445,23446],{"class":34,"line":99},[32,23447,23448],{"class":41},"  meta: {\n",[32,23450,23451,23454,23456],{"class":34,"line":106},[32,23452,23453],{"class":41},"    requiresAuth: ",[32,23455,6206],{"class":351},[32,23457,547],{"class":41},[32,23459,23460,23462],{"class":34,"line":114},[32,23461,9758],{"class":41},[32,23463,547],{"class":5049},[32,23465,23466,23468,23470],{"class":34,"line":120},[32,23467,9728],{"class":217},[32,23469,6075],{"class":5049},[32,23471,5252],{"class":41},[32,23473,23474],{"class":34,"line":127},[32,23475,14360],{"class":608},[32,23477,23478,23480],{"class":34,"line":132},[32,23479,9758],{"class":41},[32,23481,547],{"class":5049},[32,23483,23484,23486],{"class":34,"line":141},[32,23485,9765],{"class":5049},[32,23487,96],{"class":41},[32,23489,23490,23492,23494],{"class":34,"line":147},[32,23491,4105],{"class":41},[32,23493,4125],{"class":4049},[32,23495,4062],{"class":41},[11,23497,23498,23501,23502,23504,23505,23508,23509,23511],{},[29,23499,23500],{},"middleware: 'auth'"," 的值對應 ",[29,23503,23278],{}," 的檔名，",[29,23506,23507],{},"meta.requiresAuth"," 則是自訂欄位，讓同一支 middleware 可以套用在多個頁面上，各自用 ",[29,23510,5665],{}," 決定要不要真正檢查。",[11,23513,23514,23523,23524,23526,23527,23530],{},[12249,23515,23516,23519,23520,23522],{},[29,23517,23518],{},"route.meta[0]","——這個用陣列索引存取 ",[29,23521,5665],{}," 是 Nuxt 2 一個容易搞混的地方","：多層 layout／頁面疊加時，",[29,23525,5665],{}," 實際上會是一個陣列（對應各層各自的 meta 設定），單一頁面沒有巢狀 layout 疊加時，",[29,23528,23529],{},"meta[0]"," 通常就是你要的那個物件，但這個索引寫法本身很容易讓人誤解成陣列裡有多筆不同資料，是 Nuxt 2 這套機制設計上不太直覺的地方。",[15,23532,17846],{"id":17846},[11,23534,23535,23538,23539,23541],{},[12249,23536,23537],{},"進頁面前先驗證的概念完全沒變","，但 Nuxt 3 之後 middleware 的寫法整個重寫過，",[29,23540,23529],{}," 這種容易誤解的陣列索引寫法也不存在了，整理在《Nuxt 3 的 Route Middleware：defineNuxtRouteMiddleware》。",[15,23543,4536],{"id":4536},[4538,23545,23546],{},[4541,23547,23548],{},[412,23549,23552],{"href":23550,"rel":23551},"https:\u002F\u002Fv2.nuxt.com\u002Fdocs\u002Fdirectory-structure\u002Fmiddleware",[416],"Nuxt 2：Middleware（封存版文件）",[368,23554,23555],{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}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 .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}",{"title":27,"searchDepth":51,"depth":51,"links":23557},[23558,23559,23560,23561,23562],{"id":23253,"depth":45,"text":23254},{"id":23272,"depth":45,"text":23273},{"id":23403,"depth":45,"text":23403},{"id":17846,"depth":45,"text":17846},{"id":4536,"depth":45,"text":4536},"整理 Nuxt 2 用 middleware 在頁面渲染前先做驗證的做法——在 middleware 資料夾新增檔案、頁面用 middleware 屬性指定要套用哪個驗證、meta 帶自訂欄位判斷是否需要登入，沒通過就用 redirect() 導回登入頁。Nuxt 3 之後改用 defineNuxtRouteMiddleware，寫法不同。",{},"\u002Fposts\u002Fnuxt-middleware-auth",[],{"title":23245,"description":23563},"posts\u002F20200827.nuxt-middleware-auth",[15981,23570],"驗證","2BvLWwfNVwG5ItQLE8gw5ymeoD3LfKwrwBXRMu_-KZc",{"id":23573,"title":23574,"body":23575,"category":378,"description":23881,"draft":380,"extension":381,"image":382,"meta":23882,"navigation":102,"path":23883,"publishedAt":23884,"relatedExperiments":23885,"seo":23886,"stem":23887,"tags":23888,"updatedAt":14433,"__hash__":23890},"posts\u002Fposts\u002F20200830.nuxt-config-redirect.md","Nuxt 2 設定重新導向：nuxt.config.js 的 extendRoutes",{"type":8,"value":23576,"toc":23875},[23577,23580,23584,23663,23678,23684,23688,23775,23828,23838,23840,23861,23863,23872],[11,23578,23579],{},"需要幫某個路徑設定重新導向（例如首頁固定轉去登入頁），Nuxt 2 有兩種常見做法。",[15,23581,23583],{"id":23582},"方法一nuxtconfigjs-的-extendroutes","方法一：nuxt.config.js 的 extendRoutes",[22,23585,23587],{"className":24,"code":23586,"language":26,"meta":27,"style":27},"export default {\n  router: {\n    extendRoutes(routes) {\n      routes.push({\n        path: '\u002F',\n        redirect: '\u002FLogin',\n      });\n    },\n  },\n};\n",[29,23588,23589,23597,23602,23614,23624,23633,23643,23647,23651,23657],{"__ignoreMap":27},[32,23590,23591,23593,23595],{"class":34,"line":35},[32,23592,5038],{"class":38},[32,23594,5041],{"class":38},[32,23596,703],{"class":5049},[32,23598,23599],{"class":34,"line":45},[32,23600,23601],{"class":41},"  router: {\n",[32,23603,23604,23607,23609,23612],{"class":34,"line":51},[32,23605,23606],{"class":217},"    extendRoutes",[32,23608,477],{"class":41},[32,23610,23611],{"class":903},"routes",[32,23613,520],{"class":41},[32,23615,23616,23619,23622],{"class":34,"line":90},[32,23617,23618],{"class":41},"      routes.",[32,23620,23621],{"class":217},"push",[32,23623,1143],{"class":41},[32,23625,23626,23629,23631],{"class":34,"line":99},[32,23627,23628],{"class":41},"        path: ",[32,23630,5067],{"class":175},[32,23632,547],{"class":41},[32,23634,23635,23638,23641],{"class":34,"line":106},[32,23636,23637],{"class":41},"        redirect: ",[32,23639,23640],{"class":175},"'\u002FLogin'",[32,23642,547],{"class":41},[32,23644,23645],{"class":34,"line":114},[32,23646,18085],{"class":41},[32,23648,23649],{"class":34,"line":120},[32,23650,4204],{"class":41},[32,23652,23653,23655],{"class":34,"line":127},[32,23654,9758],{"class":41},[32,23656,547],{"class":5049},[32,23658,23659,23661],{"class":34,"line":132},[32,23660,9765],{"class":5049},[32,23662,96],{"class":41},[11,23664,23665,23668,23669,23671,23672,23674,23675,23677],{},[29,23666,23667],{},"extendRoutes"," 是一個回呼函式，拿到 Nuxt 自動產生的路由清單（",[29,23670,23611],{},"），可以在這裡額外 ",[29,23673,23621],{}," 新的路由規則進去，",[29,23676,23300],{}," 欄位指定這個路徑要轉去哪裡。",[11,23679,23680,23683],{},[12249,23681,23682],{},"缺點是這是設定檔，改完要重啟開發伺服器才會生效","，不像頁面元件的修改能被 HMR（熱更新）立即反映，每次調整重新導向規則都要中斷開發流程重開一次伺服器，這是這個做法最麻煩的地方。",[15,23685,23687],{"id":23686},"方法二透過-middleware-檔案","方法二：透過 middleware 檔案",[22,23689,23691],{"className":24,"code":23690,"language":26,"meta":27,"style":27},"\u002F\u002F middleware\u002Fredirect.js\nexport default function ({ isHMR, route, redirect }) {\n  if (isHMR) return; \u002F\u002F 開發模式下 HMR 觸發時不重複執行\n\n  if (route.fullPath === '\u002F') {\n    return redirect('\u002FLogin');\n  }\n}\n",[29,23692,23693,23698,23723,23737,23741,23755,23767,23771],{"__ignoreMap":27},[32,23694,23695],{"class":34,"line":35},[32,23696,23697],{"class":608},"\u002F\u002F middleware\u002Fredirect.js\n",[32,23699,23700,23702,23704,23706,23708,23711,23713,23715,23717,23719,23721],{"class":34,"line":45},[32,23701,5038],{"class":38},[32,23703,5041],{"class":38},[32,23705,897],{"class":38},[32,23707,23292],{"class":5049},[32,23709,23710],{"class":903},"isHMR",[32,23712,635],{"class":5049},[32,23714,23295],{"class":903},[32,23716,635],{"class":5049},[32,23718,23300],{"class":903},[32,23720,21577],{"class":5049},[32,23722,5252],{"class":41},[32,23724,23725,23727,23730,23732,23734],{"class":34,"line":51},[32,23726,1021],{"class":38},[32,23728,23729],{"class":41}," (isHMR) ",[32,23731,4428],{"class":38},[32,23733,23346],{"class":41},[32,23735,23736],{"class":608},"\u002F\u002F 開發模式下 HMR 觸發時不重複執行\n",[32,23738,23739],{"class":34,"line":90},[32,23740,103],{"emptyLinePlaceholder":102},[32,23742,23743,23745,23748,23750,23753],{"class":34,"line":99},[32,23744,1021],{"class":38},[32,23746,23747],{"class":41}," (route.fullPath ",[32,23749,5560],{"class":38},[32,23751,23752],{"class":175}," '\u002F'",[32,23754,520],{"class":41},[32,23756,23757,23759,23761,23763,23765],{"class":34,"line":106},[32,23758,971],{"class":38},[32,23760,23377],{"class":217},[32,23762,477],{"class":41},[32,23764,23640],{"class":175},[32,23766,230],{"class":41},[32,23768,23769],{"class":34,"line":114},[32,23770,1203],{"class":41},[32,23772,23773],{"class":34,"line":120},[32,23774,54],{"class":41},[22,23776,23778],{"className":8665,"code":23777,"language":4112,"meta":27,"style":27},"\u003Cscript>\nexport default {\n  layout: 'dashboard',\n  middleware: 'redirect',\n};\n\u003C\u002Fscript>\n",[29,23779,23780,23788,23796,23805,23814,23820],{"__ignoreMap":27},[32,23781,23782,23784,23786],{"class":34,"line":35},[32,23783,4046],{"class":41},[32,23785,4125],{"class":4049},[32,23787,4062],{"class":41},[32,23789,23790,23792,23794],{"class":34,"line":45},[32,23791,5038],{"class":38},[32,23793,5041],{"class":38},[32,23795,703],{"class":5049},[32,23797,23798,23800,23803],{"class":34,"line":51},[32,23799,15852],{"class":41},[32,23801,23802],{"class":175},"'dashboard'",[32,23804,547],{"class":5049},[32,23806,23807,23809,23812],{"class":34,"line":90},[32,23808,23438],{"class":41},[32,23810,23811],{"class":175},"'redirect'",[32,23813,547],{"class":5049},[32,23815,23816,23818],{"class":34,"line":99},[32,23817,9765],{"class":5049},[32,23819,96],{"class":41},[32,23821,23822,23824,23826],{"class":34,"line":106},[32,23823,4105],{"class":41},[32,23825,4125],{"class":4049},[32,23827,4062],{"class":41},[11,23829,23830,23831,23834,23835,23837],{},"跟第一種比，middleware 是一般的 JS 檔案，改動後",[12249,23832,23833],{},"不需要重啟伺服器","，這也是 middleware 慢慢變成更常見選擇的原因——本質上跟《",[412,23836,23245],{"href":23565},"》是同一套機制，只是這裡拿來做重新導向而不是權限驗證。",[15,23839,17846],{"id":17846},[11,23841,23842,23848,23849,23852,23853,23856,23857,23860],{},[12249,23843,23844,23847],{},[29,23845,23846],{},"router.extendRoutes"," 這種寫法在 Nuxt 3 之後已經不是設定重新導向的方式","，Nuxt 3 用 ",[29,23850,23851],{},"routeRules"," 統一管理路由層級的設定（不只重新導向，快取策略、渲染模式也在同一個地方設定）。改設定檔要重啟這件事本身也緩解了不少——Nuxt 3 的開發伺服器偵測到 ",[29,23854,23855],{},"nuxt.config.ts"," 變動時會",[12249,23858,23859],{},"自動","重啟，不需要自己手動中斷再重開。整理在《Nuxt 3 的 routeRules：重新導向與路由設定統一入口》。",[15,23862,4536],{"id":4536},[4538,23864,23865],{},[4541,23866,23867],{},[412,23868,23871],{"href":23869,"rel":23870},"https:\u002F\u002Fv2.nuxt.com\u002Fdocs\u002Fconfiguration-glossary\u002Fconfiguration-router",[416],"Nuxt 2：router 設定（封存版文件）",[368,23873,23874],{},"html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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 .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}",{"title":27,"searchDepth":51,"depth":51,"links":23876},[23877,23878,23879,23880],{"id":23582,"depth":45,"text":23583},{"id":23686,"depth":45,"text":23687},{"id":17846,"depth":45,"text":17846},{"id":4536,"depth":45,"text":4536},"記錄在 nuxt.config.js 用 router.extendRoutes 新增重新導向規則的做法，缺點是改了設定要重啟開發伺服器才會生效，另外也記錄透過 middleware 檔案做重新導向的替代方式。Nuxt 3 之後改用 routeRules 統一設定，寫法更簡潔也不用重啟。",{},"\u002Fposts\u002Fnuxt-config-redirect","2020-08-30",[],{"title":23574,"description":23881},"posts\u002F20200830.nuxt-config-redirect",[15981,23889],"重新導向","Qui0fzQ67lmBWKRC2emAZSqFUwR14W573VhYGuBevgQ",{"id":23892,"title":23893,"body":23894,"category":378,"description":24065,"draft":380,"extension":381,"image":382,"meta":24066,"navigation":102,"path":24067,"publishedAt":24068,"relatedExperiments":24069,"seo":24070,"stem":24071,"tags":24072,"updatedAt":14433,"__hash__":24074},"posts\u002Fposts\u002F20200901.nuxt-cors-proxy-module.md","Nuxt 2 解決 CORS：用 @nuxtjs\u002Fproxy 把 Nuxt 當成代理伺服器",{"type":8,"value":23895,"toc":24058},[23896,23899,23903,23909,23913,23919,23921,23935,23940,24016,24027,24029,24038,24040,24055],[11,23897,23898],{},"串接後端 API 時遇到 CORS 問題。",[15,23900,23902],{"id":23901},"什麼是-cors","什麼是 CORS",[11,23904,23905,23906,3775],{},"CORS（Cross-Origin Resource Sharing，跨域資源共享）是瀏覽器基於安全性考量的限制：載入其他網域的資源時，除非對方伺服器明確用回應標頭表示允許，不然瀏覽器會擋下這個請求。Firefox 跟 Chrome 對這個錯誤都有各自的提示訊息，但本質上是同一件事——",[12249,23907,23908],{},"後端沒有回傳允許跨域的標頭",[15,23910,23912],{"id":23911},"解法讓-nuxt-自己的伺服器當代理","解法：讓 Nuxt 自己的伺服器當代理",[11,23914,23915,23918],{},[12249,23916,23917],{},"瀏覽器的同源限制只針對「瀏覽器直接發出的跨網域請求」，伺服器對伺服器的請求不受這個限制。"," 解法是讓瀏覽器只跟「提供網頁的那台伺服器」（也就是 Nuxt 自己）要資料，再由 Nuxt 的伺服器去跟真正的後端 API 要資料，轉手一次繞過瀏覽器端的限制。",[15,23920,21465],{"id":21465},[22,23922,23924],{"className":2057,"code":23923,"language":2059,"meta":27,"style":27},"npm install @nuxtjs\u002Fproxy\n",[29,23925,23926],{"__ignoreMap":27},[32,23927,23928,23930,23932],{"class":34,"line":35},[32,23929,4625],{"class":2066},[32,23931,4650],{"class":175},[32,23933,23934],{"class":175}," @nuxtjs\u002Fproxy\n",[11,23936,23937,23939],{},[29,23938,21484],{}," 設定：",[22,23941,23943],{"className":24,"code":23942,"language":26,"meta":27,"style":27},"export default {\n  modules: ['@nuxtjs\u002Fproxy'],\n  proxy: {\n    '\u002Fapi\u002F': {\n      target: 'https:\u002F\u002Fbackend.example.com',\n      pathRewrite: { '^\u002Fapi\u002F': '' },\n    },\n  },\n};\n",[29,23944,23945,23953,23964,23969,23976,23986,24000,24004,24010],{"__ignoreMap":27},[32,23946,23947,23949,23951],{"class":34,"line":35},[32,23948,5038],{"class":38},[32,23950,5041],{"class":38},[32,23952,703],{"class":5049},[32,23954,23955,23957,23960,23962],{"class":34,"line":45},[32,23956,21503],{"class":41},[32,23958,23959],{"class":175},"'@nuxtjs\u002Fproxy'",[32,23961,21509],{"class":41},[32,23963,547],{"class":5049},[32,23965,23966],{"class":34,"line":51},[32,23967,23968],{"class":41},"  proxy: {\n",[32,23970,23971,23974],{"class":34,"line":90},[32,23972,23973],{"class":175},"    '\u002Fapi\u002F'",[32,23975,5261],{"class":41},[32,23977,23978,23981,23984],{"class":34,"line":99},[32,23979,23980],{"class":41},"      target: ",[32,23982,23983],{"class":175},"'https:\u002F\u002Fbackend.example.com'",[32,23985,547],{"class":41},[32,23987,23988,23991,23994,23996,23998],{"class":34,"line":106},[32,23989,23990],{"class":41},"      pathRewrite: { ",[32,23992,23993],{"class":175},"'^\u002Fapi\u002F'",[32,23995,5269],{"class":41},[32,23997,4197],{"class":175},[32,23999,16024],{"class":41},[32,24001,24002],{"class":34,"line":114},[32,24003,4204],{"class":41},[32,24005,24006,24008],{"class":34,"line":120},[32,24007,9758],{"class":41},[32,24009,547],{"class":5049},[32,24011,24012,24014],{"class":34,"line":127},[32,24013,9765],{"class":5049},[32,24015,96],{"class":41},[11,24017,24018,24019,24022,24023,24026],{},"設定完成後，前端請求打 ",[29,24020,24021],{},"\u002Fapi\u002Fposts","，實際上會被 Nuxt 的伺服器轉發到 ",[29,24024,24025],{},"https:\u002F\u002Fbackend.example.com\u002Fposts","，瀏覽器全程只跟同一個網域（Nuxt 自己）溝通，不會觸發 CORS 檢查。",[15,24028,17846],{"id":17846},[11,24030,24031,24037],{},[12249,24032,24033,24036],{},[29,24034,24035],{},"@nuxtjs\u002Fproxy"," 是 Nuxt 2 專屬的模組","，Nuxt 3 之後的架構（Nitro）本身就是一個功能完整的伺服器，不需要額外裝模組來做代理，直接用 Nitro 的 server routes 就能達成同樣的效果，而且能力更完整（能順便做金鑰隱藏、回應快取這些事）。整理在《Nuxt 3 用 server routes 做代理與轉發》。",[15,24039,4536],{"id":4536},[4538,24041,24042,24049],{},[4541,24043,24044],{},[412,24045,24048],{"href":24046,"rel":24047},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fzh-TW\u002Fdocs\u002FWeb\u002FHTTP\u002FCORS",[416],"MDN：CORS",[4541,24050,24051],{},[412,24052,24035],{"href":24053,"rel":24054},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002F@nuxtjs\u002Fproxy",[416],[368,24056,24057],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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 .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}",{"title":27,"searchDepth":51,"depth":51,"links":24059},[24060,24061,24062,24063,24064],{"id":23901,"depth":45,"text":23902},{"id":23911,"depth":45,"text":23912},{"id":21465,"depth":45,"text":21465},{"id":17846,"depth":45,"text":17846},{"id":4536,"depth":45,"text":4536},"記錄後端沒有提供 CORS 標頭時，串接 API 遇到跨域錯誤的解法——安裝 @nuxtjs\u002Fproxy，讓瀏覽器改向 Nuxt 自己的 web server 發請求，再由 Nuxt 伺服器去跟真正的後端要資料，繞開瀏覽器的同源限制。",{},"\u002Fposts\u002Fnuxt-cors-proxy-module","2020-09-01",[],{"title":23893,"description":24065},"posts\u002F20200901.nuxt-cors-proxy-module",[15981,24073],"CORS","oVf_3fd1z0WypoPTKRGFHY9M3vjL37TJyuV88_ZYXPE",{"id":24076,"title":24077,"body":24078,"category":378,"description":24943,"draft":380,"extension":381,"image":382,"meta":24944,"navigation":102,"path":24945,"publishedAt":24946,"relatedExperiments":24947,"seo":24948,"stem":24949,"tags":24950,"updatedAt":14433,"__hash__":24951},"posts\u002Fposts\u002F20200908.vee-validate-v3-setup.md","vee-validate 表單驗證安裝紀錄（v3，Options API）",{"type":8,"value":24079,"toc":24935},[24080,24083,24085,24101,24106,24301,24306,24343,24347,24406,24494,24508,24512,24639,24797,24815,24819,24822,24828,24846,24852,24892,24902,24904,24921,24923,24932],[11,24081,24082],{},"調整表單驗證，用了 vee-validate 這套套件，踩了不少坑，整理一次完整流程。",[15,24084,21465],{"id":21465},[22,24086,24088],{"className":2057,"code":24087,"language":2059,"meta":27,"style":27},"npm install vee-validate --save\n",[29,24089,24090],{"__ignoreMap":27},[32,24091,24092,24094,24096,24099],{"class":34,"line":35},[32,24093,4625],{"class":2066},[32,24095,4650],{"class":175},[32,24097,24098],{"class":175}," vee-validate",[32,24100,8638],{"class":351},[11,24102,24103,5240],{},[29,24104,24105],{},"plugins\u002Fvee-validate.js",[22,24107,24109],{"className":24,"code":24108,"language":26,"meta":27,"style":27},"import Vue from 'vue';\nimport { ValidationObserver, ValidationProvider, extend, localize } from 'vee-validate';\nimport * as rules from 'vee-validate\u002Fdist\u002Frules';\nimport zhTW from 'vee-validate\u002Fdist\u002Flocale\u002Fzh_TW.json';\nimport { required, email } from 'vee-validate\u002Fdist\u002Frules'; \u002F\u002F 按照需求導入規則\n\nObject.keys(rules).forEach((rule) => {\n  extend(rule, rules[rule]);\n});\n\nextend('required', required);\nextend('email', email);\n\nlocalize('zh_TW', zhTW);\n\nVue.component('ValidationObserver', ValidationObserver);\nVue.component('ValidationProvider', ValidationProvider);\n",[29,24110,24111,24123,24136,24155,24169,24185,24189,24213,24221,24225,24229,24242,24254,24258,24269,24273,24287],{"__ignoreMap":27},[32,24112,24113,24115,24117,24119,24121],{"class":34,"line":35},[32,24114,8794],{"class":38},[32,24116,8847],{"class":41},[32,24118,8850],{"class":38},[32,24120,8853],{"class":175},[32,24122,96],{"class":41},[32,24124,24125,24127,24130,24132,24134],{"class":34,"line":45},[32,24126,8794],{"class":38},[32,24128,24129],{"class":41}," { ValidationObserver, ValidationProvider, extend, localize } ",[32,24131,8850],{"class":38},[32,24133,9814],{"class":175},[32,24135,96],{"class":41},[32,24137,24138,24140,24142,24145,24148,24150,24153],{"class":34,"line":51},[32,24139,8794],{"class":38},[32,24141,2341],{"class":351},[32,24143,24144],{"class":38}," as",[32,24146,24147],{"class":41}," rules ",[32,24149,8850],{"class":38},[32,24151,24152],{"class":175}," 'vee-validate\u002Fdist\u002Frules'",[32,24154,96],{"class":41},[32,24156,24157,24159,24162,24164,24167],{"class":34,"line":90},[32,24158,8794],{"class":38},[32,24160,24161],{"class":41}," zhTW ",[32,24163,8850],{"class":38},[32,24165,24166],{"class":175}," 'vee-validate\u002Fdist\u002Flocale\u002Fzh_TW.json'",[32,24168,96],{"class":41},[32,24170,24171,24173,24176,24178,24180,24182],{"class":34,"line":99},[32,24172,8794],{"class":38},[32,24174,24175],{"class":41}," { required, email } ",[32,24177,8850],{"class":38},[32,24179,24152],{"class":175},[32,24181,23346],{"class":41},[32,24183,24184],{"class":608},"\u002F\u002F 按照需求導入規則\n",[32,24186,24187],{"class":34,"line":106},[32,24188,103],{"emptyLinePlaceholder":102},[32,24190,24191,24194,24197,24200,24202,24204,24207,24209,24211],{"class":34,"line":114},[32,24192,24193],{"class":41},"Object.",[32,24195,24196],{"class":217},"keys",[32,24198,24199],{"class":41},"(rules).",[32,24201,8290],{"class":217},[32,24203,8293],{"class":41},[32,24205,24206],{"class":903},"rule",[32,24208,483],{"class":41},[32,24210,5090],{"class":38},[32,24212,703],{"class":41},[32,24214,24215,24218],{"class":34,"line":120},[32,24216,24217],{"class":217},"  extend",[32,24219,24220],{"class":41},"(rule, rules[rule]);\n",[32,24222,24223],{"class":34,"line":127},[32,24224,792],{"class":41},[32,24226,24227],{"class":34,"line":132},[32,24228,103],{"emptyLinePlaceholder":102},[32,24230,24231,24234,24236,24239],{"class":34,"line":141},[32,24232,24233],{"class":217},"extend",[32,24235,477],{"class":41},[32,24237,24238],{"class":175},"'required'",[32,24240,24241],{"class":41},", required);\n",[32,24243,24244,24246,24248,24251],{"class":34,"line":147},[32,24245,24233],{"class":217},[32,24247,477],{"class":41},[32,24249,24250],{"class":175},"'email'",[32,24252,24253],{"class":41},", email);\n",[32,24255,24256],{"class":34,"line":275},[32,24257,103],{"emptyLinePlaceholder":102},[32,24259,24260,24262,24264,24266],{"class":34,"line":282},[32,24261,10060],{"class":217},[32,24263,477],{"class":41},[32,24265,10065],{"class":175},[32,24267,24268],{"class":41},", zhTW);\n",[32,24270,24271],{"class":34,"line":295},[32,24272,103],{"emptyLinePlaceholder":102},[32,24274,24275,24277,24279,24281,24284],{"class":34,"line":605},[32,24276,8892],{"class":41},[32,24278,9603],{"class":217},[32,24280,477],{"class":41},[32,24282,24283],{"class":175},"'ValidationObserver'",[32,24285,24286],{"class":41},", ValidationObserver);\n",[32,24288,24289,24291,24293,24295,24298],{"class":34,"line":612},[32,24290,8892],{"class":41},[32,24292,9603],{"class":217},[32,24294,477],{"class":41},[32,24296,24297],{"class":175},"'ValidationProvider'",[32,24299,24300],{"class":41},", ValidationProvider);\n",[11,24302,24303,24305],{},[29,24304,21484],{}," 註冊這支 plugin：",[22,24307,24309],{"className":24,"code":24308,"language":26,"meta":27,"style":27},"export default {\n  plugins: [{ src: '~plugins\u002Fvee-validate', ssr: true }],\n};\n",[29,24310,24311,24319,24337],{"__ignoreMap":27},[32,24312,24313,24315,24317],{"class":34,"line":35},[32,24314,5038],{"class":38},[32,24316,5041],{"class":38},[32,24318,703],{"class":5049},[32,24320,24321,24324,24327,24330,24332,24335],{"class":34,"line":45},[32,24322,24323],{"class":41},"  plugins: [{ src: ",[32,24325,24326],{"class":175},"'~plugins\u002Fvee-validate'",[32,24328,24329],{"class":41},", ssr: ",[32,24331,6206],{"class":351},[32,24333,24334],{"class":41}," }]",[32,24336,547],{"class":5049},[32,24338,24339,24341],{"class":34,"line":51},[32,24340,9765],{"class":5049},[32,24342,96],{"class":41},[15,24344,24346],{"id":24345},"單一欄位驗證validationprovider","單一欄位驗證：ValidationProvider",[22,24348,24350],{"className":8665,"code":24349,"language":4112,"meta":27,"style":27},"\u003Cscript>\nimport { ValidationProvider, ValidationObserver } from 'vee-validate';\n\nexport default {\n  components: { ValidationProvider, ValidationObserver },\n};\n\u003C\u002Fscript>\n",[29,24351,24352,24360,24373,24377,24385,24392,24398],{"__ignoreMap":27},[32,24353,24354,24356,24358],{"class":34,"line":35},[32,24355,4046],{"class":41},[32,24357,4125],{"class":4049},[32,24359,4062],{"class":41},[32,24361,24362,24364,24367,24369,24371],{"class":34,"line":45},[32,24363,8794],{"class":38},[32,24365,24366],{"class":41}," { ValidationProvider, ValidationObserver } ",[32,24368,8850],{"class":38},[32,24370,9814],{"class":175},[32,24372,96],{"class":41},[32,24374,24375],{"class":34,"line":51},[32,24376,103],{"emptyLinePlaceholder":102},[32,24378,24379,24381,24383],{"class":34,"line":90},[32,24380,5038],{"class":38},[32,24382,5041],{"class":38},[32,24384,703],{"class":5049},[32,24386,24387,24390],{"class":34,"line":99},[32,24388,24389],{"class":41},"  components: { ValidationProvider, ValidationObserver }",[32,24391,547],{"class":5049},[32,24393,24394,24396],{"class":34,"line":106},[32,24395,9765],{"class":5049},[32,24397,96],{"class":41},[32,24399,24400,24402,24404],{"class":34,"line":114},[32,24401,4105],{"class":41},[32,24403,4125],{"class":4049},[32,24405,4062],{"class":41},[22,24407,24409],{"className":4038,"code":24408,"language":4034,"meta":27,"style":27},"\u003CValidationProvider rules=\"required|email\" v-slot=\"{ errors }\">\n  \u003Cinput type=\"email\" v-model=\"form.user.email\" placeholder=\"請輸入 Email\">\n  \u003Cspan class=\"text-danger\">{{ errors[0] }}\u003C\u002Fspan>\n\u003C\u002FValidationProvider>\n",[29,24410,24411,24437,24467,24486],{"__ignoreMap":27},[32,24412,24413,24415,24419,24422,24424,24427,24430,24432,24435],{"class":34,"line":35},[32,24414,4046],{"class":41},[32,24416,24418],{"class":24417},"sAjPK","ValidationProvider",[32,24420,24421],{"class":4053}," rules",[32,24423,172],{"class":41},[32,24425,24426],{"class":175},"\"required|email\"",[32,24428,24429],{"class":4053}," v-slot",[32,24431,172],{"class":41},[32,24433,24434],{"class":175},"\"{ errors }\"",[32,24436,4062],{"class":41},[32,24438,24439,24441,24443,24445,24447,24450,24452,24454,24457,24460,24462,24465],{"class":34,"line":45},[32,24440,4067],{"class":41},[32,24442,4070],{"class":4049},[32,24444,4073],{"class":4053},[32,24446,172],{"class":41},[32,24448,24449],{"class":175},"\"email\"",[32,24451,4081],{"class":4053},[32,24453,172],{"class":41},[32,24455,24456],{"class":175},"\"form.user.email\"",[32,24458,24459],{"class":4053}," placeholder",[32,24461,172],{"class":41},[32,24463,24464],{"class":175},"\"請輸入 Email\"",[32,24466,4062],{"class":41},[32,24468,24469,24471,24473,24475,24477,24479,24482,24484],{"class":34,"line":51},[32,24470,4067],{"class":41},[32,24472,32],{"class":4049},[32,24474,9897],{"class":4053},[32,24476,172],{"class":41},[32,24478,9902],{"class":175},[32,24480,24481],{"class":41},">{{ errors[0] }}\u003C\u002F",[32,24483,32],{"class":4049},[32,24485,4062],{"class":41},[32,24487,24488,24490,24492],{"class":34,"line":90},[32,24489,4105],{"class":41},[32,24491,24418],{"class":24417},[32,24493,4062],{"class":41},[11,24495,24496,24499,24500,24503,24504,24507],{},[29,24497,24498],{},"rules=\"required|email\""," 用直式分隔多個規則，",[29,24501,24502],{},"v-slot=\"{ errors }\""," 拿到這個欄位目前的錯誤訊息陣列，沒有錯誤時 ",[29,24505,24506],{},"errors"," 是空陣列。",[15,24509,24511],{"id":24510},"整份表單驗證validationobserver","整份表單驗證：ValidationObserver",[22,24513,24515],{"className":4038,"code":24514,"language":4034,"meta":27,"style":27},"\u003CValidationObserver tag=\"form\" ref=\"observer\" @submit.prevent=\"createOrder\">\n  \u003Cdiv class=\"form-group\">...\u003C\u002Fdiv>\n  \u003Cdiv class=\"form-group\">...\u003C\u002Fdiv>\n  \u003Cdiv class=\"text-right\">\n    \u003Cbutton type=\"submit\">送出\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002FValidationObserver>\n",[29,24516,24517,24550,24570,24588,24603,24623,24631],{"__ignoreMap":27},[32,24518,24519,24521,24524,24527,24529,24532,24535,24537,24540,24543,24545,24548],{"class":34,"line":35},[32,24520,4046],{"class":41},[32,24522,24523],{"class":24417},"ValidationObserver",[32,24525,24526],{"class":4053}," tag",[32,24528,172],{"class":41},[32,24530,24531],{"class":175},"\"form\"",[32,24533,24534],{"class":4053}," ref",[32,24536,172],{"class":41},[32,24538,24539],{"class":175},"\"observer\"",[32,24541,24542],{"class":4053}," @submit.prevent",[32,24544,172],{"class":41},[32,24546,24547],{"class":175},"\"createOrder\"",[32,24549,4062],{"class":41},[32,24551,24552,24554,24556,24558,24560,24563,24566,24568],{"class":34,"line":45},[32,24553,4067],{"class":41},[32,24555,4050],{"class":4049},[32,24557,9897],{"class":4053},[32,24559,172],{"class":41},[32,24561,24562],{"class":175},"\"form-group\"",[32,24564,24565],{"class":41},">...\u003C\u002F",[32,24567,4050],{"class":4049},[32,24569,4062],{"class":41},[32,24571,24572,24574,24576,24578,24580,24582,24584,24586],{"class":34,"line":51},[32,24573,4067],{"class":41},[32,24575,4050],{"class":4049},[32,24577,9897],{"class":4053},[32,24579,172],{"class":41},[32,24581,24562],{"class":175},[32,24583,24565],{"class":41},[32,24585,4050],{"class":4049},[32,24587,4062],{"class":41},[32,24589,24590,24592,24594,24596,24598,24601],{"class":34,"line":90},[32,24591,4067],{"class":41},[32,24593,4050],{"class":4049},[32,24595,9897],{"class":4053},[32,24597,172],{"class":41},[32,24599,24600],{"class":175},"\"text-right\"",[32,24602,4062],{"class":41},[32,24604,24605,24607,24609,24611,24613,24616,24619,24621],{"class":34,"line":99},[32,24606,9638],{"class":41},[32,24608,11493],{"class":4049},[32,24610,4073],{"class":4053},[32,24612,172],{"class":41},[32,24614,24615],{"class":175},"\"submit\"",[32,24617,24618],{"class":41},">送出\u003C\u002F",[32,24620,11493],{"class":4049},[32,24622,4062],{"class":41},[32,24624,24625,24627,24629],{"class":34,"line":106},[32,24626,9691],{"class":41},[32,24628,4050],{"class":4049},[32,24630,4062],{"class":41},[32,24632,24633,24635,24637],{"class":34,"line":114},[32,24634,4105],{"class":41},[32,24636,24523],{"class":24417},[32,24638,4062],{"class":41},[22,24640,24642],{"className":24,"code":24641,"language":26,"meta":27,"style":27},"methods: {\n  createOrder() {\n    this.$refs.observer.validate().then((result) => {\n      if (result) {\n        this.$axios.$post(url, { data: order }).then((res) => {\n          if (res.success) {\n            this.$router.push(`\u002FcustomerCheckOut\u002F${res.data.orderId}`);\n          }\n        });\n      } else {\n        console.log('訂單未完成');\n      }\n    });\n  },\n}\n",[29,24643,24644,24650,24657,24680,24686,24711,24719,24750,24755,24759,24767,24781,24785,24789,24793],{"__ignoreMap":27},[32,24645,24646,24648],{"class":34,"line":35},[32,24647,18395],{"class":2066},[32,24649,5261],{"class":41},[32,24651,24652,24655],{"class":34,"line":45},[32,24653,24654],{"class":217},"  createOrder",[32,24656,4221],{"class":41},[32,24658,24659,24661,24664,24666,24668,24670,24672,24674,24676,24678],{"class":34,"line":51},[32,24660,10114],{"class":351},[32,24662,24663],{"class":41},".$refs.observer.",[32,24665,10086],{"class":217},[32,24667,10089],{"class":41},[32,24669,8950],{"class":217},[32,24671,8293],{"class":41},[32,24673,10096],{"class":903},[32,24675,483],{"class":41},[32,24677,5090],{"class":38},[32,24679,703],{"class":41},[32,24681,24682,24684],{"class":34,"line":90},[32,24683,1071],{"class":38},[32,24685,10109],{"class":41},[32,24687,24688,24691,24694,24697,24699,24701,24703,24705,24707,24709],{"class":34,"line":99},[32,24689,24690],{"class":351},"        this",[32,24692,24693],{"class":41},".$axios.",[32,24695,24696],{"class":217},"$post",[32,24698,10121],{"class":41},[32,24700,8950],{"class":217},[32,24702,8293],{"class":41},[32,24704,10128],{"class":903},[32,24706,483],{"class":41},[32,24708,5090],{"class":38},[32,24710,703],{"class":41},[32,24712,24713,24716],{"class":34,"line":106},[32,24714,24715],{"class":38},"          if",[32,24717,24718],{"class":41}," (res.success) {\n",[32,24720,24721,24724,24727,24729,24731,24734,24736,24738,24741,24743,24746,24748],{"class":34,"line":114},[32,24722,24723],{"class":351},"            this",[32,24725,24726],{"class":41},".$router.",[32,24728,23621],{"class":217},[32,24730,477],{"class":41},[32,24732,24733],{"class":175},"`\u002FcustomerCheckOut\u002F${",[32,24735,10128],{"class":41},[32,24737,1278],{"class":175},[32,24739,24740],{"class":41},"data",[32,24742,1278],{"class":175},[32,24744,24745],{"class":41},"orderId",[32,24747,8325],{"class":175},[32,24749,230],{"class":41},[32,24751,24752],{"class":34,"line":120},[32,24753,24754],{"class":41},"          }\n",[32,24756,24757],{"class":34,"line":127},[32,24758,1188],{"class":41},[32,24760,24761,24763,24765],{"class":34,"line":132},[32,24762,9477],{"class":41},[32,24764,565],{"class":38},[32,24766,703],{"class":41},[32,24768,24769,24772,24774,24776,24779],{"class":34,"line":141},[32,24770,24771],{"class":41},"        console.",[32,24773,5197],{"class":217},[32,24775,477],{"class":41},[32,24777,24778],{"class":175},"'訂單未完成'",[32,24780,230],{"class":41},[32,24782,24783],{"class":34,"line":147},[32,24784,1193],{"class":41},[32,24786,24787],{"class":34,"line":275},[32,24788,1198],{"class":41},[32,24790,24791],{"class":34,"line":282},[32,24792,5639],{"class":41},[32,24794,24795],{"class":34,"line":295},[32,24796,54],{"class":41},[11,24798,24799,24802,24803,24805,24806,24808,24809,24811,24812,24814],{},[29,24800,24801],{},"this.$refs.observer.validate()"," 會一次驗證這個 ",[29,24804,24523],{}," 底下所有 ",[29,24807,24418],{},"，回傳一個 Promise，結果是布林值——全部通過才是 ",[29,24810,6206],{},"，任何一個欄位沒過就是 ",[29,24813,9746],{},"，這時候各欄位自己的錯誤訊息也會自動顯示出來，不需要自己手動去組合錯誤清單。",[15,24816,24818],{"id":24817},"踩到的坑unexpected-token-export","踩到的坑：Unexpected token export",[11,24820,24821],{},"安裝規則檔時撞到：",[22,24823,24826],{"className":24824,"code":24825,"language":426,"meta":27},[424],"Unexpected token export\n",[29,24827,24825],{"__ignoreMap":27},[11,24829,24830,24831,24834,24835,24837,24838,24845],{},"原因是 ",[29,24832,24833],{},"vee-validate\u002Fdist\u002Frules"," 這個檔案是用 ES Module 格式（",[29,24836,5038],{},"）寫的，而 Nuxt 預設",[12249,24839,24840,24841,24844],{},"不會轉譯 ",[29,24842,24843],{},"node_modules"," 底下的檔案","（避免每個第三方套件都要重新編譯一次，拖慢建置速度），但這個規則檔沒有先被轉譯成瀏覽器看得懂的格式，就直接被拿去執行，才會噴出這個錯誤。",[11,24847,24848,24849,24851],{},"修法是在 ",[29,24850,21484],{}," 明確把這個路徑加進轉譯清單的例外：",[22,24853,24855],{"className":24,"code":24854,"language":26,"meta":27,"style":27},"export default {\n  build: {\n    transpile: ['vee-validate\u002Fdist\u002Frules'],\n  },\n};\n",[29,24856,24857,24865,24870,24880,24886],{"__ignoreMap":27},[32,24858,24859,24861,24863],{"class":34,"line":35},[32,24860,5038],{"class":38},[32,24862,5041],{"class":38},[32,24864,703],{"class":5049},[32,24866,24867],{"class":34,"line":45},[32,24868,24869],{"class":41},"  build: {\n",[32,24871,24872,24875,24878],{"class":34,"line":51},[32,24873,24874],{"class":41},"    transpile: [",[32,24876,24877],{"class":175},"'vee-validate\u002Fdist\u002Frules'",[32,24879,8212],{"class":41},[32,24881,24882,24884],{"class":34,"line":90},[32,24883,9758],{"class":41},[32,24885,547],{"class":5049},[32,24887,24888,24890],{"class":34,"line":99},[32,24889,9765],{"class":5049},[32,24891,96],{"class":41},[11,24893,24894,24895,24897,24898,24901],{},"這是官方文件裡明確寫的解法——",[29,24896,24843],{}," 底下的東西預設不轉譯，遇到少數幾個「明明是第三方套件但沒有先編譯好」的情況，就要手動加進 ",[29,24899,24900],{},"transpile"," 清單。",[15,24903,17846],{"id":17846},[11,24905,24906,24913,24914,11621,24917,24920],{},[12249,24907,24908,11621,24910,24912],{},[29,24909,24523],{},[29,24911,24418],{}," 這套元件式的寫法屬於 vee-validate 3.x（v2 的用法也類似）","，vee-validate 4 已經全面改成 Composition API 風格，",[29,24915,24916],{},"useField",[29,24918,24919],{},"useForm"," 取代了整組元件，整理在《vee-validate 4：改用 Composition API》。",[15,24922,4536],{"id":4536},[4538,24924,24925],{},[4541,24926,24927],{},[412,24928,24931],{"href":24929,"rel":24930},"https:\u002F\u002Fvee-validate.logaretm.com\u002Fv3\u002Fguide\u002Fcomponents\u002Fvalidation-observer.html",[416],"vee-validate 3：ValidationObserver（封存版文件）",[368,24933,24934],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}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 .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .sAjPK, html code.shiki .sAjPK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FFA198;--shiki-dark-font-style:italic}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}",{"title":27,"searchDepth":51,"depth":51,"links":24936},[24937,24938,24939,24940,24941,24942],{"id":21465,"depth":45,"text":21465},{"id":24345,"depth":45,"text":24346},{"id":24510,"depth":45,"text":24511},{"id":24817,"depth":45,"text":24818},{"id":17846,"depth":45,"text":17846},{"id":4536,"depth":45,"text":4536},"記錄在 Nuxt 2 安裝 vee-validate 3.x 的完整過程——全域註冊規則與中文語系、用 ValidationProvider 包裹單一欄位、用 ValidationObserver 統一驗證整份表單再送出，以及安裝規則檔時會撞到的 Unexpected token export 錯誤跟修法。vee-validate 4 已經全面改成 Composition API，整理在新的一篇。",{},"\u002Fposts\u002Fvee-validate-v3-setup","2020-09-08",[],{"title":24077,"description":24943},"posts\u002F20200908.vee-validate-v3-setup",[9778,4113,4572],"8E80c_h-9K4aHB9h3b6CMyexhDwF2gQ3Mk0mfL0GTek",{"id":24953,"title":24954,"body":24955,"category":378,"description":25336,"draft":380,"extension":381,"image":382,"meta":25337,"navigation":102,"path":25338,"publishedAt":25339,"relatedExperiments":25340,"seo":25341,"stem":25342,"tags":25343,"updatedAt":14433,"__hash__":25346},"posts\u002Fposts\u002F20200910.moment-vue-filter.md","用 moment.js 搭配 Vue 2 全域 filter 做日期格式化",{"type":8,"value":24956,"toc":25329},[24957,24960,24962,24976,24980,25181,25185,25219,25221,25228,25246,25254,25286,25288,25303,25312,25315,25317,25326],[11,24958,24959],{},"日期格式要在很多地方重複用到，整理成 Vue 2 的全域 filter，模板裡用管線符號直接套用。",[15,24961,21465],{"id":21465},[22,24963,24965],{"className":2057,"code":24964,"language":2059,"meta":27,"style":27},"npm install moment\n",[29,24966,24967],{"__ignoreMap":27},[32,24968,24969,24971,24973],{"class":34,"line":35},[32,24970,4625],{"class":2066},[32,24972,4650],{"class":175},[32,24974,24975],{"class":175}," moment\n",[15,24977,24979],{"id":24978},"註冊成全域-filter","註冊成全域 filter",[22,24981,24983],{"className":24,"code":24982,"language":26,"meta":27,"style":27},"\u002F\u002F plugins\u002Ffilter.js\nimport Vue from 'vue';\nimport moment from 'moment';\n\nVue.filter('dateFormat', (value, format = 'YYYY-MM-DD') => {\n  const datetime = moment(value);\n  return datetime.format(format);\n});\n\nVue.filter('dateFormatFromUTC', (value, format = 'YYYY-MM-DD') => {\n  const utc = moment.tz(value, 'UTC');\n  const timezone = moment.tz.guess();\n  return utc.clone().tz(timezone).format(format);\n});\n",[29,24984,24985,24990,25002,25016,25020,25052,25067,25079,25083,25087,25116,25139,25156,25177],{"__ignoreMap":27},[32,24986,24987],{"class":34,"line":35},[32,24988,24989],{"class":608},"\u002F\u002F plugins\u002Ffilter.js\n",[32,24991,24992,24994,24996,24998,25000],{"class":34,"line":45},[32,24993,8794],{"class":38},[32,24995,8847],{"class":41},[32,24997,8850],{"class":38},[32,24999,8853],{"class":175},[32,25001,96],{"class":41},[32,25003,25004,25006,25009,25011,25014],{"class":34,"line":51},[32,25005,8794],{"class":38},[32,25007,25008],{"class":41}," moment ",[32,25010,8850],{"class":38},[32,25012,25013],{"class":175}," 'moment'",[32,25015,96],{"class":41},[32,25017,25018],{"class":34,"line":90},[32,25019,103],{"emptyLinePlaceholder":102},[32,25021,25022,25024,25026,25028,25031,25034,25036,25038,25041,25043,25046,25048,25050],{"class":34,"line":99},[32,25023,8892],{"class":41},[32,25025,16124],{"class":217},[32,25027,477],{"class":41},[32,25029,25030],{"class":175},"'dateFormat'",[32,25032,25033],{"class":41},", (",[32,25035,17664],{"class":903},[32,25037,635],{"class":41},[32,25039,25040],{"class":903},"format",[32,25042,894],{"class":38},[32,25044,25045],{"class":175}," 'YYYY-MM-DD'",[32,25047,483],{"class":41},[32,25049,5090],{"class":38},[32,25051,703],{"class":41},[32,25053,25054,25056,25059,25061,25064],{"class":34,"line":106},[32,25055,4157],{"class":38},[32,25057,25058],{"class":351}," datetime",[32,25060,894],{"class":38},[32,25062,25063],{"class":217}," moment",[32,25065,25066],{"class":41},"(value);\n",[32,25068,25069,25071,25074,25076],{"class":34,"line":114},[32,25070,10974],{"class":38},[32,25072,25073],{"class":41}," datetime.",[32,25075,25040],{"class":217},[32,25077,25078],{"class":41},"(format);\n",[32,25080,25081],{"class":34,"line":120},[32,25082,792],{"class":41},[32,25084,25085],{"class":34,"line":127},[32,25086,103],{"emptyLinePlaceholder":102},[32,25088,25089,25091,25093,25095,25098,25100,25102,25104,25106,25108,25110,25112,25114],{"class":34,"line":132},[32,25090,8892],{"class":41},[32,25092,16124],{"class":217},[32,25094,477],{"class":41},[32,25096,25097],{"class":175},"'dateFormatFromUTC'",[32,25099,25033],{"class":41},[32,25101,17664],{"class":903},[32,25103,635],{"class":41},[32,25105,25040],{"class":903},[32,25107,894],{"class":38},[32,25109,25045],{"class":175},[32,25111,483],{"class":41},[32,25113,5090],{"class":38},[32,25115,703],{"class":41},[32,25117,25118,25120,25123,25125,25128,25131,25134,25137],{"class":34,"line":141},[32,25119,4157],{"class":38},[32,25121,25122],{"class":351}," utc",[32,25124,894],{"class":38},[32,25126,25127],{"class":41}," moment.",[32,25129,25130],{"class":217},"tz",[32,25132,25133],{"class":41},"(value, ",[32,25135,25136],{"class":175},"'UTC'",[32,25138,230],{"class":41},[32,25140,25141,25143,25146,25148,25151,25154],{"class":34,"line":147},[32,25142,4157],{"class":38},[32,25144,25145],{"class":351}," timezone",[32,25147,894],{"class":38},[32,25149,25150],{"class":41}," moment.tz.",[32,25152,25153],{"class":217},"guess",[32,25155,687],{"class":41},[32,25157,25158,25160,25163,25166,25168,25170,25173,25175],{"class":34,"line":275},[32,25159,10974],{"class":38},[32,25161,25162],{"class":41}," utc.",[32,25164,25165],{"class":217},"clone",[32,25167,10089],{"class":41},[32,25169,25130],{"class":217},[32,25171,25172],{"class":41},"(timezone).",[32,25174,25040],{"class":217},[32,25176,25078],{"class":41},[32,25178,25179],{"class":34,"line":282},[32,25180,792],{"class":41},[11,25182,25183,24305],{},[29,25184,21484],{},[22,25186,25188],{"className":24,"code":25187,"language":26,"meta":27,"style":27},"export default {\n  plugins: [{ src: '~plugins\u002Ffilter', ssr: false }],\n};\n",[29,25189,25190,25198,25213],{"__ignoreMap":27},[32,25191,25192,25194,25196],{"class":34,"line":35},[32,25193,5038],{"class":38},[32,25195,5041],{"class":38},[32,25197,703],{"class":5049},[32,25199,25200,25202,25205,25207,25209,25211],{"class":34,"line":45},[32,25201,24323],{"class":41},[32,25203,25204],{"class":175},"'~plugins\u002Ffilter'",[32,25206,24329],{"class":41},[32,25208,9746],{"class":351},[32,25210,24334],{"class":41},[32,25212,547],{"class":5049},[32,25214,25215,25217],{"class":34,"line":51},[32,25216,9765],{"class":5049},[32,25218,96],{"class":41},[15,25220,12853],{"id":12853},[11,25222,25223,25224,25227],{},"在模板裡用管線符號 ",[29,25225,25226],{},"|"," 串接 filter：",[22,25229,25231],{"className":4038,"code":25230,"language":4034,"meta":27,"style":27},"\u003Cspan>{{ data.item.paid_date | dateFormat('YYYY.MM.DD') }}\u003C\u002Fspan>\n",[29,25232,25233],{"__ignoreMap":27},[32,25234,25235,25237,25239,25242,25244],{"class":34,"line":35},[32,25236,4046],{"class":41},[32,25238,32],{"class":4049},[32,25240,25241],{"class":41},">{{ data.item.paid_date | dateFormat('YYYY.MM.DD') }}\u003C\u002F",[32,25243,32],{"class":4049},[32,25245,4062],{"class":41},[11,25247,5134,25248,25250,25251,5240],{},[29,25249,18395],{}," 裡也能直接呼叫 ",[29,25252,25253],{},"moment()",[22,25255,25257],{"className":24,"code":25256,"language":26,"meta":27,"style":27},"this.tempCoupon.due_date = moment(this.tempCoupon.due_date).format('YYYY-MM-DD');\n",[29,25258,25259],{"__ignoreMap":27},[32,25260,25261,25263,25266,25268,25270,25272,25274,25277,25279,25281,25284],{"class":34,"line":35},[32,25262,1083],{"class":351},[32,25264,25265],{"class":41},".tempCoupon.due_date ",[32,25267,172],{"class":38},[32,25269,25063],{"class":217},[32,25271,477],{"class":41},[32,25273,1083],{"class":351},[32,25275,25276],{"class":41},".tempCoupon.due_date).",[32,25278,25040],{"class":217},[32,25280,477],{"class":41},[32,25282,25283],{"class":175},"'YYYY-MM-DD'",[32,25285,230],{"class":41},[15,25287,12245],{"id":12245},[11,25289,25290,25291,25294,25295,25298,25299,25302],{},"Moment.js 的兩個設計選擇，在它剛推出的年代不是問題，規模用大之後會慢慢感覺到包袱：",[12249,25292,25293],{},"體積龐大","（連同常用的 locale 動輒 200KB 以上）；",[12249,25296,25297],{},"物件是可變的","（",[29,25300,25301],{},"moment().add(1, 'day')"," 會直接修改原本的物件，多處共用同一個 moment 實例時容易出現意外的副作用）。",[11,25304,25305,25306,25308,25309,25311],{},"Vue 的全域 filter 語法本身也有限制——",[29,25307,16124],{}," 能做的事，",[29,25310,18378],{}," 或一般方法幾乎都能做，多維護一套語意相近的機制，長期看來是重複的心智負擔。",[11,25313,25314],{},"這兩件事後來都有了新的做法，整理在《day.js 搭配 Vue 3 的日期格式化》。",[15,25316,4536],{"id":4536},[4538,25318,25319],{},[4541,25320,25321],{},[412,25322,25325],{"href":25323,"rel":25324},"https:\u002F\u002Fmomentjs.com\u002Fdocs\u002F#\u002F-project-status\u002F",[416],"Moment.js 官方文件：專案狀態",[368,25327,25328],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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 .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}",{"title":27,"searchDepth":51,"depth":51,"links":25330},[25331,25332,25333,25334,25335],{"id":21465,"depth":45,"text":21465},{"id":24978,"depth":45,"text":24979},{"id":12853,"depth":45,"text":12853},{"id":12245,"depth":45,"text":12245},{"id":4536,"depth":45,"text":4536},"記錄安裝 moment.js、註冊成 Vue 2 全域 filter，在模板裡用管線符號格式化日期的做法，包含處理 UTC 時區轉換的版本，並記下這個做法的兩個代價：套件體積與物件可變性。",{},"\u002Fposts\u002Fmoment-vue-filter","2020-09-10",[],{"title":24954,"description":25336},"posts\u002F20200910.moment-vue-filter",[4113,25344,25345],"moment.js","日期","IaQVc8gr5GmsYWRVBGk42HWSgRq5XLV1nnwHmRfHJ7E",{"id":25348,"title":25349,"body":25350,"category":378,"description":25735,"draft":380,"extension":381,"image":382,"meta":25736,"navigation":102,"path":25737,"publishedAt":25738,"relatedExperiments":25739,"seo":25740,"stem":25741,"tags":25742,"updatedAt":25744,"__hash__":25745},"posts\u002Fposts\u002F20220409.vue-cli4-project-setup.md","用 Vue CLI 4 建立 Vue 3 專案：手動選擇設定逐步紀錄",{"type":8,"value":25351,"toc":25717},[25352,25358,25361,25375,25381,25384,25388,25394,25401,25405,25411,25414,25417,25429,25432,25436,25442,25448,25452,25458,25461,25493,25504,25508,25514,25519,25523,25529,25535,25539,25545,25548,25562,25566,25572,25595,25599,25605,25611,25615,25621,25624,25628,25634,25640,25648,25662,25665,25676,25678,25715],[11,25353,25354,25355,25357],{},"之前寫過 ",[412,25356,4576],{"href":5390},"，那篇是剛裝好時的流程紀錄。這篇把互動式選單每一步在問什麼記下來——第一次建專案時很容易一路 Enter 到底，之後才發現選錯，方便下次直接照著選。",[15,25359,25360],{"id":25360},"建立專案",[22,25362,25364],{"className":2057,"code":25363,"language":2059,"meta":27,"style":27},"vue create 專案名稱\n",[29,25365,25366],{"__ignoreMap":27},[32,25367,25368,25370,25372],{"class":34,"line":35},[32,25369,4112],{"class":2066},[32,25371,4799],{"class":175},[32,25373,25374],{"class":175}," 專案名稱\n",[11,25376,25377,25380],{},[12249,25378,25379],{},"專案名稱不能有大寫字母","，這是最容易踩到的一點，後面會提到。",[15,25382,25383],{"id":25383},"逐步設定",[151,25385,25387],{"id":25386},"_1-選擇-preset","1. 選擇 preset",[11,25389,25390],{},[10742,25391],{"alt":25392,"src":25393},"Vue CLI 4.5.13 的 preset 選單，選項由上到下是自訂的「初始」預設、Default (Vue 2)、Default (Vue 3)、Manually select features","\u002Fimages\u002Fposts\u002Fvue-cli4-project-setup\u002F01.png",[11,25395,25396,25397,25400],{},"選最後一個 ",[29,25398,25399],{},"Manually select features","。前面幾個 Default 只給 Babel 與 ESLint，少了 Router 與 CSS 前處理器，還是要自己補。",[151,25402,25404],{"id":25403},"_2-勾選需要的功能","2. 勾選需要的功能",[11,25406,25407],{},[10742,25408],{"alt":25409,"src":25410},"Vue CLI 的功能勾選清單，已勾選 Choose Vue version、Babel、Router、Vuex、CSS Pre-processors、Linter \u002F Formatter，未勾選 TypeScript、PWA Support、Unit Testing、E2E Testing","\u002Fimages\u002Fposts\u002Fvue-cli4-project-setup\u002F02.png",[11,25412,25413],{},"用空白鍵勾選、Enter 確認。這次勾了 Choose Vue version、Babel、Router、Vuex、CSS Pre-processors、Linter \u002F Formatter。",[11,25415,25416],{},"兩個測試選項這次沒有勾，但值得知道差別：",[4538,25418,25419,25424],{},[4541,25420,25421,25423],{},[12249,25422,4856],{},"：單元測試，測單一函式或元件的行為。Vue CLI 會把環境配置好，選 Jest 當測試執行器即可直接開始寫。",[4541,25425,25426,25428],{},[12249,25427,4859],{},"：端對端測試，用程式驅動真實瀏覽器，模擬使用者從開啟頁面到完成操作的整個流程。Vue CLI 提供 Cypress 與 Nightwatch 兩種選擇。",[11,25430,25431],{},"兩者的差別是測試的範圍：單元測試確認「這個函式算得對不對」，E2E 確認「使用者真的點得下去、流程走得完」。",[151,25433,25435],{"id":25434},"_3-選-vue-版本","3. 選 Vue 版本",[11,25437,25438],{},[10742,25439],{"alt":25440,"src":25441},"Vue CLI 詢問要用哪個 Vue.js 版本，選項為 2.x 與 3.x，游標停在 3.x","\u002Fimages\u002Fposts\u002Fvue-cli4-project-setup\u002F03.png",[11,25443,25444,25445,3775],{},"選 ",[29,25446,25447],{},"3.x",[151,25449,25451],{"id":25450},"_4-router-要不要用-history-mode","4. Router 要不要用 history mode",[11,25453,25454],{},[10742,25455],{"alt":25456,"src":25457},"Vue CLI 詢問 Use history mode for router?，括號註明 Requires proper server setup for index fallback in production","\u002Fimages\u002Fposts\u002Fvue-cli4-project-setup\u002F04.png",[11,25459,25460],{},"這題是在問網址要長什麼樣子：",[4538,25462,25463,25479],{},[4541,25464,25465,25468,25469,25472,25473,3775,25476,25478],{},[12249,25466,25467],{},"hash mode","（選 No）：網址會帶 ",[29,25470,25471],{},"#","，像 ",[29,25474,25475],{},"example.com\u002F#\u002Fabout",[29,25477,25471],{}," 後面的部分不會送到伺服器，所以不需要任何伺服器設定，丟到靜態空間就能跑。",[4541,25480,25481,25484,25485,25488,25489,25492],{},[12249,25482,25483],{},"history mode","（選 Yes）：網址是乾淨的 ",[29,25486,25487],{},"example.com\u002Fabout","。但使用者直接開這個網址或按重新整理時，請求會真的送到伺服器，伺服器上並沒有 ",[29,25490,25491],{},"\u002Fabout"," 這個檔案，就會回 404。",[11,25494,25495,25496,25499,25500,25503],{},"所以提示才寫「Requires proper server setup for index fallback in production」——選 history mode 的話，伺服器要設定成把找不到的路徑一律回傳 ",[29,25497,25498],{},"index.html","，交給前端 router 處理。本機 ",[29,25501,25502],{},"npm run serve"," 的開發伺服器已經幫你做好這件事，所以開發時不會有感覺，部署到正式站才會踩到。",[151,25505,25507],{"id":25506},"_5-css-前處理器","5. CSS 前處理器",[11,25509,25510],{},[10742,25511],{"alt":25512,"src":25513},"Vue CLI 的 CSS 前處理器選單，選項為 Sass\u002FSCSS (with dart-sass)、Sass\u002FSCSS (with node-sass)、Less、Stylus","\u002Fimages\u002Fposts\u002Fvue-cli4-project-setup\u002F05.png",[11,25515,25444,25516,3775],{},[29,25517,25518],{},"Sass\u002FSCSS (with node-sass)",[151,25520,25522],{"id":25521},"_6-linter-formatter","6. Linter \u002F Formatter",[11,25524,25525],{},[10742,25526],{"alt":25527,"src":25528},"Vue CLI 的 linter 設定選單，選項為 ESLint with error prevention only、ESLint + Airbnb config、ESLint + Standard config、ESLint + Prettier，游標停在 Airbnb","\u002Fimages\u002Fposts\u002Fvue-cli4-project-setup\u002F06.png",[11,25530,25444,25531,25534],{},[29,25532,25533],{},"ESLint + Airbnb config","。Airbnb 的規則是四個選項裡最嚴格的，會強制縮排、引號、結尾逗號等寫法，一開始會覺得很囉唆，但團隊協作時省下很多 code review 在吵格式的時間。",[151,25536,25538],{"id":25537},"_7-什麼時候檢查程式碼","7. 什麼時候檢查程式碼",[11,25540,25541],{},[10742,25542],{"alt":25543,"src":25544},"Vue CLI 詢問額外的 lint 功能，選項為 Lint on save 與 Lint and fix on commit","\u002Fimages\u002Fposts\u002Fvue-cli4-project-setup\u002F07.png",[11,25546,25547],{},"兩個選項可以複選：",[4538,25549,25550,25556],{},[4541,25551,25552,25555],{},[12249,25553,25554],{},"Lint on save","：存檔就檢查，錯誤立刻出現在終端機與編輯器。",[4541,25557,25558,25561],{},[12249,25559,25560],{},"Lint and fix on commit","：commit 前檢查並自動修正，擋掉不符規範的程式碼進版控。",[151,25563,25565],{"id":25564},"_8-設定檔放哪裡","8. 設定檔放哪裡",[11,25567,25568],{},[10742,25569],{"alt":25570,"src":25571},"Vue CLI 詢問 Babel、ESLint 等設定要放在哪，選項為 In dedicated config files 與 In package.json","\u002Fimages\u002Fposts\u002Fvue-cli4-project-setup\u002F08.png",[11,25573,25574,25577,25578,3750,25581,25584,25585,25588,25589,25591,25592,25594],{},[29,25575,25576],{},"In dedicated config files"," 會產生 ",[29,25579,25580],{},"babel.config.js",[29,25582,25583],{},".eslintrc.js"," 等獨立檔案；",[29,25586,25587],{},"In package.json"," 則全部塞進 ",[29,25590,4945],{}," 的欄位裡。專案根目錄的檔案數量與 ",[29,25593,4945],{}," 的長度，兩者擇一，設定變多之後獨立檔案比較好找。",[151,25596,25598],{"id":25597},"_9-要不要存成-preset","9. 要不要存成 preset",[11,25600,25601],{},[10742,25602],{"alt":25603,"src":25604},"Vue CLI 詢問 Save this as a preset for future projects? (y\u002FN)","\u002Fimages\u002Fposts\u002Fvue-cli4-project-setup\u002F09.png",[11,25606,25607,25608,25610],{},"存起來的話，下次 ",[29,25609,4784],{}," 就會在第一步的清單裡看到它（也就是第 1 張圖最上面那個「初始」）。",[151,25612,25614],{"id":25613},"_10-開始建立","10. 開始建立",[11,25616,25617],{},[10742,25618],{"alt":25619,"src":25620},"Vue CLI 開始建立專案的終端機輸出，顯示 Creating project、Initializing git repository、Installing CLI plugins","\u002Fimages\u002Fposts\u002Fvue-cli4-project-setup\u002F10.png",[11,25622,25623],{},"CLI 會自動初始化 git repository 並安裝相依套件。",[15,25625,25627],{"id":25626},"遇到的問題專案名稱不能有大寫","遇到的問題：專案名稱不能有大寫",[11,25629,25630],{},[10742,25631],{"alt":25632,"src":25633},"終端機顯示錯誤訊息 Invalid project name: \"vue3-AdminLTE\"，以及警告 name can no longer contain capital letters","\u002Fimages\u002Fposts\u002Fvue-cli4-project-setup\u002F11.png",[22,25635,25638],{"className":25636,"code":25637,"language":426,"meta":27},[424],"Invalid project name: \"vue3-AdminLTE\"\nWarning: name can no longer contain capital letters\n",[29,25639,25637],{"__ignoreMap":27},[11,25641,25642,25643,7218,25645,25647],{},"原因不在 Vue CLI，而在 npm。專案名稱會直接寫進 ",[29,25644,4945],{},[29,25646,16893],{}," 欄位，而 npm 從 v5 之後就不再允許套件名稱含有大寫字母。改成全小寫加連字號即可：",[22,25649,25651],{"className":2057,"code":25650,"language":2059,"meta":27,"style":27},"vue create vue3-adminlte\n",[29,25652,25653],{"__ignoreMap":27},[32,25654,25655,25657,25659],{"class":34,"line":35},[32,25656,4112],{"class":2066},[32,25658,4799],{"class":175},[32,25660,25661],{"class":175}," vue3-adminlte\n",[15,25663,25664],{"id":25664},"後記",[11,25666,25667,25668,25671,25672,3775],{},"這篇記錄的是 2022 年的做法。現在建 Vue 專案官方已改推 Vite（",[29,25669,25670],{},"npm create vue@latest","），Vue CLI 進入維護模式，選項與流程都不一樣了。現在的做法與踩坑另外寫在 ",[412,25673,25675],{"href":25674},"\u002Fposts\u002Fvite-vue3-tailwind4-shadcn-vue","Vite + Vue 3 + Tailwind CSS 4 + shadcn-vue 安裝心得與常見踩坑",[15,25677,4536],{"id":4536},[4538,25679,25680,25687,25694,25701,25708],{},[4541,25681,25682],{},[412,25683,25686],{"href":25684,"rel":25685},"https:\u002F\u002Fcli.vuejs.org\u002Fzh\u002Fguide\u002Fcreating-a-project.html#vue-create",[416],"Vue CLI 官方文件：建立專案",[4541,25688,25689],{},[412,25690,25693],{"href":25691,"rel":25692},"https:\u002F\u002Fmedium.com\u002Fweb-design-zone\u002F%E4%BD%BF%E7%94%A8vue-cli-%E5%BB%BA%E7%AB%8Bvue-router-%E5%B0%88%E6%A1%88-f37e4b037ef0",[416],"使用 Vue Cli 建立 Vue Router 專案",[4541,25695,25696],{},[412,25697,25700],{"href":25698,"rel":25699},"https:\u002F\u002Fithelp.ithome.com.tw\u002Farticles\u002F10277447",[416],"不只懂 Vue 語法：試解釋 hash 與 history 模式的分別？為何 history 模式會回傳 404？",[4541,25702,25703],{},[412,25704,25707],{"href":25705,"rel":25706},"https:\u002F\u002Fithelp.ithome.com.tw\u002Farticles\u002F10275591",[416],"Vue 3 單元測試 (Unit Testing) - Vue Test Utils + Jest 基本介紹 & 安裝",[4541,25709,25710],{},[412,25711,25714],{"href":25712,"rel":25713},"https:\u002F\u002Fithelp.ithome.com.tw\u002Farticles\u002F10279077",[416],"Vue3 E2E Testing: Cypress 基本介紹",[368,25716,12796],{},{"title":27,"searchDepth":51,"depth":51,"links":25718},[25719,25720,25732,25733,25734],{"id":25360,"depth":45,"text":25360},{"id":25383,"depth":45,"text":25383,"children":25721},[25722,25723,25724,25725,25726,25727,25728,25729,25730,25731],{"id":25386,"depth":51,"text":25387},{"id":25403,"depth":51,"text":25404},{"id":25434,"depth":51,"text":25435},{"id":25450,"depth":51,"text":25451},{"id":25506,"depth":51,"text":25507},{"id":25521,"depth":51,"text":25522},{"id":25537,"depth":51,"text":25538},{"id":25564,"depth":51,"text":25565},{"id":25597,"depth":51,"text":25598},{"id":25613,"depth":51,"text":25614},{"id":25626,"depth":45,"text":25627},{"id":25664,"depth":45,"text":25664},{"id":4536,"depth":45,"text":4536},"用 vue create 建立 Vue 3 專案的完整流程紀錄，逐步說明手動選擇模式的每一個選項：Vue 版本、history mode、CSS 前處理器、ESLint Airbnb 設定與設定檔放置位置，以及專案名稱不能有大寫的踩坑。",{},"\u002Fposts\u002Fvue-cli4-project-setup","2022-04-09",[],{"title":25349,"description":25735},"posts\u002F20220409.vue-cli4-project-setup",[5396,25743,4853,4847],"Vue3","2026-08-09","LaKV0Rpn6UhB-_iHE9GmZKP7_TM_0Rr-mLA0iNp3JF0",{"id":25747,"title":17937,"body":25748,"category":378,"description":27974,"draft":380,"extension":381,"image":382,"meta":27975,"navigation":102,"path":17936,"publishedAt":27976,"relatedExperiments":27977,"seo":27978,"stem":27979,"tags":27980,"updatedAt":25744,"__hash__":27982},"posts\u002Fposts\u002F20240505.pinia-vuex-state-management.md",{"type":8,"value":25749,"toc":27955},[25750,25753,25756,25760,25767,25770,25805,25808,25811,25814,25837,25841,25844,25847,25873,25876,25981,25988,25990,25994,26008,26012,26102,26106,26123,26127,26161,26164,26168,26171,26524,26533,26535,26807,26811,26817,26876,26887,26912,26916,26926,27305,27313,27316,27501,27507,27511,27514,27519,27684,27689,27869,27883,27886,27889,27892,27918,27921,27923,27926,27929,27952],[11,25751,25752],{},"元件之間要共用資料時，用 props 一層一層傳很快就會受不了。狀態管理就是把這些資料抽出來放在一個共用的地方。",[11,25754,25755],{},"Vue 生態裡先有 Vuex，後來有了 Pinia。這篇整理兩者的差異，以及 Pinia 實際怎麼寫。",[15,25757,25759],{"id":25758},"vuex-概要","Vuex 概要",[11,25761,25762,25763,25766],{},"Vuex 是 Vue 官方早期提供的狀態管理庫，核心是",[12249,25764,25765],{},"集中式存儲","：整個應用共用一棵狀態樹（store），各個元件都能存取或修改同一份狀態。",[11,25768,25769],{},"四個核心概念：",[6426,25771,25772,25778,25790,25799],{},[4541,25773,25774,25777],{},[12249,25775,25776],{},"State（狀態）","：響應式的物件，放應用層級的所有資料。元件透過 getter 讀取。",[4541,25779,25780,5240,25783,25786,25787,25789],{},[12249,25781,25782],{},"Mutation（突變）",[12249,25784,25785],{},"唯一","允許修改狀態的地方，而且必須是同步的。接收 ",[29,25788,17996],{}," 當第一個參數，加上可選的 payload。",[4541,25791,25792,25795,25796,25798],{},[12249,25793,25794],{},"Action（動作）","：處理非同步操作或批次呼叫多個 mutation。接收 ",[29,25797,7449],{}," 物件。",[4541,25800,25801,25804],{},[12249,25802,25803],{},"Module（模組）","：把 store 拆成模組，各自有自己的 state、mutations、actions、getters。",[11,25806,25807],{},"「非同步的事情放 action、真正改狀態的動作放 mutation」這個設計是為了讓每一次狀態變更都可追蹤——Vue DevTools 能列出每一個 mutation，出問題時倒著看就知道狀態是被誰改的。",[151,25809,25810],{"id":25810},"遇到的瓶頸",[11,25812,25813],{},"規模上來之後，這套設計的代價開始浮現：",[4538,25815,25816,25822,25828],{},[4541,25817,25818,25821],{},[12249,25819,25820],{},"模組結構讓狀態分散","在多個模組裡，追蹤與管理變得困難。",[4541,25823,25824,25827],{},[12249,25825,25826],{},"API 冗長","。改一個值要先定義 mutation、再定義 action、再在元件裡 dispatch，中間隔了兩層。",[4541,25829,25830,3775,25833,25836],{},[12249,25831,25832],{},"TypeScript 支援不好",[29,25834,25835],{},"commit('someMutation', payload)"," 是字串，型別推不出來，打錯字要到執行期才會發現。",[15,25838,25840],{"id":25839},"pinia-概要","Pinia 概要",[11,25842,25843],{},"為了解決這些問題，Vue 團隊開發了 Pinia，用來取代 Vuex 的全域資料管理函式庫。",[11,25845,25846],{},"主要特點：",[6426,25848,25849,25855,25861,25867],{},[4541,25850,25851,25854],{},[12249,25852,25853],{},"與 Composition API 緊密整合","：可以用組合式的方式組織與重用邏輯。",[4541,25856,25857,25860],{},[12249,25858,25859],{},"扁平的 store 結構","：每個 store 都是獨立的實例，有自己的 state、getters、actions，不用巢狀模組。",[4541,25862,25863,25866],{},[12249,25864,25865],{},"TypeScript 支援良好","：大量使用型別推斷，不需要額外寫型別包裝。",[4541,25868,25869,25872],{},[12249,25870,25871],{},"輕量","：壓縮後約 1KB，並支援自訂 plugin。",[15,25874,25875],{"id":25875},"兩者的差異",[13433,25877,25878,25889],{},[13436,25879,25880],{},[13439,25881,25882,25884,25886],{},[13442,25883,13544],{},[13442,25885,4844],{},[13442,25887,25888],{},"Pinia",[13449,25890,25891,25905,25919,25930,25946,25957,25970],{},[13439,25892,25893,25896,25899],{},[13454,25894,25895],{},"Mutations",[13454,25897,25898],{},"必須有",[13454,25900,25901,25904],{},[12249,25902,25903],{},"移除","，直接在 action 改 state",[13439,25906,25907,25910,25916],{},[13454,25908,25909],{},"模組 namespace",[13454,25911,25912,25913],{},"要自己設定 ",[29,25914,25915],{},"namespaced",[13454,25917,25918],{},"自動，每個 store 天生獨立",[13439,25920,25921,25924,25927],{},[13454,25922,25923],{},"TypeScript",[13454,25925,25926],{},"需要額外的型別包裝",[13454,25928,25929],{},"型別推斷",[13439,25931,25932,25935,25941],{},[13454,25933,25934],{},"取得 state",[13454,25936,25937,25938,25940],{},"要傳 ",[29,25939,17996],{}," 參數",[13454,25942,25943,25944],{},"直接用 ",[29,25945,1083],{},[13439,25947,25948,25951,25954],{},[13454,25949,25950],{},"SSR",[13454,25952,25953],{},"支援但設定繁瑣",[13454,25955,25956],{},"原生支援",[13439,25958,25959,25962,25965],{},[13454,25960,25961],{},"Nuxt 3",[13454,25963,25964],{},"不支援",[13454,25966,25967],{},[12249,25968,25969],{},"完整支援",[13439,25971,25972,25975,25978],{},[13454,25973,25974],{},"體積",[13454,25976,25977],{},"較大",[13454,25979,25980],{},"約 1KB",[11,25982,25983,25984,25987],{},"最大的差異是",[12249,25985,25986],{},"移除了 Mutations","。少了一層之後寫法簡潔很多，代價是從程式碼上看不出它是 Flux 架構——但實務上，多數人本來就覺得 mutation 這層是多餘的儀式。",[15,25989,21465],{"id":21465},[151,25991,25993],{"id":25992},"vue-專案","Vue 專案",[22,25995,25997],{"className":2057,"code":25996,"language":2059,"meta":27,"style":27},"npm install pinia\n",[29,25998,25999],{"__ignoreMap":27},[32,26000,26001,26003,26005],{"class":34,"line":35},[32,26002,4625],{"class":2066},[32,26004,4650],{"class":175},[32,26006,26007],{"class":175}," pinia\n",[11,26009,26010,5240],{},[29,26011,5137],{},[22,26013,26017],{"className":26014,"code":26015,"language":26016,"meta":27,"style":27},"language-javascript shiki shiki-themes github-light github-dark-default","import { createApp } from 'vue'\nimport { createPinia } from 'pinia'\nimport App from '.\u002FApp.vue'\n\nconst app = createApp(App)\napp.use(createPinia())\napp.mount('#app')\n","javascript",[29,26018,26019,26031,26043,26055,26059,26074,26089],{"__ignoreMap":27},[32,26020,26021,26023,26026,26028],{"class":34,"line":35},[32,26022,8794],{"class":38},[32,26024,26025],{"class":41}," { createApp } ",[32,26027,8850],{"class":38},[32,26029,26030],{"class":175}," 'vue'\n",[32,26032,26033,26035,26038,26040],{"class":34,"line":45},[32,26034,8794],{"class":38},[32,26036,26037],{"class":41}," { createPinia } ",[32,26039,8850],{"class":38},[32,26041,26042],{"class":175}," 'pinia'\n",[32,26044,26045,26047,26050,26052],{"class":34,"line":51},[32,26046,8794],{"class":38},[32,26048,26049],{"class":41}," App ",[32,26051,8850],{"class":38},[32,26053,26054],{"class":175}," '.\u002FApp.vue'\n",[32,26056,26057],{"class":34,"line":90},[32,26058,103],{"emptyLinePlaceholder":102},[32,26060,26061,26063,26066,26068,26071],{"class":34,"line":99},[32,26062,6617],{"class":38},[32,26064,26065],{"class":351}," app",[32,26067,894],{"class":38},[32,26069,26070],{"class":217}," createApp",[32,26072,26073],{"class":41},"(App)\n",[32,26075,26076,26079,26081,26083,26086],{"class":34,"line":106},[32,26077,26078],{"class":41},"app.",[32,26080,8895],{"class":217},[32,26082,477],{"class":41},[32,26084,26085],{"class":217},"createPinia",[32,26087,26088],{"class":41},"())\n",[32,26090,26091,26093,26096,26098,26100],{"class":34,"line":114},[32,26092,26078],{"class":41},[32,26094,26095],{"class":217},"mount",[32,26097,477],{"class":41},[32,26099,4178],{"class":175},[32,26101,515],{"class":41},[151,26103,26105],{"id":26104},"nuxt-專案","Nuxt 專案",[22,26107,26109],{"className":2057,"code":26108,"language":2059,"meta":27,"style":27},"npm install pinia @pinia\u002Fnuxt\n",[29,26110,26111],{"__ignoreMap":27},[32,26112,26113,26115,26117,26120],{"class":34,"line":35},[32,26114,4625],{"class":2066},[32,26116,4650],{"class":175},[32,26118,26119],{"class":175}," pinia",[32,26121,26122],{"class":175}," @pinia\u002Fnuxt\n",[11,26124,26125,5240],{},[29,26126,23855],{},[22,26128,26132],{"className":26129,"code":26130,"language":26131,"meta":27,"style":27},"language-typescript shiki shiki-themes github-light github-dark-default","export default defineNuxtConfig({\n  modules: ['@pinia\u002Fnuxt'],\n})\n","typescript",[29,26133,26134,26145,26156],{"__ignoreMap":27},[32,26135,26136,26138,26140,26143],{"class":34,"line":35},[32,26137,5038],{"class":38},[32,26139,5041],{"class":38},[32,26141,26142],{"class":217}," defineNuxtConfig",[32,26144,1143],{"class":5049},[32,26146,26147,26149,26152,26154],{"class":34,"line":45},[32,26148,21503],{"class":41},[32,26150,26151],{"class":175},"'@pinia\u002Fnuxt'",[32,26153,21509],{"class":41},[32,26155,547],{"class":5049},[32,26157,26158],{"class":34,"line":51},[32,26159,26160],{"class":5049},"})\n",[11,26162,26163],{},"Nuxt 模組會自動處理 SSR 的狀態注水（hydration），不用自己搬狀態。",[15,26165,26167],{"id":26166},"寫法一options-風格","寫法一：Options 風格",[11,26169,26170],{},"跟 Vuex 比較像，適合從 Vuex 搬過來：",[22,26172,26174],{"className":26014,"code":26173,"language":26016,"meta":27,"style":27},"\u002F\u002F src\u002Fstores\u002Fcart.js\nimport { defineStore } from 'pinia'\n\nexport const useCartStore = defineStore('cart', {\n  state: () => ({\n    items: []\n  }),\n  getters: {\n    totalCost(state) {\n      return state.items.reduce(\n        (total, item) => total + item.price * item.quantity,\n        0\n      )\n    }\n  },\n  actions: {\n    addItem(item) {\n      const existingItem = this.items.find((i) => i.id === item.id)\n      if (existingItem) {\n        const newItem = {\n          ...existingItem,\n          quantity: existingItem.quantity + item.quantity\n        }\n        this.items = this.items.map((i) => (i.id === item.id ? newItem : i))\n      } else {\n        this.items.push(item)\n      }\n    },\n    removeItem(itemId) {\n      this.items = this.items.filter((item) => item.id !== itemId)\n    }\n  }\n})\n",[29,26175,26176,26181,26192,26196,26219,26230,26235,26239,26244,26255,26266,26296,26301,26306,26310,26314,26318,26329,26361,26368,26380,26388,26398,26403,26444,26452,26463,26467,26471,26483,26512,26516,26520],{"__ignoreMap":27},[32,26177,26178],{"class":34,"line":35},[32,26179,26180],{"class":608},"\u002F\u002F src\u002Fstores\u002Fcart.js\n",[32,26182,26183,26185,26188,26190],{"class":34,"line":45},[32,26184,8794],{"class":38},[32,26186,26187],{"class":41}," { defineStore } ",[32,26189,8850],{"class":38},[32,26191,26042],{"class":175},[32,26193,26194],{"class":34,"line":51},[32,26195,103],{"emptyLinePlaceholder":102},[32,26197,26198,26200,26203,26206,26208,26211,26213,26216],{"class":34,"line":90},[32,26199,5038],{"class":38},[32,26201,26202],{"class":38}," const",[32,26204,26205],{"class":351}," useCartStore",[32,26207,894],{"class":38},[32,26209,26210],{"class":217}," defineStore",[32,26212,477],{"class":41},[32,26214,26215],{"class":175},"'cart'",[32,26217,26218],{"class":41},", {\n",[32,26220,26221,26224,26226,26228],{"class":34,"line":99},[32,26222,26223],{"class":217},"  state",[32,26225,5087],{"class":41},[32,26227,5090],{"class":38},[32,26229,22110],{"class":41},[32,26231,26232],{"class":34,"line":106},[32,26233,26234],{"class":41},"    items: []\n",[32,26236,26237],{"class":34,"line":114},[32,26238,20389],{"class":41},[32,26240,26241],{"class":34,"line":120},[32,26242,26243],{"class":41},"  getters: {\n",[32,26245,26246,26249,26251,26253],{"class":34,"line":127},[32,26247,26248],{"class":217},"    totalCost",[32,26250,477],{"class":41},[32,26252,17996],{"class":903},[32,26254,520],{"class":41},[32,26256,26257,26259,26262,26264],{"class":34,"line":132},[32,26258,18463],{"class":38},[32,26260,26261],{"class":41}," state.items.",[32,26263,22726],{"class":217},[32,26265,539],{"class":41},[32,26267,26268,26271,26274,26276,26279,26281,26283,26286,26288,26291,26293],{"class":34,"line":141},[32,26269,26270],{"class":41},"        (",[32,26272,26273],{"class":903},"total",[32,26275,635],{"class":41},[32,26277,26278],{"class":903},"item",[32,26280,483],{"class":41},[32,26282,5090],{"class":38},[32,26284,26285],{"class":41}," total ",[32,26287,224],{"class":38},[32,26289,26290],{"class":41}," item.price ",[32,26292,6078],{"class":38},[32,26294,26295],{"class":41}," item.quantity,\n",[32,26297,26298],{"class":34,"line":147},[32,26299,26300],{"class":351},"        0\n",[32,26302,26303],{"class":34,"line":275},[32,26304,26305],{"class":41},"      )\n",[32,26307,26308],{"class":34,"line":282},[32,26309,5591],{"class":41},[32,26311,26312],{"class":34,"line":295},[32,26313,5639],{"class":41},[32,26315,26316],{"class":34,"line":605},[32,26317,18027],{"class":41},[32,26319,26320,26323,26325,26327],{"class":34,"line":612},[32,26321,26322],{"class":217},"    addItem",[32,26324,477],{"class":41},[32,26326,26278],{"class":903},[32,26328,520],{"class":41},[32,26330,26331,26333,26336,26338,26340,26343,26345,26347,26349,26351,26353,26356,26358],{"class":34,"line":617},[32,26332,21153],{"class":38},[32,26334,26335],{"class":351}," existingItem",[32,26337,894],{"class":38},[32,26339,4234],{"class":351},[32,26341,26342],{"class":41},".items.",[32,26344,20891],{"class":217},[32,26346,8293],{"class":41},[32,26348,15212],{"class":903},[32,26350,483],{"class":41},[32,26352,5090],{"class":38},[32,26354,26355],{"class":41}," i.id ",[32,26357,5560],{"class":38},[32,26359,26360],{"class":41}," item.id)\n",[32,26362,26363,26365],{"class":34,"line":644},[32,26364,1071],{"class":38},[32,26366,26367],{"class":41}," (existingItem) {\n",[32,26369,26370,26373,26376,26378],{"class":34,"line":649},[32,26371,26372],{"class":38},"        const",[32,26374,26375],{"class":351}," newItem",[32,26377,894],{"class":38},[32,26379,703],{"class":41},[32,26381,26382,26385],{"class":34,"line":671},[32,26383,26384],{"class":38},"          ...",[32,26386,26387],{"class":41},"existingItem,\n",[32,26389,26390,26393,26395],{"class":34,"line":690},[32,26391,26392],{"class":41},"          quantity: existingItem.quantity ",[32,26394,224],{"class":38},[32,26396,26397],{"class":41}," item.quantity\n",[32,26399,26400],{"class":34,"line":696},[32,26401,26402],{"class":41},"        }\n",[32,26404,26405,26407,26410,26412,26414,26416,26418,26420,26422,26424,26426,26429,26431,26434,26436,26439,26441],{"class":34,"line":706},[32,26406,24690],{"class":351},[32,26408,26409],{"class":41},".items ",[32,26411,172],{"class":38},[32,26413,4234],{"class":351},[32,26415,26342],{"class":41},[32,26417,11130],{"class":217},[32,26419,8293],{"class":41},[32,26421,15212],{"class":903},[32,26423,483],{"class":41},[32,26425,5090],{"class":38},[32,26427,26428],{"class":41}," (i.id ",[32,26430,5560],{"class":38},[32,26432,26433],{"class":41}," item.id ",[32,26435,316],{"class":38},[32,26437,26438],{"class":41}," newItem ",[32,26440,322],{"class":38},[32,26442,26443],{"class":41}," i))\n",[32,26445,26446,26448,26450],{"class":34,"line":721},[32,26447,9477],{"class":41},[32,26449,565],{"class":38},[32,26451,703],{"class":41},[32,26453,26454,26456,26458,26460],{"class":34,"line":728},[32,26455,24690],{"class":351},[32,26457,26342],{"class":41},[32,26459,23621],{"class":217},[32,26461,26462],{"class":41},"(item)\n",[32,26464,26465],{"class":34,"line":737},[32,26466,1193],{"class":41},[32,26468,26469],{"class":34,"line":742},[32,26470,4204],{"class":41},[32,26472,26473,26476,26478,26481],{"class":34,"line":747},[32,26474,26475],{"class":217},"    removeItem",[32,26477,477],{"class":41},[32,26479,26480],{"class":903},"itemId",[32,26482,520],{"class":41},[32,26484,26485,26487,26489,26491,26493,26495,26497,26499,26501,26503,26505,26507,26509],{"class":34,"line":752},[32,26486,21345],{"class":351},[32,26488,26409],{"class":41},[32,26490,172],{"class":38},[32,26492,4234],{"class":351},[32,26494,26342],{"class":41},[32,26496,16124],{"class":217},[32,26498,8293],{"class":41},[32,26500,26278],{"class":903},[32,26502,483],{"class":41},[32,26504,5090],{"class":38},[32,26506,26433],{"class":41},[32,26508,1553],{"class":38},[32,26510,26511],{"class":41}," itemId)\n",[32,26513,26514],{"class":34,"line":773},[32,26515,5591],{"class":41},[32,26517,26518],{"class":34,"line":789},[32,26519,1203],{"class":41},[32,26521,26522],{"class":34,"line":795},[32,26523,26160],{"class":41},[11,26525,26526,26529,26530,26532],{},[29,26527,26528],{},"defineStore"," 的第一個參數 ",[29,26531,26215],{}," 是這個 store 的唯一 ID，DevTools 會用它來標示。",[11,26534,20932],{},[22,26536,26538],{"className":8665,"code":26537,"language":4112,"meta":27,"style":27},"\u003C!-- src\u002Fcomponents\u002FCart.vue -->\n\u003Ctemplate>\n  \u003Cdiv>\n    \u003Ch2>你的購物車\u003C\u002Fh2>\n    \u003Cul>\n      \u003Cli v-for=\"item in items\" :key=\"item.id\">\n        {{ item.name }} - ${{ item.price }} x {{ item.quantity }}\n        \u003Cbutton @click=\"removeItem(item.id)\">刪除\u003C\u002Fbutton>\n      \u003C\u002Fli>\n    \u003C\u002Ful>\n    \u003Cdiv>總費用: ${{ totalCost }}\u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nimport { storeToRefs } from \"pinia\";\nimport { useCartStore } from \"..\u002Fstores\u002Fcart\";\n\nconst cartStore = useCartStore();\n\nconst { items, totalCost } = storeToRefs(cartStore);\n\nfunction removeItem(id) {\n  cartStore.removeItem(id);\n}\n\u003C\u002Fscript>\n",[29,26539,26540,26545,26553,26561,26574,26582,26604,26609,26631,26640,26648,26661,26669,26677,26681,26692,26706,26720,26724,26737,26741,26767,26771,26784,26795,26799],{"__ignoreMap":27},[32,26541,26542],{"class":34,"line":35},[32,26543,26544],{"class":608},"\u003C!-- src\u002Fcomponents\u002FCart.vue -->\n",[32,26546,26547,26549,26551],{"class":34,"line":45},[32,26548,4046],{"class":41},[32,26550,9623],{"class":4049},[32,26552,4062],{"class":41},[32,26554,26555,26557,26559],{"class":34,"line":51},[32,26556,4067],{"class":41},[32,26558,4050],{"class":4049},[32,26560,4062],{"class":41},[32,26562,26563,26565,26567,26570,26572],{"class":34,"line":90},[32,26564,9638],{"class":41},[32,26566,15],{"class":4049},[32,26568,26569],{"class":41},">你的購物車\u003C\u002F",[32,26571,15],{"class":4049},[32,26573,4062],{"class":41},[32,26575,26576,26578,26580],{"class":34,"line":99},[32,26577,9638],{"class":41},[32,26579,4538],{"class":4049},[32,26581,4062],{"class":41},[32,26583,26584,26586,26588,26590,26592,26595,26597,26599,26602],{"class":34,"line":106},[32,26585,18315],{"class":41},[32,26587,4541],{"class":4049},[32,26589,18320],{"class":4053},[32,26591,172],{"class":41},[32,26593,26594],{"class":175},"\"item in items\"",[32,26596,18328],{"class":4053},[32,26598,172],{"class":41},[32,26600,26601],{"class":175},"\"item.id\"",[32,26603,4062],{"class":41},[32,26605,26606],{"class":34,"line":114},[32,26607,26608],{"class":41},"        {{ item.name }} - ${{ item.price }} x {{ item.quantity }}\n",[32,26610,26611,26614,26616,26619,26621,26624,26627,26629],{"class":34,"line":120},[32,26612,26613],{"class":41},"        \u003C",[32,26615,11493],{"class":4049},[32,26617,26618],{"class":4053}," @click",[32,26620,172],{"class":41},[32,26622,26623],{"class":175},"\"removeItem(item.id)\"",[32,26625,26626],{"class":41},">刪除\u003C\u002F",[32,26628,11493],{"class":4049},[32,26630,4062],{"class":41},[32,26632,26633,26636,26638],{"class":34,"line":127},[32,26634,26635],{"class":41},"      \u003C\u002F",[32,26637,4541],{"class":4049},[32,26639,4062],{"class":41},[32,26641,26642,26644,26646],{"class":34,"line":132},[32,26643,18345],{"class":41},[32,26645,4538],{"class":4049},[32,26647,4062],{"class":41},[32,26649,26650,26652,26654,26657,26659],{"class":34,"line":141},[32,26651,9638],{"class":41},[32,26653,4050],{"class":4049},[32,26655,26656],{"class":41},">總費用: ${{ totalCost }}\u003C\u002F",[32,26658,4050],{"class":4049},[32,26660,4062],{"class":41},[32,26662,26663,26665,26667],{"class":34,"line":147},[32,26664,9691],{"class":41},[32,26666,4050],{"class":4049},[32,26668,4062],{"class":41},[32,26670,26671,26673,26675],{"class":34,"line":275},[32,26672,4105],{"class":41},[32,26674,9623],{"class":4049},[32,26676,4062],{"class":41},[32,26678,26679],{"class":34,"line":282},[32,26680,103],{"emptyLinePlaceholder":102},[32,26682,26683,26685,26687,26690],{"class":34,"line":295},[32,26684,4046],{"class":41},[32,26686,4125],{"class":4049},[32,26688,26689],{"class":4053}," setup",[32,26691,4062],{"class":41},[32,26693,26694,26696,26699,26701,26704],{"class":34,"line":605},[32,26695,8794],{"class":38},[32,26697,26698],{"class":41}," { storeToRefs } ",[32,26700,8850],{"class":38},[32,26702,26703],{"class":175}," \"pinia\"",[32,26705,96],{"class":41},[32,26707,26708,26710,26713,26715,26718],{"class":34,"line":612},[32,26709,8794],{"class":38},[32,26711,26712],{"class":41}," { useCartStore } ",[32,26714,8850],{"class":38},[32,26716,26717],{"class":175}," \"..\u002Fstores\u002Fcart\"",[32,26719,96],{"class":41},[32,26721,26722],{"class":34,"line":617},[32,26723,103],{"emptyLinePlaceholder":102},[32,26725,26726,26728,26731,26733,26735],{"class":34,"line":644},[32,26727,6617],{"class":38},[32,26729,26730],{"class":351}," cartStore",[32,26732,894],{"class":38},[32,26734,26205],{"class":217},[32,26736,687],{"class":41},[32,26738,26739],{"class":34,"line":649},[32,26740,103],{"emptyLinePlaceholder":102},[32,26742,26743,26745,26748,26751,26753,26756,26759,26761,26764],{"class":34,"line":671},[32,26744,6617],{"class":38},[32,26746,26747],{"class":41}," { ",[32,26749,26750],{"class":351},"items",[32,26752,635],{"class":41},[32,26754,26755],{"class":351},"totalCost",[32,26757,26758],{"class":41}," } ",[32,26760,172],{"class":38},[32,26762,26763],{"class":217}," storeToRefs",[32,26765,26766],{"class":41},"(cartStore);\n",[32,26768,26769],{"class":34,"line":690},[32,26770,103],{"emptyLinePlaceholder":102},[32,26772,26773,26775,26778,26780,26782],{"class":34,"line":696},[32,26774,665],{"class":38},[32,26776,26777],{"class":217}," removeItem",[32,26779,477],{"class":41},[32,26781,20882],{"class":903},[32,26783,520],{"class":41},[32,26785,26786,26789,26792],{"class":34,"line":706},[32,26787,26788],{"class":41},"  cartStore.",[32,26790,26791],{"class":217},"removeItem",[32,26793,26794],{"class":41},"(id);\n",[32,26796,26797],{"class":34,"line":721},[32,26798,54],{"class":41},[32,26800,26801,26803,26805],{"class":34,"line":728},[32,26802,4105],{"class":41},[32,26804,4125],{"class":4049},[32,26806,4062],{"class":41},[151,26808,26810],{"id":26809},"為什麼要用-storetorefs","為什麼要用 storeToRefs",[11,26812,26813,26814,5240],{},"這是最常踩的坑。直接解構會",[12249,26815,26816],{},"失去響應性",[22,26818,26820],{"className":26014,"code":26819,"language":26016,"meta":27,"style":27},"\u002F\u002F 壞掉：items 變成一般變數，store 更新時畫面不會動\nconst { items, totalCost } = useCartStore();\n\n\u002F\u002F 正確：storeToRefs 把 state 與 getters 包成 ref\nconst { items, totalCost } = storeToRefs(cartStore);\n",[29,26821,26822,26827,26847,26851,26856],{"__ignoreMap":27},[32,26823,26824],{"class":34,"line":35},[32,26825,26826],{"class":608},"\u002F\u002F 壞掉：items 變成一般變數，store 更新時畫面不會動\n",[32,26828,26829,26831,26833,26835,26837,26839,26841,26843,26845],{"class":34,"line":45},[32,26830,6617],{"class":38},[32,26832,26747],{"class":41},[32,26834,26750],{"class":351},[32,26836,635],{"class":41},[32,26838,26755],{"class":351},[32,26840,26758],{"class":41},[32,26842,172],{"class":38},[32,26844,26205],{"class":217},[32,26846,687],{"class":41},[32,26848,26849],{"class":34,"line":51},[32,26850,103],{"emptyLinePlaceholder":102},[32,26852,26853],{"class":34,"line":90},[32,26854,26855],{"class":608},"\u002F\u002F 正確：storeToRefs 把 state 與 getters 包成 ref\n",[32,26857,26858,26860,26862,26864,26866,26868,26870,26872,26874],{"class":34,"line":99},[32,26859,6617],{"class":38},[32,26861,26747],{"class":41},[32,26863,26750],{"class":351},[32,26865,635],{"class":41},[32,26867,26755],{"class":351},[32,26869,26758],{"class":41},[32,26871,172],{"class":38},[32,26873,26763],{"class":217},[32,26875,26766],{"class":41},[11,26877,26878,26879,26886],{},"但 ",[12249,26880,26881,26882,26885],{},"action 不要用 ",[29,26883,26884],{},"storeToRefs"," 取","，它們是函式、本來就不需要響應性，直接從 store 取即可：",[22,26888,26890],{"className":26014,"code":26889,"language":26016,"meta":27,"style":27},"const { addItem, removeItem } = cartStore;\n",[29,26891,26892],{"__ignoreMap":27},[32,26893,26894,26896,26898,26901,26903,26905,26907,26909],{"class":34,"line":35},[32,26895,6617],{"class":38},[32,26897,26747],{"class":41},[32,26899,26900],{"class":351},"addItem",[32,26902,635],{"class":41},[32,26904,26791],{"class":351},[32,26906,26758],{"class":41},[32,26908,172],{"class":38},[32,26910,26911],{"class":41}," cartStore;\n",[15,26913,26915],{"id":26914},"寫法二setup-風格","寫法二：setup 風格",[11,26917,26918,26919,26922,26923,26925],{},"用 Composition API 的方式寫，跟平常寫元件邏輯一模一樣——",[29,26920,26921],{},"ref"," 就是 state、",[29,26924,18378],{}," 就是 getter、function 就是 action。",[22,26927,26929],{"className":26129,"code":26928,"language":26131,"meta":27,"style":27},"import { getElectionGroups, getCounties } from '~\u002Fassets\u002Fapi\u002Felection'\n\ninterface Candidate {\n  id: string\n  society: string\n  societyEng: string\n  name: string\n  subName: string\n}\n\nexport const useCandidateAndCountyStore = defineStore('candidatesAndCounty', () => {\n  \u002F\u002F state\n  const candidates = ref\u003CCandidate[]>([])\n  const counties = ref\u003Cstring[]>([])\n\n  \u002F\u002F getters\n  const candidatesGetter = computed(() => candidates.value || [])\n  const countiesGetter = computed(() => counties.value || [])\n\n  \u002F\u002F actions\n  const fetchCandidatesAndCounties = async (): Promise\u003Cvoid> => {\n    const [candidatesData, countiesData] = await Promise.all([\n      getElectionGroups(),\n      getCounties()\n    ])\n    candidates.value = candidatesData as Candidate[]\n    counties.value = countiesData as string[]\n  }\n\n  return {\n    candidates,\n    candidatesGetter,\n    counties,\n    countiesGetter,\n    fetchCandidatesAndCounties\n  }\n})\n",[29,26930,26931,26943,26947,26957,26967,26976,26985,26993,27002,27006,27010,27034,27039,27058,27076,27080,27085,27110,27132,27136,27141,27172,27202,27210,27218,27223,27241,27258,27262,27266,27272,27277,27282,27287,27292,27297,27301],{"__ignoreMap":27},[32,26932,26933,26935,26938,26940],{"class":34,"line":35},[32,26934,8794],{"class":38},[32,26936,26937],{"class":41}," { getElectionGroups, getCounties } ",[32,26939,8850],{"class":38},[32,26941,26942],{"class":175}," '~\u002Fassets\u002Fapi\u002Felection'\n",[32,26944,26945],{"class":34,"line":45},[32,26946,103],{"emptyLinePlaceholder":102},[32,26948,26949,26952,26955],{"class":34,"line":51},[32,26950,26951],{"class":38},"interface",[32,26953,26954],{"class":2066}," Candidate",[32,26956,703],{"class":41},[32,26958,26959,26962,26964],{"class":34,"line":90},[32,26960,26961],{"class":903},"  id",[32,26963,322],{"class":38},[32,26965,26966],{"class":351}," string\n",[32,26968,26969,26972,26974],{"class":34,"line":99},[32,26970,26971],{"class":903},"  society",[32,26973,322],{"class":38},[32,26975,26966],{"class":351},[32,26977,26978,26981,26983],{"class":34,"line":106},[32,26979,26980],{"class":903},"  societyEng",[32,26982,322],{"class":38},[32,26984,26966],{"class":351},[32,26986,26987,26989,26991],{"class":34,"line":114},[32,26988,9859],{"class":903},[32,26990,322],{"class":38},[32,26992,26966],{"class":351},[32,26994,26995,26998,27000],{"class":34,"line":120},[32,26996,26997],{"class":903},"  subName",[32,26999,322],{"class":38},[32,27001,26966],{"class":351},[32,27003,27004],{"class":34,"line":127},[32,27005,54],{"class":41},[32,27007,27008],{"class":34,"line":132},[32,27009,103],{"emptyLinePlaceholder":102},[32,27011,27012,27014,27016,27019,27021,27023,27025,27028,27030,27032],{"class":34,"line":141},[32,27013,5038],{"class":38},[32,27015,26202],{"class":38},[32,27017,27018],{"class":351}," useCandidateAndCountyStore",[32,27020,894],{"class":38},[32,27022,26210],{"class":217},[32,27024,477],{"class":41},[32,27026,27027],{"class":175},"'candidatesAndCounty'",[32,27029,11218],{"class":41},[32,27031,5090],{"class":38},[32,27033,703],{"class":41},[32,27035,27036],{"class":34,"line":147},[32,27037,27038],{"class":608},"  \u002F\u002F state\n",[32,27040,27041,27043,27046,27048,27050,27052,27055],{"class":34,"line":275},[32,27042,4157],{"class":38},[32,27044,27045],{"class":351}," candidates",[32,27047,894],{"class":38},[32,27049,24534],{"class":217},[32,27051,4046],{"class":41},[32,27053,27054],{"class":2066},"Candidate",[32,27056,27057],{"class":41},"[]>([])\n",[32,27059,27060,27062,27065,27067,27069,27071,27074],{"class":34,"line":282},[32,27061,4157],{"class":38},[32,27063,27064],{"class":351}," counties",[32,27066,894],{"class":38},[32,27068,24534],{"class":217},[32,27070,4046],{"class":41},[32,27072,27073],{"class":351},"string",[32,27075,27057],{"class":41},[32,27077,27078],{"class":34,"line":295},[32,27079,103],{"emptyLinePlaceholder":102},[32,27081,27082],{"class":34,"line":605},[32,27083,27084],{"class":608},"  \u002F\u002F getters\n",[32,27086,27087,27089,27092,27094,27097,27100,27102,27105,27107],{"class":34,"line":612},[32,27088,4157],{"class":38},[32,27090,27091],{"class":351}," candidatesGetter",[32,27093,894],{"class":38},[32,27095,27096],{"class":217}," computed",[32,27098,27099],{"class":41},"(() ",[32,27101,5090],{"class":38},[32,27103,27104],{"class":41}," candidates.value ",[32,27106,1036],{"class":38},[32,27108,27109],{"class":41}," [])\n",[32,27111,27112,27114,27117,27119,27121,27123,27125,27128,27130],{"class":34,"line":617},[32,27113,4157],{"class":38},[32,27115,27116],{"class":351}," countiesGetter",[32,27118,894],{"class":38},[32,27120,27096],{"class":217},[32,27122,27099],{"class":41},[32,27124,5090],{"class":38},[32,27126,27127],{"class":41}," counties.value ",[32,27129,1036],{"class":38},[32,27131,27109],{"class":41},[32,27133,27134],{"class":34,"line":644},[32,27135,103],{"emptyLinePlaceholder":102},[32,27137,27138],{"class":34,"line":649},[32,27139,27140],{"class":608},"  \u002F\u002F actions\n",[32,27142,27143,27145,27148,27150,27153,27156,27158,27161,27163,27166,27168,27170],{"class":34,"line":671},[32,27144,4157],{"class":38},[32,27146,27147],{"class":217}," fetchCandidatesAndCounties",[32,27149,894],{"class":38},[32,27151,27152],{"class":38}," async",[32,27154,27155],{"class":41}," ()",[32,27157,322],{"class":38},[32,27159,27160],{"class":2066}," Promise",[32,27162,4046],{"class":41},[32,27164,27165],{"class":351},"void",[32,27167,12401],{"class":41},[32,27169,5090],{"class":38},[32,27171,703],{"class":41},[32,27173,27174,27176,27178,27181,27183,27186,27188,27190,27192,27194,27196,27199],{"class":34,"line":690},[32,27175,9409],{"class":38},[32,27177,8261],{"class":41},[32,27179,27180],{"class":351},"candidatesData",[32,27182,635],{"class":41},[32,27184,27185],{"class":351},"countiesData",[32,27187,10616],{"class":41},[32,27189,172],{"class":38},[32,27191,20431],{"class":38},[32,27193,27160],{"class":351},[32,27195,1278],{"class":41},[32,27197,27198],{"class":217},"all",[32,27200,27201],{"class":41},"([\n",[32,27203,27204,27207],{"class":34,"line":696},[32,27205,27206],{"class":217},"      getElectionGroups",[32,27208,27209],{"class":41},"(),\n",[32,27211,27212,27215],{"class":34,"line":706},[32,27213,27214],{"class":217},"      getCounties",[32,27216,27217],{"class":41},"()\n",[32,27219,27220],{"class":34,"line":721},[32,27221,27222],{"class":41},"    ])\n",[32,27224,27225,27228,27230,27233,27236,27238],{"class":34,"line":728},[32,27226,27227],{"class":41},"    candidates.value ",[32,27229,172],{"class":38},[32,27231,27232],{"class":41}," candidatesData ",[32,27234,27235],{"class":38},"as",[32,27237,26954],{"class":2066},[32,27239,27240],{"class":41},"[]\n",[32,27242,27243,27246,27248,27251,27253,27256],{"class":34,"line":737},[32,27244,27245],{"class":41},"    counties.value ",[32,27247,172],{"class":38},[32,27249,27250],{"class":41}," countiesData ",[32,27252,27235],{"class":38},[32,27254,27255],{"class":351}," string",[32,27257,27240],{"class":41},[32,27259,27260],{"class":34,"line":742},[32,27261,1203],{"class":41},[32,27263,27264],{"class":34,"line":747},[32,27265,103],{"emptyLinePlaceholder":102},[32,27267,27268,27270],{"class":34,"line":752},[32,27269,10974],{"class":38},[32,27271,703],{"class":41},[32,27273,27274],{"class":34,"line":773},[32,27275,27276],{"class":41},"    candidates,\n",[32,27278,27279],{"class":34,"line":789},[32,27280,27281],{"class":41},"    candidatesGetter,\n",[32,27283,27284],{"class":34,"line":795},[32,27285,27286],{"class":41},"    counties,\n",[32,27288,27289],{"class":34,"line":800},[32,27290,27291],{"class":41},"    countiesGetter,\n",[32,27293,27294],{"class":34,"line":816},[32,27295,27296],{"class":41},"    fetchCandidatesAndCounties\n",[32,27298,27299],{"class":34,"line":824},[32,27300,1203],{"class":41},[32,27302,27303],{"class":34,"line":833},[32,27304,26160],{"class":41},[11,27306,27307,27312],{},[12249,27308,27309,27310],{},"要記得 ",[29,27311,4428],{},"——沒有回傳的東西外面拿不到，這是 setup 寫法最常見的錯誤。",[11,27314,27315],{},"在元件裡用起來完全一樣：",[22,27317,27319],{"className":8665,"code":27318,"language":4112,"meta":27,"style":27},"\u003Cscript setup lang=\"ts\">\nimport { storeToRefs } from 'pinia'\nimport { useCandidateAndCountyStore } from '~\u002Fstore\u002FcandidatesAndCounty'\n\nconst store = useCandidateAndCountyStore()\nconst { candidatesGetter, countiesGetter } = storeToRefs(store)\n\n\u002F\u002F 進頁面就抓資料\nawait store.fetchCandidatesAndCounties()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cul>\n    \u003Cli v-for=\"c in candidatesGetter\" :key=\"c.id\">\n      {{ c.name }}（{{ c.society }}）\n    \u003C\u002Fli>\n  \u003C\u002Ful>\n\u003C\u002Ftemplate>\n",[29,27320,27321,27338,27348,27360,27364,27377,27400,27404,27409,27422,27430,27434,27442,27450,27472,27477,27485,27493],{"__ignoreMap":27},[32,27322,27323,27325,27327,27329,27331,27333,27336],{"class":34,"line":35},[32,27324,4046],{"class":41},[32,27326,4125],{"class":4049},[32,27328,26689],{"class":4053},[32,27330,8677],{"class":4053},[32,27332,172],{"class":41},[32,27334,27335],{"class":175},"\"ts\"",[32,27337,4062],{"class":41},[32,27339,27340,27342,27344,27346],{"class":34,"line":45},[32,27341,8794],{"class":38},[32,27343,26698],{"class":41},[32,27345,8850],{"class":38},[32,27347,26042],{"class":175},[32,27349,27350,27352,27355,27357],{"class":34,"line":51},[32,27351,8794],{"class":38},[32,27353,27354],{"class":41}," { useCandidateAndCountyStore } ",[32,27356,8850],{"class":38},[32,27358,27359],{"class":175}," '~\u002Fstore\u002FcandidatesAndCounty'\n",[32,27361,27362],{"class":34,"line":90},[32,27363,103],{"emptyLinePlaceholder":102},[32,27365,27366,27368,27371,27373,27375],{"class":34,"line":99},[32,27367,6617],{"class":38},[32,27369,27370],{"class":351}," store",[32,27372,894],{"class":38},[32,27374,27018],{"class":217},[32,27376,27217],{"class":41},[32,27378,27379,27381,27383,27386,27388,27391,27393,27395,27397],{"class":34,"line":106},[32,27380,6617],{"class":38},[32,27382,26747],{"class":41},[32,27384,27385],{"class":351},"candidatesGetter",[32,27387,635],{"class":41},[32,27389,27390],{"class":351},"countiesGetter",[32,27392,26758],{"class":41},[32,27394,172],{"class":38},[32,27396,26763],{"class":217},[32,27398,27399],{"class":41},"(store)\n",[32,27401,27402],{"class":34,"line":114},[32,27403,103],{"emptyLinePlaceholder":102},[32,27405,27406],{"class":34,"line":120},[32,27407,27408],{"class":608},"\u002F\u002F 進頁面就抓資料\n",[32,27410,27411,27414,27417,27420],{"class":34,"line":127},[32,27412,27413],{"class":38},"await",[32,27415,27416],{"class":41}," store.",[32,27418,27419],{"class":217},"fetchCandidatesAndCounties",[32,27421,27217],{"class":41},[32,27423,27424,27426,27428],{"class":34,"line":132},[32,27425,4105],{"class":41},[32,27427,4125],{"class":4049},[32,27429,4062],{"class":41},[32,27431,27432],{"class":34,"line":141},[32,27433,103],{"emptyLinePlaceholder":102},[32,27435,27436,27438,27440],{"class":34,"line":147},[32,27437,4046],{"class":41},[32,27439,9623],{"class":4049},[32,27441,4062],{"class":41},[32,27443,27444,27446,27448],{"class":34,"line":275},[32,27445,4067],{"class":41},[32,27447,4538],{"class":4049},[32,27449,4062],{"class":41},[32,27451,27452,27454,27456,27458,27460,27463,27465,27467,27470],{"class":34,"line":282},[32,27453,9638],{"class":41},[32,27455,4541],{"class":4049},[32,27457,18320],{"class":4053},[32,27459,172],{"class":41},[32,27461,27462],{"class":175},"\"c in candidatesGetter\"",[32,27464,18328],{"class":4053},[32,27466,172],{"class":41},[32,27468,27469],{"class":175},"\"c.id\"",[32,27471,4062],{"class":41},[32,27473,27474],{"class":34,"line":295},[32,27475,27476],{"class":41},"      {{ c.name }}（{{ c.society }}）\n",[32,27478,27479,27481,27483],{"class":34,"line":605},[32,27480,18345],{"class":41},[32,27482,4541],{"class":4049},[32,27484,4062],{"class":41},[32,27486,27487,27489,27491],{"class":34,"line":612},[32,27488,9691],{"class":41},[32,27490,4538],{"class":4049},[32,27492,4062],{"class":41},[32,27494,27495,27497,27499],{"class":34,"line":617},[32,27496,4105],{"class":41},[32,27498,9623],{"class":4049},[32,27500,4062],{"class":41},[11,27502,27503,27504,27506],{},"兩種寫法可以混用，同一個專案裡不同 store 用不同寫法沒問題。setup 風格對 TypeScript 比較友善，型別直接從 ",[29,27505,26921],{}," 推導出來。",[15,27508,27510],{"id":27509},"store-之間互相呼叫","store 之間互相呼叫",[11,27512,27513],{},"Pinia 的 store 就是函式，要用別的 store 直接呼叫它就好，不需要像 Vuex 那樣處理 namespace。",[11,27515,27516,5240],{},[29,27517,27518],{},"stores\u002Fuser.ts",[22,27520,27522],{"className":26129,"code":27521,"language":26131,"meta":27,"style":27},"export const useUserStore = defineStore('user', () => {\n  const token = ref\u003Cstring | null>(null)\n  const isLoggedIn = computed(() => !!token.value)\n\n  const login = async (email: string, password: string) => {\n    const res = await api.login(email, password)\n    token.value = res.token\n  }\n\n  return { token, isLoggedIn, login }\n})\n",[29,27523,27524,27548,27577,27597,27601,27636,27655,27665,27669,27673,27680],{"__ignoreMap":27},[32,27525,27526,27528,27530,27533,27535,27537,27539,27542,27544,27546],{"class":34,"line":35},[32,27527,5038],{"class":38},[32,27529,26202],{"class":38},[32,27531,27532],{"class":351}," useUserStore",[32,27534,894],{"class":38},[32,27536,26210],{"class":217},[32,27538,477],{"class":41},[32,27540,27541],{"class":175},"'user'",[32,27543,11218],{"class":41},[32,27545,5090],{"class":38},[32,27547,703],{"class":41},[32,27549,27550,27552,27555,27557,27559,27561,27563,27566,27569,27572,27575],{"class":34,"line":45},[32,27551,4157],{"class":38},[32,27553,27554],{"class":351}," token",[32,27556,894],{"class":38},[32,27558,24534],{"class":217},[32,27560,4046],{"class":41},[32,27562,27073],{"class":351},[32,27564,27565],{"class":38}," |",[32,27567,27568],{"class":351}," null",[32,27570,27571],{"class":41},">(",[32,27573,27574],{"class":351},"null",[32,27576,515],{"class":41},[32,27578,27579,27581,27584,27586,27588,27590,27592,27594],{"class":34,"line":51},[32,27580,4157],{"class":38},[32,27582,27583],{"class":351}," isLoggedIn",[32,27585,894],{"class":38},[32,27587,27096],{"class":217},[32,27589,27099],{"class":41},[32,27591,5090],{"class":38},[32,27593,1490],{"class":38},[32,27595,27596],{"class":41},"token.value)\n",[32,27598,27599],{"class":34,"line":90},[32,27600,103],{"emptyLinePlaceholder":102},[32,27602,27603,27605,27608,27610,27612,27614,27617,27619,27621,27623,27626,27628,27630,27632,27634],{"class":34,"line":99},[32,27604,4157],{"class":38},[32,27606,27607],{"class":217}," login",[32,27609,894],{"class":38},[32,27611,27152],{"class":38},[32,27613,900],{"class":41},[32,27615,27616],{"class":903},"email",[32,27618,322],{"class":38},[32,27620,27255],{"class":351},[32,27622,635],{"class":41},[32,27624,27625],{"class":903},"password",[32,27627,322],{"class":38},[32,27629,27255],{"class":351},[32,27631,483],{"class":41},[32,27633,5090],{"class":38},[32,27635,703],{"class":41},[32,27637,27638,27640,27642,27644,27646,27649,27652],{"class":34,"line":106},[32,27639,9409],{"class":38},[32,27641,20426],{"class":351},[32,27643,894],{"class":38},[32,27645,20431],{"class":38},[32,27647,27648],{"class":41}," api.",[32,27650,27651],{"class":217},"login",[32,27653,27654],{"class":41},"(email, password)\n",[32,27656,27657,27660,27662],{"class":34,"line":114},[32,27658,27659],{"class":41},"    token.value ",[32,27661,172],{"class":38},[32,27663,27664],{"class":41}," res.token\n",[32,27666,27667],{"class":34,"line":120},[32,27668,1203],{"class":41},[32,27670,27671],{"class":34,"line":127},[32,27672,103],{"emptyLinePlaceholder":102},[32,27674,27675,27677],{"class":34,"line":132},[32,27676,10974],{"class":38},[32,27678,27679],{"class":41}," { token, isLoggedIn, login }\n",[32,27681,27682],{"class":34,"line":141},[32,27683,26160],{"class":41},[11,27685,27686,5240],{},[29,27687,27688],{},"stores\u002Fcart.ts",[22,27690,27692],{"className":26129,"code":27691,"language":26131,"meta":27,"style":27},"import { useUserStore } from '.\u002Fuser'\n\nexport const useCartStore = defineStore('cart', () => {\n  const items = ref\u003CCartItem[]>([])\n\n  const checkout = async () => {\n    \u002F\u002F 在 action 裡呼叫，不要放在 store 的最外層\n    const userStore = useUserStore()\n\n    if (!userStore.isLoggedIn) {\n      throw new Error('請先登入')\n    }\n\n    await api.checkout(items.value, userStore.token)\n    items.value = []\n  }\n\n  return { items, checkout }\n})\n",[29,27693,27694,27706,27710,27732,27750,27754,27770,27775,27788,27792,27803,27820,27824,27828,27840,27850,27854,27858,27865],{"__ignoreMap":27},[32,27695,27696,27698,27701,27703],{"class":34,"line":35},[32,27697,8794],{"class":38},[32,27699,27700],{"class":41}," { useUserStore } ",[32,27702,8850],{"class":38},[32,27704,27705],{"class":175}," '.\u002Fuser'\n",[32,27707,27708],{"class":34,"line":45},[32,27709,103],{"emptyLinePlaceholder":102},[32,27711,27712,27714,27716,27718,27720,27722,27724,27726,27728,27730],{"class":34,"line":51},[32,27713,5038],{"class":38},[32,27715,26202],{"class":38},[32,27717,26205],{"class":351},[32,27719,894],{"class":38},[32,27721,26210],{"class":217},[32,27723,477],{"class":41},[32,27725,26215],{"class":175},[32,27727,11218],{"class":41},[32,27729,5090],{"class":38},[32,27731,703],{"class":41},[32,27733,27734,27736,27739,27741,27743,27745,27748],{"class":34,"line":90},[32,27735,4157],{"class":38},[32,27737,27738],{"class":351}," items",[32,27740,894],{"class":38},[32,27742,24534],{"class":217},[32,27744,4046],{"class":41},[32,27746,27747],{"class":2066},"CartItem",[32,27749,27057],{"class":41},[32,27751,27752],{"class":34,"line":99},[32,27753,103],{"emptyLinePlaceholder":102},[32,27755,27756,27758,27760,27762,27764,27766,27768],{"class":34,"line":106},[32,27757,4157],{"class":38},[32,27759,2923],{"class":217},[32,27761,894],{"class":38},[32,27763,27152],{"class":38},[32,27765,11257],{"class":41},[32,27767,5090],{"class":38},[32,27769,703],{"class":41},[32,27771,27772],{"class":34,"line":114},[32,27773,27774],{"class":608},"    \u002F\u002F 在 action 裡呼叫，不要放在 store 的最外層\n",[32,27776,27777,27779,27782,27784,27786],{"class":34,"line":120},[32,27778,9409],{"class":38},[32,27780,27781],{"class":351}," userStore",[32,27783,894],{"class":38},[32,27785,27532],{"class":217},[32,27787,27217],{"class":41},[32,27789,27790],{"class":34,"line":127},[32,27791,103],{"emptyLinePlaceholder":102},[32,27793,27794,27796,27798,27800],{"class":34,"line":132},[32,27795,5554],{"class":38},[32,27797,900],{"class":41},[32,27799,23362],{"class":38},[32,27801,27802],{"class":41},"userStore.isLoggedIn) {\n",[32,27804,27805,27808,27810,27813,27815,27818],{"class":34,"line":141},[32,27806,27807],{"class":38},"      throw",[32,27809,4165],{"class":38},[32,27811,27812],{"class":217}," Error",[32,27814,477],{"class":41},[32,27816,27817],{"class":175},"'請先登入'",[32,27819,515],{"class":41},[32,27821,27822],{"class":34,"line":147},[32,27823,5591],{"class":41},[32,27825,27826],{"class":34,"line":275},[32,27827,103],{"emptyLinePlaceholder":102},[32,27829,27830,27833,27835,27837],{"class":34,"line":282},[32,27831,27832],{"class":38},"    await",[32,27834,27648],{"class":41},[32,27836,3763],{"class":217},[32,27838,27839],{"class":41},"(items.value, userStore.token)\n",[32,27841,27842,27845,27847],{"class":34,"line":295},[32,27843,27844],{"class":41},"    items.value ",[32,27846,172],{"class":38},[32,27848,27849],{"class":41}," []\n",[32,27851,27852],{"class":34,"line":605},[32,27853,1203],{"class":41},[32,27855,27856],{"class":34,"line":612},[32,27857,103],{"emptyLinePlaceholder":102},[32,27859,27860,27862],{"class":34,"line":617},[32,27861,10974],{"class":38},[32,27863,27864],{"class":41}," { items, checkout }\n",[32,27866,27867],{"class":34,"line":644},[32,27868,26160],{"class":41},[11,27870,27871,27878,27879,27882],{},[12249,27872,27873,27874,27877],{},"關鍵是 ",[29,27875,27876],{},"useUserStore()"," 要寫在 action 裡面，不要寫在 store 的最外層。"," 寫在外層時，那行程式會在模組載入當下就執行，而那時候 Pinia 實例可能還沒建立好（尤其在 SSR 環境），會拿到 ",[29,27880,27881],{},"getActivePinia was called with no active Pinia"," 這個錯誤。",[11,27884,27885],{},"要注意兩個 store 互相引用會形成循環相依。就 JavaScript 模組來說這是可行的，但如果 A 跟 B 頻繁互相呼叫，通常代表它們該合併成一個 store，或該把共用的邏輯抽成一般的 composable。",[15,27887,27888],{"id":27888},"什麼時候該用狀態管理",[11,27890,27891],{},"不是所有共用資料都需要 store。判斷標準大致是：",[4538,27893,27894,27900,27906],{},[4541,27895,27896,27899],{},[12249,27897,27898],{},"父子元件之間"," → props 與 emit 就夠了",[4541,27901,27902,27905],{},[12249,27903,27904],{},"跨多層、多個分支的元件"," → 用 store",[4541,27907,27908,27911,27912,11621,27914,27917],{},[12249,27909,27910],{},"只是伺服器資料的快取","（列表、詳情） → Nuxt 的 ",[29,27913,21652],{},[29,27915,27916],{},"useAsyncData"," 通常就夠，不必進 store",[11,27919,27920],{},"過早引入狀態管理，只會讓資料流變得更難追。",[15,27922,25664],{"id":25664},[11,27924,27925],{},"這篇的內容到現在基本上還適用——Pinia 沒有像其他工具那樣被取代，反而成了 Vue 官方推薦的預設方案，Vuex 進入維護模式。要補充的只有一點：Pinia 3 之後不再支援 Vue 2，只跑 Vue 3；還在 Vue 2 專案上的話要留在 Pinia 2。上面所有的寫法都沒有變動。",[15,27927,27928],{"id":27928},"相關連結",[4538,27930,27931,27938,27945],{},[4541,27932,27933],{},[412,27934,27937],{"href":27935,"rel":27936},"https:\u002F\u002Fpinia.vuejs.org\u002F",[416],"Pinia 官方文件",[4541,27939,27940],{},[412,27941,27944],{"href":27942,"rel":27943},"https:\u002F\u002Fstackblitz.com\u002Fgithub\u002Fpiniajs\u002Fexample-vue-3-vite?file=src%2Fstores%2Fcart.ts",[416],"Pinia 官方範例（StackBlitz）",[4541,27946,27947],{},[412,27948,27951],{"href":27949,"rel":27950},"https:\u002F\u002Fwww.vuemastery.com\u002Fblog\u002Fadvantages-of-pinia-vs-vuex\u002F",[416],"Advantages of Pinia vs Vuex",[368,27953,27954],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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 .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}",{"title":27,"searchDepth":51,"depth":51,"links":27956},[27957,27960,27961,27962,27966,27969,27970,27971,27972,27973],{"id":25758,"depth":45,"text":25759,"children":27958},[27959],{"id":25810,"depth":51,"text":25810},{"id":25839,"depth":45,"text":25840},{"id":25875,"depth":45,"text":25875},{"id":21465,"depth":45,"text":21465,"children":27963},[27964,27965],{"id":25992,"depth":51,"text":25993},{"id":26104,"depth":51,"text":26105},{"id":26166,"depth":45,"text":26167,"children":27967},[27968],{"id":26809,"depth":51,"text":26810},{"id":26914,"depth":45,"text":26915},{"id":27509,"depth":45,"text":27510},{"id":27888,"depth":45,"text":27888},{"id":25664,"depth":45,"text":25664},{"id":27928,"depth":45,"text":27928},"整理 Vuex 的核心概念與它遇到的瓶頸，說明 Pinia 為什麼移除 Mutations、對 TypeScript 好在哪，並實際比較 Options 與 setup 兩種 store 寫法，含 storeToRefs 的用途與 store 之間互相呼叫的做法。",{},"2024-05-05",[],{"title":17937,"description":27974},"posts\u002F20240505.pinia-vuex-state-management",[25888,4844,25743,27981,15981],"狀態管理","6nsfLZsaROBpGRo_kjsgMXMEJApYfVhokkI-QWJWqmA",{"id":27984,"title":27985,"body":27986,"category":378,"description":30659,"draft":380,"extension":381,"image":382,"meta":30660,"navigation":102,"path":15947,"publishedAt":30661,"relatedExperiments":30662,"seo":30663,"stem":30664,"tags":30665,"updatedAt":30667,"__hash__":30668},"posts\u002Fposts\u002F20250724.nuxt3-setup-notes.md","Nuxt 3 專案建立筆記：CLI、目錄結構、路由與 Layout",{"type":8,"value":27987,"toc":30619},[27988,27992,27995,27998,28001,28014,28020,28022,28047,28050,28056,28059,28065,28068,28081,28083,28090,28093,28110,28118,28156,28159,28162,28165,28169,28191,28195,28200,28302,28316,28329,28332,28335,28341,28505,28508,28554,28573,28582,28585,28591,28594,28603,28649,28662,28665,28668,28680,28695,28767,28770,28780,28826,28829,28832,28838,29024,29027,29030,29036,29181,29199,29203,29209,29347,29356,29359,29362,29368,29377,29383,29396,29399,29401,29407,29410,29415,29524,29531,29535,29727,29735,29738,29743,29841,29846,29886,29906,29909,29917,29923,29943,29960,29963,29966,29984,29988,30022,30026,30032,30066,30073,30076,30097,30126,30130,30146,30150,30178,30183,30287,30293,30297,30300,30316,30320,30323,30338,30341,30347,30350,30363,30366,30370,30382,30386,30403,30409,30459,30462,30545,30547,30570,30577,30579,30616],[15,27989,27991],{"id":27990},"安裝-nuxt-cli","安裝 Nuxt CLI",[11,27993,27994],{},"nuxi 全名是 Nuxt CLI（Nuxt Command Line Interface），是 Nuxt 官方提供的標準工具。它之於 Nuxt，就像 Vue CLI 之於 Vue——用來建立與管理專案。",[151,27996,27997],{"id":27997},"環境準備",[11,27999,28000],{},"為了確保 nuxi 不會拿到快取的舊版本，可以先清一次 npx 快取：",[22,28002,28004],{"className":2057,"code":28003,"language":2059,"meta":27,"style":27},"npx clear-npx-cache\n",[29,28005,28006],{"__ignoreMap":27},[32,28007,28008,28011],{"class":34,"line":35},[32,28009,28010],{"class":2066},"npx",[32,28012,28013],{"class":175}," clear-npx-cache\n",[11,28015,28016,28017,3775],{},"Node.js 需要 ",[12249,28018,28019],{},"v16.10.0 以上",[151,28021,25360],{"id":25360},[22,28023,28025],{"className":2057,"code":28024,"language":2059,"meta":27,"style":27},"npx nuxi@latest init \u003Cproject-name>\n",[29,28026,28027],{"__ignoreMap":27},[32,28028,28029,28031,28034,28036,28039,28042,28045],{"class":34,"line":35},[32,28030,28010],{"class":2066},[32,28032,28033],{"class":175}," nuxi@latest",[32,28035,8581],{"class":175},[32,28037,28038],{"class":38}," \u003C",[32,28040,28041],{"class":175},"project-nam",[32,28043,28044],{"class":41},"e",[32,28046,4062],{"class":38},[11,28048,28049],{},"執行後會問幾個問題，一路照預設走就好：",[22,28051,28054],{"className":28052,"code":28053,"language":426,"meta":27},[424],"✔ Which package manager would you like to use?\n  › npm \u002F pnpm \u002F yarn \u002F bun\n\n✔ Initialize git repository?\n  › Yes \u002F No\n",[29,28055,28053],{"__ignoreMap":27},[11,28057,28058],{},"裝完的專案長這樣（只有最基本的幾個檔案，其他資料夾要自己開）：",[22,28060,28063],{"className":28061,"code":28062,"language":426,"meta":27},[424],"nuxt-app\u002F\n├── .nuxt\u002F\n├── node_modules\u002F\n├── public\u002F\n├── server\u002F\n├── app.vue\n├── nuxt.config.ts\n├── package.json\n├── tsconfig.json\n└── README.md\n",[29,28064,28062],{"__ignoreMap":27},[11,28066,28067],{},"啟動：",[22,28069,28071],{"className":2057,"code":28070,"language":2059,"meta":27,"style":27},"npm run dev\n",[29,28072,28073],{"__ignoreMap":27},[32,28074,28075,28077,28079],{"class":34,"line":35},[32,28076,4625],{"class":2066},[32,28078,4743],{"class":175},[32,28080,10438],{"class":175},[15,28082,25923],{"id":26131},[11,28084,28085,28086,28089],{},"Nuxt 3 內建支援 TypeScript，設定放在專案根目錄的 ",[29,28087,28088],{},"tsconfig.json","。我習慣在開發時就開型別檢查，出錯當下就看到，不要等到 build。",[11,28091,28092],{},"安裝 Vue 的型別檢查工具：",[22,28094,28096],{"className":2057,"code":28095,"language":2059,"meta":27,"style":27},"npm install -D vue-tsc\n",[29,28097,28098],{"__ignoreMap":27},[32,28099,28100,28102,28104,28107],{"class":34,"line":35},[32,28101,4625],{"class":2066},[32,28103,4650],{"class":175},[32,28105,28106],{"class":351}," -D",[32,28108,28109],{"class":175}," vue-tsc\n",[11,28111,5134,28112,28114,28115,5240],{},[29,28113,23855],{}," 打開 ",[29,28116,28117],{},"typeCheck",[22,28119,28123],{"className":28120,"code":28121,"language":28122,"meta":27,"style":27},"language-ts shiki shiki-themes github-light github-dark-default","export default defineNuxtConfig({\n  typescript: {\n    typeCheck: true\n  }\n})\n","ts",[29,28124,28125,28135,28140,28148,28152],{"__ignoreMap":27},[32,28126,28127,28129,28131,28133],{"class":34,"line":35},[32,28128,5038],{"class":38},[32,28130,5041],{"class":38},[32,28132,26142],{"class":217},[32,28134,1143],{"class":5049},[32,28136,28137],{"class":34,"line":45},[32,28138,28139],{"class":41},"  typescript: {\n",[32,28141,28142,28145],{"class":34,"line":51},[32,28143,28144],{"class":41},"    typeCheck: ",[32,28146,28147],{"class":351},"true\n",[32,28149,28150],{"class":34,"line":90},[32,28151,1203],{"class":41},[32,28153,28154],{"class":34,"line":99},[32,28155,26160],{"class":5049},[15,28157,4853],{"id":28158},"eslint",[11,28160,28161],{},"ESLint 是 JavaScript 的 linter，用來檢查程式碼風格（縮排、單雙引號⋯⋯）並找出語法問題。在團隊裡它的價值更明顯：大家寫出來的東西會長得一樣，code review 就不用花時間在排版上吵架。",[11,28163,28164],{},"它可以直接套用大公司的規則配置（Google、Airbnb），也可以自己客製。這裡選 Nuxt 官方提供的那組。",[151,28166,28168],{"id":28167},"_1-安裝套件","1. 安裝套件",[22,28170,28172],{"className":2057,"code":28171,"language":2059,"meta":27,"style":27},"npm install -D eslint @nuxtjs\u002Feslint-config-typescript eslint-plugin-vue\n",[29,28173,28174],{"__ignoreMap":27},[32,28175,28176,28178,28180,28182,28185,28188],{"class":34,"line":35},[32,28177,4625],{"class":2066},[32,28179,4650],{"class":175},[32,28181,28106],{"class":351},[32,28183,28184],{"class":175}," eslint",[32,28186,28187],{"class":175}," @nuxtjs\u002Feslint-config-typescript",[32,28189,28190],{"class":175}," eslint-plugin-vue\n",[151,28192,28194],{"id":28193},"_2-建立設定檔","2. 建立設定檔",[11,28196,28197,28198,5240],{},"專案根目錄新增 ",[29,28199,25583],{},[22,28201,28203],{"className":24,"code":28202,"language":26,"meta":27,"style":27},"module.exports = {\n  env: {\n    browser: true,\n    es2023: true\n  },\n  extends: ['@nuxtjs\u002Feslint-config-typescript'],\n  parserOptions: {\n    ecmaVersion: 2023,\n    sourceType: 'module'\n  },\n  rules: {\n    'no-undef': 'off'\n  }\n}\n",[29,28204,28205,28217,28222,28231,28238,28242,28252,28257,28267,28275,28279,28284,28294,28298],{"__ignoreMap":27},[32,28206,28207,28209,28211,28213,28215],{"class":34,"line":35},[32,28208,7130],{"class":351},[32,28210,1278],{"class":41},[32,28212,7135],{"class":351},[32,28214,894],{"class":38},[32,28216,703],{"class":41},[32,28218,28219],{"class":34,"line":45},[32,28220,28221],{"class":41},"  env: {\n",[32,28223,28224,28227,28229],{"class":34,"line":51},[32,28225,28226],{"class":41},"    browser: ",[32,28228,6206],{"class":351},[32,28230,547],{"class":41},[32,28232,28233,28236],{"class":34,"line":90},[32,28234,28235],{"class":41},"    es2023: ",[32,28237,28147],{"class":351},[32,28239,28240],{"class":34,"line":99},[32,28241,5639],{"class":41},[32,28243,28244,28247,28250],{"class":34,"line":106},[32,28245,28246],{"class":41},"  extends: [",[32,28248,28249],{"class":175},"'@nuxtjs\u002Feslint-config-typescript'",[32,28251,8212],{"class":41},[32,28253,28254],{"class":34,"line":114},[32,28255,28256],{"class":41},"  parserOptions: {\n",[32,28258,28259,28262,28265],{"class":34,"line":120},[32,28260,28261],{"class":41},"    ecmaVersion: ",[32,28263,28264],{"class":351},"2023",[32,28266,547],{"class":41},[32,28268,28269,28272],{"class":34,"line":127},[32,28270,28271],{"class":41},"    sourceType: ",[32,28273,28274],{"class":175},"'module'\n",[32,28276,28277],{"class":34,"line":132},[32,28278,5639],{"class":41},[32,28280,28281],{"class":34,"line":141},[32,28282,28283],{"class":41},"  rules: {\n",[32,28285,28286,28289,28291],{"class":34,"line":147},[32,28287,28288],{"class":175},"    'no-undef'",[32,28290,5269],{"class":41},[32,28292,28293],{"class":175},"'off'\n",[32,28295,28296],{"class":34,"line":275},[32,28297,1203],{"class":41},[32,28299,28300],{"class":34,"line":282},[32,28301,54],{"class":41},[11,28303,28304,28307,28308,28311,28312,28315],{},[29,28305,28306],{},"extends"," 是陣列，放要擴展的規則配置。",[29,28309,28310],{},"@nuxtjs\u002Feslint-config-typescript"," 是官方針對 Nuxt + TypeScript 的規則；如果專案沒用 TypeScript，改用 ",[29,28313,28314],{},"@nuxtjs\u002Feslint-config"," 即可。",[11,28317,28318,28321,28322,3750,28325,28328],{},[29,28319,28320],{},"no-undef"," 要關掉，是因為 Nuxt 有大量自動匯入的 API（",[29,28323,28324],{},"useRoute",[29,28326,28327],{},"defineNuxtConfig","⋯⋯），ESLint 看不到它們的宣告，開著會滿江紅。",[15,28330,28331],{"id":28331},"預設目錄結構",[11,28333,28334],{},"Nuxt 3 是「約定優於設定」，資料夾名稱本身就是設定，所以要先知道每個資料夾在幹嘛。",[22,28336,28339],{"className":28337,"code":28338,"language":426,"meta":27},[424],"nuxt-app\u002F\n├── .nuxt\u002F\n├── .output\u002F\n├── assets\u002F\n├── components\u002F\n├── composables\u002F\n├── content\u002F\n├── layouts\u002F\n├── middleware\u002F\n├── node_modules\u002F\n├── pages\u002F\n├── plugins\u002F\n├── public\u002F\n└── server\u002F\n    ├── api\u002F\n    ├── routes\u002F\n    └── middleware\u002F\n├── .gitignore\n├── .nuxtignore\n├── app.config.ts\n├── app.vue\n├── nuxt.config.ts\n├── package.json\n└── tsconfig.json\n",[29,28340,28338],{"__ignoreMap":27},[13433,28342,28343,28353],{},[13436,28344,28345],{},[13439,28346,28347,28350],{},[13442,28348,28349],{},"目錄／檔案",[13442,28351,28352],{},"用途",[13449,28354,28355,28368,28380,28390,28400,28410,28433,28442,28452,28465,28475,28491],{},[13439,28356,28357,28362],{},[13454,28358,28359],{},[29,28360,28361],{},".nuxt\u002F",[13454,28363,28364,28365],{},"開發環境下自動產生的 Vue 網站，",[12249,28366,28367],{},"不要手動改",[13439,28369,28370,28375],{},[13454,28371,28372],{},[29,28373,28374],{},".output\u002F",[13454,28376,28377,28378],{},"build 的產出，每次建置都會重新產生，",[12249,28379,28367],{},[13439,28381,28382,28387],{},[13454,28383,28384],{},[29,28385,28386],{},"assets\u002F",[13454,28388,28389],{},"需要被 Vite 處理的靜態資源（CSS\u002FSASS、字型、圖片）",[13439,28391,28392,28397],{},[13454,28393,28394],{},[29,28395,28396],{},"components\u002F",[13454,28398,28399],{},"Vue 元件，Nuxt 會自動載入，不用 import",[13439,28401,28402,28407],{},[13454,28403,28404],{},[29,28405,28406],{},"composables\u002F",[13454,28408,28409],{},"組合式函數，同樣自動載入",[13439,28411,28412,28417],{},[13454,28413,28414],{},[29,28415,28416],{},"content\u002F",[13454,28418,28419,28420,11621,28423,11621,28426,11621,28429,28432],{},"給 Nuxt Content 用，放 ",[29,28421,28422],{},".md",[29,28424,28425],{},".yml",[29,28427,28428],{},".csv",[29,28430,28431],{},".json","，做檔案式 CMS",[13439,28434,28435,28439],{},[13454,28436,28437],{},[29,28438,15560],{},[13454,28440,28441],{},"版面模板，提供跨頁面重複使用的外框",[13439,28443,28444,28449],{},[13454,28445,28446],{},[29,28447,28448],{},"middleware\u002F",[13454,28450,28451],{},"路由中間件，導航到下一頁之前執行（例如權限驗證）",[13439,28453,28454,28459],{},[13454,28455,28456],{},[29,28457,28458],{},"pages\u002F",[13454,28460,28461,28462],{},"頁面，",[12249,28463,28464],{},"檔案結構直接決定路由",[13439,28466,28467,28472],{},[13454,28468,28469],{},[29,28470,28471],{},"plugins\u002F",[13454,28473,28474],{},"外掛，會自動載入",[13439,28476,28477,28482],{},[13454,28478,28479],{},[29,28480,28481],{},"public\u002F",[13454,28483,28484,28485,3750,28488,22856],{},"直接放在網站根目錄的檔案（",[29,28486,28487],{},"robots.txt",[29,28489,28490],{},"favicon.ico",[13439,28492,28493,28498],{},[13454,28494,28495],{},[29,28496,28497],{},"server\u002F",[13454,28499,28500,28501,28504],{},"後端邏輯（API、routes、middleware），",[12249,28502,28503],{},"不自動載入","但支援 HMR",[11,28506,28507],{},"幾個檔案：",[4538,28509,28510,28518,28529,28537,28544],{},[4541,28511,28512,28517],{},[12249,28513,28514],{},[29,28515,28516],{},".nuxtignore","：建置時要忽略的檔案",[4541,28519,28520,28525,28526],{},[12249,28521,28522],{},[29,28523,28524],{},"app.config.ts","：執行期會暴露給客戶端的設定，",[12249,28527,28528],{},"不要放任何機密",[4541,28530,28531,28536],{},[12249,28532,28533],{},[29,28534,28535],{},"app.vue","：Nuxt 3 網站的入口元件",[4541,28538,28539,28543],{},[12249,28540,28541],{},[29,28542,23855],{},"：專案設定檔",[4541,28545,28546,28550,28551,28553],{},[12249,28547,28548],{},[29,28549,28088],{},"：Nuxt 3 會在 ",[29,28552,28361],{}," 底下自動產一份含路徑別名的設定，根目錄這份用來擴展或覆蓋它",[11,28555,28556,7090,28558,28560,28561,17852,28566,28568,28569,28572],{},[29,28557,28386],{},[29,28559,28481],{}," 最容易搞混：",[12249,28562,28563,28565],{},[29,28564,28386],{}," 裡的東西要在 JS\u002FCSS 裡 import 才會被處理",[29,28567,28481],{}," 則是原封不動搬到根目錄，用絕對路徑 ",[29,28570,28571],{},"\u002Fxxx.png"," 引用。",[11,28574,28575,28576,7218,28578,28581],{},"目錄名稱也可以改，在 ",[29,28577,23855],{},[29,28579,28580],{},"dir"," 參數調整，但不是每個都能改，能改的只有官方列出的那幾個。",[15,28583,28584],{"id":28584},"檔案系統路由",[11,28586,28587,28588,28590],{},"Nuxt 的核心特性是「檔案系統路由器（file system router）」——",[29,28589,28458],{}," 底下每一個 Vue 檔案都會產生一個對應的 URL。底層還是 vue-router，只是路由表由檔名自動推出來，不用自己維護。",[151,28592,28593],{"id":28593},"建立第一個頁面",[11,28595,12767,28596,28599,28600,28602],{},[29,28597,28598],{},"pages\u002Findex.vue","，然後把 ",[29,28601,28535],{}," 改成：",[22,28604,28606],{"className":8665,"code":28605,"language":4112,"meta":27,"style":27},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003CNuxtPage \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[29,28607,28608,28616,28624,28633,28641],{"__ignoreMap":27},[32,28609,28610,28612,28614],{"class":34,"line":35},[32,28611,4046],{"class":41},[32,28613,9623],{"class":4049},[32,28615,4062],{"class":41},[32,28617,28618,28620,28622],{"class":34,"line":45},[32,28619,4067],{"class":41},[32,28621,4050],{"class":4049},[32,28623,4062],{"class":41},[32,28625,28626,28628,28631],{"class":34,"line":51},[32,28627,9638],{"class":41},[32,28629,28630],{"class":4049},"NuxtPage",[32,28632,15603],{"class":41},[32,28634,28635,28637,28639],{"class":34,"line":90},[32,28636,9691],{"class":41},[32,28638,4050],{"class":4049},[32,28640,4062],{"class":41},[32,28642,28643,28645,28647],{"class":34,"line":99},[32,28644,4105],{"class":41},[32,28646,9623],{"class":4049},[32,28648,4062],{"class":41},[11,28650,28651,28654,28655,28661],{},[29,28652,28653],{},"\u003CNuxtPage \u002F>"," 就是路由頁面被塞進來的位置。",[12249,28656,28657,28658,28660],{},"一旦建立了 ",[29,28659,28458],{}," 目錄，就一定要有這個元件","，否則什麼都不會顯示。",[151,28663,28664],{"id":28664},"查看自動產生的路由",[11,28666,28667],{},"想看 Nuxt 實際產出的路由表長什麼樣，可以 build 之後翻產物：",[22,28669,28670],{"className":2057,"code":4973,"language":2059,"meta":27,"style":27},[29,28671,28672],{"__ignoreMap":27},[32,28673,28674,28676,28678],{"class":34,"line":35},[32,28675,4625],{"class":2066},[32,28677,4743],{"class":175},[32,28679,4755],{"class":175},[11,28681,28682,28683,28686,28687,28690,28691,28694],{},"打開 ",[29,28684,28685],{},".output\u002Fserver\u002Fchunks\u002Fapp\u002Fserver.mjs","，搜尋 ",[29,28688,28689],{},"const _routes =","，或直接搜尋你剛建立的檔名（例如 ",[29,28692,28693],{},"about","），就會看到類似這樣的結構：",[22,28696,28698],{"className":24,"code":28697,"language":26,"meta":27,"style":27},"const _routes = [\n  {\n    name: \"about\",\n    path: \"\u002Fabout\",\n    component: () => import('.\u002F_nuxt\u002Fabout-xxxxxxxx.mjs')\n  },\n  ...\n]\n",[29,28699,28700,28711,28715,28725,28735,28753,28757,28762],{"__ignoreMap":27},[32,28701,28702,28704,28707,28709],{"class":34,"line":35},[32,28703,6617],{"class":38},[32,28705,28706],{"class":351}," _routes",[32,28708,894],{"class":38},[32,28710,16007],{"class":41},[32,28712,28713],{"class":34,"line":45},[32,28714,6526],{"class":41},[32,28716,28717,28720,28723],{"class":34,"line":51},[32,28718,28719],{"class":41},"    name: ",[32,28721,28722],{"class":175},"\"about\"",[32,28724,547],{"class":41},[32,28726,28727,28730,28733],{"class":34,"line":90},[32,28728,28729],{"class":41},"    path: ",[32,28731,28732],{"class":175},"\"\u002Fabout\"",[32,28734,547],{"class":41},[32,28736,28737,28740,28742,28744,28746,28748,28751],{"class":34,"line":99},[32,28738,28739],{"class":217},"    component",[32,28741,5087],{"class":41},[32,28743,5090],{"class":38},[32,28745,5093],{"class":38},[32,28747,477],{"class":41},[32,28749,28750],{"class":175},"'.\u002F_nuxt\u002Fabout-xxxxxxxx.mjs'",[32,28752,515],{"class":41},[32,28754,28755],{"class":34,"line":106},[32,28756,5639],{"class":41},[32,28758,28759],{"class":34,"line":114},[32,28760,28761],{"class":38},"  ...\n",[32,28763,28764],{"class":34,"line":120},[32,28765,28766],{"class":41},"]\n",[151,28768,28769],{"id":28769},"路由連結",[11,28771,20308,28772,28775,28776,28779],{},[29,28773,28774],{},"\u003CNuxtLink>"," 而不是 ",[29,28777,28778],{},"\u003Ca>","，才會走前端導航不重新載入整頁：",[22,28781,28783],{"className":8665,"code":28782,"language":4112,"meta":27,"style":27},"\u003CNuxtLink to=\"\u002Fabout\">前往 About\u003C\u002FNuxtLink>\n\u003CNuxtLink to=\"\u002Fcontact\">前往 Contact\u003C\u002FNuxtLink>\n",[29,28784,28785,28806],{"__ignoreMap":27},[32,28786,28787,28789,28792,28795,28797,28799,28802,28804],{"class":34,"line":35},[32,28788,4046],{"class":41},[32,28790,28791],{"class":4049},"NuxtLink",[32,28793,28794],{"class":4053}," to",[32,28796,172],{"class":41},[32,28798,28732],{"class":175},[32,28800,28801],{"class":41},">前往 About\u003C\u002F",[32,28803,28791],{"class":4049},[32,28805,4062],{"class":41},[32,28807,28808,28810,28812,28814,28816,28819,28822,28824],{"class":34,"line":45},[32,28809,4046],{"class":41},[32,28811,28791],{"class":4049},[32,28813,28794],{"class":4053},[32,28815,172],{"class":41},[32,28817,28818],{"class":175},"\"\u002Fcontact\"",[32,28820,28821],{"class":41},">前往 Contact\u003C\u002F",[32,28823,28791],{"class":4049},[32,28825,4062],{"class":41},[151,28827,28828],{"id":28828},"動態路由",[11,28830,28831],{},"檔名用中括號包起來就是動態參數：",[22,28833,28836],{"className":28834,"code":28835,"language":426,"meta":27},[424],".\u002Fpages\u002F\n└── users\u002F\n    └── [id].vue\n",[29,28837,28835],{"__ignoreMap":27},[22,28839,28841],{"className":8665,"code":28840,"language":4112,"meta":27,"style":27},"\u003Ctemplate>\n  \u003Cdiv class=\"bg-white py-24\">\n    \u003Cdiv class=\"flex flex-col items-center\">\n      \u003Ch1 class=\"text-3xl text-gray-600\">這裡是 Users 動態路由頁面\u003C\u002Fh1>\n      \u003Cp class=\"my-8 text-3xl text-gray-600\">\n        匹配到的 Id:\n        \u003Cspan class=\"text-5xl font-semibold text-blue-600\">{{ id }}\u003C\u002Fspan>\n      \u003C\u002Fp>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nconst route = useRoute()\nconst { id } = route.params\n\u003C\u002Fscript>\n",[29,28842,28843,28851,28866,28881,28901,28916,28921,28941,28949,28957,28965,28973,28977,28987,29001,29016],{"__ignoreMap":27},[32,28844,28845,28847,28849],{"class":34,"line":35},[32,28846,4046],{"class":41},[32,28848,9623],{"class":4049},[32,28850,4062],{"class":41},[32,28852,28853,28855,28857,28859,28861,28864],{"class":34,"line":45},[32,28854,4067],{"class":41},[32,28856,4050],{"class":4049},[32,28858,9897],{"class":4053},[32,28860,172],{"class":41},[32,28862,28863],{"class":175},"\"bg-white py-24\"",[32,28865,4062],{"class":41},[32,28867,28868,28870,28872,28874,28876,28879],{"class":34,"line":51},[32,28869,9638],{"class":41},[32,28871,4050],{"class":4049},[32,28873,9897],{"class":4053},[32,28875,172],{"class":41},[32,28877,28878],{"class":175},"\"flex flex-col items-center\"",[32,28880,4062],{"class":41},[32,28882,28883,28885,28887,28889,28891,28894,28897,28899],{"class":34,"line":90},[32,28884,18315],{"class":41},[32,28886,12467],{"class":4049},[32,28888,9897],{"class":4053},[32,28890,172],{"class":41},[32,28892,28893],{"class":175},"\"text-3xl text-gray-600\"",[32,28895,28896],{"class":41},">這裡是 Users 動態路由頁面\u003C\u002F",[32,28898,12467],{"class":4049},[32,28900,4062],{"class":41},[32,28902,28903,28905,28907,28909,28911,28914],{"class":34,"line":99},[32,28904,18315],{"class":41},[32,28906,11],{"class":4049},[32,28908,9897],{"class":4053},[32,28910,172],{"class":41},[32,28912,28913],{"class":175},"\"my-8 text-3xl text-gray-600\"",[32,28915,4062],{"class":41},[32,28917,28918],{"class":34,"line":106},[32,28919,28920],{"class":41},"        匹配到的 Id:\n",[32,28922,28923,28925,28927,28929,28931,28934,28937,28939],{"class":34,"line":114},[32,28924,26613],{"class":41},[32,28926,32],{"class":4049},[32,28928,9897],{"class":4053},[32,28930,172],{"class":41},[32,28932,28933],{"class":175},"\"text-5xl font-semibold text-blue-600\"",[32,28935,28936],{"class":41},">{{ id }}\u003C\u002F",[32,28938,32],{"class":4049},[32,28940,4062],{"class":41},[32,28942,28943,28945,28947],{"class":34,"line":120},[32,28944,26635],{"class":41},[32,28946,11],{"class":4049},[32,28948,4062],{"class":41},[32,28950,28951,28953,28955],{"class":34,"line":127},[32,28952,18345],{"class":41},[32,28954,4050],{"class":4049},[32,28956,4062],{"class":41},[32,28958,28959,28961,28963],{"class":34,"line":132},[32,28960,9691],{"class":41},[32,28962,4050],{"class":4049},[32,28964,4062],{"class":41},[32,28966,28967,28969,28971],{"class":34,"line":141},[32,28968,4105],{"class":41},[32,28970,9623],{"class":4049},[32,28972,4062],{"class":41},[32,28974,28975],{"class":34,"line":147},[32,28976,103],{"emptyLinePlaceholder":102},[32,28978,28979,28981,28983,28985],{"class":34,"line":275},[32,28980,4046],{"class":41},[32,28982,4125],{"class":4049},[32,28984,26689],{"class":4053},[32,28986,4062],{"class":41},[32,28988,28989,28991,28994,28996,28999],{"class":34,"line":282},[32,28990,6617],{"class":38},[32,28992,28993],{"class":351}," route",[32,28995,894],{"class":38},[32,28997,28998],{"class":217}," useRoute",[32,29000,27217],{"class":41},[32,29002,29003,29005,29007,29009,29011,29013],{"class":34,"line":295},[32,29004,6617],{"class":38},[32,29006,26747],{"class":41},[32,29008,20882],{"class":351},[32,29010,26758],{"class":41},[32,29012,172],{"class":38},[32,29014,29015],{"class":41}," route.params\n",[32,29017,29018,29020,29022],{"class":34,"line":605},[32,29019,4105],{"class":41},[32,29021,4125],{"class":4049},[32,29023,4062],{"class":41},[151,29025,29026],{"id":29026},"匹配所有層級",[11,29028,29029],{},"三個點加參數名，可以吃掉後面所有層級：",[22,29031,29034],{"className":29032,"code":29033,"language":426,"meta":27},[424],".\u002Fpages\u002F\n└── catch-all\u002F\n    └── [...slug].vue\n",[29,29035,29033],{"__ignoreMap":27},[22,29037,29039],{"className":8665,"code":29038,"language":4112,"meta":27,"style":27},"\u003Ctemplate>\n  \u003Cdiv class=\"bg-white py-24\">\n    \u003Cdiv class=\"flex flex-col items-center\">\n      \u003Ch1 class=\"text-4xl text-gray-800\">這是 catch-all\u002F... 下的頁面\u003C\u002Fh1>\n      \u003Cp class=\"mt-8 text-3xl text-gray-600\">匹配到的 Params:\u003C\u002Fp>\n      \u003Cp class=\"my-4 text-5xl font-semibold text-violet-500\">{{ $route.params.slug }}\u003C\u002Fp>\n      \u003Cspan class=\"text-xl text-gray-400\">每個陣列元素對應一個層級\u003C\u002Fspan>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[29,29040,29041,29049,29063,29077,29097,29117,29137,29157,29165,29173],{"__ignoreMap":27},[32,29042,29043,29045,29047],{"class":34,"line":35},[32,29044,4046],{"class":41},[32,29046,9623],{"class":4049},[32,29048,4062],{"class":41},[32,29050,29051,29053,29055,29057,29059,29061],{"class":34,"line":45},[32,29052,4067],{"class":41},[32,29054,4050],{"class":4049},[32,29056,9897],{"class":4053},[32,29058,172],{"class":41},[32,29060,28863],{"class":175},[32,29062,4062],{"class":41},[32,29064,29065,29067,29069,29071,29073,29075],{"class":34,"line":51},[32,29066,9638],{"class":41},[32,29068,4050],{"class":4049},[32,29070,9897],{"class":4053},[32,29072,172],{"class":41},[32,29074,28878],{"class":175},[32,29076,4062],{"class":41},[32,29078,29079,29081,29083,29085,29087,29090,29093,29095],{"class":34,"line":90},[32,29080,18315],{"class":41},[32,29082,12467],{"class":4049},[32,29084,9897],{"class":4053},[32,29086,172],{"class":41},[32,29088,29089],{"class":175},"\"text-4xl text-gray-800\"",[32,29091,29092],{"class":41},">這是 catch-all\u002F... 下的頁面\u003C\u002F",[32,29094,12467],{"class":4049},[32,29096,4062],{"class":41},[32,29098,29099,29101,29103,29105,29107,29110,29113,29115],{"class":34,"line":99},[32,29100,18315],{"class":41},[32,29102,11],{"class":4049},[32,29104,9897],{"class":4053},[32,29106,172],{"class":41},[32,29108,29109],{"class":175},"\"mt-8 text-3xl text-gray-600\"",[32,29111,29112],{"class":41},">匹配到的 Params:\u003C\u002F",[32,29114,11],{"class":4049},[32,29116,4062],{"class":41},[32,29118,29119,29121,29123,29125,29127,29130,29133,29135],{"class":34,"line":106},[32,29120,18315],{"class":41},[32,29122,11],{"class":4049},[32,29124,9897],{"class":4053},[32,29126,172],{"class":41},[32,29128,29129],{"class":175},"\"my-4 text-5xl font-semibold text-violet-500\"",[32,29131,29132],{"class":41},">{{ $route.params.slug }}\u003C\u002F",[32,29134,11],{"class":4049},[32,29136,4062],{"class":41},[32,29138,29139,29141,29143,29145,29147,29150,29153,29155],{"class":34,"line":114},[32,29140,18315],{"class":41},[32,29142,32],{"class":4049},[32,29144,9897],{"class":4053},[32,29146,172],{"class":41},[32,29148,29149],{"class":175},"\"text-xl text-gray-400\"",[32,29151,29152],{"class":41},">每個陣列元素對應一個層級\u003C\u002F",[32,29154,32],{"class":4049},[32,29156,4062],{"class":41},[32,29158,29159,29161,29163],{"class":34,"line":120},[32,29160,18345],{"class":41},[32,29162,4050],{"class":4049},[32,29164,4062],{"class":41},[32,29166,29167,29169,29171],{"class":34,"line":127},[32,29168,9691],{"class":41},[32,29170,4050],{"class":4049},[32,29172,4062],{"class":41},[32,29174,29175,29177,29179],{"class":34,"line":132},[32,29176,4105],{"class":41},[32,29178,9623],{"class":4049},[32,29180,4062],{"class":41},[11,29182,29183,29184,7090,29187,29190,29191,29194,29195,29198],{},"輸入 ",[29,29185,29186],{},"\u002Fcatch-all\u002Fhello",[29,29188,29189],{},"\u002Fcatch-all\u002Fhello\u002Fworld"," 都會進到這頁，差別在 ",[29,29192,29193],{},"slug"," 是一個",[12249,29196,29197],{},"陣列","，每個元素對應一個層級。",[151,29200,29202],{"id":29201},"_404-頁面","404 頁面",[11,29204,29205,29206,29208],{},"把 catch-all 放在 ",[29,29207,28458],{}," 根目錄，所有沒被匹配到的路由都會交給它：",[22,29210,29212],{"className":8665,"code":29211,"language":4112,"meta":27,"style":27},"\u003Ctemplate>\n  \u003Cdiv class=\"bg-white py-24\">\n    \u003Cdiv class=\"flex flex-col items-center\">\n      \u003Ch1 class=\"text-8xl font-semibold text-red-500\">404\u003C\u002Fh1>\n      \u003Cp class=\"my-8 text-3xl text-gray-800\">Not Found\u003C\u002Fp>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript setup>\nsetResponseStatus(404)\n\u003C\u002Fscript>\n",[29,29213,29214,29222,29236,29250,29270,29290,29298,29306,29314,29318,29328,29339],{"__ignoreMap":27},[32,29215,29216,29218,29220],{"class":34,"line":35},[32,29217,4046],{"class":41},[32,29219,9623],{"class":4049},[32,29221,4062],{"class":41},[32,29223,29224,29226,29228,29230,29232,29234],{"class":34,"line":45},[32,29225,4067],{"class":41},[32,29227,4050],{"class":4049},[32,29229,9897],{"class":4053},[32,29231,172],{"class":41},[32,29233,28863],{"class":175},[32,29235,4062],{"class":41},[32,29237,29238,29240,29242,29244,29246,29248],{"class":34,"line":51},[32,29239,9638],{"class":41},[32,29241,4050],{"class":4049},[32,29243,9897],{"class":4053},[32,29245,172],{"class":41},[32,29247,28878],{"class":175},[32,29249,4062],{"class":41},[32,29251,29252,29254,29256,29258,29260,29263,29266,29268],{"class":34,"line":90},[32,29253,18315],{"class":41},[32,29255,12467],{"class":4049},[32,29257,9897],{"class":4053},[32,29259,172],{"class":41},[32,29261,29262],{"class":175},"\"text-8xl font-semibold text-red-500\"",[32,29264,29265],{"class":41},">404\u003C\u002F",[32,29267,12467],{"class":4049},[32,29269,4062],{"class":41},[32,29271,29272,29274,29276,29278,29280,29283,29286,29288],{"class":34,"line":99},[32,29273,18315],{"class":41},[32,29275,11],{"class":4049},[32,29277,9897],{"class":4053},[32,29279,172],{"class":41},[32,29281,29282],{"class":175},"\"my-8 text-3xl text-gray-800\"",[32,29284,29285],{"class":41},">Not Found\u003C\u002F",[32,29287,11],{"class":4049},[32,29289,4062],{"class":41},[32,29291,29292,29294,29296],{"class":34,"line":106},[32,29293,18345],{"class":41},[32,29295,4050],{"class":4049},[32,29297,4062],{"class":41},[32,29299,29300,29302,29304],{"class":34,"line":114},[32,29301,9691],{"class":41},[32,29303,4050],{"class":4049},[32,29305,4062],{"class":41},[32,29307,29308,29310,29312],{"class":34,"line":120},[32,29309,4105],{"class":41},[32,29311,9623],{"class":4049},[32,29313,4062],{"class":41},[32,29315,29316],{"class":34,"line":127},[32,29317,103],{"emptyLinePlaceholder":102},[32,29319,29320,29322,29324,29326],{"class":34,"line":132},[32,29321,4046],{"class":41},[32,29323,4125],{"class":4049},[32,29325,26689],{"class":4053},[32,29327,4062],{"class":41},[32,29329,29330,29333,29335,29337],{"class":34,"line":141},[32,29331,29332],{"class":217},"setResponseStatus",[32,29334,477],{"class":41},[32,29336,23118],{"class":351},[32,29338,515],{"class":41},[32,29340,29341,29343,29345],{"class":34,"line":147},[32,29342,4105],{"class":41},[32,29344,4125],{"class":4049},[32,29346,4062],{"class":41},[11,29348,29349,29355],{},[12249,29350,29351,29354],{},[29,29352,29353],{},"setResponseStatus(404)"," 不能省","。少了它畫面上雖然寫著 404，HTTP 狀態碼卻是 200，搜尋引擎會把這些不存在的頁面通通收錄進去。",[151,29357,29358],{"id":29358},"多層目錄與巢狀路由",[11,29360,29361],{},"目錄結構直接對應路徑層級：",[22,29363,29366],{"className":29364,"code":29365,"language":426,"meta":27},[424],".\u002Fpages\u002F\n└── posts\u002F\n    ├── [postId]\u002F\n    │   ├── comments\u002F\n    │   │   └── [commentId].vue\n    │   └── index.vue\n    ├── index.vue\n    └── top-[number].vue\n",[29,29367,29365],{"__ignoreMap":27},[11,29369,29370,29371,5240],{},"巢狀路由則是",[12249,29372,29373,29374,29376],{},"同名的資料夾與 ",[29,29375,5141],{}," 檔並存",[22,29378,29381],{"className":29379,"code":29380,"language":426,"meta":27},[424],".\u002Fpages\u002F\n├── docs\u002F\n│   ├── doc-1.vue\n│   └── doc-2.vue\n└── docs.vue\n",[29,29382,29380],{"__ignoreMap":27},[11,29384,29385,29388,29389,29391,29392,29395],{},[29,29386,29387],{},"docs.vue"," 是外層容器（裡面要放 ",[29,29390,28653],{},"），",[29,29393,29394],{},"docs\u002F"," 底下的檔案會被渲染進去。",[11,29397,29398],{},"靠目錄結構與中括號就能涵蓋大部分情境，實務上很少需要手動定義路由規則。",[15,29400,13062],{"id":15939},[11,29402,29403,29404,29406],{},"版面模板放在 ",[29,29405,15560],{},"，同樣會自動載入。",[151,29408,29409],{"id":29409},"預設模板",[11,29411,12767,29412,5240],{},[29,29413,29414],{},"layouts\u002Fdefault.vue",[22,29416,29418],{"className":8665,"code":29417,"language":4112,"meta":27,"style":27},"\u003Ctemplate>\n  \u003Cdiv class=\"bg-sky-100 py-2\">\n    \u003Cp class=\"px-6 py-4 text-2xl text-gray-700\">這是預設的布局，全部頁面都會使用到\u003C\u002Fp>\n    \u003Cslot name=\"header\" \u002F>\n    \u003Cslot \u002F>\n    \u003Cslot name=\"footer\" \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[29,29419,29420,29428,29443,29463,29481,29491,29508,29516],{"__ignoreMap":27},[32,29421,29422,29424,29426],{"class":34,"line":35},[32,29423,4046],{"class":41},[32,29425,9623],{"class":4049},[32,29427,4062],{"class":41},[32,29429,29430,29432,29434,29436,29438,29441],{"class":34,"line":45},[32,29431,4067],{"class":41},[32,29433,4050],{"class":4049},[32,29435,9897],{"class":4053},[32,29437,172],{"class":41},[32,29439,29440],{"class":175},"\"bg-sky-100 py-2\"",[32,29442,4062],{"class":41},[32,29444,29445,29447,29449,29451,29453,29456,29459,29461],{"class":34,"line":51},[32,29446,9638],{"class":41},[32,29448,11],{"class":4049},[32,29450,9897],{"class":4053},[32,29452,172],{"class":41},[32,29454,29455],{"class":175},"\"px-6 py-4 text-2xl text-gray-700\"",[32,29457,29458],{"class":41},">這是預設的布局，全部頁面都會使用到\u003C\u002F",[32,29460,11],{"class":4049},[32,29462,4062],{"class":41},[32,29464,29465,29467,29470,29472,29474,29477,29479],{"class":34,"line":90},[32,29466,9638],{"class":41},[32,29468,29469],{"class":4049},"slot",[32,29471,5668],{"class":4053},[32,29473,172],{"class":41},[32,29475,29476],{"class":175},"\"header\"",[32,29478,8160],{"class":24417},[32,29480,4062],{"class":41},[32,29482,29483,29485,29487,29489],{"class":34,"line":99},[32,29484,9638],{"class":41},[32,29486,29469],{"class":4049},[32,29488,8160],{"class":24417},[32,29490,4062],{"class":41},[32,29492,29493,29495,29497,29499,29501,29504,29506],{"class":34,"line":106},[32,29494,9638],{"class":41},[32,29496,29469],{"class":4049},[32,29498,5668],{"class":4053},[32,29500,172],{"class":41},[32,29502,29503],{"class":175},"\"footer\"",[32,29505,8160],{"class":24417},[32,29507,4062],{"class":41},[32,29509,29510,29512,29514],{"class":34,"line":114},[32,29511,9691],{"class":41},[32,29513,4050],{"class":4049},[32,29515,4062],{"class":41},[32,29517,29518,29520,29522],{"class":34,"line":120},[32,29519,4105],{"class":41},[32,29521,9623],{"class":4049},[32,29523,4062],{"class":41},[11,29525,29526,29527,29530],{},"模板裡一定要有一個未命名的 ",[29,29528,29529],{},"\u003Cslot \u002F>","，那是採用這個模板的頁面內容會出現的位置。",[151,29532,29534],{"id":29533},"在-appvue-掛上-nuxtlayout","在 app.vue 掛上 NuxtLayout",[22,29536,29538],{"className":8665,"code":29537,"language":4112,"meta":27,"style":27},"\u003Ctemplate>\n  \u003Cdiv>\n    \u003Cp class=\"pb-4 text-2xl text-slate-600\">這裡是最外層 app.vue\u003C\u002Fp>\n    \u003CNuxtLayout name=\"default\">\n      \u003Ctemplate #header>\n        \u003Cp class=\"px-6 pt-4 text-xl text-green-500\">這段會放置在 header 插槽\u003C\u002Fp>\n      \u003C\u002Ftemplate>\n      \u003Ctemplate #default>\n        \u003CNuxtPage \u002F>\n      \u003C\u002Ftemplate>\n      \u003Ctemplate #footer>\n        \u003Cp class=\"px-6 pt-4 text-xl text-blue-500\">這段會放置在 footer 插槽\u003C\u002Fp>\n      \u003C\u002Ftemplate>\n    \u003C\u002FNuxtLayout>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[29,29539,29540,29548,29556,29576,29592,29606,29626,29634,29647,29655,29663,29675,29695,29703,29711,29719],{"__ignoreMap":27},[32,29541,29542,29544,29546],{"class":34,"line":35},[32,29543,4046],{"class":41},[32,29545,9623],{"class":4049},[32,29547,4062],{"class":41},[32,29549,29550,29552,29554],{"class":34,"line":45},[32,29551,4067],{"class":41},[32,29553,4050],{"class":4049},[32,29555,4062],{"class":41},[32,29557,29558,29560,29562,29564,29566,29569,29572,29574],{"class":34,"line":51},[32,29559,9638],{"class":41},[32,29561,11],{"class":4049},[32,29563,9897],{"class":4053},[32,29565,172],{"class":41},[32,29567,29568],{"class":175},"\"pb-4 text-2xl text-slate-600\"",[32,29570,29571],{"class":41},">這裡是最外層 app.vue\u003C\u002F",[32,29573,11],{"class":4049},[32,29575,4062],{"class":41},[32,29577,29578,29580,29583,29585,29587,29590],{"class":34,"line":90},[32,29579,9638],{"class":41},[32,29581,29582],{"class":4049},"NuxtLayout",[32,29584,5668],{"class":4053},[32,29586,172],{"class":41},[32,29588,29589],{"class":175},"\"default\"",[32,29591,4062],{"class":41},[32,29593,29594,29596,29598,29601,29604],{"class":34,"line":99},[32,29595,18315],{"class":41},[32,29597,9623],{"class":4049},[32,29599,29600],{"class":41}," #",[32,29602,29603],{"class":4053},"header",[32,29605,4062],{"class":41},[32,29607,29608,29610,29612,29614,29616,29619,29622,29624],{"class":34,"line":106},[32,29609,26613],{"class":41},[32,29611,11],{"class":4049},[32,29613,9897],{"class":4053},[32,29615,172],{"class":41},[32,29617,29618],{"class":175},"\"px-6 pt-4 text-xl text-green-500\"",[32,29620,29621],{"class":41},">這段會放置在 header 插槽\u003C\u002F",[32,29623,11],{"class":4049},[32,29625,4062],{"class":41},[32,29627,29628,29630,29632],{"class":34,"line":114},[32,29629,26635],{"class":41},[32,29631,9623],{"class":4049},[32,29633,4062],{"class":41},[32,29635,29636,29638,29640,29642,29645],{"class":34,"line":120},[32,29637,18315],{"class":41},[32,29639,9623],{"class":4049},[32,29641,29600],{"class":41},[32,29643,29644],{"class":4053},"default",[32,29646,4062],{"class":41},[32,29648,29649,29651,29653],{"class":34,"line":127},[32,29650,26613],{"class":41},[32,29652,28630],{"class":4049},[32,29654,15603],{"class":41},[32,29656,29657,29659,29661],{"class":34,"line":132},[32,29658,26635],{"class":41},[32,29660,9623],{"class":4049},[32,29662,4062],{"class":41},[32,29664,29665,29667,29669,29671,29673],{"class":34,"line":141},[32,29666,18315],{"class":41},[32,29668,9623],{"class":4049},[32,29670,29600],{"class":41},[32,29672,14254],{"class":4053},[32,29674,4062],{"class":41},[32,29676,29677,29679,29681,29683,29685,29688,29691,29693],{"class":34,"line":147},[32,29678,26613],{"class":41},[32,29680,11],{"class":4049},[32,29682,9897],{"class":4053},[32,29684,172],{"class":41},[32,29686,29687],{"class":175},"\"px-6 pt-4 text-xl text-blue-500\"",[32,29689,29690],{"class":41},">這段會放置在 footer 插槽\u003C\u002F",[32,29692,11],{"class":4049},[32,29694,4062],{"class":41},[32,29696,29697,29699,29701],{"class":34,"line":275},[32,29698,26635],{"class":41},[32,29700,9623],{"class":4049},[32,29702,4062],{"class":41},[32,29704,29705,29707,29709],{"class":34,"line":282},[32,29706,18345],{"class":41},[32,29708,29582],{"class":4049},[32,29710,4062],{"class":41},[32,29712,29713,29715,29717],{"class":34,"line":295},[32,29714,9691],{"class":41},[32,29716,4050],{"class":4049},[32,29718,4062],{"class":41},[32,29720,29721,29723,29725],{"class":34,"line":605},[32,29722,4105],{"class":41},[32,29724,9623],{"class":4049},[32,29726,4062],{"class":41},[11,29728,29729,29731,29732,29734],{},[29,29730,16893],{}," 預設就是 ",[29,29733,29644],{},"，這裡明寫出來只是避免誤會。",[151,29736,29737],{"id":29737},"指定其他模板",[11,29739,12767,29740,5240],{},[29,29741,29742],{},"layouts\u002Fcustom.vue",[22,29744,29746],{"className":8665,"code":29745,"language":4112,"meta":27,"style":27},"\u003Ctemplate>\n  \u003Cdiv class=\"bg-rose-100 py-2\">\n    \u003Cp class=\"px-6 py-4 text-2xl text-gray-700\">\n      使用 \u003Cspan class=\"font-bold text-rose-500\">Custom\u003C\u002Fspan> 布局\n    \u003C\u002Fp>\n    \u003Cslot \u002F>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[29,29747,29748,29756,29771,29785,29807,29815,29825,29833],{"__ignoreMap":27},[32,29749,29750,29752,29754],{"class":34,"line":35},[32,29751,4046],{"class":41},[32,29753,9623],{"class":4049},[32,29755,4062],{"class":41},[32,29757,29758,29760,29762,29764,29766,29769],{"class":34,"line":45},[32,29759,4067],{"class":41},[32,29761,4050],{"class":4049},[32,29763,9897],{"class":4053},[32,29765,172],{"class":41},[32,29767,29768],{"class":175},"\"bg-rose-100 py-2\"",[32,29770,4062],{"class":41},[32,29772,29773,29775,29777,29779,29781,29783],{"class":34,"line":51},[32,29774,9638],{"class":41},[32,29776,11],{"class":4049},[32,29778,9897],{"class":4053},[32,29780,172],{"class":41},[32,29782,29455],{"class":175},[32,29784,4062],{"class":41},[32,29786,29787,29790,29792,29794,29796,29799,29802,29804],{"class":34,"line":90},[32,29788,29789],{"class":41},"      使用 \u003C",[32,29791,32],{"class":4049},[32,29793,9897],{"class":4053},[32,29795,172],{"class":41},[32,29797,29798],{"class":175},"\"font-bold text-rose-500\"",[32,29800,29801],{"class":41},">Custom\u003C\u002F",[32,29803,32],{"class":4049},[32,29805,29806],{"class":41},"> 布局\n",[32,29808,29809,29811,29813],{"class":34,"line":99},[32,29810,18345],{"class":41},[32,29812,11],{"class":4049},[32,29814,4062],{"class":41},[32,29816,29817,29819,29821,29823],{"class":34,"line":106},[32,29818,9638],{"class":41},[32,29820,29469],{"class":4049},[32,29822,8160],{"class":24417},[32,29824,4062],{"class":41},[32,29826,29827,29829,29831],{"class":34,"line":114},[32,29828,9691],{"class":41},[32,29830,4050],{"class":4049},[32,29832,4062],{"class":41},[32,29834,29835,29837,29839],{"class":34,"line":120},[32,29836,4105],{"class":41},[32,29838,9623],{"class":4049},[32,29840,4062],{"class":41},[11,29842,29843,29844,5240],{},"在要套用的頁面用 ",[29,29845,15943],{},[22,29847,29849],{"className":8665,"code":29848,"language":4112,"meta":27,"style":27},"\u003Cscript setup>\ndefinePageMeta({\n  layout: 'custom'\n})\n\u003C\u002Fscript>\n",[29,29850,29851,29861,29867,29874,29878],{"__ignoreMap":27},[32,29852,29853,29855,29857,29859],{"class":34,"line":35},[32,29854,4046],{"class":41},[32,29856,4125],{"class":4049},[32,29858,26689],{"class":4053},[32,29860,4062],{"class":41},[32,29862,29863,29865],{"class":34,"line":45},[32,29864,15943],{"class":217},[32,29866,1143],{"class":41},[32,29868,29869,29871],{"class":34,"line":51},[32,29870,15852],{"class":41},[32,29872,29873],{"class":175},"'custom'\n",[32,29875,29876],{"class":34,"line":90},[32,29877,26160],{"class":41},[32,29879,29880,29882,29884],{"class":34,"line":99},[32,29881,4105],{"class":41},[32,29883,4125],{"class":4049},[32,29885,4062],{"class":41},[11,29887,29888,29891,29892,29895,29896,29898,29899,29902,29903,3775],{},[12249,29889,29890],{},"模板名稱一律是 kebab-case","。檔名寫 ",[29,29893,29894],{},"customLayout.vue"," 的話，要傳給 ",[29,29897,15939],{}," 的值是 ",[29,29900,29901],{},"custom-layout","，不是 ",[29,29904,29905],{},"customLayout",[15,29907,29908],{"id":29908},"元件",[11,29910,29911,29913,29914,5240],{},[29,29912,28396],{}," 底下的元件會被自動載入，不需要 import。命名規則是",[12249,29915,29916],{},"用路徑推出元件名稱",[22,29918,29921],{"className":29919,"code":29920,"language":426,"meta":27},[424],".\u002Fcomponents\u002F\n├── AppHeader.vue          → \u003CAppHeader \u002F>\n└── base\u002F\n    └── Button.vue         → \u003CBaseButton \u002F>\n",[29,29922,29920],{"__ignoreMap":27},[11,29924,29925,29926,29929,29930,28775,29933,29936,29937,29939,29940,3775],{},"子資料夾名稱會變成元件名稱的前綴，所以 ",[29,29927,29928],{},"base\u002FButton.vue"," 是 ",[29,29931,29932],{},"\u003CBaseButton \u002F>",[29,29934,29935],{},"\u003CButton \u002F>","。想關掉這個行為，在 ",[29,29938,23855],{}," 設 ",[29,29941,29942],{},"components: { pathPrefix: false }",[11,29944,29945,29946,10450,29949,29952,29953,29956,29957,29959],{},"檔名加上 ",[29,29947,29948],{},".client",[29,29950,29951],{},".server"," 後綴可以決定它只在哪一端渲染，例如 ",[29,29954,29955],{},"Chart.client.vue"," 只在瀏覽器渲染——用到 ",[29,29958,11296],{}," 的元件很需要這個。",[15,29961,25888],{"id":29962},"pinia",[11,29964,29965],{},"Pinia 是取代 Vuex 的全域狀態管理函式庫，比較輕量，適合中小型專案。",[22,29967,29969],{"className":2057,"code":29968,"language":2059,"meta":27,"style":27},"npm install pinia @pinia\u002Fnuxt --save-dev\n",[29,29970,29971],{"__ignoreMap":27},[32,29972,29973,29975,29977,29979,29982],{"class":34,"line":35},[32,29974,4625],{"class":2066},[32,29976,4650],{"class":175},[32,29978,26119],{"class":175},[32,29980,29981],{"class":175}," @pinia\u002Fnuxt",[32,29983,7066],{"class":351},[11,29985,29986,5240],{},[29,29987,23855],{},[22,29989,29991],{"className":28120,"code":29990,"language":28122,"meta":27,"style":27},"export default defineNuxtConfig({\n  modules: [\n    '@pinia\u002Fnuxt'\n  ]\n})\n",[29,29992,29993,30003,30008,30013,30018],{"__ignoreMap":27},[32,29994,29995,29997,29999,30001],{"class":34,"line":35},[32,29996,5038],{"class":38},[32,29998,5041],{"class":38},[32,30000,26142],{"class":217},[32,30002,1143],{"class":5049},[32,30004,30005],{"class":34,"line":45},[32,30006,30007],{"class":41},"  modules: [\n",[32,30009,30010],{"class":34,"line":51},[32,30011,30012],{"class":175},"    '@pinia\u002Fnuxt'\n",[32,30014,30015],{"class":34,"line":90},[32,30016,30017],{"class":41},"  ]\n",[32,30019,30020],{"class":34,"line":99},[32,30021,26160],{"class":5049},[151,30023,30025],{"id":30024},"踩坑pinia-或相依套件裝不起來","踩坑：Pinia 或相依套件裝不起來",[11,30027,30028,30029,30031],{},"裝的時候如果一直卡在相依衝突，在 ",[29,30030,4945],{}," 加上：",[22,30033,30035],{"className":5243,"code":30034,"language":5245,"meta":27,"style":27},"{\n  \"overrides\": {\n    \"vue\": \"latest\"\n  }\n}\n",[29,30036,30037,30041,30048,30058,30062],{"__ignoreMap":27},[32,30038,30039],{"class":34,"line":35},[32,30040,5252],{"class":41},[32,30042,30043,30046],{"class":34,"line":45},[32,30044,30045],{"class":5257},"  \"overrides\"",[32,30047,5261],{"class":41},[32,30049,30050,30053,30055],{"class":34,"line":51},[32,30051,30052],{"class":5257},"    \"vue\"",[32,30054,5269],{"class":41},[32,30056,30057],{"class":175},"\"latest\"\n",[32,30059,30060],{"class":34,"line":90},[32,30061,1203],{"class":41},[32,30063,30064],{"class":34,"line":99},[32,30065,54],{"class":41},[11,30067,30068,30069,30072],{},"原因是 Pinia 與 Nuxt 各自宣告的 vue 版本範圍對不上，npm 解不出一組共同的答案。",[29,30070,30071],{},"overrides"," 直接指定用哪一版，繞過解析。",[15,30074,4847],{"id":30075},"sass",[22,30077,30079],{"className":2057,"code":30078,"language":2059,"meta":27,"style":27},"npm install node-sass sass-loader sass -D\n",[29,30080,30081],{"__ignoreMap":27},[32,30082,30083,30085,30087,30089,30091,30094],{"class":34,"line":35},[32,30084,4625],{"class":2066},[32,30086,4650],{"class":175},[32,30088,8632],{"class":175},[32,30090,8635],{"class":175},[32,30092,30093],{"class":175}," sass",[32,30095,30096],{"class":351}," -D\n",[22,30098,30100],{"className":28120,"code":30099,"language":28122,"meta":27,"style":27},"export default defineNuxtConfig({\n  css: ['~\u002Fassets\u002Fscss\u002Fapp.scss']\n})\n",[29,30101,30102,30112,30122],{"__ignoreMap":27},[32,30103,30104,30106,30108,30110],{"class":34,"line":35},[32,30105,5038],{"class":38},[32,30107,5041],{"class":38},[32,30109,26142],{"class":217},[32,30111,1143],{"class":5049},[32,30113,30114,30117,30120],{"class":34,"line":45},[32,30115,30116],{"class":41},"  css: [",[32,30118,30119],{"class":175},"'~\u002Fassets\u002Fscss\u002Fapp.scss'",[32,30121,28766],{"class":41},[32,30123,30124],{"class":34,"line":51},[32,30125,26160],{"class":5049},[15,30127,30129],{"id":30128},"tailwind-css","Tailwind CSS",[22,30131,30133],{"className":2057,"code":30132,"language":2059,"meta":27,"style":27},"npm install -D @nuxtjs\u002Ftailwindcss\n",[29,30134,30135],{"__ignoreMap":27},[32,30136,30137,30139,30141,30143],{"class":34,"line":35},[32,30138,4625],{"class":2066},[32,30140,4650],{"class":175},[32,30142,28106],{"class":351},[32,30144,30145],{"class":175}," @nuxtjs\u002Ftailwindcss\n",[11,30147,30148,5240],{},[29,30149,23855],{},[22,30151,30153],{"className":28120,"code":30152,"language":28122,"meta":27,"style":27},"export default defineNuxtConfig({\n  modules: ['@nuxtjs\u002Ftailwindcss']\n})\n",[29,30154,30155,30165,30174],{"__ignoreMap":27},[32,30156,30157,30159,30161,30163],{"class":34,"line":35},[32,30158,5038],{"class":38},[32,30160,5041],{"class":38},[32,30162,26142],{"class":217},[32,30164,1143],{"class":5049},[32,30166,30167,30169,30172],{"class":34,"line":45},[32,30168,21503],{"class":41},[32,30170,30171],{"class":175},"'@nuxtjs\u002Ftailwindcss'",[32,30173,28766],{"class":41},[32,30175,30176],{"class":34,"line":51},[32,30177,26160],{"class":5049},[11,30179,30180,5240],{},[29,30181,30182],{},"tailwind.config.js",[22,30184,30186],{"className":24,"code":30185,"language":26,"meta":27,"style":27},"\u002F** @type {import('tailwindcss').Config} *\u002F\nmodule.exports = {\n  content: [\n    '.\u002Fcomponents\u002F**\u002F*.{vue,js,ts}',\n    '.\u002Flayouts\u002F**\u002F*.vue',\n    '.\u002Fpages\u002F**\u002F*.vue',\n    '.\u002Fcomposables\u002F**\u002F*.{js,ts}',\n    '.\u002Fplugins\u002F**\u002F*.{js,ts}',\n    '.\u002Fapp.{js,ts,vue}'\n  ],\n  theme: {\n    extend: {}\n  },\n  plugins: []\n}\n",[29,30187,30188,30202,30214,30219,30226,30233,30240,30247,30254,30259,30264,30269,30274,30278,30283],{"__ignoreMap":27},[32,30189,30190,30193,30196,30199],{"class":34,"line":35},[32,30191,30192],{"class":608},"\u002F** ",[32,30194,30195],{"class":38},"@type",[32,30197,30198],{"class":2066}," {import('tailwindcss').Config}",[32,30200,30201],{"class":608}," *\u002F\n",[32,30203,30204,30206,30208,30210,30212],{"class":34,"line":45},[32,30205,7130],{"class":351},[32,30207,1278],{"class":41},[32,30209,7135],{"class":351},[32,30211,894],{"class":38},[32,30213,703],{"class":41},[32,30215,30216],{"class":34,"line":51},[32,30217,30218],{"class":41},"  content: [\n",[32,30220,30221,30224],{"class":34,"line":90},[32,30222,30223],{"class":175},"    '.\u002Fcomponents\u002F**\u002F*.{vue,js,ts}'",[32,30225,547],{"class":41},[32,30227,30228,30231],{"class":34,"line":99},[32,30229,30230],{"class":175},"    '.\u002Flayouts\u002F**\u002F*.vue'",[32,30232,547],{"class":41},[32,30234,30235,30238],{"class":34,"line":106},[32,30236,30237],{"class":175},"    '.\u002Fpages\u002F**\u002F*.vue'",[32,30239,547],{"class":41},[32,30241,30242,30245],{"class":34,"line":114},[32,30243,30244],{"class":175},"    '.\u002Fcomposables\u002F**\u002F*.{js,ts}'",[32,30246,547],{"class":41},[32,30248,30249,30252],{"class":34,"line":120},[32,30250,30251],{"class":175},"    '.\u002Fplugins\u002F**\u002F*.{js,ts}'",[32,30253,547],{"class":41},[32,30255,30256],{"class":34,"line":127},[32,30257,30258],{"class":175},"    '.\u002Fapp.{js,ts,vue}'\n",[32,30260,30261],{"class":34,"line":132},[32,30262,30263],{"class":41},"  ],\n",[32,30265,30266],{"class":34,"line":141},[32,30267,30268],{"class":41},"  theme: {\n",[32,30270,30271],{"class":34,"line":147},[32,30272,30273],{"class":41},"    extend: {}\n",[32,30275,30276],{"class":34,"line":275},[32,30277,5639],{"class":41},[32,30279,30280],{"class":34,"line":282},[32,30281,30282],{"class":41},"  plugins: []\n",[32,30284,30285],{"class":34,"line":295},[32,30286,54],{"class":41},[11,30288,30289,30292],{},[29,30290,30291],{},"content"," 這個陣列很關鍵——Tailwind 只會產出這些檔案裡真的出現過的 class。漏掉某個資料夾，那裡的樣式在正式環境就會整組消失（開發環境常常看不出來）。",[151,30294,30296],{"id":30295},"搭配-prettier-自動排序","搭配 Prettier 自動排序",[11,30298,30299],{},"class 一多就很難一眼確認有沒有重複，官方的 Prettier 外掛可以自動照推薦順序排列：",[22,30301,30303],{"className":2057,"code":30302,"language":2059,"meta":27,"style":27},"npm install -D prettier-plugin-tailwindcss\n",[29,30304,30305],{"__ignoreMap":27},[32,30306,30307,30309,30311,30313],{"class":34,"line":35},[32,30308,4625],{"class":2066},[32,30310,4650],{"class":175},[32,30312,28106],{"class":351},[32,30314,30315],{"class":175}," prettier-plugin-tailwindcss\n",[15,30317,30319],{"id":30318},"附錄nuxt-2-的-create-nuxt-app","附錄：Nuxt 2 的 create-nuxt-app",[11,30321,30322],{},"Nuxt 2 用的是另一支 CLI，會問一長串問題：",[22,30324,30326],{"className":2057,"code":30325,"language":2059,"meta":27,"style":27},"npx create-nuxt-app my-nuxt-project\n",[29,30327,30328],{"__ignoreMap":27},[32,30329,30330,30332,30335],{"class":34,"line":35},[32,30331,28010],{"class":2066},[32,30333,30334],{"class":175}," create-nuxt-app",[32,30336,30337],{"class":175}," my-nuxt-project\n",[11,30339,30340],{},"逐題的選項大致如下：",[22,30342,30345],{"className":30343,"code":30344,"language":426,"meta":27},[424],"? Project name: my-nuxt-project\n? Programming language: (Use arrow keys)\n❯ JavaScript\n  TypeScript\n\n? Package manager:\n❯ Yarn\n  Npm\n\n? UI framework:\n❯ None\n  Ant Design Vue\n  BalmUI\n  Bootstrap Vue\n  Buefy\n  Chakra UI\n  Element\n  Framevuerk\n  Oruga\n  Tachyons\n  Tailwind CSS\n  Windi CSS\n  Vant\n  View UI\n  Vuetify.js\n\n? Template engine:\n❯ HTML\n  Pug\n\n? Nuxt.js modules: (Press \u003Cspace> to select)\n❯ ◯ Axios - Promise based HTTP client\n  ◯ Progressive Web App (PWA)\n  ◯ Content - Git-based headless CMS\n\n? Linting tools: (Press \u003Cspace> to select)\n❯ ◯ ESLint\n  ◯ Prettier\n  ◯ Lint staged files\n  ◯ StyleLint\n  ◯ Commitlint\n\n? Testing framework:\n❯ None\n  Jest\n  AVA\n  WebdriverIO\n  Nightwatch\n\n? Rendering mode:\n❯ Universal (SSR \u002F SSG)\n  Single Page App\n\n? Deployment target:\n❯ Server (Node.js hosting)\n  Static (Static\u002FJamstack hosting)\n\n? Development tools: (Press \u003Cspace> to select)\n❯ ◯ jsconfig.json\n  ◯ Semantic Pull Requests\n  ◯ Dependabot\n",[29,30346,30344],{"__ignoreMap":27},[11,30348,30349],{},"ESLint 與 Prettier 兩個都會出現在 linting 那題，它們的分工是：",[4538,30351,30352,30357],{},[4541,30353,30354,30356],{},[12249,30355,4853],{},"：靜態分析工具，找出程式碼裡的問題，也管風格",[4541,30358,30359,30362],{},[12249,30360,30361],{},"Prettier","：純格式化工具，只管排版，不管品質",[11,30364,30365],{},"通常兩個一起用——ESLint 負責分析，Prettier 負責排版。",[151,30367,30369],{"id":30368},"nuxt-2-裝-pinia","Nuxt 2 裝 Pinia",[22,30371,30372],{"className":2057,"code":25996,"language":2059,"meta":27,"style":27},[29,30373,30374],{"__ignoreMap":27},[32,30375,30376,30378,30380],{"class":34,"line":35},[32,30377,4625],{"class":2066},[32,30379,4650],{"class":175},[32,30381,26007],{"class":175},[151,30383,30385],{"id":30384},"nuxt-2-裝-sass","Nuxt 2 裝 Sass",[22,30387,30389],{"className":2057,"code":30388,"language":2059,"meta":27,"style":27},"npm install node-sass sass-loader --save-dev\n",[29,30390,30391],{"__ignoreMap":27},[32,30392,30393,30395,30397,30399,30401],{"class":34,"line":35},[32,30394,4625],{"class":2066},[32,30396,4650],{"class":175},[32,30398,8632],{"class":175},[32,30400,8635],{"class":175},[32,30402,7066],{"class":351},[11,30404,5134,30405,7218,30407,30031],{},[29,30406,21484],{},[29,30408,7225],{},[22,30410,30412],{"className":24,"code":30411,"language":26,"meta":27,"style":27},"build: {\n  loaders: {\n    scss: { implementation: require('sass') }\n  }\n}\n",[29,30413,30414,30420,30427,30451,30455],{"__ignoreMap":27},[32,30415,30416,30418],{"class":34,"line":35},[32,30417,7225],{"class":2066},[32,30419,5261],{"class":41},[32,30421,30422,30425],{"class":34,"line":45},[32,30423,30424],{"class":2066},"  loaders",[32,30426,5261],{"class":41},[32,30428,30429,30432,30435,30438,30440,30443,30445,30448],{"class":34,"line":51},[32,30430,30431],{"class":2066},"    scss",[32,30433,30434],{"class":41},": { ",[32,30436,30437],{"class":2066},"implementation",[32,30439,5269],{"class":41},[32,30441,30442],{"class":217},"require",[32,30444,477],{"class":41},[32,30446,30447],{"class":175},"'sass'",[32,30449,30450],{"class":41},") }\n",[32,30452,30453],{"class":34,"line":90},[32,30454,1203],{"class":41},[32,30456,30457],{"class":34,"line":99},[32,30458,54],{"class":41},[11,30460,30461],{},"TypeScript 版本：",[22,30463,30465],{"className":28120,"code":30464,"language":28122,"meta":27,"style":27},"import { Configuration } from '@nuxt\u002Ftypes'\n\nconst config: Configuration = {\n  build: {\n    loaders: {\n      scss: { implementation: require('sass') }\n    }\n  }\n}\n\nexport default config\n",[29,30466,30467,30479,30483,30498,30502,30507,30520,30524,30528,30532,30536],{"__ignoreMap":27},[32,30468,30469,30471,30474,30476],{"class":34,"line":35},[32,30470,8794],{"class":38},[32,30472,30473],{"class":41}," { Configuration } ",[32,30475,8850],{"class":38},[32,30477,30478],{"class":175}," '@nuxt\u002Ftypes'\n",[32,30480,30481],{"class":34,"line":45},[32,30482,103],{"emptyLinePlaceholder":102},[32,30484,30485,30487,30489,30491,30494,30496],{"class":34,"line":51},[32,30486,6617],{"class":38},[32,30488,3425],{"class":351},[32,30490,322],{"class":38},[32,30492,30493],{"class":2066}," Configuration",[32,30495,894],{"class":38},[32,30497,703],{"class":41},[32,30499,30500],{"class":34,"line":90},[32,30501,24869],{"class":41},[32,30503,30504],{"class":34,"line":99},[32,30505,30506],{"class":41},"    loaders: {\n",[32,30508,30509,30512,30514,30516,30518],{"class":34,"line":106},[32,30510,30511],{"class":41},"      scss: { implementation: ",[32,30513,30442],{"class":217},[32,30515,477],{"class":41},[32,30517,30447],{"class":175},[32,30519,30450],{"class":41},[32,30521,30522],{"class":34,"line":114},[32,30523,5591],{"class":41},[32,30525,30526],{"class":34,"line":120},[32,30527,1203],{"class":41},[32,30529,30530],{"class":34,"line":127},[32,30531,54],{"class":41},[32,30533,30534],{"class":34,"line":132},[32,30535,103],{"emptyLinePlaceholder":102},[32,30537,30538,30540,30542],{"class":34,"line":141},[32,30539,5038],{"class":38},[32,30541,5041],{"class":38},[32,30543,30544],{"class":41}," config\n",[15,30546,25664],{"id":25664},[11,30548,30549,30550,30556,30557,30559,30560,7090,30563,30565,30566,30569],{},"這篇整理完沒多久，Nuxt 4 就成了預設版本，上面有好幾段已經不能照抄了——最明顯的是",[12249,30551,30552,30553],{},"整個 srcDir 搬進 ",[29,30554,30555],{},"app\u002F","，還有 ",[29,30558,25583],{}," 這種 legacy config、",[29,30561,30562],{},"@nuxtjs\u002Ftailwindcss",[29,30564,30182],{}," 都換掉了。老實說 ESLint 那段在寫的時候就已經該換成 ",[29,30567,30568],{},"@nuxt\u002Feslint"," 了，這裡留著是當時的實況。",[11,30571,30572,30573,3775],{},"新的做法另外寫在 ",[412,30574,30576],{"href":30575},"\u002Fposts\u002Fnuxt4-project-setup","Nuxt 4 現在怎麼開專案",[15,30578,4536],{"id":4536},[4538,30580,30581,30588,30595,30602,30609],{},[4541,30582,30583],{},[412,30584,30587],{"href":30585,"rel":30586},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fgetting-started\u002Finstallation",[416],"Nuxt 3 官方文件",[4541,30589,30590],{},[412,30591,30594],{"href":30592,"rel":30593},"https:\u002F\u002Fithelp.ithome.com.tw\u002Farticles\u002F10295432",[416],"iThome：Nuxt 3 教學",[4541,30596,30597],{},[412,30598,30601],{"href":30599,"rel":30600},"https:\u002F\u002Fwww.penguin-cho.com\u002Fposts\u002Fnuxt3-pinia",[416],"Nuxt 3 + Pinia",[4541,30603,30604],{},[412,30605,30608],{"href":30606,"rel":30607},"https:\u002F\u002Fpinia.vuejs.org\u002Fssr\u002Fnuxt.html",[416],"Pinia 官方文件（Nuxt SSR）",[4541,30610,30611],{},[412,30612,30615],{"href":30613,"rel":30614},"https:\u002F\u002Fhackmd.io\u002F@Yan06\u002FSJgkRrhej",[416],"Nuxt 3 配置入門",[368,30617,30618],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}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 .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}html pre.shiki code .sAjPK, html code.shiki .sAjPK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FFA198;--shiki-dark-font-style:italic}html pre.shiki code .swgif, html code.shiki .swgif{--shiki-default:#005CC5;--shiki-dark:#7EE787}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}",{"title":27,"searchDepth":51,"depth":51,"links":30620},[30621,30625,30626,30630,30631,30640,30645,30646,30649,30650,30653,30657,30658],{"id":27990,"depth":45,"text":27991,"children":30622},[30623,30624],{"id":27997,"depth":51,"text":27997},{"id":25360,"depth":51,"text":25360},{"id":26131,"depth":45,"text":25923},{"id":28158,"depth":45,"text":4853,"children":30627},[30628,30629],{"id":28167,"depth":51,"text":28168},{"id":28193,"depth":51,"text":28194},{"id":28331,"depth":45,"text":28331},{"id":28584,"depth":45,"text":28584,"children":30632},[30633,30634,30635,30636,30637,30638,30639],{"id":28593,"depth":51,"text":28593},{"id":28664,"depth":51,"text":28664},{"id":28769,"depth":51,"text":28769},{"id":28828,"depth":51,"text":28828},{"id":29026,"depth":51,"text":29026},{"id":29201,"depth":51,"text":29202},{"id":29358,"depth":51,"text":29358},{"id":15939,"depth":45,"text":13062,"children":30641},[30642,30643,30644],{"id":29409,"depth":51,"text":29409},{"id":29533,"depth":51,"text":29534},{"id":29737,"depth":51,"text":29737},{"id":29908,"depth":45,"text":29908},{"id":29962,"depth":45,"text":25888,"children":30647},[30648],{"id":30024,"depth":51,"text":30025},{"id":30075,"depth":45,"text":4847},{"id":30128,"depth":45,"text":30129,"children":30651},[30652],{"id":30295,"depth":51,"text":30296},{"id":30318,"depth":45,"text":30319,"children":30654},[30655,30656],{"id":30368,"depth":51,"text":30369},{"id":30384,"depth":51,"text":30385},{"id":25664,"depth":45,"text":25664},{"id":4536,"depth":45,"text":4536},"從 nuxi 建立 Nuxt 3 專案開始的完整筆記，包含 TypeScript 型別檢查與 ESLint 設定、預設目錄結構每個資料夾的用途、檔案系統路由的動態與 catch-all 寫法、Layout 與插槽的用法，以及 Pinia、Sass、Tailwind 的安裝與踩到的 overrides 問題。",{},"2025-07-24",[],{"title":27985,"description":30659},"posts\u002F20250724.nuxt3-setup-notes",[30666,25743,25888,30129,4853,25923],"Nuxt3","2026-08-18","B_snXMolDB2qVI3vgJQjmpW5aHiExq8OCuUOZ0m5fhw",{"id":30670,"title":25675,"body":30671,"category":378,"description":31200,"draft":380,"extension":381,"image":382,"meta":31201,"navigation":102,"path":25674,"publishedAt":31202,"relatedExperiments":31203,"seo":31204,"stem":31205,"tags":31206,"updatedAt":31202,"__hash__":31207},"posts\u002Fposts\u002F20260506.vite-vue3-tailwind4-shadcn-vue.md",{"type":8,"value":30672,"toc":31181},[30673,30676,30699,30703,30707,30718,30721,30725,30736,30740,30743,30746,30757,30760,30764,30767,30778,30781,30783,30807,30811,30818,30821,30830,30833,30849,30852,30918,30926,30929,30952,30955,30966,30984,30987,31000,31006,31014,31017,31023,31026,31029,31037,31043,31047,31050,31053,31058,31061,31065,31068,31072,31075,31078,31080,31083,31087,31089,31095,31098,31101,31120,31123,31139,31142,31178],[15,30674,30675],{"id":30675},"專案環境",[4538,30677,30678,30681,30684,30686,30688,30690,30693,30696],{},[4541,30679,30680],{},"Vite 7",[4541,30682,30683],{},"Vue 3",[4541,30685,25923],{},[4541,30687,25888],{},[4541,30689,4841],{},[4541,30691,30692],{},"unplugin-vue-router",[4541,30694,30695],{},"Tailwind CSS 4",[4541,30697,30698],{},"shadcn-vue",[15,30700,30702],{"id":30701},"_1-vite-與-vike-的差異","1. Vite 與 Vike 的差異",[151,30704,30706],{"id":30705},"vite","Vite",[4538,30708,30709,30712,30715],{},[4541,30710,30711],{},"前端建置工具（Build Tool）",[4541,30713,30714],{},"提供快速開發伺服器（HMR）",[4541,30716,30717],{},"不內建 Router、SSR",[11,30719,30720],{},"適合： - SPA - 後台系統 - Vue \u002F React 專案",[151,30722,30724],{"id":30723},"vike","Vike",[4538,30726,30727,30730,30733],{},[4541,30728,30729],{},"建立在 Vite 之上的 Web Framework",[4541,30731,30732],{},"提供 SSR、Routing、SEO 等能力",[4541,30734,30735],{},"類似 Next.js（React）或 Nuxt（Vue）",[15,30737,30739],{"id":30738},"_2-vue-專案一定要安裝-vue-router-嗎","2. Vue 專案一定要安裝 vue-router 嗎？",[11,30741,30742],{},"如果只有一個畫面，可以不用。",[11,30744,30745],{},"只要有：",[4538,30747,30748,30751,30754],{},[4541,30749,30750],{},"\u002Flogin",[4541,30752,30753],{},"\u002Fdashboard",[4541,30755,30756],{},"\u002Fhouse\u002F:id",[11,30758,30759],{},"就需要 vue-router。",[151,30761,30763],{"id":30762},"unplugin-vue-router-不是-router","unplugin-vue-router 不是 Router",[11,30765,30766],{},"它只是：",[4538,30768,30769,30772,30775],{},[4541,30770,30771],{},"自動產生 routes",[4541,30773,30774],{},"提供型別安全",[4541,30776,30777],{},"使用檔案式路由",[11,30779,30780],{},"底層仍然是 vue-router。",[11,30782,6399],{},[22,30784,30786],{"className":2057,"code":30785,"language":2059,"meta":27,"style":27},"npm install vue-router\nnpm install -D unplugin-vue-router\n",[29,30787,30788,30796],{"__ignoreMap":27},[32,30789,30790,30792,30794],{"class":34,"line":35},[32,30791,4625],{"class":2066},[32,30793,4650],{"class":175},[32,30795,9164],{"class":175},[32,30797,30798,30800,30802,30804],{"class":34,"line":45},[32,30799,4625],{"class":2066},[32,30801,4650],{"class":175},[32,30803,28106],{"class":351},[32,30805,30806],{"class":175}," unplugin-vue-router\n",[15,30808,30810],{"id":30809},"_3-tailwind-css-4-的重大變更","3. Tailwind CSS 4 的重大變更",[11,30812,30813,30814,30817],{},"Tailwind CSS 4 已經不能再把 ",[29,30815,30816],{},"tailwindcss"," 當作 PostCSS Plugin 使用。",[11,30819,30820],{},"以前：",[22,30822,30824],{"className":24,"code":30823,"language":26,"meta":27,"style":27},"tailwindcss\n",[29,30825,30826],{"__ignoreMap":27},[32,30827,30828],{"class":34,"line":35},[32,30829,30823],{"class":41},[11,30831,30832],{},"現在需要：",[22,30834,30836],{"className":2057,"code":30835,"language":2059,"meta":27,"style":27},"npm install -D @tailwindcss\u002Fpostcss\n",[29,30837,30838],{"__ignoreMap":27},[32,30839,30840,30842,30844,30846],{"class":34,"line":35},[32,30841,4625],{"class":2066},[32,30843,4650],{"class":175},[32,30845,28106],{"class":351},[32,30847,30848],{"class":175}," @tailwindcss\u002Fpostcss\n",[11,30850,30851],{},"PostCSS：",[22,30853,30855],{"className":24,"code":30854,"language":26,"meta":27,"style":27},"import tailwindcss from \"@tailwindcss\u002Fpostcss\"\nimport autoprefixer from \"autoprefixer\"\n\nexport default {\n  plugins: {\n    tailwindcss,\n    autoprefixer,\n  },\n}\n",[29,30856,30857,30869,30881,30885,30893,30898,30903,30908,30914],{"__ignoreMap":27},[32,30858,30859,30861,30864,30866],{"class":34,"line":35},[32,30860,8794],{"class":38},[32,30862,30863],{"class":41}," tailwindcss ",[32,30865,8850],{"class":38},[32,30867,30868],{"class":175}," \"@tailwindcss\u002Fpostcss\"\n",[32,30870,30871,30873,30876,30878],{"class":34,"line":45},[32,30872,8794],{"class":38},[32,30874,30875],{"class":41}," autoprefixer ",[32,30877,8850],{"class":38},[32,30879,30880],{"class":175}," \"autoprefixer\"\n",[32,30882,30883],{"class":34,"line":51},[32,30884,103],{"emptyLinePlaceholder":102},[32,30886,30887,30889,30891],{"class":34,"line":90},[32,30888,5038],{"class":38},[32,30890,5041],{"class":38},[32,30892,703],{"class":5049},[32,30894,30895],{"class":34,"line":99},[32,30896,30897],{"class":41},"  plugins: {\n",[32,30899,30900],{"class":34,"line":106},[32,30901,30902],{"class":41},"    tailwindcss,\n",[32,30904,30905],{"class":34,"line":114},[32,30906,30907],{"class":41},"    autoprefixer,\n",[32,30909,30910,30912],{"class":34,"line":120},[32,30911,9758],{"class":41},[32,30913,547],{"class":5049},[32,30915,30916],{"class":34,"line":127},[32,30917,54],{"class":5049},[15,30919,30921,30922,30925],{"id":30920},"_4-packagejson-使用-type-module-要注意","4. package.json 使用 ",[29,30923,30924],{},"\"type\": \"module\""," 要注意",[11,30927,30928],{},"如果 package.json：",[22,30930,30932],{"className":5243,"code":30931,"language":5245,"meta":27,"style":27},"{\n  \"type\": \"module\"\n}\n",[29,30933,30934,30938,30948],{"__ignoreMap":27},[32,30935,30936],{"class":34,"line":35},[32,30937,5252],{"class":41},[32,30939,30940,30943,30945],{"class":34,"line":45},[32,30941,30942],{"class":5257},"  \"type\"",[32,30944,5269],{"class":41},[32,30946,30947],{"class":175},"\"module\"\n",[32,30949,30950],{"class":34,"line":51},[32,30951,54],{"class":41},[11,30953,30954],{},"代表：",[4538,30956,30957,30963],{},[4541,30958,30959,30962],{},[29,30960,30961],{},".js"," 會被視為 ESM",[4541,30964,30965],{},"不可使用：",[22,30967,30969],{"className":24,"code":30968,"language":26,"meta":27,"style":27},"module.exports = {}\n",[29,30970,30971],{"__ignoreMap":27},[32,30972,30973,30975,30977,30979,30981],{"class":34,"line":35},[32,30974,7130],{"class":351},[32,30976,1278],{"class":41},[32,30978,7135],{"class":351},[32,30980,894],{"class":38},[32,30982,30983],{"class":41}," {}\n",[11,30985,30986],{},"必須改成：",[22,30988,30990],{"className":24,"code":30989,"language":26,"meta":27,"style":27},"export default {}\n",[29,30991,30992],{"__ignoreMap":27},[32,30993,30994,30996,30998],{"class":34,"line":35},[32,30995,5038],{"class":38},[32,30997,5041],{"class":38},[32,30999,30983],{"class":5049},[11,31001,31002,31003,3775],{},"或者把設定檔改成 ",[29,31004,31005],{},".cjs",[15,31007,31009,31010,31013],{"id":31008},"_5-為什麼-npx-tailwindcss-init-p-失敗","5. 為什麼 ",[29,31011,31012],{},"npx tailwindcss init -p"," 失敗？",[11,31015,31016],{},"錯誤：",[22,31018,31021],{"className":31019,"code":31020,"language":426,"meta":27},[424],"npm error could not determine executable to run\n",[29,31022,31020],{"__ignoreMap":27},[11,31024,31025],{},"在 Tailwind CSS 4 已經不一定需要依賴這個指令。",[11,31027,31028],{},"可以直接：",[4538,31030,31031,31034],{},[4541,31032,31033],{},"手動建立 Tailwind Config",[4541,31035,31036],{},"或直接使用官方新的設定方式",[11,31038,31039,31040,3775],{},"因此不用執著於 ",[29,31041,31042],{},"init -p",[15,31044,31046],{"id":31045},"_6-shadcn-vue-是否可以使用","6. shadcn-vue 是否可以使用？",[11,31048,31049],{},"可以。",[11,31051,31052],{},"React 使用：",[4538,31054,31055],{},[4541,31056,31057],{},"shadcn\u002Fui",[11,31059,31060],{},"Vue 使用：",[4538,31062,31063],{},[4541,31064,30698],{},[11,31066,31067],{},"不能直接把 React 的 shadcn\u002Fui 放進 Vue 專案。",[15,31069,31071],{"id":31070},"_7-element-plus-shadcn-vue","7. Element Plus → shadcn-vue",[11,31073,31074],{},"常見對照：",[11,31076,31077],{},"Element Plus   shadcn-vue",[10525,31079],{},[11,31081,31082],{},"el-card        Card\nel-button      Button\nel-input       Input\nel-select      Select\nel-dialog      Dialog\nel-slider      Slider\nel-table       Table",[15,31084,31086],{"id":31085},"_8-git-push-出現-invalid-username-or-token","8. Git Push 出現 Invalid username or token",[11,31088,2427],{},[22,31090,31093],{"className":31091,"code":31092,"language":426,"meta":27},[424],"Password authentication is not supported\n",[29,31094,31092],{"__ignoreMap":27},[11,31096,31097],{},"代表目前還在使用 HTTPS。",[11,31099,31100],{},"建議改用 SSH：",[22,31102,31104],{"className":2057,"code":31103,"language":2059,"meta":27,"style":27},"git remote set-url origin git@github.com:USER\u002FREPO.git\n",[29,31105,31106],{"__ignoreMap":27},[32,31107,31108,31110,31112,31115,31117],{"class":34,"line":35},[32,31109,2126],{"class":2066},[32,31111,12725],{"class":175},[32,31113,31114],{"class":175}," set-url",[32,31116,12730],{"class":175},[32,31118,31119],{"class":175}," git@github.com:USER\u002FREPO.git\n",[11,31121,31122],{},"測試：",[22,31124,31126],{"className":2057,"code":31125,"language":2059,"meta":27,"style":27},"ssh -T git@github.com\n",[29,31127,31128],{"__ignoreMap":27},[32,31129,31130,31133,31136],{"class":34,"line":35},[32,31131,31132],{"class":2066},"ssh",[32,31134,31135],{"class":351}," -T",[32,31137,31138],{"class":175}," git@github.com\n",[15,31140,31141],{"id":31141},"本次重點整理",[4538,31143,31144,31147,31150,31153,31156,31162,31172,31175],{},[4541,31145,31146],{},"Vite 是 Build Tool，不是 Framework。",[4541,31148,31149],{},"Vike 提供 SSR 與 Routing。",[4541,31151,31152],{},"Vue 多頁面專案需要 vue-router。",[4541,31154,31155],{},"unplugin-vue-router 只是自動產生 routes。",[4541,31157,31158,31159,3775],{},"Tailwind CSS 4 已改用 ",[29,31160,31161],{},"@tailwindcss\u002Fpostcss",[4541,31163,31164,31166,31167,31169,31170,3775],{},[29,31165,30924],{}," 專案要使用 ESM (",[29,31168,15922],{},") 或 ",[29,31171,31005],{},[4541,31173,31174],{},"Vue 專案請使用 shadcn-vue，不是 React 的 shadcn\u002Fui。",[4541,31176,31177],{},"GitHub 建議使用 SSH 推送，不要使用密碼。",[368,31179,31180],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}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 .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .swgif, html code.shiki .swgif{--shiki-default:#005CC5;--shiki-dark:#7EE787}",{"title":27,"searchDepth":51,"depth":51,"links":31182},[31183,31184,31188,31191,31192,31194,31196,31197,31198,31199],{"id":30675,"depth":45,"text":30675},{"id":30701,"depth":45,"text":30702,"children":31185},[31186,31187],{"id":30705,"depth":51,"text":30706},{"id":30723,"depth":51,"text":30724},{"id":30738,"depth":45,"text":30739,"children":31189},[31190],{"id":30762,"depth":51,"text":30763},{"id":30809,"depth":45,"text":30810},{"id":30920,"depth":45,"text":31193},"4. package.json 使用 \"type\": \"module\" 要注意",{"id":31008,"depth":45,"text":31195},"5. 為什麼 npx tailwindcss init -p 失敗？",{"id":31045,"depth":45,"text":31046},{"id":31070,"depth":45,"text":31071},{"id":31085,"depth":45,"text":31086},{"id":31141,"depth":45,"text":31141},"記錄用 Vite、Vue 3、Tailwind CSS 4、shadcn-vue 建專案時踩到的坑，包含 vue-router 安裝、Tailwind 4 的 PostCSS 變更、ESM 設定與 Git SSH 推送問題。",{},"2026-05-06",[],{"title":25675,"description":31200},"posts\u002F20260506.vite-vue3-tailwind4-shadcn-vue",[30706,25743,30695,30698],"ipF_6dy1h87-9ZQ-SEwWcghcwFuGrYJpFiS4LncEL0M",{"id":31209,"title":31210,"body":31211,"category":31302,"description":31303,"draft":380,"extension":381,"image":382,"meta":31304,"navigation":102,"path":31305,"publishedAt":392,"relatedExperiments":31306,"seo":31308,"stem":31309,"tags":31310,"updatedAt":6716,"__hash__":31313},"posts\u002Fposts\u002F20260805.hello.md","自由實驗室開張",{"type":8,"value":31212,"toc":31299},[31213,31216,31219,31222,31225,31228,31296],[11,31214,31215],{},"很開心，終於有一個屬於自己的部落格了！\n其實從好幾年前開始，我就一直想做自己的部落格，只是每次做到一半，都覺得好像少了點什麼。\n光是名字就想了很久，也因此拖了很長一段時間。",[11,31217,31218],{},"這次原本也沒有想得這麼複雜，只是想做一個放小工具的站台。\n結果做到一半才發現，站名不但太泛用，還剛好跟其他同類型的網站撞名。\n看到的時候只覺得……\n真的超級無敵尷尬。\n最後乾脆決定整個打掉重來。",[11,31220,31221],{},"重新整理的過程中，我也開始覺得，如果只拿來放小工具好像有點可惜。\n既然本來就有自己的部落格，也有很多平常寫下來的東西，那不如全部放在一起。\n文章、小工具、小遊戲、互動實驗，甚至一些突然想到、單純只是想做看看的東西，都可以丟進來。\n不用特別限制它一定要是什麼。\n「自由實驗室」也就這樣慢慢成形了。\n所以往後這個網站上，應該會出現很多奇奇怪怪的東西。\n技術文章可能旁邊突然出現一個小遊戲，過幾天又多了一個不知道拿來幹嘛的小工具。\n想做什麼就做什麼。\n大概就是這個地方存在的原因。",[15,31223,31224],{"id":31224},"技術選擇",[11,31226,31227],{},"這次也順便把整個網站重新整理了一次，目前主要使用這些技術：",[13433,31229,31230,31242],{},[13436,31231,31232],{},[13439,31233,31234,31236,31239],{},[13442,31235,13544],{},[13442,31237,31238],{},"選擇",[13442,31240,31241],{},"原因",[13449,31243,31244,31255,31266,31275,31285],{},[13439,31245,31246,31249,31252],{},[13454,31247,31248],{},"框架",[13454,31250,31251],{},"Nuxt 4",[13454,31253,31254],{},"想繼續熟悉 Nuxt，也很適合拿來做內容型網站",[13439,31256,31257,31260,31263],{},[13454,31258,31259],{},"樣式",[13454,31261,31262],{},"Tailwind CSS v4",[13454,31264,31265],{},"寫樣式方便，網站要慢慢調整也比較自由",[13439,31267,31268,31270,31272],{},[13454,31269,29908],{},[13454,31271,30698],{},[13454,31273,31274],{},"元件直接放在專案裡，需要修改時可以自己調整",[13439,31276,31277,31279,31282],{},[13454,31278,23570],{},[13454,31280,31281],{},"Zod",[13454,31283,31284],{},"可以統一處理資料與 Content frontmatter 的格式驗證",[13439,31286,31287,31290,31293],{},[13454,31288,31289],{},"部署",[13454,31291,31292],{},"Cloudflare Workers",[13454,31294,31295],{},"部署方便，也可以直接搭配自己的網域",[11,31297,31298],{},"目前網站還在慢慢整理中。\n以前寫過的文章也會陸續搬回來，有些甚至已經是很多年前留下來的內容了。\n就先一點一點整理吧。\n至少這次，終於真的把它做出來了。",{"title":27,"searchDepth":51,"depth":51,"links":31300},[31301],{"id":31224,"depth":45,"text":31224},"project","原本只是想做一個小工具站，做到一半卻因為撞名決定整個重來，最後變成現在的自由實驗室。",{},"\u002Fposts\u002Fhello",[31307],"bmi-calculator",{"title":31210,"description":31303},"posts\u002F20260805.hello",[15981,31311,31312],"Cloudflare","重構","GDqPOiGjUiqmnYHJ-q9L4C3qENyU0WuYEWnCz6LuxhA",{"id":31315,"title":31316,"body":31317,"category":378,"description":32595,"draft":380,"extension":381,"image":382,"meta":32596,"navigation":102,"path":30575,"publishedAt":30667,"relatedExperiments":32597,"seo":32598,"stem":32599,"tags":32600,"updatedAt":30667,"__hash__":32603},"posts\u002Fposts\u002F20260818.nuxt4-project-setup.md","Nuxt 4 現在怎麼開專案：app\u002F 目錄、@nuxt\u002Feslint 與 Tailwind 4",{"type":8,"value":31318,"toc":32579},[31319,31335,31342,31345,31354,31376,31382,31420,31425,31429,31452,31458,31476,31479,31505,31509,31515,31561,31567,31571,31574,31601,31608,31610,31621,31632,31637,31671,31689,31711,31715,31723,31742,31770,31782,31835,31845,31849,31855,31874,31938,31943,31992,31995,32038,32047,32051,32062,32103,32106,32117,32120,32124,32138,32263,32270,32279,32283,32286,32322,32337,32362,32364,32479,32482,32485,32532,32538,32540,32576],[11,31320,25354,31321,31323,31324,3750,31327,3750,31329,31331,31332,31334],{},[412,31322,15948],{"href":15947},"，那篇裡的 ",[29,31325,31326],{},"npx nuxi@latest init",[29,31328,25583],{},[29,31330,30562],{}," + ",[29,31333,30182],{}," 現在照著做都會撞牆。",[11,31336,31337,31338,31341],{},"Nuxt 4 的改動不算多，但",[12249,31339,31340],{},"每一項都在你開專案的第一個小時就會遇到","，所以值得單獨記一次。這篇的設定都是這個站自己在跑的。",[15,31343,31344],{"id":31344},"建專案",[11,31346,31347,31350,31351,31353],{},[29,31348,31349],{},"nuxi"," 這個獨立指令已經併回 ",[29,31352,15600],{}," 本身，現在的入口是：",[22,31355,31357],{"className":2057,"code":31356,"language":2059,"meta":27,"style":27},"npm create nuxt@latest \u003Cproject-name>\n",[29,31358,31359],{"__ignoreMap":27},[32,31360,31361,31363,31365,31368,31370,31372,31374],{"class":34,"line":35},[32,31362,4625],{"class":2066},[32,31364,4799],{"class":175},[32,31366,31367],{"class":175}," nuxt@latest",[32,31369,28038],{"class":38},[32,31371,28041],{"class":175},[32,31373,28044],{"class":41},[32,31375,4062],{"class":38},[11,31377,31378,31379,31381],{},"裝完之後，日常指令都掛在 ",[29,31380,15600],{}," 底下：",[22,31383,31385],{"className":2057,"code":31384,"language":2059,"meta":27,"style":27},"nuxt dev\nnuxt build\nnuxt preview\nnuxt typecheck\nnuxt prepare\n",[29,31386,31387,31393,31399,31406,31413],{"__ignoreMap":27},[32,31388,31389,31391],{"class":34,"line":35},[32,31390,15600],{"class":2066},[32,31392,10438],{"class":175},[32,31394,31395,31397],{"class":34,"line":45},[32,31396,15600],{"class":2066},[32,31398,4755],{"class":175},[32,31400,31401,31403],{"class":34,"line":51},[32,31402,15600],{"class":2066},[32,31404,31405],{"class":175}," preview\n",[32,31407,31408,31410],{"class":34,"line":90},[32,31409,15600],{"class":2066},[32,31411,31412],{"class":175}," typecheck\n",[32,31414,31415,31417],{"class":34,"line":99},[32,31416,15600],{"class":2066},[32,31418,31419],{"class":175}," prepare\n",[11,31421,31422,31424],{},[29,31423,31326],{}," 還能動，但它只是轉呼叫上面那個，文件已經不提了。",[15,31426,31428],{"id":31427},"最大的改動srcdir-變成-app","最大的改動：srcDir 變成 app\u002F",[11,31430,31431,31432,3750,31434,3750,31436,3750,31438,31440,31441,3750,31444,31446,31447,5240],{},"Nuxt 3 的 ",[29,31433,28458],{},[29,31435,28396],{},[29,31437,28406],{},[29,31439,15560],{}," 都在根目錄，跟 ",[29,31442,31443],{},"node_modules\u002F",[29,31445,28374],{},"、設定檔混在一起。Nuxt 4 把",[12249,31448,31449,31450],{},"前端的部分整組搬進 ",[29,31451,30555],{},[22,31453,31456],{"className":31454,"code":31455,"language":426,"meta":27},[424],"my-app\u002F\n├── app\u002F                  ← 前端全部在這裡\n│   ├── assets\u002F\n│   ├── components\u002F\n│   ├── composables\u002F\n│   ├── layouts\u002F\n│   ├── middleware\u002F\n│   ├── pages\u002F\n│   ├── plugins\u002F\n│   ├── app.vue\n│   └── app.config.ts\n├── server\u002F               ← 留在根目錄\n│   ├── api\u002F\n│   ├── routes\u002F\n│   └── middleware\u002F\n├── content\u002F              ← 留在根目錄\n├── public\u002F               ← 留在根目錄\n├── nuxt.config.ts\n└── package.json\n",[29,31457,31455],{"__ignoreMap":27},[11,31459,31460,31472,31473,31475],{},[12249,31461,31462,31463,3750,31465,3750,31467,7090,31469],{},"留在根目錄的是 ",[29,31464,28497],{},[29,31466,28416],{},[29,31468,28481],{},[29,31470,31471],{},"modules\u002F","。判準很單純：",[29,31474,30555],{}," 底下的東西會被打包進前端 bundle，其他的不會。",[11,31477,31478],{},"實務上的影響：",[4538,31480,31481,31496,31502],{},[4541,31482,31483,31486,31487,31489,31490,29929,31493],{},[29,31484,31485],{},"~\u002F"," 別名現在指向 ",[29,31488,30555],{},"，所以 ",[29,31491,31492],{},"~\u002Fcomponents\u002FFoo.vue",[29,31494,31495],{},"app\u002Fcomponents\u002FFoo.vue",[4541,31497,31498,31499,31501],{},"從 ",[29,31500,30555],{}," 底下要引用根目錄的設定檔，得用相對路徑往上跳一層",[4541,31503,31504],{},"檔案監看的範圍縮小了，dev server 啟動與 HMR 都比較快",[151,31506,31508],{"id":31507},"從-nuxt-3-升上來","從 Nuxt 3 升上來",[11,31510,31511,31512,31514],{},"不想一次搬完的話，",[29,31513,23855],{}," 可以留在舊行為：",[22,31516,31518],{"className":28120,"code":31517,"language":28122,"meta":27,"style":27},"export default defineNuxtConfig({\n  srcDir: '.',\n  dir: {\n    app: 'app'\n  }\n})\n",[29,31519,31520,31530,31540,31545,31553,31557],{"__ignoreMap":27},[32,31521,31522,31524,31526,31528],{"class":34,"line":35},[32,31523,5038],{"class":38},[32,31525,5041],{"class":38},[32,31527,26142],{"class":217},[32,31529,1143],{"class":5049},[32,31531,31532,31535,31538],{"class":34,"line":45},[32,31533,31534],{"class":41},"  srcDir: ",[32,31536,31537],{"class":175},"'.'",[32,31539,547],{"class":5049},[32,31541,31542],{"class":34,"line":51},[32,31543,31544],{"class":41},"  dir: {\n",[32,31546,31547,31550],{"class":34,"line":90},[32,31548,31549],{"class":41},"    app: ",[32,31551,31552],{"class":175},"'app'\n",[32,31554,31555],{"class":34,"line":99},[32,31556,1203],{"class":41},[32,31558,31559],{"class":34,"line":106},[32,31560,26160],{"class":5049},[11,31562,31563,31564,31566],{},"但這只是緩衝。新專案直接用 ",[29,31565,30555],{},"，不要為了省一次搬檔案而長期背著一份跟文件不一致的結構。",[15,31568,31570],{"id":31569},"compatibilitydate","compatibilityDate",[11,31572,31573],{},"Nuxt 4 的設定檔多了一個一定要填的欄位：",[22,31575,31577],{"className":28120,"code":31576,"language":28122,"meta":27,"style":27},"export default defineNuxtConfig({\n  compatibilityDate: '2025-07-15'\n})\n",[29,31578,31579,31589,31597],{"__ignoreMap":27},[32,31580,31581,31583,31585,31587],{"class":34,"line":35},[32,31582,5038],{"class":38},[32,31584,5041],{"class":38},[32,31586,26142],{"class":217},[32,31588,1143],{"class":5049},[32,31590,31591,31594],{"class":34,"line":45},[32,31592,31593],{"class":41},"  compatibilityDate: ",[32,31595,31596],{"class":175},"'2025-07-15'\n",[32,31598,31599],{"class":34,"line":51},[32,31600,26160],{"class":5049},[11,31602,31603,31604,31607],{},"它鎖住 Nitro 與各家部署平台的行為版本。",[12249,31605,31606],{},"沒填的話升級 Nuxt 時可能會靜靜地換掉某些預設值","——填一個日期，之後升版行為就不會自己漂移，等你有空再往前推。",[15,31609,25923],{"id":26131},[11,31611,31612,31613,31616,31617,31620],{},"型別檢查不用再自己裝 ",[29,31614,31615],{},"vue-tsc"," 加 ",[29,31618,31619],{},"typeCheck: true"," 了，直接有指令：",[22,31622,31624],{"className":2057,"code":31623,"language":2059,"meta":27,"style":27},"nuxt typecheck\n",[29,31625,31626],{"__ignoreMap":27},[32,31627,31628,31630],{"class":34,"line":35},[32,31629,15600],{"class":2066},[32,31631,31412],{"class":175},[11,31633,31634,31636],{},[29,31635,23855],{}," 只留 strict 就好：",[22,31638,31640],{"className":28120,"code":31639,"language":28122,"meta":27,"style":27},"export default defineNuxtConfig({\n  typescript: {\n    strict: true\n  }\n})\n",[29,31641,31642,31652,31656,31663,31667],{"__ignoreMap":27},[32,31643,31644,31646,31648,31650],{"class":34,"line":35},[32,31645,5038],{"class":38},[32,31647,5041],{"class":38},[32,31649,26142],{"class":217},[32,31651,1143],{"class":5049},[32,31653,31654],{"class":34,"line":45},[32,31655,28139],{"class":41},[32,31657,31658,31661],{"class":34,"line":51},[32,31659,31660],{"class":41},"    strict: ",[32,31662,28147],{"class":351},[32,31664,31665],{"class":34,"line":90},[32,31666,1203],{"class":41},[32,31668,31669],{"class":34,"line":99},[32,31670,26160],{"class":5049},[11,31672,31673,31674,3750,31676,3750,31678,31681,31682,31684,31685,31688],{},"Nuxt 4 會為 ",[29,31675,30555],{},[29,31677,28497],{},[29,31679,31680],{},"shared\u002F"," 各產生一份獨立的 tsconfig，所以在 ",[29,31683,28497],{}," 底下寫 ",[29,31686,31687],{},"document.querySelector"," 會直接被型別擋下來——Nuxt 3 是不會的。",[2184,31690,31691],{},[11,31692,31693,31702,31703,31706,31707,31710],{},[12249,31694,31695,31698,31699],{},[29,31696,31697],{},"nuxt typecheck"," 內部會跑 ",[29,31700,31701],{},"nuxt prepare","。如果專案用了 ",[29,31704,31705],{},"@nuxt\u002Fcontent","，它會重建本機的 content 資料庫，",[12249,31708,31709],{},"dev server 開著的時候跑會把 dev 那邊的文章清空","。停掉 dev 再跑。",[15,31712,31714],{"id":31713},"eslint改用-nuxteslint","ESLint：改用 @nuxt\u002Feslint",[11,31716,31717,31719,31720,31722],{},[29,31718,25583],{}," 這種 legacy config 已經不再是 ESLint 的預設，",[29,31721,28310],{}," 也停在原地了。現在的做法是官方的模組：",[22,31724,31726],{"className":2057,"code":31725,"language":2059,"meta":27,"style":27},"npm install -D @nuxt\u002Feslint eslint\n",[29,31727,31728],{"__ignoreMap":27},[32,31729,31730,31732,31734,31736,31739],{"class":34,"line":35},[32,31731,4625],{"class":2066},[32,31733,4650],{"class":175},[32,31735,28106],{"class":351},[32,31737,31738],{"class":175}," @nuxt\u002Feslint",[32,31740,31741],{"class":175}," eslint\n",[22,31743,31745],{"className":28120,"code":31744,"language":28122,"meta":27,"style":27},"export default defineNuxtConfig({\n  modules: ['@nuxt\u002Feslint']\n})\n",[29,31746,31747,31757,31766],{"__ignoreMap":27},[32,31748,31749,31751,31753,31755],{"class":34,"line":35},[32,31750,5038],{"class":38},[32,31752,5041],{"class":38},[32,31754,26142],{"class":217},[32,31756,1143],{"class":5049},[32,31758,31759,31761,31764],{"class":34,"line":45},[32,31760,21503],{"class":41},[32,31762,31763],{"class":175},"'@nuxt\u002Feslint'",[32,31765,28766],{"class":41},[32,31767,31768],{"class":34,"line":51},[32,31769,26160],{"class":5049},[11,31771,31772,31773,31775,31776,5240],{},"跑一次 ",[29,31774,31701],{},"，它會",[12249,31777,31778,31779],{},"自動產生一份 ",[29,31780,31781],{},"eslint.config.mjs",[22,31783,31785],{"className":24,"code":31784,"language":26,"meta":27,"style":27},"import withNuxt from '.\u002F.nuxt\u002Feslint.config.mjs'\n\nexport default withNuxt({\n  rules: {\n    'vue\u002Fmulti-word-component-names': 'off'\n  }\n})\n",[29,31786,31787,31799,31803,31814,31818,31827,31831],{"__ignoreMap":27},[32,31788,31789,31791,31794,31796],{"class":34,"line":35},[32,31790,8794],{"class":38},[32,31792,31793],{"class":41}," withNuxt ",[32,31795,8850],{"class":38},[32,31797,31798],{"class":175}," '.\u002F.nuxt\u002Feslint.config.mjs'\n",[32,31800,31801],{"class":34,"line":45},[32,31802,103],{"emptyLinePlaceholder":102},[32,31804,31805,31807,31809,31812],{"class":34,"line":51},[32,31806,5038],{"class":38},[32,31808,5041],{"class":38},[32,31810,31811],{"class":217}," withNuxt",[32,31813,1143],{"class":5049},[32,31815,31816],{"class":34,"line":90},[32,31817,28283],{"class":41},[32,31819,31820,31823,31825],{"class":34,"line":99},[32,31821,31822],{"class":175},"    'vue\u002Fmulti-word-component-names'",[32,31824,5269],{"class":41},[32,31826,28293],{"class":175},[32,31828,31829],{"class":34,"line":106},[32,31830,1203],{"class":41},[32,31832,31833],{"class":34,"line":114},[32,31834,26160],{"class":5049},[11,31836,31837,31838,31841,31842,31844],{},"它比手寫設定好的地方在於：規則是",[12249,31839,31840],{},"依你實際安裝的模組產生的","，Nuxt 自動匯入的那些 API 也已經登記好，不用再像以前那樣為了消掉滿江紅的 ",[29,31843,28320],{}," 而整條關掉。",[15,31846,31848],{"id":31847},"tailwind-4不再有-tailwindconfigjs","Tailwind 4：不再有 tailwind.config.js",[11,31850,31851,31852,31854],{},"這是變動最大的一塊。",[29,31853,30562],{}," 那條路已經不需要了，Tailwind 4 自己提供 Vite plugin：",[22,31856,31858],{"className":2057,"code":31857,"language":2059,"meta":27,"style":27},"npm install -D tailwindcss @tailwindcss\u002Fvite\n",[29,31859,31860],{"__ignoreMap":27},[32,31861,31862,31864,31866,31868,31871],{"class":34,"line":35},[32,31863,4625],{"class":2066},[32,31865,4650],{"class":175},[32,31867,28106],{"class":351},[32,31869,31870],{"class":175}," tailwindcss",[32,31872,31873],{"class":175}," @tailwindcss\u002Fvite\n",[22,31875,31877],{"className":28120,"code":31876,"language":28122,"meta":27,"style":27},"import tailwindcss from '@tailwindcss\u002Fvite'\n\nexport default defineNuxtConfig({\n  css: ['~\u002Fassets\u002Fcss\u002Fmain.css'],\n  vite: {\n    plugins: [tailwindcss()]\n  }\n})\n",[29,31878,31879,31890,31894,31904,31915,31920,31930,31934],{"__ignoreMap":27},[32,31880,31881,31883,31885,31887],{"class":34,"line":35},[32,31882,8794],{"class":38},[32,31884,30863],{"class":41},[32,31886,8850],{"class":38},[32,31888,31889],{"class":175}," '@tailwindcss\u002Fvite'\n",[32,31891,31892],{"class":34,"line":45},[32,31893,103],{"emptyLinePlaceholder":102},[32,31895,31896,31898,31900,31902],{"class":34,"line":51},[32,31897,5038],{"class":38},[32,31899,5041],{"class":38},[32,31901,26142],{"class":217},[32,31903,1143],{"class":5049},[32,31905,31906,31908,31911,31913],{"class":34,"line":90},[32,31907,30116],{"class":41},[32,31909,31910],{"class":175},"'~\u002Fassets\u002Fcss\u002Fmain.css'",[32,31912,21509],{"class":41},[32,31914,547],{"class":5049},[32,31916,31917],{"class":34,"line":99},[32,31918,31919],{"class":41},"  vite: {\n",[32,31921,31922,31925,31927],{"class":34,"line":106},[32,31923,31924],{"class":41},"    plugins: [",[32,31926,30816],{"class":217},[32,31928,31929],{"class":41},"()]\n",[32,31931,31932],{"class":34,"line":114},[32,31933,1203],{"class":41},[32,31935,31936],{"class":34,"line":120},[32,31937,26160],{"class":5049},[11,31939,31940,5240],{},[29,31941,31942],{},"app\u002Fassets\u002Fcss\u002Fmain.css",[22,31944,31946],{"className":5412,"code":31945,"language":1140,"meta":27,"style":27},"@import \"tailwindcss\";\n\n@theme {\n  --color-brand: #000000;\n  --font-sans: \"Noto Sans TC\", sans-serif;\n}\n",[29,31947,31948,31957,31961,31968,31978,31988],{"__ignoreMap":27},[32,31949,31950,31952,31955],{"class":34,"line":35},[32,31951,8744],{"class":38},[32,31953,31954],{"class":175}," \"tailwindcss\"",[32,31956,96],{"class":41},[32,31958,31959],{"class":34,"line":45},[32,31960,103],{"emptyLinePlaceholder":102},[32,31962,31963,31966],{"class":34,"line":51},[32,31964,31965],{"class":38},"@theme",[32,31967,703],{"class":41},[32,31969,31970,31973,31976],{"class":34,"line":90},[32,31971,31972],{"class":41},"  --color-brand: ",[32,31974,31975],{"class":24417},"#000000",[32,31977,96],{"class":41},[32,31979,31980,31983,31986],{"class":34,"line":99},[32,31981,31982],{"class":41},"  --font-sans: \"Noto Sans TC\", ",[32,31984,31985],{"class":4049},"sans-serif",[32,31987,96],{"class":41},[32,31989,31990],{"class":34,"line":106},[32,31991,54],{"class":41},[11,31993,31994],{},"三個要注意的：",[6426,31996,31997,32011,32020],{},[4541,31998,31999,32004,32005,32007,32008,32010],{},[12249,32000,32001,32002],{},"沒有 ",[29,32003,30182],{},"。主題設定改用 CSS 的 ",[29,32006,31965],{}," 區塊寫，跑 ",[29,32009,31012],{}," 會直接失敗。",[4541,32012,32013,32019],{},[12249,32014,32015,32016,32018],{},"不用再維護 ",[29,32017,30291],{}," 陣列","。Tailwind 4 自動掃描，以前那種「漏掉一個資料夾、正式站樣式整組消失」的坑沒有了。",[4541,32021,32022,32031,32032,32035,32036,3775],{},[12249,32023,32024,32027,32028],{},[29,32025,32026],{},"@apply"," 在 scoped style 裡要先 ",[29,32029,32030],{},"@reference","。單檔元件的 ",[29,32033,32034],{},"\u003Cstyle scoped>"," 是獨立編譯的，看不到主檔案的 ",[29,32037,8744],{},[11,32039,32040,32041,32043,32044,3775],{},"Tailwind 4 本身的其他變更（PostCSS 設定、",[29,32042,31965],{}," 的用法）另外寫在 ",[412,32045,32046],{"href":25674},"Vite + Vue 3 + Tailwind CSS 4 + shadcn-vue 安裝心得",[15,32048,32050],{"id":32049},"狀態管理先問要不要-pinia","狀態管理：先問要不要 Pinia",[11,32052,32053,32054,32057,32058,32061],{},"Nuxt 3 那篇裡裝 Pinia 是預設動作。實際上 Nuxt 內建的 ",[29,32055,32056],{},"useState"," 已經能處理大部分情況——它是 ",[12249,32059,32060],{},"SSR 友善的共用 ref","，伺服器端算出來的值會被序列化帶到瀏覽器：",[22,32063,32065],{"className":28120,"code":32064,"language":28122,"meta":27,"style":27},"export const useCounter = () => useState\u003Cnumber>('counter', () => 0)\n",[29,32066,32067],{"__ignoreMap":27},[32,32068,32069,32071,32073,32076,32078,32080,32082,32085,32087,32090,32092,32095,32097,32099,32101],{"class":34,"line":35},[32,32070,5038],{"class":38},[32,32072,26202],{"class":38},[32,32074,32075],{"class":217}," useCounter",[32,32077,894],{"class":38},[32,32079,11257],{"class":41},[32,32081,5090],{"class":38},[32,32083,32084],{"class":217}," useState",[32,32086,4046],{"class":41},[32,32088,32089],{"class":351},"number",[32,32091,27571],{"class":41},[32,32093,32094],{"class":175},"'counter'",[32,32096,11218],{"class":41},[32,32098,5090],{"class":38},[32,32100,1523],{"class":351},[32,32102,515],{"class":41},[11,32104,32105],{},"在任何元件裡呼叫同一個 key 就拿到同一份狀態。什麼時候才真的需要 Pinia：",[4538,32107,32108,32111,32114],{},[4541,32109,32110],{},"需要 store 的 actions／getters 組織一大包邏輯",[4541,32112,32113],{},"需要 devtools 的時間旅行除錯",[4541,32115,32116],{},"已經有一套 Pinia store 要從別的專案搬過來",[11,32118,32119],{},"不然多一層抽象只是多一層要維護的東西。",[15,32121,32123],{"id":32122},"資料取得預渲染一定要-await","資料取得：預渲染一定要 await",[11,32125,32126,32127,7090,32129,32131,32132,5240],{},"這是最容易吃虧的一個。",[29,32128,27916],{},[29,32130,21652],{}," 回傳的是 ref，",[12249,32133,32134,32135,32137],{},"在 setup 階段沒有 ",[29,32136,27413],{},"，預渲染就會在資料回來之前先把 HTML 吐出去",[22,32139,32141],{"className":8665,"code":32140,"language":4112,"meta":27,"style":27},"\u003Cscript setup lang=\"ts\">\n\u002F\u002F ✗ 預渲染出來的 HTML 裡 posts 是空的\nconst { data: posts } = useAsyncData('posts', () => queryCollection('posts').all())\n\n\u002F\u002F ✓\nconst { data: posts } = await useAsyncData('posts', () => queryCollection('posts').all())\n\u003C\u002Fscript>\n",[29,32142,32143,32159,32164,32206,32210,32215,32255],{"__ignoreMap":27},[32,32144,32145,32147,32149,32151,32153,32155,32157],{"class":34,"line":35},[32,32146,4046],{"class":41},[32,32148,4125],{"class":4049},[32,32150,26689],{"class":4053},[32,32152,8677],{"class":4053},[32,32154,172],{"class":41},[32,32156,27335],{"class":175},[32,32158,4062],{"class":41},[32,32160,32161],{"class":34,"line":45},[32,32162,32163],{"class":608},"\u002F\u002F ✗ 預渲染出來的 HTML 裡 posts 是空的\n",[32,32165,32166,32168,32170,32172,32174,32177,32179,32181,32184,32186,32189,32191,32193,32196,32198,32200,32202,32204],{"class":34,"line":51},[32,32167,6617],{"class":38},[32,32169,26747],{"class":41},[32,32171,24740],{"class":903},[32,32173,5269],{"class":41},[32,32175,32176],{"class":351},"posts",[32,32178,26758],{"class":41},[32,32180,172],{"class":38},[32,32182,32183],{"class":217}," useAsyncData",[32,32185,477],{"class":41},[32,32187,32188],{"class":175},"'posts'",[32,32190,11218],{"class":41},[32,32192,5090],{"class":38},[32,32194,32195],{"class":217}," queryCollection",[32,32197,477],{"class":41},[32,32199,32188],{"class":175},[32,32201,533],{"class":41},[32,32203,27198],{"class":217},[32,32205,26088],{"class":41},[32,32207,32208],{"class":34,"line":90},[32,32209,103],{"emptyLinePlaceholder":102},[32,32211,32212],{"class":34,"line":99},[32,32213,32214],{"class":608},"\u002F\u002F ✓\n",[32,32216,32217,32219,32221,32223,32225,32227,32229,32231,32233,32235,32237,32239,32241,32243,32245,32247,32249,32251,32253],{"class":34,"line":106},[32,32218,6617],{"class":38},[32,32220,26747],{"class":41},[32,32222,24740],{"class":903},[32,32224,5269],{"class":41},[32,32226,32176],{"class":351},[32,32228,26758],{"class":41},[32,32230,172],{"class":38},[32,32232,20431],{"class":38},[32,32234,32183],{"class":217},[32,32236,477],{"class":41},[32,32238,32188],{"class":175},[32,32240,11218],{"class":41},[32,32242,5090],{"class":38},[32,32244,32195],{"class":217},[32,32246,477],{"class":41},[32,32248,32188],{"class":175},[32,32250,533],{"class":41},[32,32252,27198],{"class":217},[32,32254,26088],{"class":41},[32,32256,32257,32259,32261],{"class":34,"line":114},[32,32258,4105],{"class":41},[32,32260,4125],{"class":4049},[32,32262,4062],{"class":41},[11,32264,32265,32266,32269],{},"畫面上看不出差別——瀏覽器端 hydrate 完照樣有資料。",[12249,32267,32268],{},"壞掉的是原始 HTML","，爬蟲跟社群分享卡片讀到的就是空的。這種問題不會有錯誤訊息，只能自己記得。",[11,32271,32272,32273,7218,32275,32278],{},"同一份資料在多個元件用時，",[29,32274,27916],{},[12249,32276,32277],{},"key 要一致","，Nuxt 才會共用同一次請求而不是各打各的。",[15,32280,32282],{"id":32281},"部署nitro-preset","部署：Nitro preset",[11,32284,32285],{},"後端由 Nitro 負責，換部署目標基本上就是換一行：",[22,32287,32289],{"className":28120,"code":32288,"language":28122,"meta":27,"style":27},"export default defineNuxtConfig({\n  nitro: {\n    preset: 'cloudflare_module'\n  }\n})\n",[29,32290,32291,32301,32306,32314,32318],{"__ignoreMap":27},[32,32292,32293,32295,32297,32299],{"class":34,"line":35},[32,32294,5038],{"class":38},[32,32296,5041],{"class":38},[32,32298,26142],{"class":217},[32,32300,1143],{"class":5049},[32,32302,32303],{"class":34,"line":45},[32,32304,32305],{"class":41},"  nitro: {\n",[32,32307,32308,32311],{"class":34,"line":51},[32,32309,32310],{"class":41},"    preset: ",[32,32312,32313],{"class":175},"'cloudflare_module'\n",[32,32315,32316],{"class":34,"line":90},[32,32317,1203],{"class":41},[32,32319,32320],{"class":34,"line":99},[32,32321,26160],{"class":5049},[11,32323,32324,32325,3750,32328,3750,32331,3750,32334,3775],{},"常見的還有 ",[29,32326,32327],{},"node-server",[29,32329,32330],{},"vercel",[29,32332,32333],{},"netlify",[29,32335,32336],{},"static",[11,32338,32339,32342,32343,32345,32346,32349,32350,32353,32354,32361],{},[12249,32340,32341],{},"要注意 preset 之間的執行環境差異不會被型別擋下來。"," 這個站踩過一個：workerd 在請求階段解不了動態 ",[29,32344,5027],{},"，配上 ",[29,32347,32348],{},"defineAsyncComponent"," 會安靜地渲染出空白而不是報錯——",[29,32351,32352],{},"npm run dev"," 走 vite-node、預渲染是建置時在 Node 跑的，兩者都碰不到那個限制。",[12249,32355,32356,32357,32360],{},"請求時才 SSR 的頁面一定要用 ",[29,32358,32359],{},"nuxt preview"," 在真正的 runtime 驗過","，靜態伺服器看不到 worker 的行為。",[15,32363,22792],{"id":22792},[13433,32365,32366,32376],{},[13436,32367,32368],{},[13439,32369,32370,32372,32374],{},[13442,32371,13544],{},[13442,32373,25961],{},[13442,32375,31251],{},[13449,32377,32378,32391,32403,32419,32435,32453,32465],{},[13439,32379,32380,32382,32386],{},[13454,32381,31344],{},[13454,32383,32384],{},[29,32385,31326],{},[13454,32387,32388],{},[29,32389,32390],{},"npm create nuxt@latest",[13439,32392,32393,32396,32399],{},[13454,32394,32395],{},"前端原始碼位置",[13454,32397,32398],{},"根目錄",[13454,32400,32401],{},[29,32402,30555],{},[13439,32404,32405,32408,32415],{},[13454,32406,32407],{},"型別檢查",[13454,32409,32410,32411,31331,32413],{},"自己裝 ",[29,32412,31615],{},[29,32414,31619],{},[13454,32416,32417],{},[29,32418,31697],{},[13439,32420,32421,32423,32429],{},[13454,32422,4853],{},[13454,32424,32425,31331,32427],{},[29,32426,25583],{},[29,32428,28310],{},[13454,32430,32431,31331,32433],{},[29,32432,30568],{},[29,32434,31781],{},[13439,32436,32437,32440,32446],{},[13454,32438,32439],{},"Tailwind",[13454,32441,32442,31331,32444],{},[29,32443,30562],{},[29,32445,30182],{},[13454,32447,32448,31331,32451],{},[29,32449,32450],{},"@tailwindcss\u002Fvite",[29,32452,31965],{},[13439,32454,32455,32458,32461],{},[13454,32456,32457],{},"相容性鎖定",[13454,32459,32460],{},"無",[13454,32462,32463],{},[29,32464,31570],{},[13439,32466,32467,32470,32473],{},[13454,32468,32469],{},"全域狀態",[13454,32471,32472],{},"大多直接上 Pinia",[13454,32474,32475,32476,32478],{},"先看 ",[29,32477,32056],{}," 夠不夠",[15,32480,32481],{"id":32481},"沒有變的部分",[11,32483,32484],{},"Nuxt 3 那篇裡有一半以上仍然完全適用，不用重學：",[4538,32486,32487,32497,32502,32511,32516,32524],{},[4541,32488,32489,32490,3750,32493,32496],{},"檔案系統路由（",[29,32491,32492],{},"[id].vue",[29,32494,32495],{},"[...slug].vue","、巢狀路由）",[4541,32498,32499,32500],{},"404 頁面與 ",[29,32501,29353],{},[4541,32503,32504,3750,32506,3750,32508],{},[29,32505,28653],{},[29,32507,28774],{},[29,32509,32510],{},"\u003CNuxtLayout>",[4541,32512,32513,32514],{},"Layout 與具名插槽、",[29,32515,15943],{},[4541,32517,32518,32519,32521,32522,22856],{},"元件自動載入與路徑前綴命名（",[29,32520,29928],{}," → ",[29,32523,29932],{},[4541,32525,32526,32528,32529,32531],{},[29,32527,29948],{}," \u002F ",[29,32530,29951],{}," 後綴",[11,32533,32534,32535,32537],{},"差別只在檔案現在放 ",[29,32536,30555],{}," 底下。",[15,32539,4536],{"id":4536},[4538,32541,32542,32549,32556,32562,32569],{},[4541,32543,32544],{},[412,32545,32548],{"href":32546,"rel":32547},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fgetting-started\u002Fupgrade",[416],"Nuxt 4 升級指南",[4541,32550,32551],{},[412,32552,32555],{"href":32553,"rel":32554},"https:\u002F\u002Fnuxt.com\u002Fdocs\u002Fguide\u002Fdirectory-structure\u002Fapp",[416],"Nuxt 目錄結構",[4541,32557,32558],{},[412,32559,30568],{"href":32560,"rel":32561},"https:\u002F\u002Feslint.nuxt.com\u002F",[416],[4541,32563,32564],{},[412,32565,32568],{"href":32566,"rel":32567},"https:\u002F\u002Ftailwindcss.com\u002Fdocs\u002Finstallation\u002Fusing-vite",[416],"Tailwind CSS 4：Vite plugin",[4541,32570,32571],{},[412,32572,32575],{"href":32573,"rel":32574},"https:\u002F\u002Fnitro.build\u002Fdeploy",[416],"Nitro 部署 preset",[368,32577,32578],{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}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 .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--shiki-dark:#FFA657}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}html pre.shiki code .sAjPK, html code.shiki .sAjPK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FFA198;--shiki-dark-font-style:italic}html pre.shiki code .sXQ1o, html code.shiki .sXQ1o{--shiki-default:#22863A;--shiki-dark:#7EE787}html pre.shiki code .syv4q, html code.shiki .syv4q{--shiki-default:#6F42C1;--shiki-dark:#79C0FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}html pre.shiki code .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}",{"title":27,"searchDepth":51,"depth":51,"links":32580},[32581,32582,32585,32586,32587,32588,32589,32590,32591,32592,32593,32594],{"id":31344,"depth":45,"text":31344},{"id":31427,"depth":45,"text":31428,"children":32583},[32584],{"id":31507,"depth":51,"text":31508},{"id":31569,"depth":45,"text":31570},{"id":26131,"depth":45,"text":25923},{"id":31713,"depth":45,"text":31714},{"id":31847,"depth":45,"text":31848},{"id":32049,"depth":45,"text":32050},{"id":32122,"depth":45,"text":32123},{"id":32281,"depth":45,"text":32282},{"id":22792,"depth":45,"text":22792},{"id":32481,"depth":45,"text":32481},{"id":4536,"depth":45,"text":4536},"對照 Nuxt 3 的建立流程，整理 Nuxt 4 實際變了什麼——srcDir 搬進 app\u002F、nuxi 併入 nuxt CLI、ESLint 改用 @nuxt\u002Feslint 的 flat config、Tailwind 4 改掛 Vite plugin 不再需要 tailwind.config.js，以及預渲染時 useAsyncData 沒 await 會安靜出錯的陷阱。",{},[],{"title":31316,"description":32595},"posts\u002F20260818.nuxt4-project-setup",[32601,25743,30695,4853,25923,32602],"Nuxt4","Nitro","ODepHVQEFuCgJscw6ddZRJ5orBW31R4ETthbWRXh3GE",{"id":11651,"title":11652,"body":32605,"category":378,"description":12307,"draft":380,"extension":381,"image":382,"meta":33145,"navigation":102,"path":12309,"publishedAt":11644,"relatedExperiments":33146,"seo":33147,"stem":12312,"tags":33148,"updatedAt":10829,"__hash__":12315},{"type":8,"value":32606,"toc":33137},[32607,32611,32613,32723,32729,32731,32885,32897,32899,32907,33093,33099,33101,33105,33109,33111,33121,33123,33135],[11,32608,11657,32609,11661],{},[29,32610,11660],{},[15,32612,11665],{"id":11664},[22,32614,32615],{"className":8735,"code":11668,"language":8737,"meta":27,"style":27},[29,32616,32617,32647,32651,32657,32667,32677,32687,32697,32707,32717],{"__ignoreMap":27},[32,32618,32619,32621,32623,32625,32627,32629,32631,32633,32635,32637,32639,32641,32643,32645],{"class":34,"line":35},[32,32620,11675],{"class":903},[32,32622,5269],{"class":5049},[32,32624,11680],{"class":175},[32,32626,635],{"class":5049},[32,32628,11685],{"class":175},[32,32630,635],{"class":5049},[32,32632,11690],{"class":175},[32,32634,635],{"class":5049},[32,32636,11695],{"class":175},[32,32638,635],{"class":5049},[32,32640,11700],{"class":175},[32,32642,635],{"class":5049},[32,32644,11705],{"class":175},[32,32646,96],{"class":41},[32,32648,32649],{"class":34,"line":45},[32,32650,103],{"emptyLinePlaceholder":102},[32,32652,32653,32655],{"class":34,"line":51},[32,32654,11716],{"class":903},[32,32656,11719],{"class":5049},[32,32658,32659,32661,32663,32665],{"class":34,"line":90},[32,32660,11724],{"class":175},[32,32662,5269],{"class":5049},[32,32664,11729],{"class":175},[32,32666,547],{"class":5049},[32,32668,32669,32671,32673,32675],{"class":34,"line":99},[32,32670,11736],{"class":175},[32,32672,5269],{"class":5049},[32,32674,11741],{"class":175},[32,32676,547],{"class":5049},[32,32678,32679,32681,32683,32685],{"class":34,"line":106},[32,32680,11748],{"class":175},[32,32682,5269],{"class":5049},[32,32684,11753],{"class":175},[32,32686,547],{"class":5049},[32,32688,32689,32691,32693,32695],{"class":34,"line":114},[32,32690,11760],{"class":175},[32,32692,5269],{"class":5049},[32,32694,11765],{"class":175},[32,32696,547],{"class":5049},[32,32698,32699,32701,32703,32705],{"class":34,"line":120},[32,32700,11772],{"class":175},[32,32702,5269],{"class":5049},[32,32704,11777],{"class":175},[32,32706,547],{"class":5049},[32,32708,32709,32711,32713,32715],{"class":34,"line":127},[32,32710,11784],{"class":175},[32,32712,5269],{"class":5049},[32,32714,11789],{"class":175},[32,32716,547],{"class":5049},[32,32718,32719,32721],{"class":34,"line":132},[32,32720,11796],{"class":5049},[32,32722,96],{"class":41},[11,32724,32725,11803,32727,11806],{},[29,32726,11675],{},[29,32728,11716],{},[15,32730,11810],{"id":11809},[22,32732,32733],{"className":8735,"code":11813,"language":8737,"meta":27,"style":27},[29,32734,32735,32747,32757,32769,32781,32795,32821,32831,32843,32855,32867,32877,32881],{"__ignoreMap":27},[32,32736,32737,32739,32741,32743,32745],{"class":34,"line":35},[32,32738,11660],{"class":38},[32,32740,11822],{"class":903},[32,32742,5527],{"class":38},[32,32744,11827],{"class":903},[32,32746,703],{"class":41},[32,32748,32749,32751,32753,32755],{"class":34,"line":45},[32,32750,11834],{"class":4049},[32,32752,11837],{"class":4053},[32,32754,11840],{"class":903},[32,32756,703],{"class":41},[32,32758,32759,32761,32763,32765,32767],{"class":34,"line":51},[32,32760,11847],{"class":351},[32,32762,5269],{"class":41},[32,32764,11852],{"class":351},[32,32766,11855],{"class":38},[32,32768,96],{"class":41},[32,32770,32771,32773,32775,32777,32779],{"class":34,"line":90},[32,32772,11862],{"class":351},[32,32774,5269],{"class":41},[32,32776,11867],{"class":351},[32,32778,11855],{"class":38},[32,32780,96],{"class":41},[32,32782,32783,32785,32787,32789,32791,32793],{"class":34,"line":99},[32,32784,11876],{"class":351},[32,32786,5269],{"class":41},[32,32788,11881],{"class":351},[32,32790,11855],{"class":38},[32,32792,11886],{"class":38},[32,32794,96],{"class":41},[32,32796,32797,32799,32801,32803,32805,32807,32809,32811,32813,32815,32817,32819],{"class":34,"line":106},[32,32798,11893],{"class":351},[32,32800,5269],{"class":41},[32,32802,11898],{"class":351},[32,32804,477],{"class":41},[32,32806,11903],{"class":351},[32,32808,477],{"class":41},[32,32810,11716],{"class":903},[32,32812,635],{"class":41},[32,32814,11912],{"class":903},[32,32816,1799],{"class":38},[32,32818,11917],{"class":175},[32,32820,8328],{"class":41},[32,32822,32823,32825,32827,32829],{"class":34,"line":114},[32,32824,11924],{"class":351},[32,32826,5269],{"class":41},[32,32828,11929],{"class":351},[32,32830,96],{"class":41},[32,32832,32833,32835,32837,32839,32841],{"class":34,"line":120},[32,32834,11936],{"class":351},[32,32836,5269],{"class":41},[32,32838,11941],{"class":351},[32,32840,11944],{"class":351},[32,32842,96],{"class":41},[32,32844,32845,32847,32849,32851,32853],{"class":34,"line":127},[32,32846,11951],{"class":351},[32,32848,5269],{"class":41},[32,32850,11956],{"class":351},[32,32852,11959],{"class":38},[32,32854,96],{"class":41},[32,32856,32857,32859,32861,32863,32865],{"class":34,"line":132},[32,32858,11966],{"class":351},[32,32860,5269],{"class":41},[32,32862,11971],{"class":351},[32,32864,11855],{"class":38},[32,32866,96],{"class":41},[32,32868,32869,32871,32873,32875],{"class":34,"line":141},[32,32870,11980],{"class":351},[32,32872,5269],{"class":41},[32,32874,11985],{"class":351},[32,32876,96],{"class":41},[32,32878,32879],{"class":34,"line":147},[32,32880,1203],{"class":41},[32,32882,32883],{"class":34,"line":275},[32,32884,54],{"class":41},[11,32886,32887,12001,32889,12004,32891,3750,32893,12011,32895,12015],{},[29,32888,12000],{},[29,32890,11840],{},[29,32892,12007],{},[29,32894,12010],{},[29,32896,12014],{},[15,32898,12018],{"id":12018},[11,32900,12021,32901,11621,32903,12028,32905,12032],{},[29,32902,12024],{},[29,32904,12027],{},[29,32906,12031],{},[22,32908,32909],{"className":8735,"code":12035,"language":8737,"meta":27,"style":27},[29,32910,32911,32923,32933,32937,32941,32955,32963,32973,32995,33007,33019,33023,33031,33043,33055,33077,33081,33085,33089],{"__ignoreMap":27},[32,32912,32913,32915,32917,32919,32921],{"class":34,"line":35},[32,32914,11660],{"class":38},[32,32916,11822],{"class":903},[32,32918,5527],{"class":38},[32,32920,11827],{"class":903},[32,32922,703],{"class":41},[32,32924,32925,32927,32929,32931],{"class":34,"line":45},[32,32926,11834],{"class":4049},[32,32928,11837],{"class":4053},[32,32930,11840],{"class":903},[32,32932,703],{"class":41},[32,32934,32935],{"class":34,"line":51},[32,32936,12064],{"class":608},[32,32938,32939],{"class":34,"line":90},[32,32940,103],{"emptyLinePlaceholder":102},[32,32942,32943,32945,32947,32949,32951,32953],{"class":34,"line":99},[32,32944,12073],{"class":38},[32,32946,900],{"class":41},[32,32948,11912],{"class":903},[32,32950,12080],{"class":41},[32,32952,11680],{"class":175},[32,32954,520],{"class":41},[32,32956,32957,32959,32961],{"class":34,"line":106},[32,32958,12089],{"class":4049},[32,32960,12092],{"class":4053},[32,32962,703],{"class":41},[32,32964,32965,32967,32969,32971],{"class":34,"line":114},[32,32966,12099],{"class":4049},[32,32968,5269],{"class":41},[32,32970,12104],{"class":351},[32,32972,96],{"class":41},[32,32974,32975,32977,32979,32981,32983,32985,32987,32989,32991,32993],{"class":34,"line":120},[32,32976,12111],{"class":351},[32,32978,5269],{"class":41},[32,32980,11898],{"class":351},[32,32982,477],{"class":41},[32,32984,11903],{"class":351},[32,32986,477],{"class":41},[32,32988,11716],{"class":903},[32,32990,635],{"class":41},[32,32992,12128],{"class":175},[32,32994,8328],{"class":41},[32,32996,32997,32999,33001,33003,33005],{"class":34,"line":127},[32,32998,12135],{"class":351},[32,33000,5269],{"class":41},[32,33002,12140],{"class":351},[32,33004,11855],{"class":38},[32,33006,96],{"class":41},[32,33008,33009,33011,33013,33015,33017],{"class":34,"line":132},[32,33010,12149],{"class":351},[32,33012,5269],{"class":41},[32,33014,12154],{"class":351},[32,33016,11855],{"class":38},[32,33018,96],{"class":41},[32,33020,33021],{"class":34,"line":141},[32,33022,1193],{"class":41},[32,33024,33025,33027,33029],{"class":34,"line":147},[32,33026,12089],{"class":4049},[32,33028,12169],{"class":4053},[32,33030,703],{"class":41},[32,33032,33033,33035,33037,33039,33041],{"class":34,"line":275},[32,33034,12135],{"class":351},[32,33036,5269],{"class":41},[32,33038,12140],{"class":351},[32,33040,11855],{"class":38},[32,33042,96],{"class":41},[32,33044,33045,33047,33049,33051,33053],{"class":34,"line":282},[32,33046,12149],{"class":351},[32,33048,5269],{"class":41},[32,33050,12154],{"class":351},[32,33052,11855],{"class":38},[32,33054,96],{"class":41},[32,33056,33057,33059,33061,33063,33065,33067,33069,33071,33073,33075],{"class":34,"line":295},[32,33058,12111],{"class":351},[32,33060,5269],{"class":41},[32,33062,11898],{"class":351},[32,33064,477],{"class":41},[32,33066,11903],{"class":351},[32,33068,477],{"class":41},[32,33070,11716],{"class":903},[32,33072,635],{"class":41},[32,33074,12216],{"class":175},[32,33076,8328],{"class":41},[32,33078,33079],{"class":34,"line":605},[32,33080,1193],{"class":41},[32,33082,33083],{"class":34,"line":612},[32,33084,5591],{"class":41},[32,33086,33087],{"class":34,"line":617},[32,33088,1203],{"class":41},[32,33090,33091],{"class":34,"line":644},[32,33092,54],{"class":41},[11,33094,33095,12239,33097,12242],{},[29,33096,12031],{},[29,33098,11912],{},[15,33100,12245],{"id":12245},[11,33102,33103,12252],{},[12249,33104,12251],{},[11,33106,33107,12258],{},[12249,33108,12257],{},[15,33110,2004],{"id":2003},[11,33112,33113,12266,33115,12269,33117,12273,33119,12277],{},[12249,33114,12265],{},[29,33116,11660],{},[12249,33118,12272],{},[29,33120,12276],{},[15,33122,4536],{"id":4536},[4538,33124,33125,33130],{},[4541,33126,33127],{},[412,33128,12288],{"href":12286,"rel":33129},[416],[4541,33131,33132],{},[412,33133,12295],{"href":12293,"rel":33134},[416],[368,33136,12298],{},{"title":27,"searchDepth":51,"depth":51,"links":33138},[33139,33140,33141,33142,33143,33144],{"id":11664,"depth":45,"text":11665},{"id":11809,"depth":45,"text":11810},{"id":12018,"depth":45,"text":12018},{"id":12245,"depth":45,"text":12245},{"id":2003,"depth":45,"text":2004},{"id":4536,"depth":45,"text":4536},{},[],{"title":11652,"description":12307},[12314,1757,4847],[33150,33151],{"title":11604,"path":11643,"stem":11647,"description":11641,"category":378,"publishedAt":11644,"children":-1},{"title":11569,"path":11594,"stem":11598,"description":11592,"category":378,"publishedAt":11595,"children":-1},1787656913659]