[{"data":1,"prerenderedAt":958},["ShallowReactive",2],{"blog:\u002Fblog\u002Fnodejs\u002Fvite-plus-env":3,"blog:chapters":871,"blog:\u002Fblog\u002Fnodejs\u002Fvite-plus-env:surround":922,"blog:\u002Fblog\u002Fnodejs\u002Fvite-plus-env:series":933,"blog:\u002Fblog\u002Fnodejs\u002Fvite-plus-env:author":934,"blog:\u002Fblog\u002Fnodejs\u002Fvite-plus-env:related":939},{"id":4,"title":5,"author":6,"body":7,"category":854,"date":855,"description":856,"draft":857,"extension":858,"image":859,"kind":860,"meta":861,"navigation":862,"path":863,"seo":864,"series":859,"seriesOrder":859,"seriesTitle":859,"stem":865,"tags":866,"updatedAt":859,"__hash__":870},"blog\u002Fblog\u002Fnodejs\u002Fvite-plus-env\u002Findex.md","Vite+ 的 vp env：pin 了版本卻沒生效","charles",{"type":8,"value":9,"toc":837},"minimark",[10,26,40,48,54,156,166,173,183,192,199,202,228,250,253,261,265,278,301,310,313,319,343,348,351,378,395,404,432,435,441,624,627,672,676,691,694,698,704,738,741,789,806,812,815,833],[11,12,13,14,18,19,22,23,25],"p",{},"過去切換 Node.js 版本的標準答案是 nvm。Vite+（指令是 ",[15,16,17],"code",{},"vp","）把這件事收進同一個工具：",[15,20,21],{},"vp env"," 管 Node.js 與套件管理器的版本，同一個 ",[15,24,17],{}," 還接手檢查、格式化、測試。",[11,27,28,29,32,33],{},"這個站用的是 ",[15,30,31],{},"vite-plus"," 1.0.0-rc.0。這篇先講它收了哪些事，再講一個我們實際遇到、而且工具自己沒有報錯的坑：",[34,35,36,39],"strong",{},[15,37,38],{},".node-version"," 寫好、也 pin 成功了，實際跑的卻是另一個版本。",[41,42,44,45,47],"h2",{"id":43},"一vp-一個指令收了哪些事","一、",[15,46,17],{}," 一個指令收了哪些事",[11,49,50,53],{},[15,51,52],{},"vp --help"," 列出的指令裡，這個站實際在用的是下面幾組：",[55,56,57,73],"table",{},[58,59,60],"thead",{},[61,62,63,67,70],"tr",{},[64,65,66],"th",{},"以前要裝的東西",[64,68,69],{},"現在的指令",[64,71,72],{},"這個站有沒有在用",[74,75,76,89,105,118,131,143],"tbody",{},[61,77,78,82,86],{},[79,80,81],"td",{},"nvm 之類的 Node.js 版本管理",[79,83,84],{},[15,85,21],{},[79,87,88],{},"有，見下一節",[61,90,91,94,99],{},[79,92,93],{},"ESLint",[79,95,96],{},[15,97,98],{},"vp lint",[79,100,101,102],{},"有，",[15,103,104],{},"--deny-warnings",[61,106,107,110,115],{},[79,108,109],{},"Prettier",[79,111,112],{},[15,113,114],{},"vp fmt",[79,116,117],{},"有",[61,119,120,123,128],{},[79,121,122],{},"格式、檢查、型別三件事分開跑",[79,124,125],{},[15,126,127],{},"vp check",[79,129,130],{},"有，一次跑完",[61,132,133,136,141],{},[79,134,135],{},"Vitest",[79,137,138],{},[15,139,140],{},"vp test",[79,142,117],{},[61,144,145,148,153],{},[79,146,147],{},"安裝依賴",[79,149,150],{},[15,151,152],{},"vp install",[79,154,155],{},"沒有，仍用 pnpm",[11,157,158,161,162,165],{},[15,159,160],{},"vp staged","（對暫存檔跑檢查）與 ",[15,163,164],{},"vp hooks","（管理 Git hook）在指令列表裡也有，我們這篇不評價，因為還沒有拿它們取代過別的東西。",[11,167,168,169,172],{},"底層是哪些工具，",[15,170,171],{},"vp toolchain"," 會列出來。這個站目前的結果：",[174,175,181],"pre",{"className":176,"code":178,"language":179,"meta":180},[177],"language-text","vite-plus@1.0.0-rc.0\n├── depends on @voidzero-dev\u002Fvite-plus-core@1.0.0-rc.0\n│   ├── bundles vite@8.3.0\n│   │   └── uses rolldown@1.2.9\n│   │       ├── compiles oxc@0.150.0\n├── depends on vitest@5.0.1\n├── depends on oxlint@1.85.0\n├── depends on oxfmt@0.70.0\n","text","",[15,182,178],{"__ignoreMap":180},[11,184,185,186,188,189,191],{},"也就是說 ",[15,187,98],{}," 背後是 oxlint、",[15,190,114],{}," 背後是 oxfmt。",[41,193,195,196,198],{"id":194},"二用-node-version-釘住-nodejs-版本","二、用 ",[15,197,38],{}," 釘住 Node.js 版本",[11,200,201],{},"在專案根目錄跑：",[174,203,207],{"className":204,"code":205,"language":206,"meta":180,"style":180},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark-high-contrast","vp env pin 22.19.0\n","bash",[15,208,209],{"__ignoreMap":180},[210,211,214,217,221,224],"span",{"class":212,"line":213},"line",1,[210,215,17],{"class":216},"sLLcs",[210,218,220],{"class":219},"sswa9"," env",[210,222,223],{"class":219}," pin",[210,225,227],{"class":226},"sDtfP"," 22.19.0\n",[11,229,230,231,234,235,237,238,241,242,245,246,249],{},"實測會做兩件事：寫出內容為 ",[15,232,233],{},"22.19.0"," 的 ",[15,236,38],{},"，並且把這個版本預先下載好（不想下載可加 ",[15,239,240],{},"--no-install","）。之後在這個目錄裡跑 ",[15,243,244],{},"vp node -v","，預期得到 ",[15,247,248],{},"v22.19.0","。",[11,251,252],{},"在一個乾淨的環境裡，這確實成立。問題出在不乾淨的環境。",[41,254,256,257,260],{"id":255},"三坑vp-env-use-的覆寫會蓋過所有專案的-pin","三、坑：",[15,258,259],{},"vp env use"," 的覆寫會蓋過所有專案的 pin",[262,263,264],"h3",{"id":264},"症狀",[11,266,267,268,270,271,273,274,277],{},"專案裡 ",[15,269,38],{}," 是 ",[15,272,233],{},"，",[15,275,276],{},"vp env pin"," 也顯示成功，但：",[174,279,281],{"className":204,"code":280,"language":206,"meta":180,"style":180},"vp node -v\n# v24.21.0\n",[15,282,283,294],{"__ignoreMap":180},[210,284,285,287,290],{"class":212,"line":213},[210,286,17],{"class":216},[210,288,289],{"class":219}," node",[210,291,293],{"class":292},"sVYEq"," -v\n",[210,295,297],{"class":212,"line":296},2,[210,298,300],{"class":299},"sZf2p","# v24.21.0\n",[11,302,303,306,307,249],{},[15,304,305],{},"vp env doctor"," 的最後一行是 ",[15,308,309],{},"All checks passed",[262,311,312],{"id":312},"為什麼直覺會猜錯",[11,314,315,316,318],{},"直覺有兩個：懷疑 ",[15,317,38],{}," 沒被讀到，或懷疑 Vite+ 當下是「優先用系統 Node.js」的模式。我們兩個都先查了，都不是主因：",[320,321,322,333],"ul",{},[323,324,325,326,329,330,332],"li",{},"在 ",[15,327,328],{},"vp env off node","（優先用系統）的模式下，",[15,331,244],{}," 仍然用了 pin 的版本。",[323,334,335,336,339,340,342],{},"換成全新的 ",[15,337,338],{},"VP_HOME","（Vite+ 存放自己資料的目錄），同一個 ",[15,341,38],{}," 立刻生效。",[11,344,345,346,249],{},"差別在於，原本的環境裡曾經跑過 ",[15,347,259],{},[262,349,350],{"id":350},"根因",[11,352,353,356,357,359,360,363,364,367,368,370,371,373,374,377],{},[15,354,355],{},"vp env use 24.21.0"," 的說明寫的是「Activate ... for this shell session」，容易讓人以為只影響當下這個終端機。以一般執行檔呼叫 ",[15,358,17],{}," 時，實測並不是：它在 Vite+ 的資料目錄寫了一個 ",[15,361,362],{},".session-node-version"," 檔，內容是 ",[15,365,366],{},"24.21.0","（設了 ",[15,369,338],{}," 就在 ",[15,372,338],{}," 底下；沒設的話，在我們的機器上是 ",[15,375,376],{},"~\u002F.local\u002Fstate\u002Fvite-plus\u002F","）。檔案是全域的，所以：",[320,379,380,383,390],{},[323,381,382],{},"開另一個終端機、另一個 shell，一樣被覆寫。",[323,384,385,386,389],{},"切到完全不同的專案（那裡 pin 的是 ",[15,387,388],{},"20.19.0","），也一樣被覆寫。",[323,391,392,393,249],{},"優先序高過專案的 ",[15,394,38],{},[11,396,397,399,400,403],{},[15,398,305],{}," 會把它標成警告（",[15,401,402],{},"⚠ Session override (file)","），但整體仍然顯示通過，不會讓指令失敗。",[11,405,406,407,409,410,413,414,417,418,420,421,424,425,427,428,431],{},"以上是 ",[15,408,17],{}," 以一般執行檔呼叫時的行為。如果 shell 載入了 Vite+ 的整合（",[15,411,412],{},"type vp"," 顯示 ",[15,415,416],{},"vp is a function","），",[15,419,259],{}," 改設當前 shell 的環境變數 ",[15,422,423],{},"VP_NODE_VERSION","，不寫檔，影響範圍只到這個 shell 和它開出的子行程；",[15,426,305],{}," 顯示的是 ",[15,429,430],{},"Session override  VP_NODE_VERSION=24.21.0","。兩種情況都會蓋過切過去的每一個專案的 pin，差別只在新開的終端機會不會一起被蓋。",[262,433,434],{"id":434},"最小重現",[11,436,437,438,440],{},"在隔離的 ",[15,439,338],{}," 裡做，不碰既有環境：",[174,442,444],{"className":204,"code":443,"language":206,"meta":180,"style":180},"# vp 若是 shell 函式（type vp 顯示 function），以下的 vp 改成 command vp\nexport VP_HOME=$(mktemp -d)\nmkdir demo other\n(cd other && vp env pin 20.19.0)\ncd demo\nvp env pin 22.19.0\nvp node -v                  # v22.19.0\nvp env use 24.21.0\nvp node -v                  # v24.21.0，.node-version 還是 22.19.0\ncd ..\u002Fother && vp node -v   # v24.21.0，這個目錄 pin 的是 20.19.0\n# 另開終端機，export 同一個 VP_HOME 後到 other 跑 vp node -v，仍是 v24.21.0\nvp env use --unset\nvp node -v                  # v20.19.0，回到這個目錄自己 pin 的版本\n",[15,445,446,451,478,490,518,526,537,550,563,575,594,600,612],{"__ignoreMap":180},[210,447,448],{"class":212,"line":213},[210,449,450],{"class":299},"# vp 若是 shell 函式（type vp 顯示 function），以下的 vp 改成 command vp\n",[210,452,453,457,461,465,469,472,475],{"class":212,"line":296},[210,454,456],{"class":455},"sVLu3","export",[210,458,460],{"class":459},"sCGoy"," VP_HOME",[210,462,464],{"class":463},"sZaAb","=",[210,466,468],{"class":467},"s9KhW","$(",[210,470,471],{"class":216},"mktemp",[210,473,474],{"class":292}," -d",[210,476,477],{"class":467},")\n",[210,479,481,484,487],{"class":212,"line":480},3,[210,482,483],{"class":216},"mkdir",[210,485,486],{"class":219}," demo",[210,488,489],{"class":219}," other\n",[210,491,493,496,500,503,506,509,511,513,516],{"class":212,"line":492},4,[210,494,495],{"class":467},"(",[210,497,499],{"class":498},"sNB22","cd",[210,501,502],{"class":219}," other",[210,504,505],{"class":467}," &&",[210,507,508],{"class":216}," vp",[210,510,220],{"class":219},[210,512,223],{"class":219},[210,514,515],{"class":226}," 20.19.0",[210,517,477],{"class":467},[210,519,521,523],{"class":212,"line":520},5,[210,522,499],{"class":498},[210,524,525],{"class":219}," demo\n",[210,527,529,531,533,535],{"class":212,"line":528},6,[210,530,17],{"class":216},[210,532,220],{"class":219},[210,534,223],{"class":219},[210,536,227],{"class":226},[210,538,540,542,544,547],{"class":212,"line":539},7,[210,541,17],{"class":216},[210,543,289],{"class":219},[210,545,546],{"class":292}," -v",[210,548,549],{"class":299},"                  # v22.19.0\n",[210,551,553,555,557,560],{"class":212,"line":552},8,[210,554,17],{"class":216},[210,556,220],{"class":219},[210,558,559],{"class":219}," use",[210,561,562],{"class":226}," 24.21.0\n",[210,564,566,568,570,572],{"class":212,"line":565},9,[210,567,17],{"class":216},[210,569,289],{"class":219},[210,571,546],{"class":292},[210,573,574],{"class":299},"                  # v24.21.0，.node-version 還是 22.19.0\n",[210,576,578,580,583,585,587,589,591],{"class":212,"line":577},10,[210,579,499],{"class":498},[210,581,582],{"class":219}," ..\u002Fother",[210,584,505],{"class":467},[210,586,508],{"class":216},[210,588,289],{"class":219},[210,590,546],{"class":292},[210,592,593],{"class":299},"   # v24.21.0，這個目錄 pin 的是 20.19.0\n",[210,595,597],{"class":212,"line":596},11,[210,598,599],{"class":299},"# 另開終端機，export 同一個 VP_HOME 後到 other 跑 vp node -v，仍是 v24.21.0\n",[210,601,603,605,607,609],{"class":212,"line":602},12,[210,604,17],{"class":216},[210,606,220],{"class":219},[210,608,559],{"class":219},[210,610,611],{"class":292}," --unset\n",[210,613,615,617,619,621],{"class":212,"line":614},13,[210,616,17],{"class":216},[210,618,289],{"class":219},[210,620,546],{"class":292},[210,622,623],{"class":299},"                  # v20.19.0，回到這個目錄自己 pin 的版本\n",[262,625,626],{"id":626},"判準",[320,628,629,646,657],{},[323,630,631,634,635,637,638,641,642,645],{},[34,632,633],{},"pin 好了卻沒生效，先看覆寫，不要先改 pin。"," ",[15,636,305],{}," 裡有 ",[15,639,640],{},"Session override"," 就是它，",[15,643,644],{},"vp env use --unset"," 拿掉。",[323,647,648,654,655,249],{},[34,649,650,651,653],{},"不要把 ",[15,652,259],{}," 當成「只試一下」的指令。"," 它改的是全域狀態。要在單一專案固定版本，用 ",[15,656,276],{},[323,658,659,668,669,671],{},[34,660,661,664,665,667],{},[15,662,663],{},"vp env current"," 與 ",[15,666,244],{}," 兩個指令的答案不一定一致。"," 在「優先用系統」模式下，我們測到前者回報系統的 24.21.0，後者卻用了 pin 的 22.19.0。判斷實際跑的版本，以 ",[15,670,244],{}," 為準。",[41,673,675],{"id":674},"四管理模式是分開開關的","四、管理模式是分開開關的",[11,677,678,680,681,683,684,234,687,690],{},[15,679,21],{}," 不是全收或全不收。",[15,682,305],{}," 會分開列出 Node.js、npm、pnpm、Yarn、Bun 各自是「託管」還是「優先用系統」。這個站的開發機就是混合的：Node.js 仍由系統端的版本管理工具提供，pnpm 則交給 Vite+ 託管，版本取自 ",[15,685,686],{},"package.json",[15,688,689],{},"packageManager"," 欄位。",[11,692,693],{},"所以「Vite+ 能不能取代 nvm」要拆開看：版本管理它做得到，但你得決定哪些元件交給它。把 Node.js 留給原本的工具、只讓它管 pnpm，是可行的做法。",[41,695,697],{"id":696},"五升級紀錄與版本綁定","五、升級紀錄與版本綁定",[11,699,700,701,703],{},"這個站的 ",[15,702,31],{}," 在一個多月內升了兩次：",[55,705,706,716],{},[58,707,708],{},[61,709,710,713],{},[64,711,712],{},"日期",[64,714,715],{},"變化",[74,717,718,726],{},[61,719,720,723],{},[79,721,722],{},"2026-09-10",[79,724,725],{},"0.1.22 → 0.3.1",[61,727,728,731],{},[79,729,730],{},"2026-10-03",[79,732,733,734,737],{},"0.3.1 → 1.0.0-rc.0，",[15,735,736],{},"vitest"," 同時從 4.1.11 升到 5.0.1",[11,739,740],{},"兩點值得記：",[320,742,743,757],{},[323,744,745,753,754,756],{},[34,746,747,749,750,752],{},[15,748,736],{}," 版本跟著 ",[15,751,31],{}," 走。"," 1.0.0-rc.0 搭 ",[15,755,736],{}," 5.0.1，兩者是同一個 commit 一起換的，沒有單獨升其中一個。",[323,758,759,634,765,768,769,771,772,775,776,779,780,783,784,664,786,788],{},[34,760,761,764],{},[15,762,763],{},"vite"," 是被別名取代的。",[15,766,767],{},"pnpm-workspace.yaml"," 用 catalog 把 ",[15,770,763],{}," 指向 ",[15,773,774],{},"@voidzero-dev\u002Fvite-plus-core","，再用 ",[15,777,778],{},"overrides"," 讓所有依賴它的套件拿到同一份，並且在 ",[15,781,782],{},"peerDependencyRules"," 放行 ",[15,785,763],{},[15,787,736],{}," 的任意版本。",[11,790,791,792,795,796,799,800,802,803,805],{},"另一個會踩到的是 pnpm 的 release-age 冷卻：剛發布的版本在冷卻期內裝不起來。升 ",[15,793,794],{},"1.0.0-rc.0"," 時，pnpm 把例外寫進 ",[15,797,798],{},"minimumReleaseAgeExclude","，而且是一個一個列：",[15,801,31],{}," 本身、",[15,804,774],{},"，加上 8 個平台的二進位套件（macOS 與 Windows 各 arm64、x64 兩個，Linux 的 arm64、x64 再各分 glibc 與 musl 兩種）。例外名單要跟著版本號寫，下一個版本就得重來一次。",[11,807,808,809,811],{},"升完之後的驗證，這個站是跑完整的 generate、build、型別檢查、",[15,810,140],{},"、lint 與 format 檢查，全部退出碼為 0 才算數。",[41,813,814],{"id":814},"這篇沒有驗證的",[320,816,817,823,830],{},[323,818,819,820,822],{},"只在 Linux 上測過 ",[15,821,21],{},"，Windows 與 macOS 沒測。",[323,824,825,664,827,829],{},[15,826,160],{},[15,828,164],{}," 沒有拿來取代過別的東西，所以沒有評價。",[323,831,832],{},"1.0.0-rc.0 仍是候選版，指令與行為在正式版前可能再變。這篇的重現步驟以此版本為準。",[834,835],"content-highlight-style",{"css":836},"html pre.shiki code .sLLcs, html code.shiki .sLLcs{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .sswa9, html code.shiki .sswa9{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#ADDCFF}html pre.shiki code .sDtfP, html code.shiki .sDtfP{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#91CBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .sVYEq, html code.shiki .sVYEq{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .sZf2p, html code.shiki .sZf2p{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#BDC4CC;--shiki-dark-font-style:inherit}html pre.shiki code .sVLu3, html code.shiki .sVLu3{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .sCGoy, html code.shiki .sCGoy{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sZaAb, html code.shiki .sZaAb{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .s9KhW, html code.shiki .s9KhW{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sNB22, html code.shiki .sNB22{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#91CBFF}",{"title":180,"searchDepth":480,"depth":480,"links":838},[839,841,843,851,852,853],{"id":43,"depth":296,"text":840},"一、vp 一個指令收了哪些事",{"id":194,"depth":296,"text":842},"二、用 .node-version 釘住 Node.js 版本",{"id":255,"depth":296,"text":844,"children":845},"三、坑：vp env use 的覆寫會蓋過所有專案的 pin",[846,847,848,849,850],{"id":264,"depth":480,"text":264},{"id":312,"depth":480,"text":312},{"id":350,"depth":480,"text":350},{"id":434,"depth":480,"text":434},{"id":626,"depth":480,"text":626},{"id":674,"depth":296,"text":675},{"id":696,"depth":296,"text":697},{"id":814,"depth":296,"text":814},"看不見的故障","2026-10-09","vp env 能接手 Node.js 與套件管理器的版本，專案裡寫了 .node-version 也 pin 好了，跑出來的卻是別的版本。 原因是 vp env use 寫下的覆寫檔不只對當下的 shell 有效，還會蓋過所有專案的 pin。 附上隔離環境的重現步驟，以及這個站實際用 Vite+ 的版本與升級紀錄。",false,"md",null,"insight",{},true,"\u002Fblog\u002Fnodejs\u002Fvite-plus-env",{"title":5,"description":856},"blog\u002Fnodejs\u002Fvite-plus-env\u002Findex",[867,868,869],"Node.js","Vite","Debug","sUu87kIRWEgDvLYPXJdAmHM6V1g6vUiA9d1h7YHihN0",[872,874,875,876,877,878,881,883,884,885,886,887,889,890,891,892,893,894,896,898,899,901,902,903,904,905,906,907,908,909,910,911,912,913,914,915,916,917,919,921],{"category":873,"kind":860},"導入 AI",{"category":873,"kind":860},{"category":873,"kind":860},{"category":854,"kind":860},{"category":854,"kind":860},{"category":879,"kind":880},"Git","reference",{"category":882,"kind":880},"Linux",{"category":882,"kind":880},{"category":882,"kind":880},{"category":867,"kind":880},{"category":867,"kind":880},{"category":888,"kind":860},"品質與交付",{"category":867,"kind":880},{"category":888,"kind":860},{"category":854,"kind":860},{"category":867,"kind":880},{"category":873,"kind":860},{"category":895,"kind":860},"權限與資料安全",{"category":897,"kind":860},"上線前的決策",{"category":897,"kind":860},{"category":900,"kind":880},"Nuxt",{"category":888,"kind":860},{"category":900,"kind":880},{"category":900,"kind":880},{"category":888,"kind":860},{"category":888,"kind":860},{"category":895,"kind":860},{"category":854,"kind":860},{"category":897,"kind":860},{"category":854,"kind":860},{"category":888,"kind":860},{"category":888,"kind":860},{"category":854,"kind":860},{"category":895,"kind":860},{"category":854,"kind":860},{"category":897,"kind":860},{"category":854,"kind":860},{"category":918,"kind":880},"VS Code",{"category":920,"kind":880},"Vue",{"category":920,"kind":880},[923,928],{"title":924,"path":925,"stem":926,"description":927,"children":-1},"pnpm 的六個坑","\u002Fblog\u002Fnodejs\u002Fpnpm-pitfalls","blog\u002Fnodejs\u002Fpnpm-pitfalls\u002Findex","保留字讓你的 script 從沒被執行、dangling symlink 炸掉 build、跨版 orphan 讓 app 初始化 就崩、script 前置 install 把檢查機制自己修好、超長 PATH 讓 spawn 慢十倍。六個實際踩過的 pnpm 行為，以及各自的判準。",{"title":929,"path":930,"stem":931,"description":932,"children":-1},"AI 輔助開發與 CLAUDE.md","\u002Fblog\u002Fnuxt\u002Fai-assisted-claude-md","blog\u002Fnuxt\u002Fai-assisted-claude-md\u002Findex","寫給 AI 看的專案規範，寫了不代表會被遵守。真正決定它有沒有效的是三件事： 規範有沒有對應的機械檢查、彼此有沒有衝突、以及它有多長。",[],{"name":935,"avatar":936,"bio":937,"url":938},"Charles","\u002Ficons\u002Favatar.webp","前端工程師，主要使用 Vue.js \u002F Nuxt，分享軟體開發的踩坑經驗、新技術與最佳實踐心得。","\u002F",[940,944,951],{"path":925,"title":924,"description":927,"date":941,"category":888,"tags":942},"2026-08-25",[867,943,900,869],"pnpm",{"path":945,"title":946,"description":947,"date":941,"category":897,"tags":948},"\u002Fblog\u002Fnuxt\u002Fedge-runtime-gaps","邊緣執行環境的四個落差","本機正常、上了邊緣就壞。CDN 快取鍵不含 Accept 把 CSS 餵成 module script、dev 模式不套 Cloudflare preset 讓 binding 全數缺席、Nitro 追不到動態載入的 WASM、CDN 自動注入的 analytics beacon 撞上 CSP。四個落差與各自的判準。",[900,949,950,868,869],"Cloudflare","Nitro",{"path":952,"title":953,"description":954,"date":955,"category":873,"tags":956},"\u002Fblog\u002Fai\u002Fevan-you-taiwan-2026","聽尤雨溪談 AI 時代的工具鏈：瓶頸在驗證，不在寫","Evan You 在 Vue Conf Taiwan 2026 的演講，前半是 Vue、Vite 與 VoidZero 的近況，後半談 JavaScript 工具鏈該怎麼為 agent 重新設計。 這是我在現場聽完的整理，並對照我們團隊同時跑多個 agent 的經驗：後半幾乎每一點都對得上我們踩過的痛。","2026-10-18",[957,868,920],"AI",1792681764854]