🧡 SvelteKit 路由加载

深度解析客户端路由源码实现

基于 SvelteKit 源码深度解析
2026-03-19 | 技术深度解读

📑 目录

第一部分:基础架构

  • SvelteKit 简介
  • 路由系统架构
  • 客户端路由核心
  • 路由加载流程

第二部分:演进历史

  • 架构演进历史
  • 版本特性对比

第三部分:核心数据结构

  • CSRRoute / NavigationIntent
  • BranchNode / NavigationState
  • Uses / SvelteKitApp

第四部分:核心函数

  • start / navigate / load_route
  • load_node / parse
  • preload / 缓存策略

🧡 SvelteKit 简介

SvelteKit 是 Svelte 的官方全栈框架,提供文件系统路由、SSR、SSG 等功能。

核心特性

  • 文件系统路由
  • 服务端渲染 (SSR)
  • 静态站点生成 (SSG)
  • 客户端路由 (CSR)
  • 类型安全

路由系统目标

  • 零配置路由
  • 预加载优化
  • 渐进式增强
  • SEO 友好
  • 极致性能

🏗️ 路由系统架构

┌─────────────────────────────────────────┐
│           SvelteKit 路由系统              │
├─────────────────────────────────────────┤
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  │
│  │ 文件系统 │→ │  解析器 │→ │  路由表 │  │
│  │  路由   │  │  parse  │  │ routes  │  │
│  └─────────┘  └─────────┘  └─────────┘  │
│       ↓                                  │
│  ┌─────────────────────────────────────┐│
│  │      客户端路由 (client.js)          ││
│  │  • navigate() 导航控制               ││
│  │  • load_route() 路由加载             ││
│  │  • load_node() 节点加载              ││
│  └─────────────────────────────────────┘│
│       ↓                                  │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐  │
│  │  fetch  │→ │  render │→ │  页面   │  │
│  └─────────┘  └─────────┘  └─────────┘  │
└─────────────────────────────────────────┘

⚡ 客户端路由核心

client.js - SvelteKit 客户端路由的核心实现

// 核心文件位置
packages/kit/src/runtime/client/client.js

// 主要职责
1. 初始化应用 (start)
2. 处理导航 (navigate)
3. 加载路由数据 (load_route)
4. 管理历史记录
5. 处理预加载 (preload)
6. 管理滚动位置

🔄 路由加载流程

1. URL 变化 → 用户点击链接或调用 goto()

2. get_navigation_intent() → 解析 URL,匹配路由

3. load_route() → 加载路由数据(服务端+客户端)

4. load_node() → 执行每个节点的 load 函数

5. render → 渲染新页面组件

// 简化的导航流程
用户点击 → navigate() 
         → get_navigation_intent()
         → load_route()
         → get_navigation_result_from_branch()
         → root.$set(props)
         → 更新 DOM

📜 架构演进历史

Sapper 时代 (2017-2021)

  • 基于 Express 的 SSR
  • 简单的文件路由
  • 有限的类型支持

SvelteKit 1.0 (2022)

  • 全新的适配器系统
  • 增强的路由匹配
  • 更好的 TypeScript 支持

SvelteKit 2.0 (2023)

  • Shallow routing
  • 改进的数据加载
  • Server functions

SvelteKit 最新版 (2026)

  • Svelte 5 Runes 支持
  • Remote Functions
  • 增强的预加载

📈 版本特性对比

版本 关键特性 路由增强
1.0 适配器系统 基础文件路由
1.5 增量静态生成 参数匹配器
2.0 Shallow routing 并行路由
2.5 Remote Functions 服务端函数
最新 Svelte 5 集成 Runes 响应式

📊 核心数据结构

结构 职责 核心属性
CSRRoute 客户端路由定义 id, exec, layouts, leaf
NavigationIntent 导航意图 url, params, route
BranchNode 路由节点 node, loader, data
NavigationState 导航状态 branch, url, error
Uses 依赖追踪 dependencies, params

🔀 CSRRoute 接口

/**
 * 客户端路由定义
 * 由 parse() 函数从路由字典创建
 */
interface CSRRoute {
  id: string;           // 路由 ID,如 '/blog/[slug]'
  
  // 路由匹配函数
  exec: (path: string) => Record<string, string> | undefined;
  
  // 错误页面加载器数组
  errors: CSRPageNodeLoader[];
  
