由vite项目引起的Nginx学习

100 阅读2分钟

一直对nginx处于比较浅显的了解,趁着这次调试项目,对了解到的nginx也做一次总结。

问题背景

本地vite项目运行正常,但是部署到对应环境后,页面无法正常访问。

图片

 预计此问题是未正常请求到资源导致返回了兜底的html内容,然而请求的是js文件,得到的却是html文件,所以导致的此报错。

查看了vite.config.ts,发现当前base配置是./【相对路径】,将其修改为/【绝对路径】后,希望在本地确认一下是否正常后再部署到对应环境,就有了以下流程。

调试过程

先在本地执行对应环境的打包命令:pnpm run build:test,会在项目根目录下生成dist文件。

由于Vite中内置的Vite preview不支持代理,所以无法使用pnpm run preview直接运行dist

此时选择使用Nginx运行本地dist

在本地nginx配置中添加对此项目的代理:

# 所有行都需要 ; 结尾
...
server {
    # 监听端口号
    listen 3333;
    # 配置后在浏览器中的地址就是localhost:3333
    server_name localhost;

    # 静态文件目录【使用绝对路径】
    root E:/work/object/dist;

    index index.html;

    # 静态资源缓存
    location~* .(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|wasm)$ {
        expires 1y;
    }
    
    # API 代理
    location /api/ {
        proxy_pass http://1.1.1.1:1111;
                client_max_body_size 500m; # 允许上传最大 500MB 文件
        proxy_connect_timeout 600; # 连接超时 600 秒
        proxy_send_timeout 600;
        proxy_read_timeout 600; # 读取超时 600 秒
    }
    
    # SPA 路由支持
    location / {
        # 文件查找顺序 先找精确路径(/aa.html) 再找目录(/aa/) 最后用备用页面(回退到首页)
        try_files $uri $uri/ /index.html;
    }
    
    error_page 500 502 503 504 /50x.html;
    location = 50x.html {
        root html;
    }
}
...

重启nginx

# 进入nginx.exe所在目录

# 1. 停止Nginx
./nginx -s stop

# 2. 重新加载配置
./nginx -s reload

# 3. 检查 Nginx 进程
tasklist | findstr nginx

除了以上方法也可以在任务管理器中手动停止对应的nginx进行后,再双击nginx.exe启动

nginx正常运行后,就可以在浏览器中直接访问了http:localhost:3333。发现修改base的设置后,确实解决了上述问题。就可以正常提交代码并在对应环境部署了。

问题解析

此次问题是因为新增了一个二级路由,当以base: "./"设置访问子路由/second/page时,

当前URL为:http://localhost:3333/second/page

资源路径为:./assets/xxx.js

解析结果为:http://localhost:3333/second/assets/xxx.js

浏览器会基于当前路径解析相对路径,./assets/xxx.js被解析成了second/assets/xxx.js,而实际文件在/assets/xxx.js,所以导致资源文件404,服务器返回了index.htmlSPA fallback】,但浏览器期望js文件,因此报错Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of 'text/html'

这个问题没有在本地运行时报错,是因为vite开发服务器会动态处理所有请求,不受base配置影响。

