AspDotNet+VueCli3从零开始

C#

浏览数:136

2019-8-29

AD:资源代下载服务

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获得前后端同时刷新无缝开发体验,👍

作者:Angeladaddy