  // 布局加载器数组
  layouts: [boolean, CSRPageNodeLoader][];
  
  // 叶子节点(页面)加载器
  leaf: [boolean, CSRPageNodeLoader];
}

// exec 示例
route.exec('/blog/hello')  // { slug: 'hello' }
route.exec('/about')       // undefined (不匹配)

🎯 NavigationIntent

/**
 * 导航意图 - 描述一次导航的完整信息
 * 由 get_navigation_intent() 创建
 */
interface NavigationIntent {
  // 页面唯一标识
  id: string;  // pathname + search
  
  // 是否为 invalidation 触发
  invalidating: boolean;
  
  // 匹配的路由
  route: CSRRoute;
  
  // 解析后的参数
  params: Record<string, string>;
  
  // 目标 URL
  url: URL;
}

// 创建示例
const intent = await get_navigation_intent(
  new URL('/blog/hello', location.href),
  false
);
// intent.params = { slug: 'hello' }

🌳 BranchNode 接口

/**
 * 路由分支节点 - 表示一个 layout 或 page
 */
interface BranchNode {
  // 节点模块(包含 component, load 等)
  node: CSRPageNode;
  
  // 加载器函数引用
  loader: CSRPageNodeLoader;
  
  // 服务端数据
  server: DataNode | null;
  
  // 通用 load 数据和依赖追踪
  universal: {
    type: 'data';
    data: any;
    uses: Uses;  // 依赖追踪
  } | null;
  
  // 合并后的数据
  data: any;
  
  // 尾斜杠设置
  slash?: TrailingSlash;
}

📍 NavigationState

/**
 * 当前导航状态
 * 存储在 current 变量中
 */
interface NavigationState {
  // 当前激活的分支节点
  branch: Array<BranchNode | undefined>;
  
  // 当前错误(如果有)
  error: App.Error | null;
  
  // 当前 URL
  url: URL;
  
  // 当前路由
  route: CSRRoute | null;
  
  // 当前参数
  params: Record<string, string>;
}

// 全局状态
let current: NavigationState & { nav: NavigationEvent } = {
  branch: [],
  error: null,
  url: null,
  nav: null
};

📦 NavigationResult

/**
 * 导航结果 - load_route 的返回值
 */
type NavigationResult = 
  | NavigationFinished
  | { type: 'redirect'; location: string };

interface NavigationFinished {
  type: 'loaded';
  
  state: NavigationState;
  
  props: {
    // 组件构造函数数组
    constructors: ComponentType[];
    
    // 页面状态
    page: Page;
    
    // 数据属性 (data_0, data_1, ...)
    [key: `data_${number}`]: any;
    
    // 表单数据
    form?: any;
    
    // 错误(如果有)
    error?: any;
  };
}

🔗 Uses 接口 - 依赖追踪

/**
 * 依赖追踪 - 用于 invalidation 判断
 */
interface Uses {
  // fetch 依赖的 URL
  dependencies: Set<string>;
  
  // 使用的参数名
  params: Set<string>;
  
  // 是否使用 parent
  parent: boolean;
  
  // 是否使用 route
  route: boolean;
  
  // 是否使用 url
  url: boolean;
  
  // 使用的搜索参数
  search_params: Set<string>;
}

// 使用示例
function load({ url, params, depends }) {
  depends('custom:cache-key');
  return { 
    post: await fetch(`/api/${params.slug}`).json() 
  };
}

⚙️ SvelteKitApp

/**
 * 应用配置 - 编译时生成
 */
interface SvelteKitApp {
  // 根组件
  root: ComponentType;
  
  // 节点加载器数组
  nodes: CSRPageNodeLoader[];
  
  // 服务端 load 索引
  server_loads: number[];
  
  // 路由字典
  dictionary: Record<string, [leaf, layouts, errors]>;
  
  // 参数匹配器
  matchers: Record<string, ParamMatcher>;
  
  // 钩子函数
  hooks: {
    init?: () => Promise<void>;
    reroute?: (event) => URL | string | void;
  };
}

// 全局导出
export let app: SvelteKitApp;

🚀 start 函数

/**
 * 初始化 SvelteKit 客户端
 * 在页面加载时自动调用
 */
