Create a Nuxt.js project
First, make sure you have installed Node.js and yarn or npm. Then, create a new Nuxt.js project through the command line:
yarn create nuxt-app my-nuxt-project
cd my-nuxt-project
During the creation process, you can choose whether you need options such as UI framework, preprocessor, etc., and configure them as needed.
Directory structure
Nuxt.js follows a specific directory structure, some of the key directories are as follows:
├── .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
-
.nuxt/
: This directory is automatically generated and contains compiled code. Generally, it does not need to be modified directly. -
assets/
: Stores uncompiled static resources, such as CSS, JavaScript, and images. Nuxt.js will process these resources during the build. -
components/
: Stores custom Vue components that can be reused in different parts of the application. -
layouts/
: Defines the layout of the page. There can be a default layout or multiple specific layouts. -
pages/
: Each file corresponds to a route, and the file name is the route name. Dynamic routes are represented by square brackets []. -
middleware/
: Place custom middleware, which can execute logic before and after page rendering. -
plugins/
: Custom entry file for Vue.js plugins. -
static/
: Directly copied to the build output directory without any processing, often used to store robots.txt or favicon.ico, etc. -
store/
: Vuex state management directory, storing actions, mutations, getters and the entry file of the entire store. -
nuxt.config.js
: Nuxt.js configuration file, used to customize project settings. -
package.json
: Project dependencies and script configuration. -
yarn.lock
ornpm.lock
: Record the exact version of project dependencies to ensure dependency consistency in different environments.
Page rendering
Create an index.vue
file in the pages/
directory. This is the homepage of the application:
<!-- pages/index.vue -->
<template>
<div>
<h1>Hello from Nuxt.js SSR</h1>
<p>{{ message }}</p>
</div>
</template>
<script>
export default {
data() {
return {
message: 'This content is server-rendered!'
};
},
asyncData() {
// Here you can get data on the server side
// The returned data will be used as the default value of data
return { message: 'Data fetched on server' };
}
};
</script>
The process of Nuxt.js page rendering is divided into two main stages: server-side rendering (SSR) and client-side rendering (CSR). Here are the detailed steps of Nuxt.js page rendering:
Initialization:
The user enters the URL in the browser and sends a request to the server.
After the server receives the request, it starts processing.
Route resolution:
Nuxt.js uses the routes
configuration in nuxt.config.js
(if it exists) or automatically generates routes from the pages/ directory.
The corresponding page file is identified, such as pages/index.vue
or pages/about.vue
.
Data prefetching:
Nuxt.js looks for asyncData
or fetch
methods in the page component (if they exist).
These methods will run on the server side to get data from APIs or other data sources.
After the data is obtained, it will be serialized and injected into the page template.
Template rendering:
Nuxt.js uses Vue.js's rendering engine to convert components and pre-fetched data into an HTML string.
The HTML string contains all the initial data required by the client, inlined in the <script>
tag in JSON format.
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:
<!-- pages/post/[id].vue -->
<template>
<div>
<h1>{{ post.title }}</h1>
<p>{{ post.content }}</p>
</div>
</template>
<script>
export default {
async asyncData({ params, $axios }) {
const response = await $axios.$get(`/api/posts/${params.id}`);
return { post: response.post };
}
};
</script>
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:
<!-- layouts/default.vue -->
<template>
<div>
<header>
<nav>
<!-- Navigation links, etc. -->
</nav>
</header>
<main>
<nuxt /> <!-- The page content will be inserted here -->
</main>
<footer>
<!-- Bottom information, etc. -->
</footer>
</div>
</template>
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:
<template>
<form @submit.prevent="submitForm">
<input v-model="email" name="email" v-validate="'required|email'"/>
<span>{{ errors.first('email') }}</span>
<button type="submit">Submit</button>
</form>
</template>
<script>
export default {
data() {
return {
email: ''
};
},
methods: {
submitForm() {
this.$validator.validateAll().then(result => {
if (result) {
// Verify success logic
} else {
// Validation failure logic
}
});
}
}
};
</script>
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
<nuxt-child :lazy="true">
or<component :is="...">
combined with async components to achieve this.Optimize resources:
Images: Use the correct format (such as WebP), compress images, use lazy loading (
<img :src="..." :loading="lazy">
), 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.
Top comments (0)