[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"post-\u002Fblog\u002Fdialog-esc-state-sync":3},{"id":4,"title":5,"body":6,"category":338,"date":339,"description":340,"draft":341,"extension":342,"meta":343,"navigation":344,"path":345,"readingTime":242,"seo":346,"stem":347,"tags":348,"__hash__":352},"blog\u002Fblog\u002Fdialog-esc-state-sync.md","Dialog 按 ESC 關閉後，再也打不開",{"type":7,"value":8,"toc":332},"minimark",[9,13,18,21,61,72,75,138,141,145,148,184,190,193,196,208,219,314,317,328],[10,11,12],"p",{},"有一個常見問題的 Dialog，用按鈕關閉完全正常，但按鍵盤 ESC 關閉之後，再點一次開啟按鈕，視窗就打不開了。",[14,15,17],"h2",{"id":16},"原因兩個地方都記著開關狀態","原因：兩個地方都記著開關狀態",[10,19,20],{},"這個 Dialog 的開關是由 Vuex 控制的，外部用 action 打開：",[22,23,28],"pre",{"className":24,"code":25,"language":26,"meta":27,"style":27},"language-js shiki shiki-themes github-dark","store.dispatch('toggleFAQdialog', { action: true })\n","js","",[29,30,31],"code",{"__ignoreMap":27},[32,33,36,40,44,47,51,54,58],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"s95oV","store.",[32,41,43],{"class":42},"svObZ","dispatch",[32,45,46],{"class":38},"(",[32,48,50],{"class":49},"sU2Wk","'toggleFAQdialog'",[32,52,53],{"class":38},", { action: ",[32,55,57],{"class":56},"sDLfK","true",[32,59,60],{"class":38}," })\n",[10,62,63,64,67,68,71],{},"按鈕關閉時會走 ",[29,65,66],{},"closeFAQ","，把狀態改回 ",[29,69,70],{},"false","。問題是 ESC 不會經過這條路。Vuetify 的 Dialog 內建 ESC 關閉，它自己把視窗收起來了，但我們的關閉邏輯沒有執行。",[10,73,74],{},"於是狀態變成這樣：",[76,77,78,94],"table",{},[79,80,81],"thead",{},[82,83,84,88,91],"tr",{},[85,86,87],"th",{},"時機",[85,89,90],{},"Vuex 狀態",[85,92,93],{},"畫面",[95,96,97,110,123],"tbody",{},[82,98,99,103,107],{},[100,101,102],"td",{},"開啟",[100,104,105],{},[29,106,57],{},[100,108,109],{},"顯示",[82,111,112,115,120],{},[100,113,114],{},"按 ESC",[100,116,117,118],{},"仍然是 ",[29,119,57],{},[100,121,122],{},"關閉",[82,124,125,128,135],{},[100,126,127],{},"再按開啟",[100,129,130,132,133],{},[29,131,57],{}," → ",[29,134,57],{},[100,136,137],{},"沒有變化",[10,139,140],{},"狀態沒有改變，Vue 也就不會觸發更新，畫面當然沒有反應。",[14,142,144],{"id":143},"修法讓-esc-走同一條關閉流程","修法：讓 ESC 走同一條關閉流程",[10,146,147],{},"在 Dialog 上監聽 ESC，呼叫同一個關閉方法：",[22,149,153],{"className":150,"code":151,"language":152,"meta":27,"style":27},"language-html shiki shiki-themes github-dark","\u003Cv-dialog v-model=\"FAQdialog\" @keydown.esc=\"closeFAQ\">\n","html",[29,154,155],{"__ignoreMap":27},[32,156,157,160,164,167,170,173,176,178,181],{"class":34,"line":35},[32,158,159],{"class":38},"\u003C",[32,161,163],{"class":162},"s4JwU","v-dialog",[32,165,166],{"class":42}," v-model",[32,168,169],{"class":38},"=",[32,171,172],{"class":49},"\"FAQdialog\"",[32,174,175],{"class":42}," @keydown.esc",[32,177,169],{"class":38},[32,179,180],{"class":49},"\"closeFAQ\"",[32,182,183],{"class":38},">\n",[10,185,186,187,189],{},"這樣不管從哪裡關閉，最後都會把狀態改回 ",[29,188,70],{},"，兩邊就一致了。",[14,191,192],{"id":192},"其他會發生同樣問題的情況",[10,194,195],{},"只要元件有自己關閉的方式，就要確認狀態有同步：",[197,198,199,202,205],"ul",{},[200,201,114],"li",{},[200,203,204],{},"點擊遮罩",[200,206,207],{},"路由切換",[10,209,210,211,214,215,218],{},"這也是 ",[29,212,213],{},"v-model"," 重要的原因：雙向綁定的 setter 要真的把值寫回狀態來源，元件內部的關閉才會同步到外面。如果綁定的是只有 getter 的 computed，或用單向的 ",[29,216,217],{},":value","，就很容易遇到這個問題。",[22,220,222],{"className":24,"code":221,"language":26,"meta":27,"style":27},"computed: {\n  dialog: {\n    get() {\n      return this.$store.state.showDialog\n    },\n    set(value) {\n      this.$store.commit('setShowDialog', value)\n    },\n  },\n}\n",[29,223,224,232,240,249,262,268,277,297,302,308],{"__ignoreMap":27},[32,225,226,229],{"class":34,"line":35},[32,227,228],{"class":42},"computed",[32,230,231],{"class":38},": {\n",[32,233,235,238],{"class":34,"line":234},2,[32,236,237],{"class":42},"  dialog",[32,239,231],{"class":38},[32,241,243,246],{"class":34,"line":242},3,[32,244,245],{"class":42},"    get",[32,247,248],{"class":38},"() {\n",[32,250,252,256,259],{"class":34,"line":251},4,[32,253,255],{"class":254},"snl16","      return",[32,257,258],{"class":56}," this",[32,260,261],{"class":38},".$store.state.showDialog\n",[32,263,265],{"class":34,"line":264},5,[32,266,267],{"class":38},"    },\n",[32,269,271,274],{"class":34,"line":270},6,[32,272,273],{"class":42},"    set",[32,275,276],{"class":38},"(value) {\n",[32,278,280,283,286,289,291,294],{"class":34,"line":279},7,[32,281,282],{"class":56},"      this",[32,284,285],{"class":38},".$store.",[32,287,288],{"class":42},"commit",[32,290,46],{"class":38},[32,292,293],{"class":49},"'setShowDialog'",[32,295,296],{"class":38},", value)\n",[32,298,300],{"class":34,"line":299},8,[32,301,267],{"class":38},[32,303,305],{"class":34,"line":304},9,[32,306,307],{"class":38},"  },\n",[32,309,311],{"class":34,"line":310},10,[32,312,313],{"class":38},"}\n",[14,315,316],{"id":316},"這次學到的事",[197,318,319,322,325],{},[200,320,321],{},"同一個狀態存在兩個地方時，遲早會不同步。",[200,323,324],{},"測試關閉功能時，不只要測按鈕，鍵盤和遮罩也要測。",[200,326,327],{},"「只有第二次失敗」的 Bug，通常代表狀態沒有被重設。",[329,330,331],"style",{},"html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}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 pre.shiki code .s4JwU, html code.shiki .s4JwU{--shiki-default:#85E89D}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}",{"title":27,"searchDepth":234,"depth":234,"links":333},[334,335,336,337],{"id":16,"depth":234,"text":17},{"id":143,"depth":234,"text":144},{"id":192,"depth":234,"text":192},{"id":316,"depth":234,"text":316},"vue","2025-12-05","Vuetify Dialog 用 ESC 關閉後，再次點擊卻打不開。問題出在元件內部狀態和外部狀態沒有同步，記錄原因與修正方式。",false,"md",{},true,"\u002Fblog\u002Fdialog-esc-state-sync",{"title":5,"description":340},"blog\u002Fdialog-esc-state-sync",[349,350,351],"Vuetify","Vuex","State","VRzGbls9ZJzt0JXNqoDl9ZRXY-TXFd6xnF3bR_WytR4"]