Lesson 14-Svelte Advanced Application Development

Server-Side Rendering (SSR)

SvelteKit SSR Configuration and Implementation

SvelteKit SSR Basic Configuration:

// svelte.config.js
import adapter from '@sveltejs/adapter-node'; // Node.js server adapter
// or import adapter from '@sveltejs/adapter-vercel'; // Vercel adapter

export default {
  kit: {
    adapter: adapter(),
    // SSR-related configuration
    ssr: {
      // Enable SSR (default: true)
      enabled: true,
      // List of external resources (not bundled into server)
      external: ['@sentry/node']
    }
  }
};

Server-Side Data Prefetching Process:

  1. Client requests a page
  2. SvelteKit server executes load function to fetch data
  3. Data is serialized and injected into page HTML
  4. Client reuses prefetched data during hydration

Custom Server Logic:

// src/hooks.server.js
export async function handle({ request, resolve }) {
  // Pre-request handling (e.g., authentication)
  const user = await authenticate(request);
  request.locals.user = user;
  
  // Process routing
  const response = await resolve(request, {
    // SSR configuration options
    ssr: true,
    transformPage: ({ html }) => {
      // Modify final HTML
      return html.replace('%USER%', user?.name || 'Guest');
    }
  });
  
  // Post-response handling (e.g., adding headers)
  response.headers['x-custom-header'] = 'value';
  return response;
}

Data Prefetching and State Synchronization

Page-Level Data Prefetching:

<!-- src/routes/blog/[slug]/+page.svelte -->
<script>
  export let data;
  // Data is prefetched and injected by load function
  const { post } = data;
</script>

<h1>{post.title}</h1>
<p>{post.content}</p>

Load Function Implementation:

// src/routes/blog/[slug]/+page.js
export async function load({ params, fetch }) {
  // Fetch data from API
  const res = await fetch(`/api/blog/${params.slug}`);
  const post = await res.json();
  
  // Returned data is injected into page component
  return {
    post
  };
}

Layout-Level Data Prefetching:

// src/routes/+layout.js
export async function load({ fetch }) {
  // Fetch global navigation data
  const navRes = await fetch('/api/navigation');
  const navigation = await navRes.json();
  
  return {
    navigation
  };
}

SSR Performance Optimization

Streaming SSR Implementation:

// src/routes/+page.svelte
<script>
  // Mark sections for streaming rendering
  export let streamData;
</script>

