ASP.NET Core 6.0 如何处理丢失的 Startup.cs 文件

介绍

    .NET 6.0 已经发布,ASP.NET Core 6.0 也已发布。其中有不少变化让很多人感到困惑。例如,“谁动了我的奶酪”,它在哪里Startup.cs?在这篇文章中,我将深入研究这个问题,看看它移动到了哪里以及其他变化。

    ASP.NET Core 的中间件并没有发生根本性的变化,但部分项目结构以及注册依赖项的位置发生了变化。为了更好地理解它,最好从 .NET Core 3.1 项目模板开始,然后手动升级它,看看它与新模板相比如何。

升级旧式控制台项目

    首先,让我们创建一个新的控制台项目。我将其命名为OldToNew。我选择了 .NET Core 3.1 目标,并将其升级到 .NET 6.0 以查看差异。如果您已经使用 .NET 一段时间,您会在文件中认出这个项目结构Program.cs。 

using System;

namespace OldToNew
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}

        在 .NET 6.0 中,这些变化旨在简化和消除应用程序中的冗余。他们引入的首批功能之一是所谓的Filescoped Namespaces。传统命名空间如下所示:

namespace OldToNew
{
    // code goes here.
}

        如果您已经使用过 .NET 一段时间,那么您可能从未在文件中放置过多个命名空间。您可以删除花括号,只需添加分号,将整个文件标记为使用一个命名空间。

namespace OldToNew;
// code goes here.
 

using System;

namespace OldToNew;

internal class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
    }
}
 

        Visual Studio 将会向我抱怨,因为这是一个 .NET Core 3.1 项目,所以在我们走得太远之前,我们需要编辑 .NET Core 3.1 .csproj 文件并将其转换为 .NET 6.0 应用程序。

<!-- .NET Core 3.1 -->
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

</Project>

<!-- .NET 6.0 -->
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>

</Project>

        一旦我们做出这一改变,Visual Studio 就会对文件范围的命名空间感到满意。

        我们要做的下一个更改是删除该using System;行。为此,我们需要再次编辑项目文件并启用Implicit Usings。

<!-- .NET 6.0 -->
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

</Project>

        如果我们启用隐式 using 语句,我们通常使用的大多数常用 using 语句将默认包含在 SDK 中,您不再需要将它们包含在文件中。我们可以删除该using System;行,因为编译器会自动为我们添加 using 语句。

namespace OldToNew;

internal class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
    }

        他们引入的下一个功能是所谓的“顶级语句”。 其目的是删除每个控制台应用程序或 ASP.NET Core 应用程序中存在的“垃圾”。

        使用顶级语句,我们可以删除static void Main(string[] args)方法和花括号以及命名空间和class Program声明。

Console.WriteLine("Hello World!");

        一旦删除所有这些,您就会看到我们剩下的唯一代码就是我们的Console.WriteLine()方法!

将控制台应用更改为 Web 应用 (ASP.NET Core)

    目前,这只是一个简单的控制台应用程序,但我想将其转换为 ASP.NET Core 应用程序。在执行此操作之前,让我们先查看解决方案资源管理器中的依赖项和框架节点。

        您可以在上方看到,Frameworks这Microsoft.NETCore.App是包含创建控制台应用程序所需的所有包的 SDK。让我们将 .csproj 文件中的 SDK 类型更改为 .Web 类型项目,看看会发生什么Microsoft.NET.Sdk:Microsoft.NET.Sdk.Web。

<!-- .NET 6.0 -->
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>

</Project>
注意现在依赖下发生的情况:

        该框架现在包括Microsoft.AspNetCore.App将引入创建 ASP.NET Core 应用程序所需的所有包。它还将修改全局 using 语句以包含 ASP.NET 特定的 using 语句。

        现在让我们删除该Console.WriteLine("Hello World!");行并添加 ASP.NET Core 特定的代码。

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run(); 

        我们现在正在构建一个 Web 应用程序。WebApplication对象从哪里来?它属于Microsoft.AspNetCore.Builder命名空间。

        我们可以运行这个应用程序,但是由于没有端点,所以会有点无聊。让我们添加一个端点来完成这个任务:

app.MapGet("/", () => {
    return "Hello World!";
});   

        我们刚刚在 ASP.NET Core 6.0 中创建了一个最小 API Web 应用程序。让我们运行它并看看会发生什么。 

        当我们启动默认路径时,它会返回,hello world因此我们有一个功能齐全的 Web 应用程序。最少的 API 是快速构建 Web API 项目的好方法。

