欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

第六章 使用Visual Studio

程序员文章站 2024-03-02 16:00:04
...

第六章 使用Visual Studio

本章中将介绍一些Visual Studio用于开发ASP.NET Core MVC的关键特性, 下面是章节概括

问题 解决方案
添加程序包 使用NuGet添加.NET包, Bower添加前端包
即时查看视图和类改变的影响 迭代开发模式
在浏览器中显示详细信息 开发者异常页面
得到程序执行的详细信息 调试器
使用VS重新加载浏览器 浏览器链接
减少JS和CSS文件的HTTP请求数量和带宽占用 文件打包

准备示例项目

仍然和上一章一样, 但创建的项目叫WorkingWithVisualStudio

// StartUp.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;

namespace WorkingWithVisualStudio {
    public class Startup {
        public void ConfigureServices(IServiceCollection services) {
            services.AddMvc();
        }
        public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
            app.UseMvcWithDefaultRoute();
        }
    }
}

不同的是把默认在debug环境中开启的developer exception page关闭了.

创建模型

// Models\Product.cs

namespace WorkingWithVisualStudio.Models
{

    public class Product
    {
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
}

再创建一个用于存储的类(内存中)

// Models\SimpleRespository.cs

using System.Collections.Generic;

namespace WorkingWithVisualStudio.Models
{
    public class SimpleRepository
    {
        private static SimpleRepository sharedRepository = new SimpleRepository();

        private Dictionary<string, Product> products = new Dictionary<string, Product>();

        public static SimpleRepository SharedRepository => sharedRepository;

