[{"data":1,"prerenderedAt":1444},["ShallowReactive",2],{"search-dialog-posts":3,"post-\u002Fposts\u002Fpyenv-python-version-management":316,"post-surround-\u002Fposts\u002Fpyenv-python-version-management":1437},[4,12,21,29,37,47,57,67,77,85,96,104,112,121,129,137,147,155,164,173,181,188,195,207,215,224,236,241,247,254,260,266,272,279,286,291,298,304,310],{"path":5,"title":6,"description":7,"category":8,"tags":9},"\u002Fposts\u002Fgit-commands-cheatsheet","Git 常用指令與終端機筆記","2018 年整理的 Git 學習筆記，包含基本終端機指令、Git 操作、分支、reset、SourceTree、Vim 與一些常用設定。","notes",[10,11],"Git","版本控制",{"path":13,"title":14,"description":15,"category":16,"tags":17},"\u002Fposts\u002Fjs-prototype-inheritance","JavaScript 物件導向：建構函式、prototype 與繼承三步驟","整理 ES5 時代用建構函式加 prototype 手刻物件導向的完整流程——封裝屬性與方法、共用函式為什麼要掛在 prototype 上、以及繼承的三個步驟（call 借用母類別建構子、Object.create 接上原型鏈、修正 constructor 指向），並附上物件縮寫與計算屬性這兩個順便學到的 ES6 寫法。","frontend",[18,19,20],"JavaScript","物件導向","prototype",{"path":22,"title":23,"description":24,"category":16,"tags":25},"\u002Fposts\u002Fflexbox-notes","Flexbox 對齊與彈性尺寸筆記","記錄 Flexbox 的主軸與交錯軸對齊、flex-direction、flex-wrap，以及 flex-grow、flex-shrink、flex-basis 等常用屬性。",[26,27,28],"CSS","Flexbox","版面",{"path":30,"title":31,"description":32,"category":16,"tags":33},"\u002Fposts\u002Fscreen-orientation-api","screen.orientation：現在判斷與鎖定螢幕方向的做法","整理現在 JavaScript 判斷螢幕方向的方式，包括 screen.orientation、change 事件與 lock()，並說明 RWD 使用 matchMedia 或 CSS orientation 會更適合。",[18,34,35,36],"RWD","螢幕方向","PWA",{"path":38,"title":39,"description":40,"category":16,"tags":41},"\u002Fposts\u002Fpinia-vuex-state-management","Pinia \u002F Vuex 狀態管理及差異","紀錄 Vuex、Pinia 的狀態管理概念、差異與用法。",[42,43,44,45,46],"Pinia","Vuex","Vue3","狀態管理","Nuxt",{"path":48,"title":49,"description":50,"category":8,"tags":51},"\u002Fposts\u002Fpyenv-python-version-management","Python 多版本管理 pyenv","紀錄 macOS 與 Windows 使用 pyenv 管理 Python 版本的方式。",[52,53,54,55,56],"Python","pyenv","macOS","Windows","版本管理",{"path":58,"title":59,"description":60,"category":8,"tags":61},"\u002Fposts\u002Fnpm-publish-vue2-component","npm 發布流程","紀錄 Vue 2 元件的 npm 發布、dist-tag 與打包流程。",[62,63,64,65,66],"npm","Vue2","Vue CLI","套件發布","MIT",{"path":68,"title":69,"description":70,"category":8,"tags":71},"\u002Fposts\u002Fmobile-debugging-today","現在怎麼 debug 手機網頁：從裝一堆工具到幾乎不用裝","對照 2022 年那套 scoop、iTunes、remotedebug-ios-webkit-adapter 的繁瑣設定，整理現在偵錯手機網頁的做法：Android 無線偵錯、iOS 的實際選項、on-device console、Playwright 裝置模擬，以及本機 HTTPS 的處理。",[72,73,74,75,76],"debug","iOS","Android","Chrome DevTools","Playwright",{"path":78,"title":79,"description":80,"category":16,"tags":81},"\u002Fposts\u002Fplaywright-vs-cypress","E2E 測試現在選什麼：Playwright 與 Cypress 的實際差異","對照幾年前用 Cypress 的做法，說明 Playwright 為什麼變成多數新專案的預設選擇：真正的多瀏覽器支援、免費的平行執行、trace viewer 除錯，以及兩者在架構上的根本差異與各自仍然適合的場景。",[76,82,83,84],"Cypress","E2E","測試",{"path":86,"title":87,"description":88,"category":89,"tags":90},"\u002Fposts\u002Fjs-encoding-today","現在處理編碼：TextEncoder、Uint8Array 的 Base64 方法與 URLSearchParams","整理 JavaScript 現代編碼方式，使用 TextEncoder、TextDecoder 與 Uint8Array 處理 UTF-8 與 Base64，並說明 Base64URL、URLSearchParams 與 encodeURIComponent 的差異。","research",[91,92,93,94,95,18],"編碼","TextEncoder","Base64","URLSearchParams","Uint8Array",{"path":97,"title":98,"description":99,"category":16,"tags":100},"\u002Fposts\u002Fdart-sass-migration","Dart Sass 遷移筆記：node-sass 之後怎麼裝 Sass","node-sass 與 LibSass 已停止維護，現在改用 Dart Sass。整理 webpack 與 Vite 的安裝方式，以及為什麼不再像 node-sass 一樣容易遇到 Node.js 版本與原生編譯問題。",[101,102,103],"Sass","Webpack","Vite",{"path":105,"title":106,"description":107,"category":16,"tags":108},"\u002Fposts\u002Fdayjs-vue3-computed","day.js 搭配 Vue 3 的日期格式化","整理 Vue 3 日期格式化的常見做法，使用 day.js 取代舊專案常見的 moment.js，並以 method 或 computed 取代 Vue 2 filter，也補充原生 Intl.DateTimeFormat 的使用方式。",[44,109,110,111],"day.js","日期","Intl",{"path":113,"title":114,"description":115,"category":16,"tags":116},"\u002Fposts\u002Fes-modules-today","ES Module 現在的樣子：Node ESM、exports 與 import maps","整理現代 JavaScript ES Module 的常見用法，包括 Node.js 原生 ESM、package.json exports、瀏覽器 import maps、JSON import attributes，以及 Vite 動態載入的注意事項。",[117,118,119,120,103,18],"ES Module","Node.js","import maps","exports",{"path":122,"title":123,"description":124,"category":89,"tags":125},"\u002Fposts\u002Fstructured-clone","structuredClone()：終於有內建的深層拷貝","整理 JavaScript structuredClone() 的深層拷貝用法，說明 Date、Map、Set、循環參考的處理方式，以及 Function、class 與 Vue reactive Proxy 的注意事項。",[18,126,127,128],"structuredClone","深拷貝","Vue",{"path":130,"title":131,"description":132,"category":16,"tags":133},"\u002Fposts\u002Fdynamic-import","JavaScript 動態載入：import() 與 Vue 非同步元件","整理靜態 import、動態 import() 與 Vue defineAsyncComponent 的使用方式，並簡單補充 Vite import.meta.glob 的用途。",[18,134,135,103,136],"ES Modules","Vue 3","Code Splitting",{"path":138,"title":139,"description":140,"category":16,"tags":141},"\u002Fposts\u002Fscreen-wake-lock-api","Screen Wake Lock API：不用套件也能讓頁面保持亮著","整理 Screen Wake Lock API 的基本用法，說明 HTTPS、頁面可見性、切換分頁後重新取得 wake lock，以及 Vue／Nuxt 計時器使用時的注意事項。",[142,128,143,144,145,146],"Wake Lock","Nuxt4","行動裝置","計時器","Web API",{"path":148,"title":149,"description":150,"category":16,"tags":151},"\u002Fposts\u002Fhtml-meta-boilerplate-today","現在的 HTML meta 標籤怎麼寫","整理現在一個新頁面該有的 meta 標籤骨架——viewport 補上 viewport-fit=cover 因應瀏海與圓角螢幕、color-scheme 讓表單控制項跟著深色模式換色、theme-color 決定行動瀏覽器工具列顏色，以及基本的 SEO 與 Open Graph 標籤。",[152,153,154],"HTML","meta","SEO",{"path":156,"title":157,"description":158,"category":8,"tags":159},"\u002Fposts\u002Fdocker-node-postgres","把 Node + PostgreSQL 的 backend 裝進 Docker：Dockerfile、.dockerignore 與 compose","從「本機裝了就跑」到打包成容器的通用做法——Dockerfile 每一行的理由與 layer cache 為什麼決定了指令順序、.dockerignore 漏掉 node_modules 對原生模組是災難級的問題、容器之間為什麼不能用 localhost 互相找到對方，以及怎麼驗證資料真的寫進了 volume。",[160,161,118,162,163],"Docker","Docker Compose","PostgreSQL","Express",{"path":165,"title":166,"description":167,"category":16,"tags":168},"\u002Fposts\u002Fnuxt-server-proxy","Nuxt 3 \u002F 4 用 Server API 做代理與轉發","整理 Nuxt 3 與 Nuxt 4 使用 server\u002Fapi 轉發第三方 API 的方式，可以避免瀏覽器 CORS 問題、隱藏 API Key，也能在伺服器端加入快取。",[169,143,170,171,172],"Nuxt3","CORS","Nitro","API",{"path":174,"title":175,"description":176,"category":16,"tags":177},"\u002Fposts\u002Fnuxt4-project-setup","Nuxt 4 建立專案：和 Nuxt 3 有哪些不同","整理 Nuxt 4 建立專案的基本流程與目錄結構，並對照 Nuxt 3 的差異，包括 app\u002F 目錄、路徑別名、TypeScript，以及目前常用的 ESLint 與 Tailwind CSS 4 設定。",[143,44,178,179,180],"TypeScript","ESLint","Tailwind CSS 4",{"path":182,"title":183,"description":184,"category":16,"tags":185},"\u002Fposts\u002Fnuxt3-route-middleware","Nuxt 3 \u002F 4 的 Route Middleware：進頁面前先做判斷","整理 Nuxt 3 與 Nuxt 4 的 Route Middleware，用 defineNuxtRouteMiddleware 在進入頁面前檢查登入狀態，搭配 navigateTo 重新導向，並說明一般 middleware、global middleware 與 definePageMeta 的用法。",[169,143,186,187],"Middleware","驗證",{"path":189,"title":190,"description":191,"category":16,"tags":192},"\u002Fposts\u002Fnuxt-route-rules-redirect","Nuxt 3 \u002F 4 的 routeRules：重新導向與頁面設定","整理 Nuxt 3 與 Nuxt 4 的 routeRules 用法，可以依照不同網址設定重新導向、預渲染、SSR 與快取，並簡單對照 Nuxt 2 的舊做法。",[169,143,193,194],"routeRules","重新導向",{"path":196,"title":197,"description":198,"category":16,"tags":199},"\u002Fposts\u002Fios-iframe-google-login-blank-page","iOS iframe 串接 Google 登入出現空白頁：Popup 與頁面回收問題排查紀錄","記錄 iframe 內串接 Google 第三方登入時，部分 iOS 裝置因背景頁面遭到回收並重新載入，導致 Popup 登入流程中斷，以及最後透過 Redirect 分流與登入狀態接回機制處理的過程。",[73,200,201,202,203,204,205,206],"Safari","iframe","Google Login","GIS","OAuth","Popup","Redirect",{"path":208,"title":209,"description":210,"category":16,"tags":211},"\u002Fposts\u002Fios-login-tab-return-behavior","iOS 第三方登入後回不到原頁？分頁開啟關係與回頁行為實測","記錄 iOS Safari 與 Chrome 在多分頁第三方登入流程中，登入頁關閉後不一定回到原頁的實測，以及分頁開啟關係、window.close 與 window.focus 的限制。",[73,200,212,202,204,201,213,214],"Chrome","window.close","window.focus",{"path":216,"title":217,"description":218,"category":16,"tags":219},"\u002Fposts\u002Fgoogle-app-about-blank-placeholder","Google App 內建瀏覽器：about:blank 佔位頁被關閉的排查紀錄","記錄 iOS Google App 內建瀏覽器在非同步開啟外部頁面時，about:blank 佔位分頁被自動關閉，導致第三方登入或外部導轉失敗的排查與解法。",[73,220,221,222,204,223],"Google App","window.open","about:blank","WebView",{"path":225,"title":226,"description":227,"category":228,"tags":229},"\u002Fposts\u002Fclaude-code-codex-cli-init-basics","AI 相關紀錄 v1｜Claude Code \u002F Codex CLI 初始化與基本概念","從 Claude Code 與 Codex CLI 開始，整理 CLI、Coding Agent、Context、\u002Finit 與 CLAUDE.md／AGENTS.md 這幾個一開始最容易搞混的概念，以及在既有專案中的安裝與啟動方式。","ai",[230,231,232,233,234,235],"AI","Claude Code","Codex CLI","Coding Agent","CLAUDE.md","AGENTS.md",{"path":237,"title":238,"description":239,"category":228,"tags":240},"\u002Fposts\u002Fclaude-agents-md-project-guidelines","AI 相關紀錄 v2｜CLAUDE.md \u002F AGENTS.md 怎麼寫？專案規範與實際範例","整理 CLAUDE.md 與 AGENTS.md 的用途、適合記錄的專案規範，以及可直接參考的設定範例。",[230,231,232,234,235],{"path":242,"title":243,"description":244,"category":228,"tags":245},"\u002Fposts\u002Fclaude-codex-config-locations","AI 相關紀錄 v3｜Claude Code `.claude\u002F` \u002F Codex CLI 設定位置：全域與專案設定基本概要介紹","整理 Claude Code 與 Codex CLI 的使用者層級、專案層級設定位置，以及 rules、skills、agents 等目錄的用途。",[230,231,232,246],"設定檔",{"path":248,"title":249,"description":250,"category":228,"tags":251},"\u002Fposts\u002Fclaude-settings-codex-config","AI 相關紀錄 v4｜Claude Code settings \u002F Codex config 基本設定","整理 Claude Code settings.json 與 Codex config.toml 的設定層級、權限控制和常見基本設定。",[230,231,232,252,253],"settings.json","config.toml",{"path":255,"title":256,"description":257,"category":228,"tags":258},"\u002Fposts\u002Fclaude-codex-rules-structure","AI 相關紀錄 v5｜Rules：專案規則多了之後怎麼拆？","整理 Claude Code Rules 與 Codex AGENTS.md 的拆分方式，說明何時該拆、怎麼依目錄管理專案規則。",[230,231,232,259,235],"Rules",{"path":261,"title":262,"description":263,"category":228,"tags":264},"\u002Fposts\u002Fclaude-codex-slash-commands","AI 相關紀錄 v6｜Commands \u002F Slash Commands：CLI 裡的 `\u002F` 指令是什麼？","介紹 Claude Code 與 Codex CLI 的 Slash Commands，整理常用內建指令、自訂指令與 Skills 的差異。",[230,231,232,265],"Slash Commands",{"path":267,"title":268,"description":269,"category":228,"tags":270},"\u002Fposts\u002Fclaude-codex-skills","AI 相關紀錄 v7｜Skills：把重複工作流程整理起來","介紹 Claude Code 與 Codex Skills，整理適合保存成 Skill 的重複流程、基本結構與使用方式。",[230,231,232,271],"Skills",{"path":273,"title":274,"description":275,"category":228,"tags":276},"\u002Fposts\u002Fclaude-codex-agents-subagents","AI 相關紀錄 v8｜Agent \u002F Subagent：什麼時候真的需要分工？","整理 Agent、Subagent 與 Custom Agent 的用途，說明何時適合分工，以及它們和 Rules、Skills 的差異。",[230,231,232,277,278],"Agent","Subagent",{"path":280,"title":281,"description":282,"category":228,"tags":283},"\u002Fposts\u002Fmcp-basics","AI 相關紀錄 v9｜MCP：讓 AI 使用其他工具與資料","介紹 MCP 如何讓 AI 使用外部工具與資料，並以 Pencil MCP 說明實際用途、權限風險及與 Skill 的差異。",[230,284,231,232,285],"MCP","Pencil",{"path":287,"title":288,"description":289,"category":228,"tags":290},"\u002Fposts\u002Fai-coding-agent-concepts-summary","AI 相關紀錄 v10｜基本概念小結論","用一篇快速整理 CLAUDE.md、AGENTS.md、設定檔、Rules、Commands、Skills、Agent 與 MCP 的角色和差異。",[230,231,232,284,271,277],{"path":292,"title":293,"description":294,"category":228,"tags":295},"\u002Fposts\u002Fclaude-code-codex-model-selection","Claude Code 與 Codex：不同模型怎麼選？","比較 Claude Code 與 Codex 不同模型的速度、能力與成本取捨，依工作難度整理實用的選擇方向。",[230,231,296,297],"Codex","模型選擇",{"path":299,"title":300,"description":301,"category":228,"tags":302},"\u002Fposts\u002Fclaude-model-selection","Claude 模型簡單怎麼選？","整理 Claude 各模型在速度、能力與使用情境上的差異，提供從輕量工作到複雜任務的選擇方向。",[230,231,303,297],"Claude",{"path":305,"title":306,"description":307,"category":228,"tags":308},"\u002Fposts\u002Fcodex-model-selection","Codex 模型簡單怎麼選？","整理 Codex 各模型在速度、能力與使用情境上的差異，提供從簡單修改到大型端到端任務的選擇方向。",[230,296,309,297],"OpenAI",{"path":311,"title":312,"description":313,"category":228,"tags":314},"\u002Fposts\u002Fclaude-code-codex-cli-command-cheatsheet","Claude Code \u002F Codex CLI 常用指令速查表","整理 Claude Code 與 Codex CLI 的常用斜線指令、快捷操作與進階功能，方便需要時快速查找。",[230,231,232,265,315],"速查表",{"id":317,"title":49,"body":318,"category":8,"description":50,"draft":1426,"extension":1427,"image":1428,"meta":1429,"navigation":761,"path":48,"publishedAt":1430,"relatedExperiments":1431,"seo":1432,"stem":1433,"tags":1434,"updatedAt":1435,"__hash__":1436},"posts\u002Fposts\u002F20240807.pyenv-python-version-management.md",{"type":319,"value":320,"toc":1403},"minimark",[321,350,354,359,394,398,426,440,443,456,459,486,497,500,512,515,536,540,555,558,570,574,588,595,669,676,679,696,703,707,721,724,734,738,777,794,797,807,811,818,822,827,856,861,872,876,879,982,993,1061,1071,1075,1081,1090,1093,1099,1229,1232,1244,1247,1255,1258,1261,1287,1293,1296,1332,1336,1354,1366,1369,1373,1376,1379,1399],[322,323,324,328,336,347],"ul",{},[325,326,327],"li",{},"Python 的版本問題比 Node 麻煩：系統本身可能依賴某個版本的 Python，隨便升級會弄壞系統工具；不同專案又常常卡在不同版本。",[325,329,330,331,335],{},"pyenv 的做法是把各版本裝在使用者目錄下，用 shim 攔截 ",[332,333,334],"code",{},"python"," 指令再轉發到指定版本，不動到系統的 Python。",[325,337,338,342,343,346],{},[339,340,341],"strong",{},"要先分清楚兩件事","：pyenv 管的是「用哪一版 Python」，",[332,344,345],{},"venv"," 管的是「這個專案裝了哪些套件」。",[325,348,349],{},"兩者是互補的，不是二選一。",[351,352,54],"h2",{"id":353},"macos",[355,356,358],"h3",{"id":357},"一安裝-homebrew","一、安裝 Homebrew",[360,361,366],"pre",{"className":362,"code":363,"language":364,"meta":365,"style":365},"language-bash shiki shiki-themes github-light github-dark-default","\u002Fbin\u002Fbash -c \"$(curl -fsSL https:\u002F\u002Fraw.githubusercontent.com\u002FHomebrew\u002Finstall\u002FHEAD\u002Finstall.sh)\"\n","bash","",[332,367,368],{"__ignoreMap":365},[369,370,373,377,381,385,388,391],"span",{"class":371,"line":372},"line",1,[369,374,376],{"class":375},"sYRkA","\u002Fbin\u002Fbash",[369,378,380],{"class":379},"sPgVT"," -c",[369,382,384],{"class":383},"sNbzA"," \"$(",[369,386,387],{"class":375},"curl",[369,389,390],{"class":379}," -fsSL",[369,392,393],{"class":383}," https:\u002F\u002Fraw.githubusercontent.com\u002FHomebrew\u002Finstall\u002FHEAD\u002Finstall.sh)\"\n",[395,396,397],"p",{},"確認安裝路徑存在：",[360,399,401],{"className":362,"code":400,"language":364,"meta":365,"style":365},"ls \u002Fusr\u002Flocal\u002Fbin\u002Fbrew    # Intel Mac\nls \u002Fopt\u002Fhomebrew\u002Fbin\u002Fbrew # Apple Silicon (M1\u002FM2)\n",[332,402,403,415],{"__ignoreMap":365},[369,404,405,408,411],{"class":371,"line":372},[369,406,407],{"class":375},"ls",[369,409,410],{"class":383}," \u002Fusr\u002Flocal\u002Fbin\u002Fbrew",[369,412,414],{"class":413},"sPyeA","    # Intel Mac\n",[369,416,418,420,423],{"class":371,"line":417},2,[369,419,407],{"class":375},[369,421,422],{"class":383}," \u002Fopt\u002Fhomebrew\u002Fbin\u002Fbrew",[369,424,425],{"class":413}," # Apple Silicon (M1\u002FM2)\n",[395,427,428,431,432,435,436,439],{},[339,429,430],{},"兩個路徑不同是重點","：Apple Silicon 的 Homebrew 裝在 ",[332,433,434],{},"\u002Fopt\u002Fhomebrew","，而不是 Intel 時代的 ",[332,437,438],{},"\u002Fusr\u002Flocal","。網路上舊教學給的路徑常常是錯的。",[395,441,442],{},"設定 PATH：",[360,444,446],{"className":362,"code":445,"language":364,"meta":365,"style":365},"nano ~\u002F.zshrc\n",[332,447,448],{"__ignoreMap":365},[369,449,450,453],{"class":371,"line":372},[369,451,452],{"class":375},"nano",[369,454,455],{"class":383}," ~\u002F.zshrc\n",[395,457,458],{},"加入（Apple Silicon）：",[360,460,462],{"className":362,"code":461,"language":364,"meta":365,"style":365},"export PATH=\"\u002Fopt\u002Fhomebrew\u002Fbin:$PATH\"\n",[332,463,464],{"__ignoreMap":365},[369,465,466,470,474,477,480,483],{"class":371,"line":372},[369,467,469],{"class":468},"sDgYj","export",[369,471,473],{"class":472},"szmH1"," PATH",[369,475,476],{"class":468},"=",[369,478,479],{"class":383},"\"\u002Fopt\u002Fhomebrew\u002Fbin:",[369,481,482],{"class":472},"$PATH",[369,484,485],{"class":383},"\"\n",[395,487,488,489,492,493,496],{},"在 nano 裡按 ",[332,490,491],{},"Ctrl + X","，再按 ",[332,494,495],{},"Y"," 確認存檔。",[395,498,499],{},"重新載入設定：",[360,501,503],{"className":362,"code":502,"language":364,"meta":365,"style":365},"source ~\u002F.zshrc\n",[332,504,505],{"__ignoreMap":365},[369,506,507,510],{"class":371,"line":372},[369,508,509],{"class":379},"source",[369,511,455],{"class":383},[395,513,514],{},"確認變數正確：",[360,516,518],{"className":362,"code":517,"language":364,"meta":365,"style":365},"echo $PATH\nbrew --version\n",[332,519,520,528],{"__ignoreMap":365},[369,521,522,525],{"class":371,"line":372},[369,523,524],{"class":379},"echo",[369,526,527],{"class":472}," $PATH\n",[369,529,530,533],{"class":371,"line":417},[369,531,532],{"class":375},"brew",[369,534,535],{"class":379}," --version\n",[355,537,539],{"id":538},"二安裝-pyenv","二、安裝 pyenv",[360,541,543],{"className":362,"code":542,"language":364,"meta":365,"style":365},"brew install pyenv\n",[332,544,545],{"__ignoreMap":365},[369,546,547,549,552],{"class":371,"line":372},[369,548,532],{"class":375},[369,550,551],{"class":383}," install",[369,553,554],{"class":383}," pyenv\n",[395,556,557],{},"確認：",[360,559,561],{"className":362,"code":560,"language":364,"meta":365,"style":365},"pyenv versions\n",[332,562,563],{"__ignoreMap":365},[369,564,565,567],{"class":371,"line":372},[369,566,53],{"class":375},[369,568,569],{"class":383}," versions\n",[355,571,573],{"id":572},"三初始化-shell","三、初始化 shell",[395,575,576,579,580,583,584,587],{},[339,577,578],{},"這一步不能跳過。"," 只裝好 pyenv 但沒設定初始化的話，",[332,581,582],{},"pyenv install"," 能用，但 ",[332,585,586],{},"pyenv global"," 切了版本卻沒有效果——因為 shim 沒有掛進 PATH。",[395,589,590,591,594],{},"在 ",[332,592,593],{},"~\u002F.zshrc"," 加入：",[360,596,598],{"className":362,"code":597,"language":364,"meta":365,"style":365},"# pyenv\nexport PATH=\"$HOME\u002F.pyenv\u002Fbin:$PATH\"\neval \"$(pyenv init --path)\"\neval \"$(pyenv init -)\"\neval \"$(pyenv virtualenv-init -)\"\n",[332,599,600,605,626,645,657],{"__ignoreMap":365},[369,601,602],{"class":371,"line":372},[369,603,604],{"class":413},"# pyenv\n",[369,606,607,609,611,613,616,619,622,624],{"class":371,"line":417},[369,608,469],{"class":468},[369,610,473],{"class":472},[369,612,476],{"class":468},[369,614,615],{"class":383},"\"",[369,617,618],{"class":472},"$HOME",[369,620,621],{"class":383},"\u002F.pyenv\u002Fbin:",[369,623,482],{"class":472},[369,625,485],{"class":383},[369,627,629,632,634,636,639,642],{"class":371,"line":628},3,[369,630,631],{"class":379},"eval",[369,633,384],{"class":383},[369,635,53],{"class":375},[369,637,638],{"class":383}," init ",[369,640,641],{"class":379},"--path",[369,643,644],{"class":383},")\"\n",[369,646,648,650,652,654],{"class":371,"line":647},4,[369,649,631],{"class":379},[369,651,384],{"class":383},[369,653,53],{"class":375},[369,655,656],{"class":383}," init -)\"\n",[369,658,660,662,664,666],{"class":371,"line":659},5,[369,661,631],{"class":379},[369,663,384],{"class":383},[369,665,53],{"class":375},[369,667,668],{"class":383}," virtualenv-init -)\"\n",[395,670,671,672,675],{},"最後一行是給 ",[332,673,674],{},"pyenv-virtualenv"," 外掛用的，沒裝那個外掛可以省略。",[395,677,678],{},"重新載入並確認：",[360,680,682],{"className":362,"code":681,"language":364,"meta":365,"style":365},"source ~\u002F.zshrc\necho $PATH\n",[332,683,684,690],{"__ignoreMap":365},[369,685,686,688],{"class":371,"line":372},[369,687,509],{"class":379},[369,689,455],{"class":383},[369,691,692,694],{"class":371,"line":417},[369,693,524],{"class":379},[369,695,527],{"class":472},[395,697,698,699,702],{},"PATH 的最前面應該要看到 ",[332,700,701],{},".pyenv\u002Fshims","。",[355,704,706],{"id":705},"四安裝-python-版本","四、安裝 Python 版本",[360,708,710],{"className":362,"code":709,"language":364,"meta":365,"style":365},"pyenv install 3.9.6\n",[332,711,712],{"__ignoreMap":365},[369,713,714,716,718],{"class":371,"line":372},[369,715,53],{"class":375},[369,717,551],{"class":383},[369,719,720],{"class":379}," 3.9.6\n",[395,722,723],{},"確認裝好了：",[360,725,726],{"className":362,"code":560,"language":364,"meta":365,"style":365},[332,727,728],{"__ignoreMap":365},[369,729,730,732],{"class":371,"line":372},[369,731,53],{"class":375},[369,733,569],{"class":383},[355,735,737],{"id":736},"五設定要用哪一版","五、設定要用哪一版",[360,739,741],{"className":362,"code":740,"language":364,"meta":365,"style":365},"# 全域預設\npyenv global 3.9.6\n\n# 只在目前這個資料夾\npyenv local 3.9.6\n",[332,742,743,748,757,763,768],{"__ignoreMap":365},[369,744,745],{"class":371,"line":372},[369,746,747],{"class":413},"# 全域預設\n",[369,749,750,752,755],{"class":371,"line":417},[369,751,53],{"class":375},[369,753,754],{"class":383}," global",[369,756,720],{"class":379},[369,758,759],{"class":371,"line":628},[369,760,762],{"emptyLinePlaceholder":761},true,"\n",[369,764,765],{"class":371,"line":647},[369,766,767],{"class":413},"# 只在目前這個資料夾\n",[369,769,770,772,775],{"class":371,"line":659},[369,771,53],{"class":375},[369,773,774],{"class":383}," local",[369,776,720],{"class":379},[395,778,779,782,783,786,787,790,791,702],{},[332,780,781],{},"pyenv local"," 會在當前目錄產生一個 ",[332,784,785],{},".python-version"," 檔案，之後只要 ",[332,788,789],{},"cd"," 進這個目錄就自動切換版本。這個檔案建議進版控，效果類似 Node 的 ",[332,792,793],{},".nvmrc",[351,795,55],{"id":796},"windows",[395,798,799,800,702],{},"Windows 用的是另一個專案：",[801,802,806],"a",{"href":803,"rel":804},"https:\u002F\u002Fgithub.com\u002Fpyenv-win\u002Fpyenv-win",[805],"nofollow","pyenv-win",[355,808,810],{"id":809},"一確認-pip-可用","一、確認 pip 可用",[395,812,813,814,817],{},"開啟 PowerShell 或 cmd，輸入 ",[332,815,816],{},"pip","。有出現說明訊息就代表可以用 pip 安裝。",[355,819,821],{"id":820},"二安裝","二、安裝",[395,823,824],{},[339,825,826],{},"PowerShell：",[360,828,832],{"className":829,"code":830,"language":831,"meta":365,"style":365},"language-powershell shiki shiki-themes github-light github-dark-default","pip install pyenv-win --target $HOME\\.pyenv\n","powershell",[332,833,834],{"__ignoreMap":365},[369,835,836,839,842,845,848,851,853],{"class":371,"line":372},[369,837,838],{"class":472},"pip install pyenv",[369,840,841],{"class":468},"-",[369,843,844],{"class":472},"win ",[369,846,847],{"class":468},"--",[369,849,850],{"class":472},"target ",[369,852,618],{"class":379},[369,854,855],{"class":472},"\\.pyenv\n",[395,857,858],{},[339,859,860],{},"cmd：",[360,862,866],{"className":863,"code":864,"language":865,"meta":365,"style":365},"language-cmd shiki shiki-themes github-light github-dark-default","pip install pyenv-win --target %USERPROFILE%\\.pyenv\n","cmd",[332,867,868],{"__ignoreMap":365},[369,869,870],{"class":371,"line":372},[369,871,864],{"class":472},[355,873,875],{"id":874},"三設定環境變數","三、設定環境變數",[395,877,878],{},"在 PowerShell 執行：",[360,880,882],{"className":829,"code":881,"language":831,"meta":365,"style":365},"[System.Environment]::SetEnvironmentVariable('PYENV', $env:USERPROFILE + \"\\.pyenv\\pyenv-win\\\", \"User\")\n[System.Environment]::SetEnvironmentVariable('PYENV_ROOT', $env:USERPROFILE + \"\\.pyenv\\pyenv-win\\\", \"User\")\n[System.Environment]::SetEnvironmentVariable('PYENV_HOME', $env:USERPROFILE + \"\\.pyenv\\pyenv-win\\\", \"User\")\n",[332,883,884,924,953],{"__ignoreMap":365},[369,885,886,889,892,895,898,901,904,907,910,913,916,918,921],{"class":371,"line":372},[369,887,888],{"class":472},"[",[369,890,891],{"class":468},"System.Environment",[369,893,894],{"class":472},"]::SetEnvironmentVariable(",[369,896,897],{"class":383},"'PYENV'",[369,899,900],{"class":468},",",[369,902,903],{"class":472}," $",[369,905,906],{"class":379},"env:",[369,908,909],{"class":472},"USERPROFILE ",[369,911,912],{"class":468},"+",[369,914,915],{"class":383}," \"\\.pyenv\\pyenv-win\\\"",[369,917,900],{"class":468},[369,919,920],{"class":383}," \"User\"",[369,922,923],{"class":472},")\n",[369,925,926,928,930,932,935,937,939,941,943,945,947,949,951],{"class":371,"line":417},[369,927,888],{"class":472},[369,929,891],{"class":468},[369,931,894],{"class":472},[369,933,934],{"class":383},"'PYENV_ROOT'",[369,936,900],{"class":468},[369,938,903],{"class":472},[369,940,906],{"class":379},[369,942,909],{"class":472},[369,944,912],{"class":468},[369,946,915],{"class":383},[369,948,900],{"class":468},[369,950,920],{"class":383},[369,952,923],{"class":472},[369,954,955,957,959,961,964,966,968,970,972,974,976,978,980],{"class":371,"line":628},[369,956,888],{"class":472},[369,958,891],{"class":468},[369,960,894],{"class":472},[369,962,963],{"class":383},"'PYENV_HOME'",[369,965,900],{"class":468},[369,967,903],{"class":472},[369,969,906],{"class":379},[369,971,909],{"class":472},[369,973,912],{"class":468},[369,975,915],{"class":383},[369,977,900],{"class":468},[369,979,920],{"class":383},[369,981,923],{"class":472},[395,983,984,985,988,989,992],{},"再把 pyenv 的 ",[332,986,987],{},"bin"," 與 ",[332,990,991],{},"shims"," 加進 PATH：",[360,994,996],{"className":829,"code":995,"language":831,"meta":365,"style":365},"[System.Environment]::SetEnvironmentVariable('path', $env:USERPROFILE + \"\\.pyenv\\pyenv-win\\bin;\" + $env:USERPROFILE + \"\\.pyenv\\pyenv-win\\shims;\" + [System.Environment]::GetEnvironmentVariable('path', \"User\"), \"User\")\n",[332,997,998],{"__ignoreMap":365},[369,999,1000,1002,1004,1006,1009,1011,1013,1015,1017,1019,1022,1025,1027,1029,1031,1033,1036,1038,1041,1043,1046,1048,1050,1052,1055,1057,1059],{"class":371,"line":372},[369,1001,888],{"class":472},[369,1003,891],{"class":468},[369,1005,894],{"class":472},[369,1007,1008],{"class":383},"'path'",[369,1010,900],{"class":468},[369,1012,903],{"class":472},[369,1014,906],{"class":379},[369,1016,909],{"class":472},[369,1018,912],{"class":468},[369,1020,1021],{"class":383}," \"\\.pyenv\\pyenv-win\\bin;\"",[369,1023,1024],{"class":468}," +",[369,1026,903],{"class":472},[369,1028,906],{"class":379},[369,1030,909],{"class":472},[369,1032,912],{"class":468},[369,1034,1035],{"class":383}," \"\\.pyenv\\pyenv-win\\shims;\"",[369,1037,1024],{"class":468},[369,1039,1040],{"class":472}," [",[369,1042,891],{"class":468},[369,1044,1045],{"class":472},"]::GetEnvironmentVariable(",[369,1047,1008],{"class":383},[369,1049,900],{"class":468},[369,1051,920],{"class":383},[369,1053,1054],{"class":472},")",[369,1056,900],{"class":468},[369,1058,920],{"class":383},[369,1060,923],{"class":472},[395,1062,1063,1064,1067,1068,1070],{},"注意 shims 要放在 PATH 的",[339,1065,1066],{},"前面","，才能攔截到 ",[332,1069,334],{}," 指令。",[355,1072,1074],{"id":1073},"四驗證","四、驗證",[395,1076,1077,1080],{},[339,1078,1079],{},"關閉所有開著的終端機（PowerShell 與 cmd），重新開啟。"," 環境變數的變更不會套用到已經開著的視窗，這是最容易卡住的一步。",[360,1082,1084],{"className":863,"code":1083,"language":865,"meta":365,"style":365},"pyenv --version\n",[332,1085,1086],{"__ignoreMap":365},[369,1087,1088],{"class":371,"line":372},[369,1089,1083],{"class":472},[351,1091,1092],{"id":1092},"常用指令",[395,1094,1095,1096,1098],{},"輸入 ",[332,1097,53],{}," 會列出所有可用指令：",[1100,1101,1102,1115],"table",{},[1103,1104,1105],"thead",{},[1106,1107,1108,1112],"tr",{},[1109,1110,1111],"th",{},"指令",[1109,1113,1114],{},"用途",[1116,1117,1118,1129,1139,1149,1159,1169,1179,1189,1199,1209,1219],"tbody",{},[1106,1119,1120,1126],{},[1121,1122,1123],"td",{},[332,1124,1125],{},"pyenv versions",[1121,1127,1128],{},"列出所有已安裝的 Python 版本",[1106,1130,1131,1136],{},[1121,1132,1133],{},[332,1134,1135],{},"pyenv version",[1121,1137,1138],{},"顯示目前使用的版本與它的來源",[1106,1140,1141,1146],{},[1121,1142,1143],{},[332,1144,1145],{},"pyenv install -l",[1121,1147,1148],{},"列出所有可安裝的版本",[1106,1150,1151,1156],{},[1121,1152,1153],{},[332,1154,1155],{},"pyenv install \u003Cversion>",[1121,1157,1158],{},"安裝指定版本",[1106,1160,1161,1166],{},[1121,1162,1163],{},[332,1164,1165],{},"pyenv uninstall \u003Cversion>",[1121,1167,1168],{},"移除指定版本",[1106,1170,1171,1176],{},[1121,1172,1173],{},[332,1174,1175],{},"pyenv global \u003Cversion>",[1121,1177,1178],{},"設定全域版本",[1106,1180,1181,1186],{},[1121,1182,1183],{},[332,1184,1185],{},"pyenv local \u003Cversion>",[1121,1187,1188],{},"設定當前目錄的版本",[1106,1190,1191,1196],{},[1121,1192,1193],{},[332,1194,1195],{},"pyenv shell \u003Cversion>",[1121,1197,1198],{},"只對目前這個 shell 生效",[1106,1200,1201,1206],{},[1121,1202,1203],{},[332,1204,1205],{},"pyenv rehash",[1121,1207,1208],{},"重建 shims",[1106,1210,1211,1216],{},[1121,1212,1213],{},[332,1214,1215],{},"pyenv which \u003Cexecutable>",[1121,1217,1218],{},"顯示某個執行檔的完整路徑",[1106,1220,1221,1226],{},[1121,1222,1223],{},[332,1224,1225],{},"pyenv update",[1121,1227,1228],{},"更新可安裝版本的清單快取",[355,1230,1231],{"id":1231},"查看目前版本",[360,1233,1235],{"className":362,"code":1234,"language":364,"meta":365,"style":365},"pyenv version\n",[332,1236,1237],{"__ignoreMap":365},[369,1238,1239,1241],{"class":371,"line":372},[369,1240,53],{"class":375},[369,1242,1243],{"class":383}," version\n",[395,1245,1246],{},"還沒設定過的話會看到提示：",[360,1248,1253],{"className":1249,"code":1251,"language":1252,"meta":365},[1250],"language-text","No global\u002Flocal python version has been set yet. Please set the global\u002Flocal version by typing:\npyenv global 3.7.4\npyenv local 3.7.4\n","text",[332,1254,1251],{"__ignoreMap":365},[355,1256,1257],{"id":1257},"版本的優先順序",[395,1259,1260],{},"三個層級由高到低：",[1262,1263,1264,1274,1282],"ol",{},[325,1265,1266,1269,1270,1273],{},[332,1267,1268],{},"pyenv shell","（環境變數 ",[332,1271,1272],{},"PYENV_VERSION","）——只在目前 shell 生效",[325,1275,1276,1278,1279,1281],{},[332,1277,781],{},"（",[332,1280,785],{}," 檔案）——當前目錄與其子目錄",[325,1283,1284,1286],{},[332,1285,586],{},"——其他所有情況",[395,1288,1289,1290,1292],{},"搞不清楚現在為什麼是某個版本時，",[332,1291,1135],{}," 會直接告訴你它是從哪一層來的。",[355,1294,1295],{"id":1295},"安裝特定版本",[360,1297,1299],{"className":362,"code":1298,"language":364,"meta":365,"style":365},"# 正式版\npyenv install 3.9.6\n\n# 預覽版也裝得到\npyenv install 3.10.0b3\n",[332,1300,1301,1306,1314,1318,1323],{"__ignoreMap":365},[369,1302,1303],{"class":371,"line":372},[369,1304,1305],{"class":413},"# 正式版\n",[369,1307,1308,1310,1312],{"class":371,"line":417},[369,1309,53],{"class":375},[369,1311,551],{"class":383},[369,1313,720],{"class":379},[369,1315,1316],{"class":371,"line":628},[369,1317,762],{"emptyLinePlaceholder":761},[369,1319,1320],{"class":371,"line":647},[369,1321,1322],{"class":413},"# 預覽版也裝得到\n",[369,1324,1325,1327,1329],{"class":371,"line":659},[369,1326,53],{"class":375},[369,1328,551],{"class":383},[369,1330,1331],{"class":383}," 3.10.0b3\n",[355,1333,1335],{"id":1334},"什麼時候要-pyenv-rehash","什麼時候要 pyenv rehash",[395,1337,1338,1339,1342,1343,1342,1346,1349,1350,1353],{},"如果用 pip 安裝或移除了會產生執行檔的套件（例如 ",[332,1340,1341],{},"black","、",[332,1344,1345],{},"pytest",[332,1347,1348],{},"django-admin","），或是手動改動了某個版本資料夾的內容，要執行 ",[332,1351,1352],{},"rehash"," 通知 pyenv 重建對應的 shim：",[360,1355,1357],{"className":362,"code":1356,"language":364,"meta":365,"style":365},"pyenv rehash\n",[332,1358,1359],{"__ignoreMap":365},[369,1360,1361,1363],{"class":371,"line":372},[369,1362,53],{"class":375},[369,1364,1365],{"class":383}," rehash\n",[395,1367,1368],{},"沒 rehash 的症狀是：套件明明裝好了，但輸入指令卻說找不到。",[351,1370,1372],{"id":1371},"_2026-年後記","2026 年後記",[395,1374,1375],{},"本文保留當時 pyenv 與 pyenv-win 的安裝紀錄。Python、Homebrew、pip 與作業系統的安裝方式會更新，實作前請以目前官方文件確認。",[351,1377,1378],{"id":1378},"相關連結",[322,1380,1381,1387,1392],{},[325,1382,1383],{},[801,1384,53],{"href":1385,"rel":1386},"https:\u002F\u002Fgithub.com\u002Fpyenv\u002Fpyenv",[805],[325,1388,1389],{},[801,1390,806],{"href":803,"rel":1391},[805],[325,1393,1394],{},[801,1395,1398],{"href":1396,"rel":1397},"https:\u002F\u002Fbrew.sh\u002F",[805],"Homebrew",[1400,1401,1402],"style",{},"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 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}",{"title":365,"searchDepth":628,"depth":628,"links":1404},[1405,1412,1418,1424,1425],{"id":353,"depth":417,"text":54,"children":1406},[1407,1408,1409,1410,1411],{"id":357,"depth":628,"text":358},{"id":538,"depth":628,"text":539},{"id":572,"depth":628,"text":573},{"id":705,"depth":628,"text":706},{"id":736,"depth":628,"text":737},{"id":796,"depth":417,"text":55,"children":1413},[1414,1415,1416,1417],{"id":809,"depth":628,"text":810},{"id":820,"depth":628,"text":821},{"id":874,"depth":628,"text":875},{"id":1073,"depth":628,"text":1074},{"id":1092,"depth":417,"text":1092,"children":1419},[1420,1421,1422,1423],{"id":1231,"depth":628,"text":1231},{"id":1257,"depth":628,"text":1257},{"id":1295,"depth":628,"text":1295},{"id":1334,"depth":628,"text":1335},{"id":1371,"depth":417,"text":1372},{"id":1378,"depth":417,"text":1378},false,"md",null,{},"2024-08-07",[],{"title":49,"description":50},"posts\u002F20240807.pyenv-python-version-management",[52,53,54,55,56],"2026-08-12","loUW6BPozKCQO5Z7N5DKZ0W9JuMiPqDENWwHHDq3kkI",[1438,1441],{"title":59,"path":58,"stem":1439,"description":60,"category":8,"publishedAt":1440,"children":-1},"posts\u002F20250122.npm-publish-vue2-component","2025-01-22",{"title":39,"path":38,"stem":1442,"description":40,"category":16,"publishedAt":1443,"children":-1},"posts\u002F20240505.pinia-vuex-state-management","2024-05-05",1791786901526]