[{"data":1,"prerenderedAt":544},["ShallowReactive",2],{"note-\u002Fnotes\u002Fsync\u002Fvue-04-computed-properties":3},{"id":4,"title":5,"body":6,"category":531,"date":532,"description":533,"draft":534,"extension":535,"meta":536,"navigation":38,"path":537,"seo":538,"sourcePath":539,"stem":540,"tags":541,"__hash__":543},"notes\u002Fnotes\u002Fsync\u002Fvue-04-computed-properties.md","Computed Properties",{"type":7,"value":8,"toc":526},"minimark",[9,151,188,193,200,206,211,241,244,248,251,341,343,347,355,358,435,438,522],[10,11,16],"pre",{"className":12,"code":13,"language":14,"meta":15,"style":15},"language-javascript shiki shiki-themes github-light github-dark","\u003Cscript setup>\nimport { reactive, computed } from 'vue'\n\nconst author = reactive({\n  name: 'John Doe',\n  books: [\n    'Vue 2 - Advanced Guide',\n    'Vue 3 - Basic Guide',\n    'Vue 4 - The Mystery'\n  ]\n})\n\n\u002F\u002F a computed ref\nconst publishedBooksMessage = computed(() => {\n  return author.books.length > 0 ? 'Yes' : 'No'\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>Has published books:\u003C\u002Fp>\n  \u003Cspan>{{ publishedBooksMessage }}\u003C\u002Fspan>\n\u003C\u002Ftemplate>\n","javascript","",[17,18,19,27,33,40,46,52,58,64,70,76,82,88,93,99,105,111,116,122,127,133,139,145],"code",{"__ignoreMap":15},[20,21,24],"span",{"class":22,"line":23},"line",1,[20,25,26],{},"\u003Cscript setup>\n",[20,28,30],{"class":22,"line":29},2,[20,31,32],{},"import { reactive, computed } from 'vue'\n",[20,34,36],{"class":22,"line":35},3,[20,37,39],{"emptyLinePlaceholder":38},true,"\n",[20,41,43],{"class":22,"line":42},4,[20,44,45],{},"const author = reactive({\n",[20,47,49],{"class":22,"line":48},5,[20,50,51],{},"  name: 'John Doe',\n",[20,53,55],{"class":22,"line":54},6,[20,56,57],{},"  books: [\n",[20,59,61],{"class":22,"line":60},7,[20,62,63],{},"    'Vue 2 - Advanced Guide',\n",[20,65,67],{"class":22,"line":66},8,[20,68,69],{},"    'Vue 3 - Basic Guide',\n",[20,71,73],{"class":22,"line":72},9,[20,74,75],{},"    'Vue 4 - The Mystery'\n",[20,77,79],{"class":22,"line":78},10,[20,80,81],{},"  ]\n",[20,83,85],{"class":22,"line":84},11,[20,86,87],{},"})\n",[20,89,91],{"class":22,"line":90},12,[20,92,39],{"emptyLinePlaceholder":38},[20,94,96],{"class":22,"line":95},13,[20,97,98],{},"\u002F\u002F a computed ref\n",[20,100,102],{"class":22,"line":101},14,[20,103,104],{},"const publishedBooksMessage = computed(() => {\n",[20,106,108],{"class":22,"line":107},15,[20,109,110],{},"  return author.books.length > 0 ? 'Yes' : 'No'\n",[20,112,114],{"class":22,"line":113},16,[20,115,87],{},[20,117,119],{"class":22,"line":118},17,[20,120,121],{},"\u003C\u002Fscript>\n",[20,123,125],{"class":22,"line":124},18,[20,126,39],{"emptyLinePlaceholder":38},[20,128,130],{"class":22,"line":129},19,[20,131,132],{},"\u003Ctemplate>\n",[20,134,136],{"class":22,"line":135},20,[20,137,138],{},"  \u003Cp>Has published books:\u003C\u002Fp>\n",[20,140,142],{"class":22,"line":141},21,[20,143,144],{},"  \u003Cspan>{{ publishedBooksMessage }}\u003C\u002Fspan>\n",[20,146,148],{"class":22,"line":147},22,[20,149,150],{},"\u003C\u002Ftemplate>\n",[152,153,154],"ul",{},[155,156,157,158,161,162],"li",{},"為什麼要用 ",[17,159,160],{},"computed"," 而不是普通函式？\n",[152,163,164,175],{},[155,165,166,170,171,174],{},[167,168,169],"strong",{},"自動追蹤依賴："," 你不需要手動告訴 Vue「當 ",[17,172,173],{},"author.books"," 變動時要重新計算」，Vue 自己會知道。",[155,176,177,180,181,183,184,187],{},[167,178,179],{},"快取（Cache）機制："," 只要 ",[17,182,173],{}," 沒有改變，不管畫面重新渲染多少次，",[17,185,186],{},"publishedBooksMessage"," 都會直接回傳上一次算好的舊結果，不會浪費效能重新計算。",[189,190,192],"h2",{"id":191},"computed-caching-vs-methods","Computed Caching vs. Methods",[194,195,196,197,199],"p",{},"既然寫成普通函式（Method）也可以在畫面上印出一樣的結果，為什麼還要多一個 ",[17,198,160],{},"（計算屬性）？",[201,202,203],"blockquote",{},[194,204,205],{},"快取機制",[207,208,210],"h4",{"id":209},"補充angular的實現方法跟改革","補充：Angular的實現方法跟改革",[152,212,213,238],{},[155,214,215,216,219,220],{},"在Angular當中舊版的機制是用zone.js來處理 -> 攔截所有非步事件（如 ",[17,217,218],{},"setTimeout","、HTTP 請求、點擊事件），但會導致兩個問題：",[152,221,222,232],{},[155,223,224,227,228,231],{},[167,225,226],{},"沒有精準追蹤","：Zone.js 其實",[167,229,230],{},"不知道","到底是哪一個變數改了，只知道「有事情發生」，所以會進行髒檢查（Dirty Checking）。",[155,233,234,237],{},[167,235,236],{},"效能負擔","：當專案變大時，容易造成不必要的全局過濾與重新渲染。",[155,239,240],{},"新版的Angular引入Signals的概念",[242,243],"hr",{},[189,245,247],{"id":246},"writable-computed","Writable Computed",[194,249,250],{},"計算屬性預設僅支援 getter 方法。如果您嘗試為計算屬性賦值，將會收到執行時間警告。可以透過同時提供 getter 和 setter 方法來建立一個：",[10,252,254],{"className":12,"code":253,"language":14,"meta":15,"style":15},"\u003Cscript setup>\nimport { ref, computed } from 'vue'\n\nconst firstName = ref('John')\nconst lastName = ref('Doe')\n\nconst fullName = computed({\n  \u002F\u002F getter\n  get() {\n    return firstName.value + ' ' + lastName.value\n  },\n  \u002F\u002F setter\n  set(newValue) {\n    \u002F\u002F Note: we are using destructuring assignment syntax here.\n    [firstName.value, lastName.value] = newValue.split(' ')\n  }\n})\n\u003C\u002Fscript>\n",[17,255,256,260,265,269,274,279,283,288,293,298,303,308,313,318,323,328,333,337],{"__ignoreMap":15},[20,257,258],{"class":22,"line":23},[20,259,26],{},[20,261,262],{"class":22,"line":29},[20,263,264],{},"import { ref, computed } from 'vue'\n",[20,266,267],{"class":22,"line":35},[20,268,39],{"emptyLinePlaceholder":38},[20,270,271],{"class":22,"line":42},[20,272,273],{},"const firstName = ref('John')\n",[20,275,276],{"class":22,"line":48},[20,277,278],{},"const lastName = ref('Doe')\n",[20,280,281],{"class":22,"line":54},[20,282,39],{"emptyLinePlaceholder":38},[20,284,285],{"class":22,"line":60},[20,286,287],{},"const fullName = computed({\n",[20,289,290],{"class":22,"line":66},[20,291,292],{},"  \u002F\u002F getter\n",[20,294,295],{"class":22,"line":72},[20,296,297],{},"  get() {\n",[20,299,300],{"class":22,"line":78},[20,301,302],{},"    return firstName.value + ' ' + lastName.value\n",[20,304,305],{"class":22,"line":84},[20,306,307],{},"  },\n",[20,309,310],{"class":22,"line":90},[20,311,312],{},"  \u002F\u002F setter\n",[20,314,315],{"class":22,"line":95},[20,316,317],{},"  set(newValue) {\n",[20,319,320],{"class":22,"line":101},[20,321,322],{},"    \u002F\u002F Note: we are using destructuring assignment syntax here.\n",[20,324,325],{"class":22,"line":107},[20,326,327],{},"    [firstName.value, lastName.value] = newValue.split(' ')\n",[20,329,330],{"class":22,"line":113},[20,331,332],{},"  }\n",[20,334,335],{"class":22,"line":118},[20,336,87],{},[20,338,339],{"class":22,"line":124},[20,340,121],{},[242,342],{},[189,344,346],{"id":345},"getting-the-previous-value","Getting the Previous Value",[201,348,349],{},[194,350,351,352],{},"可以取得上一次計算後回傳的結果，並非一定是",[167,353,354],{},"初始值",[194,356,357],{},"可以取得先前的值：",[10,359,361],{"className":12,"code":360,"language":14,"meta":15,"style":15},"\u003Cscript setup>\nimport { ref, computed } from 'vue'\n\nconst count = ref(2)\n\n\u002F\u002F This computed will return the value of count when it's less or equal to 3.\n\u002F\u002F When count is >=4, the last value that fulfilled our condition will be returned\n\u002F\u002F instead until count is less or equal to 3\nconst alwaysSmall = computed((previous) => {\n  if (count.value \u003C= 3) {\n    return count.value\n  }\n\n  return previous\n})\n\u003C\u002Fscript>\n",[17,362,363,367,371,375,380,384,389,394,399,404,409,414,418,422,427,431],{"__ignoreMap":15},[20,364,365],{"class":22,"line":23},[20,366,26],{},[20,368,369],{"class":22,"line":29},[20,370,264],{},[20,372,373],{"class":22,"line":35},[20,374,39],{"emptyLinePlaceholder":38},[20,376,377],{"class":22,"line":42},[20,378,379],{},"const count = ref(2)\n",[20,381,382],{"class":22,"line":48},[20,383,39],{"emptyLinePlaceholder":38},[20,385,386],{"class":22,"line":54},[20,387,388],{},"\u002F\u002F This computed will return the value of count when it's less or equal to 3.\n",[20,390,391],{"class":22,"line":60},[20,392,393],{},"\u002F\u002F When count is >=4, the last value that fulfilled our condition will be returned\n",[20,395,396],{"class":22,"line":66},[20,397,398],{},"\u002F\u002F instead until count is less or equal to 3\n",[20,400,401],{"class":22,"line":72},[20,402,403],{},"const alwaysSmall = computed((previous) => {\n",[20,405,406],{"class":22,"line":78},[20,407,408],{},"  if (count.value \u003C= 3) {\n",[20,410,411],{"class":22,"line":84},[20,412,413],{},"    return count.value\n",[20,415,416],{"class":22,"line":90},[20,417,332],{},[20,419,420],{"class":22,"line":95},[20,421,39],{"emptyLinePlaceholder":38},[20,423,424],{"class":22,"line":101},[20,425,426],{},"  return previous\n",[20,428,429],{"class":22,"line":107},[20,430,87],{},[20,432,433],{"class":22,"line":113},[20,434,121],{},[194,436,437],{},"搭配可寫入值：",[10,439,441],{"className":12,"code":440,"language":14,"meta":15,"style":15},"\u003Cscript setup>\nimport { ref, computed } from 'vue'\n\nconst count = ref(2)\n\nconst alwaysSmall = computed({\n  get(previous) {\n    if (count.value \u003C= 3) {\n      return count.value\n    }\n\n    return previous\n  },\n  set(newValue) {\n    count.value = newValue * 2\n  }\n})\n\u003C\u002Fscript>\n",[17,442,443,447,451,455,459,463,468,473,478,483,488,492,497,501,505,510,514,518],{"__ignoreMap":15},[20,444,445],{"class":22,"line":23},[20,446,26],{},[20,448,449],{"class":22,"line":29},[20,450,264],{},[20,452,453],{"class":22,"line":35},[20,454,39],{"emptyLinePlaceholder":38},[20,456,457],{"class":22,"line":42},[20,458,379],{},[20,460,461],{"class":22,"line":48},[20,462,39],{"emptyLinePlaceholder":38},[20,464,465],{"class":22,"line":54},[20,466,467],{},"const alwaysSmall = computed({\n",[20,469,470],{"class":22,"line":60},[20,471,472],{},"  get(previous) {\n",[20,474,475],{"class":22,"line":66},[20,476,477],{},"    if (count.value \u003C= 3) {\n",[20,479,480],{"class":22,"line":72},[20,481,482],{},"      return count.value\n",[20,484,485],{"class":22,"line":78},[20,486,487],{},"    }\n",[20,489,490],{"class":22,"line":84},[20,491,39],{"emptyLinePlaceholder":38},[20,493,494],{"class":22,"line":90},[20,495,496],{},"    return previous\n",[20,498,499],{"class":22,"line":95},[20,500,307],{},[20,502,503],{"class":22,"line":101},[20,504,317],{},[20,506,507],{"class":22,"line":107},[20,508,509],{},"    count.value = newValue * 2\n",[20,511,512],{"class":22,"line":113},[20,513,332],{},[20,515,516],{"class":22,"line":118},[20,517,87],{},[20,519,520],{"class":22,"line":124},[20,521,121],{},[523,524,525],"style",{},"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);}",{"title":15,"searchDepth":29,"depth":29,"links":527},[528,529,530],{"id":191,"depth":29,"text":192},{"id":246,"depth":29,"text":247},{"id":345,"depth":29,"text":346},"frontend","2026-08-05","\u003Cscript setup>",false,"md",{},"\u002Fnotes\u002Fsync\u002Fvue-04-computed-properties",{"title":5,"description":533},"Vue\u002F04-Computed Properties.md","notes\u002Fsync\u002Fvue-04-computed-properties",[542],"Vue","gArBt7_1DSvhG0u4ZeMNeAfWxUF1ZjTWOUSiZmxUUZ4",1788767747594]