{#await streamData}
  <p>Loading initial content...</p>
{:then data}
  <StreamingContent {data} />
{/await}

Caching Strategy Configuration:

// svelte.config.js
export default {
  kit: {
    adapter: adapter({
      // Cache configuration
      cache: {
        // Static assets caching
        static: {
          maxAge: 60 * 60 * 24 * 30 // 30 days
        },
        // Page caching
        pages: {
          maxAge: 60 // 1 minute
        }
      }
    })
  }
};

Incremental Static Regeneration (ISR):

// src/routes/blog/[slug]/+page.js
export async function load({ params, fetch }) {
  // Check cache
  const cacheKey = `post-${params.slug}`;
  const cached = await caches.get(cacheKey);
  
  if (cached) {
    return cached;
  }
  
  // Fetch new data if cache miss
  const res = await fetch(`/api/blog/${params.slug}`);
  const post = await res.json();
  
  // Set cache (expires after 60 seconds)
  await caches.set(cacheKey, { post }, { ttl: 60 });
  
  return { post };
}

SSR Security and Error Handling

Security Protection Measures:

// src/hooks.server.js
export async function handle({ request, resolve }) {
  // 1. CSRF protection
  if (request.method === 'POST') {
    const csrfToken = request.headers['x-csrf-token'];
    if (!validateCsrfToken(csrfToken)) {
      return new Response('Invalid CSRF token', { status: 403 });
    }
  }
  
  // 2. XSS protection
  request.locals.sanitizedInput = sanitize(request.body);
  
  // 3. Rate limiting
  const ip = request.headers['x-forwarded-for'];
  if (isRateLimited(ip)) {
    return new Response('Too many requests', { status: 429 });
  }
  
  return resolve(request);
}

Error Handling Middleware:

// src/hooks.server.js
export async function handleError({ error, request }) {
  // Log error
  console.error(error);
  
  // Handle different error types
  if (error instanceof DatabaseError) {
    return {
      message: 'Database error occurred',
      code: 'DB_ERROR'
    };
  }
  
  if (error instanceof ValidationError) {
    return {
      message: 'Validation failed',
      code: 'VALIDATION_ERROR',
      details: error.details
    };
  }
  
  // Default error response
  return {
    message: 'Internal server error',
    code: 'INTERNAL_ERROR'
  };
}

SSR Application Scenarios

SEO Optimization Implementation:

<!-- src/routes/products/+page.svelte -->
<script>
  export let data;
  // Data is prefetched by load function
  const { products } = data;
</script>

<svelte:head>
  <title>Product List - Our Store</title>
  <meta name="description" content="Browse our wide selection of products">
  <!-- Structured data markup -->
  <script type="application/ld+json">
    {
      "@context": "https://schema.org",
      "@type": "Product",
      "name": "{products[0].name}",
      "description": "{products[0].description}"
    }
  </script>
</svelte:head>

<ul>
  {#each products as product}
    <li>{product.name} - ${product.price}</li>
  {/each}
</ul>

First-Screen Performance Optimization:

  1. Inline critical CSS
  2. Preload fonts
  3. Lazy-load images
  4. Code splitting
<!-- src/routes/+page.svelte -->
<svelte:head>
  <!-- Inline critical CSS -->
  <style>
    /* Critical CSS styles */
    .hero { background: #fff; padding: 1rem; }
  </style>
  
  <!-- Font preloading -->
  <link rel="preload" href="/fonts/example.woff2" as="font" type="font/woff2" crossorigin>
</svelte:head>

<!-- Lazy-load images -->
<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" alt="Example">

Static Site Generation (SSG)

SvelteKit SSG Mechanism

Static Generation Process:

  1. Execute load function to fetch data during build
  2. Generate corresponding HTML files
  3. Generate JavaScript required for client-side hydration
  4. Output to build directory

Configuration Example:

// svelte.config.js
import adapter from '@sveltejs/adapter-static';

export default {
  kit: {
    adapter: adapter({
      // Deployment directories
      pages: 'build',
      assets: 'build',
      // 404 page
      fallback: '404.html'
    })
  }
};

Prerendering Control:

// src/routes/+page.js
export async function load() {
  // Return fallback: true to mark as dynamic route
  return {
    fallback: true
  };
}

Content Management and Dynamic Routing

CMS Integration Example:

// src/routes/blog/[slug]/+page.js
export async function load({ params }) {
  // Fetch content from CMS API
  const res = await fetch(`https://cms.example.com/api/posts/${params.slug}`);
  if (!res.ok) {
    // Return fallback to mark as dynamic route
    return {
      fallback: true
    };
  }
  
  const post = await res.json();
  return {
    post
  };
}

Dynamic Route Generation:

// src/routes/+layout.js
export async function load() {
  // Fetch all blog posts
  const res = await fetch('https://cms.example.com/api/posts');
  const posts = await res.json();
  
  // Generate dynamic route configuration
  return {
    posts,
    // Prerender known routes
    trailingSlash: 'always'
  };
}

SSG Performance Optimization

Resource Optimization Strategies:

  1. Image optimization:
    • Use WebP format
    • Responsive images
    • Lazy loading
  2. Code optimization:
    • Tree-shaking
    • Code splitting
    • Compression and minification

Caching Strategy:

# Nginx configuration example
server {
  location / {
    # Cache static assets for 1 year
    expires 1y;
    add_header Cache-Control "public, immutable";
    
    # No cache or short cache for HTML/JSON
    if ($request_uri ~* \.(html|json)$) {
      expires -1;
      add_header Cache-Control "no-cache";
    }
  }
}

SSG and CMS Integration

Headless CMS Integration Process:

  1. Fetch content from CMS API during build
  2. Convert content to static HTML
  3. Generate data required for client-side hydration
  4. Deploy static files to CDN

Example Implementation:

// src/routes/blog/+page.js
export async function load() {
  // Fetch post list
  const postsRes = await fetch('https://cms.example.com/api/posts');
  const posts = await postsRes.json();
  
  // Prerender known posts
  const knownPosts = posts.filter(post => post.published);
  
  return {
    posts: knownPosts,
    // Mark unpublished posts as dynamic routes
    fallback: posts.some(post => !post.published)
  };
}

SSG Application Scenarios

Documentation Website Implementation:

src/routes/
├── docs/
│   ├── +layout.svelte    # Documentation layout
│   ├── getting-started/  # /docs/getting-started
│   │   └── +page.svelte
│   ├── api-reference/    # /docs/api-reference
│   │   └── +page.svelte
│   └── +page.svelte      # /docs

Blog System Optimization:

  1. Prerender published posts
  2. Dynamically handle draft posts
  3. Generate tag and category pages
  4. Implement RSS feed functionality
// src/routes/blog/+page.js
export async function load() {
  // Fetch all published posts
  const postsRes = await fetch('https://cms.example.com/api/posts?status=published');
  const posts = await postsRes.json();
  
  // Generate tag pages
  const tags = [...new Set(posts.flatMap(post => post.tags))];
  
  return {
    posts,
    tags,
    // Mark draft posts as dynamic routes
    fallback: posts.some(post => post.status === 'draft')
  };
}

Real-Time Application Development

WebSocket Integration with Svelte

WebSocket Connection Management:

// src/lib/websocket.js
export function createWebSocket(url) {
  const socket = new WebSocket(url);
  const listeners = new Set();
  
  socket.onmessage = (event) => {
    const data = JSON.parse(event.data);
    listeners.forEach(listener => listener(data));
  };
  
  return {
    subscribe(listener) {
      listeners.add(listener);
      return () => listeners.delete(listener);
    },
    send(data) {
      if (socket.readyState === WebSocket.OPEN) {
        socket.send(JSON.stringify(data));
      }
    },
    close() {
      socket.close();
    }
  };
}

Svelte Component Integration:

<script>
  import { createWebSocket } from '$lib/websocket';
  import { onMount, onDestroy } from 'svelte';
  
  let messages = [];
  const ws = createWebSocket('wss://api.example.com/ws');
  
  const unsubscribe = ws.subscribe(message => {
    messages = [...messages, message];
  });
  
  function sendMessage() {
    ws.send({ type: 'chat', text: 'Hello' });
  }
  
  onMount(() => {
    // Send authentication message after connection
    ws.send({ type: 'auth', token: '...' });
  });
  
  onDestroy(() => {
    unsubscribe();
    ws.close();
  });
</script>

<input bind:value={message} />
<button on:click={sendMessage}>Send</button>

<ul>
  {#each messages as message}
    <li>{message.text}</li>
  {/each}
</ul>

GraphQL Integration with Svelte

Apollo Client Configuration:

// src/lib/apollo.js
import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client/core';
import { setContext } from '@apollo/client/link/context';

const httpLink = new HttpLink({
  uri: 'https://api.example.com/graphql',
});

const authLink = setContext((_, { headers }) => {
  const token = localStorage.getItem('token');
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : '',
    }
  };
});

export const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache()
});

