[{"data":1,"prerenderedAt":1153},["ShallowReactive",2],{"search-dialog-posts":3,"post-\u002Fposts\u002Fdynamic-import":316,"post-surround-\u002Fposts\u002Fdynamic-import":1146},[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":131,"body":318,"category":16,"description":132,"draft":1136,"extension":1137,"image":1138,"meta":1139,"navigation":389,"path":130,"publishedAt":1140,"relatedExperiments":1141,"seo":1142,"stem":1143,"tags":1144,"updatedAt":1140,"__hash__":1145},"posts\u002Fposts\u002F20260417.dynamic-import.md",{"type":319,"value":320,"toc":1127},"minimark",[321,347,352,356,401,404,415,421,425,430,502,505,519,522,525,543,546,550,557,703,706,709,717,720,724,727,760,763,769,772,842,845,873,876,879,927,930,936,942,945,948,1005,1008,1011,1028,1031,1055,1058,1089,1095,1098,1123],[322,323,324,337,340],"ul",{},[325,326,327,328,332,333,336],"li",{},"一般 ",[329,330,331],"code",{},"import"," 會在模組載入時建立依賴；",[329,334,335],{},"import()"," 則可以等到真的需要功能時再載入。",[325,338,339],{},"打包工具通常也會利用動態載入進行 code splitting，把較大的功能拆成獨立 chunk。",[325,341,342],{},[343,344],"img",{"alt":345,"src":346},"靜態載入與動態載入流程比較","\u002Fimages\u002Fposts\u002Fdynamic-import\u002Floading-flow.webp",[348,349,351],"h2",{"id":350},"靜態-import","靜態 import",[353,354,355],"p",{},"一般最常使用的是靜態 import：",[357,358,363],"pre",{"className":359,"code":360,"language":361,"meta":362,"style":362},"language-ts shiki shiki-themes github-light github-dark-default","import { formatReport } from '.\u002Freport'\n\nformatReport(data)\n","ts","",[329,364,365,384,391],{"__ignoreMap":362},[366,367,370,373,377,380],"span",{"class":368,"line":369},"line",1,[366,371,331],{"class":372},"sDgYj",[366,374,376],{"class":375},"szmH1"," { formatReport } ",[366,378,379],{"class":372},"from",[366,381,383],{"class":382},"sNbzA"," '.\u002Freport'\n",[366,385,387],{"class":368,"line":386},2,[366,388,390],{"emptyLinePlaceholder":389},true,"\n",[366,392,394,398],{"class":368,"line":393},3,[366,395,397],{"class":396},"sVjZL","formatReport",[366,399,400],{"class":375},"(data)\n",[353,402,403],{},"適合：",[322,405,406,409,412],{},[325,407,408],{},"頁面一定會使用的功能",[325,410,411],{},"體積不大的模組",[325,413,414],{},"元件初始化時就需要的程式碼",[353,416,417,418,420],{},"如果沒有特別需要延後載入，使用一般 ",[329,419,331],{}," 就可以。",[348,422,424],{"id":423},"動態-import","動態 import()",[353,426,427,429],{},[329,428,335],{}," 會回傳 Promise，可以等到需要時再載入：",[357,431,433],{"className":359,"code":432,"language":361,"meta":362,"style":362},"async function exportReport() {\n  const { formatReport } = await import('.\u002Freport')\n\n  return formatReport(data)\n}\n",[329,434,435,449,481,485,496],{"__ignoreMap":362},[366,436,437,440,443,446],{"class":368,"line":369},[366,438,439],{"class":372},"async",[366,441,442],{"class":372}," function",[366,444,445],{"class":396}," exportReport",[366,447,448],{"class":375},"() {\n",[366,450,451,454,457,460,463,466,469,472,475,478],{"class":368,"line":386},[366,452,453],{"class":372},"  const",[366,455,456],{"class":375}," { ",[366,458,397],{"class":459},"sPgVT",[366,461,462],{"class":375}," } ",[366,464,465],{"class":372},"=",[366,467,468],{"class":372}," await",[366,470,471],{"class":372}," import",[366,473,474],{"class":375},"(",[366,476,477],{"class":382},"'.\u002Freport'",[366,479,480],{"class":375},")\n",[366,482,483],{"class":368,"line":393},[366,484,390],{"emptyLinePlaceholder":389},[366,486,488,491,494],{"class":368,"line":487},4,[366,489,490],{"class":372},"  return",[366,492,493],{"class":396}," formatReport",[366,495,400],{"class":375},[366,497,499],{"class":368,"line":498},5,[366,500,501],{"class":375},"}\n",[353,503,504],{},"比較適合：",[322,506,507,510,513,516],{},[325,508,509],{},"大型圖表",[325,511,512],{},"編輯器",[325,514,515],{},"匯出功能",[325,517,518],{},"只有部分使用者會使用的功能",[353,520,521],{},"例如使用者按下「匯出」之後，才載入相關套件，就不用把這些程式碼全部放進初始 bundle。",[353,523,524],{},"路徑最好讓打包工具可以在 build 時分析：",[357,526,528],{"className":359,"code":527,"language":361,"meta":362,"style":362},"await import('.\u002Freport')\n",[329,529,530],{"__ignoreMap":362},[366,531,532,535,537,539,541],{"class":368,"line":369},[366,533,534],{"class":372},"await",[366,536,471],{"class":372},[366,538,474],{"class":375},[366,540,477],{"class":382},[366,542,480],{"class":375},[353,544,545],{},"不要依賴完全由執行期字串拼出的路徑，否則打包工具可能無法判斷要把哪些檔案放進產物。",[348,547,549],{"id":548},"vue-3-非同步元件","Vue 3 非同步元件",[353,551,552,553,556],{},"Vue 可以搭配 ",[329,554,555],{},"defineAsyncComponent()"," 延後載入元件：",[357,558,562],{"className":559,"code":560,"language":561,"meta":362,"style":362},"language-vue shiki shiki-themes github-light github-dark-default","\u003Cscript setup lang=\"ts\">\nimport { defineAsyncComponent } from 'vue'\n\nconst HeavyEditor = defineAsyncComponent(\n  () => import('.\u002FHeavyEditor.vue')\n)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CHeavyEditor v-if=\"isEditing\" \u002F>\n\u003C\u002Ftemplate>\n","vue",[329,563,564,588,600,604,621,638,643,653,658,668,694],{"__ignoreMap":362},[366,565,566,569,573,577,580,582,585],{"class":368,"line":369},[366,567,568],{"class":375},"\u003C",[366,570,572],{"class":571},"sXQ1o","script",[366,574,576],{"class":575},"syv4q"," setup",[366,578,579],{"class":575}," lang",[366,581,465],{"class":375},[366,583,584],{"class":382},"\"ts\"",[366,586,587],{"class":375},">\n",[366,589,590,592,595,597],{"class":368,"line":386},[366,591,331],{"class":372},[366,593,594],{"class":375}," { defineAsyncComponent } ",[366,596,379],{"class":372},[366,598,599],{"class":382}," 'vue'\n",[366,601,602],{"class":368,"line":393},[366,603,390],{"emptyLinePlaceholder":389},[366,605,606,609,612,615,618],{"class":368,"line":487},[366,607,608],{"class":372},"const",[366,610,611],{"class":459}," HeavyEditor",[366,613,614],{"class":372}," =",[366,616,617],{"class":396}," defineAsyncComponent",[366,619,620],{"class":375},"(\n",[366,622,623,626,629,631,633,636],{"class":368,"line":498},[366,624,625],{"class":375},"  () ",[366,627,628],{"class":372},"=>",[366,630,471],{"class":372},[366,632,474],{"class":375},[366,634,635],{"class":382},"'.\u002FHeavyEditor.vue'",[366,637,480],{"class":375},[366,639,641],{"class":368,"line":640},6,[366,642,480],{"class":375},[366,644,646,649,651],{"class":368,"line":645},7,[366,647,648],{"class":375},"\u003C\u002F",[366,650,572],{"class":571},[366,652,587],{"class":375},[366,654,656],{"class":368,"line":655},8,[366,657,390],{"emptyLinePlaceholder":389},[366,659,661,663,666],{"class":368,"line":660},9,[366,662,568],{"class":375},[366,664,665],{"class":571},"template",[366,667,587],{"class":375},[366,669,671,674,677,680,682,686,689,691],{"class":368,"line":670},10,[366,672,673],{"class":375},"  \u003C",[366,675,676],{"class":571},"HeavyEditor",[366,678,679],{"class":372}," v-if",[366,681,465],{"class":375},[366,683,685],{"class":684},"spddc","\"",[366,687,688],{"class":375},"isEditing",[366,690,685],{"class":684},[366,692,693],{"class":375}," \u002F>\n",[366,695,697,699,701],{"class":368,"line":696},11,[366,698,648],{"class":375},[366,700,665],{"class":571},[366,702,587],{"class":375},[353,704,705],{},"這種方式適合只在特定情況下才會顯示的元件。",[353,707,708],{},"例如：",[357,710,715],{"className":711,"code":713,"language":714,"meta":362},[712],"language-text","開啟編輯器\n打開大型圖表\n開啟報表\n進入特定功能頁\n","text",[329,716,713],{"__ignoreMap":362},[353,718,719],{},"如果是首屏一定會顯示的核心元件，就沒有必要全部改成非同步載入。",[348,721,723],{"id":722},"補充vite-的-importmetaglob","補充：Vite 的 import.meta.glob",[353,725,726],{},"如果需要一次取得某個資料夾裡的一批模組，Vite 另外提供：",[357,728,730],{"className":359,"code":729,"language":361,"meta":362,"style":362},"const modules = import.meta.glob('.\u002Fwidgets\u002F*.vue')\n",[329,731,732],{"__ignoreMap":362},[366,733,734,736,739,741,743,746,748,750,753,755,758],{"class":368,"line":369},[366,735,608],{"class":372},[366,737,738],{"class":459}," modules",[366,740,614],{"class":372},[366,742,471],{"class":372},[366,744,745],{"class":375},".",[366,747,153],{"class":459},[366,749,745],{"class":375},[366,751,752],{"class":396},"glob",[366,754,474],{"class":375},[366,756,757],{"class":382},"'.\u002Fwidgets\u002F*.vue'",[366,759,480],{"class":375},[353,761,762],{},"假設資料夾是：",[357,764,767],{"className":765,"code":766,"language":714,"meta":362},[712],"widgets\u002F\n├── chart.vue\n├── map.vue\n└── table.vue\n",[329,768,766],{"__ignoreMap":362},[353,770,771],{},"Vite 會建立類似這樣的對照：",[357,773,775],{"className":359,"code":774,"language":361,"meta":362,"style":362},"{\n  '.\u002Fwidgets\u002Fchart.vue': () => import('.\u002Fwidgets\u002Fchart.vue'),\n  '.\u002Fwidgets\u002Fmap.vue': () => import('.\u002Fwidgets\u002Fmap.vue'),\n  '.\u002Fwidgets\u002Ftable.vue': () => import('.\u002Fwidgets\u002Ftable.vue')\n}\n",[329,776,777,782,802,820,838],{"__ignoreMap":362},[366,778,779],{"class":368,"line":369},[366,780,781],{"class":375},"{\n",[366,783,784,787,790,792,794,796,799],{"class":368,"line":386},[366,785,786],{"class":382},"  '.\u002Fwidgets\u002Fchart.vue'",[366,788,789],{"class":375},": () ",[366,791,628],{"class":372},[366,793,471],{"class":372},[366,795,474],{"class":375},[366,797,798],{"class":382},"'.\u002Fwidgets\u002Fchart.vue'",[366,800,801],{"class":375},"),\n",[366,803,804,807,809,811,813,815,818],{"class":368,"line":393},[366,805,806],{"class":382},"  '.\u002Fwidgets\u002Fmap.vue'",[366,808,789],{"class":375},[366,810,628],{"class":372},[366,812,471],{"class":372},[366,814,474],{"class":375},[366,816,817],{"class":382},"'.\u002Fwidgets\u002Fmap.vue'",[366,819,801],{"class":375},[366,821,822,825,827,829,831,833,836],{"class":368,"line":487},[366,823,824],{"class":382},"  '.\u002Fwidgets\u002Ftable.vue'",[366,826,789],{"class":375},[366,828,628],{"class":372},[366,830,471],{"class":372},[366,832,474],{"class":375},[366,834,835],{"class":382},"'.\u002Fwidgets\u002Ftable.vue'",[366,837,480],{"class":375},[366,839,840],{"class":368,"line":498},[366,841,501],{"class":375},[353,843,844],{},"需要某個模組時再載入：",[357,846,848],{"className":359,"code":847,"language":361,"meta":362,"style":362},"const module =\n  await modules['.\u002Fwidgets\u002Fchart.vue']?.()\n",[329,849,850,860],{"__ignoreMap":362},[366,851,852,854,857],{"class":368,"line":369},[366,853,608],{"class":372},[366,855,856],{"class":459}," module",[366,858,859],{"class":372}," =\n",[366,861,862,865,868,870],{"class":368,"line":386},[366,863,864],{"class":372},"  await",[366,866,867],{"class":375}," modules[",[366,869,798],{"class":382},[366,871,872],{"class":375},"]?.()\n",[353,874,875],{},"預設是 lazy loading。",[353,877,878],{},"如果希望全部直接載入，可以使用：",[357,880,882],{"className":359,"code":881,"language":361,"meta":362,"style":362},"const modules = import.meta.glob(\n  '.\u002Fwidgets\u002F*.vue',\n  { eager: true }\n)\n",[329,883,884,904,912,923],{"__ignoreMap":362},[366,885,886,888,890,892,894,896,898,900,902],{"class":368,"line":369},[366,887,608],{"class":372},[366,889,738],{"class":459},[366,891,614],{"class":372},[366,893,471],{"class":372},[366,895,745],{"class":375},[366,897,153],{"class":459},[366,899,745],{"class":375},[366,901,752],{"class":396},[366,903,620],{"class":375},[366,905,906,909],{"class":368,"line":386},[366,907,908],{"class":382},"  '.\u002Fwidgets\u002F*.vue'",[366,910,911],{"class":375},",\n",[366,913,914,917,920],{"class":368,"line":393},[366,915,916],{"class":375},"  { eager: ",[366,918,919],{"class":459},"true",[366,921,922],{"class":375}," }\n",[366,924,925],{"class":368,"line":487},[366,926,480],{"class":375},[353,928,929],{},"可以簡單理解成：",[357,931,934],{"className":932,"code":933,"language":714,"meta":362},[712],"import.meta.glob(...)\n→ 找出一批符合條件的檔案\n→ 需要時才載入\n\nimport.meta.glob(..., { eager: true })\n→ 找出一批符合條件的檔案\n→ 一開始全部載入\n",[329,935,933],{"__ignoreMap":362},[353,937,938,941],{},[329,939,940],{},"import.meta.glob()"," 是 Vite 提供的功能，不是 JavaScript 原生 API。",[353,943,944],{},"一般 Vue \u002F Nuxt 專案不一定會用到，只有需要批次取得一組檔案時才比較方便。",[348,946,947],{"id":947},"怎麼選",[949,950,951,964],"table",{},[952,953,954],"thead",{},[955,956,957,961],"tr",{},[958,959,960],"th",{},"情況",[958,962,963],{},"建議",[965,966,967,978,987,996],"tbody",{},[955,968,969,973],{},[970,971,972],"td",{},"一開始就會使用",[970,974,975,976],{},"靜態 ",[329,977,331],{},[955,979,980,983],{},[970,981,982],{},"使用者操作後才需要",[970,984,985],{},[329,986,335],{},[955,988,989,992],{},[970,990,991],{},"Vue 大型元件延後載入",[970,993,994],{},[329,995,555],{},[955,997,998,1001],{},[970,999,1000],{},"Vite 批次取得一組檔案",[970,1002,1003],{},[329,1004,940],{},[348,1006,1007],{"id":1007},"小結",[353,1009,1010],{},"一般情況先使用：",[357,1012,1014],{"className":359,"code":1013,"language":361,"meta":362,"style":362},"import something from '.\u002Fsomething'\n",[329,1015,1016],{"__ignoreMap":362},[366,1017,1018,1020,1023,1025],{"class":368,"line":369},[366,1019,331],{"class":372},[366,1021,1022],{"class":375}," something ",[366,1024,379],{"class":372},[366,1026,1027],{"class":382}," '.\u002Fsomething'\n",[353,1029,1030],{},"只有確定某個功能不需要一開始載入時，再考慮：",[357,1032,1034],{"className":359,"code":1033,"language":361,"meta":362,"style":362},"const module = await import('.\u002Fsomething')\n",[329,1035,1036],{"__ignoreMap":362},[366,1037,1038,1040,1042,1044,1046,1048,1050,1053],{"class":368,"line":369},[366,1039,608],{"class":372},[366,1041,856],{"class":459},[366,1043,614],{"class":372},[366,1045,468],{"class":372},[366,1047,471],{"class":372},[366,1049,474],{"class":375},[366,1051,1052],{"class":382},"'.\u002Fsomething'",[366,1054,480],{"class":375},[353,1056,1057],{},"Vue 元件則可以使用：",[357,1059,1061],{"className":359,"code":1060,"language":361,"meta":362,"style":362},"defineAsyncComponent(\n  () => import('.\u002FHeavyComponent.vue')\n)\n",[329,1062,1063,1070,1085],{"__ignoreMap":362},[366,1064,1065,1068],{"class":368,"line":369},[366,1066,1067],{"class":396},"defineAsyncComponent",[366,1069,620],{"class":375},[366,1071,1072,1074,1076,1078,1080,1083],{"class":368,"line":386},[366,1073,625],{"class":375},[366,1075,628],{"class":372},[366,1077,471],{"class":372},[366,1079,474],{"class":375},[366,1081,1082],{"class":382},"'.\u002FHeavyComponent.vue'",[366,1084,480],{"class":375},[366,1086,1087],{"class":368,"line":393},[366,1088,480],{"class":375},[353,1090,1091,1092,1094],{},"至於 ",[329,1093,940],{},"，把它當成 Vite 提供的批次 import 工具就可以，不需要每個專案都使用。",[348,1096,1097],{"id":1097},"參考資料",[322,1099,1100,1109,1116],{},[325,1101,1102],{},[1103,1104,1108],"a",{"href":1105,"rel":1106},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FOperators\u002Fimport",[1107],"nofollow","MDN：import()",[325,1110,1111],{},[1103,1112,1115],{"href":1113,"rel":1114},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fcomponents\u002Fasync",[1107],"Vue：Async Components",[325,1117,1118],{},[1103,1119,1122],{"href":1120,"rel":1121},"https:\u002F\u002Fvite.dev\u002Fguide\u002Ffeatures.html#glob-import",[1107],"Vite：Glob Import",[1124,1125,1126],"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 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 .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 .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 .spddc, html code.shiki .spddc{--shiki-default:#032F62;--shiki-dark:#E6EDF3}",{"title":362,"searchDepth":393,"depth":393,"links":1128},[1129,1130,1131,1132,1133,1134,1135],{"id":350,"depth":386,"text":351},{"id":423,"depth":386,"text":424},{"id":548,"depth":386,"text":549},{"id":722,"depth":386,"text":723},{"id":947,"depth":386,"text":947},{"id":1007,"depth":386,"text":1007},{"id":1097,"depth":386,"text":1097},false,"md",null,{},"2026-04-17",[],{"title":131,"description":132},"posts\u002F20260417.dynamic-import",[18,134,135,103,136],"bABGWCSlPTQcR9NvVkiFT2QTgRDW1rAt16zK6qOBZbw",[1147,1150],{"title":149,"path":148,"stem":1148,"description":150,"category":16,"publishedAt":1149,"children":-1},"posts\u002F20260519.html-meta-boilerplate-today","2026-05-19",{"title":123,"path":122,"stem":1151,"description":124,"category":89,"publishedAt":1152,"children":-1},"posts\u002F20260415.structured-clone","2026-04-15",1791786901234]