[{"data":1,"prerenderedAt":2891},["ShallowReactive",2],{"note:code\u002Fvue\u002F06-composition-composables":3},{"id":4,"title":5,"body":6,"description":16,"extension":2885,"meta":2886,"navigation":273,"path":2887,"seo":2888,"stem":2889,"__hash__":2890},"notes\u002Fsource\u002Fcode\u002Fvue\u002F06-composition-composables.md","06 Composition API 和组合式函数",{"type":7,"value":8,"toc":2860},"minimark",[9,13,17,29,34,37,210,213,235,238,359,362,366,369,386,404,468,471,478,633,636,662,666,673,851,854,1017,1023,1029,1061,1064,1079,1088,1091,1095,1098,1101,1169,1178,1210,1220,1223,1291,1297,1304,1479,1481,1504,1507,1567,1574,1580,1829,1832,1868,1870,1894,1897,1930,1933,1940,1943,2207,2210,2302,2304,2383,2387,2390,2404,2407,2436,2439,2450,2454,2457,2494,2497,2500,2539,2542,2545,2561,2565,2567,2578,2581,2592,2594,2652,2656,2661,2701,2707,2717,2721,2756,2762,2775,2779,2785,2801,2804,2807,2856],[10,11,5],"h1",{"id":12},"_06-composition-api-和组合式函数",[14,15,16],"p",{},"Composition API 是 Vue 3 的核心写法之一。它不是为了让代码看起来高级，而是为了把同一件事相关的状态和逻辑放在一起，并让逻辑可以复用。",[14,18,19,20,24,25,28],{},"本章目标：你能看懂 ",[21,22,23],"code",{},"\u003Cscript setup>"," 中的 Composition API，能写自己的组合式函数，能判断什么时候该抽 ",[21,26,27],{},"useXxx","，什么时候不该抽。",[30,31,33],"h2",{"id":32},"_1-options-api-和-composition-api-的区别","1. Options API 和 Composition API 的区别",[14,35,36],{},"早期 Vue 常见 Options API：",[38,39,44],"pre",{"className":40,"code":41,"language":42,"meta":43,"style":43},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript>\nexport default {\n  data() {\n    return {\n      count: 0\n    }\n  },\n  computed: {\n    doubleCount() {\n      return this.count * 2\n    }\n  },\n  methods: {\n    increase() {\n      this.count++\n    }\n  }\n}\n\u003C\u002Fscript>\n","vue","",[21,45,46,62,75,85,93,103,109,115,121,129,147,152,157,163,171,183,188,194,200],{"__ignoreMap":43},[47,48,51,55,59],"span",{"class":49,"line":50},"line",1,[47,52,54],{"class":53},"sVt8B","\u003C",[47,56,58],{"class":57},"s9eBZ","script",[47,60,61],{"class":53},">\n",[47,63,65,69,72],{"class":49,"line":64},2,[47,66,68],{"class":67},"szBVR","export",[47,70,71],{"class":67}," default",[47,73,74],{"class":53}," {\n",[47,76,78,82],{"class":49,"line":77},3,[47,79,81],{"class":80},"sScJk","  data",[47,83,84],{"class":53},"() {\n",[47,86,88,91],{"class":49,"line":87},4,[47,89,90],{"class":67},"    return",[47,92,74],{"class":53},[47,94,96,99],{"class":49,"line":95},5,[47,97,98],{"class":53},"      count: ",[47,100,102],{"class":101},"sj4cs","0\n",[47,104,106],{"class":49,"line":105},6,[47,107,108],{"class":53},"    }\n",[47,110,112],{"class":49,"line":111},7,[47,113,114],{"class":53},"  },\n",[47,116,118],{"class":49,"line":117},8,[47,119,120],{"class":53},"  computed: {\n",[47,122,124,127],{"class":49,"line":123},9,[47,125,126],{"class":80},"    doubleCount",[47,128,84],{"class":53},[47,130,132,135,138,141,144],{"class":49,"line":131},10,[47,133,134],{"class":67},"      return",[47,136,137],{"class":101}," this",[47,139,140],{"class":53},".count ",[47,142,143],{"class":67},"*",[47,145,146],{"class":101}," 2\n",[47,148,150],{"class":49,"line":149},11,[47,151,108],{"class":53},[47,153,155],{"class":49,"line":154},12,[47,156,114],{"class":53},[47,158,160],{"class":49,"line":159},13,[47,161,162],{"class":53},"  methods: {\n",[47,164,166,169],{"class":49,"line":165},14,[47,167,168],{"class":80},"    increase",[47,170,84],{"class":53},[47,172,174,177,180],{"class":49,"line":173},15,[47,175,176],{"class":101},"      this",[47,178,179],{"class":53},".count",[47,181,182],{"class":67},"++\n",[47,184,186],{"class":49,"line":185},16,[47,187,108],{"class":53},[47,189,191],{"class":49,"line":190},17,[47,192,193],{"class":53},"  }\n",[47,195,197],{"class":49,"line":196},18,[47,198,199],{"class":53},"}\n",[47,201,203,206,208],{"class":49,"line":202},19,[47,204,205],{"class":53},"\u003C\u002F",[47,207,58],{"class":57},[47,209,61],{"class":53},[14,211,212],{},"逻辑按选项分类：",[214,215,216,223,229],"ul",{},[217,218,219,222],"li",{},[21,220,221],{},"data"," 放状态。",[217,224,225,228],{},[21,226,227],{},"computed"," 放计算属性。",[217,230,231,234],{},[21,232,233],{},"methods"," 放方法。",[14,236,237],{},"Composition API：",[38,239,241],{"className":40,"code":240,"language":42,"meta":43,"style":43},"\u003Cscript setup>\nimport { computed, ref } from 'vue'\n\nconst count = ref(0)\nconst doubleCount = computed(() => count.value * 2)\n\nfunction increase() {\n  count.value++\n}\n\u003C\u002Fscript>\n",[21,242,243,254,269,275,298,326,330,340,347,351],{"__ignoreMap":43},[47,244,245,247,249,252],{"class":49,"line":50},[47,246,54],{"class":53},[47,248,58],{"class":57},[47,250,251],{"class":80}," setup",[47,253,61],{"class":53},[47,255,256,259,262,265],{"class":49,"line":64},[47,257,258],{"class":67},"import",[47,260,261],{"class":53}," { computed, ref } ",[47,263,264],{"class":67},"from",[47,266,268],{"class":267},"sZZnC"," 'vue'\n",[47,270,271],{"class":49,"line":77},[47,272,274],{"emptyLinePlaceholder":273},true,"\n",[47,276,277,280,283,286,289,292,295],{"class":49,"line":87},[47,278,279],{"class":67},"const",[47,281,282],{"class":101}," count",[47,284,285],{"class":67}," =",[47,287,288],{"class":80}," ref",[47,290,291],{"class":53},"(",[47,293,294],{"class":101},"0",[47,296,297],{"class":53},")\n",[47,299,300,302,305,307,310,313,316,319,321,324],{"class":49,"line":95},[47,301,279],{"class":67},[47,303,304],{"class":101}," doubleCount",[47,306,285],{"class":67},[47,308,309],{"class":80}," computed",[47,311,312],{"class":53},"(() ",[47,314,315],{"class":67},"=>",[47,317,318],{"class":53}," count.value ",[47,320,143],{"class":67},[47,322,323],{"class":101}," 2",[47,325,297],{"class":53},[47,327,328],{"class":49,"line":105},[47,329,274],{"emptyLinePlaceholder":273},[47,331,332,335,338],{"class":49,"line":111},[47,333,334],{"class":67},"function",[47,336,337],{"class":80}," increase",[47,339,84],{"class":53},[47,341,342,345],{"class":49,"line":117},[47,343,344],{"class":53},"  count.value",[47,346,182],{"class":67},[47,348,349],{"class":49,"line":123},[47,350,199],{"class":53},[47,352,353,355,357],{"class":49,"line":131},[47,354,205],{"class":53},[47,356,58],{"class":57},[47,358,61],{"class":53},[14,360,361],{},"逻辑按功能组织。和计数器有关的状态、计算属性、函数放在一起。",[30,363,365],{"id":364},"_2-为什么-composition-api-适合复杂组件","2. 为什么 Composition API 适合复杂组件",[14,367,368],{},"假设一个组件同时有：",[214,370,371,374,377,380,383],{},[217,372,373],{},"搜索逻辑。",[217,375,376],{},"分页逻辑。",[217,378,379],{},"表单校验。",[217,381,382],{},"弹窗状态。",[217,384,385],{},"请求状态。",[14,387,388,389,391,392,391,394,391,396,399,400,403],{},"Options API 里，搜索相关代码可能分散在 ",[21,390,221],{},"、",[21,393,227],{},[21,395,233],{},[21,397,398],{},"watch","。Composition API 可以把搜索相关内容写在一起，甚至抽成 ",[21,401,402],{},"useSearch","。",[38,405,409],{"className":406,"code":407,"language":408,"meta":43,"style":43},"language-js shiki shiki-themes github-light github-dark","const keyword = ref('')\nconst filteredItems = computed(...)\nwatch(keyword, ...)\nfunction clearKeyword() {}\n","js",[21,410,411,429,447,458],{"__ignoreMap":43},[47,412,413,415,418,420,422,424,427],{"class":49,"line":50},[47,414,279],{"class":67},[47,416,417],{"class":101}," keyword",[47,419,285],{"class":67},[47,421,288],{"class":80},[47,423,291],{"class":53},[47,425,426],{"class":267},"''",[47,428,297],{"class":53},[47,430,431,433,436,438,440,442,445],{"class":49,"line":64},[47,432,279],{"class":67},[47,434,435],{"class":101}," filteredItems",[47,437,285],{"class":67},[47,439,309],{"class":80},[47,441,291],{"class":53},[47,443,444],{"class":67},"...",[47,446,297],{"class":53},[47,448,449,451,454,456],{"class":49,"line":77},[47,450,398],{"class":80},[47,452,453],{"class":53},"(keyword, ",[47,455,444],{"class":67},[47,457,297],{"class":53},[47,459,460,462,465],{"class":49,"line":87},[47,461,334],{"class":67},[47,463,464],{"class":80}," clearKeyword",[47,466,467],{"class":53},"() {}\n",[14,469,470],{},"这些代码挨在一起，读起来更像“一个功能块”。",[30,472,474,475,477],{"id":473},"_3-script-setup-中的基本写法","3. ",[21,476,23],{}," 中的基本写法",[38,479,481],{"className":40,"code":480,"language":42,"meta":43,"style":43},"\u003Cscript setup>\nimport { computed, ref } from 'vue'\n\nconst count = ref(0)\nconst doubleCount = computed(() => count.value * 2)\n\nfunction increase() {\n  count.value++\n}\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cbutton @click=\"increase\">{{ count }}\u003C\u002Fbutton>\n  \u003Cp>两倍：{{ doubleCount }}\u003C\u002Fp>\n\u003C\u002Ftemplate>\n",[21,482,483,493,503,507,523,545,549,557,563,567,575,579,588,612,625],{"__ignoreMap":43},[47,484,485,487,489,491],{"class":49,"line":50},[47,486,54],{"class":53},[47,488,58],{"class":57},[47,490,251],{"class":80},[47,492,61],{"class":53},[47,494,495,497,499,501],{"class":49,"line":64},[47,496,258],{"class":67},[47,498,261],{"class":53},[47,500,264],{"class":67},[47,502,268],{"class":267},[47,504,505],{"class":49,"line":77},[47,506,274],{"emptyLinePlaceholder":273},[47,508,509,511,513,515,517,519,521],{"class":49,"line":87},[47,510,279],{"class":67},[47,512,282],{"class":101},[47,514,285],{"class":67},[47,516,288],{"class":80},[47,518,291],{"class":53},[47,520,294],{"class":101},[47,522,297],{"class":53},[47,524,525,527,529,531,533,535,537,539,541,543],{"class":49,"line":95},[47,526,279],{"class":67},[47,528,304],{"class":101},[47,530,285],{"class":67},[47,532,309],{"class":80},[47,534,312],{"class":53},[47,536,315],{"class":67},[47,538,318],{"class":53},[47,540,143],{"class":67},[47,542,323],{"class":101},[47,544,297],{"class":53},[47,546,547],{"class":49,"line":105},[47,548,274],{"emptyLinePlaceholder":273},[47,550,551,553,555],{"class":49,"line":111},[47,552,334],{"class":67},[47,554,337],{"class":80},[47,556,84],{"class":53},[47,558,559,561],{"class":49,"line":117},[47,560,344],{"class":53},[47,562,182],{"class":67},[47,564,565],{"class":49,"line":123},[47,566,199],{"class":53},[47,568,569,571,573],{"class":49,"line":131},[47,570,205],{"class":53},[47,572,58],{"class":57},[47,574,61],{"class":53},[47,576,577],{"class":49,"line":149},[47,578,274],{"emptyLinePlaceholder":273},[47,580,581,583,586],{"class":49,"line":154},[47,582,54],{"class":53},[47,584,585],{"class":57},"template",[47,587,61],{"class":53},[47,589,590,593,596,599,602,605,608,610],{"class":49,"line":159},[47,591,592],{"class":53},"  \u003C",[47,594,595],{"class":57},"button",[47,597,598],{"class":80}," @click",[47,600,601],{"class":53},"=",[47,603,604],{"class":267},"\"increase\"",[47,606,607],{"class":53},">{{ count }}\u003C\u002F",[47,609,595],{"class":57},[47,611,61],{"class":53},[47,613,614,616,618,621,623],{"class":49,"line":165},[47,615,592],{"class":53},[47,617,14],{"class":57},[47,619,620],{"class":53},">两倍：{{ doubleCount }}\u003C\u002F",[47,622,14],{"class":57},[47,624,61],{"class":53},[47,626,627,629,631],{"class":49,"line":173},[47,628,205],{"class":53},[47,630,585],{"class":57},[47,632,61],{"class":53},[14,634,635],{},"重要规则：",[214,637,638,648,656,659],{},[217,639,640,641,644,645,403],{},"脚本中访问 ",[21,642,643],{},"ref"," 要 ",[21,646,647],{},".value",[217,649,650,651,653,654,403],{},"模板中访问 ",[21,652,643],{}," 不用 ",[21,655,647],{},[217,657,658],{},"顶层声明的变量和函数可以直接在模板使用。",[217,660,661],{},"导入的组件也可以直接在模板使用。",[30,663,665],{"id":664},"_4-组合式函数是什么","4. 组合式函数是什么",[14,667,668,669,672],{},"组合式函数通常是一个以 ",[21,670,671],{},"use"," 开头的函数，它使用 Vue 的响应式 API，并返回状态和方法。",[38,674,676],{"className":406,"code":675,"language":408,"meta":43,"style":43},"\u002F\u002F useCounter.js\nimport { computed, ref } from 'vue'\n\nexport function useCounter(initialValue = 0) {\n  const count = ref(initialValue)\n  const doubleCount = computed(() => count.value * 2)\n\n  function increase() {\n    count.value++\n  }\n\n  function reset() {\n    count.value = initialValue\n  }\n\n  return {\n    count,\n    doubleCount,\n    increase,\n    reset\n  }\n}\n",[21,677,678,684,694,698,722,736,758,762,771,778,782,786,795,805,809,813,820,825,830,835,841,846],{"__ignoreMap":43},[47,679,680],{"class":49,"line":50},[47,681,683],{"class":682},"sJ8bj","\u002F\u002F useCounter.js\n",[47,685,686,688,690,692],{"class":49,"line":64},[47,687,258],{"class":67},[47,689,261],{"class":53},[47,691,264],{"class":67},[47,693,268],{"class":267},[47,695,696],{"class":49,"line":77},[47,697,274],{"emptyLinePlaceholder":273},[47,699,700,702,705,708,710,714,716,719],{"class":49,"line":87},[47,701,68],{"class":67},[47,703,704],{"class":67}," function",[47,706,707],{"class":80}," useCounter",[47,709,291],{"class":53},[47,711,713],{"class":712},"s4XuR","initialValue",[47,715,285],{"class":67},[47,717,718],{"class":101}," 0",[47,720,721],{"class":53},") {\n",[47,723,724,727,729,731,733],{"class":49,"line":95},[47,725,726],{"class":67},"  const",[47,728,282],{"class":101},[47,730,285],{"class":67},[47,732,288],{"class":80},[47,734,735],{"class":53},"(initialValue)\n",[47,737,738,740,742,744,746,748,750,752,754,756],{"class":49,"line":105},[47,739,726],{"class":67},[47,741,304],{"class":101},[47,743,285],{"class":67},[47,745,309],{"class":80},[47,747,312],{"class":53},[47,749,315],{"class":67},[47,751,318],{"class":53},[47,753,143],{"class":67},[47,755,323],{"class":101},[47,757,297],{"class":53},[47,759,760],{"class":49,"line":111},[47,761,274],{"emptyLinePlaceholder":273},[47,763,764,767,769],{"class":49,"line":117},[47,765,766],{"class":67},"  function",[47,768,337],{"class":80},[47,770,84],{"class":53},[47,772,773,776],{"class":49,"line":123},[47,774,775],{"class":53},"    count.value",[47,777,182],{"class":67},[47,779,780],{"class":49,"line":131},[47,781,193],{"class":53},[47,783,784],{"class":49,"line":149},[47,785,274],{"emptyLinePlaceholder":273},[47,787,788,790,793],{"class":49,"line":154},[47,789,766],{"class":67},[47,791,792],{"class":80}," reset",[47,794,84],{"class":53},[47,796,797,800,802],{"class":49,"line":159},[47,798,799],{"class":53},"    count.value ",[47,801,601],{"class":67},[47,803,804],{"class":53}," initialValue\n",[47,806,807],{"class":49,"line":165},[47,808,193],{"class":53},[47,810,811],{"class":49,"line":173},[47,812,274],{"emptyLinePlaceholder":273},[47,814,815,818],{"class":49,"line":185},[47,816,817],{"class":67},"  return",[47,819,74],{"class":53},[47,821,822],{"class":49,"line":190},[47,823,824],{"class":53},"    count,\n",[47,826,827],{"class":49,"line":196},[47,828,829],{"class":53},"    doubleCount,\n",[47,831,832],{"class":49,"line":202},[47,833,834],{"class":53},"    increase,\n",[47,836,838],{"class":49,"line":837},20,[47,839,840],{"class":53},"    reset\n",[47,842,844],{"class":49,"line":843},21,[47,845,193],{"class":53},[47,847,849],{"class":49,"line":848},22,[47,850,199],{"class":53},[14,852,853],{},"组件使用：",[38,855,857],{"className":40,"code":856,"language":42,"meta":43,"style":43},"\u003Cscript setup>\nimport { useCounter } from '..\u002Fcomposables\u002FuseCounter'\n\nconst { count, doubleCount, increase, reset } = useCounter(5)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cp>{{ count }}\u003C\u002Fp>\n  \u003Cp>{{ doubleCount }}\u003C\u002Fp>\n  \u003Cbutton @click=\"increase\">增加\u003C\u002Fbutton>\n  \u003Cbutton @click=\"reset\">重置\u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n",[21,858,859,869,881,885,925,933,937,945,957,970,989,1009],{"__ignoreMap":43},[47,860,861,863,865,867],{"class":49,"line":50},[47,862,54],{"class":53},[47,864,58],{"class":57},[47,866,251],{"class":80},[47,868,61],{"class":53},[47,870,871,873,876,878],{"class":49,"line":64},[47,872,258],{"class":67},[47,874,875],{"class":53}," { useCounter } ",[47,877,264],{"class":67},[47,879,880],{"class":267}," '..\u002Fcomposables\u002FuseCounter'\n",[47,882,883],{"class":49,"line":77},[47,884,274],{"emptyLinePlaceholder":273},[47,886,887,889,892,895,898,901,903,906,908,911,914,916,918,920,923],{"class":49,"line":87},[47,888,279],{"class":67},[47,890,891],{"class":53}," { ",[47,893,894],{"class":101},"count",[47,896,897],{"class":53},", ",[47,899,900],{"class":101},"doubleCount",[47,902,897],{"class":53},[47,904,905],{"class":101},"increase",[47,907,897],{"class":53},[47,909,910],{"class":101},"reset",[47,912,913],{"class":53}," } ",[47,915,601],{"class":67},[47,917,707],{"class":80},[47,919,291],{"class":53},[47,921,922],{"class":101},"5",[47,924,297],{"class":53},[47,926,927,929,931],{"class":49,"line":95},[47,928,205],{"class":53},[47,930,58],{"class":57},[47,932,61],{"class":53},[47,934,935],{"class":49,"line":105},[47,936,274],{"emptyLinePlaceholder":273},[47,938,939,941,943],{"class":49,"line":111},[47,940,54],{"class":53},[47,942,585],{"class":57},[47,944,61],{"class":53},[47,946,947,949,951,953,955],{"class":49,"line":117},[47,948,592],{"class":53},[47,950,14],{"class":57},[47,952,607],{"class":53},[47,954,14],{"class":57},[47,956,61],{"class":53},[47,958,959,961,963,966,968],{"class":49,"line":123},[47,960,592],{"class":53},[47,962,14],{"class":57},[47,964,965],{"class":53},">{{ doubleCount }}\u003C\u002F",[47,967,14],{"class":57},[47,969,61],{"class":53},[47,971,972,974,976,978,980,982,985,987],{"class":49,"line":131},[47,973,592],{"class":53},[47,975,595],{"class":57},[47,977,598],{"class":80},[47,979,601],{"class":53},[47,981,604],{"class":267},[47,983,984],{"class":53},">增加\u003C\u002F",[47,986,595],{"class":57},[47,988,61],{"class":53},[47,990,991,993,995,997,999,1002,1005,1007],{"class":49,"line":149},[47,992,592],{"class":53},[47,994,595],{"class":57},[47,996,598],{"class":80},[47,998,601],{"class":53},[47,1000,1001],{"class":267},"\"reset\"",[47,1003,1004],{"class":53},">重置\u003C\u002F",[47,1006,595],{"class":57},[47,1008,61],{"class":53},[47,1010,1011,1013,1015],{"class":49,"line":154},[47,1012,205],{"class":53},[47,1014,585],{"class":57},[47,1016,61],{"class":53},[30,1018,1020,1021],{"id":1019},"_5-为什么组合式函数需要-export","5. 为什么组合式函数需要 ",[21,1022,68],{},[14,1024,1025,1028],{},[21,1026,1027],{},"useCounter.js"," 是一个单独模块。组件要导入它，就必须从模块中导出。",[38,1030,1032],{"className":406,"code":1031,"language":408,"meta":43,"style":43},"export function useCounter(initialValue = 0) {\n  \u002F\u002F ...\n}\n",[21,1033,1034,1052,1057],{"__ignoreMap":43},[47,1035,1036,1038,1040,1042,1044,1046,1048,1050],{"class":49,"line":50},[47,1037,68],{"class":67},[47,1039,704],{"class":67},[47,1041,707],{"class":80},[47,1043,291],{"class":53},[47,1045,713],{"class":712},[47,1047,285],{"class":67},[47,1049,718],{"class":101},[47,1051,721],{"class":53},[47,1053,1054],{"class":49,"line":64},[47,1055,1056],{"class":682},"  \u002F\u002F ...\n",[47,1058,1059],{"class":49,"line":77},[47,1060,199],{"class":53},[14,1062,1063],{},"然后组件才能：",[38,1065,1067],{"className":406,"code":1066,"language":408,"meta":43,"style":43},"import { useCounter } from '..\u002Fcomposables\u002FuseCounter'\n",[21,1068,1069],{"__ignoreMap":43},[47,1070,1071,1073,1075,1077],{"class":49,"line":50},[47,1072,258],{"class":67},[47,1074,875],{"class":53},[47,1076,264],{"class":67},[47,1078,880],{"class":267},[14,1080,1081,1082,1084,1085,1087],{},"如果不写 ",[21,1083,68],{},"，这个函数只属于 ",[21,1086,1027],{}," 文件内部，其他文件不能按名字导入。",[14,1089,1090],{},"这和第 1 章讲的模块规则完全一致。",[30,1092,1094],{"id":1093},"_6-每次调用组合式函数都会创建新状态吗","6. 每次调用组合式函数都会创建新状态吗",[14,1096,1097],{},"看函数内部状态写在哪里。",[14,1099,1100],{},"如果状态写在函数内部：",[38,1102,1104],{"className":406,"code":1103,"language":408,"meta":43,"style":43},"export function useCounter() {\n  const count = ref(0)\n\n  function increase() {\n    count.value++\n  }\n\n  return { count, increase }\n}\n",[21,1105,1106,1116,1132,1136,1144,1150,1154,1158,1165],{"__ignoreMap":43},[47,1107,1108,1110,1112,1114],{"class":49,"line":50},[47,1109,68],{"class":67},[47,1111,704],{"class":67},[47,1113,707],{"class":80},[47,1115,84],{"class":53},[47,1117,1118,1120,1122,1124,1126,1128,1130],{"class":49,"line":64},[47,1119,726],{"class":67},[47,1121,282],{"class":101},[47,1123,285],{"class":67},[47,1125,288],{"class":80},[47,1127,291],{"class":53},[47,1129,294],{"class":101},[47,1131,297],{"class":53},[47,1133,1134],{"class":49,"line":77},[47,1135,274],{"emptyLinePlaceholder":273},[47,1137,1138,1140,1142],{"class":49,"line":87},[47,1139,766],{"class":67},[47,1141,337],{"class":80},[47,1143,84],{"class":53},[47,1145,1146,1148],{"class":49,"line":95},[47,1147,775],{"class":53},[47,1149,182],{"class":67},[47,1151,1152],{"class":49,"line":105},[47,1153,193],{"class":53},[47,1155,1156],{"class":49,"line":111},[47,1157,274],{"emptyLinePlaceholder":273},[47,1159,1160,1162],{"class":49,"line":117},[47,1161,817],{"class":67},[47,1163,1164],{"class":53}," { count, increase }\n",[47,1166,1167],{"class":49,"line":123},[47,1168,199],{"class":53},[14,1170,1171,1172,1175,1176,403],{},"每个组件调用 ",[21,1173,1174],{},"useCounter()"," 都会得到自己的 ",[21,1177,894],{},[38,1179,1181],{"className":406,"code":1180,"language":408,"meta":43,"style":43},"const counterA = useCounter()\nconst counterB = useCounter()\n",[21,1182,1183,1197],{"__ignoreMap":43},[47,1184,1185,1187,1190,1192,1194],{"class":49,"line":50},[47,1186,279],{"class":67},[47,1188,1189],{"class":101}," counterA",[47,1191,285],{"class":67},[47,1193,707],{"class":80},[47,1195,1196],{"class":53},"()\n",[47,1198,1199,1201,1204,1206,1208],{"class":49,"line":64},[47,1200,279],{"class":67},[47,1202,1203],{"class":101}," counterB",[47,1205,285],{"class":67},[47,1207,707],{"class":80},[47,1209,1196],{"class":53},[14,1211,1212,1215,1216,1219],{},[21,1213,1214],{},"counterA.count"," 和 ",[21,1217,1218],{},"counterB.count"," 是两份状态。",[14,1221,1222],{},"如果状态写在函数外部：",[38,1224,1226],{"className":406,"code":1225,"language":408,"meta":43,"style":43},"const count = ref(0)\n\nexport function useSharedCounter() {\n  function increase() {\n    count.value++\n  }\n\n  return { count, increase }\n}\n",[21,1227,1228,1244,1248,1259,1267,1273,1277,1281,1287],{"__ignoreMap":43},[47,1229,1230,1232,1234,1236,1238,1240,1242],{"class":49,"line":50},[47,1231,279],{"class":67},[47,1233,282],{"class":101},[47,1235,285],{"class":67},[47,1237,288],{"class":80},[47,1239,291],{"class":53},[47,1241,294],{"class":101},[47,1243,297],{"class":53},[47,1245,1246],{"class":49,"line":64},[47,1247,274],{"emptyLinePlaceholder":273},[47,1249,1250,1252,1254,1257],{"class":49,"line":77},[47,1251,68],{"class":67},[47,1253,704],{"class":67},[47,1255,1256],{"class":80}," useSharedCounter",[47,1258,84],{"class":53},[47,1260,1261,1263,1265],{"class":49,"line":87},[47,1262,766],{"class":67},[47,1264,337],{"class":80},[47,1266,84],{"class":53},[47,1268,1269,1271],{"class":49,"line":95},[47,1270,775],{"class":53},[47,1272,182],{"class":67},[47,1274,1275],{"class":49,"line":105},[47,1276,193],{"class":53},[47,1278,1279],{"class":49,"line":111},[47,1280,274],{"emptyLinePlaceholder":273},[47,1282,1283,1285],{"class":49,"line":117},[47,1284,817],{"class":67},[47,1286,1164],{"class":53},[47,1288,1289],{"class":49,"line":123},[47,1290,199],{"class":53},[14,1292,1293,1294,1296],{},"所有调用都会共享同一个 ",[21,1295,894],{},"。这有时有用，但要小心，因为它更像全局状态。",[30,1298,1300,1301],{"id":1299},"_7-示例usetoggle","7. 示例：",[21,1302,1303],{},"useToggle",[38,1305,1307],{"className":406,"code":1306,"language":408,"meta":43,"style":43},"import { ref } from 'vue'\n\nexport function useToggle(initialValue = false) {\n  const value = ref(initialValue)\n\n  function toggle() {\n    value.value = !value.value\n  }\n\n  function setTrue() {\n    value.value = true\n  }\n\n  function setFalse() {\n    value.value = false\n  }\n\n  return {\n    value,\n    toggle,\n    setTrue,\n    setFalse\n  }\n}\n",[21,1308,1309,1320,1324,1344,1357,1361,1370,1383,1387,1391,1400,1409,1413,1417,1426,1435,1439,1443,1449,1454,1459,1464,1469,1474],{"__ignoreMap":43},[47,1310,1311,1313,1316,1318],{"class":49,"line":50},[47,1312,258],{"class":67},[47,1314,1315],{"class":53}," { ref } ",[47,1317,264],{"class":67},[47,1319,268],{"class":267},[47,1321,1322],{"class":49,"line":64},[47,1323,274],{"emptyLinePlaceholder":273},[47,1325,1326,1328,1330,1333,1335,1337,1339,1342],{"class":49,"line":77},[47,1327,68],{"class":67},[47,1329,704],{"class":67},[47,1331,1332],{"class":80}," useToggle",[47,1334,291],{"class":53},[47,1336,713],{"class":712},[47,1338,285],{"class":67},[47,1340,1341],{"class":101}," false",[47,1343,721],{"class":53},[47,1345,1346,1348,1351,1353,1355],{"class":49,"line":87},[47,1347,726],{"class":67},[47,1349,1350],{"class":101}," value",[47,1352,285],{"class":67},[47,1354,288],{"class":80},[47,1356,735],{"class":53},[47,1358,1359],{"class":49,"line":95},[47,1360,274],{"emptyLinePlaceholder":273},[47,1362,1363,1365,1368],{"class":49,"line":105},[47,1364,766],{"class":67},[47,1366,1367],{"class":80}," toggle",[47,1369,84],{"class":53},[47,1371,1372,1375,1377,1380],{"class":49,"line":111},[47,1373,1374],{"class":53},"    value.value ",[47,1376,601],{"class":67},[47,1378,1379],{"class":67}," !",[47,1381,1382],{"class":53},"value.value\n",[47,1384,1385],{"class":49,"line":117},[47,1386,193],{"class":53},[47,1388,1389],{"class":49,"line":123},[47,1390,274],{"emptyLinePlaceholder":273},[47,1392,1393,1395,1398],{"class":49,"line":131},[47,1394,766],{"class":67},[47,1396,1397],{"class":80}," setTrue",[47,1399,84],{"class":53},[47,1401,1402,1404,1406],{"class":49,"line":149},[47,1403,1374],{"class":53},[47,1405,601],{"class":67},[47,1407,1408],{"class":101}," true\n",[47,1410,1411],{"class":49,"line":154},[47,1412,193],{"class":53},[47,1414,1415],{"class":49,"line":159},[47,1416,274],{"emptyLinePlaceholder":273},[47,1418,1419,1421,1424],{"class":49,"line":165},[47,1420,766],{"class":67},[47,1422,1423],{"class":80}," setFalse",[47,1425,84],{"class":53},[47,1427,1428,1430,1432],{"class":49,"line":173},[47,1429,1374],{"class":53},[47,1431,601],{"class":67},[47,1433,1434],{"class":101}," false\n",[47,1436,1437],{"class":49,"line":185},[47,1438,193],{"class":53},[47,1440,1441],{"class":49,"line":190},[47,1442,274],{"emptyLinePlaceholder":273},[47,1444,1445,1447],{"class":49,"line":196},[47,1446,817],{"class":67},[47,1448,74],{"class":53},[47,1450,1451],{"class":49,"line":202},[47,1452,1453],{"class":53},"    value,\n",[47,1455,1456],{"class":49,"line":837},[47,1457,1458],{"class":53},"    toggle,\n",[47,1460,1461],{"class":49,"line":843},[47,1462,1463],{"class":53},"    setTrue,\n",[47,1465,1466],{"class":49,"line":848},[47,1467,1468],{"class":53},"    setFalse\n",[47,1470,1472],{"class":49,"line":1471},23,[47,1473,193],{"class":53},[47,1475,1477],{"class":49,"line":1476},24,[47,1478,199],{"class":53},[14,1480,853],{},[38,1482,1484],{"className":406,"code":1483,"language":408,"meta":43,"style":43},"const modal = useToggle(false)\n",[21,1485,1486],{"__ignoreMap":43},[47,1487,1488,1490,1493,1495,1497,1499,1502],{"class":49,"line":50},[47,1489,279],{"class":67},[47,1491,1492],{"class":101}," modal",[47,1494,285],{"class":67},[47,1496,1332],{"class":80},[47,1498,291],{"class":53},[47,1500,1501],{"class":101},"false",[47,1503,297],{"class":53},[14,1505,1506],{},"模板：",[38,1508,1510],{"className":40,"code":1509,"language":42,"meta":43,"style":43},"\u003Cbutton @click=\"modal.setTrue\">打开\u003C\u002Fbutton>\n\u003Cdialog v-if=\"modal.value\">...\u003C\u002Fdialog>\n",[21,1511,1512,1541],{"__ignoreMap":43},[47,1513,1514,1516,1518,1521,1524,1526,1529,1532,1534,1537,1539],{"class":49,"line":50},[47,1515,54],{"class":53},[47,1517,595],{"class":57},[47,1519,1520],{"class":53}," @",[47,1522,1523],{"class":80},"click",[47,1525,601],{"class":53},[47,1527,1528],{"class":267},"\"",[47,1530,1531],{"class":53},"modal.setTrue",[47,1533,1528],{"class":267},[47,1535,1536],{"class":53},">打开\u003C\u002F",[47,1538,595],{"class":57},[47,1540,61],{"class":53},[47,1542,1543,1545,1548,1551,1553,1555,1558,1560,1563,1565],{"class":49,"line":64},[47,1544,54],{"class":53},[47,1546,1547],{"class":57},"dialog",[47,1549,1550],{"class":67}," v-if",[47,1552,601],{"class":53},[47,1554,1528],{"class":267},[47,1556,1557],{"class":53},"modal.value",[47,1559,1528],{"class":267},[47,1561,1562],{"class":53},">...\u003C\u002F",[47,1564,1547],{"class":57},[47,1566,61],{"class":53},[30,1568,1570,1571],{"id":1569},"_8-示例uselocalstorage","8. 示例：",[21,1572,1573],{},"useLocalStorage",[14,1575,1576,1577,403],{},"目标：让一个响应式状态自动保存到浏览器 ",[21,1578,1579],{},"localStorage",[38,1581,1583],{"className":406,"code":1582,"language":408,"meta":43,"style":43},"import { ref, watch } from 'vue'\n\nfunction readStorage(key, defaultValue) {\n  try {\n    const rawValue = localStorage.getItem(key)\n    return rawValue ? JSON.parse(rawValue) : defaultValue\n  } catch {\n    return defaultValue\n  }\n}\n\nexport function useLocalStorage(key, defaultValue) {\n  const state = ref(readStorage(key, defaultValue))\n\n  watch(\n    state,\n    (value) => {\n      localStorage.setItem(key, JSON.stringify(value))\n    },\n    { deep: true }\n  )\n\n  return state\n}\n",[21,1584,1585,1596,1600,1619,1626,1645,1673,1683,1689,1693,1697,1701,1720,1739,1743,1751,1756,1771,1793,1798,1809,1814,1818,1825],{"__ignoreMap":43},[47,1586,1587,1589,1592,1594],{"class":49,"line":50},[47,1588,258],{"class":67},[47,1590,1591],{"class":53}," { ref, watch } ",[47,1593,264],{"class":67},[47,1595,268],{"class":267},[47,1597,1598],{"class":49,"line":64},[47,1599,274],{"emptyLinePlaceholder":273},[47,1601,1602,1604,1607,1609,1612,1614,1617],{"class":49,"line":77},[47,1603,334],{"class":67},[47,1605,1606],{"class":80}," readStorage",[47,1608,291],{"class":53},[47,1610,1611],{"class":712},"key",[47,1613,897],{"class":53},[47,1615,1616],{"class":712},"defaultValue",[47,1618,721],{"class":53},[47,1620,1621,1624],{"class":49,"line":87},[47,1622,1623],{"class":67},"  try",[47,1625,74],{"class":53},[47,1627,1628,1631,1634,1636,1639,1642],{"class":49,"line":95},[47,1629,1630],{"class":67},"    const",[47,1632,1633],{"class":101}," rawValue",[47,1635,285],{"class":67},[47,1637,1638],{"class":53}," localStorage.",[47,1640,1641],{"class":80},"getItem",[47,1643,1644],{"class":53},"(key)\n",[47,1646,1647,1649,1652,1655,1658,1661,1664,1667,1670],{"class":49,"line":105},[47,1648,90],{"class":67},[47,1650,1651],{"class":53}," rawValue ",[47,1653,1654],{"class":67},"?",[47,1656,1657],{"class":101}," JSON",[47,1659,1660],{"class":53},".",[47,1662,1663],{"class":80},"parse",[47,1665,1666],{"class":53},"(rawValue) ",[47,1668,1669],{"class":67},":",[47,1671,1672],{"class":53}," defaultValue\n",[47,1674,1675,1678,1681],{"class":49,"line":111},[47,1676,1677],{"class":53},"  } ",[47,1679,1680],{"class":67},"catch",[47,1682,74],{"class":53},[47,1684,1685,1687],{"class":49,"line":117},[47,1686,90],{"class":67},[47,1688,1672],{"class":53},[47,1690,1691],{"class":49,"line":123},[47,1692,193],{"class":53},[47,1694,1695],{"class":49,"line":131},[47,1696,199],{"class":53},[47,1698,1699],{"class":49,"line":149},[47,1700,274],{"emptyLinePlaceholder":273},[47,1702,1703,1705,1707,1710,1712,1714,1716,1718],{"class":49,"line":154},[47,1704,68],{"class":67},[47,1706,704],{"class":67},[47,1708,1709],{"class":80}," useLocalStorage",[47,1711,291],{"class":53},[47,1713,1611],{"class":712},[47,1715,897],{"class":53},[47,1717,1616],{"class":712},[47,1719,721],{"class":53},[47,1721,1722,1724,1727,1729,1731,1733,1736],{"class":49,"line":159},[47,1723,726],{"class":67},[47,1725,1726],{"class":101}," state",[47,1728,285],{"class":67},[47,1730,288],{"class":80},[47,1732,291],{"class":53},[47,1734,1735],{"class":80},"readStorage",[47,1737,1738],{"class":53},"(key, defaultValue))\n",[47,1740,1741],{"class":49,"line":165},[47,1742,274],{"emptyLinePlaceholder":273},[47,1744,1745,1748],{"class":49,"line":173},[47,1746,1747],{"class":80},"  watch",[47,1749,1750],{"class":53},"(\n",[47,1752,1753],{"class":49,"line":185},[47,1754,1755],{"class":53},"    state,\n",[47,1757,1758,1761,1764,1767,1769],{"class":49,"line":190},[47,1759,1760],{"class":53},"    (",[47,1762,1763],{"class":712},"value",[47,1765,1766],{"class":53},") ",[47,1768,315],{"class":67},[47,1770,74],{"class":53},[47,1772,1773,1776,1779,1782,1785,1787,1790],{"class":49,"line":196},[47,1774,1775],{"class":53},"      localStorage.",[47,1777,1778],{"class":80},"setItem",[47,1780,1781],{"class":53},"(key, ",[47,1783,1784],{"class":101},"JSON",[47,1786,1660],{"class":53},[47,1788,1789],{"class":80},"stringify",[47,1791,1792],{"class":53},"(value))\n",[47,1794,1795],{"class":49,"line":202},[47,1796,1797],{"class":53},"    },\n",[47,1799,1800,1803,1806],{"class":49,"line":837},[47,1801,1802],{"class":53},"    { deep: ",[47,1804,1805],{"class":101},"true",[47,1807,1808],{"class":53}," }\n",[47,1810,1811],{"class":49,"line":843},[47,1812,1813],{"class":53},"  )\n",[47,1815,1816],{"class":49,"line":848},[47,1817,274],{"emptyLinePlaceholder":273},[47,1819,1820,1822],{"class":49,"line":1471},[47,1821,817],{"class":67},[47,1823,1824],{"class":53}," state\n",[47,1826,1827],{"class":49,"line":1476},[47,1828,199],{"class":53},[14,1830,1831],{},"逐步理解：",[214,1833,1834,1839,1842,1848,1855,1862],{},[217,1835,1836,1838],{},[21,1837,1735],{}," 先尝试从浏览器读取已有值。",[217,1840,1841],{},"读不到就使用默认值。",[217,1843,1844,1847],{},[21,1845,1846],{},"state"," 是响应式状态。",[217,1849,1850,1852,1853,403],{},[21,1851,398],{}," 观察 ",[21,1854,1846],{},[217,1856,1857,1859,1860,403],{},[21,1858,1846],{}," 一变化，就写回 ",[21,1861,1579],{},[217,1863,1864,1867],{},[21,1865,1866],{},"{ deep: true }"," 用来监听数组或对象内部变化。",[14,1869,853],{},[38,1871,1873],{"className":406,"code":1872,"language":408,"meta":43,"style":43},"const todos = useLocalStorage('todos', [])\n",[21,1874,1875],{"__ignoreMap":43},[47,1876,1877,1879,1882,1884,1886,1888,1891],{"class":49,"line":50},[47,1878,279],{"class":67},[47,1880,1881],{"class":101}," todos",[47,1883,285],{"class":67},[47,1885,1709],{"class":80},[47,1887,291],{"class":53},[47,1889,1890],{"class":267},"'todos'",[47,1892,1893],{"class":53},", [])\n",[14,1895,1896],{},"之后你像普通 ref 一样使用：",[38,1898,1900],{"className":406,"code":1899,"language":408,"meta":43,"style":43},"todos.value.push({ id: 1, title: '学习持久化', done: false })\n",[21,1901,1902],{"__ignoreMap":43},[47,1903,1904,1907,1910,1913,1916,1919,1922,1925,1927],{"class":49,"line":50},[47,1905,1906],{"class":53},"todos.value.",[47,1908,1909],{"class":80},"push",[47,1911,1912],{"class":53},"({ id: ",[47,1914,1915],{"class":101},"1",[47,1917,1918],{"class":53},", title: ",[47,1920,1921],{"class":267},"'学习持久化'",[47,1923,1924],{"class":53},", done: ",[47,1926,1501],{"class":101},[47,1928,1929],{"class":53}," })\n",[14,1931,1932],{},"刷新后数据还在。",[30,1934,1936,1937],{"id":1935},"_9-示例usefetch","9. 示例：",[21,1938,1939],{},"useFetch",[14,1941,1942],{},"目标：封装请求的 loading、error、data。",[38,1944,1946],{"className":406,"code":1945,"language":408,"meta":43,"style":43},"import { ref } from 'vue'\n\nexport function useFetch(requester) {\n  const data = ref(null)\n  const loading = ref(false)\n  const error = ref('')\n\n  async function execute(...args) {\n    loading.value = true\n    error.value = ''\n\n    try {\n      data.value = await requester(...args)\n    } catch (err) {\n      error.value = err instanceof Error ? err.message : '请求失败'\n    } finally {\n      loading.value = false\n    }\n  }\n\n  return {\n    data,\n    loading,\n    error,\n    execute\n  }\n}\n",[21,1947,1948,1958,1962,1978,1996,2013,2030,2034,2053,2062,2072,2076,2083,2103,2113,2140,2149,2158,2162,2166,2170,2176,2181,2186,2191,2197,2202],{"__ignoreMap":43},[47,1949,1950,1952,1954,1956],{"class":49,"line":50},[47,1951,258],{"class":67},[47,1953,1315],{"class":53},[47,1955,264],{"class":67},[47,1957,268],{"class":267},[47,1959,1960],{"class":49,"line":64},[47,1961,274],{"emptyLinePlaceholder":273},[47,1963,1964,1966,1968,1971,1973,1976],{"class":49,"line":77},[47,1965,68],{"class":67},[47,1967,704],{"class":67},[47,1969,1970],{"class":80}," useFetch",[47,1972,291],{"class":53},[47,1974,1975],{"class":712},"requester",[47,1977,721],{"class":53},[47,1979,1980,1982,1985,1987,1989,1991,1994],{"class":49,"line":87},[47,1981,726],{"class":67},[47,1983,1984],{"class":101}," data",[47,1986,285],{"class":67},[47,1988,288],{"class":80},[47,1990,291],{"class":53},[47,1992,1993],{"class":101},"null",[47,1995,297],{"class":53},[47,1997,1998,2000,2003,2005,2007,2009,2011],{"class":49,"line":95},[47,1999,726],{"class":67},[47,2001,2002],{"class":101}," loading",[47,2004,285],{"class":67},[47,2006,288],{"class":80},[47,2008,291],{"class":53},[47,2010,1501],{"class":101},[47,2012,297],{"class":53},[47,2014,2015,2017,2020,2022,2024,2026,2028],{"class":49,"line":105},[47,2016,726],{"class":67},[47,2018,2019],{"class":101}," error",[47,2021,285],{"class":67},[47,2023,288],{"class":80},[47,2025,291],{"class":53},[47,2027,426],{"class":267},[47,2029,297],{"class":53},[47,2031,2032],{"class":49,"line":111},[47,2033,274],{"emptyLinePlaceholder":273},[47,2035,2036,2039,2041,2044,2046,2048,2051],{"class":49,"line":117},[47,2037,2038],{"class":67},"  async",[47,2040,704],{"class":67},[47,2042,2043],{"class":80}," execute",[47,2045,291],{"class":53},[47,2047,444],{"class":67},[47,2049,2050],{"class":712},"args",[47,2052,721],{"class":53},[47,2054,2055,2058,2060],{"class":49,"line":123},[47,2056,2057],{"class":53},"    loading.value ",[47,2059,601],{"class":67},[47,2061,1408],{"class":101},[47,2063,2064,2067,2069],{"class":49,"line":131},[47,2065,2066],{"class":53},"    error.value ",[47,2068,601],{"class":67},[47,2070,2071],{"class":267}," ''\n",[47,2073,2074],{"class":49,"line":149},[47,2075,274],{"emptyLinePlaceholder":273},[47,2077,2078,2081],{"class":49,"line":154},[47,2079,2080],{"class":67},"    try",[47,2082,74],{"class":53},[47,2084,2085,2088,2090,2093,2096,2098,2100],{"class":49,"line":159},[47,2086,2087],{"class":53},"      data.value ",[47,2089,601],{"class":67},[47,2091,2092],{"class":67}," await",[47,2094,2095],{"class":80}," requester",[47,2097,291],{"class":53},[47,2099,444],{"class":67},[47,2101,2102],{"class":53},"args)\n",[47,2104,2105,2108,2110],{"class":49,"line":165},[47,2106,2107],{"class":53},"    } ",[47,2109,1680],{"class":67},[47,2111,2112],{"class":53}," (err) {\n",[47,2114,2115,2118,2120,2123,2126,2129,2132,2135,2137],{"class":49,"line":173},[47,2116,2117],{"class":53},"      error.value ",[47,2119,601],{"class":67},[47,2121,2122],{"class":53}," err ",[47,2124,2125],{"class":67},"instanceof",[47,2127,2128],{"class":80}," Error",[47,2130,2131],{"class":67}," ?",[47,2133,2134],{"class":53}," err.message ",[47,2136,1669],{"class":67},[47,2138,2139],{"class":267}," '请求失败'\n",[47,2141,2142,2144,2147],{"class":49,"line":185},[47,2143,2107],{"class":53},[47,2145,2146],{"class":67},"finally",[47,2148,74],{"class":53},[47,2150,2151,2154,2156],{"class":49,"line":190},[47,2152,2153],{"class":53},"      loading.value ",[47,2155,601],{"class":67},[47,2157,1434],{"class":101},[47,2159,2160],{"class":49,"line":196},[47,2161,108],{"class":53},[47,2163,2164],{"class":49,"line":202},[47,2165,193],{"class":53},[47,2167,2168],{"class":49,"line":837},[47,2169,274],{"emptyLinePlaceholder":273},[47,2171,2172,2174],{"class":49,"line":843},[47,2173,817],{"class":67},[47,2175,74],{"class":53},[47,2177,2178],{"class":49,"line":848},[47,2179,2180],{"class":53},"    data,\n",[47,2182,2183],{"class":49,"line":1471},[47,2184,2185],{"class":53},"    loading,\n",[47,2187,2188],{"class":49,"line":1476},[47,2189,2190],{"class":53},"    error,\n",[47,2192,2194],{"class":49,"line":2193},25,[47,2195,2196],{"class":53},"    execute\n",[47,2198,2200],{"class":49,"line":2199},26,[47,2201,193],{"class":53},[47,2203,2205],{"class":49,"line":2204},27,[47,2206,199],{"class":53},[14,2208,2209],{},"使用：",[38,2211,2213],{"className":406,"code":2212,"language":408,"meta":43,"style":43},"import { useFetch } from '..\u002Fcomposables\u002FuseFetch'\nimport { getMockLessons } from '..\u002Fdata\u002FmockApi'\n\nconst { data, loading, error, execute } = useFetch(getMockLessons)\n\nonMounted(() => {\n  execute()\n})\n",[21,2214,2215,2227,2239,2243,2275,2279,2290,2297],{"__ignoreMap":43},[47,2216,2217,2219,2222,2224],{"class":49,"line":50},[47,2218,258],{"class":67},[47,2220,2221],{"class":53}," { useFetch } ",[47,2223,264],{"class":67},[47,2225,2226],{"class":267}," '..\u002Fcomposables\u002FuseFetch'\n",[47,2228,2229,2231,2234,2236],{"class":49,"line":64},[47,2230,258],{"class":67},[47,2232,2233],{"class":53}," { getMockLessons } ",[47,2235,264],{"class":67},[47,2237,2238],{"class":267}," '..\u002Fdata\u002FmockApi'\n",[47,2240,2241],{"class":49,"line":77},[47,2242,274],{"emptyLinePlaceholder":273},[47,2244,2245,2247,2249,2251,2253,2256,2258,2261,2263,2266,2268,2270,2272],{"class":49,"line":87},[47,2246,279],{"class":67},[47,2248,891],{"class":53},[47,2250,221],{"class":101},[47,2252,897],{"class":53},[47,2254,2255],{"class":101},"loading",[47,2257,897],{"class":53},[47,2259,2260],{"class":101},"error",[47,2262,897],{"class":53},[47,2264,2265],{"class":101},"execute",[47,2267,913],{"class":53},[47,2269,601],{"class":67},[47,2271,1970],{"class":80},[47,2273,2274],{"class":53},"(getMockLessons)\n",[47,2276,2277],{"class":49,"line":95},[47,2278,274],{"emptyLinePlaceholder":273},[47,2280,2281,2284,2286,2288],{"class":49,"line":105},[47,2282,2283],{"class":80},"onMounted",[47,2285,312],{"class":53},[47,2287,315],{"class":67},[47,2289,74],{"class":53},[47,2291,2292,2295],{"class":49,"line":111},[47,2293,2294],{"class":80},"  execute",[47,2296,1196],{"class":53},[47,2298,2299],{"class":49,"line":117},[47,2300,2301],{"class":53},"})\n",[14,2303,1506],{},[38,2305,2307],{"className":40,"code":2306,"language":42,"meta":43,"style":43},"\u003Cp v-if=\"loading\">加载中...\u003C\u002Fp>\n\u003Cp v-else-if=\"error\">{{ error }}\u003C\u002Fp>\n\u003CLessonList v-else :lessons=\"data\" \u002F>\n",[21,2308,2309,2332,2356],{"__ignoreMap":43},[47,2310,2311,2313,2315,2317,2319,2321,2323,2325,2328,2330],{"class":49,"line":50},[47,2312,54],{"class":53},[47,2314,14],{"class":57},[47,2316,1550],{"class":67},[47,2318,601],{"class":53},[47,2320,1528],{"class":267},[47,2322,2255],{"class":53},[47,2324,1528],{"class":267},[47,2326,2327],{"class":53},">加载中...\u003C\u002F",[47,2329,14],{"class":57},[47,2331,61],{"class":53},[47,2333,2334,2336,2338,2341,2343,2345,2347,2349,2352,2354],{"class":49,"line":64},[47,2335,54],{"class":53},[47,2337,14],{"class":57},[47,2339,2340],{"class":67}," v-else-if",[47,2342,601],{"class":53},[47,2344,1528],{"class":267},[47,2346,2260],{"class":53},[47,2348,1528],{"class":267},[47,2350,2351],{"class":53},">{{ error }}\u003C\u002F",[47,2353,14],{"class":57},[47,2355,61],{"class":53},[47,2357,2358,2360,2363,2366,2369,2372,2374,2376,2378,2380],{"class":49,"line":77},[47,2359,54],{"class":53},[47,2361,2362],{"class":57},"LessonList",[47,2364,2365],{"class":67}," v-else",[47,2367,2368],{"class":53}," :",[47,2370,2371],{"class":80},"lessons",[47,2373,601],{"class":53},[47,2375,1528],{"class":267},[47,2377,221],{"class":53},[47,2379,1528],{"class":267},[47,2381,2382],{"class":53}," \u002F>\n",[30,2384,2386],{"id":2385},"_10-什么时候应该抽组合式函数","10. 什么时候应该抽组合式函数",[14,2388,2389],{},"适合抽：",[214,2391,2392,2395,2398,2401],{},[217,2393,2394],{},"多个组件使用同样逻辑。",[217,2396,2397],{},"逻辑和 UI 没有强绑定。",[217,2399,2400],{},"组件太长，其中一组逻辑可以独立命名。",[217,2402,2403],{},"你能清楚描述这个函数负责什么。",[14,2405,2406],{},"例子：",[214,2408,2409,2414,2418,2422,2426,2431],{},[217,2410,2411],{},[21,2412,2413],{},"useCounter",[217,2415,2416],{},[21,2417,1303],{},[217,2419,2420],{},[21,2421,1573],{},[217,2423,2424],{},[21,2425,1939],{},[217,2427,2428],{},[21,2429,2430],{},"useDebounce",[217,2432,2433],{},[21,2434,2435],{},"useMousePosition",[14,2437,2438],{},"不适合抽：",[214,2440,2441,2444,2447],{},[217,2442,2443],{},"只用一次，而且抽出来更难读。",[217,2445,2446],{},"需要依赖很多组件内部变量。",[217,2448,2449],{},"抽出来只是为了“看起来像高级代码”。",[30,2451,2453],{"id":2452},"_11-组合式函数和普通工具函数区别","11. 组合式函数和普通工具函数区别",[14,2455,2456],{},"普通工具函数：",[38,2458,2460],{"className":406,"code":2459,"language":408,"meta":43,"style":43},"export function formatMinutes(minutes) {\n  return `${minutes} 分钟`\n}\n",[21,2461,2462,2478,2490],{"__ignoreMap":43},[47,2463,2464,2466,2468,2471,2473,2476],{"class":49,"line":50},[47,2465,68],{"class":67},[47,2467,704],{"class":67},[47,2469,2470],{"class":80}," formatMinutes",[47,2472,291],{"class":53},[47,2474,2475],{"class":712},"minutes",[47,2477,721],{"class":53},[47,2479,2480,2482,2485,2487],{"class":49,"line":64},[47,2481,817],{"class":67},[47,2483,2484],{"class":267}," `${",[47,2486,2475],{"class":53},[47,2488,2489],{"class":267},"} 分钟`\n",[47,2491,2492],{"class":49,"line":77},[47,2493,199],{"class":53},[14,2495,2496],{},"它不使用 Vue 响应式 API，只是输入一个值，返回一个值。",[14,2498,2499],{},"组合式函数：",[38,2501,2503],{"className":406,"code":2502,"language":408,"meta":43,"style":43},"export function useCounter() {\n  const count = ref(0)\n  \u002F\u002F ...\n}\n",[21,2504,2505,2515,2531,2535],{"__ignoreMap":43},[47,2506,2507,2509,2511,2513],{"class":49,"line":50},[47,2508,68],{"class":67},[47,2510,704],{"class":67},[47,2512,707],{"class":80},[47,2514,84],{"class":53},[47,2516,2517,2519,2521,2523,2525,2527,2529],{"class":49,"line":64},[47,2518,726],{"class":67},[47,2520,282],{"class":101},[47,2522,285],{"class":67},[47,2524,288],{"class":80},[47,2526,291],{"class":53},[47,2528,294],{"class":101},[47,2530,297],{"class":53},[47,2532,2533],{"class":49,"line":77},[47,2534,1056],{"class":682},[47,2536,2537],{"class":49,"line":87},[47,2538,199],{"class":53},[14,2540,2541],{},"它内部有响应式状态或生命周期，返回给组件使用。",[14,2543,2544],{},"通常：",[214,2546,2547,2556],{},[217,2548,2549,391,2552,2555],{},[21,2550,2551],{},"formatXxx",[21,2553,2554],{},"calculateXxx"," 是普通工具函数。",[217,2557,2558,2560],{},[21,2559,27],{}," 是组合式函数。",[30,2562,2564],{"id":2563},"_12-组合式函数和-pinia-的区别","12. 组合式函数和 Pinia 的区别",[14,2566,2499],{},[214,2568,2569,2572,2575],{},[217,2570,2571],{},"复用逻辑。",[217,2573,2574],{},"可以每次调用创建独立状态。",[217,2576,2577],{},"适合组件级或功能级逻辑。",[14,2579,2580],{},"Pinia：",[214,2582,2583,2586,2589],{},[217,2584,2585],{},"管理全局或跨页面共享状态。",[217,2587,2588],{},"多个组件看到同一份 store。",[217,2590,2591],{},"适合业务状态，例如登录用户、购物车、任务列表。",[14,2593,2406],{},[2595,2596,2597,2610],"table",{},[2598,2599,2600],"thead",{},[2601,2602,2603,2607],"tr",{},[2604,2605,2606],"th",{},"状态\u002F逻辑",[2604,2608,2609],{},"推荐",[2611,2612,2613,2622,2630,2638,2645],"tbody",{},[2601,2614,2615,2619],{},[2616,2617,2618],"td",{},"鼠标位置",[2616,2620,2621],{},"组合式函数",[2601,2623,2624,2627],{},[2616,2625,2626],{},"弹窗开关",[2616,2628,2629],{},"组件内或组合式函数",[2601,2631,2632,2635],{},[2616,2633,2634],{},"任务列表跨页面共享",[2616,2636,2637],{},"Pinia",[2601,2639,2640,2643],{},[2616,2641,2642],{},"本地存储同步逻辑",[2616,2644,2621],{},[2601,2646,2647,2650],{},[2616,2648,2649],{},"当前登录用户",[2616,2651,2637],{},[30,2653,2655],{"id":2654},"_13-常见错误","13. 常见错误",[2657,2658,2660],"h3",{"id":2659},"错误一解构-reactive-导致响应丢失","错误一：解构 reactive 导致响应丢失",[38,2662,2664],{"className":406,"code":2663,"language":408,"meta":43,"style":43},"const form = reactive({ title: '' })\nconst { title } = form\n",[21,2665,2666,2685],{"__ignoreMap":43},[47,2667,2668,2670,2673,2675,2678,2681,2683],{"class":49,"line":50},[47,2669,279],{"class":67},[47,2671,2672],{"class":101}," form",[47,2674,285],{"class":67},[47,2676,2677],{"class":80}," reactive",[47,2679,2680],{"class":53},"({ title: ",[47,2682,426],{"class":267},[47,2684,1929],{"class":53},[47,2686,2687,2689,2691,2694,2696,2698],{"class":49,"line":64},[47,2688,279],{"class":67},[47,2690,891],{"class":53},[47,2692,2693],{"class":101},"title",[47,2695,913],{"class":53},[47,2697,601],{"class":67},[47,2699,2700],{"class":53}," form\n",[14,2702,2703,2704,2706],{},"这样解构出来的 ",[21,2705,2693],{}," 不是响应式 ref。初学阶段避免这样写。",[14,2708,2709,2710,2713,2714,403],{},"如果需要解构，使用 ",[21,2711,2712],{},"toRefs","，或者直接使用 ",[21,2715,2716],{},"form.title",[2657,2718,2720],{"id":2719},"错误二忘记返回状态或方法","错误二：忘记返回状态或方法",[38,2722,2724],{"className":406,"code":2723,"language":408,"meta":43,"style":43},"export function useCounter() {\n  const count = ref(0)\n}\n",[21,2725,2726,2736,2752],{"__ignoreMap":43},[47,2727,2728,2730,2732,2734],{"class":49,"line":50},[47,2729,68],{"class":67},[47,2731,704],{"class":67},[47,2733,707],{"class":80},[47,2735,84],{"class":53},[47,2737,2738,2740,2742,2744,2746,2748,2750],{"class":49,"line":64},[47,2739,726],{"class":67},[47,2741,282],{"class":101},[47,2743,285],{"class":67},[47,2745,288],{"class":80},[47,2747,291],{"class":53},[47,2749,294],{"class":101},[47,2751,297],{"class":53},[47,2753,2754],{"class":49,"line":77},[47,2755,199],{"class":53},[14,2757,2758,2759,2761],{},"组件拿不到 ",[21,2760,894],{},"。需要：",[38,2763,2765],{"className":406,"code":2764,"language":408,"meta":43,"style":43},"return { count }\n",[21,2766,2767],{"__ignoreMap":43},[47,2768,2769,2772],{"class":49,"line":50},[47,2770,2771],{"class":67},"return",[47,2773,2774],{"class":53}," { count }\n",[2657,2776,2778],{"id":2777},"错误三组合式函数命名不清","错误三：组合式函数命名不清",[14,2780,2781,2784],{},[21,2782,2783],{},"useSomething"," 太模糊。更好的命名：",[214,2786,2787,2792,2796],{},[217,2788,2789],{},[21,2790,2791],{},"useTodoFilter",[217,2793,2794],{},[21,2795,1573],{},[217,2797,2798],{},[21,2799,2800],{},"useCourseProgress",[14,2802,2803],{},"名字应该告诉别人这个函数负责什么。",[30,2805,2806],{"id":2806},"本章练习",[2808,2809,2810,2825,2841,2847,2853],"ol",{},[217,2811,2812,2813,2816,2817,391,2819,391,2821,391,2823,403],{},"写 ",[21,2814,2815],{},"useCounter(initialValue)","，返回 ",[21,2818,894],{},[21,2820,900],{},[21,2822,905],{},[21,2824,910],{},[217,2826,2812,2827,2816,2830,391,2832,391,2835,391,2838,403],{},[21,2828,2829],{},"useToggle(initialValue)",[21,2831,1763],{},[21,2833,2834],{},"toggle",[21,2836,2837],{},"setTrue",[21,2839,2840],{},"setFalse",[217,2842,2812,2843,2846],{},[21,2844,2845],{},"useLocalStorage(key, defaultValue)","，保存数组状态。",[217,2848,2849,2850,2852],{},"写一个页面调用两个 ",[21,2851,1174],{},"，观察它们是否互相影响。",[217,2854,2855],{},"回答：组合式函数和 Pinia 最大区别是什么？",[2857,2858,2859],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#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 .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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":43,"searchDepth":64,"depth":64,"links":2861},[2862,2863,2864,2866,2867,2869,2870,2872,2874,2876,2877,2878,2879,2884],{"id":32,"depth":64,"text":33},{"id":364,"depth":64,"text":365},{"id":473,"depth":64,"text":2865},"3. \u003Cscript setup> 中的基本写法",{"id":664,"depth":64,"text":665},{"id":1019,"depth":64,"text":2868},"5. 为什么组合式函数需要 export",{"id":1093,"depth":64,"text":1094},{"id":1299,"depth":64,"text":2871},"7. 示例：useToggle",{"id":1569,"depth":64,"text":2873},"8. 示例：useLocalStorage",{"id":1935,"depth":64,"text":2875},"9. 示例：useFetch",{"id":2385,"depth":64,"text":2386},{"id":2452,"depth":64,"text":2453},{"id":2563,"depth":64,"text":2564},{"id":2654,"depth":64,"text":2655,"children":2880},[2881,2882,2883],{"id":2659,"depth":77,"text":2660},{"id":2719,"depth":77,"text":2720},{"id":2777,"depth":77,"text":2778},{"id":2806,"depth":64,"text":2806},"md",{"order":105},"\u002Fsource\u002Fcode\u002Fvue\u002F06-composition-composables",{"title":5,"description":16},"source\u002Fcode\u002Fvue\u002F06-composition-composables","7DjhOqqgZV1VWd-kl7xrmlW8k4KhiBaBt2UCTmblQe8",1784032889391]