[{"data":1,"prerenderedAt":1634},["ShallowReactive",2],{"search-dialog-posts":3,"post-\u002Fposts\u002Fes-modules-today":316,"post-surround-\u002Fposts\u002Fes-modules-today":1627},[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":114,"body":318,"category":16,"description":115,"draft":1616,"extension":1617,"image":1618,"meta":1619,"navigation":439,"path":113,"publishedAt":1620,"relatedExperiments":1621,"seo":1622,"stem":1623,"tags":1624,"updatedAt":1625,"__hash__":1626},"posts\u002Fposts\u002F20260414.es-modules-today.md",{"type":319,"value":320,"toc":1604},"minimark",[321,338,343,346,472,479,497,511,515,522,549,556,597,604,612,619,646,653,678,681,705,708,712,718,790,793,822,825,847,856,864,868,871,888,895,898,963,966,1004,1007,1010,1014,1017,1046,1052,1055,1079,1088,1092,1095,1133,1136,1177,1180,1184,1187,1219,1225,1276,1279,1325,1328,1331,1334,1378,1381,1384,1390,1396,1399,1508,1511,1514,1531,1534,1537,1543,1550,1553,1600],[322,323,324,325,329,330,333,334,337],"p",{},"早期接觸 ES Module 時，重點多半只有瀏覽器裡的 ",[326,327,328],"code",{},"import","、",[326,331,332],{},"export"," 與 ",[326,335,336],{},"\u003Cscript type=\"module\">","，也常會覺得這是「瀏覽器搭配打包工具」才會使用的東西。現在 Node.js 本身已經完整走向 ESM，瀏覽器也有 import maps 等原生功能。",[339,340,342],"h2",{"id":341},"先複習-import-與-export","先複習 import 與 export",[322,344,345],{},"一個模組可以有多個具名匯出，以及一個預設匯出：",[347,348,353],"pre",{"className":349,"code":350,"language":351,"meta":352,"style":352},"language-js shiki shiki-themes github-light github-dark-default","\u002F\u002F tools.js\nexport const name = 'madebyw'\nexport function add(a, b) {\n  return a + b\n}\n\nexport default function init() {\n  \u002F\u002F ...\n}\n","js","",[326,354,355,364,384,413,428,434,441,461,467],{"__ignoreMap":352},[356,357,360],"span",{"class":358,"line":359},"line",1,[356,361,363],{"class":362},"sPyeA","\u002F\u002F tools.js\n",[356,365,367,370,373,377,380],{"class":358,"line":366},2,[356,368,332],{"class":369},"sDgYj",[356,371,372],{"class":369}," const",[356,374,376],{"class":375},"sPgVT"," name",[356,378,379],{"class":369}," =",[356,381,383],{"class":382},"sNbzA"," 'madebyw'\n",[356,385,387,389,392,396,400,404,407,410],{"class":358,"line":386},3,[356,388,332],{"class":369},[356,390,391],{"class":369}," function",[356,393,395],{"class":394},"sVjZL"," add",[356,397,399],{"class":398},"szmH1","(",[356,401,403],{"class":402},"sjlk-","a",[356,405,406],{"class":398},", ",[356,408,409],{"class":402},"b",[356,411,412],{"class":398},") {\n",[356,414,416,419,422,425],{"class":358,"line":415},4,[356,417,418],{"class":369},"  return",[356,420,421],{"class":398}," a ",[356,423,424],{"class":369},"+",[356,426,427],{"class":398}," b\n",[356,429,431],{"class":358,"line":430},5,[356,432,433],{"class":398},"}\n",[356,435,437],{"class":358,"line":436},6,[356,438,440],{"emptyLinePlaceholder":439},true,"\n",[356,442,444,446,449,451,454,458],{"class":358,"line":443},7,[356,445,332],{"class":369},[356,447,448],{"class":369}," default",[356,450,391],{"class":369},[356,452,453],{"class":394}," init",[356,455,457],{"class":456},"sfpNf","() ",[356,459,460],{"class":398},"{\n",[356,462,464],{"class":358,"line":463},8,[356,465,466],{"class":362},"  \u002F\u002F ...\n",[356,468,470],{"class":358,"line":469},9,[356,471,433],{"class":398},[322,473,474,475,478],{},"載入時，具名匯出要使用 ",[326,476,477],{},"{}","，預設匯出的名稱則由載入端決定：",[347,480,482],{"className":349,"code":481,"language":351,"meta":352,"style":352},"import setup, { name, add } from '.\u002Ftools.js'\n",[326,483,484],{"__ignoreMap":352},[356,485,486,488,491,494],{"class":358,"line":359},[356,487,328],{"class":369},[356,489,490],{"class":398}," setup, { name, add } ",[356,492,493],{"class":369},"from",[356,495,496],{"class":382}," '.\u002Ftools.js'\n",[322,498,499,500,503,504,506,507,510],{},"只寫路徑的 ",[326,501,502],{},"import '.\u002Fsetup.js'"," 會執行模組但不取得匯出值，常用於載入初始化程式或樣式等 side effect。瀏覽器中的 ",[326,505,336],{}," 也有自己的模組作用域，頂層變數不會自動掛到 ",[326,508,509],{},"window","。",[339,512,514],{"id":513},"nodejs-使用-esm","Node.js 使用 ESM",[322,516,517,518,521],{},"最常見的方式是在 ",[326,519,520],{},"package.json"," 加上：",[347,523,527],{"className":524,"code":525,"language":526,"meta":352,"style":352},"language-json shiki shiki-themes github-light github-dark-default","{\n  \"type\": \"module\"\n}\n","json",[326,528,529,533,545],{"__ignoreMap":352},[356,530,531],{"class":358,"line":359},[356,532,460],{"class":398},[356,534,535,539,542],{"class":358,"line":366},[356,536,538],{"class":537},"swgif","  \"type\"",[356,540,541],{"class":398},": ",[356,543,544],{"class":382},"\"module\"\n",[356,546,547],{"class":358,"line":386},[356,548,433],{"class":398},[322,550,551,552,555],{},"之後 ",[326,553,554],{},".js"," 就會按照 ES Module 解析：",[347,557,559],{"className":349,"code":558,"language":351,"meta":352,"style":352},"import { readFile } from 'node:fs\u002Fpromises'\n\nexport function loadFile() {\n  \u002F\u002F ...\n}\n",[326,560,561,573,577,589,593],{"__ignoreMap":352},[356,562,563,565,568,570],{"class":358,"line":359},[356,564,328],{"class":369},[356,566,567],{"class":398}," { readFile } ",[356,569,493],{"class":369},[356,571,572],{"class":382}," 'node:fs\u002Fpromises'\n",[356,574,575],{"class":358,"line":366},[356,576,440],{"emptyLinePlaceholder":439},[356,578,579,581,583,586],{"class":358,"line":386},[356,580,332],{"class":369},[356,582,391],{"class":369},[356,584,585],{"class":394}," loadFile",[356,587,588],{"class":398},"() {\n",[356,590,591],{"class":358,"line":415},[356,592,466],{"class":362},[356,594,595],{"class":358,"line":430},[356,596,433],{"class":398},[322,598,599,600,603],{},"如果沒有設定 ",[326,601,602],{},"\"type\": \"module\"","，也可以使用：",[347,605,610],{"className":606,"code":608,"language":609,"meta":352},[607],"language-text",".mjs → ES Module\n.cjs → CommonJS\n","text",[326,611,608],{"__ignoreMap":352},[322,613,614,615,618],{},"Node 內建模組現在也建議加上 ",[326,616,617],{},"node:","：",[347,620,622],{"className":349,"code":621,"language":351,"meta":352,"style":352},"import path from 'node:path'\nimport { readFile } from 'node:fs\u002Fpromises'\n",[326,623,624,636],{"__ignoreMap":352},[356,625,626,628,631,633],{"class":358,"line":359},[356,627,328],{"class":369},[356,629,630],{"class":398}," path ",[356,632,493],{"class":369},[356,634,635],{"class":382}," 'node:path'\n",[356,637,638,640,642,644],{"class":358,"line":366},[356,639,328],{"class":369},[356,641,567],{"class":398},[356,643,493],{"class":369},[356,645,572],{"class":382},[322,647,648,649,652],{},"ESM 裡沒有 CommonJS 的 ",[326,650,651],{},"__dirname","，現在可以使用：",[347,654,656],{"className":349,"code":655,"language":351,"meta":352,"style":352},"console.log(import.meta.dirname)\n",[326,657,658],{"__ignoreMap":352},[356,659,660,663,666,668,670,673,675],{"class":358,"line":359},[356,661,662],{"class":398},"console.",[356,664,665],{"class":394},"log",[356,667,399],{"class":398},[356,669,328],{"class":369},[356,671,672],{"class":398},".",[356,674,153],{"class":375},[356,676,677],{"class":398},".dirname)\n",[322,679,680],{},"ES Module 也可以直接使用 top-level await：",[347,682,684],{"className":349,"code":683,"language":351,"meta":352,"style":352},"const config = await loadConfig()\n",[326,685,686],{"__ignoreMap":352},[356,687,688,691,694,696,699,702],{"class":358,"line":359},[356,689,690],{"class":369},"const",[356,692,693],{"class":375}," config",[356,695,379],{"class":369},[356,697,698],{"class":369}," await",[356,700,701],{"class":394}," loadConfig",[356,703,704],{"class":398},"()\n",[322,706,707],{},"不需要再包一層 async function。",[339,709,711],{"id":710},"packagejson-的-exports","package.json 的 exports",[322,713,714,715,717],{},"如果自己發布 npm 套件，現在可以使用 ",[326,716,120],{}," 定義公開入口：",[347,719,721],{"className":524,"code":720,"language":526,"meta":352,"style":352},"{\n  \"name\": \"my-lib\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": \".\u002Fdist\u002Findex.js\",\n    \".\u002Futils\": \".\u002Fdist\u002Futils.js\"\n  }\n}\n",[326,722,723,727,740,751,759,771,781,786],{"__ignoreMap":352},[356,724,725],{"class":358,"line":359},[356,726,460],{"class":398},[356,728,729,732,734,737],{"class":358,"line":366},[356,730,731],{"class":537},"  \"name\"",[356,733,541],{"class":398},[356,735,736],{"class":382},"\"my-lib\"",[356,738,739],{"class":398},",\n",[356,741,742,744,746,749],{"class":358,"line":386},[356,743,538],{"class":537},[356,745,541],{"class":398},[356,747,748],{"class":382},"\"module\"",[356,750,739],{"class":398},[356,752,753,756],{"class":358,"line":415},[356,754,755],{"class":537},"  \"exports\"",[356,757,758],{"class":398},": {\n",[356,760,761,764,766,769],{"class":358,"line":430},[356,762,763],{"class":537},"    \".\"",[356,765,541],{"class":398},[356,767,768],{"class":382},"\".\u002Fdist\u002Findex.js\"",[356,770,739],{"class":398},[356,772,773,776,778],{"class":358,"line":436},[356,774,775],{"class":537},"    \".\u002Futils\"",[356,777,541],{"class":398},[356,779,780],{"class":382},"\".\u002Fdist\u002Futils.js\"\n",[356,782,783],{"class":358,"line":443},[356,784,785],{"class":398},"  }\n",[356,787,788],{"class":358,"line":463},[356,789,433],{"class":398},[322,791,792],{},"使用者可以：",[347,794,796],{"className":349,"code":795,"language":351,"meta":352,"style":352},"import something from 'my-lib'\nimport utils from 'my-lib\u002Futils'\n",[326,797,798,810],{"__ignoreMap":352},[356,799,800,802,805,807],{"class":358,"line":359},[356,801,328],{"class":369},[356,803,804],{"class":398}," something ",[356,806,493],{"class":369},[356,808,809],{"class":382}," 'my-lib'\n",[356,811,812,814,817,819],{"class":358,"line":366},[356,813,328],{"class":369},[356,815,816],{"class":398}," utils ",[356,818,493],{"class":369},[356,820,821],{"class":382}," 'my-lib\u002Futils'\n",[322,823,824],{},"但不能直接存取沒有公開的內部檔案：",[347,826,828],{"className":349,"code":827,"language":351,"meta":352,"style":352},"import internal from 'my-lib\u002Fsrc\u002Finternal.js'\n\u002F\u002F 會被 exports 擋下來\n",[326,829,830,842],{"__ignoreMap":352},[356,831,832,834,837,839],{"class":358,"line":359},[356,833,328],{"class":369},[356,835,836],{"class":398}," internal ",[356,838,493],{"class":369},[356,840,841],{"class":382}," 'my-lib\u002Fsrc\u002Finternal.js'\n",[356,843,844],{"class":358,"line":366},[356,845,846],{"class":362},"\u002F\u002F 會被 exports 擋下來\n",[322,848,849,851,852,855],{},[326,850,120],{}," 可以視為比 ",[326,853,854],{},"main"," 更完整的套件入口設定。",[322,857,858,859,861,862,510],{},"如果兩者同時存在，在支援 ",[326,860,120],{}," 的 Node.js 版本中會優先使用 ",[326,863,120],{},[339,865,867],{"id":866},"瀏覽器的-import-maps","瀏覽器的 import maps",[322,869,870],{},"以前瀏覽器看到這種寫法：",[347,872,874],{"className":349,"code":873,"language":351,"meta":352,"style":352},"import { debounce } from 'lodash-es'\n",[326,875,876],{"__ignoreMap":352},[356,877,878,880,883,885],{"class":358,"line":359},[356,879,328],{"class":369},[356,881,882],{"class":398}," { debounce } ",[356,884,493],{"class":369},[356,886,887],{"class":382}," 'lodash-es'\n",[322,889,890,891,894],{},"不知道 ",[326,892,893],{},"lodash-es"," 在哪裡。",[322,896,897],{},"現在可以透過 import map 告訴瀏覽器：",[347,899,903],{"className":900,"code":901,"language":902,"meta":352,"style":352},"language-html shiki shiki-themes github-light github-dark-default","\u003Cscript type=\"importmap\">\n{\n  \"imports\": {\n    \"lodash-es\": \"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Flodash-es@4\u002Flodash.js\",\n    \"#utils\u002F\": \"\u002Fjs\u002Futils\u002F\"\n  }\n}\n\u003C\u002Fscript>\n","html",[326,904,905,927,931,936,941,946,950,954],{"__ignoreMap":352},[356,906,907,910,914,918,921,924],{"class":358,"line":359},[356,908,909],{"class":398},"\u003C",[356,911,913],{"class":912},"sXQ1o","script",[356,915,917],{"class":916},"syv4q"," type",[356,919,920],{"class":398},"=",[356,922,923],{"class":382},"\"importmap\"",[356,925,926],{"class":398},">\n",[356,928,929],{"class":358,"line":366},[356,930,460],{"class":398},[356,932,933],{"class":358,"line":386},[356,934,935],{"class":398},"  \"imports\": {\n",[356,937,938],{"class":358,"line":415},[356,939,940],{"class":398},"    \"lodash-es\": \"https:\u002F\u002Fcdn.jsdelivr.net\u002Fnpm\u002Flodash-es@4\u002Flodash.js\",\n",[356,942,943],{"class":358,"line":430},[356,944,945],{"class":398},"    \"#utils\u002F\": \"\u002Fjs\u002Futils\u002F\"\n",[356,947,948],{"class":358,"line":436},[356,949,785],{"class":398},[356,951,952],{"class":358,"line":443},[356,953,433],{"class":398},[356,955,956,959,961],{"class":358,"line":463},[356,957,958],{"class":398},"\u003C\u002F",[356,960,913],{"class":912},[356,962,926],{"class":398},[322,964,965],{},"之後就能直接：",[347,967,969],{"className":900,"code":968,"language":902,"meta":352,"style":352},"\u003Cscript type=\"module\">\n  import { debounce } from 'lodash-es'\n\u003C\u002Fscript>\n",[326,970,971,985,996],{"__ignoreMap":352},[356,972,973,975,977,979,981,983],{"class":358,"line":359},[356,974,909],{"class":398},[356,976,913],{"class":912},[356,978,917],{"class":916},[356,980,920],{"class":398},[356,982,748],{"class":382},[356,984,926],{"class":398},[356,986,987,990,992,994],{"class":358,"line":366},[356,988,989],{"class":369},"  import",[356,991,882],{"class":398},[356,993,493],{"class":369},[356,995,887],{"class":382},[356,997,998,1000,1002],{"class":358,"line":386},[356,999,958],{"class":398},[356,1001,913],{"class":912},[356,1003,926],{"class":398},[322,1005,1006],{},"比較適合小工具、Demo，或不想引入完整打包工具的頁面。",[322,1008,1009],{},"一般使用 Vite、Nuxt 等框架時，大多還是交給工具鏈處理。",[339,1011,1013],{"id":1012},"import-attributes載入-json","import attributes：載入 JSON",[322,1015,1016],{},"現在可以直接使用 import attributes 載入 JSON：",[347,1018,1020],{"className":349,"code":1019,"language":351,"meta":352,"style":352},"import config from '.\u002Fconfig.json' with { type: 'json' }\n",[326,1021,1022],{"__ignoreMap":352},[356,1023,1024,1026,1029,1031,1034,1037,1040,1043],{"class":358,"line":359},[356,1025,328],{"class":369},[356,1027,1028],{"class":398}," config ",[356,1030,493],{"class":369},[356,1032,1033],{"class":382}," '.\u002Fconfig.json'",[356,1035,1036],{"class":369}," with",[356,1038,1039],{"class":398}," { type: ",[356,1041,1042],{"class":382},"'json'",[356,1044,1045],{"class":398}," }\n",[322,1047,1048,1051],{},[326,1049,1050],{},"with { type: 'json' }"," 用來告訴執行環境這個模組應該按照 JSON 處理。",[322,1053,1054],{},"以前曾經看過：",[347,1056,1058],{"className":349,"code":1057,"language":351,"meta":352,"style":352},"import config from '.\u002Fconfig.json' assert { type: 'json' }\n",[326,1059,1060],{"__ignoreMap":352},[356,1061,1062,1064,1066,1068,1070,1073,1075,1077],{"class":358,"line":359},[356,1063,328],{"class":369},[356,1065,1028],{"class":398},[356,1067,493],{"class":369},[356,1069,1033],{"class":382},[356,1071,1072],{"class":369}," assert",[356,1074,1039],{"class":398},[356,1076,1042],{"class":382},[356,1078,1045],{"class":398},[322,1080,1081,1084,1085,510],{},[326,1082,1083],{},"assert"," 是舊語法，現在使用 ",[326,1086,1087],{},"with",[339,1089,1091],{"id":1090},"動態-import","動態 import",[322,1093,1094],{},"需要時才載入模組，可以使用：",[347,1096,1098],{"className":349,"code":1097,"language":351,"meta":352,"style":352},"const { default: Chart } = await import('.\u002Fheavy-chart.js')\n",[326,1099,1100],{"__ignoreMap":352},[356,1101,1102,1104,1107,1110,1112,1115,1118,1120,1122,1125,1127,1130],{"class":358,"line":359},[356,1103,690],{"class":369},[356,1105,1106],{"class":398}," { ",[356,1108,1109],{"class":402},"default",[356,1111,541],{"class":398},[356,1113,1114],{"class":375},"Chart",[356,1116,1117],{"class":398}," } ",[356,1119,920],{"class":369},[356,1121,698],{"class":369},[356,1123,1124],{"class":369}," import",[356,1126,399],{"class":398},[356,1128,1129],{"class":382},"'.\u002Fheavy-chart.js'",[356,1131,1132],{"class":398},")\n",[322,1134,1135],{},"在 Vue 裡也常搭配：",[347,1137,1139],{"className":349,"code":1138,"language":351,"meta":352,"style":352},"const HeavyChart = defineAsyncComponent(\n  () => import('.\u002FHeavyChart.vue')\n)\n",[326,1140,1141,1156,1173],{"__ignoreMap":352},[356,1142,1143,1145,1148,1150,1153],{"class":358,"line":359},[356,1144,690],{"class":369},[356,1146,1147],{"class":375}," HeavyChart",[356,1149,379],{"class":369},[356,1151,1152],{"class":394}," defineAsyncComponent",[356,1154,1155],{"class":398},"(\n",[356,1157,1158,1161,1164,1166,1168,1171],{"class":358,"line":366},[356,1159,1160],{"class":398},"  () ",[356,1162,1163],{"class":369},"=>",[356,1165,1124],{"class":369},[356,1167,399],{"class":398},[356,1169,1170],{"class":382},"'.\u002FHeavyChart.vue'",[356,1172,1132],{"class":398},[356,1174,1175],{"class":358,"line":386},[356,1176,1132],{"class":398},[322,1178,1179],{},"這可以讓打包工具進行 code splitting，不必一開始就載入所有程式碼。",[339,1181,1183],{"id":1182},"vite-的-importmetaglob","Vite 的 import.meta.glob",[322,1185,1186],{},"如果要一次取得多個模組，Vite 提供：",[347,1188,1190],{"className":349,"code":1189,"language":351,"meta":352,"style":352},"const modules = import.meta.glob('.\u002Fcomponents\u002F*.vue')\n",[326,1191,1192],{"__ignoreMap":352},[356,1193,1194,1196,1199,1201,1203,1205,1207,1209,1212,1214,1217],{"class":358,"line":359},[356,1195,690],{"class":369},[356,1197,1198],{"class":375}," modules",[356,1200,379],{"class":369},[356,1202,1124],{"class":369},[356,1204,672],{"class":398},[356,1206,153],{"class":375},[356,1208,672],{"class":398},[356,1210,1211],{"class":394},"glob",[356,1213,399],{"class":398},[356,1215,1216],{"class":382},"'.\u002Fcomponents\u002F*.vue'",[356,1218,1132],{"class":398},[322,1220,1221,1222,618],{},"預設會使用 lazy loading，每個項目其實是一個動態 ",[326,1223,1224],{},"import()",[347,1226,1228],{"className":349,"code":1227,"language":351,"meta":352,"style":352},"{\n  '.\u002Fcomponents\u002FA.vue': () => import('.\u002Fcomponents\u002FA.vue'),\n  '.\u002Fcomponents\u002FB.vue': () => import('.\u002Fcomponents\u002FB.vue')\n}\n",[326,1229,1230,1234,1254,1272],{"__ignoreMap":352},[356,1231,1232],{"class":358,"line":359},[356,1233,460],{"class":398},[356,1235,1236,1239,1242,1244,1246,1248,1251],{"class":358,"line":366},[356,1237,1238],{"class":382},"  '.\u002Fcomponents\u002FA.vue'",[356,1240,1241],{"class":398},": () ",[356,1243,1163],{"class":369},[356,1245,1124],{"class":369},[356,1247,399],{"class":398},[356,1249,1250],{"class":382},"'.\u002Fcomponents\u002FA.vue'",[356,1252,1253],{"class":398},"),\n",[356,1255,1256,1259,1261,1263,1265,1267,1270],{"class":358,"line":386},[356,1257,1258],{"class":382},"  '.\u002Fcomponents\u002FB.vue'",[356,1260,1241],{"class":398},[356,1262,1163],{"class":369},[356,1264,1124],{"class":369},[356,1266,399],{"class":398},[356,1268,1269],{"class":382},"'.\u002Fcomponents\u002FB.vue'",[356,1271,1132],{"class":398},[356,1273,1274],{"class":358,"line":415},[356,1275,433],{"class":398},[322,1277,1278],{},"如果希望建置時直接載入，可以加：",[347,1280,1282],{"className":349,"code":1281,"language":351,"meta":352,"style":352},"const modules = import.meta.glob(\n  '.\u002Fcomponents\u002F*.vue',\n  { eager: true }\n)\n",[326,1283,1284,1304,1311,1321],{"__ignoreMap":352},[356,1285,1286,1288,1290,1292,1294,1296,1298,1300,1302],{"class":358,"line":359},[356,1287,690],{"class":369},[356,1289,1198],{"class":375},[356,1291,379],{"class":369},[356,1293,1124],{"class":369},[356,1295,672],{"class":398},[356,1297,153],{"class":375},[356,1299,672],{"class":398},[356,1301,1211],{"class":394},[356,1303,1155],{"class":398},[356,1305,1306,1309],{"class":358,"line":366},[356,1307,1308],{"class":382},"  '.\u002Fcomponents\u002F*.vue'",[356,1310,739],{"class":398},[356,1312,1313,1316,1319],{"class":358,"line":386},[356,1314,1315],{"class":398},"  { eager: ",[356,1317,1318],{"class":375},"true",[356,1320,1045],{"class":398},[356,1322,1323],{"class":358,"line":415},[356,1324,1132],{"class":398},[322,1326,1327],{},"Vite 會改成靜態 import。",[322,1329,1330],{},"這在 SSR 或 Workers 環境遇到動態模組載入問題時特別值得檢查。",[322,1332,1333],{},"我曾經在 Cloudflare Workers 部署時遇到 lazy glob 在 SSR 行為與開發環境不同，最後改成：",[347,1335,1337],{"className":349,"code":1336,"language":351,"meta":352,"style":352},"const modules = import.meta.glob(\n  '~\u002Fcomponents\u002Ftaiwan\u002F*.vue',\n  { eager: true }\n)\n",[326,1338,1339,1359,1366,1374],{"__ignoreMap":352},[356,1340,1341,1343,1345,1347,1349,1351,1353,1355,1357],{"class":358,"line":359},[356,1342,690],{"class":369},[356,1344,1198],{"class":375},[356,1346,379],{"class":369},[356,1348,1124],{"class":369},[356,1350,672],{"class":398},[356,1352,153],{"class":375},[356,1354,672],{"class":398},[356,1356,1211],{"class":394},[356,1358,1155],{"class":398},[356,1360,1361,1364],{"class":358,"line":366},[356,1362,1363],{"class":382},"  '~\u002Fcomponents\u002Ftaiwan\u002F*.vue'",[356,1365,739],{"class":398},[356,1367,1368,1370,1372],{"class":358,"line":386},[356,1369,1315],{"class":398},[356,1371,1318],{"class":375},[356,1373,1045],{"class":398},[356,1375,1376],{"class":358,"line":415},[356,1377,1132],{"class":398},[322,1379,1380],{},"問題就消失了。",[322,1382,1383],{},"這類問題比較適合理解成：",[1385,1386,1387],"blockquote",{},[322,1388,1389],{},"開發環境正常，不代表 SSR \u002F Worker 建置後的模組載入方式完全相同。",[322,1391,1392,1393,1395],{},"遇到問題時應檢查實際 build 結果與部署環境，而不是直接認定 ",[326,1394,1224],{}," 本身不能使用。",[339,1397,1398],{"id":1398},"簡單對照",[1400,1401,1402,1415],"table",{},[1403,1404,1405],"thead",{},[1406,1407,1408,1412],"tr",{},[1409,1410,1411],"th",{},"要做的事",[1409,1413,1414],{},"現在常見方式",[1416,1417,1418,1428,1441,1451,1463,1470,1479,1488,1498],"tbody",{},[1406,1419,1420,1424],{},[1421,1422,1423],"td",{},"Node 使用 ES Module",[1421,1425,1426],{},[326,1427,602],{},[1406,1429,1430,1433],{},[1421,1431,1432],{},"Node 內建模組",[1421,1434,1435,329,1438],{},[326,1436,1437],{},"node:fs",[326,1439,1440],{},"node:path",[1406,1442,1443,1446],{},[1421,1444,1445],{},"ESM 取得目錄",[1421,1447,1448],{},[326,1449,1450],{},"import.meta.dirname",[1406,1452,1453,1456],{},[1421,1454,1455],{},"套件公開入口",[1421,1457,1458,1460,1461],{},[326,1459,520],{}," 的 ",[326,1462,120],{},[1406,1464,1465,1468],{},[1421,1466,1467],{},"瀏覽器使用裸模組名",[1421,1469,119],{},[1406,1471,1472,1475],{},[1421,1473,1474],{},"載入 JSON",[1421,1476,1477],{},[326,1478,1050],{},[1406,1480,1481,1484],{},[1421,1482,1483],{},"延遲載入模組",[1421,1485,1486],{},[326,1487,1224],{},[1406,1489,1490,1493],{},[1421,1491,1492],{},"Vite 批次載入模組",[1421,1494,1495],{},[326,1496,1497],{},"import.meta.glob()",[1406,1499,1500,1503],{},[1421,1501,1502],{},"Vite 靜態載入 glob",[1421,1504,1505],{},[326,1506,1507],{},"{ eager: true }",[339,1509,1510],{"id":1510},"小結",[322,1512,1513],{},"現在 ES Module 已經不只是：",[347,1515,1517],{"className":349,"code":1516,"language":351,"meta":352,"style":352},"import ...\nexport ...\n",[326,1518,1519,1526],{"__ignoreMap":352},[356,1520,1521,1523],{"class":358,"line":359},[356,1522,328],{"class":369},[356,1524,1525],{"class":398}," ...\n",[356,1527,1528],{"class":358,"line":366},[356,1529,1530],{"class":398},"export ...\n",[322,1532,1533],{},"Node.js、瀏覽器與打包工具都直接建立在 ESM 上。",[322,1535,1536],{},"日常開發比較值得記住的是：",[347,1538,1541],{"className":1539,"code":1540,"language":609,"meta":352},[607],"Node 專案\n→ \"type\": \"module\"\n\n發布 npm 套件\n→ exports\n\n瀏覽器不用 bundler\n→ import maps\n\n載入 JSON\n→ with { type: 'json' }\n\n需要時才載入\n→ import()\n\nVite 一次找多個模組\n→ import.meta.glob()\n",[326,1542,1540],{"__ignoreMap":352},[322,1544,1545,1546,1549],{},"至於 ",[326,1547,1548],{},"export default","、具名匯出等基本語法，和以前的 ES Module 並沒有太大改變。",[339,1551,1552],{"id":1552},"參考資料",[1554,1555,1556,1565,1572,1579,1586,1593],"ul",{},[1557,1558,1559],"li",{},[403,1560,1564],{"href":1561,"rel":1562},"https:\u002F\u002Fnodejs.org\u002Fapi\u002Fesm.html",[1563],"nofollow","Node.js：ECMAScript modules",[1557,1566,1567],{},[403,1568,1571],{"href":1569,"rel":1570},"https:\u002F\u002Fnodejs.org\u002Fapi\u002Fpackages.html",[1563],"Node.js：Packages",[1557,1573,1574],{},[403,1575,1578],{"href":1576,"rel":1577},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTML\u002FReference\u002FElements\u002Fscript\u002Ftype\u002Fimportmap",[1563],"MDN：Import maps",[1557,1580,1581],{},[403,1582,1585],{"href":1583,"rel":1584},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FStatements\u002Fimport\u002Fwith",[1563],"MDN：Import attributes",[1557,1587,1588],{},[403,1589,1592],{"href":1590,"rel":1591},"https:\u002F\u002Fvite.dev\u002Fguide\u002Ffeatures.html#glob-import",[1563],"Vite：Glob Import",[1557,1594,1595],{},[403,1596,1599],{"href":1597,"rel":1598},"https:\u002F\u002Fdevelopers.cloudflare.com\u002Fworkers\u002Fwrangler\u002Fbundling\u002F",[1563],"Cloudflare Workers：Bundling",[1601,1602,1603],"style",{},"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 .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 .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 .sfpNf, html code.shiki .sfpNf{--shiki-default:#24292E;--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 .swgif, html code.shiki .swgif{--shiki-default:#005CC5;--shiki-dark:#7EE787}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":352,"searchDepth":386,"depth":386,"links":1605},[1606,1607,1608,1609,1610,1611,1612,1613,1614,1615],{"id":341,"depth":366,"text":342},{"id":513,"depth":366,"text":514},{"id":710,"depth":366,"text":711},{"id":866,"depth":366,"text":867},{"id":1012,"depth":366,"text":1013},{"id":1090,"depth":366,"text":1091},{"id":1182,"depth":366,"text":1183},{"id":1398,"depth":366,"text":1398},{"id":1510,"depth":366,"text":1510},{"id":1552,"depth":366,"text":1552},false,"md",null,{},"2026-04-14",[],{"title":114,"description":115},"posts\u002F20260414.es-modules-today",[117,118,119,120,103,18],"2026-09-08","G2W847GC90NJTlwpMYdoXwvh4U7K6a2ybkSQ96bla3o",[1628,1631],{"title":123,"path":122,"stem":1629,"description":124,"category":89,"publishedAt":1630,"children":-1},"posts\u002F20260415.structured-clone","2026-04-15",{"title":106,"path":105,"stem":1632,"description":107,"category":16,"publishedAt":1633,"children":-1},"posts\u002F20260408.dayjs-vue3-computed","2026-04-08",1791786901282]