Startup.cs 怎么样?

    ASP.NET Core 6.0 中的中间件流程与完整 Web API MVC 项目的流程类似,并且共享许多相同的实现。在以前的 ASP.NET Core 项目中,您将获得一个Startup.cs包含两个方法的类ConfigureServices()和Configure()。

    在 ConfigureServices()中,您注册了依赖项注入服务。在 Configure() 中,您概述了中间件管道顺序和结构。

    在我们的新项目中,这是什么样子的?您将依赖注入放在哪里?答案是在第一行和第二行之间,注册中间件在第二行和第三行之间,如下所示。

var builder = WebApplication.CreateBuilder(args);
// REGISTER SERVICES HERE
var app = builder.Build();
// REGISTER MIDDLEWARE HERE
app.Run();

例如,如果我想添加身份验证,我会像这样注册: 

var builder = WebApplication.CreateBuilder(args);
// REGISTER SERVICES HERE
builder.Services.AddAuthentication(...) ...
builder.Services.AddAuthorization();
var app = builder.Build();
// REGISTER MIDDLEWARE HERE
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", () => {
    return "Hello World!";
});  
app.Run();

        中间件管道中的顺序很重要,因此我在中间件MapGet()上方添加了UseAuthentication()和UseAuthorization()。但是,为了使其生效,您需要使用属性注释要保护的端点。这将需要使用语句[Authorize],必须引用:Microsoft.AspNetCore.Authorization 。

using Microsoft.AspNetCore.Authorization;

...

app.MapGet("/",[Authorize]() => {
    return "Hello World!";
});

Program.cs到目前为止,我们的文件的完整代码如下所示: 

var builder = WebApplication.CreateBuilder(args);
// REGISTER SERVICES HERE
builder.Services.AddAuthentication();
builder.Services.AddAuthorization();
var app = builder.Build();
// REGISTER MIDDLEWARE HERE
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/",[Authorize] () => {
    return "Hello World!";
});  
app.Run(); 

        如果我们现在运行它,我们将会得到一个异常,因为我们从未指定身份验证方案。

        让我们修复它。我们可以选择多种不同类型的身份验证方案,但在 WebAPI 中,我们通常使用诸如 bearer token 或 JWT token 之类的东西来保护应用程序。

        让我们继续将 JWT 承载者添加到最小 API。我们需要Microsoft.AspNetCore.Authentication.JwtBearer通过 NuGet 导入包,然后添加以下代码:

using Microsoft.AspNetCore.Authentication.JwtBearer;

...

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer();

所以完整的块应该是这样的:

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;

var builder = WebApplication.CreateBuilder(args);
// REGISTER SERVICES HERE
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer();
builder.Services.AddAuthorization();
var app = builder.Build();
// REGISTER MIDDLEWARE HERE
app.UseAuthentication();
app.UseAuthorization();
app.MapGet("/", [Authorize] () => {
    return "Hello World!";
});
app.Run();

        JWT Bearer 已添加到应用程序中,让我们运行它并查看会发生什么。现在我们得到了我们正在寻找的状态,401 unauthorized因为我们没有为其提供任何类型的令牌。还需要其他步骤来设置获取令牌的方法并确保我们只允许接受有效的令牌,但这超出了本文的范围。本文的重点是演示在哪里注册依赖项和中间件以及如何使用它们。

        我们应该做的最后一项修改是将 [Authorize] 属性更改为使用“流畅的语法”,如下所示:

//FROM THIS:
app.MapGet("/", [Authorize] () => {
    return "Hello World!";
});

//TO THIS:
app.MapGet("/", () => {
  return "Hello World!";
}).RequireAuthorization();

它们都做同样的事情,但是在最小 API 情况下,“流畅的语法”感觉更自然。 

恢复丢失的 Startup.cs 文件

    接下来,让我们看看能否恢复我们的老朋友Startup.cs文件。如果将所有内容都放在一个文件中,最小 API 结构可能会变得混乱和臃肿。

    一定有办法让我们更好地组织这些。我将首先向Startup.cs项目添加一个名为的新类。

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {

    }

     public void Configure(WebApplication app, IWebHostEnvironment env)
    {

    }

        这看起来应该与过去的完整 Web API 项目模板很相似。我只是将其粘贴到我的新Startup.cs文件中并删除了命名空间。该Startup.cs文件需要IConfiguration传入一个对象。我们可以从我们的 中提供该对象吗Program.cs? 

//  Program.cs file
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration); 

        是的。我们可以这样做。builder创建的对象包含一个Configuration属性,我们可以使用该属性将其传递给Startup构造函数。

        接下来,让我们尝试为该ConfigureServices()方法提供一个IServiceCollection。

