Skip to content

nginx部署

历史模式Nginx

在使用 Vue 3 时,如果采用历史路由模式(history mode),你需要在 Nginx 配置中做一些额外设置,以确保刷新页面或直接访问特定 URL 时能够正确地加载 Vue 应用,而不会导致 404 错误。

Vue 3 使用 history 模式时,URL 是正常的路径(如 /about),而不是带有哈希(#)的路径(如 /about#)。但是,如果用户直接访问 /about 或刷新时,Nginx 会试图寻找实际的文件或资源,这可能导致 404 错误。

Nginx 配置

你可以通过修改 Nginx 的 location 配置来处理这个问题。下面是一个常见的配置示例,假设你的 Vue 应用已经打包并部署到 Nginx 的某个路径下(如 /usr/share/nginx/html)。

shell
server {
    listen 80;
    server_name yourdomain.com;  # 根据实际情况修改

    root /usr/share/nginx/html;  # Vue 应用的根目录
    index index.html;

    location / {
        try_files $uri $uri/ /index.html;  # 这行非常关键
    }

    # 其他的 Nginx 配置可以继续放在这里
}

nginx配置

ts
server {
    listen       80;
    server_name  39.106.41.164;

    location / {
        alias  /usr/local/nginx/html/build/;  # Vue 主应用
        index  index.html index.htm;
        try_files $uri $uri/ /index.html;
    }

    location /cooling/ {
        alias /usr/local/nginx/html/cooling/;  # 子应用或独立模块
        index index.html index.htm;
        try_files $uri $uri/ /index.html;  # 确保历史路由模式正常工作
    }

    # 其他静态资源的处理
    location /static/ {
        root /usr/local/nginx/html/;
        expires 1y;  # 设置缓存时间
        add_header Cache-Control "public, max-age=31536000";
    }
	location /static/ {
        root /usr/local/nginx/html/;
        expires 1h;  # 设置缓存时间为1小时
        add_header Cache-Control "public, max-age=3600";  # 3600秒 = 1小时
	}
    # 日志配置(可选)
    access_log  /var/log/nginx/access.log;
    error_log   /var/log/nginx/error.log;
}
ts
配置解释:
expires 1h;

设置静态资源的缓存时间为 1 小时。
add_header Cache-Control "public, max-age=3600";

max-age=3600:表示浏览器缓存时间为 3600 秒(即 1 小时)。
public:资源可以被任何缓存所缓存(如浏览器缓存、CDN 等)。


location /static/ {
    root /usr/local/nginx/html/;
    expires 1y;  # 长缓存
    add_header Cache-Control "public, max-age=31536000, immutable"; 
    # immutable 表示文件不会被修改,允许更长时间缓存
}

缓存设置

使用哈希后缀可以通过在静态资源的文件名中添加唯一的哈希值来实现(通常基于文件内容生成),确保当文件更新时,浏览器会重新请求新的文件,而不是使用旧的缓存版本。

1. 构建工具生成哈希文件名
现代前端构建工具(如 Webpack、Vite 等)可以自动为文件名添加基于内容的哈希值。

Webpack 示例
在 Webpack 配置中,可以使用 [contenthash] 来生成哈希文件名:
ts
module.exports = {
  output: {
    filename: 'js/[name].[contenthash:8].js', // 为 JS 文件添加哈希
    chunkFilename: 'js/[name].[contenthash:8].js',
    assetModuleFilename: 'assets/[name].[contenthash:8][ext]', // 其他静态资源
    path: path.resolve(__dirname, 'dist'),
    clean: true, // 清理旧文件
  },
};

Vite 示例

Vite 默认会为静态资源添加内容哈希。如果需要自定义文件名格式,可以通过 build 配置修改:

javascript复制代码import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    assetsDir: 'assets',
    rollupOptions: {
      output: {
        entryFileNames: 'js/[name].[hash].js',
        chunkFileNames: 'js/[name].[hash].js',
        assetFileNames: 'assets/[name].[hash][extname]',
      },
    },
  },
});
ts
ginx 配置问题
确保 Nginx 的 location 配置生效,并且配置没有冲突。

检查当前生效的配置:
使用以下命令检查 Nginx 的配置是否正确:

nginx -t
注意:
root 指令会将请求的 完整路径 附加到 root 路径。例如,location /static/ 对应请求 /static/logo.png 时,会查找:


/usr/local/nginx/html/cooling/static/logo.png
如果你希望 /static/ 映射为 /usr/local/nginx/html/cooling/ 的根目录而不是 static 子目录,可以改为使用 alias 指令:

location /static/ {
    alias /usr/local/nginx/html/cooling/static/;
    expires 1h;
    add_header Cache-Control "public,max-age=3600";
}
alias 将替代 location 路径,而不是直接拼接。

生产环境配置(在用)

ts
server {
        listen       80;
        server_name  39.106.41.164;

        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            alias  /usr/local/nginx/html/build/;
            index  index.html index.htm;
        }

        location /cooling/ {
            root  /usr/local/nginx/html;  # 子应用或独立模块
            index index.html index.htm;
            try_files $uri $uri/ /cooling/index.html;  # 确保历史路由模式正常工作
        }
    	location /static/ {
            alias /usr/local/nginx/html/cooling/static/;
            expires 1h;
            add_header Cache-Control "public,max-age=3600";
		}
        location /app-prod/ {
            proxy_pass http://jdgz.xwydl.com:8310; # 替换为生产环境接口地址
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

配置说明

  • location /:匹配所有请求。

  • shell
    try_files $uri $uri/ /index.html

    :这是最关键的部分。它的意思是:

    • 首先检查请求的文件路径 $uri 是否存在。
    • 如果文件不存在,检查是否存在目录 $uri/
    • 如果这两者都不存在,则返回 index.html 文件。这样,当访问 Vue 应用中的任何路径时,Nginx 会把请求转发到 index.html,然后由 Vue Router 接管并根据路由规则渲染相应的页面。

其他配置

如果你有其他需求(比如设置缓存、处理静态资源等),可以根据需要添加更多的配置。比如:

shell
location /static/ {
    root /usr/share/nginx/html;
    expires 1y;
    add_header Cache-Control "public, max-age=31536000";
}

这个配置会为静态资源(如图片、CSS、JavaScript 文件)设置缓存。

使用历史路由模式时,Nginx 的配置需要将所有请求重定向到 index.html,让 Vue Router 来处理客户端路由。只要在 location / 配置中使用 try_files $uri $uri/ /index.html,就能确保你的应用正常运行,不会因为刷新或直接访问路径而导致 404 错误。

$uri 意思

在 Nginx 配置中,$uri 代表的是请求的 URI(Uniform Resource Identifier)路径,也就是客户端请求的 URL 中的路径部分。它不包括域名和查询字符串,只包括请求的路径。例如,对于请求 http://yourdomain.com/about$uri 就是 /about

当你在 Nginx 配置中看到 try_files $uri $uri/ /index.html 时,实际上有三个尝试的路径顺序:

  1. $uri:首先检查请求的路径对应的文件是否存在。
  2. $uri/:如果路径对应的文件不存在,Nginx 会尝试查找一个以 / 结尾的目录。如果请求的是 /about,Nginx 会检查是否存在 /about/ 目录。
  3. /index.html:如果上述两个都不存在,则返回 index.html,也就是 Vue 应用的入口文件。
ts
在 Nginx 配置中,try_files $uri $uri/ /index.html; 里的 /index.html 是 相对于 root 或 alias 指定的根目录 的路径,而不是文件系统的绝对路径。

Nginx root配置

ts
location / {
    alias /usr/local/nginx/html/build/;
    index index.html index.htm;
}

location /cooling/ {
    root /usr/local/nginx/html;  # 使用 root 代替 alias,便于路径处理
    index index.html index.htm;
    try_files $uri $uri/ /cooling/index.html;  # 转发到 index.html 以支持 SPA 路由
}

解释:

  1. 使用 root 替代 alias root 指令通常用于处理 URL 路径匹配(相对于文档根目录),而 alias 用于显式的路径映射。在这种情况下,使用 root 可以更好地处理 /cooling/ 路径的匹配。
  2. 调整 try_files 指令: 将回退路径改为 /cooling/index.html,确保 SPA 路由在 /cooling 路径下正常工作。

静态资源打包根路径

Vue 项目打包时默认的静态资源打包时,根路径要和nginx配置的跟路径一致,这样才不会出现404

根据你的描述,问题的根本原因在于打包后的静态资源路径与实际部署路径不匹配。你访问的是 http://39.106.41.164/cooling/,但静态资源却被解析成了 http://39.106.41.164/assets/,显然缺少了 /cooling/ 前缀。

原因分析

  1. 静态资源路径问题
    • Vue 项目打包时默认的静态资源路径是相对于根路径 / 的,导致静态资源被解析为 http://39.106.41.164/assets/...
    • 而在子路径 /cooling/ 部署时,静态资源路径应该以 /cooling/ 为前缀。
  2. 解决思路
    • 需要调整 Vue 项目打包时的 base(或 publicPath),使得静态资源路径包含 /cooling/ 前缀。

解决方案

  1. 修改 Vue 项目配置

Vite 项目

如果你使用的是 Vite,修改 vite.config.js 文件,设置 base/cooling/

javascript复制代码import { defineConfig } from 'vite';

export default defineConfig({
  base: '/cooling/', // 静态资源路径加上 /cooling/
  build: {
    outDir: 'dist', // 输出目录
  },
});

Vue CLI 项目

如果你使用的是 Vue CLI,修改 vue.config.js 文件,设置 publicPath/cooling/

javascript复制代码module.exports = {
  publicPath: '/cooling/', // 静态资源路径加上 /cooling/
  outputDir: 'dist', // 打包输出目录
};

然后重新打包项目:

bash


复制代码
npm run build

重新生成的静态资源路径会自动包含 /cooling/ 前缀。

  1. 部署打包后的文件

将打包后的文件上传到 /usr/local/nginx/html/cooling/,确保文件结构如下:

bash复制代码/usr/local/nginx/html/cooling/
├── index.html
├── assets/
    ├── index.0af6dc4c.css
    ├── index.0af6dc4c.js
  1. 修改 Nginx 配置

你的 Nginx 配置已经接近正确,只需稍作调整。关键是 try_files 的配置:

nginx复制代码location /cooling/ {
    alias /usr/local/nginx/html/cooling/;
    index index.html index.htm;
    try_files $uri $uri/ /index.html;  # 保留相对路径,重定向到 index.html
}
  1. 测试和验证

  2. 重新加载 Nginx 配置:

    bash复制代码nginx -t
    systemctl reload nginx
  3. 在浏览器中访问 http://39.106.41.164/cooling/

    • 确保页面能够正常加载。
    • 查看静态资源路径是否为 http://39.106.41.164/cooling/assets/...

如果仍然有问题

  1. 清理浏览器缓存
    • 打包后的文件名可能含有缓存版本号,确保缓存已被清理。
  2. 检查静态资源路径
    • 打开开发者工具(F12),查看网络请求的静态资源路径,确认是否以 /cooling/ 开头。
  3. 未打包静态资源
    • 如果资源路径正确但仍然返回 404,检查是否所有静态资源都被上传到了 /usr/local/nginx/html/cooling/assets/

如果问题依旧存在,请提供具体的 Nginx 配置和打包后的文件结构,我可以进一步协助你解决!

vite.config.ts配置

ts
import { defineConfig, loadEnv, type ConfigEnv } from "vite";
import { resolve } from 'path'

import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";

// https://vitejs.dev/config/
export default defineConfig((mode: ConfigEnv) => {
	// 打包

	const env = loadEnv(mode.mode, process.cwd());
	return {
		base: '/cooling/',
		plugins: [vue(), vueJsx()],
		resolve: {
			alias: {
				'@': resolve(__dirname, "src"), // 使用 @ 表示 src 目录
			},
			extensions: [".ts", ".vue", ".js", ".jsx", ".tsx"], // 导入时想要省略的扩展名列表。
		},
		css: {
			preprocessorOptions: {
				scss: {
					// 引入 var.scss 这样就可以在全局中使用 var.scss中预定义的变量了
					// 给导入的路径最后加上 ;
					// 引入全局变量文件
					additionalData: '@import "./src/styles/variables.scss";',
				},
			},
			postcss: {
				plugins: [
					{
						postcssPlugin: "internal:charset-removal",
						AtRule: {
							charset: (atRule) => {
								if (atRule.name === "charset") {
									atRule.remove();
								}
							},
						},
					},
				],
			},
		},
		// 设置代理服务器
		server: {
			proxy: {
				'/app-dev': {
					target: 'http://jdgz.xwydl.com:8310',
					changeOrigin: true,
					rewrite: (path) => path.replace(/^\/app-dev/, ''),
				},
				'/app-prod': {
					target: 'env.VITE_API_URL',
					changeOrigin: true,
					rewrite: (path) => path.replace(/^\/app-dev/, ''),
				},
				'/app-mock': {
					target: 'http://127.0.0.1:8081',
					changeOrigin: true,
					rewrite: (path) => path.replace(/^\/app-mock/, ''),
				},
			}
		},
		// 打包
		build: {
			outDir: 'dist', // 输出目录
			assetsDir: 'assets', // 静态资源目录名
			rollupOptions: {
				output: {
					manualChunks: (id) => {
						// 根据依赖拆分 chunks
						if (id.includes('node_modules')) {
							return id
								.toString()
								.split('node_modules/')[1]
								.split('/')[0]
								.toString();
						}
					},
				},
			},
		},
	};
});

生产接口配置

使用环境变量配置生产接口

在 Vue 项目中,通常通过环境变量来区分开发、测试和生产环境的接口地址。

创建或编辑 .env 文件

根据环境创建以下文件:

  • 开发环境: .env.development
  • 生产环境: .env.production

例如:

.env.development

VITE_API_BASE_URL=http://dev.example.com/api

.env.production

VITE_API_BASE_URL=https://prod.example.com/api
VITE_API_BASE_URL=https://prod.example.com/api

修改 vite.config.ts 配置

确保在 vite.config.ts 文件中正确加载环境变量。你已经使用了 loadEnv 方法,以下是修改后的代码:

ts
import { defineConfig, loadEnv, type ConfigEnv } from "vite";
import { resolve } from 'path';

import vue from "@vitejs/plugin-vue";
import vueJsx from "@vitejs/plugin-vue-jsx";

export default defineConfig((mode: ConfigEnv) => {
  const env = loadEnv(mode.mode, process.cwd()); // 加载环境变量

  return {
    base: '/cooling/', // 静态资源路径
    plugins: [vue(), vueJsx()],
    resolve: {
      alias: {
        '@': resolve(__dirname, "src"), // 路径别名
      },
    },
    server: {
      proxy: {
        '/api': {
          target: env.VITE_API_BASE_URL, // 动态加载环境变量
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, ''), // 可选:移除 "/api" 前缀
        },
      },
    },
  };
});

