[{"data":1,"prerenderedAt":1194},["ShallowReactive",2],{"articles":3},[4,572,605,806,1040],{"id":5,"title":6,"body":7,"date":558,"description":559,"extension":560,"featured":561,"meta":562,"navigation":563,"path":564,"seo":565,"stem":566,"tags":567,"__hash__":571},"articles/articles/gpt-teachers-workspace-exploit.md","无限爽毛K12?GPT For Teachers工作区漏洞的小记",{"type":8,"value":9,"toc":545},"minimark",[10,18,22,25,28,40,53,56,59,64,71,123,142,153,157,160,229,232,258,262,265,344,358,363,366,381,386,397,401,404,413,416,440,443,446,454,457,488,491,494,505,508,527,530,541],[11,12,13,17],"p",{},[14,15,16],"span",{},"!TIP","\n文章由 LongCat-2.0 生成。",[19,20,21],"h2",{"id":21},"背景",[11,23,24],{},"最近网上冲浪的时候发现了一个强行加入 K12 空间的脚本。因好奇其实现原理，遂进行分析，发现它利用了 ChatGPT 平台的一系列安全缺陷来实现「无限K12」的效果。",[19,26,27],{"id":27},"脚本概述",[11,29,30,31,35,36,39],{},"该脚本是一个浏览器 Userscript（通过 Tampermonkey 等扩展注入），运行在 ",[32,33,34],"code",{},"https://chatgpt.com/*"," 页面上（",[32,37,38],{},"@run-at document-idle","）。安装后会自动在页面右上角注入一个浮层面板，提供以下功能：",[41,42,43,47,50],"ul",{},[44,45,46],"li",{},"自动获取当前登录账号的 Session Token（AT）",[44,48,49],{},"向指定的多个 Workspace 发送「加入申请」（request）",[44,51,52],{},"自动接受已发出的邀请（accept）",[11,54,55],{},"默认配置中硬编码了十几个 Workspace ID，批量操作为母号（管理员）和子号（被加入的账号）之间建立关联。",[19,57,58],{"id":58},"核心漏洞分析",[60,61,63],"h3",{"id":62},"_1-ataccess-token窃取","1. AT（Access Token）窃取",[11,65,66,67,70],{},"脚本的核心入口是 ",[32,68,69],{},"fetchSession()"," 函数：",[72,73,78],"pre",{"className":74,"code":75,"language":76,"meta":77,"style":77},"language-javascript shiki shiki-themes github-light github-dark","async function fetchSession() {\n  const res = await fetch(\"/api/auth/session\", {\n    headers: { accept: \"*/*\" },\n    credentials: \"include\",\n  });\n  return await res.json();\n}\n","javascript","",[32,79,80,87,93,99,105,111,117],{"__ignoreMap":77},[14,81,84],{"class":82,"line":83},"line",1,[14,85,86],{},"async function fetchSession() {\n",[14,88,90],{"class":82,"line":89},2,[14,91,92],{},"  const res = await fetch(\"/api/auth/session\", {\n",[14,94,96],{"class":82,"line":95},3,[14,97,98],{},"    headers: { accept: \"*/*\" },\n",[14,100,102],{"class":82,"line":101},4,[14,103,104],{},"    credentials: \"include\",\n",[14,106,108],{"class":82,"line":107},5,[14,109,110],{},"  });\n",[14,112,114],{"class":82,"line":113},6,[14,115,116],{},"  return await res.json();\n",[14,118,120],{"class":82,"line":119},7,[14,121,122],{},"}\n",[11,124,125,126,129,130,133,134,137,138,141],{},"这里利用了 ChatGPT 的 ",[32,127,128],{},"/api/auth/session"," 端点——该接口返回当前用户的完整会话信息，包括 ",[32,131,132],{},"accessToken","（AT）。由于浏览器 Same-Origin Policy 允许同源页面发起带 ",[32,135,136],{},"credentials: \"include\""," 的请求，任何注入到 ",[32,139,140],{},"chatgpt.com"," 的 JavaScript 代码都可以无感知地获取到当前登录用户的 Token。",[11,143,144,148,149,152],{},[145,146,147],"strong",{},"漏洞本质："," 服务端没有在 Token 传输层做足够的保护（如 ",[32,150,151],{},"httpOnly"," Cookie 绑定、Token 指纹绑定等），使得前端 JS 可以直接读取到高权限的 Access Token。",[60,154,156],{"id":155},"_2-jwt-结构解析","2. JWT 结构解析",[11,158,159],{},"获取到 AT 后，脚本进一步解析 JWT 载荷：",[72,161,163],{"className":74,"code":162,"language":76,"meta":77,"style":77},"function decodeJwt(at) {\n  const payload = at.split(\".\")[1];\n  const json = JSON.parse(atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\")));\n  const auth = json[\"https://api.openai.com/auth\"] || {};\n  const prof = json[\"https://api.openai.com/profile\"] || {};\n  return {\n    account_id: auth.chatgpt_account_id || \"\",\n    email: prof.email || \"\",\n    plan_type: auth.chatgpt_plan_type || \"\",\n    exp: json.exp || 0,\n  };\n}\n",[32,164,165,170,175,180,185,190,195,200,206,212,218,224],{"__ignoreMap":77},[14,166,167],{"class":82,"line":83},[14,168,169],{},"function decodeJwt(at) {\n",[14,171,172],{"class":82,"line":89},[14,173,174],{},"  const payload = at.split(\".\")[1];\n",[14,176,177],{"class":82,"line":95},[14,178,179],{},"  const json = JSON.parse(atob(payload.replace(/-/g, \"+\").replace(/_/g, \"/\")));\n",[14,181,182],{"class":82,"line":101},[14,183,184],{},"  const auth = json[\"https://api.openai.com/auth\"] || {};\n",[14,186,187],{"class":82,"line":107},[14,188,189],{},"  const prof = json[\"https://api.openai.com/profile\"] || {};\n",[14,191,192],{"class":82,"line":113},[14,193,194],{},"  return {\n",[14,196,197],{"class":82,"line":119},[14,198,199],{},"    account_id: auth.chatgpt_account_id || \"\",\n",[14,201,203],{"class":82,"line":202},8,[14,204,205],{},"    email: prof.email || \"\",\n",[14,207,209],{"class":82,"line":208},9,[14,210,211],{},"    plan_type: auth.chatgpt_plan_type || \"\",\n",[14,213,215],{"class":82,"line":214},10,[14,216,217],{},"    exp: json.exp || 0,\n",[14,219,221],{"class":82,"line":220},11,[14,222,223],{},"  };\n",[14,225,227],{"class":82,"line":226},12,[14,228,122],{},[11,230,231],{},"这说明 AT 是一个标准的 JWT，载荷中包含：",[41,233,234,240,246,252],{},[44,235,236,239],{},[32,237,238],{},"https://api.openai.com/auth.chatgpt_account_id","：账号唯一标识",[44,241,242,245],{},[32,243,244],{},"https://api.openai.com/profile.email","：用户邮箱",[44,247,248,251],{},[32,249,250],{},"chatgpt_plan_type","：订阅类型",[44,253,254,257],{},[32,255,256],{},"exp","：过期时间",[60,259,261],{"id":260},"_3-越权加入工作区","3. 越权加入工作区",[11,263,264],{},"有了 AT 后，脚本直接调用 Workspace 管理 API：",[72,266,268],{"className":74,"code":267,"language":76,"meta":77,"style":77},"async function sendOne(wsId, route, attempt = 0) {\n  const url = `/backend-api/accounts/${wsId}/invites/${route}`;\n  const headers = {\n    authorization: `Bearer ${STATE.at}`,\n    \"oai-device-id\": STATE.deviceId,\n    \"oai-language\": navigator.language || \"en-US\",\n  };\n  const res = await fetch(url, {\n    method: \"POST\",\n    headers,\n    body: null,\n    credentials: \"include\",\n  });\n  // ...\n}\n",[32,269,270,275,280,285,290,295,300,304,309,314,319,324,328,333,339],{"__ignoreMap":77},[14,271,272],{"class":82,"line":83},[14,273,274],{},"async function sendOne(wsId, route, attempt = 0) {\n",[14,276,277],{"class":82,"line":89},[14,278,279],{},"  const url = `/backend-api/accounts/${wsId}/invites/${route}`;\n",[14,281,282],{"class":82,"line":95},[14,283,284],{},"  const headers = {\n",[14,286,287],{"class":82,"line":101},[14,288,289],{},"    authorization: `Bearer ${STATE.at}`,\n",[14,291,292],{"class":82,"line":107},[14,293,294],{},"    \"oai-device-id\": STATE.deviceId,\n",[14,296,297],{"class":82,"line":113},[14,298,299],{},"    \"oai-language\": navigator.language || \"en-US\",\n",[14,301,302],{"class":82,"line":119},[14,303,223],{},[14,305,306],{"class":82,"line":202},[14,307,308],{},"  const res = await fetch(url, {\n",[14,310,311],{"class":82,"line":208},[14,312,313],{},"    method: \"POST\",\n",[14,315,316],{"class":82,"line":214},[14,317,318],{},"    headers,\n",[14,320,321],{"class":82,"line":220},[14,322,323],{},"    body: null,\n",[14,325,326],{"class":82,"line":226},[14,327,104],{},[14,329,331],{"class":82,"line":330},13,[14,332,110],{},[14,334,336],{"class":82,"line":335},14,[14,337,338],{},"  // ...\n",[14,340,342],{"class":82,"line":341},15,[14,343,122],{},[11,345,346,349,350,353,354,357],{},[32,347,348],{},"route"," 参数可以是 ",[32,351,352],{},"\"request\"","（申请加入）或 ",[32,355,356],{},"\"accept\"","（接受邀请）。这里的关键问题在于：",[11,359,360],{},[145,361,362],{},"漏洞一：AT 直接用于跨账号操作",[11,364,365],{},"Bearer Token 直接作为身份凭证，服务端只验证 Token 本身的有效性（是否过期、签名是否正确），而没有校验操作意图的合法性。这意味着：",[41,367,368,371,378],{},[44,369,370],{},"子号的 AT 可以用于向任意 Workspace 发起加入请求",[44,372,373,374,377],{},"母号的 AT 可以通过 ",[32,375,376],{},"accept"," 接口直接接受邀请，无需二次确认",[44,379,380],{},"整个流程没有任何 CSRF Token、确认步骤或人机验证",[11,382,383],{},[145,384,385],{},"漏洞二：无速率限制与设备指纹绕过",[11,387,388,389,392,393,396],{},"脚本使用了 ",[32,390,391],{},"crypto.randomUUID()"," 生成固定的 ",[32,394,395],{},"oai-device-id","，并在请求头中发送。这种客户端生成的设备标识符显然无法作为有效的风控依据，攻击者可以随意伪造。",[60,398,400],{"id":399},"_4-token-自动刷新机制","4. Token 自动刷新机制",[11,402,403],{},"脚本还实现了自动刷新机制，保证 AT 持续有效：",[72,405,407],{"className":74,"code":406,"language":76,"meta":77,"style":77},"setInterval(refreshSession, CONFIG.sessionPollMs); // 每20秒刷新一次\n",[32,408,409],{"__ignoreMap":77},[14,410,411],{"class":82,"line":83},[14,412,406],{},[11,414,415],{},"当检测到 401/403 响应时，脚本会主动清空 AT 并重新获取：",[72,417,419],{"className":74,"code":418,"language":76,"meta":77,"style":77},"if (res.status === 401 || res.status === 403) {\n  STATE.at = \"\";\n  await refreshSession();\n}\n",[32,420,421,426,431,436],{"__ignoreMap":77},[14,422,423],{"class":82,"line":83},[14,424,425],{},"if (res.status === 401 || res.status === 403) {\n",[14,427,428],{"class":82,"line":89},[14,429,430],{},"  STATE.at = \"\";\n",[14,432,433],{"class":82,"line":95},[14,434,435],{},"  await refreshSession();\n",[14,437,438],{"class":82,"line":101},[14,439,122],{},[11,441,442],{},"这意味着即使原始 Token 过期或被吊销，只要用户保持登录状态，脚本就能持续获取新的有效 Token 来维持攻击。",[19,444,445],{"id":445},"攻击链总结",[72,447,452],{"className":448,"code":450,"language":451},[449],"language-text","用户在 chatgpt.com 登录\n       ↓\nTampermonkey 注入 Userscript\n       ↓\nfetch(\"/api/auth/session\") → 窃取 AT\n       ↓\nJWT 解析 → 提取 account_id、email、plan_type\n       ↓\nPOST /backend-api/accounts/{workspaceId}/invites/request\n       ↓\n向多个目标 Workspace 批量发送加入申请\n       ↓\nPOST /backend-api/accounts/{workspaceId}/invites/accept\n       ↓\n自动接受邀请，子号进入母号的工作区\n       ↓\n每 20 秒刷新 Session，维持持久访问\n","text",[32,453,450],{"__ignoreMap":77},[19,455,456],{"id":456},"影响与危害",[458,459,460,466,472,478],"ol",{},[44,461,462,465],{},[145,463,464],{},"账号劫持风险","：任何运行该脚本的页面都可以窃取用户的 AT，用于冒充用户身份执行任意 API 操作",[44,467,468,471],{},[145,469,470],{},"工作区权限滥用","：子号可以在不知情的情况下被批量加入工作区，母号可以无审核地扩张团队规模",[44,473,474,477],{},[145,475,476],{},"批量注册/滥用","：结合自动化注册工具，可以低成本地批量创建账号并加入工作区，绕过付费限制",[44,479,480,483,484,487],{},[145,481,482],{},"供应链攻击面","：Tampermonkey 脚本的 ",[32,485,486],{},"@grant none"," 模式意味着脚本运行在页面主上下文中，拥有与页面 JS 同等的权限",[19,489,490],{"id":490},"修复建议",[11,492,493],{},"从空间管理者角度：",[41,495,496,499,502],{},[44,497,498],{},"定期审查空间成员列表，移除不认识或不活跃的账号",[44,500,501],{},"废弃被泄漏的空间",[44,503,504],{},"关闭不需要的 Codex 权限",[11,506,507],{},"从平台角度：",[41,509,510,518,521,524],{},[44,511,512,514,515,517],{},[32,513,128],{}," 返回的 AT 应限制为短期有效，或改用 ",[32,516,151],{}," Cookie 方案",[44,519,520],{},"Workspace 操作 API 应增加人机验证和意图确认",[44,522,523],{},"引入设备指纹绑定，使窃取的 Token 在其他设备上无法使用",[44,525,526],{},"增加 API 速率限制和异常行为检测",[11,528,529],{},"从用户角度：",[41,531,532,535,538],{},[44,533,534],{},"不要安装来源不明的 Tampermonkey 脚本",[44,536,537],{},"定期检查 ChatGPT 账号的登录活动和工作区成员列表",[44,539,540],{},"使用浏览器容器隔离不同站点的登录状态",[542,543,544],"style",{},"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);}",{"title":77,"searchDepth":89,"depth":89,"links":546},[547,548,549,555,556,557],{"id":21,"depth":89,"text":21},{"id":27,"depth":89,"text":27},{"id":58,"depth":89,"text":58,"children":550},[551,552,553,554],{"id":62,"depth":95,"text":63},{"id":155,"depth":95,"text":156},{"id":260,"depth":95,"text":261},{"id":399,"depth":95,"text":400},{"id":445,"depth":89,"text":445},{"id":456,"depth":89,"text":456},{"id":490,"depth":89,"text":490},"2026-07-05","对某 ChatGPT Workspace 自动化加入脚本的核心漏洞利用逻辑进行技术分析——AT 窃取、越权加入工作区","md",false,{},true,"/articles/gpt-teachers-workspace-exploit",{"title":6,"description":559},"articles/gpt-teachers-workspace-exploit",[568,569,570],"安全","ChatGPT","漏洞分析","1VI5HwznKs1nNHM5YpPzq9JZhNqXM-omeCk5YMkeYkI",{"id":573,"title":574,"body":575,"date":595,"description":596,"extension":560,"featured":561,"meta":597,"navigation":563,"path":598,"seo":599,"stem":600,"tags":601,"__hash__":604},"articles/articles/aiceo-warning.md","被中转坑了70块钱气晕了",{"type":8,"value":576,"toc":593},[577,583,586],[11,578,579,580],{},"避雷这个垃圾中转站 ",[145,581,582],{},"aiceo.dev",[11,584,585],{},"警钟长鸣啊。我的70块钱 T T",[11,587,588],{},[589,590],"img",{"alt":591,"src":592},"截图","https://free.boltp.com/2026/06/30/6a42c263b9a0b.webp",{"title":77,"searchDepth":89,"depth":89,"links":594},[],"2026-06-30","避雷这个垃圾中转站 aiceo.dev，警钟长鸣啊。我的70块钱 T T",{},"/articles/aiceo-warning",{"title":574,"description":596},"articles/aiceo-warning",[602,603],"吐槽","避雷","QKbR3OAnAd1CMgh0oD4u8Tr5lLu58EWXXfsa4W0lm4c",{"id":606,"title":607,"body":608,"date":795,"description":796,"extension":560,"featured":563,"meta":797,"navigation":563,"path":798,"seo":799,"stem":800,"tags":801,"__hash__":805},"articles/articles/getting-started.md","开始使用 Nuxt Content 构建博客",{"type":8,"value":609,"toc":785},[610,614,617,621,653,656,660,680,684,691,730,734,740,776,779,782],[19,611,613],{"id":612},"什么是-nuxt-content","什么是 Nuxt Content？",[11,615,616],{},"Nuxt Content 是一个基于文件的内容管理模块，专为 Nuxt 设计。它让你可以直接使用 Markdown、JSON 或 YAML 文件来管理网站内容。",[19,618,620],{"id":619},"为什么选择-nuxt-content","为什么选择 Nuxt Content？",[41,622,623,629,635,641,647],{},[44,624,625,628],{},[145,626,627],{},"零数据库依赖","：内容以文件形式存储在 Git 仓库中",[44,630,631,634],{},[145,632,633],{},"Markdown 原生支持","：写文章就像写 Markdown 一样简单",[44,636,637,640],{},[145,638,639],{},"自动路由","：基于文件结构自动生成路由",[44,642,643,646],{},[145,644,645],{},"全文搜索","：内置搜索功能",[44,648,649,652],{},[145,650,651],{},"SEO 友好","：支持 SSR/SSG，搜索引擎优化",[19,654,655],{"id":655},"快速开始",[60,657,659],{"id":658},"_1-安装模块","1. 安装模块",[72,661,665],{"className":662,"code":663,"language":664,"meta":77,"style":77},"language-bash shiki shiki-themes github-light github-dark","pnpm add @nuxt/content\n","bash",[32,666,667],{"__ignoreMap":77},[14,668,669,673,677],{"class":82,"line":83},[14,670,672],{"class":671},"sScJk","pnpm",[14,674,676],{"class":675},"sZZnC"," add",[14,678,679],{"class":675}," @nuxt/content\n",[60,681,683],{"id":682},"_2-配置模块","2. 配置模块",[11,685,686,687,690],{},"在 ",[32,688,689],{},"nuxt.config.ts"," 中添加：",[72,692,696],{"className":693,"code":694,"language":695,"meta":77,"style":77},"language-ts shiki shiki-themes github-light github-dark","export default defineNuxtConfig({\n  modules: ['@nuxt/content']\n})\n","ts",[32,697,698,714,725],{"__ignoreMap":77},[14,699,700,704,707,710],{"class":82,"line":83},[14,701,703],{"class":702},"szBVR","export",[14,705,706],{"class":702}," default",[14,708,709],{"class":671}," defineNuxtConfig",[14,711,713],{"class":712},"sVt8B","({\n",[14,715,716,719,722],{"class":82,"line":89},[14,717,718],{"class":712},"  modules: [",[14,720,721],{"class":675},"'@nuxt/content'",[14,723,724],{"class":712},"]\n",[14,726,727],{"class":82,"line":95},[14,728,729],{"class":712},"})\n",[60,731,733],{"id":732},"_3-创建内容","3. 创建内容",[11,735,686,736,739],{},[32,737,738],{},"content/"," 目录下创建 Markdown 文件：",[72,741,745],{"className":742,"code":743,"language":744,"meta":77,"style":77},"language-markdown shiki shiki-themes github-light github-dark","---\ntitle: \"我的第一篇文章\"\ndate: 2026-06-13\n---\n\n这是文章内容...\n","markdown",[32,746,747,752,757,762,766,771],{"__ignoreMap":77},[14,748,749],{"class":82,"line":83},[14,750,751],{},"---\n",[14,753,754],{"class":82,"line":89},[14,755,756],{},"title: \"我的第一篇文章\"\n",[14,758,759],{"class":82,"line":95},[14,760,761],{},"date: 2026-06-13\n",[14,763,764],{"class":82,"line":101},[14,765,751],{},[14,767,768],{"class":82,"line":107},[14,769,770],{"emptyLinePlaceholder":563},"\n",[14,772,773],{"class":82,"line":113},[14,774,775],{},"这是文章内容...\n",[19,777,778],{"id":778},"结语",[11,780,781],{},"Nuxt Content 是个人博客的理想选择，它简单、强大且易于维护。",[542,783,784],{},"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 .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":77,"searchDepth":89,"depth":89,"links":786},[787,788,789,794],{"id":612,"depth":89,"text":613},{"id":619,"depth":89,"text":620},{"id":655,"depth":89,"text":655,"children":790},[791,792,793],{"id":658,"depth":95,"text":659},{"id":682,"depth":95,"text":683},{"id":732,"depth":95,"text":733},{"id":778,"depth":89,"text":778},"2026-06-13","本指南将带你了解如何使用 Nuxt Content 模块构建一个现代化的博客系统",{},"/articles/getting-started",{"title":607,"description":796},"articles/getting-started",[802,803,804],"Nuxt","前端","教程","hvJUaxq3lInmMrlXXEzSYoyDFk6QTTIJohT7vk7rFTM",{"id":807,"title":808,"body":809,"date":1030,"description":1031,"extension":560,"featured":563,"meta":1032,"navigation":563,"path":1033,"seo":1034,"stem":1035,"tags":1036,"__hash__":1039},"articles/articles/frontend-tips.md","2026 年前端开发技巧分享",{"type":8,"value":810,"toc":1022},[811,814,818,821,911,915,918,959,963,966,1013,1016,1019],[19,812,813],{"id":813},"前端开发技巧",[60,815,817],{"id":816},"_1-使用-css-container-queries","1. 使用 CSS Container Queries",[11,819,820],{},"Container Queries 让组件可以根据父容器的尺寸来调整样式：",[72,822,826],{"className":823,"code":824,"language":825,"meta":77,"style":77},"language-css shiki shiki-themes github-light github-dark",".card-container {\n  container-type: inline-size;\n}\n\n@container (min-width: 400px) {\n  .card {\n    display: grid;\n    grid-template-columns: 1fr 2fr;\n  }\n}\n","css",[32,827,828,836,845,849,853,861,868,882,902,907],{"__ignoreMap":77},[14,829,830,833],{"class":82,"line":83},[14,831,832],{"class":671},".card-container",[14,834,835],{"class":712}," {\n",[14,837,838,842],{"class":82,"line":89},[14,839,841],{"class":840},"sj4cs","  container-type",[14,843,844],{"class":712},": inline-size;\n",[14,846,847],{"class":82,"line":95},[14,848,122],{"class":712},[14,850,851],{"class":82,"line":101},[14,852,770],{"emptyLinePlaceholder":563},[14,854,855,858],{"class":82,"line":107},[14,856,857],{"class":702},"@container",[14,859,860],{"class":712}," (min-width: 400px) {\n",[14,862,863,866],{"class":82,"line":113},[14,864,865],{"class":671},"  .card",[14,867,835],{"class":712},[14,869,870,873,876,879],{"class":82,"line":119},[14,871,872],{"class":840},"    display",[14,874,875],{"class":712},": ",[14,877,878],{"class":840},"grid",[14,880,881],{"class":712},";\n",[14,883,884,887,889,892,895,898,900],{"class":82,"line":202},[14,885,886],{"class":840},"    grid-template-columns",[14,888,875],{"class":712},[14,890,891],{"class":840},"1",[14,893,894],{"class":702},"fr",[14,896,897],{"class":840}," 2",[14,899,894],{"class":702},[14,901,881],{"class":712},[14,903,904],{"class":82,"line":208},[14,905,906],{"class":712},"  }\n",[14,908,909],{"class":82,"line":214},[14,910,122],{"class":712},[60,912,914],{"id":913},"_2-利用-view-transitions-api","2. 利用 View Transitions API",[11,916,917],{},"创建流畅的页面过渡动画：",[72,919,923],{"className":920,"code":921,"language":922,"meta":77,"style":77},"language-js shiki shiki-themes github-light github-dark","document.startViewTransition(() => {\n  // 更新 DOM\n  updateContent()\n})\n","js",[32,924,925,941,947,955],{"__ignoreMap":77},[14,926,927,930,933,936,939],{"class":82,"line":83},[14,928,929],{"class":712},"document.",[14,931,932],{"class":671},"startViewTransition",[14,934,935],{"class":712},"(() ",[14,937,938],{"class":702},"=>",[14,940,835],{"class":712},[14,942,943],{"class":82,"line":89},[14,944,946],{"class":945},"sJ8bj","  // 更新 DOM\n",[14,948,949,952],{"class":82,"line":95},[14,950,951],{"class":671},"  updateContent",[14,953,954],{"class":712},"()\n",[14,956,957],{"class":82,"line":101},[14,958,729],{"class":712},[60,960,962],{"id":961},"_3-使用-temporal-api","3. 使用 Temporal API",[11,964,965],{},"JavaScript 的新日期时间 API：",[72,967,969],{"className":920,"code":968,"language":922,"meta":77,"style":77},"const now = Temporal.Now.zonedDateTime()\nconst tomorrow = now.add({ days: 1 })\n",[32,970,971,990],{"__ignoreMap":77},[14,972,973,976,979,982,985,988],{"class":82,"line":83},[14,974,975],{"class":702},"const",[14,977,978],{"class":840}," now",[14,980,981],{"class":702}," =",[14,983,984],{"class":712}," Temporal.Now.",[14,986,987],{"class":671},"zonedDateTime",[14,989,954],{"class":712},[14,991,992,994,997,999,1002,1005,1008,1010],{"class":82,"line":89},[14,993,975],{"class":702},[14,995,996],{"class":840}," tomorrow",[14,998,981],{"class":702},[14,1000,1001],{"class":712}," now.",[14,1003,1004],{"class":671},"add",[14,1006,1007],{"class":712},"({ days: ",[14,1009,891],{"class":840},[14,1011,1012],{"class":712}," })\n",[19,1014,1015],{"id":1015},"总结",[11,1017,1018],{},"保持学习，关注 Web 平台的新特性，能让你的开发工作更加高效。",[542,1020,1021],{},"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 .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}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}",{"title":77,"searchDepth":89,"depth":89,"links":1023},[1024,1029],{"id":813,"depth":89,"text":813,"children":1025},[1026,1027,1028],{"id":816,"depth":95,"text":817},{"id":913,"depth":95,"text":914},{"id":961,"depth":95,"text":962},{"id":1015,"depth":89,"text":1015},"2026-06-10","分享一些实用的前端开发技巧和最佳实践",{},"/articles/frontend-tips",{"title":808,"description":1031},"articles/frontend-tips",[803,1037,1038],"JavaScript","Vue","F-ja4fQFhyBL6icWxm9_Ak9puEq11sxeWeZSiFPtf_Q",{"id":1041,"title":1042,"body":1043,"date":1186,"description":1187,"extension":560,"featured":561,"meta":1188,"navigation":563,"path":1189,"seo":1190,"stem":1191,"tags":1192,"__hash__":1193},"articles/articles/nuxt4-guide.md","Nuxt 4 新特性详解",{"type":8,"value":1044,"toc":1176},[1045,1049,1052,1056,1063,1069,1072,1076,1087,1091,1102,1106,1113,1140,1143,1168,1170,1173],[19,1046,1048],{"id":1047},"nuxt-4-新特性","Nuxt 4 新特性",[11,1050,1051],{},"Nuxt 4 带来了许多令人兴奋的新特性，让我们一起来看看。",[60,1053,1055],{"id":1054},"_1-app-directory-模式","1. App Directory 模式",[11,1057,1058,1059,1062],{},"Nuxt 4 推荐使用 ",[32,1060,1061],{},"app/"," 目录来组织源代码：",[72,1064,1067],{"className":1065,"code":1066,"language":451},[449],"├── app/\n│   ├── components/\n│   ├── pages/\n│   ├── composables/\n│   └── app.vue\n├── public/\n└── nuxt.config.ts\n",[32,1068,1066],{"__ignoreMap":77},[11,1070,1071],{},"这种结构更加清晰，便于管理大型项目。",[60,1073,1075],{"id":1074},"_2-改进的-typescript-支持","2. 改进的 TypeScript 支持",[41,1077,1078,1081,1084],{},[44,1079,1080],{},"更好的类型推断",[44,1082,1083],{},"自动导入类型",[44,1085,1086],{},"改进的 IDE 支持",[60,1088,1090],{"id":1089},"_3-性能优化","3. 性能优化",[41,1092,1093,1096,1099],{},[44,1094,1095],{},"更快的构建速度",[44,1097,1098],{},"更小的包体积",[44,1100,1101],{},"改进的热重载",[60,1103,1105],{"id":1104},"_4-兼容性日期","4. 兼容性日期",[11,1107,1108,1109,1112],{},"使用 ",[32,1110,1111],{},"compatibilityDate"," 来确保项目稳定性：",[72,1114,1116],{"className":693,"code":1115,"language":695,"meta":77,"style":77},"export default defineNuxtConfig({\n  compatibilityDate: '2025-07-15'\n})\n",[32,1117,1118,1128,1136],{"__ignoreMap":77},[14,1119,1120,1122,1124,1126],{"class":82,"line":83},[14,1121,703],{"class":702},[14,1123,706],{"class":702},[14,1125,709],{"class":671},[14,1127,713],{"class":712},[14,1129,1130,1133],{"class":82,"line":89},[14,1131,1132],{"class":712},"  compatibilityDate: ",[14,1134,1135],{"class":675},"'2025-07-15'\n",[14,1137,1138],{"class":82,"line":95},[14,1139,729],{"class":712},[19,1141,1142],{"id":1142},"升级指南",[458,1144,1145,1152,1158,1161],{},[44,1146,1147,1148,1151],{},"更新 ",[32,1149,1150],{},"package.json"," 中的依赖版本",[44,1153,1154,1155,1157],{},"将源代码移动到 ",[32,1156,1061],{}," 目录",[44,1159,1160],{},"检查废弃的 API 并迁移到新 API",[44,1162,1163,1164,1167],{},"运行 ",[32,1165,1166],{},"nuxt prepare"," 更新类型定义",[19,1169,778],{"id":778},[11,1171,1172],{},"Nuxt 4 是一个重要的版本更新，带来了更好的开发体验和性能。",[542,1174,1175],{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .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);}",{"title":77,"searchDepth":89,"depth":89,"links":1177},[1178,1184,1185],{"id":1047,"depth":89,"text":1048,"children":1179},[1180,1181,1182,1183],{"id":1054,"depth":95,"text":1055},{"id":1074,"depth":95,"text":1075},{"id":1089,"depth":95,"text":1090},{"id":1104,"depth":95,"text":1105},{"id":1142,"depth":89,"text":1142},{"id":778,"depth":89,"text":778},"2026-06-05","深入了解 Nuxt 4 的新特性和改进",{},"/articles/nuxt4-guide",{"title":1042,"description":1187},"articles/nuxt4-guide",[802,1038,803],"C-997_lULlclB65jzE-HH_ztaewNEAEUL_hD2qug9Bo",1783865040171]