最近做项目时有一个需求,就是项目中要能够同时存在传统MVC页面和SPA页面,SPA本人比较熟悉Vue,就用VueCli3做。有关.NetCore项目中使用Vue,在我的另一篇文章AspDotNet+VueCli3从零开始有详细介绍。但是那篇文章的做法无法满足今天的需求:
- MVC页面和SPA页面单独路由控制,如
/admin
即定向到spa页面。/
或/home
则定向到home view - 从spa页面发起api请求后定向到
/api
路由下 - 使用.net core的HMR而不是webpack的,两者功能重合。开发时编译两遍,速度很慢。
工程需求
- 要有一个Home界面,此界面为MVC,是网站的默认界面,从此界面可以导航到网站其它界面。
- 一个admin spa项目,放置在工程目录下单独的admin文件夹下
- 使Vue Cli3构建。
实现步骤
- 建立工程
#创建dotnet api工程
dotnet new webapi -o dotnet_mvc_spa_demo
cd dotnet_mvc_spa_demo
#根据需要自行选择模板
vue create admin
cd admin
# 这两个包是我们实现功能的关键,必须要安装
yarn add aspnet-webpack webpack-hot-middleware --dev
安装完毕后工程目录如下:
.
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Startup.cs
├── admin
│ ├── README.md
│ ├── babel.config.js
│ ├── node_modules
│ ├── package.json
│ ├── public
│ ├── src
│ ├── vue.config.js
│ └── yarn.lock
├── appsettings.Development.json
├── appsettings.json
├── dotnet_mvc_spa_demo.csproj
├── dotnet_mvc_spa_demo.csproj.user
├── dotnet_mvc_spa_demo.sln
工程设置
- 在admin目录下建立vue.config.js文件:
module.exports = {
//将webpack打包后的输出设置为工程根目录下的wwwroot/admin文件夹下
//注意,这里文件夹名要和路由地址不同,否则静态文件路由和mvc路由会有冲突
outputDir: '../wwwroot/admin_dist',
//注意这里很重要,这样才能实现url隔离,即访问admin定位到spa
baseUrl: "/admin",
chainWebpack: config => {
//不使用weboack的HMR功能
config.plugins.delete('hmr');
}
}
- 修改Startup.js文件
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Rewrite;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System.IO;
namespace dotnet_mvc_spa_demo
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
//使用.net的hmr功能,需要指定node_module位置
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions
{
HotModuleReplacement = true,
ProjectPath= Path.Combine(env.ContentRootPath, "admin"),
ConfigFile = Path.Combine(env.ContentRootPath, @"admin\node_modules\@vue\cli-service\webpack.config.js")
});
}
app.UseStaticFiles();
// 如果路径匹配到admin,则使用AdminController
app.MapWhen(x => x.Request.Path.Value.StartsWith("/admin"), builder =>
{
builder.UseMvc(routes =>
{
routes.MapSpaFallbackRoute(
name: "spa-fallback-admin",
defaults: new { controller = "Admin", action = "Index" });
});
});
// 将"/"重定向到“/home”
app.UseRewriter(new RewriteOptions().AddRewrite("/", "/Home", true));
//其余走默认的路由
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
新建Controllers
├── Controllers
│ ├── AdminController.cs
│ ├── HomeController.cs
HomeController.cs就是正常的MVC:
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
既然有Controller就需要有View,新建Views/Home文件夹:
./Views/
└── Home
└── Index.cshtml
Index.cshtml中,我们使用一个超链接到/admin
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<h1>this is home page</h1>
<p><a href="/admin">to admin spa</a></p>
</body>
</html>
AdminController.cs,只是简单的读取了wwwroot/admin文件夹下的index.html文件,这个文件是webpack打包自动生成的,不用管。
public class AdminController : Controller
{
public IActionResult Index()
{
return File("~/admin_dist/index.html", "text/html");
}
}
在项目根目录下新建wwwroot文件夹
按下F5进行测试,
这样就齐活了。
感觉这种方法比上一篇文章中介绍的方法更加灵活。开发起来也更加顺畅。