export async function start(
  _app: SvelteKitApp,
  _target: HTMLElement,
  hydrate?: HydrationData
) {
  app = _app;
  
  // 1. 解析路由表
  routes = __SVELTEKIT_CLIENT_ROUTING__ 
    ? parse(_app) 
    : [];
  
  // 2. 初始化历史索引
  current_history_index = history.state?.[HISTORY_INDEX];
  
  // 3. 水合或导航
  if (hydrate) {
    await _hydrate(target, hydrate);
  } else {
    await navigate({
      type: 'enter',
      url: resolve_url(location.href),
      replace_state: true
    });
  }
  
  // 4. 启动路由监听
  _start_router();
}

🔄 navigate 函数

/**
 * 核心导航函数
 * 处理所有类型的导航
 */
async function navigate({
  type,         // 'goto' | 'popstate' | 'link' | 'enter'
  url,          // 目标 URL
  popped,       // popstate 数据
  keepfocus,    // 保持焦点
  noscroll,     // 禁用滚动
  replace_state, // 替换历史
  state,        // 自定义状态
  redirect_count, // 重定向计数
  nav_token     // 导航令牌
}) {
  // 1. 获取导航意图
  const intent = await get_navigation_intent(url, false);
  
  // 2. 触发 before_navigate 回调
  const nav = _before_navigate({ url, type, intent });
  
  // 3. 加载路由
  let result = intent && await load_route(intent);
  
  // 4. 处理重定向
  if (result.type === 'redirect') {
    return navigate({ url: new URL(result.location) });
  }
  
  // 5. 更新历史记录
  history.pushState(entry, '', url);
  
  // 6. 渲染新页面
  root.$set(result.props);
}

📦 load_route 函数

/**
 * 加载路由数据
 * 核心数据加载逻辑
 */
async function load_route(intent: NavigationIntent) {
  const { errors, layouts, leaf } = intent.route;
  const loaders = [...layouts, leaf];
  
  // 1. 加载服务端数据(如果需要)
  if (__SVELTEKIT_HAS_SERVER_LOAD__) {
    const invalid_nodes = detect_invalid_nodes(loaders);
    if (invalid_nodes.some(Boolean)) {
      server_data = await load_data(url, invalid_nodes);
    }
  }
  
  // 2. 并行加载所有节点
  const branch_promises = loaders.map(async (loader, i) => {
    if (!loader) return;
    
    // 检查是否可以复用
    if (can_reuse(previous, loader)) return previous;
    
    // 加载新节点
    return load_node({ loader, url, params, ... });
  });
  
  // 3. 等待所有节点加载完成
  const branch = await Promise.all(branch_promises);
  
  // 4. 构建导航结果
  return get_navigation_result_from_branch({ branch, ... });
}

🔧 load_node 函数

/**
 * 加载单个节点
 * 执行 load 函数并追踪依赖
 */
async function load_node({
  loader, parent, url, params, route, server_data_node
}) {
  const node = await loader();
  
  // 依赖追踪
  const uses = {
    dependencies: new Set(),
    params: new Set(),
    parent: false,
    route: false,
    url: false,
    search_params: new Set()
  };
  
  let is_tracking = true;
  
  // 执行通用 load 函数
  if (node.universal?.load) {
    const load_input = {
      params: new Proxy(params, {
        get: (target, key) => {
          if (is_tracking) uses.params.add(key);
          return target[key];
        }
      }),
      url: make_trackable(url, () => {
        if (is_tracking) uses.url = true;
      }),
      depends: (...deps) => {
        deps.forEach(dep => uses.dependencies.add(dep));
      },
      parent: () => {
        if (is_tracking) uses.parent = true;
        return parent();
      }
    };
    
    data = await node.universal.load(load_input);
  }
  
  return { node, loader, data, universal: { data, uses } };
}

🎯 get_navigation_intent

/**
 * 获取导航意图
 * URL → 路由匹配 → NavigationIntent
 */
export async function get_navigation_intent(
  url: URL,
  invalidating: boolean
): Promise<NavigationIntent | undefined> {
  // 检查外部 URL
  if (is_external_url(url, base, app.hash)) return;
  
  // 处理 reroute 钩子
  if (__SVELTEKIT_CLIENT_ROUTING__) {
    const rerouted = await get_rerouted_url(url);
    if (!rerouted) return;
    
    const path = get_url_path(rerouted);
    
    // 遍历所有路由进行匹配
    for (const route of routes) {
      const params = route.exec(path);
      if (params) {
        return {
          id: get_page_key(url),
          invalidating,
          route,
          params: decode_params(params),
          url
        };
      }
    }
  }
  
  // 服务端路由解析(非 client routing 模式)
  return parse_server_route(...);
}