GraphQL Query Component:

<script>
  import { query } from '@sveltejs/apollo';
  import { gql } from '@apollo/client/core';
  
  const GET_POSTS = gql`
    query GetPosts {
      posts {
        id
        title
        content
      }
    }
  `;
  
  const { data, error, loading } = query(client, {
    query: GET_POSTS
  });
</script>

{#if loading}
  <p>Loading...</p>
{:else if error}
  <p>Error: {error.message}</p>
{:else}
  <ul>
    {#each data.posts as post}
      <li>{post.title}</li>
    {/each}
  </ul>
{/if}

Real-Time Collaborative Editing Implementation

CRDT Algorithm Integration:

// src/lib/crdt.js
import { Y } from 'yjs';
import { WebsocketProvider } from 'y-websocket';

export function createCollaborativeDoc(roomId) {
  const ydoc = new Y.Doc();
  const provider = new WebsocketProvider(
    'wss://collab.example.com',
    roomId,
    ydoc
  );
  
  return {
    ydoc,
    provider,
    getText(name = 'default') {
      return ydoc.getText(name);
    }
  };
}

Svelte Component Integration:

<script>
  import { onMount, onDestroy } from 'svelte';
  import { createCollaborativeDoc } from '$lib/crdt';
  
  let doc;
  let text = '';
  
  onMount(() => {
    doc = createCollaborativeDoc('room-123');
    const ytext = doc.getText();
    
    // Sync local edits to CRDT
    const binding = new Y.TextBinding(ytext, {
      text: {
        get value() { return text; },
        set value(v) { text = v; }
      }
    });
    
    return () => {
      binding.destroy();
      doc.provider.destroy();
    };
  });
</script>

<textarea bind:value={text} />

Real-Time Communication Performance Optimization

Bandwidth Optimization Strategies:

  1. Data Compression: Use gzip or Brotli
  2. Differential Updates: Send only changed data
  3. Throttling: Limit update frequency

Example Implementation:

// src/lib/realtime.js
export function createOptimizedWebSocket(url) {
  const socket = new WebSocket(url);
  let lastSent = {};
  
  function sendUpdate(update) {
    // Calculate diff
    const diff = calculateDiff(lastSent, update);
    
    if (Object.keys(diff).length > 0) {
      // Throttle control
      if (Date.now() - lastSendTime > 100) {
        socket.send(JSON.stringify(diff));
        lastSent = update;
        lastSendTime = Date.now();
      } else {
        // Queue for later sending
        pendingUpdates.push(diff);
      }
    }
  }
  
  return {
    sendUpdate
  };
}

Real-Time Application Security and Permissions

Authentication and Authorization Implementation:

// src/hooks.server.js
export async function handle({ request, resolve }) {
  // 1. Validate WebSocket connection
  if (request.headers['upgrade'] === 'websocket') {
    const token = request.headers['sec-websocket-protocol'];
    if (!validateToken(token)) {
      return new Response(null, { status: 401 });
    }
  }
  
  // 2. HTTP request authentication
  const token = request.headers['authorization'];
  if (token && !validateToken(token)) {
    return new Response('Unauthorized', { status: 401 });
  }
  
  return resolve(request);
}

Permission Control Example:

<script>
  import { onMount } from 'svelte';
  import { getPermissions } from '$lib/auth';
  
  let permissions = [];
  
  onMount(async () => {
    permissions = await getPermissions();
  });
</script>

{#if permissions.includes('edit')}
  <button>Edit Document</button>
{/if}

{#if permissions.includes('delete')}
  <button>Delete Document</button>
{/if}

By leveraging these advanced features, you can build feature-rich, high-performance Svelte applications. The key is to select appropriate technical solutions based on specific scenarios while continuously monitoring performance metrics and user experience during development.

Share your love