[{"data":1,"prerenderedAt":2234},["ShallowReactive",2],{"notes-\u002Fnotes\u002Fvue3-reactivity":3},{"id":4,"title":5,"body":6,"date":2222,"description":2223,"draft":2224,"extension":2225,"meta":2226,"navigation":187,"path":2227,"seo":2228,"stem":2229,"tags":2230,"__hash__":2233},"notes\u002Fnotes\u002Fvue3-reactivity.md","Vue 3 的響應式原理：Proxy、track 和 trigger",{"type":7,"value":8,"toc":2198},"minimark",[9,13,38,42,46,50,53,119,130,228,232,237,245,286,290,293,296,299,340,343,372,379,383,439,456,460,464,467,488,497,501,514,522,592,598,602,610,719,734,738,741,790,793,875,884,888,892,899,906,965,995,999,1002,1012,1073,1079,1083,1176,1180,1187,2194],[10,11,12],"h2",{"id":12},"目錄",[14,15,16,20,23,26,29,32,35],"ol",{},[17,18,19],"li",{},"一句話",[17,21,22],{},"核心模型：三個角色",[17,24,25],{},"怎麼運作：get 登記、set 觸發、完整的循環",[17,27,28],{},"怎麼用：reactive、ref、shallowRef",[17,30,31],{},"和 Vue 2 的差別",[17,33,34],{},"我原本的誤解",[17,36,37],{},"附錄：可以執行的最小實作",[10,39,41],{"id":40},"_1-一句話","1. 一句話",[43,44,45],"p",{},"Vue 3 用 Proxy 攔截資料的讀寫：讀取（get）時登記「誰在用這筆資料」，寫入（set）時通知這些人重新執行，畫面就更新了。",[10,47,49],{"id":48},"_2-核心模型三個角色","2. 核心模型：三個角色",[43,51,52],{},"整個響應式系統只有三個角色，後面所有內容都是在講它們怎麼互動。",[54,55,56,72],"table",{},[57,58,59],"thead",{},[60,61,62,66,69],"tr",{},[63,64,65],"th",{},"角色",[63,67,68],{},"是什麼",[63,70,71],{},"例子",[73,74,75,94,105],"tbody",{},[60,76,77,81,84],{},[78,79,80],"td",{},"資料",[78,82,83],{},"被 Proxy 包住的物件，讀寫都會被攔截",[78,85,86,90,91],{},[87,88,89],"code",{},"reactive({ count: 0 })","、",[87,92,93],{},"ref(0)",[60,95,96,99,102],{},[78,97,98],{},"effect",[78,100,101],{},"會讀資料的函式，資料變了要重新執行",[78,103,104],{},"元件的渲染函式、computed、watch",[60,106,107,110,113],{},[78,108,109],{},"名單",[78,111,112],{},"每個屬性一份，記錄哪些 effect 讀過它",[78,114,115,118],{},[87,116,117],{},"count"," 的名單上有「元件 A 的渲染函式」",[43,120,121,122,125,126,129],{},"我用 axios 的攔截器來理解 Proxy：請求和回應都先經過攔截器處理，使用的人照常呼叫 ",[87,123,124],{},"axios.get()","，感覺不到它存在。Proxy 也一樣，照常寫 ",[87,127,128],{},"state.count","，讀寫都會先被攔下來。",[131,132,137],"pre",{"className":133,"code":134,"language":135,"meta":136,"style":136},"language-js shiki shiki-themes github-dark","const raw = { count: 0 }\nconst state = reactive(raw)\n\nstate.count       \u002F\u002F 經過 Proxy 的 get\nstate.count = 1   \u002F\u002F 經過 Proxy 的 set\nraw.count = 2     \u002F\u002F 繞過 Proxy，Vue 不知道，畫面不會更新\n","js","",[87,138,139,165,182,189,199,214],{"__ignoreMap":136},[140,141,144,148,152,155,159,162],"span",{"class":142,"line":143},"line",1,[140,145,147],{"class":146},"snl16","const",[140,149,151],{"class":150},"sDLfK"," raw",[140,153,154],{"class":146}," =",[140,156,158],{"class":157},"s95oV"," { count: ",[140,160,161],{"class":150},"0",[140,163,164],{"class":157}," }\n",[140,166,168,170,173,175,179],{"class":142,"line":167},2,[140,169,147],{"class":146},[140,171,172],{"class":150}," state",[140,174,154],{"class":146},[140,176,178],{"class":177},"svObZ"," reactive",[140,180,181],{"class":157},"(raw)\n",[140,183,185],{"class":142,"line":184},3,[140,186,188],{"emptyLinePlaceholder":187},true,"\n",[140,190,192,195],{"class":142,"line":191},4,[140,193,194],{"class":157},"state.count       ",[140,196,198],{"class":197},"sAwPA","\u002F\u002F 經過 Proxy 的 get\n",[140,200,202,205,208,211],{"class":142,"line":201},5,[140,203,204],{"class":157},"state.count ",[140,206,207],{"class":146},"=",[140,209,210],{"class":150}," 1",[140,212,213],{"class":197},"   \u002F\u002F 經過 Proxy 的 set\n",[140,215,217,220,222,225],{"class":142,"line":216},6,[140,218,219],{"class":157},"raw.count ",[140,221,207],{"class":146},[140,223,224],{"class":150}," 2",[140,226,227],{"class":197},"     \u002F\u002F 繞過 Proxy，Vue 不知道，畫面不會更新\n",[10,229,231],{"id":230},"_3-怎麼運作","3. 怎麼運作",[233,234,236],"h3",{"id":235},"get回傳值順便登記track","get：回傳值，順便登記（track）",[43,238,239,240,244],{},"get 本來的工作是把值交出去，同時把",[241,242,243],"strong",{},"正在執行的 effect"," 加進這個屬性的名單。",[246,247,248,260,270,276],"ul",{},[17,249,250,253,254,256,257,259],{},[241,251,252],{},"每個屬性各有一份名單","：改 ",[87,255,117],{}," 只會通知讀過 ",[87,258,117],{}," 的 effect",[17,261,262,265,266,269],{},[241,263,264],{},"不會重複登記","：名單用 ",[87,267,268],{},"Set"," 存",[17,271,272,275],{},[241,273,274],{},"只有在 effect 裡讀才會登記","：在一般函式或事件處理裡讀，沒有正在執行的 effect，就不登記",[17,277,278,281,282,285],{},[241,279,280],{},"每次執行都重新登記","：有 ",[87,283,284],{},"if"," 判斷時，只有實際讀到的屬性會被登記",[233,287,289],{"id":288},"set寫入然後觸發trigger","set：寫入，然後觸發（trigger）",[43,291,292],{},"觸發是 set 最主要的工作。沒有觸發，名單上的 effect 就不知道資料變了，會一直用舊的值。",[43,294,295],{},"set 寫入新的值之後，如果值真的有變，就把這個屬性名單上的 effect 一個一個拿出來重新執行。值沒變就不通知。",[43,297,298],{},"被通知的 effect 不一定馬上執行：",[54,300,301,310],{},[57,302,303],{},[60,304,305,307],{},[63,306,98],{},[63,308,309],{},"收到通知後",[73,311,312,324,332],{},[60,313,314,317],{},[78,315,316],{},"元件渲染",[78,318,319,320,323],{},"排進佇列，同一輪的多次修改合併成一次渲染，",[87,321,322],{},"nextTick"," 就是在等這個佇列",[60,325,326,329],{},[78,327,328],{},"computed",[78,330,331],{},"只標記舊值作廢，下次被讀到才重算",[60,333,334,337],{},[78,335,336],{},"watch",[78,338,339],{},"依設定的時機執行回呼",[233,341,342],{"id":342},"完整的循環",[14,344,345,354,360,366,369],{},[17,346,347,348,350,351,353],{},"渲染函式執行，讀到 ",[87,349,117],{},"：get 回傳值，把渲染函式登記到 ",[87,352,117],{}," 的名單",[17,355,356,357,359],{},"修改 ",[87,358,117],{},"：set 寫入，把名單上的渲染函式拿出來重新執行",[17,361,362,363,365],{},"渲染函式重新執行，又讀到 ",[87,364,117],{},"：get 拿到新的值，重新登記",[17,367,368],{},"渲染函式產生新的虛擬 DOM，和舊的比對（diff），只更新有變化的真實 DOM",[17,370,371],{},"等下一次 set，回到第 2 步",[43,373,374,375,378],{},"第 3 步又呼叫了 get，但不會無限循環，因為 get 只登記、不觸發。反過來說，如果在渲染函式或 computed 裡",[241,376,377],{},"修改","資料，就可能變成「set、重新渲染、又 set」的循環，所以 computed 裡不應該改資料。",[233,380,382],{"id":381},"例子v-model","例子：v-model",[131,384,388],{"className":385,"code":386,"language":387,"meta":136,"style":136},"language-html shiki shiki-themes github-dark","\u003Cinput v-model=\"name\">\n\u003C!-- 編譯後大致是 -->\n\u003Cinput :value=\"name\" @input=\"name = $event.target.value\">\n","html",[87,389,390,411,416],{"__ignoreMap":136},[140,391,392,395,399,402,404,408],{"class":142,"line":143},[140,393,394],{"class":157},"\u003C",[140,396,398],{"class":397},"s4JwU","input",[140,400,401],{"class":177}," v-model",[140,403,207],{"class":157},[140,405,407],{"class":406},"sU2Wk","\"name\"",[140,409,410],{"class":157},">\n",[140,412,413],{"class":142,"line":167},[140,414,415],{"class":197},"\u003C!-- 編譯後大致是 -->\n",[140,417,418,420,422,425,427,429,432,434,437],{"class":142,"line":184},[140,419,394],{"class":157},[140,421,398],{"class":397},[140,423,424],{"class":177}," :value",[140,426,207],{"class":157},[140,428,407],{"class":406},[140,430,431],{"class":177}," @input",[140,433,207],{"class":157},[140,435,436],{"class":406},"\"name = $event.target.value\"",[140,438,410],{"class":157},[246,440,441,450],{},[17,442,443,446,447,353],{},[87,444,445],{},":value=\"name\""," 在渲染時讀取：元件的渲染函式被登記到 ",[87,448,449],{},"name",[17,451,452,455],{},[87,453,454],{},"@input"," 在打字時寫入：觸發 set，元件重新渲染",[10,457,459],{"id":458},"_4-怎麼用reactiverefshallowref","4. 怎麼用：reactive、ref、shallowRef",[233,461,463],{"id":462},"proxy-只能包物件","Proxy 只能包物件",[43,465,466],{},"數字、字串、布林值不是物件，Proxy 包不了。",[246,468,469,475],{},[17,470,471,474],{},[87,472,473],{},"reactive()"," 直接用 Proxy，所以只能放物件（物件、陣列、Map、Set）",[17,476,477,480,481,484,485,487],{},[87,478,479],{},"ref()"," 為了能放任何值，做一個物件把值放在 ",[87,482,483],{},".value"," 裡，再用 getter、setter 攔截 ",[87,486,483],{}," 的讀寫",[43,489,490,493,494,496],{},[87,491,492],{},"ref"," 要寫 ",[87,495,483],{}," 的原因就在這裡：要有一個屬性可以攔截。在 template 裡會自動解開，直接寫名稱就好。",[233,498,500],{"id":499},"ref-放物件其實是兩層","ref 放物件：其實是兩層",[43,502,503,505,506,509,510,513],{},[87,504,492],{}," 放的值如果是物件，會在內部用 ",[87,507,508],{},"reactive"," 轉成 Proxy，所以 ",[87,511,512],{},"ref([])"," 有兩層，各有自己的名單：",[131,515,520],{"className":516,"code":518,"language":519},[517],"language-text","list（ref 物件）\n └─ .value         第一層：getter、setter，名單掛在 list 的 value\n     └─ Proxy(陣列)  第二層：reactive，名單掛在陣列的索引、length\n","text",[87,521,518],{"__ignoreMap":136},[131,523,525],{"className":133,"code":524,"language":135,"meta":136,"style":136},"const list = ref([])\nlist.value.push(4)       \u002F\u002F 觸發第二層：陣列的 length 變了，.value 還是同一個陣列\nlist.value = [7, 8, 9]   \u002F\u002F 觸發第一層：.value 換成新的陣列，新陣列也會先轉成 Proxy\n",[87,526,527,542,562],{"__ignoreMap":136},[140,528,529,531,534,536,539],{"class":142,"line":143},[140,530,147],{"class":146},[140,532,533],{"class":150}," list",[140,535,154],{"class":146},[140,537,538],{"class":177}," ref",[140,540,541],{"class":157},"([])\n",[140,543,544,547,550,553,556,559],{"class":142,"line":167},[140,545,546],{"class":157},"list.value.",[140,548,549],{"class":177},"push",[140,551,552],{"class":157},"(",[140,554,555],{"class":150},"4",[140,557,558],{"class":157},")       ",[140,560,561],{"class":197},"\u002F\u002F 觸發第二層：陣列的 length 變了，.value 還是同一個陣列\n",[140,563,564,567,569,572,575,578,581,583,586,589],{"class":142,"line":184},[140,565,566],{"class":157},"list.value ",[140,568,207],{"class":146},[140,570,571],{"class":157}," [",[140,573,574],{"class":150},"7",[140,576,577],{"class":157},", ",[140,579,580],{"class":150},"8",[140,582,577],{"class":157},[140,584,585],{"class":150},"9",[140,587,588],{"class":157},"]   ",[140,590,591],{"class":197},"\u002F\u002F 觸發第一層：.value 換成新的陣列，新陣列也會先轉成 Proxy\n",[43,593,594,595,597],{},"第二行是陣列用 ",[87,596,492],{}," 最實用的地方：打 API 拿到新的列表，可以直接整個換掉。",[233,599,601],{"id":600},"reactive-的坑","reactive 的坑",[43,603,604,606,607,609],{},[87,605,508],{}," 只有第二層，沒有外面那層 ",[87,608,483],{},"：",[131,611,613],{"className":133,"code":612,"language":135,"meta":136,"style":136},"let state = reactive({ list: [] })\nstate = reactive({ list: [1, 2] })\n\u002F\u002F 只是把普通變數指向新的 Proxy，沒有經過任何 set\n\u002F\u002F 元件登記在舊的 Proxy 上，不會收到通知\n\nconst s = reactive({ count: 0 })\nlet { count } = s\ncount++\n\u002F\u002F 解構等於把值複製出來，count 只是普通的數字，已經不經過 Proxy\n\u002F\u002F 要保留響應的話改用 toRefs(s)\n",[87,614,615,630,653,658,663,667,686,699,707,713],{"__ignoreMap":136},[140,616,617,620,623,625,627],{"class":142,"line":143},[140,618,619],{"class":146},"let",[140,621,622],{"class":157}," state ",[140,624,207],{"class":146},[140,626,178],{"class":177},[140,628,629],{"class":157},"({ list: [] })\n",[140,631,632,635,637,639,642,645,647,650],{"class":142,"line":167},[140,633,634],{"class":157},"state ",[140,636,207],{"class":146},[140,638,178],{"class":177},[140,640,641],{"class":157},"({ list: [",[140,643,644],{"class":150},"1",[140,646,577],{"class":157},[140,648,649],{"class":150},"2",[140,651,652],{"class":157},"] })\n",[140,654,655],{"class":142,"line":184},[140,656,657],{"class":197},"\u002F\u002F 只是把普通變數指向新的 Proxy，沒有經過任何 set\n",[140,659,660],{"class":142,"line":191},[140,661,662],{"class":197},"\u002F\u002F 元件登記在舊的 Proxy 上，不會收到通知\n",[140,664,665],{"class":142,"line":201},[140,666,188],{"emptyLinePlaceholder":187},[140,668,669,671,674,676,678,681,683],{"class":142,"line":216},[140,670,147],{"class":146},[140,672,673],{"class":150}," s",[140,675,154],{"class":146},[140,677,178],{"class":177},[140,679,680],{"class":157},"({ count: ",[140,682,161],{"class":150},[140,684,685],{"class":157}," })\n",[140,687,689,691,694,696],{"class":142,"line":688},7,[140,690,619],{"class":146},[140,692,693],{"class":157}," { count } ",[140,695,207],{"class":146},[140,697,698],{"class":157}," s\n",[140,700,702,704],{"class":142,"line":701},8,[140,703,117],{"class":157},[140,705,706],{"class":146},"++\n",[140,708,710],{"class":142,"line":709},9,[140,711,712],{"class":197},"\u002F\u002F 解構等於把值複製出來，count 只是普通的數字，已經不經過 Proxy\n",[140,714,716],{"class":142,"line":715},10,[140,717,718],{"class":197},"\u002F\u002F 要保留響應的話改用 toRefs(s)\n",[43,720,721,722,724,725,727,728,730,731,733],{},"所以 ",[87,723,492],{}," 多包一層 ",[87,726,483],{}," 不只是寫法上的麻煩，它就是 ",[87,729,492],{}," 比較泛用的原因：換值變成「寫 ",[87,732,483],{},"」，會經過 setter，就能觸發。",[233,735,737],{"id":736},"shallowref","shallowRef",[43,739,740],{},"只有第一層，不會把內容轉成 Proxy。適合很大、只會整個換掉的資料：",[131,742,744],{"className":133,"code":743,"language":135,"meta":136,"style":136},"const big = shallowRef([])\nbig.value = res.data   \u002F\u002F 觸發\nbig.value.push(1)      \u002F\u002F 不觸發，沒有第二層\n",[87,745,746,760,773],{"__ignoreMap":136},[140,747,748,750,753,755,758],{"class":142,"line":143},[140,749,147],{"class":146},[140,751,752],{"class":150}," big",[140,754,154],{"class":146},[140,756,757],{"class":177}," shallowRef",[140,759,541],{"class":157},[140,761,762,765,767,770],{"class":142,"line":167},[140,763,764],{"class":157},"big.value ",[140,766,207],{"class":146},[140,768,769],{"class":157}," res.data   ",[140,771,772],{"class":197},"\u002F\u002F 觸發\n",[140,774,775,778,780,782,784,787],{"class":142,"line":184},[140,776,777],{"class":157},"big.value.",[140,779,549],{"class":177},[140,781,552],{"class":157},[140,783,644],{"class":150},[140,785,786],{"class":157},")      ",[140,788,789],{"class":197},"\u002F\u002F 不觸發，沒有第二層\n",[233,791,792],{"id":792},"怎麼選",[54,794,795,819],{},[57,796,797],{},[60,798,799,801,807,810,813,816],{},[63,800],{},[63,802,803,804,806],{},"第一層（",[87,805,483],{},"）",[63,808,809],{},"第二層（Proxy）",[63,811,812],{},"整個換掉",[63,814,815],{},"改內容",[63,817,818],{},"能放基本型別",[73,820,821,842,859],{},[60,822,823,827,830,833,836,839],{},[78,824,825],{},[87,826,508],{},[78,828,829],{},"沒有",[78,831,832],{},"有",[78,834,835],{},"不會觸發",[78,837,838],{},"會觸發",[78,840,841],{},"不行",[60,843,844,848,850,852,854,856],{},[78,845,846],{},[87,847,492],{},[78,849,832],{},[78,851,832],{},[78,853,838],{},[78,855,838],{},[78,857,858],{},"可以",[60,860,861,865,867,869,871,873],{},[78,862,863],{},[87,864,737],{},[78,866,832],{},[78,868,829],{},[78,870,838],{},[78,872,835],{},[78,874,858],{},[43,876,877,878,880,881,883],{},"Vue 官方文件（Reactivity Fundamentals）列出 ",[87,879,508],{}," 的三個限制：只能放物件、不能整個換掉、解構會斷掉響應，因此建議宣告響應式狀態時優先用 ",[87,882,479],{},"。",[10,885,887],{"id":886},"_5-和-vue-2-的差別","5. 和 Vue 2 的差別",[233,889,891],{"id":890},"vue-2-用-defineproperty","Vue 2 用 defineProperty",[43,893,894,895,898],{},"Vue 2 在初始化時遞迴走過整個資料，對每一層的每個屬性用 ",[87,896,897],{},"Object.defineProperty"," 設定 getter、setter。所以 Vue 2 也有深層響應，改巢狀物件的屬性一樣會更新畫面。",[43,900,901,902,905],{},"問題在於 ",[87,903,904],{},"defineProperty"," 只能攔截設定當下已經存在的屬性，而且 Vue 2 沒有對陣列的每個索引設定 getter、setter（陣列可能很長，成本太高）。所以這些偵測不到：",[131,907,909],{"className":133,"code":908,"language":135,"meta":136,"style":136},"this.items[0] = 'x'     \u002F\u002F 用索引改\nthis.items.length = 0   \u002F\u002F 改長度\nthis.user.age = 30      \u002F\u002F 新增一開始不存在的屬性\n",[87,910,911,932,950],{"__ignoreMap":136},[140,912,913,916,919,921,924,926,929],{"class":142,"line":143},[140,914,915],{"class":150},"this",[140,917,918],{"class":157},".items[",[140,920,161],{"class":150},[140,922,923],{"class":157},"] ",[140,925,207],{"class":146},[140,927,928],{"class":406}," 'x'",[140,930,931],{"class":197},"     \u002F\u002F 用索引改\n",[140,933,934,936,939,942,944,947],{"class":142,"line":167},[140,935,915],{"class":150},[140,937,938],{"class":157},".items.",[140,940,941],{"class":150},"length",[140,943,154],{"class":146},[140,945,946],{"class":150}," 0",[140,948,949],{"class":197},"   \u002F\u002F 改長度\n",[140,951,952,954,957,959,962],{"class":142,"line":184},[140,953,915],{"class":150},[140,955,956],{"class":157},".user.age ",[140,958,207],{"class":146},[140,960,961],{"class":150}," 30",[140,963,964],{"class":197},"      \u002F\u002F 新增一開始不存在的屬性\n",[43,966,967,968,90,970,90,973,90,976,90,979,90,982,90,985,988,989,992,993,883],{},"Vue 2 的補救是改寫陣列的 7 個方法（",[87,969,549],{},[87,971,972],{},"pop",[87,974,975],{},"shift",[87,977,978],{},"unshift",[87,980,981],{},"splice",[87,983,984],{},"sort",[87,986,987],{},"reverse","），其他情況要用 ",[87,990,991],{},"Vue.set"," 或 ",[87,994,981],{},[233,996,998],{"id":997},"vue-3-用-proxy","Vue 3 用 Proxy",[43,1000,1001],{},"Proxy 攔截整個物件的所有操作，不管屬性一開始存不存在，上面三種寫法都會經過 set。",[43,1003,1004,1005,1008,1009,1011],{},"深層響應也改成",[241,1006,1007],{},"讀到才轉換","：get 讀到的值如果是物件，當下才用 ",[87,1010,508],{}," 包起來回傳，沒讀到的巢狀物件就一直不會被處理。",[54,1013,1014,1030],{},[57,1015,1016],{},[60,1017,1018,1021,1024,1027],{},[63,1019,1020],{},"問題",[63,1022,1023],{},"Vue 2",[63,1025,1026],{},"Vue 3",[63,1028,1029],{},"解決的關鍵",[73,1031,1032,1046,1059],{},[60,1033,1034,1037,1040,1043],{},[78,1035,1036],{},"巢狀物件改了要響應",[78,1038,1039],{},"可以（初始化時遞迴）",[78,1041,1042],{},"可以（讀到才轉換）",[78,1044,1045],{},"兩個版本都有，Vue 3 改成延遲",[60,1047,1048,1051,1054,1056],{},[78,1049,1050],{},"用索引改陣列、改長度",[78,1052,1053],{},"偵測不到",[78,1055,858],{},[78,1057,1058],{},"Proxy",[60,1060,1061,1064,1069,1071],{},[78,1062,1063],{},"物件新增屬性",[78,1065,1066,1067],{},"偵測不到，要用 ",[87,1068,991],{},[78,1070,858],{},[78,1072,1058],{},[43,1074,1075,1076,1078],{},"所以 Vue 3 改用 Proxy 的好處有兩個：能攔截新增的屬性、陣列索引和長度，不用再寫 ",[87,1077,991],{},"。深層響應改成讀到才轉換，大資料初始化比較快。",[10,1080,1082],{"id":1081},"_6-我原本的誤解","6. 我原本的誤解",[54,1084,1085,1095],{},[57,1086,1087],{},[60,1088,1089,1092],{},[63,1090,1091],{},"我原本以為",[63,1093,1094],{},"實際上",[73,1096,1097,1108,1120,1128,1136,1148,1168],{},[60,1098,1099,1102],{},[78,1100,1101],{},"Proxy 監聽的是元件",[78,1103,1104,1105,1107],{},"Proxy 包的是",[241,1106,80],{},"，元件是讀資料的 effect，被登記在名單上",[60,1109,1110,1113],{},[78,1111,1112],{},"Proxy 是一種封裝",[78,1114,1115,1116,1119],{},"是",[241,1117,1118],{},"攔截","。封裝是把東西藏起來，Proxy 沒有藏任何東西，介面也沒變",[60,1121,1122,1125],{},[78,1123,1124],{},"用「監聽」形容 Proxy",[78,1126,1127],{},"口語可以，但監聽是事後收到通知，攔截是事前攔下來，還能決定回傳什麼",[60,1129,1130,1133],{},[78,1131,1132],{},"set 通知是「副作用」",[78,1134,1135],{},"在 Vue 的術語裡，effect（副作用）指的是被通知的那些函式，通知這件事叫觸發",[60,1137,1138,1141],{},[78,1139,1140],{},"set 觸發後，名單上的 DOM 再呼叫一次 get",[78,1142,1143,1144,1147],{},"名單上是",[241,1145,1146],{},"函式","不是 DOM。函式重新執行時順便呼叫了 get，改 DOM 的是 diff 之後的 patch",[60,1149,1150,1156],{},[78,1151,1152,1153],{},"畫面會更新是因為 ",[87,1154,1155],{},"v-model",[78,1157,1158,1160,1161,1164,1165,1167],{},[87,1159,1155],{}," 只是 ",[87,1162,1163],{},":value"," 讀加 ",[87,1166,454],{}," 寫，任何響應式資料改了畫面都會更新",[60,1169,1170,1173],{},[78,1171,1172],{},"深層攔截是為了解決 Vue 2 陣列的問題",[78,1174,1175],{},"兩件事。Vue 2 本來就有深層響應，陣列問題是靠 Proxy 解決的",[10,1177,1179],{"id":1178},"_7-附錄可以執行的最小實作","7. 附錄：可以執行的最小實作",[43,1181,1182,1183,1186],{},"存成 ",[87,1184,1185],{},".js"," 用 Node 執行。這個版本沒有排程佇列，所以每次修改都會馬上重新執行，Vue 實際上會合併成一次渲染。",[131,1188,1190],{"className":133,"code":1189,"language":135,"meta":136,"style":136},"const targetMap = new WeakMap()   \u002F\u002F 資料 → 屬性 → 名單\nlet activeEffect = null           \u002F\u002F 現在正在執行的 effect\n\nfunction track(target, key) {\n  if (!activeEffect) return\n  let depsMap = targetMap.get(target)\n  if (!depsMap) targetMap.set(target, (depsMap = new Map()))\n  let dep = depsMap.get(key)\n  if (!dep) depsMap.set(key, (dep = new Set()))\n  dep.add(activeEffect)\n}\n\nfunction trigger(target, key) {\n  targetMap.get(target)?.get(key)?.forEach(effect => effect())\n}\n\nfunction reactive(obj) {\n  return new Proxy(obj, {\n    get(target, key, receiver) {\n      track(target, key)\n      const res = Reflect.get(target, key, receiver)\n      \u002F\u002F 讀到物件才轉成 Proxy（延遲轉換）\n      return typeof res === 'object' && res !== null ? reactive(res) : res\n    },\n    set(target, key, value, receiver) {\n      const isNew = !Object.prototype.hasOwnProperty.call(target, key)\n      const changed = !Object.is(target[key], value)\n      const ok = Reflect.set(target, key, value, receiver)\n      if (changed) trigger(target, key)\n      \u002F\u002F 陣列新增索引時（例如 push），length 已經跟著變了，要另外通知讀過 length 的人\n      if (isNew && Array.isArray(target)) trigger(target, 'length')\n      return ok\n    }\n  })\n}\n\nfunction ref(value) {\n  const wrapper = {\n    _value: toReactive(value),\n    get value() {\n      track(wrapper, 'value')\n      return this._value\n    },\n    set value(newVal) {\n      if (Object.is(newVal, this._value)) return\n      this._value = toReactive(newVal)\n      trigger(wrapper, 'value')\n    }\n  }\n  return wrapper\n}\n\nfunction toReactive(v) {\n  return typeof v === 'object' && v !== null ? reactive(v) : v\n}\n\nfunction effect(fn) {\n  activeEffect = fn\n  fn()\n  activeEffect = null\n}\n\n\u002F\u002F reactive：只有讀過的屬性會觸發\nconst state = reactive({ count: 0, other: 0 })\neffect(() => console.log('render, count =', state.count))\nstate.count++   \u002F\u002F 印出 count = 1\nstate.other++   \u002F\u002F 不會印，effect 沒有讀 other\n\n\u002F\u002F ref 放陣列：兩層都會觸發\nconst list = ref([])\neffect(() => console.log('render, length =', list.value.length))\nlist.value.push(4)       \u002F\u002F 第二層，印出 length = 1\nlist.value = [7, 8, 9]   \u002F\u002F 第一層，印出 length = 3\n",[87,1191,1192,1213,1228,1232,1254,1271,1290,1317,1334,1359,1370,1376,1381,1399,1431,1436,1441,1455,1469,1490,1499,1518,1524,1566,1572,1597,1627,1648,1665,1679,1685,1716,1724,1730,1736,1741,1746,1759,1773,1785,1796,1809,1820,1825,1839,1859,1876,1888,1893,1899,1907,1912,1917,1931,1965,1970,1975,1989,2000,2009,2019,2024,2029,2035,2057,2082,2093,2104,2109,2115,2128,2154,2170],{"__ignoreMap":136},[140,1193,1194,1196,1199,1201,1204,1207,1210],{"class":142,"line":143},[140,1195,147],{"class":146},[140,1197,1198],{"class":150}," targetMap",[140,1200,154],{"class":146},[140,1202,1203],{"class":146}," new",[140,1205,1206],{"class":177}," WeakMap",[140,1208,1209],{"class":157},"()   ",[140,1211,1212],{"class":197},"\u002F\u002F 資料 → 屬性 → 名單\n",[140,1214,1215,1217,1220,1222,1225],{"class":142,"line":167},[140,1216,619],{"class":146},[140,1218,1219],{"class":157}," activeEffect ",[140,1221,207],{"class":146},[140,1223,1224],{"class":150}," null",[140,1226,1227],{"class":197},"           \u002F\u002F 現在正在執行的 effect\n",[140,1229,1230],{"class":142,"line":184},[140,1231,188],{"emptyLinePlaceholder":187},[140,1233,1234,1237,1240,1242,1246,1248,1251],{"class":142,"line":191},[140,1235,1236],{"class":146},"function",[140,1238,1239],{"class":177}," track",[140,1241,552],{"class":157},[140,1243,1245],{"class":1244},"s9osk","target",[140,1247,577],{"class":157},[140,1249,1250],{"class":1244},"key",[140,1252,1253],{"class":157},") {\n",[140,1255,1256,1259,1262,1265,1268],{"class":142,"line":201},[140,1257,1258],{"class":146},"  if",[140,1260,1261],{"class":157}," (",[140,1263,1264],{"class":146},"!",[140,1266,1267],{"class":157},"activeEffect) ",[140,1269,1270],{"class":146},"return\n",[140,1272,1273,1276,1279,1281,1284,1287],{"class":142,"line":216},[140,1274,1275],{"class":146},"  let",[140,1277,1278],{"class":157}," depsMap ",[140,1280,207],{"class":146},[140,1282,1283],{"class":157}," targetMap.",[140,1285,1286],{"class":177},"get",[140,1288,1289],{"class":157},"(target)\n",[140,1291,1292,1294,1296,1298,1301,1304,1307,1309,1311,1314],{"class":142,"line":688},[140,1293,1258],{"class":146},[140,1295,1261],{"class":157},[140,1297,1264],{"class":146},[140,1299,1300],{"class":157},"depsMap) targetMap.",[140,1302,1303],{"class":177},"set",[140,1305,1306],{"class":157},"(target, (depsMap ",[140,1308,207],{"class":146},[140,1310,1203],{"class":146},[140,1312,1313],{"class":177}," Map",[140,1315,1316],{"class":157},"()))\n",[140,1318,1319,1321,1324,1326,1329,1331],{"class":142,"line":701},[140,1320,1275],{"class":146},[140,1322,1323],{"class":157}," dep ",[140,1325,207],{"class":146},[140,1327,1328],{"class":157}," depsMap.",[140,1330,1286],{"class":177},[140,1332,1333],{"class":157},"(key)\n",[140,1335,1336,1338,1340,1342,1345,1347,1350,1352,1354,1357],{"class":142,"line":709},[140,1337,1258],{"class":146},[140,1339,1261],{"class":157},[140,1341,1264],{"class":146},[140,1343,1344],{"class":157},"dep) depsMap.",[140,1346,1303],{"class":177},[140,1348,1349],{"class":157},"(key, (dep ",[140,1351,207],{"class":146},[140,1353,1203],{"class":146},[140,1355,1356],{"class":177}," Set",[140,1358,1316],{"class":157},[140,1360,1361,1364,1367],{"class":142,"line":715},[140,1362,1363],{"class":157},"  dep.",[140,1365,1366],{"class":177},"add",[140,1368,1369],{"class":157},"(activeEffect)\n",[140,1371,1373],{"class":142,"line":1372},11,[140,1374,1375],{"class":157},"}\n",[140,1377,1379],{"class":142,"line":1378},12,[140,1380,188],{"emptyLinePlaceholder":187},[140,1382,1384,1386,1389,1391,1393,1395,1397],{"class":142,"line":1383},13,[140,1385,1236],{"class":146},[140,1387,1388],{"class":177}," trigger",[140,1390,552],{"class":157},[140,1392,1245],{"class":1244},[140,1394,577],{"class":157},[140,1396,1250],{"class":1244},[140,1398,1253],{"class":157},[140,1400,1402,1405,1407,1410,1412,1415,1418,1420,1422,1425,1428],{"class":142,"line":1401},14,[140,1403,1404],{"class":157},"  targetMap.",[140,1406,1286],{"class":177},[140,1408,1409],{"class":157},"(target)?.",[140,1411,1286],{"class":177},[140,1413,1414],{"class":157},"(key)?.",[140,1416,1417],{"class":177},"forEach",[140,1419,552],{"class":157},[140,1421,98],{"class":1244},[140,1423,1424],{"class":146}," =>",[140,1426,1427],{"class":177}," effect",[140,1429,1430],{"class":157},"())\n",[140,1432,1434],{"class":142,"line":1433},15,[140,1435,1375],{"class":157},[140,1437,1439],{"class":142,"line":1438},16,[140,1440,188],{"emptyLinePlaceholder":187},[140,1442,1444,1446,1448,1450,1453],{"class":142,"line":1443},17,[140,1445,1236],{"class":146},[140,1447,178],{"class":177},[140,1449,552],{"class":157},[140,1451,1452],{"class":1244},"obj",[140,1454,1253],{"class":157},[140,1456,1458,1461,1463,1466],{"class":142,"line":1457},18,[140,1459,1460],{"class":146},"  return",[140,1462,1203],{"class":146},[140,1464,1465],{"class":177}," Proxy",[140,1467,1468],{"class":157},"(obj, {\n",[140,1470,1472,1475,1477,1479,1481,1483,1485,1488],{"class":142,"line":1471},19,[140,1473,1474],{"class":177},"    get",[140,1476,552],{"class":157},[140,1478,1245],{"class":1244},[140,1480,577],{"class":157},[140,1482,1250],{"class":1244},[140,1484,577],{"class":157},[140,1486,1487],{"class":1244},"receiver",[140,1489,1253],{"class":157},[140,1491,1493,1496],{"class":142,"line":1492},20,[140,1494,1495],{"class":177},"      track",[140,1497,1498],{"class":157},"(target, key)\n",[140,1500,1502,1505,1508,1510,1513,1515],{"class":142,"line":1501},21,[140,1503,1504],{"class":146},"      const",[140,1506,1507],{"class":150}," res",[140,1509,154],{"class":146},[140,1511,1512],{"class":157}," Reflect.",[140,1514,1286],{"class":177},[140,1516,1517],{"class":157},"(target, key, receiver)\n",[140,1519,1521],{"class":142,"line":1520},22,[140,1522,1523],{"class":197},"      \u002F\u002F 讀到物件才轉成 Proxy（延遲轉換）\n",[140,1525,1527,1530,1533,1536,1539,1542,1545,1547,1550,1552,1555,1557,1560,1563],{"class":142,"line":1526},23,[140,1528,1529],{"class":146},"      return",[140,1531,1532],{"class":146}," typeof",[140,1534,1535],{"class":157}," res ",[140,1537,1538],{"class":146},"===",[140,1540,1541],{"class":406}," 'object'",[140,1543,1544],{"class":146}," &&",[140,1546,1535],{"class":157},[140,1548,1549],{"class":146},"!==",[140,1551,1224],{"class":150},[140,1553,1554],{"class":146}," ?",[140,1556,178],{"class":177},[140,1558,1559],{"class":157},"(res) ",[140,1561,1562],{"class":146},":",[140,1564,1565],{"class":157}," res\n",[140,1567,1569],{"class":142,"line":1568},24,[140,1570,1571],{"class":157},"    },\n",[140,1573,1575,1578,1580,1582,1584,1586,1588,1591,1593,1595],{"class":142,"line":1574},25,[140,1576,1577],{"class":177},"    set",[140,1579,552],{"class":157},[140,1581,1245],{"class":1244},[140,1583,577],{"class":157},[140,1585,1250],{"class":1244},[140,1587,577],{"class":157},[140,1589,1590],{"class":1244},"value",[140,1592,577],{"class":157},[140,1594,1487],{"class":1244},[140,1596,1253],{"class":157},[140,1598,1600,1602,1605,1607,1610,1613,1616,1619,1622,1625],{"class":142,"line":1599},26,[140,1601,1504],{"class":146},[140,1603,1604],{"class":150}," isNew",[140,1606,154],{"class":146},[140,1608,1609],{"class":146}," !",[140,1611,1612],{"class":150},"Object",[140,1614,1615],{"class":157},".",[140,1617,1618],{"class":150},"prototype",[140,1620,1621],{"class":157},".hasOwnProperty.",[140,1623,1624],{"class":177},"call",[140,1626,1498],{"class":157},[140,1628,1630,1632,1635,1637,1639,1642,1645],{"class":142,"line":1629},27,[140,1631,1504],{"class":146},[140,1633,1634],{"class":150}," changed",[140,1636,154],{"class":146},[140,1638,1609],{"class":146},[140,1640,1641],{"class":157},"Object.",[140,1643,1644],{"class":177},"is",[140,1646,1647],{"class":157},"(target[key], value)\n",[140,1649,1651,1653,1656,1658,1660,1662],{"class":142,"line":1650},28,[140,1652,1504],{"class":146},[140,1654,1655],{"class":150}," ok",[140,1657,154],{"class":146},[140,1659,1512],{"class":157},[140,1661,1303],{"class":177},[140,1663,1664],{"class":157},"(target, key, value, receiver)\n",[140,1666,1668,1671,1674,1677],{"class":142,"line":1667},29,[140,1669,1670],{"class":146},"      if",[140,1672,1673],{"class":157}," (changed) ",[140,1675,1676],{"class":177},"trigger",[140,1678,1498],{"class":157},[140,1680,1682],{"class":142,"line":1681},30,[140,1683,1684],{"class":197},"      \u002F\u002F 陣列新增索引時（例如 push），length 已經跟著變了，要另外通知讀過 length 的人\n",[140,1686,1688,1690,1693,1696,1699,1702,1705,1707,1710,1713],{"class":142,"line":1687},31,[140,1689,1670],{"class":146},[140,1691,1692],{"class":157}," (isNew ",[140,1694,1695],{"class":146},"&&",[140,1697,1698],{"class":157}," Array.",[140,1700,1701],{"class":177},"isArray",[140,1703,1704],{"class":157},"(target)) ",[140,1706,1676],{"class":177},[140,1708,1709],{"class":157},"(target, ",[140,1711,1712],{"class":406},"'length'",[140,1714,1715],{"class":157},")\n",[140,1717,1719,1721],{"class":142,"line":1718},32,[140,1720,1529],{"class":146},[140,1722,1723],{"class":157}," ok\n",[140,1725,1727],{"class":142,"line":1726},33,[140,1728,1729],{"class":157},"    }\n",[140,1731,1733],{"class":142,"line":1732},34,[140,1734,1735],{"class":157},"  })\n",[140,1737,1739],{"class":142,"line":1738},35,[140,1740,1375],{"class":157},[140,1742,1744],{"class":142,"line":1743},36,[140,1745,188],{"emptyLinePlaceholder":187},[140,1747,1749,1751,1753,1755,1757],{"class":142,"line":1748},37,[140,1750,1236],{"class":146},[140,1752,538],{"class":177},[140,1754,552],{"class":157},[140,1756,1590],{"class":1244},[140,1758,1253],{"class":157},[140,1760,1762,1765,1768,1770],{"class":142,"line":1761},38,[140,1763,1764],{"class":146},"  const",[140,1766,1767],{"class":150}," wrapper",[140,1769,154],{"class":146},[140,1771,1772],{"class":157}," {\n",[140,1774,1776,1779,1782],{"class":142,"line":1775},39,[140,1777,1778],{"class":157},"    _value: ",[140,1780,1781],{"class":177},"toReactive",[140,1783,1784],{"class":157},"(value),\n",[140,1786,1788,1790,1793],{"class":142,"line":1787},40,[140,1789,1474],{"class":146},[140,1791,1792],{"class":177}," value",[140,1794,1795],{"class":157},"() {\n",[140,1797,1799,1801,1804,1807],{"class":142,"line":1798},41,[140,1800,1495],{"class":177},[140,1802,1803],{"class":157},"(wrapper, ",[140,1805,1806],{"class":406},"'value'",[140,1808,1715],{"class":157},[140,1810,1812,1814,1817],{"class":142,"line":1811},42,[140,1813,1529],{"class":146},[140,1815,1816],{"class":150}," this",[140,1818,1819],{"class":157},"._value\n",[140,1821,1823],{"class":142,"line":1822},43,[140,1824,1571],{"class":157},[140,1826,1828,1830,1832,1834,1837],{"class":142,"line":1827},44,[140,1829,1577],{"class":146},[140,1831,1792],{"class":177},[140,1833,552],{"class":157},[140,1835,1836],{"class":1244},"newVal",[140,1838,1253],{"class":157},[140,1840,1842,1844,1847,1849,1852,1854,1857],{"class":142,"line":1841},45,[140,1843,1670],{"class":146},[140,1845,1846],{"class":157}," (Object.",[140,1848,1644],{"class":177},[140,1850,1851],{"class":157},"(newVal, ",[140,1853,915],{"class":150},[140,1855,1856],{"class":157},"._value)) ",[140,1858,1270],{"class":146},[140,1860,1862,1865,1868,1870,1873],{"class":142,"line":1861},46,[140,1863,1864],{"class":150},"      this",[140,1866,1867],{"class":157},"._value ",[140,1869,207],{"class":146},[140,1871,1872],{"class":177}," toReactive",[140,1874,1875],{"class":157},"(newVal)\n",[140,1877,1879,1882,1884,1886],{"class":142,"line":1878},47,[140,1880,1881],{"class":177},"      trigger",[140,1883,1803],{"class":157},[140,1885,1806],{"class":406},[140,1887,1715],{"class":157},[140,1889,1891],{"class":142,"line":1890},48,[140,1892,1729],{"class":157},[140,1894,1896],{"class":142,"line":1895},49,[140,1897,1898],{"class":157},"  }\n",[140,1900,1902,1904],{"class":142,"line":1901},50,[140,1903,1460],{"class":146},[140,1905,1906],{"class":157}," wrapper\n",[140,1908,1910],{"class":142,"line":1909},51,[140,1911,1375],{"class":157},[140,1913,1915],{"class":142,"line":1914},52,[140,1916,188],{"emptyLinePlaceholder":187},[140,1918,1920,1922,1924,1926,1929],{"class":142,"line":1919},53,[140,1921,1236],{"class":146},[140,1923,1872],{"class":177},[140,1925,552],{"class":157},[140,1927,1928],{"class":1244},"v",[140,1930,1253],{"class":157},[140,1932,1934,1936,1938,1941,1943,1945,1947,1949,1951,1953,1955,1957,1960,1962],{"class":142,"line":1933},54,[140,1935,1460],{"class":146},[140,1937,1532],{"class":146},[140,1939,1940],{"class":157}," v ",[140,1942,1538],{"class":146},[140,1944,1541],{"class":406},[140,1946,1544],{"class":146},[140,1948,1940],{"class":157},[140,1950,1549],{"class":146},[140,1952,1224],{"class":150},[140,1954,1554],{"class":146},[140,1956,178],{"class":177},[140,1958,1959],{"class":157},"(v) ",[140,1961,1562],{"class":146},[140,1963,1964],{"class":157}," v\n",[140,1966,1968],{"class":142,"line":1967},55,[140,1969,1375],{"class":157},[140,1971,1973],{"class":142,"line":1972},56,[140,1974,188],{"emptyLinePlaceholder":187},[140,1976,1978,1980,1982,1984,1987],{"class":142,"line":1977},57,[140,1979,1236],{"class":146},[140,1981,1427],{"class":177},[140,1983,552],{"class":157},[140,1985,1986],{"class":1244},"fn",[140,1988,1253],{"class":157},[140,1990,1992,1995,1997],{"class":142,"line":1991},58,[140,1993,1994],{"class":157},"  activeEffect ",[140,1996,207],{"class":146},[140,1998,1999],{"class":157}," fn\n",[140,2001,2003,2006],{"class":142,"line":2002},59,[140,2004,2005],{"class":177},"  fn",[140,2007,2008],{"class":157},"()\n",[140,2010,2012,2014,2016],{"class":142,"line":2011},60,[140,2013,1994],{"class":157},[140,2015,207],{"class":146},[140,2017,2018],{"class":150}," null\n",[140,2020,2022],{"class":142,"line":2021},61,[140,2023,1375],{"class":157},[140,2025,2027],{"class":142,"line":2026},62,[140,2028,188],{"emptyLinePlaceholder":187},[140,2030,2032],{"class":142,"line":2031},63,[140,2033,2034],{"class":197},"\u002F\u002F reactive：只有讀過的屬性會觸發\n",[140,2036,2038,2040,2042,2044,2046,2048,2050,2053,2055],{"class":142,"line":2037},64,[140,2039,147],{"class":146},[140,2041,172],{"class":150},[140,2043,154],{"class":146},[140,2045,178],{"class":177},[140,2047,680],{"class":157},[140,2049,161],{"class":150},[140,2051,2052],{"class":157},", other: ",[140,2054,161],{"class":150},[140,2056,685],{"class":157},[140,2058,2060,2062,2065,2068,2071,2074,2076,2079],{"class":142,"line":2059},65,[140,2061,98],{"class":177},[140,2063,2064],{"class":157},"(() ",[140,2066,2067],{"class":146},"=>",[140,2069,2070],{"class":157}," console.",[140,2072,2073],{"class":177},"log",[140,2075,552],{"class":157},[140,2077,2078],{"class":406},"'render, count ='",[140,2080,2081],{"class":157},", state.count))\n",[140,2083,2085,2087,2090],{"class":142,"line":2084},66,[140,2086,128],{"class":157},[140,2088,2089],{"class":146},"++",[140,2091,2092],{"class":197},"   \u002F\u002F 印出 count = 1\n",[140,2094,2096,2099,2101],{"class":142,"line":2095},67,[140,2097,2098],{"class":157},"state.other",[140,2100,2089],{"class":146},[140,2102,2103],{"class":197},"   \u002F\u002F 不會印，effect 沒有讀 other\n",[140,2105,2107],{"class":142,"line":2106},68,[140,2108,188],{"emptyLinePlaceholder":187},[140,2110,2112],{"class":142,"line":2111},69,[140,2113,2114],{"class":197},"\u002F\u002F ref 放陣列：兩層都會觸發\n",[140,2116,2118,2120,2122,2124,2126],{"class":142,"line":2117},70,[140,2119,147],{"class":146},[140,2121,533],{"class":150},[140,2123,154],{"class":146},[140,2125,538],{"class":177},[140,2127,541],{"class":157},[140,2129,2131,2133,2135,2137,2139,2141,2143,2146,2149,2151],{"class":142,"line":2130},71,[140,2132,98],{"class":177},[140,2134,2064],{"class":157},[140,2136,2067],{"class":146},[140,2138,2070],{"class":157},[140,2140,2073],{"class":177},[140,2142,552],{"class":157},[140,2144,2145],{"class":406},"'render, length ='",[140,2147,2148],{"class":157},", list.value.",[140,2150,941],{"class":150},[140,2152,2153],{"class":157},"))\n",[140,2155,2157,2159,2161,2163,2165,2167],{"class":142,"line":2156},72,[140,2158,546],{"class":157},[140,2160,549],{"class":177},[140,2162,552],{"class":157},[140,2164,555],{"class":150},[140,2166,558],{"class":157},[140,2168,2169],{"class":197},"\u002F\u002F 第二層，印出 length = 1\n",[140,2171,2173,2175,2177,2179,2181,2183,2185,2187,2189,2191],{"class":142,"line":2172},73,[140,2174,566],{"class":157},[140,2176,207],{"class":146},[140,2178,571],{"class":157},[140,2180,574],{"class":150},[140,2182,577],{"class":157},[140,2184,580],{"class":150},[140,2186,577],{"class":157},[140,2188,585],{"class":150},[140,2190,588],{"class":157},[140,2192,2193],{"class":197},"\u002F\u002F 第一層，印出 length = 3\n",[2195,2196,2197],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}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 .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}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 .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":136,"searchDepth":167,"depth":167,"links":2199},[2200,2201,2202,2203,2209,2216,2220,2221],{"id":12,"depth":167,"text":12},{"id":40,"depth":167,"text":41},{"id":48,"depth":167,"text":49},{"id":230,"depth":167,"text":231,"children":2204},[2205,2206,2207,2208],{"id":235,"depth":184,"text":236},{"id":288,"depth":184,"text":289},{"id":342,"depth":184,"text":342},{"id":381,"depth":184,"text":382},{"id":458,"depth":167,"text":459,"children":2210},[2211,2212,2213,2214,2215],{"id":462,"depth":184,"text":463},{"id":499,"depth":184,"text":500},{"id":600,"depth":184,"text":601},{"id":736,"depth":184,"text":737},{"id":792,"depth":184,"text":792},{"id":886,"depth":167,"text":887,"children":2217},[2218,2219],{"id":890,"depth":184,"text":891},{"id":997,"depth":184,"text":998},{"id":1081,"depth":167,"text":1082},{"id":1178,"depth":167,"text":1179},"2026-10-22","畫面會自動更新，是因為 Proxy 攔截了資料的讀寫：讀取時登記誰在用，寫入時通知它們重新執行。",false,"md",{},"\u002Fnotes\u002Fvue3-reactivity",{"title":5,"description":2223},"notes\u002Fvue3-reactivity",[2231,2232],"Vue","面試準備","e1rgKC2tkAG6L1zuGIg_Nsgk4xPg2fqCdJogYUi_ZYE",1793181520358]