🔍 parse 函数

/**
 * 解析路由字典为 CSRRoute 数组
 * 在应用启动时调用
 */
export function parse({ nodes, server_loads, dictionary, matchers }) {
  return Object.entries(dictionary).map(([id, [leaf, layouts, errors]]) => {
    // 解析路由模式
    const { pattern, params } = parse_route_id(id);
    
    return {
      id,
      // 路由匹配函数
      exec: (path) => {
        const match = pattern.exec(path);
        if (match) return exec(match, params, matchers);
      },
      // 错误页面加载器
      errors: [1, ...(errors || [])].map(n => nodes[n]),
      // 布局加载器
      layouts: [0, ...(layouts || [])].map(create_layout_loader),
      // 叶子加载器
      leaf: create_leaf_loader(leaf)
    };
  });
}

// 路由示例
// dictionary: { '/blog/[slug]': [5, [3], [2]] }
// 解析为: { id: '/blog/[slug]', exec: fn, layouts: [...], leaf: [...] }

🔄 has_changed 函数

/**
 * 检查节点是否需要重新加载
 * 基于 Uses 依赖追踪
 */
function has_changed(
  parent_changed: boolean,
  route_changed: boolean,
  url_changed: boolean,
  search_params_changed: Set<string>,
  uses: Uses | undefined,
  params: Record<string, string>
): boolean {
  // 强制 invalidation
  if (force_invalidation) return true;
  
  if (!uses) return false;
  
  // 检查各种依赖变化
  if (uses.parent && parent_changed) return true;
  if (uses.route && route_changed) return true;
  if (uses.url && url_changed) return true;
  
  // 检查搜索参数变化
  for (const param of uses.search_params) {
    if (search_params_changed.has(param)) return true;
  }
  
  // 检查路径参数变化
  for (const param of uses.params) {
    if (params[param] !== current.params[param]) return true;
  }
  
  // 检查自定义依赖
  for (const href of uses.dependencies) {
    if (invalidated.some(fn => fn(new URL(href)))) return true;
  }
  
  return false;
}

➡️ _goto 函数

/**
 * 编程式导航
 * 对应 $app/navigation 的 goto
 */
export async function _goto(
  url: string | URL,
  options: {
    replaceState?: boolean;
    noScroll?: boolean;
    keepFocus?: boolean;
    invalidateAll?: boolean;
    invalidate?: Array<string | URL | ((url: URL) => boolean)>;
    state?: Record<string, any>;
  },
  redirect_count: number,
  nav_token: {}
) {
  // 清除预加载缓存
  if (options.invalidateAll) {
    discard_load_cache();
  }
  
  await navigate({
    type: 'goto',
    url: resolve_url(url),
    keepfocus: options.keepFocus,
    noscroll: options.noScroll,
    replace_state: options.replaceState,
    state: options.state,
    redirect_count,
    nav_token,
    accept: () => {
      if (options.invalidateAll) {
        force_invalidation = true;
      }
      if (options.invalidate) {
        options.invalidate.forEach(push_invalidated);
      }
    }
  });
}

♻️ _invalidate 函数

/**
 * 手动触发数据重新加载
 * 对应 $app/navigation 的 invalidate / invalidateAll
 */
async function _invalidate(
  include_load_functions = true,
  reset_page_state = true
) {
  // 批量处理多个同步 invalidation
  await (pending_invalidate ||= Promise.resolve());
  if (!pending_invalidate) return;
  pending_invalidate = null;
  
  const nav_token = (token = {});
  const intent = await get_navigation_intent(current.url, true);
  
  // 清除预加载
  discard_load_cache();
  
  // 刷新查询
  if (force_invalidation) {
    query_map.forEach(({ resource }) => {
      void resource.refresh?.();
    });
  }
  
  if (include_load_functions) {
    const navigation_result = intent && await load_route(intent);
    if (navigation_result.type === 'loaded') {
      update(navigation_result.props.page);
      current = { ...navigation_result.state, nav: current.nav };
      root.$set(navigation_result.props);
    }
  }
  
  reset_invalidation();
}

⚡ preload 机制

/**
 * 预加载数据
 * 在用户悬停/点击前提前加载
 */
