[{"data":1,"prerenderedAt":622},["ShallowReactive",2],{"page-/post/nuxt/nuxt-3.17-release":3,"surrounding-page":613},{"id":4,"title":5,"author":6,"body":7,"date":602,"description":603,"extension":604,"group":6,"lastmod":605,"meta":606,"navigation":368,"path":607,"rawbody":608,"seo":609,"showTitle":6,"stem":610,"tags":611,"versions":6,"__hash__":612},"content/post/nuxt/nuxt-3.17-release.md","Nuxt 3.17 发布，对比3.16有一个重大改变",null,{"type":8,"value":9,"toc":585},"minimark",[10,26,30,34,65,68,94,97,109,112,124,127,136,139,166,169,183,186,209,212,230,233,237,247,252,382,385,388,501,510,513,526,530,544,554,557,566,581],[11,12,13,14,18,19,22,23],"p",{},"今天逛 ",[15,16,17],"code",{},"Github"," ，发现 ",[15,20,21],{},"Nuxt"," 又发新版了： ",[15,24,25],{},"3.17.0",[27,28,29],"h2",{"id":29},"更新日志",[31,32,33],"h3",{"id":33},"数据获取改进",[35,36,37,53,59],"ul",{},[38,39,40,44,45,48,49,52],"li",{},[41,42,43],"strong",{},"数据一致性","：所有使用相同键的 ",[15,46,47],{},"useAsyncData"," 或 ",[15,50,51],{},"useFetch"," 调用现在共享底层引用，确保应用中数据状态一致。",[38,54,55,58],{},[41,56,57],{},"响应式键","：支持使用计算引用、普通引用或 getter 函数作为键，当键值变化时会自动触发数据重新获取。",[38,60,61,64],{},[41,62,63],{},"优化数据重新获取","：多个组件监听同一数据源时，依赖项变化只会触发一次数据获取。",[31,66,67],{"id":67},"新增内置组件",[35,69,70,78],{},[38,71,72,77],{},[41,73,74],{},[15,75,76],{},"\u003CNuxtTime>","：用于安全显示时间的组件，解决了在服务器端渲染和客户端渲染中处理日期时的水合不匹配问题，支持多种时间格式。",[38,79,80,85,86,89,90,93],{},[41,81,82],{},[15,83,84],{},"\u003CNuxtErrorBoundary>","：转换为单文件组件，暴露 ",[15,87,88],{},"error"," 和 ",[15,91,92],{},"clearError","，便于在模板中处理错误。",[31,95,96],{"id":96},"路由改进",[35,98,99],{},[38,100,101,104,105,108],{},[15,102,103],{},"\u003CNuxtLink>"," 新增 ",[15,106,107],{},"trailingSlash"," 属性，用于控制 URL 格式，添加尾部斜杠。",[31,110,111],{"id":111},"加载指示器自定义",[35,113,114],{},[38,115,116,117,89,120,123],{},"新增 ",[15,118,119],{},"hideDelay",[15,121,122],{},"resetDelay"," 属性，用于控制加载指示器的隐藏延迟和重置延迟。",[31,125,126],{"id":126},"文档作为包",[35,128,129],{},[38,130,131,132,135],{},"Nuxt 文档现可作为 npm 包 ",[15,133,134],{},"@nuxt/docs"," 安装，提供构建文档网站的原始 Markdown 和 YAML 内容。",[31,137,138],{"id":138},"开发体验改进",[35,140,141],{},[38,142,143,144],{},"新增多个警告，帮助开发者避免常见错误，例如：\n",[35,145,146,149,156,159],{},[38,147,148],{},"服务器组件缺少根元素时发出警告。",[38,150,151,152,155],{},"使用保留的 ",[15,153,154],{},"runtimeConfig.app"," 命名空间时发出警告。",[38,157,158],{},"核心自动导入预设被覆盖时发出警告。",[38,160,161,162,165],{},"同一文件中多次使用 ",[15,163,164],{},"definePageMeta"," 时发出错误提示。",[31,167,168],{"id":168},"模块开发增强",[35,170,171,177],{},[38,172,116,173,176],{},[15,174,175],{},"experimental.enforceModuleCompatibility","，允许 Nuxt 在加载不兼容模块时抛出错误。",[38,178,116,179,182],{},[15,180,181],{},"addComponentExports","，可自动注册文件中通过命名导出的组件。",[31,184,185],{"id":185},"性能改进",[35,187,188,195,202],{},[38,189,190,191,194],{},"使用 ",[15,192,193],{},"tinyglobby"," 提升文件 glob 操作速度。",[38,196,197,198,201],{},"排除 ",[15,199,200],{},".data"," 目录的类型检查，加快构建速度。",[38,203,204,205,208],{},"通过提升 ",[15,206,207],{},"purgeCachedData"," 检查来优化树摇优化。",[31,210,211],{"id":211},"其他改进",[35,213,214,221,224,227],{},[38,215,216,217,220],{},"提供升级命令 ",[15,218,219],{},"npx nuxi@latest upgrade --dedupe","，用于刷新锁文件并拉取最新依赖。",[38,222,223],{},"修复了多个问题，包括错误处理、路由解析、类型检查等。",[38,225,226],{},"文档更新，包括对自动导入、SEO 等内容的改进。",[38,228,229],{},"测试和 CI 流程的改进。",[27,231,232],{"id":232},"主要影响点",[31,234,236],{"id":235},"useasyncdatausefetch","useAsyncData、useFetch",[11,238,239,240,243,244,246],{},"以前写 ",[15,241,242],{},"watch"," 时，每次 ",[15,245,242],{}," 的值发生改变，回调函数会再次执行一次。",[11,248,249],{},[41,250,251],{},"现在不会了！",[253,254,259],"pre",{"className":255,"code":256,"language":257,"meta":258,"style":258},"language-typescript shiki shiki-themes github-light","// In multiple components:\nconst { data } = useAsyncData(\n  'users', \n  () => $fetch(`/api/users?page=${route.query.page}`),\n  { watch: [() => route.query.page] }\n)\n\n// When route.query.page changes, only one fetch operation will occur\n// All components using this key will update simultaneously\n","typescript","",[15,260,261,270,298,308,346,357,363,370,376],{"__ignoreMap":258},[262,263,266],"span",{"class":264,"line":265},"line",1,[262,267,269],{"class":268},"sAwPA","// In multiple components:\n",[262,271,273,277,281,285,288,291,295],{"class":264,"line":272},2,[262,274,276],{"class":275},"sD7c4","const",[262,278,280],{"class":279},"sgsFI"," { ",[262,282,284],{"class":283},"sYu0t","data",[262,286,287],{"class":279}," } ",[262,289,290],{"class":275},"=",[262,292,294],{"class":293},"s7eDp"," useAsyncData",[262,296,297],{"class":279},"(\n",[262,299,301,305],{"class":264,"line":300},3,[262,302,304],{"class":303},"sYBdl","  'users'",[262,306,307],{"class":279},", \n",[262,309,311,314,317,320,323,326,329,332,335,337,340,343],{"class":264,"line":310},4,[262,312,313],{"class":279},"  () ",[262,315,316],{"class":275},"=>",[262,318,319],{"class":293}," $fetch",[262,321,322],{"class":279},"(",[262,324,325],{"class":303},"`/api/users?page=${",[262,327,328],{"class":279},"route",[262,330,331],{"class":303},".",[262,333,334],{"class":279},"query",[262,336,331],{"class":303},[262,338,339],{"class":279},"page",[262,341,342],{"class":303},"}`",[262,344,345],{"class":279},"),\n",[262,347,349,352,354],{"class":264,"line":348},5,[262,350,351],{"class":279},"  { watch: [() ",[262,353,316],{"class":275},[262,355,356],{"class":279}," route.query.page] }\n",[262,358,360],{"class":264,"line":359},6,[262,361,362],{"class":279},")\n",[262,364,366],{"class":264,"line":365},7,[262,367,369],{"emptyLinePlaceholder":368},true,"\n",[262,371,373],{"class":264,"line":372},8,[262,374,375],{"class":268},"// When route.query.page changes, only one fetch operation will occur\n",[262,377,379],{"class":264,"line":378},9,[262,380,381],{"class":268},"// All components using this key will update simultaneously\n",[11,383,384],{},"取而代之的是增加了一个响应式键",[11,386,387],{},"可以直接用 computed 作为键，里面的响应式对象发生改变时，回调函数会再次执行",[253,389,391],{"className":255,"code":390,"language":257,"meta":258,"style":258},"const userId = ref('123')\nconst { data: user } = useAsyncData(\n  computed(() => `user-${userId.value}`),\n  () => fetchUser(userId.value)\n)\n\n// Changing the userId will automatically trigger a new data fetch\n// and clean up the old data if no other components are using it\nuserId.value = '456'\n",[15,392,393,413,436,461,473,477,481,486,491],{"__ignoreMap":258},[262,394,395,397,400,403,406,408,411],{"class":264,"line":265},[262,396,276],{"class":275},[262,398,399],{"class":283}," userId",[262,401,402],{"class":275}," =",[262,404,405],{"class":293}," ref",[262,407,322],{"class":279},[262,409,410],{"class":303},"'123'",[262,412,362],{"class":279},[262,414,415,417,419,422,425,428,430,432,434],{"class":264,"line":272},[262,416,276],{"class":275},[262,418,280],{"class":279},[262,420,284],{"class":421},"sqxcx",[262,423,424],{"class":279},": ",[262,426,427],{"class":283},"user",[262,429,287],{"class":279},[262,431,290],{"class":275},[262,433,294],{"class":293},[262,435,297],{"class":279},[262,437,438,441,444,446,449,452,454,457,459],{"class":264,"line":300},[262,439,440],{"class":293},"  computed",[262,442,443],{"class":279},"(() ",[262,445,316],{"class":275},[262,447,448],{"class":303}," `user-${",[262,450,451],{"class":279},"userId",[262,453,331],{"class":303},[262,455,456],{"class":279},"value",[262,458,342],{"class":303},[262,460,345],{"class":279},[262,462,463,465,467,470],{"class":264,"line":310},[262,464,313],{"class":279},[262,466,316],{"class":275},[262,468,469],{"class":293}," fetchUser",[262,471,472],{"class":279},"(userId.value)\n",[262,474,475],{"class":264,"line":348},[262,476,362],{"class":279},[262,478,479],{"class":264,"line":359},[262,480,369],{"emptyLinePlaceholder":368},[262,482,483],{"class":264,"line":365},[262,484,485],{"class":268},"// Changing the userId will automatically trigger a new data fetch\n",[262,487,488],{"class":264,"line":372},[262,489,490],{"class":268},"// and clean up the old data if no other components are using it\n",[262,492,493,496,498],{"class":264,"line":378},[262,494,495],{"class":279},"userId.value ",[262,497,290],{"class":275},[262,499,500],{"class":303}," '456'\n",[11,502,503,504,507,508],{},"这也是对我的博客站有影响的一点。 之前文章页的几个 ",[15,505,506],{},"tab"," 切换靠的是 ",[15,509,242],{},[11,511,512],{},"不过依然没让我失望，升级之后火速发现了奇怪的bug。",[11,514,515],{},[41,516,517,518,521,522,525],{},"然后又从 ",[15,519,520],{},"3.17"," 退回了",[15,523,524],{},"3.16.2","。。。",[31,527,529],{"id":528},"_2025年05月06日093334-更新","2025年05月06日09:33:34 更新",[11,531,532,533,536,537,540,541,543],{},"五一结束了，",[15,534,535],{},"nuxt"," 更到了",[15,538,539],{},"3.17.2","，解决了客户端组件 ",[15,542,47],{}," 的问题",[11,545,546,547,549,550,553],{},"本地升级后，在 ",[15,548,47],{}," 里使用 ",[15,551,552],{},"响应式key","，看起来表现正常了",[555,556],"hr",{},[11,558,559,560,563,564],{},"之前说好的 ",[15,561,562],{},"3.11"," 是最后一个小版本，现在愣是发到了 ",[15,565,520],{},[11,567,568,569,572,573,576,577,580],{},"虽然实际上更新的内容已经在向 ",[15,570,571],{},"Nuxt4"," 过渡，但是不知道为什么 ",[15,574,575],{},"Nitro"," 为什么不发 ",[15,578,579],{},"3.0"," (之前提到不发4.0是因为Nitro不发3.0)",[582,583,584],"style",{},"html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .sD7c4, html code.shiki .sD7c4{--shiki-default:#D73A49}html pre.shiki code .sgsFI, html code.shiki .sgsFI{--shiki-default:#24292E}html pre.shiki code .sYu0t, html code.shiki .sYu0t{--shiki-default:#005CC5}html pre.shiki code .s7eDp, html code.shiki .s7eDp{--shiki-default:#6F42C1}html pre.shiki code .sYBdl, html code.shiki .sYBdl{--shiki-default:#032F62}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sqxcx, html code.shiki .sqxcx{--shiki-default:#E36209}",{"title":258,"searchDepth":272,"depth":272,"links":586},[587,598],{"id":29,"depth":272,"text":29,"children":588},[589,590,591,592,593,594,595,596,597],{"id":33,"depth":300,"text":33},{"id":67,"depth":300,"text":67},{"id":96,"depth":300,"text":96},{"id":111,"depth":300,"text":111},{"id":126,"depth":300,"text":126},{"id":138,"depth":300,"text":138},{"id":168,"depth":300,"text":168},{"id":185,"depth":300,"text":185},{"id":211,"depth":300,"text":211},{"id":232,"depth":272,"text":232,"children":599},[600,601],{"id":235,"depth":300,"text":236},{"id":528,"depth":300,"text":529},"2025-06-24T08:04:08.000Z","今天逛 Github ，发现 Nuxt 又发新版了： 3.17.0","md","2025-08-15T14:56:19.000Z",{},"/post/nuxt/nuxt-3.17-release","---\ntitle: Nuxt 3.17 发布，对比3.16有一个重大改变\ndate: 2025-06-24 16:04:08\nlastmod: 2025-08-15 22:56:19\ntags: [\"Nuxt\"]\n\n---\n今天逛 `Github` ，发现 `Nuxt` 又发新版了： `3.17.0`\n## 更新日志\n### 数据获取改进\n\n- **数据一致性**：所有使用相同键的 `useAsyncData` 或 `useFetch` 调用现在共享底层引用，确保应用中数据状态一致。\n- **响应式键**：支持使用计算引用、普通引用或 getter 函数作为键，当键值变化时会自动触发数据重新获取。\n- **优化数据重新获取**：多个组件监听同一数据源时，依赖项变化只会触发一次数据获取。\n\n### 新增内置组件\n- **`\u003CNuxtTime>`**：用于安全显示时间的组件，解决了在服务器端渲染和客户端渲染中处理日期时的水合不匹配问题，支持多种时间格式。\n- **`\u003CNuxtErrorBoundary>`**：转换为单文件组件，暴露 `error` 和 `clearError`，便于在模板中处理错误。\n\n### 路由改进\n- `\u003CNuxtLink>` 新增 `trailingSlash` 属性，用于控制 URL 格式，添加尾部斜杠。\n\n### 加载指示器自定义\n- 新增 `hideDelay` 和 `resetDelay` 属性，用于控制加载指示器的隐藏延迟和重置延迟。\n\n### 文档作为包\n- Nuxt 文档现可作为 npm 包 `@nuxt/docs` 安装，提供构建文档网站的原始 Markdown 和 YAML 内容。\n\n### 开发体验改进\n- 新增多个警告，帮助开发者避免常见错误，例如：\n  - 服务器组件缺少根元素时发出警告。\n  - 使用保留的 `runtimeConfig.app` 命名空间时发出警告。\n  - 核心自动导入预设被覆盖时发出警告。\n  - 同一文件中多次使用 `definePageMeta` 时发出错误提示。\n\n### 模块开发增强\n- 新增 `experimental.enforceModuleCompatibility`，允许 Nuxt 在加载不兼容模块时抛出错误。\n- 新增 `addComponentExports`，可自动注册文件中通过命名导出的组件。\n\n### 性能改进\n- 使用 `tinyglobby` 提升文件 glob 操作速度。\n- 排除 `.data` 目录的类型检查，加快构建速度。\n- 通过提升 `purgeCachedData` 检查来优化树摇优化。\n\n### 其他改进\n- 提供升级命令 `npx nuxi@latest upgrade --dedupe`，用于刷新锁文件并拉取最新依赖。\n- 修复了多个问题，包括错误处理、路由解析、类型检查等。\n- 文档更新，包括对自动导入、SEO 等内容的改进。\n- 测试和 CI 流程的改进。\n\n## 主要影响点\n\n### useAsyncData、useFetch\n\n以前写 `watch` 时，每次 `watch` 的值发生改变，回调函数会再次执行一次。\n\n**现在不会了！**\n\n```typescript\n// In multiple components:\nconst { data } = useAsyncData(\n  'users', \n  () => $fetch(`/api/users?page=${route.query.page}`),\n  { watch: [() => route.query.page] }\n)\n\n// When route.query.page changes, only one fetch operation will occur\n// All components using this key will update simultaneously\n```\n\n取而代之的是增加了一个响应式键\n\n可以直接用 computed 作为键，里面的响应式对象发生改变时，回调函数会再次执行\n\n```typescript\nconst userId = ref('123')\nconst { data: user } = useAsyncData(\n  computed(() => `user-${userId.value}`),\n  () => fetchUser(userId.value)\n)\n\n// Changing the userId will automatically trigger a new data fetch\n// and clean up the old data if no other components are using it\nuserId.value = '456'\n```\n\n这也是对我的博客站有影响的一点。 之前文章页的几个 `tab` 切换靠的是 `watch`\n\n不过依然没让我失望，升级之后火速发现了奇怪的bug。\n\n**然后又从 `3.17` 退回了`3.16.2`。。。**\n\n### 2025年05月06日09:33:34 更新\n\n五一结束了，`nuxt` 更到了`3.17.2`，解决了客户端组件 `useAsyncData` 的问题\n\n本地升级后，在 `useAsyncData` 里使用 `响应式key`，看起来表现正常了\n\n---- \n\n之前说好的 `3.11` 是最后一个小版本，现在愣是发到了 `3.17`\n\n虽然实际上更新的内容已经在向 `Nuxt4` 过渡，但是不知道为什么 `Nitro` 为什么不发 `3.0` (之前提到不发4.0是因为Nitro不发3.0)\n\n",{"title":5,"description":603},"post/nuxt/nuxt-3.17-release",[21],"EP_NNwdp8ADpI63SUfJRG328418h3I0enaL5EhjD5zQ",[614,618],{"title":615,"path":616,"stem":617},"OpenClaw 安装入门（Windows）","/post/zzao/openclaw/openclaw-install-windows","post/zzao/openclaw/openclaw-install-windows",{"title":619,"path":620,"stem":621},"假设你是AI，你的Skill应该是什么样的","/post/zzao/ai-skill-structure","post/zzao/ai-skill-structure",1779005085660]