0. Why笙各?
- 前后端分離開發(fā),后端提供api前端獨(dú)立建工程邦鲫,相信大家都是這么干的灸叼。但這種方式有個(gè)弊端:在部署上線時(shí),需要為前端頁面新開一個(gè)端口庆捺,而且古今,開發(fā)期間兩個(gè)編輯器切來切去總感覺很煩。
- 如果你和我一樣滔以,前后端同時(shí)開發(fā)捉腥,并且使用
dotnet core + Vue
技術(shù)棧。我覺得本文比較適合你你画,這里介紹了如何使用dotnet2x與最新的Vue Cli3無縫集成抵碟,進(jìn)行行云流水般的開發(fā)。 - 如果你依然喜歡使用
dotnet mvc + Razor
撬即, 本文不適合你立磁。不過,看看下面的代碼剥槐,我真心覺得沒幾個(gè)人再會(huì)用這樣的語法寫工程了唱歧。
//.cshtml代碼片段截取
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="Id" />
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Info" class="control-label"></label>
<textarea class="form-control" rows="15" asp-for="Info"></textarea>
<span asp-validation-for="Info" class="text-danger"></span>
</div>
- 如果你
Typescript
很熟練,本文既適合也不適合你粒竖,官方有現(xiàn)成的TS模板開箱即用颅崩,但是,VueCli3的Ts模板比官方的要好用很多蕊苗。這個(gè)你自己決定吧沿后。
下面正式開始,首先摘錄一段來自官方低沉的抱怨
As far as I’m aware, we don’t have plans to introduce Vue-specific features. This isn’t because we have anything against Vue, but rather just to limit the growth in the number of frameworks that we’re maintaining support for. The dev team only has a finite capacity for handling third-party concepts, and last year we made the strategic choice to focus on only Angular and React.
--我們暫時(shí)沒有針對(duì)Vue開發(fā)項(xiàng)目模板的計(jì)劃朽砰,這并不是說我們對(duì)Vue有什么偏見尖滚,而是我們...tm的實(shí)在是太忙了喉刘,顧不過來啊兄嘚...我們暫時(shí)只有 Angular 和React的,你們先將就著用吧...
本文所有內(nèi)容使用vs code
編輯器+控制臺(tái)完成
1. 找個(gè)合適的地方漆弄,運(yùn)行dotnet new
建項(xiàng)目
注意睦裳,官方并沒有現(xiàn)成的Vue模板(官方目前是TypeScript版本的),所以我們使用react模板撼唾,然后無偶們一步步修改成Vue:
dotnet new react
2. 開始修改
- Startup.cs文件中,將目標(biāo)文件夾改為dist:
// configuration.RootPath = "ClientApp/build";
configuration.RootPath = "ClientApp/dist";
- 移去45行的https設(shè)置廉邑,否則還要設(shè)置證書
//app.UseHttpsRedirection();
- 根據(jù)開頭那段話,官方?jīng)]有包倒谷,我們需要安裝一個(gè)第三方包:
dotnet add package VueCliMiddleware
- 繼續(xù)回到Startup文件蛛蒙,
using VueCliMiddleware;
將react替換為vue(64行附近):
//spa.UseReactDevelopmentServer(npmScript: "start");
spa.UseVueCli(npmScript: "serve", port: 8080);
- 現(xiàn)在, 你的工程看起來是這個(gè)樣子渤愁,接下來牵祟,刪除
ClienApp
文件夾下的所有內(nèi)容(保留ClientApp文件夾)
image.png
3. 建立客戶端項(xiàng)目
下一步自然是在ClientApp
文件夾下使用好評(píng)如潮的VueCli3
建立項(xiàng)目了。but wait猴伶, vue不允許使用首字母大寫的命名方式课舍,所以我們還是需要先建立一個(gè)client-app
項(xiàng)目,然后把所有內(nèi)容移動(dòng)到到ClientApp
文件夾下他挎。
經(jīng)過一番探索筝尾,可以改這個(gè)文件夾的,假定我們想把客戶端建立在admin文件夾下:
- 在項(xiàng)目根文件夾下办桨,使用
vue ui
或者vue create admin
建立項(xiàng)目筹淫。 - 打開.net 項(xiàng)目配置文件csproj,將這里改一下:
<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
<IsPackable>false</IsPackable>
<!--改這里-->
<SpaRoot>admin\</SpaRoot>
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
</PropertyGroup>
- 打開Startup.cs, 將所有ClientApp替換為admin
services.AddSpaStaticFiles (configuration =>
{
configuration.RootPath = "admin/dist";
});
//...
app.UseSpa (spa =>
{
spa.Options.SourcePath = "admin";
if (env.IsDevelopment ())
{
spa.UseVueCli (npmScript: "serve", port : 8080);
}
});
- 檢查項(xiàng)目.csproj設(shè)置呢撞,已有條目覆蓋损姜,沒有條目添加(這一步必須要做,否則將來發(fā)布時(shí)會(huì)有問題)
<PropertyGroup>
<!-- Typescript/Javascript Client Configuration -->
<SpaRoot>admin\</SpaRoot>
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
</PropertyGroup>
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build">
<!-- Build Target: Ensure Node.js is installed -->
<Exec Command="node --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." />
</Target>
<Target Name="DebugEnsureNpm" AfterTargets="DebugEnsureNodeEnv">
<!-- Build Target: Ensure Node.js is installed -->
<Exec Command="npm --version" ContinueOnError="true">
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
</Exec>
</Target>
<Target Name="EnsureNodeModulesInstalled" BeforeTargets="Build" Inputs="package.json" Outputs="packages-lock.json">
<!-- Build Target: Restore NPM packages using npm -->
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" />
</Target>
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">
<!-- Build Target: Run webpack dist build -->
<Message Importance="high" Text="Running npm build..." />
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build" />
<!-- Include the newly-built files in the publish output -->
<ItemGroup>
<DistFiles Include="$(SpaRoot)dist\**" />
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)">
<RelativePath>%(DistFiles.Identity)</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</ResolvedFileToPublish>
</ItemGroup>
</Target>
That's All...??
項(xiàng)目根目錄下運(yùn)行dotnet run
, 打開localhost:8080
, 熟悉的Vue啟動(dòng)界面出現(xiàn)了殊霞。這一切簡直不要太美好摧阅。感謝開源社區(qū)。
測試一下看看:
- 首先hotload:沒問題绷蹲,編輯器修改瀏覽器自動(dòng)刷新
- 然后測試axios:
額外bb一下:強(qiáng)推大家使用vue ui棒卷,真是好用的不得了????,插件祝钢、依賴比规、項(xiàng)目生成運(yùn)行全部用面板操作,再不用控制臺(tái)了
干貨時(shí)刻
1. 獨(dú)立前端拦英,加快開發(fā)速度
我們一般習(xí)慣使用dotnet watch run
跑程序蜒什,上面的配置就有一個(gè)問題:每次變化,不管是前端還是后端疤估,都要全部重新編譯一遍灾常,很快你就會(huì)發(fā)現(xiàn)速度不能忍了...霎冯。如果能像前后端獨(dú)立開發(fā)那樣,前端變動(dòng)了只更新前端岗憋,后端只更新后端就好了肃晚,其實(shí)锚贱,只需要修改一個(gè)地方就能實(shí)現(xiàn):
在Startup.cs里仔戈,修改一處地方:
app.UseSpa(spa =>
{
spa.Options.SourcePath = "admin";
if (env.IsDevelopment())
{
spa.UseProxyToSpaDevelopmentServer("http://localhost:8080");
//spa.UseVueCli(npmScript: "serve", port: 8080);
}
});
8080端口就是你vue項(xiàng)目的前端端口
設(shè)置好了以后
- 用vscode打開前端文件夾admin,然后就跟以前一樣
yarn serve
; - visual studio中拧廊,運(yùn)行控制臺(tái)命令
dotnet watch run
- 瀏覽器中打開
http://localhost:5000
, 注意打開的是后端端口
然后你就會(huì)發(fā)現(xiàn)监徘,前端后端分離了,爽歪歪~~
2. 使用axios
我們首先進(jìn)入clientapp目錄吧碾,運(yùn)行vue ui
,然后找到vue-cli-plugin-axios
插件凰盔,并安裝
安裝完畢后vue cli會(huì)自動(dòng)配置插件,不用手動(dòng)引入倦春。
打開plugins/axios.js文件户敬,我們看到vue已經(jīng)自動(dòng)將axios注冊(cè)成了Vue全局組件:
Plugin.install = function(Vue, options) {
Vue.axios = _axios;
window.axios = _axios;
Object.defineProperties(Vue.prototype, {
axios: {
get() {
return _axios;
}
},
$axios: {
get() {
return _axios;
}
},
});
};
Vue.use(Plugin)
在后臺(tái)Controller文件夾下,我們新建一個(gè)HelloWorldController.cs
, get時(shí)返回一個(gè)字符串列表:
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
[Route("api/[controller]")]
public class HelloWorldController : Controller
{
[HttpGet]
public IActionResult Index()
{
var data = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
return Json(data);
}
}
下面我們?cè)谇芭_(tái)的About.vue
中測試一下:
<template>
<div class="about">
<h1>This is an about page</h1>
<ul>
<li v-for="i in items" :key="i">{{i}}</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
items: []
};
},
created() {
this.axios.get("api/helloworld").then(res => {
this.items = res.data;
});
}
};
</script>
運(yùn)行報(bào)錯(cuò)睁本,我們還需要進(jìn)一步設(shè)置才能愉快玩耍:
- 修改Startup.cs文件尿庐,刪除默認(rèn)路由(沒什么用了),重新定義api路由呢堰。注意現(xiàn)在客戶端在同一個(gè)域下抄瑟,所以不需要啟用跨域。這也是這種模式的最大好處⊥魈郏現(xiàn)在chrome的跨域檢測越來越嚴(yán)格了皮假。
//Configure方法:
//不需要啟用跨域,不需要啟用跨域骂维,不需要啟用跨域
//app.UseCors();
//定義api路由:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "api",
template: "api/{controller}/{action=Index}/{id?}");
});
頁面顯示正確惹资,axios配制成功!
3. 使用靜態(tài)文件服務(wù)器注意事項(xiàng)(20190702更新)
使用了spa后航闺,如果想同時(shí)使用靜態(tài)服務(wù)褪测,那么Startup中的聲明順序非常重要,應(yīng)該是這樣的順序:
//1
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "Resources")),
RequestPath = "/resources",
EnableDirectoryBrowsing = true
});
//2
app.UseSpaStaticFiles();
//3
app.UseSpa(spa =>
{
spa.Options.SourcePath = "admin";
if (env.IsDevelopment())
{
//spa.UseProxyToSpaDevelopmentServer("http://localhost:8080");
spa.UseVueCli(npmScript: "serve", port: 8080);
}
});
否則靜態(tài)資源無法訪問
發(fā)布
- 發(fā)布時(shí)按照正常的IIS流程来颤,右鍵項(xiàng)目publish發(fā)布就行汰扭,發(fā)布后出現(xiàn)問題,可以在項(xiàng)目目錄下運(yùn)行
dotnet 你的項(xiàng)目名.dll
查看福铅。 -
若問題找不到萝毛,建議選擇“獨(dú)立”發(fā)布模式,這個(gè)模式其實(shí)就是把你現(xiàn)在用的.net core打包進(jìn)發(fā)布項(xiàng)目中滑黔,從而不用管目標(biāo)機(jī)器的環(huán)境依賴笆包。
image.png -
發(fā)布完畢后你的客戶端項(xiàng)目將出現(xiàn)在最終文件中
image.png
這時(shí)直接訪問iis里設(shè)置的地址和端口就能訪問了 - 注意环揽,前端項(xiàng)目axios的請(qǐng)求端口一定要和iis的端口保持一致,
VUE_APP_BACKEND_BASEURL=http://localhost
VUE_APP_BACKEND_PORT=8848
這里是我的.env文件設(shè)置庵佣。不同于別的項(xiàng)目歉胶,這個(gè)env在發(fā)布后是看不見的,所以這里一定要設(shè)置好巴粪。
下面是我的axios設(shè)置:
let config = {
baseURL: configs.backendUrls.apiUrl,
timeout: 60 * 1000 // Timeout
};
const _axios = axios.create(config);
這樣每次axios都請(qǐng)求8848端口通今,就不存在跨域的問題了
寫在最后:
- 注意,安裝插件肛根、依賴時(shí)一定要確保在客戶端文件夾下進(jìn)行
- 在工程根目錄運(yùn)行
dotnet watch run
獲得前后端同時(shí)刷新無縫開發(fā)體驗(yàn)辫塌,??