async function _preload_data(intent: NavigationIntent) {
  // 复用已有的预加载
  if (intent.id !== load_cache?.id) {
    discard_load_cache();
    
    const preload = {};
    preload_tokens.add(preload);
    
    load_cache = {
      id: intent.id,
      token: preload,
      promise: load_route({ ...intent, preload }).then(result => {
        preload_tokens.delete(preload);
        // 不缓存错误
        if (result.type === 'loaded' && result.state.error) {
          discard_load_cache();
        }
        return result;
      }),
      fork: __SVELTEKIT_FORK_PRELOADS__ 
        ? create_fork(result) 
        : null
    };
  }
  
  return load_cache.promise;
}

// 触发时机
// 1. data-sveltekit-preload-data="hover"
// 2. data-sveltekit-preload-data="tap"
// 3. data-sveltekit-preload-data="viewport"

🗄️ fetch 缓存策略

/**
 * fetcher.js - 智能缓存
 */
const cache = new Map();

// 初始加载:从 SSR 注入的数据恢复
export function initial_fetch(resource, opts) {
  const selector = build_selector(resource, opts);
  const script = document.querySelector(selector);
  
  if (script?.textContent) {
    // 从 SSR 数据恢复
    let { body, ...init } = JSON.parse(script.textContent);
    const ttl = script.getAttribute('data-ttl');
    if (ttl) {
      cache.set(selector, { body, init, ttl: 1000 * Number(ttl) });
    }
    return Promise.resolve(new Response(body, init));
  }
  
  return window.fetch(resource, opts);
}

// 后续导航:使用缓存或 fetch
export function subsequent_fetch(resource, resolved, opts) {
  const cached = cache.get(selector);
  if (cached && performance.now() < cached.ttl) {
    return new Response(cached.body, cached.init);
  }
  return window.fetch(resolved, opts);
}

📜 滚动位置管理

/**
 * 滚动位置存储在 sessionStorage
 */
const scroll_positions: Record<number, { x, y }> = 
  storage.get(SCROLL_KEY) ?? {};

// 更新滚动位置
function update_scroll_positions(index: number) {
  scroll_positions[index] = scroll_state();
}

// 恢复滚动位置
function restore_scroll() {
  const scroll = scroll_positions[current_history_index];
  if (scroll) {
    history.scrollRestoration = 'manual';
    scrollTo(scroll.x, scroll.y);
  }
}

// 清理向前的历史
function clear_onward_history(current_history_index, current_navigation_index) {
  let i = current_history_index + 1;
  while (scroll_positions[i]) {
    delete scroll_positions[i];
    i += 1;
  }
}

// 持久化
function persist_state() {
  update_scroll_positions(current_history_index);
  storage.set(SCROLL_KEY, scroll_positions);
}

📸 快照恢复机制

/**
 * 组件快照 - 支持自定义恢复逻辑
 */
const snapshots: Record<string, any[]> = 
  storage.get(SNAPSHOT_KEY) ?? {};

// 组件数组(通过 bind:this 更新)
const components: SvelteComponent[] = [];

// 捕获快照
function capture_snapshot(index: number) {
  if (components.some(c => c?.snapshot)) {
    snapshots[index] = components.map(c => c?.snapshot?.capture());
  }
}

// 恢复快照
function restore_snapshot(index: number) {
  snapshots[index]?.forEach((value, i) => {
    components[i]?.snapshot?.restore(value);
  });
}

// 组件中使用
// <script>
//   export const snapshot = {
//     capture: () => ({ scroll: div.scrollTop }),
//     restore: (data) => { div.scrollTop = data.scroll; }
//   };
// </script>

🎭 设计模式 - 代理模式

/**
 * 使用 Proxy 追踪依赖访问
 */
const load_input = {
  // 代理 params 对象
  params: new Proxy(params, {
    get: (target, key) => {
      if (is_tracking) {
        uses.params.add(key);  // 记录访问
      }
      return target[key];
    }
  }),
  
  // 代理 route 对象
  route: new Proxy(route, {
    get: (target, key) => {
      if (is_tracking) {
        uses.route = true;  // 记录访问
      }
      return target[key];
    }
  }),
  
  // 代理 url 对象
  url: make_trackable(url, () => {
    if (is_tracking) uses.url = true;
  }, (param) => {
    if (is_tracking) uses.search_params.add(param);
  })
};

// 好处:自动追踪依赖,无需手动声明