        public SimpleRepository()
        {
            var initialItems = new[] {
                new Product { Name = "Kayak", Price = 275M },
                new Product { Name = "Lifejacket", Price = 48.95M },
                new Product { Name = "Soccer ball", Price = 19.50M },
                new Product { Name = "Corner flag", Price = 34.95M }
            };

            foreach (var p in initialItems)
            {
                AddProduct(p);
            }
        }
        public IEnumerable<Product> Products => products.Values;
        public void AddProduct(Product p) => products.Add(p.Name, p);
    }
}

此类将数据存储在内存中, 每次应用停止数据就会丢失, 用于演示很便利, 但显然不会在真实环境中使用. 第八章中将介绍使用关系型数据库进行持久化存储的技术.

注意: 上面的代码使用了单例模式用于共享数据, 这不是最佳方案, 我会在第十八章中描述一个共享组件的更好方式.

创建控制器和视图

// Controllers\HomeController.cs

using Microsoft.AspNetCore.Mvc;
using WorkingWithVisualStudio.Models;

namespace WorkingWithVisualStudio.Controllers {
    public class HomeController : Controller {
        public IActionResult Index()
            => View(SimpleRepository.SharedRepository.Products);
    }
}

Index是一个简单的行为, 获取数据并传递给默认视图, 创建视图

<!-- \Views\Home\Index.cshtml -->

@model IEnumerable<WorkingWithVisualStudio.Models.Product>

@{ Layout = null; }

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Working with Visual Studio</title>
</head>
<body>
    <table>
        <thead>
            <tr><td>Name</td><td>Price</td></tr>
        </thead>
        <tbody>
            @foreach (var p in Model)
            {
                <tr>
                    <td>@p.Name</td>
                    <td>@p.Price</td>
                </tr>
            }
        </tbody>
    </table>
</body>
</html>

效果图如下

第六章 使用Visual Studio

管理软件包

在ASP.NET Core MVC项目中有两种不同的软件包管理方式, 我将介绍每种类型的软件包, 以及管理他们的工具.

NuGet

VS提供了管理.NET包的可视化工具. 在工具 > NuGet包管理器 > 管理解决方案的程序包打开此工具. (界面很容易理解)

!理解包Microsoft.AspNetCore.All

如果你用过早期版本的ASP.NET Core, 你可能知道在创建新项目时要添加很多NuGet包. ASP.NET Core 2采用了不同的方式, 只需要依赖一个包Microsoft.AspNetCore.All.
Microsoft.AspNetCore.All是一个元包(meta-package), 包含ASP.NET Core和MVC框架需要的所有独立的NuGet包, 也就是说你不需要一个一个地添加包了. 在发布应用的时候, 没有用到的程序包会自动被移除, 以保证应用的清洁.

理解NuGet包列表和位置

NuGet会在<ProjectName>.csproj文件中跟踪项目使用的软件包, 在此项目中存储在WorkingWithVisualStudio.csproj中. 在解决方案资源管理器中右键项目, 可以打开此文件. .csproj是一个XML文件, 其中一部分是程序包的信息

<ItemGroup>
  <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
</ItemGroup>

一个程序包需要使用名字和版本号来限定. 添加新的程序包有三种方式: 使用NuGet图形界面, 使用NuGet命令行, 以及直接修改.csproj文件. VS会检测到csproj文件的修改, 并自动下载程序包
NuGet安装程序包时会自动安装依赖项, 在解决方案资源管理器 > 依赖项 > NuGet中可以看到程序包及其依赖项

Bower

前端库包含发送到客户端的内容, 例如JS, CSS和图片. NuGet也可以管理这些包, 但ASP.NET Core MVC现在使用Bower工具. Bower是一个开源工具, 且得到了广泛应用.

注意: Bower最近被抨击了, 你可能会看到推荐使用替代工具的警告. 但Bower仍然在积极维护, 且支持集成到VS. 在某种程度上可以期望微软支持另一种前端包管理工具, 但在实现之前可以继续使用Bower. (在npm中安装bower时将提示: We don’t recommend using Bower for new projects. Please consider Yarn and Webpack or Parcel. You can read how to migrate legacy project here , bower作者都推荐使用其他工具了, 且我的VS2017版本中也找不到Bower的集成…..很绝望啊….使用方法详见github仓库的readme)

Bower包列表

Bower包在bower.json中指定.

!由于我的VS中没有集成Bower(在安装程序中也没有发现bower组件), 所以需要在项目下先添加一个配置文件.bowerrc(选择新建一个文本文件)

{
  "registry": "https://registry.bower.io",
  "directory": "wwwroot/lib"
}

这个文件指定了安装位置, 否则使用命令行会把程序包安装在bower_components

然后新建bower配置文件bower.json, 填写默认内容

{
  "name": "asp.net",
  "private": true,
  "dependencies": {
  }
}

在解决方案资源管理器中, bower.json会自动折叠.bowerrc, 且bower的一些智能感知功能还是可以用的, 然后在”依赖项”中可以打开GUI工具. 感觉微软的态度可能是在逐步脱离bower.

Bower的使用方法和NuGet类似, 可以使用UI工具, 直接修改配置文件, 或者使用PM命令行. 包会在执行编译之前下载.

{
  "name": "asp.net",
  "private": true,
  "dependencies": {
    "bootstrap": "4.1.2"
  }
}

(具体使用方法请自行搜索)

迭代开发

Web应用开发通常是一个迭代过程, 在对视图和类进行细微修改后就需要重新运行来查看效果. MVC和VS支持查看实时修改.

修改Razor页面

在开发过程中, 当从浏览器接收到HTTP请求时, Razor视图的更改就会起效. 先开始调试应用, 再修改视图(包括DOM元素和Razor代码块), 刷新页面会直接显示变化.

(个人推测这个机制是由于在返回时先检测改动, 加载文件再进行编译, 相比之下以前的C#开发在调试时不可以进行代码修改)

修改C#类

对于C#类(控制器和视图), 处理更改的方式取决于如何启动应用程序. 在”调试”菜单中有两个选项

  • 开始运行(不调试): 类会在HTTP请求到来时自动编译, 开发可以更灵活, 但不运行调试器, 就不能精确控制代码
  • 开始调试: 每次更改后都需要重新编译才能生效, 附加调试器, 可以检查代码状态并分析问题.

自动编译C#类

在一般的开发活动中, 快速的迭代循环让你能够即时看到更改. 在这种开发模式中, VS会在接收到HTTP请求并检测到更改时自动重新编译.
“开始执行(不调试)”, 之后更改代码, 刷新网页就会看到更改(但这个过程相比改前端还是挺慢的)
当一切都计划好时, 自动编译特性很有用. 缺点则是错误和异常只会显示在浏览器(没开调试器)中, 很难弄清楚到底发生了什么.

使用开发者异常页面

启用developer exception page来缓解上面的问题, 在StartUp.cs中配置(默认是打开的)

public void Configure(IApplicationBuilder app, IHostingEnvironment env) {
    app.UseDeveloperExceptionPage();
    app.UseMvcWithDefaultRoute();
}

浏览器显示的错误信息可以解决简单的问题, 特别是因为迭代开发方式意味着错误很可能是因为最近的更改. 但对于更复杂的问题, VS调试器是必须的.

使用调试器

(… 这个没什么要说的了吧…应该都会用)

浏览器链接

浏览器链接特性可以将一个或多个浏览器置于VS的控制下, 从而简化开发过程. 浏览器链接只有在”开始执行(无调试器)”模式下才有效, 可以不用手动切换到浏览器刷新页面就看到修改.
(我没看到啊…..我这用户权限设置太高了??不….需要进行一些设置)

启用功能

老规矩, StartUp.cs

// StartUp.cs
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseMvcWithDefaultRoute();
            app.UseBrowserLink();
            app.UseDeveloperExceptionPage();
        }

然后应该就可以了(反正我这不行), 如果成功链接, 在调试按钮旁边的刷新按钮就可以刷新浏览器

第六章 使用Visual Studio

使用多个浏览器

在调试按钮的IIS配置中设置

第六章 使用Visual Studio

为部署准备JS和CSS

写web前端的时候通常有很多JS和CSS文件. 这些文件需要被处理, 以便在生产环境中优化, 减少HTTP请求数量及带宽占用, 这个过程称为”bundling”或”minification”(不管叫什么, 称作”打包”了), 是一个压缩过程. 本节将介绍如何启用静态内容的交付, 以及如何为部署准备这些内容

启用静态内容交付

StartUp.cs

app.UseStaticFiles();

向项目中添加静态内容

为了演示打包过程, 需要先添加一些静态内容, 并包含进实例应用中. 创建wwwroot/css文件夹和wwwroot/js文件夹, 然后创建文件

/* wwwroot\css\first.css */

h3 {
    font-size: 18pt;
    font-family: sans-serif;
}

table, td {
    border: 2px solid black;
    border-collapse: collapse;
    padding: 5px;
    font-family: sans-serif;
}
/* wwwroot\css\second.css */

p {
    font-family: sans-serif;
    font-size: 10pt;
    color: darkgreen;
    background-color: antiquewhite;
    border: 1px solid black;
    padding: 2px;
}
// wwwroot\js\third.js

document.addEventListener("DOMContentLoaded", function () {
    var element = document.createElement("p");
    element.textContent = "This is the element from the third.js file";
    document.querySelector("body").appendChild(element);
});
// wwwroot\js\fourth.js

document.addEventListener("DOMContentLoaded", function () {
    var element = document.createElement("p");
    element.textContent = "This is the element from the fourth.js file";
    document.querySelector("body").appendChild(element);
});

更新视图

将刚创建好的静态内容引入视图

<!-- Views\Home\Index.cshtml -->

@model IEnumerable<WorkingWithVisualStudio.Models.Product>

@{ Layout = null; }

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Working with Visual Studio</title>
    <link rel="stylesheet" href="css/first.css" />
    <link rel="stylesheet" href="css/second.css" />
    <script src="js/third.js"></script>
    <script src="js/fourth.js"></script>
</head>
<body>
    <h3>Products</h3>
    <p>Request Time: @DateTime.Now.ToString("HH:mm:ss")</p>
    <table>
        <thead>
            <tr><td>Name</td><td>Price</td></tr>
        </thead>
        <tbody>
            @foreach (var p in Model)
            {
                <tr>
                    <td>@p.Name</td>
                    <td>@($"{p.Price:C2}")</td>
                </tr>
            }
        </tbody>
    </table>
</body>
</html>

显示效果应该如下(如果样式不对, 可能是没有启用静态文件交付)

第六章 使用Visual Studio

在MVC应用中对静态文件进行打包

现在有四个静态文件, 浏览器也必须发送所有的四个请求. 而且每个文件中也包含大量的空白, 这些空白可以让开发者更容易阅读, 但对于客户端是没用的, 而且会占用大量的带宽.
合并同样类型的文件称为”捆绑”(bundling), 让文件更小称为”紧缩”(minification). 这些任务都可以使用VS扩展”Bundler & Minifier”完成

安装VS扩展

工具 > 扩展和更新 > 联机 > 搜索, 插件将在VS关闭后完成安装.

打包文件

安装完毕后, 重启VS并打开项目. 在解决方案资源管理器中选区多个同类型的文件, 执行操作

第六章 使用Visual Studio

插件会创建一个捆绑后的文件bundle.css, 以及一个紧缩后的文件bundle.min.css. 对js文件重复此过程.

注意: 为了保证加载顺序, 需要按顺序在解决方案资源管理器中选取文件, 再进行执行.

之后在视图文件中修改引入的文件

<!-- Views\Home\Index.cshtml -->

@model IEnumerable<WorkingWithVisualStudio.Models.Product>
@{ Layout = null; }

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Working with Visual Studio</title>
    <link rel="stylesheet" href="css/bundle.min.css" />
    <script src="js/bundle.min.js"></script>
</head>
<body>
    <h3>Products</h3>
    <p>Request Time: @DateTime.Now.ToString("HH:mm:ss")</p>
    <table>
        <thead>
            <tr><td>Name</td><td>Price</td></tr>
        </thead>
        <tbody>
            @foreach (var p in Model) {
                <tr>
                    <td>@p.Name</td>
                    <td>@($"{p.Price:C2}")</td>
                </tr>
            }
        </tbody>
    </table>
</body>
</html>

应用程序看起来没有任何变化, 但静态文件已经被打包了.
打包完成后, 扩展将生成一个配置文件bundleconfig.json

[
  {
    "outputFileName": "wwwroot/css/bundle.css",
    "inputFiles": [
      "wwwroot/css/first.css",
      "wwwroot/css/second.css"
    ]
  },
  {
    "outputFileName": "wwwroot/js/bundle.js",
    "inputFiles": [
      "wwwroot/js/third.js",
      "wwwroot/js/fourth.js"
    ]
  }
]

可以在此修改配置(特别是文件顺序). 扩展程序会自动监视输入文件的改动, 在保存改动后自动重新进行打包操作.

总结

本章介绍了VS为web应用开发提供的特性, 包括自动编译类, 浏览器连接和静态文件打包.
下一章将介绍MVC项目的单元测试