Lesson 13-Svelte Project Architecture and Engineering

Project Architecture Design

SPA vs. MPA Architecture Design

SPA Core Architecture Implementation:

src/
├── lib/               # Reusable logic
├── routes/            # Route components
├── stores/            # State management
├── assets/            # Static assets
└── app.html           # Main HTML template

MPA Architecture Features:

  1. Multiple independent HTML entry files
  2. Each page is built and bundled separately
  3. Suitable for SEO-heavy scenarios
  4. Isolated state between pages

SPA Advantages Comparison:

FeatureSPAMPA
User ExperienceSeamless page transitionsPage reloads with white screen
Build ComplexitySingle entry pointMultiple entry configurations
SEO FriendlinessRequires SSR supportNaturally SEO-friendly
Server LoadLower after initial loadFull page requests each time

Modular and Component-Based Design

Modular Design Principles:

  1. Functional Isolation: Each module has distinct responsibilities
  2. Clear Interfaces: Well-defined module boundaries
  3. Controlled Dependencies: Avoid circular dependencies

Component Classification System:

src/routes/
├── components/      # Reusable components
│   ├── ui/          # Basic UI components (Button, Input)
│   └── layout/      # Layout components (Header, Footer)
├── features/        # Feature modules
│   ├── auth/        # Authentication-related
│   └── dashboard/   # Dashboard functionality
└── +page.svelte     # Page components

Component Design Guidelines:

  1. Single Responsibility: One component, one purpose
  2. Controlled Preference: Prioritize controlled component patterns
  3. Props Design:
    • Clearly mark required props
    • Set sensible default values
    • Define complex props with TypeScript interfaces

State Management Architecture

Svelte Store Pattern:

// stores/counter.js
import { writable } from 'svelte/store';

export const count = writable(0);

// Derived state
export const doubled = derived(count, $count => $count * 2);

Complex State Management Example:

// stores/user.js
import { writable, derived } from 'svelte/store';

export const user = writable(null);
export const isAuthenticated = derived(user, $user => !!$user);

// Async operations
export async function login(credentials) {
  const response = await fetch('/api/login', {
    method: 'POST',
    body: JSON.stringify(credentials)
  });
  const userData = await response.json();
  user.set(userData);
}

State Management Selection Guidelines:

  1. Simple state: Use writable/readable stores directly
  2. Complex state: Consider using svelte-spa-router for state management
  3. Global state: Combine with Context API

Routing Architecture Design

Dynamic Routing Implementation:

<!-- routes/blog/[slug]/+page.svelte -->
<script>
  export let data;
  const { slug } = data.params;
</script>

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

Nested Routing Configuration:

src/routes/
├── admin/
│   ├── +layout.svelte  # Admin dashboard layout
│   ├── dashboard/
│   │   └── +page.svelte
│   └── settings/
│       └── +page.svelte
└── +layout.svelte      # Root layout

Route Guard Implementation:

<!-- routes/+layout.svelte -->
<script>
  import { goto } from '$app/navigation';
  import { isAuthenticated } from '$stores/user';

  export let data;

  if (!data.user && !$isAuthenticated) {
    goto('/login');
  }
</script>

<slot />

Project Directory Standards

Recommended Directory Structure:

my-svelte-app/
├── .github/               # GitHub configurations
│   └── workflows/         # CI/CD workflows
├── public/                # Static assets (not bundled)
├── src/
│   ├── lib/               # Reusable logic
│   ├── routes/            # Route components
│   ├── stores/            # State management
│   ├── assets/            # Images, fonts, etc.
│   ├── app.html           # Main HTML template
│   └── hooks.js           # Global hooks
├── .eslintrc.cjs          # ESLint configuration
├── .prettierrc            # Prettier configuration
├── svelte.config.js       # Svelte configuration
└── package.json           # Dependency management

Naming Convention Recommendations:

  1. Component files: PascalCase (e.g., UserProfile.svelte)
  2. Utility functions: camelCase (e.g., formatDate.js)
  3. Style files: Match component name (e.g., UserProfile.module.css)
  4. Test files: ComponentName.test.js

Engineering Toolchain

SvelteKit Basic Configuration

Routing Management Example:

// src/routes/+layout.js
export const load = async ({ fetch }) => {
  const res = await fetch('/api/navigation');
  const navigation = await res.json();
  
  return {
    navigation
  };
};

Environment Variables Configuration:

# .env
VITE_API_URL=http://localhost:3000/api

# src/routes/+page.svelte
<script>
  import { env } from '$env/dynamic/public';
  const apiUrl = env.PUBLIC_API_URL;
</script>

Rollup Deep Configuration

Custom Rollup Plugin Example:

// rollup.config.js
import svelte from 'rollup-plugin-svelte';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';