👁️ 设计模式 - 观察者模式

/**
 * 导航生命周期回调
 */
const before_navigate_callbacks = new Set();
const on_navigate_callbacks = new Set();
const after_navigate_callbacks = new Set();

// 注册回调
export function beforeNavigate(fn) {
  before_navigate_callbacks.add(fn);
  return () => before_navigate_callbacks.delete(fn);
}

export function onNavigate(fn) {
  on_navigate_callbacks.add(fn);
  return () => on_navigate_callbacks.delete(fn);
}

export function afterNavigate(fn) {
  after_navigate_callbacks.add(fn);
  return () => after_navigate_callbacks.delete(fn);
}

// 触发回调
async function navigate(...) {
  // before_navigate
  before_navigate_callbacks.forEach(fn => fn(cancellable));
  
  // on_navigate
  const cleanups = await Promise.all(
    Array.from(on_navigate_callbacks, fn => fn(nav))
  );
  
  // after_navigate
  after_navigate_callbacks.forEach(fn => fn(navigation));
}

💾 设计模式 - 缓存模式

/**
 * 多层缓存策略
 */

// 1. load_cache - 预加载结果缓存
let load_cache = {
  id: string,
  token: {},
  promise: Promise<NavigationResult>,
  fork: Promise<Fork | null>
} | null;

// 2. reroute_cache - reroute 结果缓存
const reroute_cache = new Map<string, Promise<URL>>();

// 3. fetch_cache - fetch 响应缓存
const cache = new Map<string, { body, init, ttl }>();

// 4. query_map - 远程查询缓存
export const query_map = new Map<string, RemoteQueryCacheEntry>();

// 缓存复用示例
async function _preload_data(intent) {
  if (intent.id === load_cache?.id) {
    return load_cache.promise;  // 复用
  }
  // 创建新缓存
  load_cache = { id: intent.id, promise: load_route(intent) };
  return load_cache.promise;
}

🔗 设计模式 - Promise 链

/**
 * 并行加载 + 串行依赖
 */
const branch_promises = loaders.map(async (loader, i) => {
  if (!loader) return;
  
  return load_node({
    loader,
    // parent 是 Promise,等待前面的节点完成
    parent: async () => {
      const data = {};
      for (let j = 0; j < i; j += 1) {
        // 等待前面的节点
        Object.assign(data, (await branch_promises[j])?.data);
      }
      return data;
    }
  });
});

// Promise 链特点
// 1. 所有节点并行开始加载
// 2. parent() 内部等待前面的节点
// 3. 自动处理依赖关系
// 4. 避免串行瀑布流

// 数据流
// Layout 0 → Layout 1 → Page
//    ↓          ↓         ↓
//  data_0    data_1    data_2

📐 路由系统 UML

┌──────────────┐      ┌──────────────┐
│ SvelteKitApp │──────│   CSRRoute   │
└──────────────┘      └──────────────┘
       │                     │
       │                     │ contains
       │                     ↓
       │              ┌──────────────┐
       │              │ BranchNode   │
       │              │ - node       │
       │              │ - loader     │
       │              │ - data       │
       │              │ - uses       │
       │              └──────────────┘
       │                     │
       │ uses                │ creates
       ↓                     ↓
┌──────────────┐      ┌──────────────┐
│NavigationIntent│────│NavigationState│
│ - url        │      │ - branch     │
│ - params     │      │ - url        │
│ - route      │      │ - error      │
└──────────────┘      └──────────────┘

⏱️ 导航流程时序图

User         navigate()      get_intent     load_route     load_node
 │               │               │               │               │
 │──click link──→│               │               │               │
 │               │──get_intent──→│               │               │
 │               │               │──match route──→│               │
 │               │←──intent──────│               │               │
 │               │               │               │──load nodes──→│
 │               │               │               │               │──fetch server
 │               │               │               │               │──run load()
 │               │               │               │←──data────────│
 │               │               │←──result──────│               │
 │               │──update DOM───│               │               │
 │←──new page────│               │               │               │
 │               │               │               │               │

📊 数据加载流程

