Nuxt.js实际应用:Vue.js服务器端渲染框架

创建 Nuxt.js 项目Create a Nuxt.js project

首先确保你已经安装了 Node.js 和 yarn 或 npm。然后通过命令行创建一个新的 Nuxt.js 项目:

yarn create nuxt-app my-nuxt-project
cd my-nuxt-project

在创建过程中您可以选择是否需要UI框架,预处理器等选项,并根据需要进行配置。

目录结构

**Nuxt.js 遵循特定的目录结构,一些关键目录如下:**

├── .nuxt/ # Automatically generated files, including compiled code and configuration
├── assets/ # Used to store uncompiled static resources, such as CSS, images, fonts
├── components/ # Custom Vue components
├── layouts/ # Application layout files, defining the general structure of the page
│ └── default.vue # Default layout
├── middleware/ # Middleware files
├── pages/ # Application routes and views, each file corresponds to a route
│ ├── index.vue # Default homepage
│ └── [slug].vue # Dynamic routing example
├── plugins/ # Custom Vue.js plugins
├── static/ # Static resources, will be copied to the output directory as is
├── store/ # Vuex state management file
│ ├── actions.js # Vuex actions
│ ├── mutations.js # Vuex mutations
│ ├── getters.js # Vuex getters
│ └── index.js # Vuex store entry file
├── nuxt.config.js # Nuxt.js configuration file
├── package.json # Project dependencies and scripts
└── yarn.lock # Or npm.lock, record dependency versions
  • .nu​​xt/:该目录自动生成,包含编译后的代码,一般不需要直接修改。
  • assets/:存储未编译的静态资源,例如 CSS、JavaScript 和图像。Nuxt.js 会在构建过程中处理这些资源。
  • components/:存储可以在应用程序的不同部分重复使用的自定义 Vue 组件。
  • layouts/:定义页面的布局,可以有一个默认布局,也可以有多个具体布局。
  • pages/:每个文件对应一条路线,文件名即为路线名。动态路线用方括号[]表示。
  • middleware/:放置自定义中间件,可以在页面渲染之前和之后执行逻辑。
  • plugins/:Vue.js 插件的自定义入口文件。
  • static/:直接复制到构建输出目录,不做任何处理,常用于存储 robots.txt 或 favicon.ico 等。
  • store/:Vuex 状态管理目录,存储actions、mutations、getters以及整个store的入口文件。
  • nuxt.config.js:Nuxt.js配置文件,用于自定义项目设置。
  • package.json:项目依赖和脚本配置。
  • yarn.lock或npm.lock:记录项目依赖的准确版本,保证不同环境下的依赖一致性。
  • 页面渲染

    在 pages/ 目录中创建一个 index.vue 文件。这是应用程序的主页:

    
    
    
    

    Nuxt.js 页面渲染的过程主要分为两个阶段:服务端渲染(SSR)和客户端渲染(CSR)。下面是 Nuxt.js 页面渲染的详细步骤:

    初始化:

    用户在浏览器中输入URL并向服务器发送请求。

    服务器收到请求后,开始处理。

    路线解析:

    Nuxt.js 使用 `nuxt.config.js` 中的 `routes` 配置(如果存在)或从 pages/ 目录自动生成路由。

    识别相应的页面文件,例如“pages/index.vue”或“pages/about.vue”。

    数据预取:

    Nuxt.js 在页面组件中查找“asyncData”或“fetch”方法(如果存在)。

    这些方法将在服务器端运行,以从 API 或其他数据源获取数据。

    获取到数据之后会序列化并注入到页面模板中。

    模板渲染:

    Nuxt.js 使用 Vue.js 的渲染引擎将组件和预取数据转换为 HTML 字符串。

    HTML 字符串包含客户端所需的所有初始数据,内联在`

    Return HTML:

    The server sends the generated HTML response back to the client (browser).

    Client initialization:

    After the browser receives the HTML, it begins parsing and executing inline JavaScript.

    The Nuxt.js client library (`nuxt.js`) is loaded and initialized.

    Client rendering:

    The client library takes over the rendering, the Vue.js instance is created, and the data is injected from the inline JSON into the Vue instance.

    The page completes the initial rendering and the user can see the complete page content.

    At this point, the page is interactive and the user can trigger events and navigate.

    Subsequent navigation:

    When the user navigates to other pages, Nuxt.js uses client-side routing (Vue Router) for non-refresh jumps.

    If the new page requires data, the `asyncData` or `fetch` method will run on the client, fetch the new data and update the view.

    SSG (Static Site Generation):

    Outside of development, you can use the nuxt generate command to generate static HTML files.

    Each page will be pre-rendered as a separate HTML file with all the necessary data and resources.

    Using asyncData

    The `asyncData` method is unique to Nuxt.js and allows you to pre-fetch data on the server and reuse it on the client. In the example above, we simply changed the value of message, but in a real application, you might call an API to fetch data here.

    Middleware

    Middleware (`Middleware`) is a feature that allows you to execute specific logic before and after route changes. Middleware can be used globally, at the page level, or at the layout level to handle tasks such as authentication, data preloading, route guards, etc.

    1. Global middleware

    Global middleware is configured in the nuxt.config.js file and affects all pages in the application:

    // nuxt.config.js
    export default {
    // ...
        router: {
            middleware: ['globalMiddleware1', 'globalMiddleware2'] // can be a string array
        }
    };

    Middleware files are usually located in the middleware/directory, such as middleware/globalMiddleware1.js:

    // middleware/globalMiddleware1.js
    export default function (context) {
        // context contains information such as req, res, redirect, app, route, store, etc.
        console.log('Global Middleware 1 executed');
    }

    2. Page-level middleware

    Page-level middleware only affects specific pages. Declare middleware in the page component:

    // pages/about.vue
    export default {
        middleware: 'pageMiddleware' // can be a string or a function
    };

    The corresponding middleware file is located in the middleware/directory, for example, middleware/pageMiddleware.js:

    // middleware/pageMiddleware.js
    export default function (context) {
        console.log('Page Middleware executed');
    }

    3. Layout-level middleware

    Layout-level middleware is similar to page-level, but it applies to all pages that use the layout. Declare middleware in the layout component:

    // layouts/default.vue
    export default {
        middleware: ['layoutMiddleware1', 'layoutMiddleware2']
    };

    The corresponding middleware file is located in the middleware/directory:

    // middleware/layoutMiddleware1.js
    export default function (context) {
        console.log('Layout Middleware 1 executed');
    }
    
    // middleware/layoutMiddleware2.js
    export default function (context) {
        console.log('Layout Middleware 2 executed');
    }

    Context of middleware

    The middleware function receives a context object as a parameter, which contains the following properties:

  • req (HTTP request object, valid only on the server side)
  • res (HTTP response object, valid only on the server side)
  • redirect (function used for redirection)
  • app (Vue instance)
  • route (current route information)
  • store (Vuex Store, if enabled)
  • payload (if there is data returned by asyncData)
  • Middleware can be executed sequentially, and each middleware can decide whether to continue executing the next middleware in the chain or interrupt the route through the redirect function.

    Dynamic Routing

    `Nuxt.js` supports dynamic routing, which is very useful for handling content with dynamic IDs such as blog posts, user profiles, etc. Create a dynamic routing file in the pages/ directory, such as [id].vue:

    
    
    
    

    Here [id] represents a dynamic parameter, asyncData will automatically process this parameter and get the blog post with the corresponding ID.

    **Layout**

    Layout allows you to define the common structure of global or specific pages. Create a default.vue file in the layouts/ directory:

    
    

    By default, all pages will use this layout. If you want to set a different layout for a specific page, you can specify it in the page component:

    // pages/about.vue
    export default {
      layout: 'custom' // Create custom.vue under layouts/
    };

    **Plugin and library integration**

    Nuxt.js supports Vue.js plugins, which you can configure in nuxt.config.js:

    // nuxt.config.js
    export default {
      plugins: [
        { src: '~plugins/vuetify.js', ssr: true },
        { src: '~plugins/vue-chartjs.js', mode: 'client' } // Run only on the client side
      ]
    };

    Then create the corresponding files in the `plugins/` directory, such as `vuetify.js`:

    // plugins/vuetify.js
    import Vue from 'vue';
    import Vuetify from 'vuetify';
    import 'vuetify/dist/vuetify.min.css';
    
    Vue.use(Vuetify);

    Configuration and Optimization

    Nuxt.js Configuration File (nuxt.config.js)

    `nuxt.config.js` is the main configuration file for Nuxt applications, which is used to customize the behavior of the application. Here are some commonly used configuration items:

  • mode: Set the running mode of the application. The optional values ​​are 'spa' (single-page application), 'universal' (server-side rendering) and 'static' (static generation). The default is 'universal'.
  • head: Configure the part of the page, such as title, metadata, links, etc.
  • css: Specify the global CSS file, which can be an array of file paths.
  • build: Configure the build process, such as transpile, extractCSS, extend, etc. For example, you can add a Babel plugin or adjust the Webpack configuration here.
  • router: Customize routing configuration, such as base path, mode, etc.
  • axios: Configure the axios module, including base URL, proxy settings, etc.
  • plugins: Register global Vue plugins, which can be specified to be loaded on the client or server.
  • modules: Load external modules, such as @nuxtjs/axios, @nuxtjs/proxy, etc.
  • env: Define environment variables, which will be injected into the client and server at build time.
  • // nuxt.config.js
    export default {
      // Project Name
      name: 'my-nuxt-app',
    
        // Project mode: spa, universal, static
        mode: 'universal', // Default value, supports server-side rendering
    
        // Application meta information
      head: {
        title: 'My Nuxt App',
        meta: [
          { charset: 'utf-8' },
          { name: 'viewport', content: 'width=device-width, initial-scale=1' },
          { hid: 'description', name: 'description', content: 'App description' }
        ],
        link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }]
      },
    
      // CSS Styles
      css: [
        '@/assets/css/main.css'
      ],
    
      // Routing Configuration
      router: {
        base: '/my-nuxt-app/', // The base path of the application
        extendRoutes(routes, resolve) {
          // Manually extend or modify routes
        }
      },
    
        // Build Configuration
        build: {
            transpile: [/^my-vue-component/], // Modules that need to be translated
            vendor: ['lodash'], // Public library, pre-packaged
            extractCSS: true, // Extract CSS to a separate file
            optimization: {
            splitChunks: {
                cacheGroups: {
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10,
                    filename: 'vendors.js'
                }
                }
            }
            }
        },
        // Axios configuration
        axios: {
            baseURL: process.env.BASE_URL || 'http://localhost:3000/api', // API base URL
            browserBaseURL: 'https://api.example.com' // Client API base URL
            },
    
        // Plugins
        plugins: [
            { src: '@/plugins/vue-my-plugin', ssr: false } // Asynchronously loaded plugin, ssr: false means only client loading
        ],
    
        // Modules
        modules: [
            '@nuxtjs/axios', // Install and configure axios module
            '@nuxtjs/pwa' // Install and configure PWA module
        ],
    
        // Environment variables
        env: {
            apiKey: 'your-api-key',
            apiUrl: 'https://api.example.com'
        },
    
        // Vuex Store configuration
        store: true, // Automatically create Vuex store
        loading: { color: '#3B8070' }, // Loading indicator color
    
        // Server-side middleware
        serverMiddleware: [
            { path: '/api', handler: '~/api/index.js' } // Use custom server-side middleware
        ],
    
        // Static generation configuration
        generate: {
            dir: 'dist', // Output directory
            fallback: true, // Generate 404 page for dynamic routes that are not pre-rendered
            routes: () => ['/about', '/contact'] // Pre-rendered specified routes
        }
    };

    Optimization strategy

  • Asynchronous data prefetching (asyncData/fetch): Use asyncData or fetch methods to prefetch data on the server side to reduce the burden of client rendering.
  • Code splitting: Nuxt.js automatically splits the code to ensure that the relevant code is loaded only when the route is visited.
  • Static site generation (SSG): Use the nuxt generate command to generate static HTML files, which is suitable for sites with infrequent content changes, improving loading speed and SEO friendliness.
  • Cache strategy: Use HTTP cache strategies such as ETag and Last-Modified to reduce duplicate requests.
  • Vue.js optimization: Ensure the optimization of Vue components, such as avoiding useless watchers, using v-once to reduce re-rendering, etc.
  • Image optimization: Use the correct image format (such as WebP), ensure that the image size is appropriate, and use lazy loading technology.
  • Service Worker: Integrate PWA support and use Service Worker for offline caching and push notifications.
  • Tree Shaking: Make sure your dependencies support Tree Shaking to remove unused code.
  • Analysis and Monitoring: Use nuxt build --analyze or integrate third-party tools (such as Google Lighthouse) for performance analysis to continuously monitor application performance.
  • Static Site Generation (SSG)

    Nuxt.js's static site generation (SSG) is implemented through the `nuxt generate` command. This command traverses the application's routes and generates a pre-rendered HTML file for each route, which can be directly deployed to any static file hosting service. Here are some key points about SSG:

    **1. Configuration**: In the nuxt.config.js file, you can configure the generate option to control the behavior of static generation:

    export default {
        generate: {
            dir: 'dist', // Output directory, default is dist
            fallback: true, // Generate 404 page for dynamic routes that are not pre-rendered
            routes: () => ['/about', '/contact'], // Predefined static routes
            exclude: ['/admin/*'], // Exclude certain routes
            interval: 5000, // Generation interval, in milliseconds
            concurrency: 10 // Number of concurrently generated routes
        }
    }

    **2. Generate**: Run `npm run generate` or `yarn generate` to start the static generation process. Nuxt.js will generate the corresponding HTML file according to the configuration in `generate.routes`. If not explicitly defined, it will automatically scan all files under the `pages/` directory to generate routes.

    **3. Data prefetching**: In the page component, you can use the asyncData or fetch method to prefetch data. This data will be injected into the HTML when the static page is generated, so that the page does not require additional requests when the client loads:

    // pages/about.vue
    export default {
        async asyncData({ params, $axios }) {
            const aboutInfo = await $axios.$get('/api/about')
            return { aboutInfo }
        }
    }

    **4. Middleware processing**: Server-side middleware will not be executed during the SSG process because SSG generates static files without a server environment. Therefore, if you need to execute some logic when generating, it is best to handle it in asyncData or fetch.

    **5. Deployment**: The generated static files can be deployed to any static file hosting service, such as Netlify, Vercel, GitHub Pages, or AWS S3. These services usually do not require running any server-side code, just upload the generated dist folder.

    **6. SEO Optimization**: SSG improves SEO because search engine crawlers can read pre-rendered HTML content without waiting for JavaScript to execute.

    **7. Dynamic Routes**: For dynamic routes, Nuxt.js will try to generate all possible combinations. If all possible dynamic routes cannot be predicted, they can be manually specified in generate.routes, or controlled using generate.includePaths and generate.excludePaths.

    **8. 404 Page**: Setting generate.fallback to true will generate a 404 page for dynamic routes that are not pre-rendered. When users visit these routes, Nuxt.js will try to render them on the client side.

    Run the nuxt generate command and Nuxt.js will generate static HTML files.

    Validation and Error Handling

    Validation

    Validation usually involves input validation of form data or API requests. Nuxt.js itself does not directly provide a validation library, but you can integrate third-party libraries such as Vuelidate, vee-validate, or use TypeScript for type checking.

    **Using Vee-Validate**

    **1. Installation**: First, you need to install the vee-validate library:

    npm install vee-validate

    **2. Configuration**: Add the Vue plugin configuration in nuxt.config.js:

    export default {
         plugins: [{ src: '~/plugins/vee-validate', ssr: false }]
       };

    **3. Create a plugin**: Configure Vee-Validate in plugins/vee-validate.js:

    import Vue from 'vue';
       import VeeValidate from 'vee-validate';
    
       Vue.use(VeeValidate);

    **4. Usage**: Use Vee-Validate for form validation in your component:

    
       

    Error handling

    Nuxt.js provides several ways to handle errors, including global error handling and page-specific error handling.

    **Global error handling**

  • Custom error page: Create an error.vue file in the layouts directory to customize the error page layout.
  • Capture global errors: Configure the error property in nuxt.config.js to capture global errors:
  • export default {
        error: {
            // Handling when the page does not exist
            pageNotFound({ error, store, app, env }) {
            // Handling logic
            },
            // Handling any errors
            handler(error, { error: nuxtError, store, app, env }) {
            // Handling logic
            }
        }
    };

    **Page-specific error handling**

    In the page component, you can use the try-catch structure of the asyncData or fetch method to handle errors:

    export default {
      async asyncData({ params, error }) {
        try {
          const data = await fetchSomeData(params.id);
          return { data };
        } catch (err) {
          error({ statusCode: 500, message: 'Data acquisition failed' });
        }
      }
    };

    **API request error handling**

    For API requests, if you use the @nuxtjs/axios module, you can handle errors uniformly in the request interceptor:

    // plugins/axios.js
    import axios from 'axios';
    import { toast } from '~/utils/toast';
    
    axios.interceptors.response.use(null, (error) => {
      const { status } = error.response;
      if (status === 401) {
        // Handling unauthorized errors
      } else if (status >= 500) {
        // Handling Server Errors
        toast.error('Server Error');
      }
      return Promise.reject(error);
    });
    
    export default ({ $axios }, inject) => {
      inject('axios', $axios);
    };

    Make sure to register this plugin in nuxt.config.js.

    Vue Ecosystem Integration

    Vue Router:

    Nuxt.js automatically generates a routing system for your application based on the file structure. Routing configuration usually does not need to be written manually, but can be extended through the `router` property of `nuxt.config.js`.

    Vuex:

    Nuxt.js automatically creates a Vuex store. Under the `store` directory, you can create modular state, mutations, actions, and getters. For example, create a `store/modules/users.js` file to manage user data.

    // store/modules/users.js
       export const state = () => ({
         users: []
       });
    
       export const mutations = {
         SET_USERS(state, payload) {
           state.users = payload;
         }
       };
    
       export const actions = {
         async fetchUsers({ commit }) {
           const response = await this.$axios.get('/api/users');
           commit('SET_USERS', response.data);
         }
       };

    Vue CLI:

    Nuxt.js provides its own build tool, but it is also based on Vue CLI. This means you can use command-line tools similar to Vue CLI, such as npx nuxt generate (static generation) or npx nuxt build (build application).

    Babel:

    Nuxt.js is configured with Babel by default to support the latest JavaScript features. You usually don't need to configure Babel manually unless there is a special need.

    TypeScript:

    To use TypeScript, set typescript: true in nuxt.config.js and Nuxt.js will automatically configure TypeScript support.

    ESLint:

    For code quality checking, you can install ESLint in your project and configure .eslintrc.js. Nuxt.js provides @nuxt/eslint-module plugin to simplify integration.

    // nuxt.config.js
       module.exports = {
         buildModules: [
           '@nuxt/typescript-build',
           '@nuxtjs/eslint-module' // Add ESLint integration
         ],
         eslint: {
           fix: true, // Automatically fix errors
           ignoreDuringBuilds: true // Ignore errors during build
         }
       };

    VueUse:

    VueUse is a Vue use case library that contains various practical functions. To integrate, first install `@vueuse/core`, then import and use the functions in your components.

    npm install @vueuse/core
    // In the component
       import { useCounter } from '@vueuse/core';
    
       export default {
         setup() {
           const count = useCounter(0); // Using the Counter Function
           // ...
         }
       };

    Vue plugins:

    You can register Vue plugins globally through the plugins configuration item in nuxt.config.js. For example, integrate Vue Toastify to display notifications:

    // nuxt.config.js
       export default {
         plugins: [{ src: '~plugins/toastify.js', ssr: false }]
       };
    // plugins/toastify.js
       import Vue from 'vue';
       import Toastify from 'toastify-js';
    
       Vue.use(Toastify);

    Workflow using Nuxt.js

    Nuxt.js provides a complete workflow for development, building, and deployment. Use the nuxt command to start the development server, nuxt build for production building, nuxt start to start the production server, and nuxt generate to generate static files.

    Performance optimization

  • Static generation (SSG): Use the nuxt generate command to generate pre-rendered HTML files, which can greatly improve the first screen loading speed and is SEO-friendly.
  • Code splitting: Nuxt.js will perform code splitting by default, dividing the application into multiple small blocks, and only load the code required by the current page, reducing the initial loading volume.
  • Lazy loading: For large applications, you can consider lazy loading components or modules and only load them when needed. You can use or combined with async components to achieve this.
  • Optimize resources:
  • Images: Use the correct format (such as WebP), compress images, use lazy loading (), or use nuxt-image or nuxt-picture components.
  • CSS: Extract CSS to a separate file and reduce inline styles.
  • JS: Use Tree Shaking to remove unused code.
  • Asynchronous data prefetching: Use asyncData or fetch methods to preload data to ensure that the data is ready before rendering.
  • Server-side caching: Use the nuxt-ssr-cache module to cache the results of server-side rendering and reduce unnecessary API calls.
  • HTTP caching: Set the correct cache headers (such as Cache-Control) and use the browser to cache static resources.
  • Route guards: Use route guards such as beforeRouteEnter to avoid loading data when it is not needed.
  • Reduce HTTP requests: Combine multiple CSS and JS files to reduce the number of HTTP requests.
  • Optimize API performance: Optimize the backend interface, reduce response time, and use paging, filtering, and caching strategies.
  • Leverage CDN: Host static resources on CDN to speed up loading for global users.
  • Optimize Vuex state management: Avoid unnecessary calculated properties and listeners to reduce the overhead of state changes.
  • Performance audit: Use Lighthouse, Chrome DevTools, or other performance audit tools to regularly check application performance and make improvements based on the report.
  • Service Worker: If applicable, integrate PWA features and use Service Worker for offline caching and resource preloading.
  • Module optimization: Choose high-performance third-party modules and make sure they are optimized for SSR.