export default {
  plugins: [
    svelte({
      compilerOptions: {
        dev: !production,
        css: 'injected'
      }
    }),
    resolve({
      browser: true,
      dedupe: ['svelte']
    }),
    commonjs(),
    {
      // Custom plugin example
      name: 'my-plugin',
      transform(code, id) {
        if (id.endsWith('.svelte')) {
          console.log('Processing Svelte file:', id);
        }
        return code;
      }
    }
  ]
};

Optimization Strategies:

  1. Code Splitting: Automatically split code by routes
  2. Tree-shaking: Remove unused code
  3. Compression Optimization: Use terser for code minification

Vite in Svelte Projects

Vite Configuration Example:

// vite.config.js
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';

export default defineConfig({
  plugins: [svelte()],
  server: {
    port: 3000,
    open: true,
    hmr: {
      overlay: false
    }
  },
  build: {
    minify: 'terser',
    sourcemap: true,
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['svelte', 'svelte/internal']
        }
      }
    }
  }
});

Fast Cold Start Advantages:

  1. Native ESM development server
  2. On-demand compilation
  3. Instant hot module replacement

Code Linting and Style Guidelines

ESLint Configuration:

// .eslintrc.cjs
module.exports = {
  extends: [
    'eslint:recommended',
    'plugin:svelte/recommended'
  ],
  parserOptions: {
    ecmaVersion: 2020,
    sourceType: 'module'
  },
  env: {
    browser: true,
    es2020: true,
    node: true
  },
  rules: {
    'no-console': 'warn',
    'svelte/no-at-html-tags': 'error'
  }
};

Prettier Configuration:

// .prettierrc
{
  "semi": false,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 100
}

Automated Testing Framework

Jest Testing Example:

// stores/counter.test.js
import { count } from '$stores/counter';

test('increments count', () => {
  const { subscribe } = count;
  let value;
  
  const unsubscribe = subscribe(v => {
    value = v;
  });
  
  count.set(1);
  expect(value).toBe(1);
  
  unsubscribe();
});

Testing Library Example:

// +page.svelte.test.js
import { render, screen } from '@testing-library/svelte';
import Page from './+page.svelte';

test('renders learn svelte link', () => {
  render(Page);
  const linkElement = screen.getByText(/learn svelte/i);
  expect(linkElement).toBeInTheDocument();
});

Performance Optimization and Deployment

Code Splitting and Lazy Loading

Route-Level Code Splitting:

<!-- src/routes/+layout.svelte -->
<script>
  import { onMount } from 'svelte';
  
  let HeavyComponent;
  
  onMount(async () => {
    if (needsHeavyComponent) {
      HeavyComponent = (await import('./HeavyComponent.svelte')).default;
    }
  });
</script>

{#if HeavyComponent}
  <svelte:component this={HeavyComponent} />
{/if}

Component-Level Lazy Loading:

<script>
  import { onMount } from 'svelte';
  
  let LazyModal;
  
  function openModal() {
    if (!LazyModal) {
      import('./LazyModal.svelte').then(module => {
        LazyModal = module.default;
      });
    }
  }
</script>

<button on:click={openModal}>Open Modal</button>
{#if LazyModal}
  <svelte:component this={LazyModal} />
{/if}

Resource Compression and Caching Strategies

Gzip Compression Configuration:

# Nginx configuration example
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_comp_level 6;
gzip_min_length 1024;

CDN Configuration Best Practices:

  1. Static asset versioning: main.[hash].js
  2. Cache strategies:
    • HTML: no-cache
    • JS/CSS: max-age=31536000
    • Images: max-age=604800

Server-Side Rendering and Static Site Generation

SSR Implementation Example:

// src/hooks.server.js
export async function handle({ request, resolve }) {
  const response = await resolve(request, {
    ssr: true,
    transformPage: ({ html }) => html.replace('%ENV%', process.env.NODE_ENV)
  });
  
  return response;
}

SSG Configuration Example:

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

export default {
  kit: {
    adapter: adapter({
      pages: 'build',
      assets: 'build',
      fallback: null
    })
  }
};

Performance Monitoring and Analysis

Lighthouse Optimization Recommendations:

  1. Priority Metrics:
    • First Contentful Paint (FCP)
    • Largest Contentful Paint (LCP)
    • Cumulative Layout Shift (CLS)
  2. Optimization Directions:
    • Inline critical CSS
    • Preload fonts
    • Lazy-load images

Web Vitals Monitoring:

// src/routes/+layout.svelte
<script>
  import { onMount } from 'svelte';
  import { getCLS, getFID, getLCP } from 'web-vitals';
  
  onMount(() => {
    getCLS(console.log);
    getFID(console.log);
    getLCP(console.log);
  });
</script>

Deployment and Continuous Integration

GitHub Actions Example:

# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [ main ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '16'
      - run: npm ci
      - run: npm run build
      - uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./build

Dockerization Configuration:

# Dockerfile
FROM node:16-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

By implementing the above architecture design and engineering practices, you can build high-performance, maintainable, and scalable Svelte applications. The key is to select appropriate technical solutions based on project scale and team characteristics, while continuously monitoring performance metrics and user experience during development.

Share your love