load_route(intent)
    │
    ├─→ 检查服务端节点是否需要更新
    │   ├─→ invalid_server_nodes = detect_invalid()
    │   └─→ server_data = await load_data(url, invalid_nodes)
    │
    ├─→ 并行加载所有节点
    │   └─→ loaders.map(loader => load_node(...))
    │       ├─→ 检查是否可复用 (has_changed)
    │       ├─→ 获取服务端数据 (server_data_node)
    │       └─→ 执行通用 load (node.universal.load)
    │
    ├─→ 处理错误
    │   └─→ load_nearest_error_page()
    │
    └─→ 构建结果
        └─→ get_navigation_result_from_branch()
            ├─→ 合并数据 (data_0, data_1, ...)
            ├─→ 构建页面状态 (page)
            └─→ 返回 NavigationFinished

⚠️ 错误处理流程

/**
 * 多层错误处理
 */
try {
  // 1. 服务端加载错误
  server_data = await load_data(url, invalid_nodes);
} catch (error) {
  if (preload_tokens.has(preload)) {
    // 预加载错误,返回静默错误页
    return preload_error({ error, url, params, route });
  }
  // 正常导航错误,加载错误页
  return load_root_error_page({ error, url });
}

try {
  // 2. 节点加载错误
  branch.push(await branch_promises[i]);
} catch (err) {
  if (err instanceof Redirect) {
    return { type: 'redirect', location: err.location };
  }
  
  // 加载最近的错误页面
  const error_load = await load_nearest_error_page(i, branch, errors);
  return get_navigation_result_from_branch({
    branch: branch.slice(0, error_load.idx).concat(error_load.node),
    status,
    error
  });
}

⚡ 性能优化策略

加载优化

  • 预加载 (preload)
  • 并行节点加载
  • 智能缓存
  • 增量更新
  • 代码分割

渲染优化

  • Fork 预渲染
  • 条件更新 (data_changed)
  • 快照恢复
  • 滚动位置缓存
  • CSS 延迟加载

关键指标:TTI < 100ms, FCP < 1.5s

🚀 预加载优化

/**
 * 多种预加载策略
 */

// 1. hover - 鼠标悬停时预加载
// <a href="/blog" data-sveltekit-preload-data="hover">

// 2. tap - 触摸时预加载
// <a href="/blog" data-sveltekit-preload-data="tap">

// 3. viewport - 进入视口时预加载
// <a href="/blog" data-sveltekit-preload-data="viewport">

// 4. eager - 立即预加载
// <a href="/blog" data-sveltekit-preload-data="eager">

// 预加载代码
async function _preload_code(url) {
  const route = (await get_navigation_intent(url, false))?.route;
  if (route) {
    await Promise.all(
      [...route.layouts, route.leaf]
        .filter(Boolean)
        .map(load => load[1]())
    );
  }
}

// 预加载数据
async function _preload_data(intent) {
  // 复用缓存,避免重复请求
  return load_cache?.promise ?? load_route(intent);
}

✂️ 代码分割策略

/**
 * 动态导入节点
 */
interface CSRPageNodeLoader {
  (): Promise<CSRPageNode>;
}

// 编译时生成的加载器
const nodes = [
  () => import('./nodes/0'),   // root layout
  () => import('./nodes/1'),   // root error
  () => import('./nodes/2'),   // layout
  () => import('./nodes/3'),   // page
  // ...
];

// 路由字典
const dictionary = {
  '/': [3, [2], []],           // page 3, layout 2
  '/blog': [5, [2, 4], []],    // page 5, layouts 2,4
  '/blog/[slug]': [7, [2, 4], [6]]  // page 7, layouts 2,4, error 6
};

// 好处
// 1. 每个路由独立 chunk
// 2. 共享 layout 只加载一次
// 3. 错误页面按需加载
// 4. 预加载只加载需要的代码

🧠 内存管理

/**
 * 内存优化策略
 */

// 1. 清理向前的历史
function clear_onward_history(current_history_index, current_navigation_index) {
  // 删除回退后不再需要的滚动位置
  let i = current_history_index + 1;
  while (scroll_positions[i]) {
    delete scroll_positions[i];
    i += 1;
  }
  
  // 删除不再需要的快照
  i = current_navigation_index + 1;
  while (snapshots[i]) {
    delete snapshots[i];
    i += 1;
  }
}

// 2. 丢弃预加载缓存
function discard_load_cache() {
  void load_cache?.fork?.then(f => f?.discard());
  load_cache = null;
}

// 3. 清理 reroute 缓存
// 在错误时自动清理
catch (e) {
  reroute_cache.delete(href);
}

✅ 最佳实践 - 路由设计

