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