VUE项目部署IIS服务器手册

IIS部署Vue项目完整手册

📋 目录

  1. 基础概念
  2. 准备工作
  3. Vue项目构建
  4. web.config详解
  5. IIS部署步骤
  6. 不同场景配置
  7. 常见问题
  8. 实用配置模板

基础概念

Vue单页应用(SPA)工作原理

重要理解:Vue项目是单页应用,这意味着:

  • 物理层面 :整个项目只有一个HTML文件(index.html

  • 逻辑层面:用户看到多个"页面",实际上是JavaScript动态切换内容

  • 路由处理:所有路由跳转都由Vue Router在前端处理

    当用户直接访问 http://yoursite.com/about 时:

    1. 没有web.config:

      复制代码
      用户访问 /about → IIS寻找 about.html 文件 → 找不到 → 404错误
    2. 有web.config重写规则:

      复制代码
      用户访问 /about → IIS找不到about.html → web.config规则生效 → 返回index.html → Vue Router接管,显示about页面

    用户看到的:
    /home -> 首页
    /about -> 关于页面
    /products -> 产品页面
    /contact -> 联系页面

    实际服务器文件:
    dist/
    ├── index.html ← 只有这一个HTML文件!
    ├── css/
    ├── js/
    └── assets/

路由模式对比

路由模式 URL格式 IIS配置需求 说明
Hash模式 /#/home 无需配置 兼容性好,但URL不美观
History模式 /home 必须配置 URL美观,需要服务器支持

准备工作

环境要求

  • Node.js:版本 14+
  • Vue CLI:最新版本
  • IIS:Windows Server 2016+ 或 Windows 10+
  • URL Rewrite模块:必须安装

安装Vue CLI

bash 复制代码
# 全局安装Vue CLI
npm install -g @vue/cli

# 验证安装
vue --version

检查IIS URL Rewrite

  1. 打开IIS管理器
  2. 选择服务器节点
  3. 查看是否有"URL 重写"图标
  4. 如果没有,请下载安装:URL Rewrite 2.1

Vue项目构建

创建新项目

bash 复制代码
# 创建项目
vue create my-vue-project

# 选择配置
? Please pick a preset: 
  Default ([Vue 3] babel, eslint) 
❯ Default ([Vue 2] babel, eslint) 
  Manually select features

# 进入项目目录
cd my-vue-project

# 开发测试
npm run serve

构建生产版本

bash 复制代码
# 构建项目
npm run build

# 构建完成后会生成dist文件夹
dist/
├── index.html
├── css/
├── js/
├── img/
└── favicon.ico

配置自动生成web.config

方法1:在public文件夹中创建(推荐)

在项目的 public/ 文件夹中创建 web.config 文件,构建时会自动复制到 dist/

方法2:在vue.config.js中配置

javascript 复制代码
// vue.config.js
const { defineConfig } = require('@vue/cli-service')

module.exports = defineConfig({
  transpileDependencies: true,
  
  // 配置基础路径(如果部署在子目录)
  publicPath: process.env.NODE_ENV === 'production' ? '/myapp/' : '/',
  
  // 自定义构建过程
  configureWebpack: {
    plugins: [
      {
        apply: (compiler) => {
          compiler.hooks.emit.tapAsync('CopyWebConfig', (compilation, callback) => {
            const webConfigContent = `<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Handle History Mode and hash fallback" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>`;
            
            compilation.assets['web.config'] = {
              source: () => webConfigContent,
              size: () => webConfigContent.length
            };
            callback();
          });
        }
      }
    ]
  }
})

web.config详解

核心重写规则解析

xml 复制代码
<rule name="Handle History Mode and hash fallback" stopProcessing="true">
  <match url="(.*)" />  <!-- 1. 匹配所有URL路径 -->
  <conditions logicalGrouping="MatchAll">
    <!-- 2. 检查请求的不是真实存在的文件 -->
    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
    <!-- 3. 检查请求的不是真实存在的文件夹 -->
    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
  </conditions>
  <!-- 4. 如果以上条件都满足,重写到根目录 -->
  <action type="Rewrite" url="/" />
</rule>

翻译成人话:如果用户访问的不是真实存在的文件或文件夹,就给他返回首页(index.html),让Vue Router来处理。

执行逻辑

  1. 用户访问 /about

  2. IIS检查服务器上是否有 about.html 文件 → 没有

  3. IIS检查服务器上是否有 about/ 文件夹 → 没有

  4. 触发重写规则,返回 index.html

  5. Vue Router接管,显示about页面内容

    无论您的Vue项目有多少个界面,这个重写规则都不用变!

    因为:

    • 10个界面 → 还是只有1个index.html
    • 100个界面 → 还是只有1个index.html
    • 1000个界面 → 还是只有1个index.html

    所有界面的切换都是Vue Router在前端JavaScript中处理的。

重要概念

  • stopProcessing="true":匹配到此规则后停止处理后续规则
  • negate="true":条件取反,即"不是文件"的意思
  • {REQUEST_FILENAME}:IIS服务器变量,表示请求的文件路径

IIS部署步骤

第一步:复制文件

bash 复制代码
# 将dist文件夹内容复制到IIS目录
# 例如:C:\inetpub\wwwroot\myvueapp\

第二步:创建IIS网站

  1. 打开IIS管理器
  2. 右键"网站" → "添加网站"
  3. 配置网站信息
    • 网站名称:Vue-App
    • 物理路径:C:\inetpub\wwwroot\myvueapp
    • 端口:80 或其他可用端口
    • 主机名:(可选)

第三步:配置应用程序池

  1. 选择应用程序池
  2. 高级设置
    • .NET CLR版本:无托管代码
    • 托管管道模式:集成
    • 启用32位应用程序:False

第四步:设置权限

  1. 右键网站文件夹
  2. 属性安全
  3. 添加 IIS_IUSRS 用户
  4. 权限:读取和执行、列出文件夹目录、读取

第五步:启动网站

  1. 在IIS中选择网站
  2. 右键管理网站启动
  3. 测试访问http://localhost:端口号

不同场景配置

场景1:基础Vue项目(最常用)

适用:简单的展示型网站,前后端分离,后端API已部署

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="Handle History Mode and hash fallback" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

场景2:前后端同服务器部署

架构示例

复制代码
服务器(192.168.1.100)
├── IIS网站1 (端口80)   ← Vue前端
│   └── C:\inetpub\wwwroot\vue-app\
└── IIS网站2 (端口5000) ← .NET后端API
    └── C:\inetpub\wwwroot\api\

Vue项目API调用

javascript 复制代码
// 在Vue项目中直接调用后端API
axios.get('http://192.168.1.100:5000/api/users')

web.config:使用基础配置即可

场景3:前后端不同服务器 + API代理

架构示例

复制代码
前端服务器(192.168.1.100)    后端服务器(192.168.1.200)
├── IIS                        ├── IIS/Apache/Nginx
│   └── Vue项目                 │   └── API服务

优势:前端无需知道后端真实地址,避免跨域问题

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <!-- API代理规则 - 必须放在前面 -->
        <rule name="Proxy to API" stopProcessing="true">
          <match url="^api/(.*)" />
          <action type="Rewrite" url="http://192.168.1.200:5000/api/{R:1}" />
        </rule>
        
        <!-- Vue路由规则 -->
        <rule name="Handle History Mode and hash fallback" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

Vue项目API调用

javascript 复制代码
// 简化的API调用,会被自动代理
axios.get('/api/users')  // 实际访问:http://192.168.1.200:5000/api/users

场景4:子目录部署

部署路径http://domain.com/myapp/

vue.config.js配置

javascript 复制代码
module.exports = {
  publicPath: '/myapp/'
}

web.config配置

xml 复制代码
<rule name="Handle History Mode and hash fallback" stopProcessing="true">
  <match url="(.*)" />
  <conditions logicalGrouping="MatchAll">
    <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
    <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
  </conditions>
  <action type="Rewrite" url="/myapp/" />  <!-- 修改这里 -->
</rule>

场景5:企业级生产环境

特点:高安全性、高性能、多环境支持

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <!-- URL重写规则 -->
    <rewrite>
      <rules>
        <!-- 强制HTTPS -->
        <rule name="Redirect to HTTPS" stopProcessing="true">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="off" ignoreCase="true" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" 
                  redirectType="Permanent" />
        </rule>
        
        <!-- API代理 -->
        <rule name="API Proxy" stopProcessing="true">
          <match url="^api/(.*)" />
          <action type="Rewrite" url="https://api.company.com/{R:1}" />
        </rule>
        
        <!-- Vue路由 -->
        <rule name="Handle History Mode and hash fallback" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
    
    <!-- 安全头 -->
    <httpHeaders>
      <add name="X-Frame-Options" value="SAMEORIGIN" />
      <add name="X-Content-Type-Options" value="nosniff" />
      <add name="X-XSS-Protection" value="1; mode=block" />
      <add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains" />
      <add name="Referrer-Policy" value="strict-origin-when-cross-origin" />
      <add name="Content-Security-Policy" value="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" />
    </httpHeaders>
    
    <!-- 缓存策略 -->
    <staticContent>
      <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="31536000" />
    </staticContent>
    
    <!-- 启用压缩 -->
    <urlCompression doStaticCompression="true" doDynamicCompression="true" />
    
    <!-- 自定义错误页面 -->
    <httpErrors errorMode="Custom">
      <remove statusCode="404" subStatusCode="-1" />
      <error statusCode="404" path="/" responseMode="ExecuteURL" />
      <remove statusCode="500" subStatusCode="-1" />
      <error statusCode="500" path="/error.html" responseMode="ExecuteURL" />
    </httpErrors>
  </system.webServer>
</configuration>

常见问题

问题1:HTTP 错误 500.19 - MIME类型冲突

错误信息

复制代码
在唯一密钥属性"fileExtension"设置为".js"时,无法添加类型为"mimeMap"的重复集合项

解决方案

xml 复制代码
<!-- 方法1:移除staticContent配置(推荐) -->
<!-- 直接删除 <staticContent> 部分 -->

<!-- 方法2:先移除再添加 -->
<staticContent>
  <remove fileExtension=".js" />
  <mimeMap fileExtension=".js" mimeType="application/javascript" />
</staticContent>

问题2:页面刷新后404错误

原因:缺少URL重写规则

解决方案:确保web.config包含完整的重写规则

问题3:CSS/JS文件加载失败

原因:文件路径或权限问题

解决方案

  1. 检查文件是否完整复制
  2. 确认IIS_IUSRS权限
  3. 检查publicPath配置

问题4:API调用跨域错误

解决方案

  1. 在web.config中配置API代理
  2. 在后端API中配置CORS
  3. 使用nginx反向代理

问题5:子目录部署路径错误

解决方案

  1. 修改vue.config.js中的publicPath
  2. 修改web.config中的重写目标
  3. 确保路由配置正确

实用配置模板

模板1:开发/测试环境

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <!-- 开发API代理 -->
        <rule name="Dev API" stopProcessing="true">
          <match url="^api/(.*)" />
          <action type="Rewrite" url="http://localhost:3000/api/{R:1}" />
        </rule>
        
        <rule name="Handle History Mode and hash fallback" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
    
    <!-- 开发环境允许跨域 -->
    <httpHeaders>
      <add name="Access-Control-Allow-Origin" value="*" />
      <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" />
      <add name="Access-Control-Allow-Headers" value="Content-Type, Authorization" />
    </httpHeaders>
  </system.webServer>
</configuration>

模板2:生产环境(安全优化)

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <!-- 强制HTTPS -->
        <rule name="Force HTTPS" stopProcessing="true">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="off" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
        </rule>
        
        <!-- 生产API -->
        <rule name="Production API" stopProcessing="true">
          <match url="^api/(.*)" />
          <action type="Rewrite" url="https://api.production.com/{R:1}" />
        </rule>
        
        <rule name="Handle History Mode and hash fallback" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
    
    <!-- 安全头 -->
    <httpHeaders>
      <add name="X-Frame-Options" value="DENY" />
      <add name="X-Content-Type-Options" value="nosniff" />
      <add name="X-XSS-Protection" value="1; mode=block" />
      <add name="Strict-Transport-Security" value="max-age=31536000" />
    </httpHeaders>
    
    <!-- 缓存优化 -->
    <staticContent>
      <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="604800" />
    </staticContent>
  </system.webServer>
</configuration>

模板3:多环境切换

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <!-- 开发环境API(手动启用/禁用) -->
        <rule name="Dev API" enabled="false" stopProcessing="true">
          <match url="^api/(.*)" />
          <action type="Rewrite" url="http://localhost:3000/api/{R:1}" />
        </rule>
        
        <!-- 测试环境API -->
        <rule name="Test API" enabled="false" stopProcessing="true">
          <match url="^api/(.*)" />
          <action type="Rewrite" url="https://test-api.company.com/{R:1}" />
        </rule>
        
        <!-- 生产环境API -->
        <rule name="Prod API" enabled="true" stopProcessing="true">
          <match url="^api/(.*)" />
          <action type="Rewrite" url="https://api.company.com/{R:1}" />
        </rule>
        
        <rule name="Handle History Mode and hash fallback" stopProcessing="true">
          <match url="(.*)" />
          <conditions logicalGrouping="MatchAll">
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
          </conditions>
          <action type="Rewrite" url="/" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

部署检查清单

部署前检查

  • Vue项目构建成功(npm run build
  • dist文件夹包含所有文件
  • web.config文件已创建
  • IIS已安装URL Rewrite模块

部署时检查

  • 文件复制到正确位置
  • IIS网站配置正确
  • 应用程序池设置正确
  • 文件权限设置正确

部署后检查

  • 网站可以正常访问
  • 路由跳转正常
  • 页面刷新不出现404
  • API调用正常
  • 静态资源加载正常

性能优化建议

1. 启用压缩

xml 复制代码
<urlCompression doStaticCompression="true" doDynamicCompression="true" />

2. 设置缓存

xml 复制代码
<staticContent>
  <clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="2592000" />
</staticContent>

3. 优化构建

javascript 复制代码
// vue.config.js
module.exports = {
  productionSourceMap: false,  // 禁用source map
  configureWebpack: {
    optimization: {
      splitChunks: {
        chunks: 'all'
      }
    }
  }
}

总结

  1. 理解SPA原理:Vue是单页应用,所有路由都要重定向到index.html
  2. 核心配置不变:无论多少页面,基本的重写规则都一样
  3. 按需添加功能:根据具体需求添加API代理、安全头等配置
  4. 环境区分部署:开发、测试、生产使用不同的配置
  5. 注意执行顺序:web.config规则从上到下执行,注意stopProcessing属性

通过本手册,您可以根据具体情况快速选择合适的配置模板,实现Vue项目在IIS上的成功部署!

相关推荐
de之梦-御风12 分钟前
【Linux】 开启关闭MediaMTX服务
linux·运维·服务器
Morphlng13 分钟前
wstunnel 实现ssh跳板连接
linux·服务器·网络·ssh
黄智勇35 分钟前
xlsx-handlebars 一个用于处理 XLSX 文件 Handlebars 模板的 Rust 库,支持多平台使
前端
IT 小阿姨(数据库)1 小时前
PostgreSQL通过pg_basebackup物理备份搭建流复制备库(Streaming Replication Standby)
运维·服务器·数据库·sql·postgresql·centos
brzhang2 小时前
为什么 OpenAI 不让 LLM 生成 UI?深度解析 OpenAI Apps SDK 背后的新一代交互范式
前端·后端·架构
brzhang2 小时前
OpenAI Apps SDK ,一个好的 App,不是让用户知道它该怎么用,而是让用户自然地知道自己在做什么。
前端·后端·架构
夜月yeyue2 小时前
ART 加速器、流水线与指令预测的关系详解
linux·服务器·c语言·单片机·嵌入式硬件·性能优化·嵌入式高阶技巧
迎風吹頭髮2 小时前
UNIX下C语言编程与实践36-UNIX 时钟:系统时间、高分辨率时间与日历时间的转换与使用
服务器·c语言·unix
程序员王天3 小时前
【开发AGIC】Vue3+NestJS+DeepSeek AI作业批改系统(已开源)
vue.js·ai编程·nestjs
井柏然3 小时前
前端工程化—实战npm包深入理解 external 及实例唯一性
前端·javascript·前端工程化