//  Program.cs file
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services); 

        同样,builder包含一个Services我们可以用来传递给ConfigureServices()方法的属性。现在我们可以从中删除所有依赖注入代码Program.cs并将其移动到ConfigureServices中的方法Startup.cs。 

// Startup.cs file
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer();
    services.AddAuthorization();
}

接下来,让我们尝试Configure()WebApplication类提供方法。 

//  Program.cs file
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services);

var app = builder.Build();

startup.Configure(app, builder.HostingEnvironment); 

        有趣的是,虽然我们传递的app是 类型WebApplication,但如果你检查它,该Configure()方法需要的是IApplicationBuilder,但它似乎没问题。为什么?如果你深入研究该WebApplication对象,你会看到它实现了IApplicationBuilder接口。

        我们将中间件管道代码剪切出来Program.cs,并将其粘贴到Configure()方法中Startup.cs。

// Startup.cs file
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
  app.UseAuthentication();
  app.UseAuthorization();

  app.MapGet("/", () =>
  {
      return "hello world";
  }).RequireAuthorization();

  app.Run();
}

        不幸的是,这实际上行不通,因为接口IApplicationBuilder没有MapGet()方法或Run()方法。那些存在于WebApplication类中。如果我们将Configure()方法更改为接受WebApplication对象,它应该可以工作。 

// Startup.cs file
public void Configure(WebApplication app, IWebHostEnvironment env)
{
  app.UseAuthentication();
  app.UseAuthorization();

  app.MapGet("/", () =>
  {
      return "hello world";
  }).RequireAuthorization();

  app.Run();
}

让我们看一下完整的文件“Program.cs”和“Startup.cs”,看看它们现在是什么样子。 

// Program.cs file
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services);
var app = builder.Build();
startup.Configure(app, builder.Environment);

