[{"data":1,"prerenderedAt":1319},["ShallowReactive",2],{"search-dialog-posts":3,"post-\u002Fposts\u002Fdayjs-vue3-computed":316,"post-surround-\u002Fposts\u002Fdayjs-vue3-computed":1312},[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":106,"body":318,"category":16,"description":107,"draft":1302,"extension":1303,"image":1304,"meta":1305,"navigation":400,"path":105,"publishedAt":1306,"relatedExperiments":1307,"seo":1308,"stem":1309,"tags":1310,"updatedAt":1306,"__hash__":1311},"posts\u002Fposts\u002F20260408.dayjs-vue3-computed.md",{"type":319,"value":320,"toc":1292},"minimark",[321,339,344,368,372,467,470,474,477,488,491,634,637,641,648,806,809,817,821,824,887,890,943,954,958,963,1068,1071,1092,1097,1100,1163,1166,1169,1178,1181,1190,1193,1235,1238,1257,1260,1263,1288],[322,323,324,328,331],"ul",{},[325,326,327],"li",{},"Vue 2 時期常用 moment.js 搭配全域 filter 做日期格式化。",[325,329,330],{},"Moment.js 目前已經進入維護模式，而 Vue 3 也移除了 filter 語法。",[325,332,333,334,338],{},"現在可以改用 day.js，或只是單純顯示日期時直接使用瀏覽器內建的 ",[335,336,337],"code",{},"Intl.DateTimeFormat","。",[340,341,343],"h2",{"id":342},"安裝-dayjs","安裝 day.js",[345,346,351],"pre",{"className":347,"code":348,"language":349,"meta":350,"style":350},"language-bash shiki shiki-themes github-light github-dark-default","npm install dayjs\n","bash","",[335,352,353],{"__ignoreMap":350},[354,355,358,361,365],"span",{"class":356,"line":357},"line",1,[354,359,62],{"class":360},"sYRkA",[354,362,364],{"class":363},"sNbzA"," install",[354,366,367],{"class":363}," dayjs\n",[369,370,371],"p",{},"基本格式化：",[345,373,377],{"className":374,"code":375,"language":376,"meta":350,"style":350},"language-js shiki shiki-themes github-light github-dark-default","import dayjs from 'dayjs'\n\ndayjs('2026-08-19').format('YYYY-MM-DD')\n\u002F\u002F '2026-08-19'\n\ndayjs('2026-08-19').format('YYYY.MM.DD')\n\u002F\u002F '2026.08.19'\n","js",[335,378,379,395,402,429,436,441,461],{"__ignoreMap":350},[354,380,381,385,389,392],{"class":356,"line":357},[354,382,384],{"class":383},"sDgYj","import",[354,386,388],{"class":387},"szmH1"," dayjs ",[354,390,391],{"class":383},"from",[354,393,394],{"class":363}," 'dayjs'\n",[354,396,398],{"class":356,"line":397},2,[354,399,401],{"emptyLinePlaceholder":400},true,"\n",[354,403,405,409,412,415,418,421,423,426],{"class":356,"line":404},3,[354,406,408],{"class":407},"sVjZL","dayjs",[354,410,411],{"class":387},"(",[354,413,414],{"class":363},"'2026-08-19'",[354,416,417],{"class":387},").",[354,419,420],{"class":407},"format",[354,422,411],{"class":387},[354,424,425],{"class":363},"'YYYY-MM-DD'",[354,427,428],{"class":387},")\n",[354,430,432],{"class":356,"line":431},4,[354,433,435],{"class":434},"sPyeA","\u002F\u002F '2026-08-19'\n",[354,437,439],{"class":356,"line":438},5,[354,440,401],{"emptyLinePlaceholder":400},[354,442,444,446,448,450,452,454,456,459],{"class":356,"line":443},6,[354,445,408],{"class":407},[354,447,411],{"class":387},[354,449,414],{"class":363},[354,451,417],{"class":387},[354,453,420],{"class":407},[354,455,411],{"class":387},[354,457,458],{"class":363},"'YYYY.MM.DD'",[354,460,428],{"class":387},[354,462,464],{"class":356,"line":463},7,[354,465,466],{"class":434},"\u002F\u002F '2026.08.19'\n",[369,468,469],{},"day.js 的 API 和 Moment.js 相近，但核心比較精簡，需要 UTC、timezone 等功能時再另外載入 plugin。",[340,471,473],{"id":472},"vue-3-取代-filter","Vue 3 取代 filter",[369,475,476],{},"Vue 2 以前可以寫：",[345,478,482],{"className":479,"code":480,"language":481,"meta":350,"style":350},"language-vue shiki shiki-themes github-light github-dark-default","{{ item.paidDate | dateFormat }}\n","vue",[335,483,484],{"__ignoreMap":350},[354,485,486],{"class":356,"line":357},[354,487,480],{"class":387},[369,489,490],{},"Vue 3 已經移除 filter，現在直接呼叫一般函式即可：",[345,492,494],{"className":479,"code":493,"language":481,"meta":350,"style":350},"\u003Cscript setup>\nimport dayjs from 'dayjs'\n\nfunction dateFormat(value, format = 'YYYY-MM-DD') {\n  return dayjs(value).format(format)\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cspan>\n    {{ dateFormat(item.paidDate, 'YYYY.MM.DD') }}\n  \u003C\u002Fspan>\n\u003C\u002Ftemplate>\n",[335,495,496,512,522,526,554,570,575,584,589,599,609,615,625],{"__ignoreMap":350},[354,497,498,501,505,509],{"class":356,"line":357},[354,499,500],{"class":387},"\u003C",[354,502,504],{"class":503},"sXQ1o","script",[354,506,508],{"class":507},"syv4q"," setup",[354,510,511],{"class":387},">\n",[354,513,514,516,518,520],{"class":356,"line":397},[354,515,384],{"class":383},[354,517,388],{"class":387},[354,519,391],{"class":383},[354,521,394],{"class":363},[354,523,524],{"class":356,"line":404},[354,525,401],{"emptyLinePlaceholder":400},[354,527,528,531,534,536,540,543,545,548,551],{"class":356,"line":431},[354,529,530],{"class":383},"function",[354,532,533],{"class":407}," dateFormat",[354,535,411],{"class":387},[354,537,539],{"class":538},"sjlk-","value",[354,541,542],{"class":387},", ",[354,544,420],{"class":538},[354,546,547],{"class":383}," =",[354,549,550],{"class":363}," 'YYYY-MM-DD'",[354,552,553],{"class":387},") {\n",[354,555,556,559,562,565,567],{"class":356,"line":438},[354,557,558],{"class":383},"  return",[354,560,561],{"class":407}," dayjs",[354,563,564],{"class":387},"(value).",[354,566,420],{"class":407},[354,568,569],{"class":387},"(format)\n",[354,571,572],{"class":356,"line":443},[354,573,574],{"class":387},"}\n",[354,576,577,580,582],{"class":356,"line":463},[354,578,579],{"class":387},"\u003C\u002F",[354,581,504],{"class":503},[354,583,511],{"class":387},[354,585,587],{"class":356,"line":586},8,[354,588,401],{"emptyLinePlaceholder":400},[354,590,592,594,597],{"class":356,"line":591},9,[354,593,500],{"class":387},[354,595,596],{"class":503},"template",[354,598,511],{"class":387},[354,600,602,605,607],{"class":356,"line":601},10,[354,603,604],{"class":387},"  \u003C",[354,606,354],{"class":503},[354,608,511],{"class":387},[354,610,612],{"class":356,"line":611},11,[354,613,614],{"class":387},"    {{ dateFormat(item.paidDate, 'YYYY.MM.DD') }}\n",[354,616,618,621,623],{"class":356,"line":617},12,[354,619,620],{"class":387},"  \u003C\u002F",[354,622,354],{"class":503},[354,624,511],{"class":387},[354,626,628,630,632],{"class":356,"line":627},13,[354,629,579],{"class":387},[354,631,596],{"class":503},[354,633,511],{"class":387},[369,635,636],{},"需要傳入不同日期或格式時，用一般函式會比較方便。",[340,638,640],{"id":639},"使用-computed","使用 computed",[369,642,643,644,647],{},"如果顯示內容是由元件內的 reactive state 或 props 衍生而來，也可以使用 ",[335,645,646],{},"computed","：",[345,649,651],{"className":479,"code":650,"language":481,"meta":350,"style":350},"\u003Cscript setup>\nimport { computed } from 'vue'\nimport dayjs from 'dayjs'\n\nconst props = defineProps({\n  paidDate: String\n})\n\nconst formattedDate = computed(() => {\n  return dayjs(props.paidDate).format('YYYY.MM.DD')\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cspan>{{ formattedDate }}\u003C\u002Fspan>\n\u003C\u002Ftemplate>\n",[335,652,653,663,675,685,689,706,711,716,720,741,758,762,770,774,783,797],{"__ignoreMap":350},[354,654,655,657,659,661],{"class":356,"line":357},[354,656,500],{"class":387},[354,658,504],{"class":503},[354,660,508],{"class":507},[354,662,511],{"class":387},[354,664,665,667,670,672],{"class":356,"line":397},[354,666,384],{"class":383},[354,668,669],{"class":387}," { computed } ",[354,671,391],{"class":383},[354,673,674],{"class":363}," 'vue'\n",[354,676,677,679,681,683],{"class":356,"line":404},[354,678,384],{"class":383},[354,680,388],{"class":387},[354,682,391],{"class":383},[354,684,394],{"class":363},[354,686,687],{"class":356,"line":431},[354,688,401],{"emptyLinePlaceholder":400},[354,690,691,694,698,700,703],{"class":356,"line":438},[354,692,693],{"class":383},"const",[354,695,697],{"class":696},"sPgVT"," props",[354,699,547],{"class":383},[354,701,702],{"class":407}," defineProps",[354,704,705],{"class":387},"({\n",[354,707,708],{"class":356,"line":443},[354,709,710],{"class":387},"  paidDate: String\n",[354,712,713],{"class":356,"line":463},[354,714,715],{"class":387},"})\n",[354,717,718],{"class":356,"line":586},[354,719,401],{"emptyLinePlaceholder":400},[354,721,722,724,727,729,732,735,738],{"class":356,"line":591},[354,723,693],{"class":383},[354,725,726],{"class":696}," formattedDate",[354,728,547],{"class":383},[354,730,731],{"class":407}," computed",[354,733,734],{"class":387},"(() ",[354,736,737],{"class":383},"=>",[354,739,740],{"class":387}," {\n",[354,742,743,745,747,750,752,754,756],{"class":356,"line":601},[354,744,558],{"class":383},[354,746,561],{"class":407},[354,748,749],{"class":387},"(props.paidDate).",[354,751,420],{"class":407},[354,753,411],{"class":387},[354,755,458],{"class":363},[354,757,428],{"class":387},[354,759,760],{"class":356,"line":611},[354,761,715],{"class":387},[354,763,764,766,768],{"class":356,"line":617},[354,765,579],{"class":387},[354,767,504],{"class":503},[354,769,511],{"class":387},[354,771,772],{"class":356,"line":627},[354,773,401],{"emptyLinePlaceholder":400},[354,775,777,779,781],{"class":356,"line":776},14,[354,778,500],{"class":387},[354,780,596],{"class":503},[354,782,511],{"class":387},[354,784,786,788,790,793,795],{"class":356,"line":785},15,[354,787,604],{"class":387},[354,789,354],{"class":503},[354,791,792],{"class":387},">{{ formattedDate }}\u003C\u002F",[354,794,354],{"class":503},[354,796,511],{"class":387},[354,798,800,802,804],{"class":356,"line":799},16,[354,801,579],{"class":387},[354,803,596],{"class":503},[354,805,511],{"class":387},[369,807,808],{},"可以簡單區分：",[345,810,815],{"className":811,"code":813,"language":814,"meta":350},[812],"language-text","需要傳參數或處理多筆資料\n→ 一般函式\n\n由 props \u002F state 衍生固定顯示值\n→ computed\n","text",[335,816,813],{"__ignoreMap":350},[340,818,820],{"id":819},"utc-與時區","UTC 與時區",[369,822,823],{},"需要處理 UTC 時，可以使用 day.js plugin：",[345,825,827],{"className":374,"code":826,"language":376,"meta":350,"style":350},"import dayjs from 'dayjs'\nimport utc from 'dayjs\u002Fplugin\u002Futc'\nimport timezone from 'dayjs\u002Fplugin\u002Ftimezone'\n\ndayjs.extend(utc)\ndayjs.extend(timezone)\n",[335,828,829,839,851,863,867,878],{"__ignoreMap":350},[354,830,831,833,835,837],{"class":356,"line":357},[354,832,384],{"class":383},[354,834,388],{"class":387},[354,836,391],{"class":383},[354,838,394],{"class":363},[354,840,841,843,846,848],{"class":356,"line":397},[354,842,384],{"class":383},[354,844,845],{"class":387}," utc ",[354,847,391],{"class":383},[354,849,850],{"class":363}," 'dayjs\u002Fplugin\u002Futc'\n",[354,852,853,855,858,860],{"class":356,"line":404},[354,854,384],{"class":383},[354,856,857],{"class":387}," timezone ",[354,859,391],{"class":383},[354,861,862],{"class":363}," 'dayjs\u002Fplugin\u002Ftimezone'\n",[354,864,865],{"class":356,"line":431},[354,866,401],{"emptyLinePlaceholder":400},[354,868,869,872,875],{"class":356,"line":438},[354,870,871],{"class":387},"dayjs.",[354,873,874],{"class":407},"extend",[354,876,877],{"class":387},"(utc)\n",[354,879,880,882,884],{"class":356,"line":443},[354,881,871],{"class":387},[354,883,874],{"class":407},[354,885,886],{"class":387},"(timezone)\n",[369,888,889],{},"例如後端回傳的時間確定是 UTC：",[345,891,893],{"className":374,"code":892,"language":376,"meta":350,"style":350},"function formatFromUTC(value, format = 'YYYY-MM-DD HH:mm') {\n  return dayjs.utc(value).local().format(format)\n}\n",[335,894,895,917,939],{"__ignoreMap":350},[354,896,897,899,902,904,906,908,910,912,915],{"class":356,"line":357},[354,898,530],{"class":383},[354,900,901],{"class":407}," formatFromUTC",[354,903,411],{"class":387},[354,905,539],{"class":538},[354,907,542],{"class":387},[354,909,420],{"class":538},[354,911,547],{"class":383},[354,913,914],{"class":363}," 'YYYY-MM-DD HH:mm'",[354,916,553],{"class":387},[354,918,919,921,924,927,929,932,935,937],{"class":356,"line":397},[354,920,558],{"class":383},[354,922,923],{"class":387}," dayjs.",[354,925,926],{"class":407},"utc",[354,928,564],{"class":387},[354,930,931],{"class":407},"local",[354,933,934],{"class":387},"().",[354,936,420],{"class":407},[354,938,569],{"class":387},[354,940,941],{"class":356,"line":404},[354,942,574],{"class":387},[322,944,945,948],{},[325,946,947],{},"這會先按照 UTC 解讀，再轉成使用者目前的本地時區。",[325,949,950,951,338],{},"如果來源本來就是 local time，就不要額外使用 ",[335,952,953],{},"utc()",[340,955,957],{"id":956},"不裝套件intldatetimeformat","不裝套件：Intl.DateTimeFormat",[369,959,960,961,647],{},"如果只是顯示日期，不需要日期加減、比較或複雜操作，可以直接使用瀏覽器內建的 ",[335,962,337],{},[345,964,966],{"className":374,"code":965,"language":376,"meta":350,"style":350},"const formatter = new Intl.DateTimeFormat('zh-TW', {\n  year: 'numeric',\n  month: '2-digit',\n  day: '2-digit'\n})\n\nfunction formatDate(value) {\n  return formatter.format(new Date(value))\n}\n",[335,967,968,994,1005,1015,1023,1027,1031,1044,1064],{"__ignoreMap":350},[354,969,970,972,975,977,980,983,986,988,991],{"class":356,"line":357},[354,971,693],{"class":383},[354,973,974],{"class":696}," formatter",[354,976,547],{"class":383},[354,978,979],{"class":383}," new",[354,981,982],{"class":387}," Intl.",[354,984,985],{"class":407},"DateTimeFormat",[354,987,411],{"class":387},[354,989,990],{"class":363},"'zh-TW'",[354,992,993],{"class":387},", {\n",[354,995,996,999,1002],{"class":356,"line":397},[354,997,998],{"class":387},"  year: ",[354,1000,1001],{"class":363},"'numeric'",[354,1003,1004],{"class":387},",\n",[354,1006,1007,1010,1013],{"class":356,"line":404},[354,1008,1009],{"class":387},"  month: ",[354,1011,1012],{"class":363},"'2-digit'",[354,1014,1004],{"class":387},[354,1016,1017,1020],{"class":356,"line":431},[354,1018,1019],{"class":387},"  day: ",[354,1021,1022],{"class":363},"'2-digit'\n",[354,1024,1025],{"class":356,"line":438},[354,1026,715],{"class":387},[354,1028,1029],{"class":356,"line":443},[354,1030,401],{"emptyLinePlaceholder":400},[354,1032,1033,1035,1038,1040,1042],{"class":356,"line":463},[354,1034,530],{"class":383},[354,1036,1037],{"class":407}," formatDate",[354,1039,411],{"class":387},[354,1041,539],{"class":538},[354,1043,553],{"class":387},[354,1045,1046,1048,1051,1053,1055,1058,1061],{"class":356,"line":586},[354,1047,558],{"class":383},[354,1049,1050],{"class":387}," formatter.",[354,1052,420],{"class":407},[354,1054,411],{"class":387},[354,1056,1057],{"class":383},"new",[354,1059,1060],{"class":407}," Date",[354,1062,1063],{"class":387},"(value))\n",[354,1065,1066],{"class":356,"line":591},[354,1067,574],{"class":387},[369,1069,1070],{},"使用：",[345,1072,1074],{"className":374,"code":1073,"language":376,"meta":350,"style":350},"formatDate('2026-08-19')\n\u002F\u002F 2026\u002F08\u002F19\n",[335,1075,1076,1087],{"__ignoreMap":350},[354,1077,1078,1081,1083,1085],{"class":356,"line":357},[354,1079,1080],{"class":407},"formatDate",[354,1082,411],{"class":387},[354,1084,414],{"class":363},[354,1086,428],{"class":387},[354,1088,1089],{"class":356,"line":397},[354,1090,1091],{"class":434},"\u002F\u002F 2026\u002F08\u002F19\n",[369,1093,1094,1096],{},[335,1095,337],{}," 會根據指定的 locale 處理在地化日期格式，不需要額外安裝套件。",[340,1098,1099],{"id":1099},"怎麼選",[1101,1102,1103,1116],"table",{},[1104,1105,1106],"thead",{},[1107,1108,1109,1113],"tr",{},[1110,1111,1112],"th",{},"情況",[1110,1114,1115],{},"建議",[1117,1118,1119,1129,1140,1147,1155],"tbody",{},[1107,1120,1121,1125],{},[1122,1123,1124],"td",{},"單純顯示在地化日期",[1122,1126,1127],{},[335,1128,337],{},[1107,1130,1131,1138],{},[1122,1132,1133,1134,1137],{},"自訂 ",[335,1135,1136],{},"YYYY-MM-DD"," 等格式",[1122,1139,109],{},[1107,1141,1142,1145],{},[1122,1143,1144],{},"日期加減、比較",[1122,1146,109],{},[1107,1148,1149,1152],{},[1122,1150,1151],{},"UTC \u002F timezone 處理",[1122,1153,1154],{},"day.js + plugin",[1107,1156,1157,1160],{},[1122,1158,1159],{},"Vue 2 filter 遷移",[1122,1161,1162],{},"method 或 computed",[340,1164,1165],{"id":1165},"小結",[369,1167,1168],{},"Vue 3 已經沒有：",[345,1170,1172],{"className":479,"code":1171,"language":481,"meta":350,"style":350},"{{ value | dateFormat }}\n",[335,1173,1174],{"__ignoreMap":350},[354,1175,1176],{"class":356,"line":357},[354,1177,1171],{"class":387},[369,1179,1180],{},"可以改成：",[345,1182,1184],{"className":479,"code":1183,"language":481,"meta":350,"style":350},"{{ dateFormat(value) }}\n",[335,1185,1186],{"__ignoreMap":350},[354,1187,1188],{"class":356,"line":357},[354,1189,1183],{"class":387},[369,1191,1192],{},"或把 reactive state 的格式化結果寫成：",[345,1194,1196],{"className":374,"code":1195,"language":376,"meta":350,"style":350},"const formattedDate = computed(() => {\n  return dayjs(value.value).format('YYYY-MM-DD')\n})\n",[335,1197,1198,1214,1231],{"__ignoreMap":350},[354,1199,1200,1202,1204,1206,1208,1210,1212],{"class":356,"line":357},[354,1201,693],{"class":383},[354,1203,726],{"class":696},[354,1205,547],{"class":383},[354,1207,731],{"class":407},[354,1209,734],{"class":387},[354,1211,737],{"class":383},[354,1213,740],{"class":387},[354,1215,1216,1218,1220,1223,1225,1227,1229],{"class":356,"line":397},[354,1217,558],{"class":383},[354,1219,561],{"class":407},[354,1221,1222],{"class":387},"(value.value).",[354,1224,420],{"class":407},[354,1226,411],{"class":387},[354,1228,425],{"class":363},[354,1230,428],{"class":387},[354,1232,1233],{"class":356,"line":404},[354,1234,715],{"class":387},[369,1236,1237],{},"如果只是做在地化日期顯示，則可以先考慮：",[345,1239,1241],{"className":374,"code":1240,"language":376,"meta":350,"style":350},"new Intl.DateTimeFormat('zh-TW')\n",[335,1242,1243],{"__ignoreMap":350},[354,1244,1245,1247,1249,1251,1253,1255],{"class":356,"line":357},[354,1246,1057],{"class":383},[354,1248,982],{"class":387},[354,1250,985],{"class":407},[354,1252,411],{"class":387},[354,1254,990],{"class":363},[354,1256,428],{"class":387},[369,1258,1259],{},"不一定每個日期格式化需求都需要另外安裝 day.js。",[340,1261,1262],{"id":1262},"參考資料",[322,1264,1265,1274,1281],{},[325,1266,1267],{},[1268,1269,1273],"a",{"href":1270,"rel":1271},"https:\u002F\u002Fday.js.org\u002F",[1272],"nofollow","Day.js 官方文件",[325,1275,1276],{},[1268,1277,1280],{"href":1278,"rel":1279},"https:\u002F\u002Fv3-migration.vuejs.org\u002Fbreaking-changes\u002Ffilters.html",[1272],"Vue 3 Migration Guide：Filters",[325,1282,1283],{},[1268,1284,1287],{"href":1285,"rel":1286},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fdocs\u002FWeb\u002FJavaScript\u002FReference\u002FGlobal_Objects\u002FIntl\u002FDateTimeFormat",[1272],"MDN：Intl.DateTimeFormat",[1289,1290,1291],"style",{},"html pre.shiki code .sYRkA, html code.shiki .sYRkA{--shiki-default:#6F42C1;--shiki-dark:#FFA657}html pre.shiki code .sNbzA, html code.shiki .sNbzA{--shiki-default:#032F62;--shiki-dark:#A5D6FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sDgYj, html code.shiki .sDgYj{--shiki-default:#D73A49;--shiki-dark:#FF7B72}html pre.shiki code .szmH1, html code.shiki .szmH1{--shiki-default:#24292E;--shiki-dark:#E6EDF3}html pre.shiki code .sVjZL, html code.shiki .sVjZL{--shiki-default:#6F42C1;--shiki-dark:#D2A8FF}html pre.shiki code .sPyeA, html code.shiki .sPyeA{--shiki-default:#6A737D;--shiki-dark:#8B949E}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 .sjlk-, html code.shiki .sjlk-{--shiki-default:#E36209;--shiki-dark:#FFA657}html pre.shiki code .sPgVT, html code.shiki .sPgVT{--shiki-default:#005CC5;--shiki-dark:#79C0FF}",{"title":350,"searchDepth":404,"depth":404,"links":1293},[1294,1295,1296,1297,1298,1299,1300,1301],{"id":342,"depth":397,"text":343},{"id":472,"depth":397,"text":473},{"id":639,"depth":397,"text":640},{"id":819,"depth":397,"text":820},{"id":956,"depth":397,"text":957},{"id":1099,"depth":397,"text":1099},{"id":1165,"depth":397,"text":1165},{"id":1262,"depth":397,"text":1262},false,"md",null,{},"2026-04-08",[],{"title":106,"description":107},"posts\u002F20260408.dayjs-vue3-computed",[44,109,110,111],"Z-kcgL4q2Gb4s8hMX_mNKF_h-dz6YrnsK51rrI-f0H8",[1313,1316],{"title":114,"path":113,"stem":1314,"description":115,"category":16,"publishedAt":1315,"children":-1},"posts\u002F20260414.es-modules-today","2026-04-14",{"title":98,"path":97,"stem":1317,"description":99,"category":16,"publishedAt":1318,"children":-1},"posts\u002F20260407.dart-sass-migration","2026-04-07",1791786901293]