Lesson 11-Nuxt.js Project Architecture and Engineering

Project Architecture Design

SPA and Multi-Page Application Architecture Design

SPA Architecture Implementation:

// nuxt.config.js
export default {
  ssr: false, // Disable server-side rendering
  target: 'client', // Client-side rendering target
  router: {
    mode: 'history', // HTML5 History mode
    base: '/app/' // Custom base path
  },
  // Disable unnecessary SSR features
  render: {
    resourceHints: false,
    ssr: false
  }
}

Multi-Page Application Architecture Implementation:

// nuxt.config.js
export default {
  ssr: true, // Enable server-side rendering
  target: 'server', // Server-side rendering target
  router: {
    extendRoutes(routes, resolve) {
      // Add independent page routes
      routes.push({
        name: 'about',
        path: '/about',
        component: resolve(__dirname, 'pages/about.vue')
      })
    }
  },
  // Multi-entry configuration (via layout system)
  layout: {
    default: '~/layouts/default.vue',
    about: '~/layouts/about.vue' // Special page layout
  }
}

Hybrid Architecture Design:

// nuxt.config.js
export default {
  ssr: true,
  target: 'static', // Static generation
  router: {
    // Dynamic route SSR
    routes: [
      { path: '/user/:id', component: '~/pages/user/_id.vue' },
      // Static page SPA
      { path: '/dashboard', component: '~/pages/dashboard.vue', ssr: false }
    ]
  }
}

Modularization and Componentization Design

Modular Architecture Example:

modules/
├── auth/               # Authentication module
│   ├── index.ts        # Module entry
│   ├── composables/    # Composables
│   ├── components/     # Module components
│   └── store/          # Module state
├── payment/            # Payment module
│   ├── gateway.ts      # Payment gateway integration
│   └── components/     # Payment-related components
└── ui/                 # UI base module
    ├── buttons/        # Button components
    └── modals/         # Modal components

Component Design Specification:

<!-- components/BaseButton.vue -->
<template>
  <button
    :class="['base-button', `variant-${variant}`]"
    :disabled="disabled"
    @click="handleClick"
  >
    <slot></slot>
  </button>
</template>

<script setup lang="ts">
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
  variant: {
    type: String,
    default: 'primary',
    validator: (v: string) => ['primary', 'secondary', 'text'].includes(v)
  },
  disabled: Boolean
})

const emit = defineEmits(['click'])

const handleClick = (event: MouseEvent) => {
  if (!props.disabled) {
    emit('click', event)
  }
}
</script>

State Management Architecture

Pinia Modular Design:

// stores/user.ts
export const useUserStore = defineStore('user', {
  state: () => ({
    profile: null as UserProfile | null,
    token: ''
  }),
  getters: {
    isAuthenticated: (state) => !!state.token
  },
  actions: {
    async login(credentials: LoginCredentials) {
      const { data } = await useApi().post('/auth/login', credentials)
      this.token = data.value.token
      this.profile = data.value.user
    }
  }
})

// stores/index.ts (aggregate all stores)
export const useStore = () => ({
  user: useUserStore(),
  cart: useCartStore()
})

Vuex Modular Design:

// store/modules/user.ts
export default {
  namespaced: true,
  state: () => ({
    profile: null,
    token: ''
  }),
  mutations: {
    SET_TOKEN(state, token: string) {
      state.token = token
    }
  },
  actions: {
    async login({ commit }, credentials: LoginCredentials) {
      const { data } = await useApi().post('/auth/login', credentials)
      commit('SET_TOKEN', data.value.token)
    }
  }
}

// store/index.ts
export default createStore({
  modules: {
    user: userModule,
    products: productsModule
  }
})

Routing Architecture Design

Dynamic Routing Implementation:

// pages/users/_id.vue
<script setup>
const route = useRoute()
const { data: user } = await useFetch(`/api/users/${route.params.id}`)
</script>

<template>
  <div>User ID: {{ route.params.id }}</div>
  <div v-if="user">{{ user.name }}</div>
</template>

Nested Routing Implementation:

pages/
├── admin/
│   ├── index.vue       # /admin
│   └── users/
│       ├── index.vue   # /admin/users
│       └── _id.vue     # /admin/users/:id

Route Middleware:

// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
  const auth = useAuthStore()

  if (to.meta.requiresAuth && !auth.isAuthenticated) {
    return navigateTo('/login')
  }
})

// pages/protected.vue
<script setup>
definePageMeta({
  middleware: 'auth',
  requiresAuth: true
})
</script>

Project Directory Structure Specification

Recommended Directory Structure:

my-nuxt-app/
├── .nuxt/                # Nuxt generated files (do not modify manually)
├── assets/               # Uncompiled static assets
│   ├── scss/             # Global SCSS files
│   └── images/           # Global images
├── components/           # Global components
│   ├── common/           # Common components
│   └── ui/               # UI base components
├── composables/          # Composables
├── layouts/              # Layout components
├── middleware/           # Route middleware
├── modules/              # Nuxt modules
├── pages/                # Page components
├── public/               # Static files (copied directly to root)
├── server/               # API routes
│   ├── middleware/       # Server middleware
│   └── api/              # API endpoints
├── stores/               # State management
├── types/                # TypeScript type definitions
├── utils/                # Utility functions
├── app.vue               # Root component
├── nuxt.config.ts        # Nuxt configuration
└── tsconfig.json         # TypeScript configuration

Engineering Toolchain

Advanced Nuxt Configuration

Complete nuxt.config.ts Example:

export default defineNuxtConfig({
  // Module configuration
  modules: [
    '@nuxtjs/tailwindcss',
    '@pinia/nuxt',
    '@nuxtjs/i18n'
  ],

  // Build configuration
  build: {
    transpile: ['@vueuse/core'],
    babel: {
      presets({ isServer }) {
        return [
          [
            require.resolve('@nuxt/babel-preset-app'),
            {
              buildTarget: isServer ? 'server' : 'client',
              corejs: { version: 3 }
            }
          ]
        ]
      }
    }
  },

  // Pinia configuration
  pinia: {
    autoImports: [
      'defineStore',
      ['defineStore', 'definePiniaStore']
    ]
  },

  // Internationalization configuration
  i18n: {
    locales: ['en', 'zh'],
    defaultLocale: 'en',
    strategy: 'prefix_except_default'
  },

  // Runtime configuration
  runtimeConfig: {
    public: {
      apiBase: process.env.API_BASE || '/api',
      appVersion: '1.0.0'
    },
    private: {
      apiSecret: process.env.API_SECRET
    }
  },

  // Environment variable type hints
  typescript: {
    strict: true,
    typeCheck: true
  }
})

Webpack Advanced Configuration

Custom Webpack Configuration:

// nuxt.config.ts
export default defineNuxtConfig({
  vite: {
    build: {
      rollupOptions: {
        output: {
          manualChunks(id) {
            if (id.includes('node_modules')) {
              return 'vendor'
            }
            if (id.includes('components')) {
              return 'components'
            }
          }
        }
      }
    },
    plugins: [
      // Custom Vite plugin
      {
        name: 'custom-plugin',
        transform(code, id) {
          if (id.endsWith('.vue')) {
            return code.replace(/<template>/, '<template><div class="custom-wrapper">')
          }
        }
      }
    ]
  },

  // Webpack configuration (traditional build)
  build: {
    extend(config, { isClient, isServer }) {
      // Client-specific configuration
      if (isClient) {
        config.optimization.splitChunks = {
          chunks: 'all',
          cacheGroups: {
            vendors: {
              test: /[\\/]node_modules[\\/]/,
              priority: -10
            }
          }
        }
      }

      // Add custom loader
      config.module.rules.push({
        test: /\.custom$/,
        loader: '~/loaders/custom-loader.js'
      })
    },

    // Externalize dependencies
    externals: {
      jquery: 'jQuery'
    }
  }
})

TypeScript Integration

tsconfig.json Configuration:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Node",
    "strict": true,
    "jsx": "preserve",
    "sourceMap": true,
    "resolveJsonModule": true,
    "esModuleInterop": true,
    "lib": ["ES2020", "DOM"],
    "baseUrl": ".",
    "paths": {
      "~/*": ["./*"],
      "@/*": ["./*"]
    },
    "types": ["@types/node", "@nuxt/types"]
  },
  "exclude": ["node_modules"]
}

Type Enhancement Example:

// types/index.d.ts
declare module '*.vue' {
  import { DefineComponent } from 'vue'
  const component: DefineComponent<{}, {}, any>
  export default component
}

// Extend Nuxt types
declare module '@nuxt/schema' {
  interface NuxtConfig {
    myCustomOption?: string
  }
  interface PublicRuntimeConfig {
    myApiUrl: string
  }
}

// Global type declarations
interface User {
  id: number
  name: string
  email: string
}

Code Style Configuration

ESLint Configuration (.eslintrc.js):

module.exports = {
  root: true,
  env: {
    browser: true,
    node: true
  },
  extends: [
    '@nuxtjs/eslint-config-typescript',
    'plugin:prettier/recommended'
  ],
  rules: {
    'vue/multi-word-component-names': 'off',
    '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
    'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'warn'
  },
  overrides: [
    {
      files: ['*.vue'],
      rules: {
        'max-len': ['error', { code: 120 }]
      }
    }
  ]
}

