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/
路由模式对比
Hash模式 | /#/home | 无需配置 | 兼容性好,但URL不美观 |
History模式 | /home | 必须配置 | URL美观,需要服务器支持 |
准备工作
环境要求
- Node.js:版本 14+
- Vue CLI:最新版本
- IIS:Windows Server 2016+ 或 Windows 10+
- URL Rewrite模块:必须安装
安装Vue CLI
# 全局安装Vue CLI
npm install -g @vue/cli
# 验证安装
vue –version
检查IIS URL Rewrite
Vue项目构建
创建新项目
# 创建项目
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
构建生产版本
# 构建项目
npm run build
# 构建完成后会生成dist文件夹
dist/
├── index.html
├── css/
├── js/
├── img/
└── favicon.ico
配置自动生成web.config
方法1:在public文件夹中创建(推荐)
在项目的 public/ 文件夹中创建 web.config 文件,构建时会自动复制到 dist/。
方法2:在vue.config.js中配置
// 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详解
核心重写规则解析
<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部署步骤
第一步:复制文件
# 将dist文件夹内容复制到IIS目录
# 例如:C:\\inetpub\\wwwroot\\myvueapp\\
第二步:创建IIS网站
- 网站名称:Vue-App
- 物理路径:C:\\inetpub\\wwwroot\\myvueapp
- 端口:80 或其他可用端口
- 主机名:(可选)
第三步:配置应用程序池
- .NET CLR版本:无托管代码
- 托管管道模式:集成
- 启用32位应用程序:False
第四步:设置权限
第五步:启动网站
不同场景配置
场景1:基础Vue项目(最常用)
适用:简单的展示型网站,前后端分离,后端API已部署
<?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调用:
// 在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 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调用:
// 简化的API调用,会被自动代理
axios.get('/api/users') // 实际访问:http://192.168.1.200:5000/api/users
场景4:子目录部署
部署路径:http://domain.com/myapp/
vue.config.js配置:
module.exports = {
publicPath: '/myapp/'
}
web.config配置:
<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 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"的重复集合项
解决方案:
<!– 方法1:移除staticContent配置(推荐) –>
<!– 直接删除 <staticContent> 部分 –>
<!– 方法2:先移除再添加 –>
<staticContent>
<remove fileExtension=".js" />
<mimeMap fileExtension=".js" mimeType="application/javascript" />
</staticContent>
问题2:页面刷新后404错误
原因:缺少URL重写规则
解决方案:确保web.config包含完整的重写规则
问题3:CSS/JS文件加载失败
原因:文件路径或权限问题
解决方案:
问题4:API调用跨域错误
解决方案:
问题5:子目录部署路径错误
解决方案:
实用配置模板
模板1:开发/测试环境
<?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 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 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. 启用压缩
<urlCompression doStaticCompression="true" doDynamicCompression="true" />
2. 设置缓存
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="2592000" />
</staticContent>
3. 优化构建
// vue.config.js
module.exports = {
productionSourceMap: false, // 禁用source map
configureWebpack: {
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
}
总结
通过本手册,您可以根据具体情况快速选择合适的配置模板,实现Vue项目在IIS上的成功部署!
评论前必须登录!
注册