基于 SvelteKit 源码深度解析
2026-03-19 | 技术深度解读
第一部分:基础架构
第二部分:演进历史
第三部分:核心数据结构
第四部分:核心函数
SvelteKit 是 Svelte 的官方全栈框架,提供文件系统路由、SSR、SSG 等功能。
核心特性
路由系统目标
┌─────────────────────────────────────────┐
│ 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)
SvelteKit 1.0 (2022)
SvelteKit 2.0 (2023)
SvelteKit 最新版 (2026)
| 版本 | 关键特性 | 路由增强 |
|---|---|---|
| 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 |
/**
* 客户端路由定义
* 由 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 (不匹配)
/**
* 导航意图 - 描述一次导航的完整信息
* 由 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' }
/**
* 路由分支节点 - 表示一个 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;
}
/**
* 当前导航状态
* 存储在 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
};
/**
* 导航结果 - 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;
};
}
/**
* 依赖追踪 - 用于 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()
};
}
/**
* 应用配置 - 编译时生成
*/
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;
/**
* 初始化 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();
}
/**
* 核心导航函数
* 处理所有类型的导航
*/
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);
}
/**
* 加载路由数据
* 核心数据加载逻辑
*/
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 函数并追踪依赖
*/
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 } };
}
/**
* 获取导航意图
* 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(...);
}
/**
* 解析路由字典为 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: [...] }
/**
* 检查节点是否需要重新加载
* 基于 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;
}
/**
* 编程式导航
* 对应 $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);
}
}
});
}
/**
* 手动触发数据重新加载
* 对应 $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();
}
/**
* 预加载数据
* 在用户悬停/点击前提前加载
*/
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"
/**
* 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;
}
/**
* 并行加载 + 串行依赖
*/
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
┌──────────────┐ ┌──────────────┐
│ 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
});
}
加载优化
渲染优化
关键指标: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);
}
推荐做法
目录结构
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);
| 特性 | SvelteKit | Next.js |
|---|---|---|
| 路由系统 | 文件系统 + 参数匹配 | 文件系统 + App Router |
| 数据加载 | load 函数 | Server Components |
| 客户端路由 | 自定义实现 | 基于 React |
| 预加载 | hover/tap/viewport | Link prefetch |
| 包大小 | ~10KB | ~85KB |
| 类型安全 | 内置 | 需要配置 |
| 特性 | SvelteKit | Nuxt |
|---|---|---|
| 路由系统 | 文件系统 | 文件系统 + pages/ |
| 数据加载 | load 函数 | useFetch / useAsyncData |
| 状态管理 | Svelte stores | Pinia |
| 预加载 | data-sveltekit-* | NuxtLink prefetch |
| 编译时优化 | Svelte 编译器 | Vite |
| 学习曲线 | 低 | 中 |
短期 (2026)
长期
关键方向:性能、开发者体验、类型安全
/**
* 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);
}
});
核心要点
源码位置:packages/kit/src/runtime/client/client.js (~50KB)