AspDotNet+VueCli3从零开始

0. Why?

  • 前后端分离开发,后端提供api前端独立建工程,相信大家都是这么干的。但这种方式有个弊端:在部署上线时,需要为前端页面新开一个端口,而且,开发期间两个编辑器切来切去总感觉很烦。
  • 如果你和我一样,前后端同时开发,并且使用dotnet core + Vue技术栈。我觉得本文比较适合你,这里介绍了如何使用dotnet2x与最新的Vue Cli3无缝集成,进行行云流水般的开发。
  • 如果你依然喜欢使用dotnet mvc + Razor, 本文不适合你。不过,看看下面的代码,我真心觉得没几个人再会用这样的语法写工程了。
//.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很熟练,本文既适合也不适合你,官方有现成的TS模板开箱即用,但是,VueCli3的Ts模板比官方的要好用很多。这个你自己决定吧。

下面正式开始,首先摘录一段来自官方低沉的抱怨

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.

--我们暂时没有针对Vue开发项目模板的计划,这并不是说我们对Vue有什么偏见,而是我们...tm的实在是太忙了,顾不过来啊兄嘚...我们暂时只有 Angular 和React的,你们先将就着用吧...

本文所有内容使用vs code编辑器+控制台完成

1. 找个合适的地方,运行dotnet new建项目

注意,官方并没有现成的Vue模板(官方目前是TypeScript版本的),所以我们使用react模板,然后无偶们一步步修改成Vue:

dotnet new react

2. 开始修改

  • Startup.cs文件中,将目标文件夹改为dist:
// configuration.RootPath = "ClientApp/build";
 configuration.RootPath = "ClientApp/dist";
  • 移去45行的https设置,否则还要设置证书
//app.UseHttpsRedirection();
  • 根据开头那段话,官方没有包,我们需要安装一个第三方包:dotnet add package VueCliMiddleware
  • 继续回到Startup文件,
    using VueCliMiddleware;
    将react替换为vue(64行附近):
  //spa.UseReactDevelopmentServer(npmScript: "start");
  spa.UseVueCli(npmScript: "serve", port: 8080);
  • 现在, 你的工程看起来是这个样子,接下来,删除ClienApp文件夹下的所有内容(保留ClientApp文件夹)
    image.png

3. 建立客户端项目

下一步自然是在ClientApp文件夹下使用好评如潮的VueCli3建立项目了。but wait, vue不允许使用首字母大写的命名方式,所以我们还是需要先建立一个client-app项目,然后把所有内容移动到到ClientApp文件夹下。
经过一番探索,可以改这个文件夹的,假定我们想把客户端建立在admin文件夹下:

  1. 在项目根文件夹下,使用vue ui或者vue create admin建立项目。
  2. 打开.net 项目配置文件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>
  1. 打开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);
   }
});
  1. 检查项目.csproj设置,已有条目覆盖,没有条目添加(这一步必须要做,否则将来发布时会有问题)
 <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...👌

项目根目录下运行dotnet run, 打开localhost:8080, 熟悉的Vue启动界面出现了。这一切简直不要太美好。感谢开源社区。

测试一下看看:

  • 首先hotload:没问题,编辑器修改浏览器自动刷新
  • 然后测试axios:

额外bb一下:强推大家使用vue ui,真是好用的不得了👏🏻,插件、依赖、项目生成运行全部用面板操作,再不用控制台了

干货时刻

1. 独立前端,加快开发速度

我们一般习惯使用dotnet watch run跑程序,上面的配置就有一个问题:每次变化,不管是前端还是后端,都要全部重新编译一遍,很快你就会发现速度不能忍了...。如果能像前后端独立开发那样,前端变动了只更新前端,后端只更新后端就好了,其实,只需要修改一个地方就能实现:
在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项目的前端端口
设置好了以后

  1. 用vscode打开前端文件夹admin,然后就跟以前一样yarn serve;
  2. visual studio中,运行控制台命令dotnet watch run
  3. 浏览器中打开http://localhost:5000, 注意打开的是后端端口

然后你就会发现,前端后端分离了,爽歪歪~~

2. 使用axios

我们首先进入clientapp目录,运行vue ui,然后找到vue-cli-plugin-axios插件,并安装


安装完毕后vue cli会自动配置插件,不用手动引入。
打开plugins/axios.js文件,我们看到vue已经自动将axios注册成了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)

在后台Controller文件夹下,我们新建一个HelloWorldController.cs, get时返回一个字符串列表:

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);
    }
}

下面我们在前台的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>

运行报错,我们还需要进一步设置才能愉快玩耍:

  1. 修改Startup.cs文件,删除默认路由(没什么用了),重新定义api路由。注意现在客户端在同一个域下,所以不需要启用跨域。这也是这种模式的最大好处。现在chrome的跨域检测越来越严格了。
//Configure方法:
//不需要启用跨域,不需要启用跨域,不需要启用跨域
 //app.UseCors();
//定义api路由:
 app.UseMvc(routes =>
            { 
                routes.MapRoute(
                    name: "api",
                    template: "api/{controller}/{action=Index}/{id?}");
            });

页面显示正确,axios配制成功!


3. 使用静态文件服务器注意事项(20190702更新)

使用了spa后,如果想同时使用静态服务,那么Startup中的声明顺序非常重要,应该是这样的顺序:

//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);
  }
});

否则静态资源无法访问

发布

  • 发布时按照正常的IIS流程,右键项目publish发布就行,发布后出现问题,可以在项目目录下运行dotnet 你的项目名.dll查看。
  • 若问题找不到,建议选择“独立”发布模式,这个模式其实就是把你现在用的.net core打包进发布项目中,从而不用管目标机器的环境依赖。


    image.png
  • 发布完毕后你的客户端项目将出现在最终文件中


    image.png

    这时直接访问iis里设置的地址和端口就能访问了
  • 注意,前端项目axios的请求端口一定要和iis的端口保持一致,
VUE_APP_BACKEND_BASEURL=http://localhost
VUE_APP_BACKEND_PORT=8848

这里是我的.env文件设置。不同于别的项目,这个env在发布后是看不见的,所以这里一定要设置好。
下面是我的axios设置:

let config = {
    baseURL: configs.backendUrls.apiUrl,
    timeout: 60 * 1000 // Timeout 
}; 
const _axios = axios.create(config);

这样每次axios都请求8848端口,就不存在跨域的问题了

写在最后:

  1. 注意,安装插件、依赖时一定要确保在客户端文件夹下进行
  2. 在工程根目录运行dotnet watch run获得前后端同时刷新无缝开发体验,👍
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,186评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,858评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,620评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,888评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,009评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,149评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,204评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,956评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,385评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,698评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,863评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,544评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,185评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,899评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,141评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,684评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,750评论 2 351

推荐阅读更多精彩内容