// Startup.cs file
using Microsoft.AspNetCore.Authentication.JwtBearer;

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(options =>
        {
            options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer();
        services.AddAuthorization();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(WebApplication app, IWebHostEnvironment env)
    {
        app.UseAuthentication();
        app.UseAuthorization();

        app.MapGet("/", () =>
        {
            return "hello world";
        }).RequireAuthorization();

        app.Run();
    }
}

        如果我们运行它,我们会得到401 Unauthorized状态,这意味着它正在运行。除了将我们的配置内容移到 中之外Startup.cs,我们没有做太多改变,这让它看起来更像旧的 .Net Core 风格。 

我们可以做得更好吗?

    使用Startup.cs确实有助于改善组织,但我认为我们可以做得更好。我一直讨厌文件中Startup.cs单词ConfigureServices()和Configure()彼此太接近,而且你还要传递一个IConfiguration对象。这可能会让人搞不清楚什么放在哪里。

    我们为什么不尝试改进这一点呢?我不喜欢这个名字ConfigureServices()。如果我们将它重命名为RegisterDependentServices()并将其放在自己的文件中会怎么样?这将使我们更容易理解发生了什么。

// RegisterDependentServices.cs file
using Microsoft.AspNetCore.Authentication.JwtBearer;

public static class RegisterDependentServices
{
    public static WebApplicationBuilder RegisterServices(this WebApplicationBuilder builder)
    {
        // Register your dependencies
        builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
            .AddJwtBearer();
        builder.Services.AddAuthorization();
        return builder;
    }
}

        我决定创建该类static并使用扩展方法将其添加到该类中。我们稍后WebApplicationBuilder会看到这如何改进文件。Program.cs

        接下来,让我们创建一个新文件SetupMiddlewarePipeline.cs。此文件将包含中间件管道。

public static class SetupMiddlewarePipeline
{
    public static WebApplication SetupMiddleware(this WebApplication app)
    {
        // Configure the pipeline !! ORDER MATTERS !!
        app.UseAuthorization();
        app.UseAuthentication();
        app.MapGet("/", () =>
        {
            return "hello world";
        }).RequireAuthorization();
        return app;
    }
}

现在,这会如何改变我们的Program.cs文件? 

// Program.cs file
WebApplication app = WebApplication.CreateBuilder(args)
    .RegisterServices()
    .Build();

app.SetupMiddleware()
    .Run();

这样就生成了一个非常干净的Program.cs文件。事实上,如果您愿意,您可以将其全部放在一行中。

// Program.cs file
WebApplication.CreateBuilder(args)
    .RegisterServices()
    .Build()
    .SetupMiddleware()
    .Run();

我并不讨厌它。事实上,我觉得我喜欢它。

那么 IConfiguration 怎么样?

    之前,Startup()构造函数需要IConfiguration注入其中。但是,由于WebApplication和WebApplicationBuilder都具有.Configuration属性,因此我们不再需要显式注入它。

// RegisterDependentServices.cs file
public static class RegisterDependentServices
{
  public static WebApplicationBuilder RegisterServices(this WebApplicationBuilder builder)
  {
    // ******* Access the configuration *******
    var config = builder.Configuration;

    // Register your dependencies
    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer();
    builder.Services.AddAuthorization();
    return builder;
  }
}

// setupMiddlewarePipeline.cs file
public static class SetupMiddlewarePipeline
{
  public static WebApplication SetupMiddleware(this WebApplication app)
  {
    // ******** Access the configuration ********
    var config = app.Configuration;

    // Configure the pipeline !! ORDER MATTERS !!
    app.UseAuthorization();
    app.UseAuthentication();
    app.MapGet("/", () =>
    {
      return "hello world";
    }).RequireAuthorization();
    return app;
  }
}

如果我们需要访问应用程序的配置,它是可用的,而且我们的状况很好。 

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/66864.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

欧几里得算法求最小公倍数和最大公约数

一.最大公约数 gcd(a,b)gcd(b,a%b) 递归式,当且仅当b0&#xff0c;易得0和a的公约数为a.(可作为递归的出口) 证明&#xff1a; int gcd(int a, int b) {if (b 0) return a;else return gcd(b, a % b); } 二.最小公倍数 给定整数a b&#xff0c;求a b的最小公倍数 有图可知…

文档解析:PDF里的复杂表格、少线表格如何还原?

PDF中的复杂表格或少线表格还原通常需要借助专业的工具或在线服务&#xff0c;以下是一些可行的方法&#xff1a; 方法一&#xff1a;使用在线PDF转换工具 方法二&#xff1a;使用桌面PDF编辑软件 方法三&#xff1a;通过OCR技术提取表格 方法四&#xff1a;手动重建表格 …

局域网中 Windows 与 Mac 互相远程连接的最佳方案

由于工作需要&#xff0c;经常需要远程连接或登录到几台不同的工作用机上进行操作。 下面基于免费、高体验等基本诉求&#xff0c;简要记录几种不同场景下的实践方案选择&#xff0c;仅供参考。如您有更简单且更优的方案&#xff0c;欢迎一起探讨。 1 远程桌面连接的几种不同…

二叉树的所有路径(力扣257)

因为题目要求路径是从上到下的&#xff0c;所以最好采用前序遍历。这样可以保证按从上到下的顺序将节点的值存入一个路径数组中。另外&#xff0c;此题还有一个难点就是如何求得所有路径。为了解决这个问题&#xff0c;我们需要用到回溯。回溯和递归不分家&#xff0c;每递归一…

Centos 修改历史读录( HISTSIZE)

history命令 -c #清空命令历史 -r #读历史文件附加到历史列表 -w #保存历史列表到指定的历史文件 命令历史相关环境变量 HISTSIZE #命令历史记录的条数 HISTFILE #指定历史文件&#xff0c;默认为~/.bash_history HISTFILESIZE #命令历史文件记录历史的条数 以上变量可以 exp…

【C++高并发服务器WebServer】-2:exec函数簇、进程控制

本文目录 一、exec函数簇介绍二、exec函数簇 一、exec函数簇介绍 exec 函数族的作用是根据指定的文件名找到可执行文件&#xff0c;并用它来取代调用进程的内容&#xff0c;换句话说&#xff0c;就是在调用进程内部执行一个可执行文件。 exec函数族的函数执行成功后不会返回&…

TDengine 与上海电气工业互联网平台完成兼容性认证

在工业数字化转型和智能化升级的浪潮中&#xff0c;企业对高效、可靠的数据管理解决方案的需求日益增长。特别是在风电智能运维、火电远程运维、机床售后服务等复杂多样的工业场景下&#xff0c;如何实现海量设备和时序数据的高效管理&#xff0c;已经成为推动行业升级的关键。…

Jenkins pipline怎么设置定时跑脚本

目录 示例&#xff1a;在Jenkins Pipeline中设置定时触发 使用pipeline指令设置定时触发 使用Declarative Pipeline设置定时触发 使用Scripted Pipeline设置定时触发 解释Cron表达式 保存和应用配置 小结 在Jenkins中&#xff0c;定时跑脚本&#xff08;例如定时执行Pip…

kotlin的协程的基础概念

Kotlin的协程是一种用于简化异步编程的强大工具。 理解协程的基础概念可以帮助开发者有效地利用其能力。 以下是Kotlin协程的一些关键基础概念&#xff1a; 协程&#xff08;Coroutines&#xff09; &#xff1a; 协程是一种用于处理并发任务的编程模型&#xff0c;它可以在单…

machine learning knn算法之使用KNN对鸢尾花数据集进行分类

通过导入必要的scikit-learn导入必要的库&#xff0c;加载给定的数据&#xff0c;划分测试集和训练集之后训练预测和评估即可 具体代码如下&#xff1a; import numpy as np from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split f…

springEl 构建通用树

再项目开发中&#xff0c;需要构建组织的树形&#xff0c;菜单的树形&#xff0c;字典树形。感觉相似的代码写了一堆&#xff0c;就想着有没有办法&#xff0c;写个通用的方法去处理下&#xff1f; 学习了《SpringEL详解》&#xff0c;用springEl处理下。 构建树形&…

C++ 入门速通-第1章【黑马】

内容来源于&#xff1a;黑马 集成开发环境&#xff1a;CLion CLion的官方下载网址&#xff1a;CLion: A Cross-Platform IDE for C and C by JetBrains 我在b站找到了一个安装教程&#xff0c;可以按照这个视频教程进行安装&#xff08;内置汉化教程&#xff09;&#xff1a; …

Python的进程和线程

ref 接受几个设定: 进程是一家almost密不透风的公司,缅甸KK园区 线程里面工作的…人 进程**[园区]**内公共资源对于进程来说,可以共享. 别的园区[进程],一般不能和自己的园区共享人员资源,除非… 好的,现在再接受设定: 单个CPU在任一时刻只能执行单个线程&#xff0c;只有…

算法基础 -- AVL树初识

AVL树初识 一、AVL树简介 AVL树是一种自平衡二叉搜索树&#xff08;Binary Search Tree, BST&#xff09;&#xff0c;于1962年由Georgy Adelson-Velsky和Evgenii Landis提出&#xff0c;名字也来自他们两位的姓氏首字母组合。它通过在插入、删除节点后维持平衡性&#xff0c…

MySQL数值型函数详解

简介 本文主要讲解MySQL数值型函数&#xff0c;包括&#xff1a;ROUND、RAND、ABS、MOD、TRUNCATE、CEIL、CEILING、FLOOR、POW、POWER、SQRT、LOG、LOG2、LOG10、SIGN、PI。 本文所有示例中&#xff0c;双横杠左边为执行的SQL语句&#xff0c;右边为执行语句的返回值。 ROU…

自动化01

测试用例的万能公式&#xff1a;功能测试界面测试性能测试易用性测试安全性测试兼容性测试 自动化的主要目的就是用来进行回归测试 新产品--第一个版本 (具备丰富的功能)&#xff0c;将产品的整体进行测试&#xff0c;人工创造一个自动化测试用例&#xff0c;在n个版本的时候…

Spring中的事务管理器TransactionManager

目录 一、主要功能 二、使用场景说明 在Spring框架中&#xff0c;事务管理器&#xff08;TransactionManager&#xff09;是用于管理事务的重要接口。它提供了对事务的全面控制&#xff0c;包括事务的状态管理和资源管理等功能。本文将详细介绍TransactionManager的主要功能、…

c语言(转义字符)

前言&#xff1a; 内容&#xff1a; 然后记一下转义字符 \? 在书写连续多个问号时使用&#xff0c;防止他们被解析成三字母词 \ 用于表示字符常量 \\ 用于表示一个反斜杠&#xff0c;防止他被解析为一个转义序列符 \n 换行 \r …

Vue3 30天精进之旅:Day02 - 环境搭建

引言 在前一天的学习中&#xff0c;我们了解了Vue.js的基本概念和优势。今天&#xff0c;我们将进入实际开发的第一步——环境搭建。良好的开发环境是顺利开展项目的基础&#xff0c;本文将指导你在本地设置Vue开发环境&#xff0c;并快速上手第一个Vue项目。 1. 环境准备 在…

代码随想录 栈与队列 test 7

347. 前 K 个高频元素 - 力扣&#xff08;LeetCode&#xff09; 首先想到哈希&#xff0c;用key来存元素&#xff0c;value来存出现次数&#xff0c;最后进行排序&#xff0c;时间复杂度约为o(nlogn)。由于只需求前k个&#xff0c;因此可以进行优化&#xff0c;利用堆来维护这…