Prettier Configuration (.prettierrc):

{
  "semi": false,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "all",
  "printWidth": 120,
  "arrowParens": "avoid",
  "endOfLine": "auto"
}

Testing Framework Integration

Vitest Configuration (vitest.config.ts):

import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: ['./test/setup.ts'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html']
    }
  }
})

Test Example:

// test/components/Button.spec.ts
import { mount } from '@vue/test-utils'
import BaseButton from '~/components/BaseButton.vue'

describe('BaseButton', () => {
  it('renders slot content', () => {
    const wrapper = mount(BaseButton, {
      slots: {
        default: 'Click me'
      }
    })
    expect(wrapper.text()).toContain('Click me')
  })

  it('emits click event', async () => {
    const wrapper = mount(BaseButton)
    await wrapper.trigger('click')
    expect(wrapper.emitted('click')).toBeTruthy()
  })
})

Performance Optimization and Deployment

Code Splitting and Lazy Loading

Dynamic Component Import:

<script setup>
const HeavyComponent = defineAsyncComponent(() =>
  import('~/components/HeavyComponent.vue')
)
</script>

<template>
  <client-only>
    <HeavyComponent v-if="showComponent" />
  </client-only>
</template>

Route-Level Code Splitting:

// pages/products/_id.vue
<script setup>
// Automatic code splitting
const product = await useFetch(`/api/products/${route.params.id}`)
</script>

Third-Party Library Splitting:

// nuxt.config.ts
export default defineNuxtConfig({
  build: {
    transpile: ['lodash-es'], // Bundle lodash-es separately
    splitChunks: {
      layouts: true,
      pages: true,
      commons: true
    }
  }
})

Resource Compression and Caching

Gzip Compression Configuration:

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    compressPublicAssets: {
      gzip: {
        level: 6
      },
      brotli: {
        quality: 11
      }
    }
  }
})

CDN Configuration Example:

// nuxt.config.ts
export default defineNuxtConfig({
  app: {
    cdnURL: process.env.CDN_URL || 'https://cdn.example.com'
  },
  nitro: {
    publicAssets: [
      { dir: '~/assets/static', baseURL: '/static' }
    ],
    prerender: {
      crawlLinks: true,
      routes: ['/', '/about']
    }
  }
})

Cache Strategy:

# HTTP response header example
Cache-Control: public, max-age=31536000, immutable
ETag: "abc123"
Last-Modified: Wed, 21 Oct 2022 07:28:00 GMT

SSR and SSG Optimization

Hybrid Rendering Configuration:

// nuxt.config.ts
export default defineNuxtConfig({
  ssr: true,
  target: 'static', // Default SSG
  nitro: {
    prerender: {
      routes: ['/', '/about', '/contact']
    }
  },
  // Dynamic route SSR
  routes: ({ params }) => [
    { path: '/user/:id', component: 'pages/user/_id.vue', ssr: true }
  ]
})

Data Prefetching Optimization:

<script setup>
// Use useAsyncData for data prefetching
const { data } = await useAsyncData('userData', () => $fetch('/api/user'))
</script>

Incremental Static Regeneration (ISR):

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    isr: {
      // Default 30-second revalidation
      revalidate: 30
    }
  }
})

Performance Monitoring

Lighthouse CI Integration:

# .github/workflows/lighthouse.yml
name: Lighthouse Audit
on: [push]

jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: treosh/lighthouse-ci-action@v9
        with:
          urls: |
            http://localhost:3000/
            http://localhost:3000/about
          budgetPath: ./lighthouse-budget.json

Web Vitals Monitoring:

// plugins/web-vitals.ts
export default defineNuxtPlugin((nuxtApp) => {
  if (process.client) {
    import('web-vitals').then(({ getCLS, getFID, getLCP }) => {
      getCLS(console.log)
      getFID(console.log)
      getLCP(console.log)
    })
  }
})

Deployment and CI/CD

Docker Deployment Configuration:

# Dockerfile
FROM node:16-alpine

WORKDIR /app
COPY . .
RUN npm install
RUN npm run build

EXPOSE 3000
CMD ["npm", "start"]

CI/CD Pipeline Example:

# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]

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

Serverless Deployment:

// serverless.yml
service: nuxt-ssr

provider:
  name: aws
  runtime: nodejs16.x
  memorySize: 512
  timeout: 10

functions:
  render:
    handler: handler.render
    events:
      - http:
          path: /
          method: get
      - http:
          path: /{proxy+}
          method: get

plugins:
  - serverless-apigw-binary
  - serverless-domain-manager

custom:
  apigwBinary:
    types:
      - '*/*'
  customDomain:
    domainName: api.example.com
    basePath: ''
    stage: ${sls:stage}
Share your love