[{"data":1,"prerenderedAt":2158},["ShallowReactive",2],{"note:code\u002Fvue\u002F09-http-async":3},{"id":4,"title":5,"body":6,"description":16,"extension":2152,"meta":2153,"navigation":377,"path":2154,"seo":2155,"stem":2156,"__hash__":2157},"notes\u002Fsource\u002Fcode\u002Fvue\u002F09-http-async.md","09 异步请求和错误处理",{"type":7,"value":8,"toc":2125},"minimark",[9,13,17,29,34,37,65,68,71,98,105,130,139,183,187,254,257,285,292,295,334,340,449,454,458,794,797,988,991,1005,1012,1061,1064,1081,1087,1123,1133,1137,1162,1167,1170,1228,1234,1238,1241,1245,1248,1259,1263,1356,1359,1377,1379,1387,1391,1393,1401,1404,1408,1411,1595,1598,1602,1605,1613,1616,1776,1779,1783,1786,1866,1869,1880,1883,1887,1890,1906,1909,1957,1960,1971,1975,1982,2002,2009,2030,2034,2037,2041,2044,2082,2086,2089,2092,2121],[10,11,5],"h1",{"id":12},"_09-异步请求和错误处理",[14,15,16],"p",{},"真实 Vue 项目几乎都会请求后端数据。请求不是立刻完成的，所以你必须处理异步、加载中、失败、空数据、重试等状态。",[14,18,19,20,24,25,28],{},"本章目标：你能用 ",[21,22,23],"code",{},"fetch"," 请求数据，能写 ",[21,26,27],{},"async\u002Fawait","，能处理错误，能把请求逻辑拆到合适位置。",[30,31,33],"h2",{"id":32},"_1-为什么请求是异步的","1. 为什么请求是异步的",[14,35,36],{},"当你请求接口：",[38,39,44],"pre",{"className":40,"code":41,"language":42,"meta":43,"style":43},"language-js shiki shiki-themes github-light github-dark","fetch('\u002Fapi\u002Flessons')\n","js","",[21,45,46],{"__ignoreMap":43},[47,48,51,54,58,62],"span",{"class":49,"line":50},"line",1,[47,52,23],{"class":53},"sScJk",[47,55,57],{"class":56},"sVt8B","(",[47,59,61],{"class":60},"sZZnC","'\u002Fapi\u002Flessons'",[47,63,64],{"class":56},")\n",[14,66,67],{},"浏览器要通过网络和服务器通信。这个过程可能需要几十毫秒，也可能几秒，还可能失败。",[14,69,70],{},"JavaScript 不能停在那里什么都不做，所以请求会返回 Promise：",[38,72,74],{"className":40,"code":73,"language":42,"meta":43,"style":43},"const promise = fetch('\u002Fapi\u002Flessons')\n",[21,75,76],{"__ignoreMap":43},[47,77,78,82,86,89,92,94,96],{"class":49,"line":50},[47,79,81],{"class":80},"szBVR","const",[47,83,85],{"class":84},"sj4cs"," promise",[47,87,88],{"class":80}," =",[47,90,91],{"class":53}," fetch",[47,93,57],{"class":56},[47,95,61],{"class":60},[47,97,64],{"class":56},[14,99,100,101,104],{},"用 ",[21,102,103],{},"await"," 等结果：",[38,106,108],{"className":40,"code":107,"language":42,"meta":43,"style":43},"const response = await fetch('\u002Fapi\u002Flessons')\n",[21,109,110],{"__ignoreMap":43},[47,111,112,114,117,119,122,124,126,128],{"class":49,"line":50},[47,113,81],{"class":80},[47,115,116],{"class":84}," response",[47,118,88],{"class":80},[47,120,121],{"class":80}," await",[47,123,91],{"class":53},[47,125,57],{"class":56},[47,127,61],{"class":60},[47,129,64],{"class":56},[14,131,132,134,135,138],{},[21,133,103],{}," 只能写在 ",[21,136,137],{},"async"," 函数里：",[38,140,142],{"className":40,"code":141,"language":42,"meta":43,"style":43},"async function loadLessons() {\n  const response = await fetch('\u002Fapi\u002Flessons')\n}\n",[21,143,144,157,177],{"__ignoreMap":43},[47,145,146,148,151,154],{"class":49,"line":50},[47,147,137],{"class":80},[47,149,150],{"class":80}," function",[47,152,153],{"class":53}," loadLessons",[47,155,156],{"class":56},"() {\n",[47,158,160,163,165,167,169,171,173,175],{"class":49,"line":159},2,[47,161,162],{"class":80},"  const",[47,164,116],{"class":84},[47,166,88],{"class":80},[47,168,121],{"class":80},[47,170,91],{"class":53},[47,172,57],{"class":56},[47,174,61],{"class":60},[47,176,64],{"class":56},[47,178,180],{"class":49,"line":179},3,[47,181,182],{"class":56},"}\n",[30,184,186],{"id":185},"_2-最基础的-fetch","2. 最基础的 fetch",[38,188,190],{"className":40,"code":189,"language":42,"meta":43,"style":43},"async function loadLessons() {\n  const response = await fetch('\u002Fapi\u002Flessons')\n  const lessons = await response.json()\n  return lessons\n}\n",[21,191,192,202,220,240,249],{"__ignoreMap":43},[47,193,194,196,198,200],{"class":49,"line":50},[47,195,137],{"class":80},[47,197,150],{"class":80},[47,199,153],{"class":53},[47,201,156],{"class":56},[47,203,204,206,208,210,212,214,216,218],{"class":49,"line":159},[47,205,162],{"class":80},[47,207,116],{"class":84},[47,209,88],{"class":80},[47,211,121],{"class":80},[47,213,91],{"class":53},[47,215,57],{"class":56},[47,217,61],{"class":60},[47,219,64],{"class":56},[47,221,222,224,227,229,231,234,237],{"class":49,"line":179},[47,223,162],{"class":80},[47,225,226],{"class":84}," lessons",[47,228,88],{"class":80},[47,230,121],{"class":80},[47,232,233],{"class":56}," response.",[47,235,236],{"class":53},"json",[47,238,239],{"class":56},"()\n",[47,241,243,246],{"class":49,"line":242},4,[47,244,245],{"class":80},"  return",[47,247,248],{"class":56}," lessons\n",[47,250,252],{"class":49,"line":251},5,[47,253,182],{"class":56},[14,255,256],{},"逐行解释：",[258,259,260,267,273,279],"ul",{},[261,262,263,266],"li",{},[21,264,265],{},"fetch('\u002Fapi\u002Flessons')"," 发起请求。",[261,268,269,272],{},[21,270,271],{},"response"," 是响应对象，不是最终数据。",[261,274,275,278],{},[21,276,277],{},"response.json()"," 把响应体解析成 JavaScript 数据。",[261,280,281,284],{},[21,282,283],{},"return lessons"," 把数据返回给调用者。",[30,286,288,289],{"id":287},"_3-为什么要检查-responseok","3. 为什么要检查 ",[21,290,291],{},"response.ok",[14,293,294],{},"初学者常写：",[38,296,298],{"className":40,"code":297,"language":42,"meta":43,"style":43},"const response = await fetch('\u002Fapi\u002Flessons')\nconst lessons = await response.json()\n",[21,299,300,318],{"__ignoreMap":43},[47,301,302,304,306,308,310,312,314,316],{"class":49,"line":50},[47,303,81],{"class":80},[47,305,116],{"class":84},[47,307,88],{"class":80},[47,309,121],{"class":80},[47,311,91],{"class":53},[47,313,57],{"class":56},[47,315,61],{"class":60},[47,317,64],{"class":56},[47,319,320,322,324,326,328,330,332],{"class":49,"line":159},[47,321,81],{"class":80},[47,323,226],{"class":84},[47,325,88],{"class":80},[47,327,121],{"class":80},[47,329,233],{"class":56},[47,331,236],{"class":53},[47,333,239],{"class":56},[14,335,336,337,339],{},"但如果服务器返回 404 或 500，",[21,338,23],{}," 不一定自动抛错。你应该检查：",[38,341,343],{"className":40,"code":342,"language":42,"meta":43,"style":43},"async function loadLessons() {\n  const response = await fetch('\u002Fapi\u002Flessons')\n\n  if (!response.ok) {\n    throw new Error(`请求失败：${response.status}`)\n  }\n\n  return response.json()\n}\n",[21,344,345,355,373,379,393,422,428,433,444],{"__ignoreMap":43},[47,346,347,349,351,353],{"class":49,"line":50},[47,348,137],{"class":80},[47,350,150],{"class":80},[47,352,153],{"class":53},[47,354,156],{"class":56},[47,356,357,359,361,363,365,367,369,371],{"class":49,"line":159},[47,358,162],{"class":80},[47,360,116],{"class":84},[47,362,88],{"class":80},[47,364,121],{"class":80},[47,366,91],{"class":53},[47,368,57],{"class":56},[47,370,61],{"class":60},[47,372,64],{"class":56},[47,374,375],{"class":49,"line":179},[47,376,378],{"emptyLinePlaceholder":377},true,"\n",[47,380,381,384,387,390],{"class":49,"line":242},[47,382,383],{"class":80},"  if",[47,385,386],{"class":56}," (",[47,388,389],{"class":80},"!",[47,391,392],{"class":56},"response.ok) {\n",[47,394,395,398,401,404,406,409,411,414,417,420],{"class":49,"line":251},[47,396,397],{"class":80},"    throw",[47,399,400],{"class":80}," new",[47,402,403],{"class":53}," Error",[47,405,57],{"class":56},[47,407,408],{"class":60},"`请求失败：${",[47,410,271],{"class":56},[47,412,413],{"class":60},".",[47,415,416],{"class":56},"status",[47,418,419],{"class":60},"}`",[47,421,64],{"class":56},[47,423,425],{"class":49,"line":424},6,[47,426,427],{"class":56},"  }\n",[47,429,431],{"class":49,"line":430},7,[47,432,378],{"emptyLinePlaceholder":377},[47,434,436,438,440,442],{"class":49,"line":435},8,[47,437,245],{"class":80},[47,439,233],{"class":56},[47,441,236],{"class":53},[47,443,239],{"class":56},[47,445,447],{"class":49,"line":446},9,[47,448,182],{"class":56},[14,450,451,453],{},[21,452,291],{}," 表示 HTTP 状态码是否在 200 到 299。",[30,455,457],{"id":456},"_4-组件中的请求状态","4. 组件中的请求状态",[38,459,463],{"className":460,"code":461,"language":462,"meta":43,"style":43},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup>\nimport { onMounted, ref } from 'vue'\n\nconst lessons = ref([])\nconst loading = ref(false)\nconst error = ref('')\n\nasync function fetchLessons() {\n  loading.value = true\n  error.value = ''\n\n  try {\n    const response = await fetch('\u002Fapi\u002Flessons')\n\n    if (!response.ok) {\n      throw new Error('课程加载失败')\n    }\n\n    lessons.value = await response.json()\n  } catch (err) {\n    error.value = err instanceof Error ? err.message : '未知错误'\n  } finally {\n    loading.value = false\n  }\n}\n\nonMounted(() => {\n  fetchLessons()\n})\n\u003C\u002Fscript>\n","vue",[21,464,465,480,494,498,512,530,548,552,563,574,585,590,599,619,624,636,653,659,664,680,692,720,730,741,746,751,756,770,778,784],{"__ignoreMap":43},[47,466,467,470,474,477],{"class":49,"line":50},[47,468,469],{"class":56},"\u003C",[47,471,473],{"class":472},"s9eBZ","script",[47,475,476],{"class":53}," setup",[47,478,479],{"class":56},">\n",[47,481,482,485,488,491],{"class":49,"line":159},[47,483,484],{"class":80},"import",[47,486,487],{"class":56}," { onMounted, ref } ",[47,489,490],{"class":80},"from",[47,492,493],{"class":60}," 'vue'\n",[47,495,496],{"class":49,"line":179},[47,497,378],{"emptyLinePlaceholder":377},[47,499,500,502,504,506,509],{"class":49,"line":242},[47,501,81],{"class":80},[47,503,226],{"class":84},[47,505,88],{"class":80},[47,507,508],{"class":53}," ref",[47,510,511],{"class":56},"([])\n",[47,513,514,516,519,521,523,525,528],{"class":49,"line":251},[47,515,81],{"class":80},[47,517,518],{"class":84}," loading",[47,520,88],{"class":80},[47,522,508],{"class":53},[47,524,57],{"class":56},[47,526,527],{"class":84},"false",[47,529,64],{"class":56},[47,531,532,534,537,539,541,543,546],{"class":49,"line":424},[47,533,81],{"class":80},[47,535,536],{"class":84}," error",[47,538,88],{"class":80},[47,540,508],{"class":53},[47,542,57],{"class":56},[47,544,545],{"class":60},"''",[47,547,64],{"class":56},[47,549,550],{"class":49,"line":430},[47,551,378],{"emptyLinePlaceholder":377},[47,553,554,556,558,561],{"class":49,"line":435},[47,555,137],{"class":80},[47,557,150],{"class":80},[47,559,560],{"class":53}," fetchLessons",[47,562,156],{"class":56},[47,564,565,568,571],{"class":49,"line":446},[47,566,567],{"class":56},"  loading.value ",[47,569,570],{"class":80},"=",[47,572,573],{"class":84}," true\n",[47,575,577,580,582],{"class":49,"line":576},10,[47,578,579],{"class":56},"  error.value ",[47,581,570],{"class":80},[47,583,584],{"class":60}," ''\n",[47,586,588],{"class":49,"line":587},11,[47,589,378],{"emptyLinePlaceholder":377},[47,591,593,596],{"class":49,"line":592},12,[47,594,595],{"class":80},"  try",[47,597,598],{"class":56}," {\n",[47,600,602,605,607,609,611,613,615,617],{"class":49,"line":601},13,[47,603,604],{"class":80},"    const",[47,606,116],{"class":84},[47,608,88],{"class":80},[47,610,121],{"class":80},[47,612,91],{"class":53},[47,614,57],{"class":56},[47,616,61],{"class":60},[47,618,64],{"class":56},[47,620,622],{"class":49,"line":621},14,[47,623,378],{"emptyLinePlaceholder":377},[47,625,627,630,632,634],{"class":49,"line":626},15,[47,628,629],{"class":80},"    if",[47,631,386],{"class":56},[47,633,389],{"class":80},[47,635,392],{"class":56},[47,637,639,642,644,646,648,651],{"class":49,"line":638},16,[47,640,641],{"class":80},"      throw",[47,643,400],{"class":80},[47,645,403],{"class":53},[47,647,57],{"class":56},[47,649,650],{"class":60},"'课程加载失败'",[47,652,64],{"class":56},[47,654,656],{"class":49,"line":655},17,[47,657,658],{"class":56},"    }\n",[47,660,662],{"class":49,"line":661},18,[47,663,378],{"emptyLinePlaceholder":377},[47,665,667,670,672,674,676,678],{"class":49,"line":666},19,[47,668,669],{"class":56},"    lessons.value ",[47,671,570],{"class":80},[47,673,121],{"class":80},[47,675,233],{"class":56},[47,677,236],{"class":53},[47,679,239],{"class":56},[47,681,683,686,689],{"class":49,"line":682},20,[47,684,685],{"class":56},"  } ",[47,687,688],{"class":80},"catch",[47,690,691],{"class":56}," (err) {\n",[47,693,695,698,700,703,706,708,711,714,717],{"class":49,"line":694},21,[47,696,697],{"class":56},"    error.value ",[47,699,570],{"class":80},[47,701,702],{"class":56}," err ",[47,704,705],{"class":80},"instanceof",[47,707,403],{"class":53},[47,709,710],{"class":80}," ?",[47,712,713],{"class":56}," err.message ",[47,715,716],{"class":80},":",[47,718,719],{"class":60}," '未知错误'\n",[47,721,723,725,728],{"class":49,"line":722},22,[47,724,685],{"class":56},[47,726,727],{"class":80},"finally",[47,729,598],{"class":56},[47,731,733,736,738],{"class":49,"line":732},23,[47,734,735],{"class":56},"    loading.value ",[47,737,570],{"class":80},[47,739,740],{"class":84}," false\n",[47,742,744],{"class":49,"line":743},24,[47,745,427],{"class":56},[47,747,749],{"class":49,"line":748},25,[47,750,182],{"class":56},[47,752,754],{"class":49,"line":753},26,[47,755,378],{"emptyLinePlaceholder":377},[47,757,759,762,765,768],{"class":49,"line":758},27,[47,760,761],{"class":53},"onMounted",[47,763,764],{"class":56},"(() ",[47,766,767],{"class":80},"=>",[47,769,598],{"class":56},[47,771,773,776],{"class":49,"line":772},28,[47,774,775],{"class":53},"  fetchLessons",[47,777,239],{"class":56},[47,779,781],{"class":49,"line":780},29,[47,782,783],{"class":56},"})\n",[47,785,787,790,792],{"class":49,"line":786},30,[47,788,789],{"class":56},"\u003C\u002F",[47,791,473],{"class":472},[47,793,479],{"class":56},[14,795,796],{},"模板：",[38,798,800],{"className":460,"code":799,"language":462,"meta":43,"style":43},"\u003Ctemplate>\n  \u003Cp v-if=\"loading\">加载中...\u003C\u002Fp>\n  \u003Cdiv v-else-if=\"error\" role=\"alert\">\n    \u003Cp>{{ error }}\u003C\u002Fp>\n    \u003Cbutton @click=\"fetchLessons\">重试\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n  \u003Cp v-else-if=\"lessons.length === 0\">暂无课程\u003C\u002Fp>\n  \u003Cul v-else>\n    \u003Cli v-for=\"lesson in lessons\" :key=\"lesson.id\">\n      {{ lesson.title }}\n    \u003C\u002Fli>\n  \u003C\u002Ful>\n\u003C\u002Ftemplate>\n",[21,801,802,811,833,858,872,894,903,923,934,958,963,972,980],{"__ignoreMap":43},[47,803,804,806,809],{"class":49,"line":50},[47,805,469],{"class":56},[47,807,808],{"class":472},"template",[47,810,479],{"class":56},[47,812,813,816,818,821,823,826,829,831],{"class":49,"line":159},[47,814,815],{"class":56},"  \u003C",[47,817,14],{"class":472},[47,819,820],{"class":53}," v-if",[47,822,570],{"class":56},[47,824,825],{"class":60},"\"loading\"",[47,827,828],{"class":56},">加载中...\u003C\u002F",[47,830,14],{"class":472},[47,832,479],{"class":56},[47,834,835,837,840,843,845,848,851,853,856],{"class":49,"line":179},[47,836,815],{"class":56},[47,838,839],{"class":472},"div",[47,841,842],{"class":53}," v-else-if",[47,844,570],{"class":56},[47,846,847],{"class":60},"\"error\"",[47,849,850],{"class":53}," role",[47,852,570],{"class":56},[47,854,855],{"class":60},"\"alert\"",[47,857,479],{"class":56},[47,859,860,863,865,868,870],{"class":49,"line":242},[47,861,862],{"class":56},"    \u003C",[47,864,14],{"class":472},[47,866,867],{"class":56},">{{ error }}\u003C\u002F",[47,869,14],{"class":472},[47,871,479],{"class":56},[47,873,874,876,879,882,884,887,890,892],{"class":49,"line":251},[47,875,862],{"class":56},[47,877,878],{"class":472},"button",[47,880,881],{"class":53}," @click",[47,883,570],{"class":56},[47,885,886],{"class":60},"\"fetchLessons\"",[47,888,889],{"class":56},">重试\u003C\u002F",[47,891,878],{"class":472},[47,893,479],{"class":56},[47,895,896,899,901],{"class":49,"line":424},[47,897,898],{"class":56},"  \u003C\u002F",[47,900,839],{"class":472},[47,902,479],{"class":56},[47,904,905,907,909,911,913,916,919,921],{"class":49,"line":430},[47,906,815],{"class":56},[47,908,14],{"class":472},[47,910,842],{"class":53},[47,912,570],{"class":56},[47,914,915],{"class":60},"\"lessons.length === 0\"",[47,917,918],{"class":56},">暂无课程\u003C\u002F",[47,920,14],{"class":472},[47,922,479],{"class":56},[47,924,925,927,929,932],{"class":49,"line":435},[47,926,815],{"class":56},[47,928,258],{"class":472},[47,930,931],{"class":53}," v-else",[47,933,479],{"class":56},[47,935,936,938,940,943,945,948,951,953,956],{"class":49,"line":446},[47,937,862],{"class":56},[47,939,261],{"class":472},[47,941,942],{"class":53}," v-for",[47,944,570],{"class":56},[47,946,947],{"class":60},"\"lesson in lessons\"",[47,949,950],{"class":53}," :key",[47,952,570],{"class":56},[47,954,955],{"class":60},"\"lesson.id\"",[47,957,479],{"class":56},[47,959,960],{"class":49,"line":576},[47,961,962],{"class":56},"      {{ lesson.title }}\n",[47,964,965,968,970],{"class":49,"line":587},[47,966,967],{"class":56},"    \u003C\u002F",[47,969,261],{"class":472},[47,971,479],{"class":56},[47,973,974,976,978],{"class":49,"line":592},[47,975,898],{"class":56},[47,977,258],{"class":472},[47,979,479],{"class":56},[47,981,982,984,986],{"class":49,"line":601},[47,983,789],{"class":56},[47,985,808],{"class":472},[47,987,479],{"class":56},[14,989,990],{},"这才是完整请求界面：",[258,992,993,996,999,1002],{},[261,994,995],{},"加载中。",[261,997,998],{},"失败。",[261,1000,1001],{},"空数据。",[261,1003,1004],{},"成功。",[30,1006,1008,1009],{"id":1007},"_5-trycatchfinally","5. ",[21,1010,1011],{},"try\u002Fcatch\u002Ffinally",[38,1013,1015],{"className":40,"code":1014,"language":42,"meta":43,"style":43},"try {\n  \u002F\u002F 可能失败的代码\n} catch (err) {\n  \u002F\u002F 失败后执行\n} finally {\n  \u002F\u002F 无论成功失败都执行\n}\n",[21,1016,1017,1024,1030,1039,1044,1052,1057],{"__ignoreMap":43},[47,1018,1019,1022],{"class":49,"line":50},[47,1020,1021],{"class":80},"try",[47,1023,598],{"class":56},[47,1025,1026],{"class":49,"line":159},[47,1027,1029],{"class":1028},"sJ8bj","  \u002F\u002F 可能失败的代码\n",[47,1031,1032,1035,1037],{"class":49,"line":179},[47,1033,1034],{"class":56},"} ",[47,1036,688],{"class":80},[47,1038,691],{"class":56},[47,1040,1041],{"class":49,"line":242},[47,1042,1043],{"class":1028},"  \u002F\u002F 失败后执行\n",[47,1045,1046,1048,1050],{"class":49,"line":251},[47,1047,1034],{"class":56},[47,1049,727],{"class":80},[47,1051,598],{"class":56},[47,1053,1054],{"class":49,"line":424},[47,1055,1056],{"class":1028},"  \u002F\u002F 无论成功失败都执行\n",[47,1058,1059],{"class":49,"line":430},[47,1060,182],{"class":56},[14,1062,1063],{},"在请求里：",[258,1065,1066,1071,1076],{},[261,1067,1068,1070],{},[21,1069,1021],{},"：发请求、解析数据。",[261,1072,1073,1075],{},[21,1074,688],{},"：保存错误信息。",[261,1077,1078,1080],{},[21,1079,727],{},"：关闭 loading。",[14,1082,1083,1084,1086],{},"如果只在 ",[21,1085,1021],{}," 最后关闭 loading：",[38,1088,1090],{"className":40,"code":1089,"language":42,"meta":43,"style":43},"try {\n  loading.value = true\n  \u002F\u002F 如果这里失败\n  loading.value = false\n}\n",[21,1091,1092,1098,1106,1111,1119],{"__ignoreMap":43},[47,1093,1094,1096],{"class":49,"line":50},[47,1095,1021],{"class":80},[47,1097,598],{"class":56},[47,1099,1100,1102,1104],{"class":49,"line":159},[47,1101,567],{"class":56},[47,1103,570],{"class":80},[47,1105,573],{"class":84},[47,1107,1108],{"class":49,"line":179},[47,1109,1110],{"class":1028},"  \u002F\u002F 如果这里失败\n",[47,1112,1113,1115,1117],{"class":49,"line":242},[47,1114,567],{"class":56},[47,1116,570],{"class":80},[47,1118,740],{"class":84},[47,1120,1121],{"class":49,"line":251},[47,1122,182],{"class":56},[14,1124,1125,1126,1129,1130,1132],{},"一旦中途抛错，",[21,1127,1128],{},"loading.value = false"," 不会执行，页面可能一直显示加载中。所以用 ",[21,1131,727],{},"。",[30,1134,1136],{"id":1135},"_6-onmounted-请求","6. onMounted 请求",[38,1138,1140],{"className":40,"code":1139,"language":42,"meta":43,"style":43},"onMounted(() => {\n  fetchLessons()\n})\n",[21,1141,1142,1152,1158],{"__ignoreMap":43},[47,1143,1144,1146,1148,1150],{"class":49,"line":50},[47,1145,761],{"class":53},[47,1147,764],{"class":56},[47,1149,767],{"class":80},[47,1151,598],{"class":56},[47,1153,1154,1156],{"class":49,"line":159},[47,1155,775],{"class":53},[47,1157,239],{"class":56},[47,1159,1160],{"class":49,"line":179},[47,1161,783],{"class":56},[14,1163,1164,1166],{},[21,1165,761],{}," 表示组件已经挂载到页面后执行。进入页面加载数据时常用。",[14,1168,1169],{},"但如果请求依赖路由参数，参数变化时也要重新请求：",[38,1171,1173],{"className":40,"code":1172,"language":42,"meta":43,"style":43},"watch(\n  () => route.params.id,\n  () => {\n    fetchLesson()\n  },\n  { immediate: true }\n)\n",[21,1174,1175,1183,1193,1201,1208,1213,1224],{"__ignoreMap":43},[47,1176,1177,1180],{"class":49,"line":50},[47,1178,1179],{"class":53},"watch",[47,1181,1182],{"class":56},"(\n",[47,1184,1185,1188,1190],{"class":49,"line":159},[47,1186,1187],{"class":56},"  () ",[47,1189,767],{"class":80},[47,1191,1192],{"class":56}," route.params.id,\n",[47,1194,1195,1197,1199],{"class":49,"line":179},[47,1196,1187],{"class":56},[47,1198,767],{"class":80},[47,1200,598],{"class":56},[47,1202,1203,1206],{"class":49,"line":242},[47,1204,1205],{"class":53},"    fetchLesson",[47,1207,239],{"class":56},[47,1209,1210],{"class":49,"line":251},[47,1211,1212],{"class":56},"  },\n",[47,1214,1215,1218,1221],{"class":49,"line":424},[47,1216,1217],{"class":56},"  { immediate: ",[47,1219,1220],{"class":84},"true",[47,1222,1223],{"class":56}," }\n",[47,1225,1226],{"class":49,"line":430},[47,1227,64],{"class":56},[14,1229,1230,1233],{},[21,1231,1232],{},"immediate: true"," 表示一开始也执行一次。",[30,1235,1237],{"id":1236},"_7-请求逻辑放在哪里","7. 请求逻辑放在哪里",[14,1239,1240],{},"三种常见位置：",[1242,1243,1244],"h3",{"id":1244},"放组件里",[14,1246,1247],{},"适合：",[258,1249,1250,1253,1256],{},[261,1251,1252],{},"只有当前页面用。",[261,1254,1255],{},"逻辑简单。",[261,1257,1258],{},"不需要多个组件共享。",[1242,1260,1262],{"id":1261},"放-service-里","放 service 里",[38,1264,1266],{"className":40,"code":1265,"language":42,"meta":43,"style":43},"\u002F\u002F services\u002FlessonService.js\nexport async function getLessons() {\n  const response = await fetch('\u002Fapi\u002Flessons')\n\n  if (!response.ok) {\n    throw new Error('课程加载失败')\n  }\n\n  return response.json()\n}\n",[21,1267,1268,1273,1288,1306,1310,1320,1334,1338,1342,1352],{"__ignoreMap":43},[47,1269,1270],{"class":49,"line":50},[47,1271,1272],{"class":1028},"\u002F\u002F services\u002FlessonService.js\n",[47,1274,1275,1278,1281,1283,1286],{"class":49,"line":159},[47,1276,1277],{"class":80},"export",[47,1279,1280],{"class":80}," async",[47,1282,150],{"class":80},[47,1284,1285],{"class":53}," getLessons",[47,1287,156],{"class":56},[47,1289,1290,1292,1294,1296,1298,1300,1302,1304],{"class":49,"line":179},[47,1291,162],{"class":80},[47,1293,116],{"class":84},[47,1295,88],{"class":80},[47,1297,121],{"class":80},[47,1299,91],{"class":53},[47,1301,57],{"class":56},[47,1303,61],{"class":60},[47,1305,64],{"class":56},[47,1307,1308],{"class":49,"line":242},[47,1309,378],{"emptyLinePlaceholder":377},[47,1311,1312,1314,1316,1318],{"class":49,"line":251},[47,1313,383],{"class":80},[47,1315,386],{"class":56},[47,1317,389],{"class":80},[47,1319,392],{"class":56},[47,1321,1322,1324,1326,1328,1330,1332],{"class":49,"line":424},[47,1323,397],{"class":80},[47,1325,400],{"class":80},[47,1327,403],{"class":53},[47,1329,57],{"class":56},[47,1331,650],{"class":60},[47,1333,64],{"class":56},[47,1335,1336],{"class":49,"line":430},[47,1337,427],{"class":56},[47,1339,1340],{"class":49,"line":435},[47,1341,378],{"emptyLinePlaceholder":377},[47,1343,1344,1346,1348,1350],{"class":49,"line":446},[47,1345,245],{"class":80},[47,1347,233],{"class":56},[47,1349,236],{"class":53},[47,1351,239],{"class":56},[47,1353,1354],{"class":49,"line":576},[47,1355,182],{"class":56},[14,1357,1358],{},"组件：",[38,1360,1362],{"className":40,"code":1361,"language":42,"meta":43,"style":43},"lessons.value = await getLessons()\n",[21,1363,1364],{"__ignoreMap":43},[47,1365,1366,1369,1371,1373,1375],{"class":49,"line":50},[47,1367,1368],{"class":56},"lessons.value ",[47,1370,570],{"class":80},[47,1372,121],{"class":80},[47,1374,1285],{"class":53},[47,1376,239],{"class":56},[14,1378,1247],{},[258,1380,1381,1384],{},[261,1382,1383],{},"多处复用同一个接口。",[261,1385,1386],{},"想把 URL、请求头、错误处理集中。",[1242,1388,1390],{"id":1389},"放-pinia-action-里","放 Pinia action 里",[14,1392,1247],{},[258,1394,1395,1398],{},[261,1396,1397],{},"请求结果是全局共享业务状态。",[261,1399,1400],{},"多个组件都要使用同一份数据。",[14,1402,1403],{},"例如登录用户、任务列表。",[30,1405,1407],{"id":1406},"_8-模拟-api","8. 模拟 API",[14,1409,1410],{},"学习阶段可以不接真实后端，用 mock API：",[38,1412,1414],{"className":40,"code":1413,"language":42,"meta":43,"style":43},"export function wait(ms) {\n  return new Promise((resolve) => {\n    setTimeout(resolve, ms)\n  })\n}\n\nexport async function getMockLessons(shouldFail = false) {\n  await wait(500)\n\n  if (shouldFail) {\n    throw new Error('模拟请求失败')\n  }\n\n  return [\n    { id: 1, title: '响应式基础' },\n    { id: 2, title: '组件通信' }\n  ]\n}\n",[21,1415,1416,1434,1456,1464,1469,1473,1477,1500,1514,1518,1525,1540,1544,1548,1555,1572,1586,1591],{"__ignoreMap":43},[47,1417,1418,1420,1422,1425,1427,1431],{"class":49,"line":50},[47,1419,1277],{"class":80},[47,1421,150],{"class":80},[47,1423,1424],{"class":53}," wait",[47,1426,57],{"class":56},[47,1428,1430],{"class":1429},"s4XuR","ms",[47,1432,1433],{"class":56},") {\n",[47,1435,1436,1438,1440,1443,1446,1449,1452,1454],{"class":49,"line":159},[47,1437,245],{"class":80},[47,1439,400],{"class":80},[47,1441,1442],{"class":84}," Promise",[47,1444,1445],{"class":56},"((",[47,1447,1448],{"class":1429},"resolve",[47,1450,1451],{"class":56},") ",[47,1453,767],{"class":80},[47,1455,598],{"class":56},[47,1457,1458,1461],{"class":49,"line":179},[47,1459,1460],{"class":53},"    setTimeout",[47,1462,1463],{"class":56},"(resolve, ms)\n",[47,1465,1466],{"class":49,"line":242},[47,1467,1468],{"class":56},"  })\n",[47,1470,1471],{"class":49,"line":251},[47,1472,182],{"class":56},[47,1474,1475],{"class":49,"line":424},[47,1476,378],{"emptyLinePlaceholder":377},[47,1478,1479,1481,1483,1485,1488,1490,1493,1495,1498],{"class":49,"line":430},[47,1480,1277],{"class":80},[47,1482,1280],{"class":80},[47,1484,150],{"class":80},[47,1486,1487],{"class":53}," getMockLessons",[47,1489,57],{"class":56},[47,1491,1492],{"class":1429},"shouldFail",[47,1494,88],{"class":80},[47,1496,1497],{"class":84}," false",[47,1499,1433],{"class":56},[47,1501,1502,1505,1507,1509,1512],{"class":49,"line":435},[47,1503,1504],{"class":80},"  await",[47,1506,1424],{"class":53},[47,1508,57],{"class":56},[47,1510,1511],{"class":84},"500",[47,1513,64],{"class":56},[47,1515,1516],{"class":49,"line":446},[47,1517,378],{"emptyLinePlaceholder":377},[47,1519,1520,1522],{"class":49,"line":576},[47,1521,383],{"class":80},[47,1523,1524],{"class":56}," (shouldFail) {\n",[47,1526,1527,1529,1531,1533,1535,1538],{"class":49,"line":587},[47,1528,397],{"class":80},[47,1530,400],{"class":80},[47,1532,403],{"class":53},[47,1534,57],{"class":56},[47,1536,1537],{"class":60},"'模拟请求失败'",[47,1539,64],{"class":56},[47,1541,1542],{"class":49,"line":592},[47,1543,427],{"class":56},[47,1545,1546],{"class":49,"line":601},[47,1547,378],{"emptyLinePlaceholder":377},[47,1549,1550,1552],{"class":49,"line":621},[47,1551,245],{"class":80},[47,1553,1554],{"class":56}," [\n",[47,1556,1557,1560,1563,1566,1569],{"class":49,"line":626},[47,1558,1559],{"class":56},"    { id: ",[47,1561,1562],{"class":84},"1",[47,1564,1565],{"class":56},", title: ",[47,1567,1568],{"class":60},"'响应式基础'",[47,1570,1571],{"class":56}," },\n",[47,1573,1574,1576,1579,1581,1584],{"class":49,"line":638},[47,1575,1559],{"class":56},[47,1577,1578],{"class":84},"2",[47,1580,1565],{"class":56},[47,1582,1583],{"class":60},"'组件通信'",[47,1585,1223],{"class":56},[47,1587,1588],{"class":49,"line":655},[47,1589,1590],{"class":56},"  ]\n",[47,1592,1593],{"class":49,"line":661},[47,1594,182],{"class":56},[14,1596,1597],{},"这样你可以练习 loading 和 error，而不依赖后端。",[30,1599,1601],{"id":1600},"_9-取消请求-abortcontroller","9. 取消请求 AbortController",[14,1603,1604],{},"搜索框场景：",[38,1606,1611],{"className":1607,"code":1609,"language":1610,"meta":43},[1608],"language-text","用户输入 v -> 发请求 1\n用户输入 vu -> 发请求 2\n用户输入 vue -> 发请求 3\n","text",[21,1612,1609],{"__ignoreMap":43},[14,1614,1615],{},"如果请求 1 最后才返回，可能覆盖请求 3 的结果。可以取消旧请求：",[38,1617,1619],{"className":40,"code":1618,"language":42,"meta":43,"style":43},"let controller\n\nasync function search(keyword) {\n  if (controller) {\n    controller.abort()\n  }\n\n  controller = new AbortController()\n\n  const response = await fetch(`\u002Fapi\u002Fsearch?q=${keyword}`, {\n    signal: controller.signal\n  })\n\n  if (!response.ok) {\n    throw new Error('搜索失败')\n  }\n\n  return response.json()\n}\n",[21,1620,1621,1629,1633,1649,1656,1666,1670,1674,1688,1692,1716,1721,1725,1729,1739,1754,1758,1762,1772],{"__ignoreMap":43},[47,1622,1623,1626],{"class":49,"line":50},[47,1624,1625],{"class":80},"let",[47,1627,1628],{"class":56}," controller\n",[47,1630,1631],{"class":49,"line":159},[47,1632,378],{"emptyLinePlaceholder":377},[47,1634,1635,1637,1639,1642,1644,1647],{"class":49,"line":179},[47,1636,137],{"class":80},[47,1638,150],{"class":80},[47,1640,1641],{"class":53}," search",[47,1643,57],{"class":56},[47,1645,1646],{"class":1429},"keyword",[47,1648,1433],{"class":56},[47,1650,1651,1653],{"class":49,"line":242},[47,1652,383],{"class":80},[47,1654,1655],{"class":56}," (controller) {\n",[47,1657,1658,1661,1664],{"class":49,"line":251},[47,1659,1660],{"class":56},"    controller.",[47,1662,1663],{"class":53},"abort",[47,1665,239],{"class":56},[47,1667,1668],{"class":49,"line":424},[47,1669,427],{"class":56},[47,1671,1672],{"class":49,"line":430},[47,1673,378],{"emptyLinePlaceholder":377},[47,1675,1676,1679,1681,1683,1686],{"class":49,"line":435},[47,1677,1678],{"class":56},"  controller ",[47,1680,570],{"class":80},[47,1682,400],{"class":80},[47,1684,1685],{"class":53}," AbortController",[47,1687,239],{"class":56},[47,1689,1690],{"class":49,"line":446},[47,1691,378],{"emptyLinePlaceholder":377},[47,1693,1694,1696,1698,1700,1702,1704,1706,1709,1711,1713],{"class":49,"line":576},[47,1695,162],{"class":80},[47,1697,116],{"class":84},[47,1699,88],{"class":80},[47,1701,121],{"class":80},[47,1703,91],{"class":53},[47,1705,57],{"class":56},[47,1707,1708],{"class":60},"`\u002Fapi\u002Fsearch?q=${",[47,1710,1646],{"class":56},[47,1712,419],{"class":60},[47,1714,1715],{"class":56},", {\n",[47,1717,1718],{"class":49,"line":587},[47,1719,1720],{"class":56},"    signal: controller.signal\n",[47,1722,1723],{"class":49,"line":592},[47,1724,1468],{"class":56},[47,1726,1727],{"class":49,"line":601},[47,1728,378],{"emptyLinePlaceholder":377},[47,1730,1731,1733,1735,1737],{"class":49,"line":621},[47,1732,383],{"class":80},[47,1734,386],{"class":56},[47,1736,389],{"class":80},[47,1738,392],{"class":56},[47,1740,1741,1743,1745,1747,1749,1752],{"class":49,"line":626},[47,1742,397],{"class":80},[47,1744,400],{"class":80},[47,1746,403],{"class":53},[47,1748,57],{"class":56},[47,1750,1751],{"class":60},"'搜索失败'",[47,1753,64],{"class":56},[47,1755,1756],{"class":49,"line":638},[47,1757,427],{"class":56},[47,1759,1760],{"class":49,"line":655},[47,1761,378],{"emptyLinePlaceholder":377},[47,1763,1764,1766,1768,1770],{"class":49,"line":661},[47,1765,245],{"class":80},[47,1767,233],{"class":56},[47,1769,236],{"class":53},[47,1771,239],{"class":56},[47,1773,1774],{"class":49,"line":666},[47,1775,182],{"class":56},[14,1777,1778],{},"如果组件卸载，也可以取消正在进行的请求，避免无意义更新。",[30,1780,1782],{"id":1781},"_10-防抖-debounce","10. 防抖 debounce",[14,1784,1785],{},"搜索时不应该每输入一个字都请求，可以等待用户停顿：",[38,1787,1789],{"className":40,"code":1788,"language":42,"meta":43,"style":43},"let timerId\n\nfunction debounceSearch(keyword) {\n  clearTimeout(timerId)\n\n  timerId = setTimeout(() => {\n    search(keyword)\n  }, 300)\n}\n",[21,1790,1791,1798,1802,1816,1824,1828,1844,1852,1862],{"__ignoreMap":43},[47,1792,1793,1795],{"class":49,"line":50},[47,1794,1625],{"class":80},[47,1796,1797],{"class":56}," timerId\n",[47,1799,1800],{"class":49,"line":159},[47,1801,378],{"emptyLinePlaceholder":377},[47,1803,1804,1807,1810,1812,1814],{"class":49,"line":179},[47,1805,1806],{"class":80},"function",[47,1808,1809],{"class":53}," debounceSearch",[47,1811,57],{"class":56},[47,1813,1646],{"class":1429},[47,1815,1433],{"class":56},[47,1817,1818,1821],{"class":49,"line":242},[47,1819,1820],{"class":53},"  clearTimeout",[47,1822,1823],{"class":56},"(timerId)\n",[47,1825,1826],{"class":49,"line":251},[47,1827,378],{"emptyLinePlaceholder":377},[47,1829,1830,1833,1835,1838,1840,1842],{"class":49,"line":424},[47,1831,1832],{"class":56},"  timerId ",[47,1834,570],{"class":80},[47,1836,1837],{"class":53}," setTimeout",[47,1839,764],{"class":56},[47,1841,767],{"class":80},[47,1843,598],{"class":56},[47,1845,1846,1849],{"class":49,"line":430},[47,1847,1848],{"class":53},"    search",[47,1850,1851],{"class":56},"(keyword)\n",[47,1853,1854,1857,1860],{"class":49,"line":435},[47,1855,1856],{"class":56},"  }, ",[47,1858,1859],{"class":84},"300",[47,1861,64],{"class":56},[47,1863,1864],{"class":49,"line":446},[47,1865,182],{"class":56},[14,1867,1868],{},"意思：",[258,1870,1871,1874,1877],{},[261,1872,1873],{},"每次输入先清除上一个定时器。",[261,1875,1876],{},"重新等待 300ms。",[261,1878,1879],{},"300ms 内没有新输入，才执行搜索。",[14,1881,1882],{},"真实项目可以使用成熟工具库，但理解原理很重要。",[30,1884,1886],{"id":1885},"_11-错误信息应该给用户看","11. 错误信息应该给用户看",[14,1888,1889],{},"不要只写：",[38,1891,1893],{"className":40,"code":1892,"language":42,"meta":43,"style":43},"console.error(err)\n",[21,1894,1895],{"__ignoreMap":43},[47,1896,1897,1900,1903],{"class":49,"line":50},[47,1898,1899],{"class":56},"console.",[47,1901,1902],{"class":53},"error",[47,1904,1905],{"class":56},"(err)\n",[14,1907,1908],{},"用户看不到控制台。页面应该显示：",[38,1910,1912],{"className":460,"code":1911,"language":462,"meta":43,"style":43},"\u003Cdiv v-if=\"error\" role=\"alert\">\n  \u003Cp>{{ error }}\u003C\u002Fp>\n  \u003Cbutton @click=\"fetchLessons\">重试\u003C\u002Fbutton>\n\u003C\u002Fdiv>\n",[21,1913,1914,1939,1944,1949],{"__ignoreMap":43},[47,1915,1916,1918,1920,1922,1924,1927,1929,1931,1933,1935,1937],{"class":49,"line":50},[47,1917,469],{"class":56},[47,1919,839],{"class":472},[47,1921,820],{"class":80},[47,1923,570],{"class":56},[47,1925,1926],{"class":60},"\"",[47,1928,1902],{"class":56},[47,1930,1926],{"class":60},[47,1932,850],{"class":53},[47,1934,570],{"class":56},[47,1936,855],{"class":60},[47,1938,479],{"class":56},[47,1940,1941],{"class":49,"line":159},[47,1942,1943],{"class":56},"  \u003Cp>{{ error }}\u003C\u002Fp>\n",[47,1945,1946],{"class":49,"line":179},[47,1947,1948],{"class":56},"  \u003Cbutton @click=\"fetchLessons\">重试\u003C\u002Fbutton>\n",[47,1950,1951,1953,1955],{"class":49,"line":242},[47,1952,789],{"class":56},[47,1954,839],{"class":472},[47,1956,479],{"class":56},[14,1958,1959],{},"错误信息应该：",[258,1961,1962,1965,1968],{},[261,1963,1964],{},"简短。",[261,1966,1967],{},"告诉用户发生了什么。",[261,1969,1970],{},"尽量提供下一步，例如重试。",[30,1972,1974],{"id":1973},"_12-常见错误","12. 常见错误",[1242,1976,1978,1979],{"id":1977},"错误一忘记-await-responsejson","错误一：忘记 ",[21,1980,1981],{},"await response.json()",[38,1983,1985],{"className":40,"code":1984,"language":42,"meta":43,"style":43},"const data = response.json()\n",[21,1986,1987],{"__ignoreMap":43},[47,1988,1989,1991,1994,1996,1998,2000],{"class":49,"line":50},[47,1990,81],{"class":80},[47,1992,1993],{"class":84}," data",[47,1995,88],{"class":80},[47,1997,233],{"class":56},[47,1999,236],{"class":53},[47,2001,239],{"class":56},[14,2003,2004,2005,2008],{},"这里 ",[21,2006,2007],{},"data"," 是 Promise，不是数据。应该：",[38,2010,2012],{"className":40,"code":2011,"language":42,"meta":43,"style":43},"const data = await response.json()\n",[21,2013,2014],{"__ignoreMap":43},[47,2015,2016,2018,2020,2022,2024,2026,2028],{"class":49,"line":50},[47,2017,81],{"class":80},[47,2019,1993],{"class":84},[47,2021,88],{"class":80},[47,2023,121],{"class":80},[47,2025,233],{"class":56},[47,2027,236],{"class":53},[47,2029,239],{"class":56},[1242,2031,2033],{"id":2032},"错误二没有处理失败状态","错误二：没有处理失败状态",[14,2035,2036],{},"只写成功时，一旦接口失败，页面空白或一直 loading。",[1242,2038,2040],{"id":2039},"错误三请求返回空数组时显示错误","错误三：请求返回空数组时显示错误",[14,2042,2043],{},"空数据不是错误。应该用 empty 状态：",[38,2045,2047],{"className":460,"code":2046,"language":462,"meta":43,"style":43},"\u003Cp v-else-if=\"items.length === 0\">暂无数据\u003C\u002Fp>\n",[21,2048,2049],{"__ignoreMap":43},[47,2050,2051,2053,2055,2057,2059,2061,2064,2067,2070,2073,2075,2078,2080],{"class":49,"line":50},[47,2052,469],{"class":56},[47,2054,14],{"class":472},[47,2056,842],{"class":80},[47,2058,570],{"class":56},[47,2060,1926],{"class":60},[47,2062,2063],{"class":56},"items.",[47,2065,2066],{"class":84},"length",[47,2068,2069],{"class":80}," ===",[47,2071,2072],{"class":84}," 0",[47,2074,1926],{"class":60},[47,2076,2077],{"class":56},">暂无数据\u003C\u002F",[47,2079,14],{"class":472},[47,2081,479],{"class":56},[1242,2083,2085],{"id":2084},"错误四组件卸载后还更新状态","错误四：组件卸载后还更新状态",[14,2087,2088],{},"复杂请求场景可能需要取消请求或检查组件是否仍然有效。初学阶段先知道有这个风险即可。",[30,2090,2091],{"id":2091},"本章练习",[2093,2094,2095,2101,2106,2109,2112,2115,2118],"ol",{},[261,2096,2097,2098,1132],{},"写 ",[21,2099,2100],{},"wait(ms)",[261,2102,2097,2103,1132],{},[21,2104,2105],{},"getMockLessons({ shouldFail, empty })",[261,2107,2108],{},"页面进入时请求课程。",[261,2110,2111],{},"显示 loading、error、empty、success。",[261,2113,2114],{},"错误状态提供重试按钮。",[261,2116,2117],{},"搜索框加 debounce。",[261,2119,2120],{},"回答：请求逻辑什么时候放组件，什么时候放 service，什么时候放 Pinia？",[2122,2123,2124],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}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":159,"depth":159,"links":2126},[2127,2128,2129,2131,2132,2134,2135,2140,2141,2142,2143,2144,2151],{"id":32,"depth":159,"text":33},{"id":185,"depth":159,"text":186},{"id":287,"depth":159,"text":2130},"3. 为什么要检查 response.ok",{"id":456,"depth":159,"text":457},{"id":1007,"depth":159,"text":2133},"5. try\u002Fcatch\u002Ffinally",{"id":1135,"depth":159,"text":1136},{"id":1236,"depth":159,"text":1237,"children":2136},[2137,2138,2139],{"id":1244,"depth":179,"text":1244},{"id":1261,"depth":179,"text":1262},{"id":1389,"depth":179,"text":1390},{"id":1406,"depth":159,"text":1407},{"id":1600,"depth":159,"text":1601},{"id":1781,"depth":159,"text":1782},{"id":1885,"depth":159,"text":1886},{"id":1973,"depth":159,"text":1974,"children":2145},[2146,2148,2149,2150],{"id":1977,"depth":179,"text":2147},"错误一：忘记 await response.json()",{"id":2032,"depth":179,"text":2033},{"id":2039,"depth":179,"text":2040},{"id":2084,"depth":179,"text":2085},{"id":2091,"depth":159,"text":2091},"md",{"order":446},"\u002Fsource\u002Fcode\u002Fvue\u002F09-http-async",{"title":5,"description":16},"source\u002Fcode\u002Fvue\u002F09-http-async","a2iTRYc-YxUPiT8GEDSWor4Zm1AoTelxDHRxKgXjgQg",1784032889391]