IIS部署Vue项目完整手册
📋 目录
基础概念
Vue单页应用(SPA)工作原理
重要理解:Vue项目是单页应用,这意味着:
-
物理层面 :整个项目只有一个HTML文件(
index.html
) -
逻辑层面:用户看到多个"页面",实际上是JavaScript动态切换内容
-
路由处理:所有路由跳转都由Vue Router在前端处理
当用户直接访问
http://yoursite.com/about
时:-
没有web.config:
用户访问 /about → IIS寻找 about.html 文件 → 找不到 → 404错误
-
有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
- 打开IIS管理器
- 选择服务器节点
- 查看是否有"URL 重写"图标
- 如果没有,请下载安装: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来处理。
执行逻辑:
-
用户访问
/about
-
IIS检查服务器上是否有
about.html
文件 → 没有 -
IIS检查服务器上是否有
about/
文件夹 → 没有 -
触发重写规则,返回
index.html
-
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网站
- 打开IIS管理器
- 右键"网站" → "添加网站"
- 配置网站信息 :
- 网站名称:
Vue-App
- 物理路径:
C:\inetpub\wwwroot\myvueapp
- 端口:
80
或其他可用端口 - 主机名:(可选)
- 网站名称:
第三步:配置应用程序池
- 选择应用程序池
- 高级设置 :
- .NET CLR版本:无托管代码
- 托管管道模式:集成
- 启用32位应用程序:False
第四步:设置权限
- 右键网站文件夹
- 属性 → 安全
- 添加
IIS_IUSRS
用户 - 权限:读取和执行、列出文件夹目录、读取
第五步:启动网站
- 在IIS中选择网站
- 右键 → 管理网站 → 启动
- 测试访问 :
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文件加载失败
原因:文件路径或权限问题
解决方案:
- 检查文件是否完整复制
- 确认IIS_IUSRS权限
- 检查publicPath配置
问题4:API调用跨域错误
解决方案:
- 在web.config中配置API代理
- 在后端API中配置CORS
- 使用nginx反向代理
问题5:子目录部署路径错误
解决方案:
- 修改vue.config.js中的publicPath
- 修改web.config中的重写目标
- 确保路由配置正确
实用配置模板
模板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'
}
}
}
}
总结
- 理解SPA原理:Vue是单页应用,所有路由都要重定向到index.html
- 核心配置不变:无论多少页面,基本的重写规则都一样
- 按需添加功能:根据具体需求添加API代理、安全头等配置
- 环境区分部署:开发、测试、生产使用不同的配置
- 注意执行顺序:web.config规则从上到下执行,注意stopProcessing属性
通过本手册,您可以根据具体情况快速选择合适的配置模板,实现Vue项目在IIS上的成功部署!