推荐做法

  • 使用 rest 参数 [...slug]
  • 参数匹配器 [slug=integer]
  • 布局分组 (group)
  • 错误边界 +error.svelte
  • 懒加载 +page.js

目录结构

src/routes/
├── +layout.svelte
├── +error.svelte
├── (group)/
│   ├── +layout.svelte
│   └── blog/
│       ├── +page.svelte
│       └── [slug]/
│           └── +page.svelte
└── api/
    └── [...path]/+server.js

✅ 最佳实践 - 数据加载

// ✅ 好的做法
export async function load({ params, depends, parent }) {
  // 声明依赖
  depends('blog:post:' + params.slug);
  
  // 并行加载
  const [post, comments] = await Promise.all([
    fetch(`/api/posts/${params.slug}`).json(),
    fetch(`/api/comments?post=${params.slug}`).json()
  ]);
  
  // 复用父布局数据
  const { user } = await parent();
  
  return { post, comments, user };
}

// ❌ 避免
export async function load({ params }) {
  // 串行加载 - 慢
  const post = await fetch(...).json();
  const comments = await fetch(...).json();  // 等待 post
  return { post, comments };
}

❌ 常见反模式

反模式 1:过度 invalidation

// ❌ 每次都重新加载
export async function load() {
  return { data: await fetch(...) };
}

// ✅ 使用依赖追踪
export async function load({ depends }) {
  depends('api:users');
  return { data: await fetch(...) };
}

反模式 2:阻塞 parent

// ❌ 不必要的等待
const { layoutData } = await parent();

// ✅ 按需使用
// parent() 只在被调用时
// 才会等待

🐛 调试技巧

/**
 * 开发模式下的调试工具
 */

// 1. 查看当前路由状态
console.log('current:', current);
console.log('routes:', routes);

// 2. 追踪导航
beforeNavigate((nav) => {
  console.log('beforeNavigate:', nav);
});

onNavigate((nav) => {
  console.log('onNavigate:', nav);
  return () => console.log('cleanup');
});

afterNavigate((nav) => {
  console.log('afterNavigate:', nav);
});

// 3. 查看依赖追踪
// 在 load 函数中
export async function load({ url, params }) {
  console.log('url accessed:', url.pathname);
  console.log('params accessed:', params);
  return { ... };
}

// 4. 查看 preload 状态
console.log('load_cache:', load_cache);
console.log('preload_tokens:', preload_tokens);

⚖️ 与 Next.js 对比

特性 SvelteKit Next.js
路由系统 文件系统 + 参数匹配 文件系统 + App Router
数据加载 load 函数 Server Components
客户端路由 自定义实现 基于 React
预加载 hover/tap/viewport Link prefetch
包大小 ~10KB ~85KB
类型安全 内置 需要配置

⚖️ 与 Nuxt 对比

特性 SvelteKit Nuxt
路由系统 文件系统 文件系统 + pages/
数据加载 load 函数 useFetch / useAsyncData
状态管理 Svelte stores Pinia
预加载 data-sveltekit-* NuxtLink prefetch
编译时优化 Svelte 编译器 Vite
学习曲线

🔮 未来发展趋势

短期 (2026)

  • Svelte 5 全面集成
  • Remote Functions 增强
  • 更好的 streaming
  • 性能优化

长期

  • View Transitions API
  • 更强的类型推导
  • 边缘部署优化
  • AI 辅助开发

关键方向:性能、开发者体验、类型安全

🌟 Svelte 5 集成

/**
 * Svelte 5 Runes 支持
 */

// 1. 响应式状态
let count = $state(0);

// 2. 派生状态
let doubled = $derived(count * 2);

// 3. 副作用
$effect(() => {
  console.log('count changed:', count);
});

// 4. 在 load 中使用
export async function load({ params }) {
  // Svelte 5 支持 async 组件
  return { data: await fetchData(params) };
}

// 5. transformError 集成
root = new app.root({
  target,
  props: result.props,
  sync: false,  // 异步实例化
  transformError: async (e) => {
    return await handle_error(e, current.nav);
  }
});

📚 总结与扩展阅读

核心要点

  • 文件系统路由 → CSRRoute
  • 依赖追踪 → 智能更新
  • 并行加载 → 性能优化
  • 多层缓存 → 快速响应
  • 生命周期 → 灵活控制

扩展阅读

源码位置:packages/kit/src/runtime/client/client.js (~50KB)