其他补充

  • 正向代理: 

    Nginx作为客户端代理,通常用于VPN、翻墙、公司内网代理

    # 公司内网代理配置
    # 员工电脑配置代理:192.168.1.100:3128
    # 所有外网请求都通过这个代理
    server {
        listen 3128; # 常用代理端口
        server_name proxy.example.com;
        
        resolver 8.8.8.8; # DNS 服务器地址
    
        # 允许的客户端IP(安全)
        allow 192.168.1.0/24; 只允许内网
        deny all;
    
        location / { 
            proxy_pass http://$http_host$request_uri; # 动态目标
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_buffering off; # 关闭缓冲,提高性能
            # 日志
            access_log logs/proxy_access.log;
        }
    }
    
  • 反向代理:

    Nginx作为服务器代理,上面的例子就是反向代理

    localtion /api/ {
        proxy_pass http://1.1.1.1:7788; # 转给后端
    }
    
  • 负载均衡:

    可以设置自动轮询,设置权重,或者自定义

    # 多个后端,按权重分配请求
    upstream backend {
        server localhost:8080 weight=3; # 权重 3
        server localhost:8081 weight=1; # 权重 1
        server localhost:8082 backup; # 备用服务器
    }
    server {
        location /api/ {
       proxy_pass http://backend; # 使用负载均衡
        }
    }
    
  • 静态文件处理:

    server {
        root E:/work/app/dist; # 文件仓库【绝对路径】
    
        location / {
       try_files $uri $uri/ /index.html;
        }
    }
    
  • Gzip压缩

    传输前压缩,可以减少带宽

    图片、视频、PDF等如果已经压缩过,就不需要再压缩了,再次压缩效果差

    小文件压缩收益小,反而增加CPU开销

    http {
        gzip on; # 开启压缩
        
        # 压缩级别(1-9,数字越大压缩越好但越慢)
        gzip_comp_level 6;  # 推荐 6,平衡压缩率和速度
        
        # 大于 1KB 才压缩,太小压缩意义不大
        gzip_min_length 1024;
        
        # 压缩的文件类型
        gzip_types
            text/plain           # 纯文本
            text/css             # CSS
            text/javascript      # JavaScript
            application/javascript
            application/json     # JSON
            text/xml             # XML
            application/xml
            application/xml+rss
            text/html;           # HTML
        
        # 压缩缓冲区大小
        gzip_buffers 16 8k;  # 16个8KB缓冲区
        
        # 是否添加 Vary: Accept-Encoding 头
        # 告诉缓存服务器支持压缩  支持压缩的浏览器给压缩版,不支持的给原版
        gzip_vary on;  
        
        # 禁用某些浏览器的压缩(旧版 IE)
        gzip_disable "msie6";  # 禁用 IE6
    }
    
  • 缓存控制

    静态资源缓存,减少重复请求,减轻服务器压力

    第一次请求:浏览器 -> 服务器 -> 返回文件 -> 浏览器(同时缓存)

    第二次请求:浏览器 -> nginx -> 直接返回缓存

    # 静态资源(JS、CSS、图片)- 长期缓存
    location ~* .(js|css|png|jpg)$ {
        expires 1y; # 缓存1年  -1【不缓存】| epoch【不缓存】 | max【最大缓存时间10年】| 1y【一年】| 1M【一个月】...
    # 更详细的缓存规则 公共缓存,1年有效,不会改变
        add_header Cache-Control "public, max-age=31536000, immutable";
        access_log off;  # 不记录访问日志(可选)
    }
    
    # HTML文件 - 短期缓存或不缓存
    location ~* .html$ {
        expires 1h; # 缓存1小时
        add_header Cache-Control "public, max-age=3600";
    }
    
    # API响应 - 不缓存
    location /api {
        proxy_pass http://localhost:8080;
        add_header Cache-Control "no-cache, no-store, must-revalidate";
    }
    
    # 其他文件
    location / {
        try_files $uri $uri/ /index.html;
    }
    

    Cache-Control参数说明:

    指令含义比喻
    public可以被任何缓存存储公共货架,谁都能用
    private只能被浏览器缓存私人货架,只有你能用
    max-age=秒数缓存有效期(秒)保质期
    immutable文件不会改变永久不变
    no-cache需要验证才能使用每次都要检查
    no-store不缓存不放在货架上
  • HTTPS【SSL】

    server {
        listen 443 ssl; # HTTPS 端口
        server_name example.com;
    
        # SSL证书配置
        ssl_certificate /path/to/certificate.crt; # 证书文件
        ssl_certificate_key /path/to/private.key; # 私钥文件
    
    }
    
  • 限流

    防止请求过多,保护服务器

    limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
    
    location /api/ {
        limit_req zone=api_limit burst=20; # 每秒10个请求
        proxy_pass http://localhost:8080;
    }
    
  • 跨域【CORS】

    允许不同域名的前端访问API

    location /api {
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE';
        add_header Access-Control-Allow-Headers 'Content-Type,     Authorization';
    
        proxy_pass http://localhost:8080;
    }
    
  • 重定向

    URL变更,自动跳转

    # 301 永久重定向
    # 浏览器行为:记住新地址,下次直接访问;适用于域名变更或URL永久变更
    location /old {
        return 301 /new;
    }
    
    # 302 临时重定向
    # 浏览器行为:不记住,每次都询问;适用于临时维护、A/B测试
    location /temp {
        return 302 /new;
    }
    
  • 请求头修改

    location /api {
        proxy_set_header Host $host# 设置Host
        proxy_set_header X-Real-IP $remote_addr# 真实IP
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
        proxy_pass http://localhost:8080;
    }