重要理解: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美观,需要服务器支持 |
# 全局安装Vue CLI
npm install -g @vue/cli
# 验证安装
vue --version
# 创建项目
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
方法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 = `
`;
compilation.assets['web.config'] = {
source: () => webConfigContent,
size: () => webConfigContent.length
};
callback();
});
}
}
]
}
})
<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>
翻译成人话:如果用户访问的不是真实存在的文件或文件夹,就给他返回首页(index.html),让Vue Router来处理。
执行逻辑:
用户访问 /about
IIS检查服务器上是否有 about.html
文件 → 没有
IIS检查服务器上是否有 about/
文件夹 → 没有
触发重写规则,返回 index.html
Vue Router接管,显示about页面内容
无论您的Vue项目有多少个界面,这个重写规则都不用变!
因为:
所有界面的切换都是Vue Router在前端JavaScript中处理的。
# 将dist文件夹内容复制到IIS目录
# 例如:C:\inetpub\wwwroot\myvueapp\
Vue-App
C:\inetpub\wwwroot\myvueapp
80
或其他可用端口IIS_IUSRS
用户http://localhost:端口号
适用:简单的展示型网站,前后端分离,后端API已部署
<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>
架构示例:
服务器(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:使用基础配置即可
架构示例:
前端服务器(192.168.1.100) 后端服务器(192.168.1.200)
├── IIS ├── IIS/Apache/Nginx
│ └── Vue项目 │ └── API服务
优势:前端无需知道后端真实地址,避免跨域问题
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Proxy to API" stopProcessing="true">
<match url="^api/(.*)" />
<action type="Rewrite" url="http://192.168.1.200:5000/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>
system.webServer>
configuration>
Vue项目API调用:
// 简化的API调用,会被自动代理
axios.get('/api/users') // 实际访问:http://192.168.1.200:5000/api/users
部署路径: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>
特点:高安全性、高性能、多环境支持
<configuration>
<system.webServer>
<rewrite>
<rules>
<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>
<rule name="API Proxy" 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>
<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>
错误信息:
在唯一密钥属性"fileExtension"设置为".js"时,无法添加类型为"mimeMap"的重复集合项
解决方案:
<staticContent>
<remove fileExtension=".js" />
<mimeMap fileExtension=".js" mimeType="application/javascript" />
staticContent>
原因:缺少URL重写规则
解决方案:确保web.config包含完整的重写规则
原因:文件路径或权限问题
解决方案:
解决方案:
解决方案:
<configuration>
<system.webServer>
<rewrite>
<rules>
<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>
<configuration>
<system.webServer>
<rewrite>
<rules>
<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>
<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>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Dev API" enabled="false" stopProcessing="true">
<match url="^api/(.*)" />
<action type="Rewrite" url="http://localhost:3000/api/{R:1}" />
rule>
<rule name="Test API" enabled="false" stopProcessing="true">
<match url="^api/(.*)" />
<action type="Rewrite" url="https://test-api.company.com/{R:1}" />
rule>
<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>
npm run build
)<urlCompression doStaticCompression="true" doDynamicCompression="true" />
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="2592000" />
staticContent>
// vue.config.js
module.exports = {
productionSourceMap: false, // 禁用source map
configureWebpack: {
optimization: {
splitChunks: {
chunks: 'all'
}
}
}
}
通过本手册,您可以根据具体情况快速选择合适的配置模板,实现Vue项目在IIS上的成功部署!