解释:

  • env.VITE_API_BASE_URL 根据当前环境动态加载不同的接口地址。
  • 前端在开发时可以通过 /api 调用接口,而在生产环境中,代理将请求转发到真实的接口地址。

在项目中使用接口地址

在项目代码中,可以通过 import.meta.env 获取环境变量值。例如:

创建一个 Axios 实例

ts
import axios from 'axios';

const apiClient = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL, // 动态加载接口地址
  timeout: 10000, // 超时时间
});

// 请求拦截器
apiClient.interceptors.request.use((config) => {
  // 可添加 token 或其他全局配置
  return config;
});

// 响应拦截器
apiClient.interceptors.response.use(
  (response) => response.data,
  (error) => {
    console.error('请求出错:', error);
    return Promise.reject(error);
  }
);

export default apiClient;

生产环境 Nginx 配置

在生产环境中,通常通过 Nginx 配置反向代理,将 /api 请求转发到后端接口地址。

配置示例

ts
server {
    listen 80;
    server_name example.com;

    location /cooling/ {
        alias /usr/local/nginx/html/cooling/;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    # 配置生产接口代理
    location /api/ {
        proxy_pass https://prod.example.com/api/; # 替换为生产环境接口地址
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
ts
server {
    listen 80;
    server_name example.com;

    location /cooling/ {
        alias /usr/local/nginx/html/cooling/;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    # 配置生产接口代理
    location /api/ {
        proxy_pass https://prod.example.com/api/; # 替换为生产环境接口地址
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

解释:

  • /api/ 的请求会被转发到后端接口地址 https://prod.example.com/api/
  • 静态资源请求依然会走 /cooling/ 子路径。

验证配置

  1. 本地测试:
    • 运行 npm run dev,确保代理在开发环境下正常工作。
    • 访问 http://localhost:5173/api/users,检查是否请求被正确代理。
  2. 生产环境测试:
    • 打包后,部署项目到服务器。
    • 确保 /api 请求能够被转发到生产接口地址。

VueRouter路由配置和nginx同步

确保 Vue Router 的基础路径与实际部署路径一致。如果你使用的是 createRouter,可以这样设置:

ts
import { createRouter, createWebHistory } from 'vue-router';

const router = createRouter({
  history: createWebHistory('/'), // 如果部署在根路径
  routes: [
    { path: '/', component: () => import('@/views/Login.vue') },
    { path: '/home', component: () => import('@/views/Home.vue') },
  ],
});

export default router;

如果应用部署在子路径 /cooling/,需要改为

ts
history: createWebHistory('/cooling/'),

路由重定向

路由重定向到 / 都设置为 /home

nginx配置

ts
解决方案
1. 修改 rewrite 指令范围
确保 rewrite ^/$ /website permanent; 仅影响根路径 /,而不会影响 /cooling/

将 rewrite 指令移入单独的 location 块:


location = / {
    rewrite ^/$ /website permanent;
}
这确保只有访问根路径时会被重定向到 /website/