第六章 使用Visual Studio
第六章 使用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>
效果图如下
管理软件包
在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();
}
然后应该就可以了(反正我这不行), 如果成功链接, 在调试按钮旁边的刷新按钮就可以刷新浏览器
使用多个浏览器
在调试按钮的IIS配置中设置
为部署准备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>
显示效果应该如下(如果样式不对, 可能是没有启用静态文件交付)
在MVC应用中对静态文件进行打包
现在有四个静态文件, 浏览器也必须发送所有的四个请求. 而且每个文件中也包含大量的空白, 这些空白可以让开发者更容易阅读, 但对于客户端是没用的, 而且会占用大量的带宽.
合并同样类型的文件称为”捆绑”(bundling), 让文件更小称为”紧缩”(minification). 这些任务都可以使用VS扩展”Bundler & Minifier”完成
安装VS扩展
工具 > 扩展和更新 > 联机 > 搜索
, 插件将在VS关闭后完成安装.
打包文件
安装完毕后, 重启VS并打开项目. 在解决方案资源管理器中选区多个同类型的文件, 执行操作
插件会创建一个捆绑后的文件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项目的单元测试
下一篇: 在geany下输出中文的方法
推荐阅读
-
第六章 使用Visual Studio
-
Android Studio应用开发集成百度语音合成使用方法实例讲解
-
关于visual studio 2012 update 2中的新功能介绍
-
使用Fiddler调试visual studion多个虚拟站点的问题分析
-
【Visual Studio】VC工具 vcvars32.bat 和 nmake 的使用
-
54.Visual Studio2017调用MASM32
-
在Visual Studio上构建C++的GUI框架wxWidgets的开发环境
-
第一次使用Android Studio时你应该知道的一切配置(推荐)
-
SQLServer 在Visual Studio的2种连接方法
-
Android Studio应用开发集成百度语音合成使用方法实例讲解