《ASP.NET Core 6框架揭秘》實例演示[24]:中間件的多種定義方式

ASP.NET Core的請求處理管道由一個服務器和一組中間件組成,位于 “龍頭” 的服務器負責請求的監聽、接收、分發和最終的響應,針對請求的處理由后續的中間件來完成。中間件最終體現為一個Func<RequestDelegate, RequestDelegate>委托,但是我們具有不同的定義和注冊方式。[本文節選《ASP.NET Core 6框架揭秘》第15章]

[S1505]以Func<RequestDelegate, RequestDelegate>形式定義中間件(源代碼)
[S1506]定義強類型中間件類型(源代碼)
[S1507]定義基于約定的中間件類型(源代碼)
[S1508]查看默認注冊的服務(源代碼)
[S1509]中間件類型的構造函數注入(源代碼)
[S1510]中間件類型的方法注入(源代碼)
[S1511]服務實例的周期(源代碼)
[S1512]針對服務范圍的驗證(源代碼)

[S1505]以Func<RequestDelegate, RequestDelegate>形式定義中間件

如下所示的演示程序創建了兩個Func<RequestDelegate, RequestDelegate>委托,它們會在響應中寫入兩個字符串(“Hello”和“World!”)。在創建出代表承載應用的WebApplication對象之后,我們將其轉成IApplicationBuilder接口后(IApplicationBuilder接口的Use方法在WebApplication類型中是顯式實現的,所以不得不作這樣的類型轉換),我們調用其Use方法將這兩個委托對象注冊為中間件。

var?app?=?WebApplication.Create(args);
IApplicationBuilder?applicationBuilder?=?app;
applicationBuilder.Use(Middleware1).Use(Middleware2);
app.Run();static?RequestDelegate?Middleware1(RequestDelegate?next)?
=>?async?context?=>{await?context.Response.WriteAsync("Hello");await?next(context);};
static?RequestDelegate?Middleware2(RequestDelegate?next)?
=>?context?=>?context.Response.WriteAsync("?World!");

運行該程序后,我們利用瀏覽器對應用監聽地址(“http://localhost:5000”)發送請求,兩個中間件寫入的字符串會以圖1所示的形式呈現出來。

6b323c00ab91a59228cae81e7d147a78.png
圖1 利用注冊的中間件處理請求

[S1506]定義強類型中間件類型

如果采用強類型中間件類型定義方式,只需要實現如下這個IMiddleware接口。該接口定義了唯一的InvokeAsync方法來處理請求。這個InvokeAsync方法定義了兩個參數,前者表示當前HttpContext上下文,后者是一個RequestDelegate委托,代表后續中間件組成的管道。如果當前中間件需要將請求分發給后續中間件進行處理,只需要調用這個委托對象即可,否則針對請求的處理就到此為止。

public?interface?IMiddleware
{Task?InvokeAsync(HttpContext?context,?RequestDelegate?next);
}

如下所示的演示程序定義了一個實現了IMiddleware接口的StringContentMiddleware中間件類型,實現的InvokeAsync方法將構造函數中指定的字符串作為響應的內容。由于中間件最終是采用依賴注入的方式來提供的,所以需要預先對它注冊為服務。用于存放服務注冊的 IServiceCollection對象可以通過WebApplicationBuilder的Services屬性獲得,演示程序利用它完成了針對StringContentMiddleware的服務注冊。由于代表承載應用的WebApplication類型實現了IApplicationBuilder接口,所以我們直接調用它的UseMiddleware<TMiddleware>擴展方法來注冊中間件類型。啟動該程序后利用瀏覽器訪問監聽地址,依然可以得到圖1所示的輸出結果

var?builder?=?WebApplication.CreateBuilder();
builder.Services.AddSingleton<StringContentMiddleware>(new?StringContentMiddleware("Hello?World!"));
var?app?=?builder.Build();
app.UseMiddleware<StringContentMiddleware>();
app.Run();public?sealed?class?StringContentMiddleware?:?IMiddleware
{private?readonly?string?_contents;public?StringContentMiddleware(string?contents)=>?_contents?=?contents;public?Task?InvokeAsync(HttpContext?context,?RequestDelegate?next)=>?context.Response.WriteAsync(_contents);
}

[S1507]定義基于約定的中間件類型

可能我們已經習慣了通過實現某個接口或者繼承某個抽象類的擴展方式,其實這種方式有時顯得約束過重,不夠靈活,基于約定來定義中間件類型更常用。這種定義方式比較自由,因為它并不需要實現某個預定義的接口或者繼承某個基類,而只需要遵循如下這些約定即可

  • 中間件類型需要有一個有效的公共實例構造函數,該構造函數必須包含一個RequestDelegate類型的參數,當中間件實例被創建的時候,代表后續中間件管道的RequestDelegate對象將與這個參數進行綁定。構造函數可以包含任意其他參數,RequestDelegate參數出現的位置也沒有限制。

  • 針對請求的處理實現在返回類型為Task的InvokeAsync或者Invoke方法中,它們的第一個參數為HttpContext上下文。約定并未對后續參數作限制,但是由于這些參數最終由依賴注入框架提供,所以相應的服務注冊必須存在。

這種方式定義的中間件依然通過前面介紹的UseMiddleware方法和UseMiddleware<TMiddleware>方法進行注冊。由于這兩個方法會利用依賴注入框架來提供指定類型的中間件對象,所以它會利用注冊的服務來提供傳入構造函數的參數。如果構造函數的參數沒有對應的服務注冊,就必須在調用這個方法的時候顯式指定。

演示實例定義了如下這個StringContentMiddleware類型,它的InvokeAsync方法會將預先指定的字符串作為響應內容。StringContentMiddleware的構造函數定義了contents和forewardToNext參數,前者表示響應內容,后者表示是否需要將請求分發給后續中間件進行處理。在調用UseMiddleware<TMiddleware>擴展方法對這個中間件進行注冊時,我們顯式指定了響應的內容,至于參數forewardToNext,我們之所以沒有每次都顯式指定,是因為默認值的存在。

var?app?=?WebApplication.CreateBuilder().Build();
app.UseMiddleware<StringContentMiddleware>("Hello").UseMiddleware<StringContentMiddleware>("?World!",?false);
app.Run();public?sealed?class?StringContentMiddleware
{private?readonly?RequestDelegate?_next;private?readonly?string?_contents;private?readonly?bool?_forewardToNext;public?StringContentMiddleware(RequestDelegate?next,?string?contents,?bool?forewardToNext?=?true){_next?=?next;_forewardToNext??=?forewardToNext;_contents?=?contents;}public?async?Task?Invoke(HttpContext?context){await?context.Response.WriteAsync(_contents);if?(_forewardToNext){await?_next(context);}}
}

啟動該程序后,利用瀏覽器訪問監聽地址依然可以得到圖1所示的輸出結果。對于前面介紹的形式定義的中間件,它們的不同之處除了體現在定義和注冊方式上,還體現在自身生命周期上。強類型方式定義的中間件采用的生命周期取決于對應的服務注冊,但是按照約定定義的中間件則總是一個單例對象。

[S1508]查看默認注冊的服務

ASP.NET Core框架本身在構建請求處理管道之前會注冊一些必要的服務,這些公共服務除了供框架自身消費外,也可以供應用程序使用。那么應用啟動后究竟預先注冊了哪些服務?我們編寫了如下這個簡單的程序來回答這個問題。

using?System.Text;var?builder?=?WebApplication.CreateBuilder();
var?app?=?builder.Build();
app.Run(InvokeAsync);
app.Run();Task?InvokeAsync(HttpContext?httpContext)
{var?sb?=?new?StringBuilder();foreach?(var?service?in?builder.Services){var?serviceTypeName?=?GetName(service.ServiceType);var?implementationType?=?service.ImplementationType???service.ImplementationInstance?.GetType()???service.ImplementationFactory?.Invoke(httpContext.RequestServices)?.GetType();if?(implementationType?!=?null){sb.AppendLine(@$"{service.Lifetime,-15}{GetName(service.ServiceType),-60}{?GetName(implementationType)}");}}return?httpContext.Response.WriteAsync(sb.ToString());
}static?string?GetName(Type?type)
{if?(!type.IsGenericType){return?type.Name;}var?name?=?type.Name.Split('`')[0];var?args?=?type.GetGenericArguments().Select(it?=>?it.Name);return?@$"{name}<{string.Join(",",?args)}>";
}

演示程序調用WebApplication對象的Run擴展方法注冊了一個中間件,它會將每個服務對應的聲明類型、實現類型和生命周期作為響應內容進行輸出。啟動這段程序執行之后,系統注冊的所有公共服務會以圖2所示的方式輸出請求的瀏覽器上。

98a72f234f18b8cdd6fb845b519daa69.png
圖2 ASP.NET Core框架注冊的公共服務

[S1509]中間件類型的構造函數注入

在構造函數或者約定的方法中注入依賴服務對象是主要的服務消費方式。對于以處理管道為核心的ASP.NET Core框架來說,依賴注入主要體現在中間件的定義上。由于ASP.NET Core框架在創建中間件對象并利用它們構建整個管道時,所有的服務都已經注冊完畢,所以注冊的任何一個服務都可以采用如下的方式注入到構造函數中。

using?System.Diagnostics;var?builder?=?WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<FoobarMiddleware>().AddSingleton<Foo>().AddSingleton<Bar>();
var?app?=?builder.Build();
app.UseMiddleware<FoobarMiddleware>();
app.Run();public?class?FoobarMiddleware?:?IMiddleware
{public?FoobarMiddleware(Foo?foo,?Bar?bar){Debug.Assert(foo?!=?null);Debug.Assert(bar?!=?null);}public?Task?InvokeAsync(HttpContext?context,?RequestDelegate?next){Debug.Assert(next?!=?null);return?Task.CompletedTask;}
}public?class?Foo?{}
public?class?Bar?{}

[S1510]中間件類型的方法注入

上面演示的是強類型中間件的定義方式,如果采用約定方式來定義中間件類型,依賴服務還可以采用如下的方式注入用于處理請求的InvokeAsync或者Invoke方法中。

using?System.Diagnostics;var?builder?=?WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<Foo>().AddSingleton<Bar>();
var?app?=?builder.Build();
app.UseMiddleware<FoobarMiddleware>();
app.Run();public?class?FoobarMiddleware
{private?readonly?RequestDelegate?_next;public?FoobarMiddleware(RequestDelegate?next)?=>?_next?=?next;public?Task?InvokeAsync(HttpContext?context,?Foo?foo,?Bar?bar){Debug.Assert(context?!=?null);Debug.Assert(foo?!=?null);Debug.Assert(bar?!=?null);return?_next(context);}
}public?class?Foo?{}
public?class?Bar?{}

[S1511]服務實例的周期

我們演示了如下的實例使讀者對注入服務的生命周期具有更加深刻的認識,。如代碼片段所示,我們定義了Foo、Bar和Baz三個服務類,它們的基類Base實現了IDisposable接口。我們分別在Base的構造函數和實現的Dispose方法中輸出相應的文字,以確定服務實例被創建和釋放的時機。

var?builder?=?WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Services.AddSingleton<Foo>().AddScoped<Bar>().AddTransient<Baz>();var?app?=?builder.Build();
app.Run(InvokeAsync);
app.Run();static?Task?InvokeAsync(HttpContext?httpContext)
{var?path?=?httpContext.Request.Path;var?requestServices?=?httpContext.RequestServices;Console.WriteLine($"Receive?request?to?{path}");requestServices.GetRequiredService<Foo>();requestServices.GetRequiredService<Bar>();requestServices.GetRequiredService<Baz>();requestServices.GetRequiredService<Foo>();requestServices.GetRequiredService<Bar>();requestServices.GetRequiredService<Baz>();if?(path?==?"/stop"){requestServices.GetRequiredService<IHostApplicationLifetime>().StopApplication();}return?httpContext.Response.WriteAsync("OK");
}public?class?Base?:?IDisposable
{public?Base()?=>?Console.WriteLine($"{GetType().Name}?is?created.");public?void?Dispose()?=>?Console.WriteLine($"{GetType().Name}?is?disposed.");
}
public?class?Foo?:?Base?{}
public?class?Bar?:?Base?{}
public?class?Baz?:?Base?{}

我們采用不同的生命周期對這三個服務進行了注冊,并將針對請求的處理實現在InvokeAsync這個本地方法中。該方法會從HttpContext上下文中提取出RequestServices,并利用它“兩次”提取出三個服務對應的實例。若請求路徑為“/stop”,它會采用相同的方式提取出IHostApplicationLifetime對象,并通過調用其StopApplication方法將應用關閉。

我們采用命令行的形式來啟動該應用程序,然后利用瀏覽器依次向該應用發送兩個請求,采用的路徑分別為 “/index”和“ /stop”,控制臺上會出現如圖3所示的輸出。由于Foo服務采用的生命周期模式為Singleton,所以在整個應用的生命周期內只會創建一次。對于每個接收的請求,雖然Bar和Baz都被使用了兩次,但是采用Scoped模式的Bar對象只會被創建一次,而采用Transient模式的Baz對象則被創建了兩次。再來看釋放服務相關的輸出,采用Singleton模式的Foo對象會在應用被關閉的時候被釋放,而生命周期模式分別為Scoped和Transient的Bar與Baz對象都會在應用處理完當前請求之后被釋放。

a3767c6f518ad5565bb92e5937f5ce7c.png
圖3 服務的生命周期

[S1512]針對服務范圍的驗證

Scoped服務既不應該由ApplicationServices來提供,也不能注入一個Singleton服務中,否則它將無法在請求結束之后被及時釋放。如果忽視了這個問題,就容易造成內存泄漏,下面是一個典型的例子。下面的演示程序使用的FoobarMiddleware的中間件需要從數據庫中加載由Foobar類型表示的數據。這里采用Entity Framework Core從SQL Server中提取數據,所以我們為實體類型Foobar定義的DbContext(FoobarDbContext),我們調用IServiceCollection接口的AddDbContext<TDbContext>擴展方法對它以Scoped生命周期進行了注冊。

using?Microsoft.EntityFrameworkCore;
using?System.ComponentModel.DataAnnotations;var?builder?=?WebApplication.CreateBuilder(args);
builder.Host.UseDefaultServiceProvider(options?=>?options.ValidateScopes?=?false);
builder.Services.AddDbContext<FoobarDbContext>(options?=>?options.UseSqlServer("{your?connection?string}"));
var?app?=?builder.Build();
app.UseMiddleware<FoobarMiddleware>();
app.Run();public?class?FoobarMiddleware
{private?readonly?RequestDelegate?_next;private?readonly?Foobar??_foobar;public?FoobarMiddleware(RequestDelegate?next,?FoobarDbContext?dbContext){_next?=?next;_foobar?=?dbContext.Foobar.SingleOrDefault();}public?Task?InvokeAsync(HttpContext?context){return?_next(context);}
}public?class?Foobar
{[Key]public?string?Foo?{?get;?set;?}public?string?Bar?{?get;?set;?}
}public?class?FoobarDbContext?:?DbContext
{public?DbSet<Foobar>?Foobar?{?get;?set;?}public?FoobarDbContext(DbContextOptions?options)?:?base(options)?{?}
}

采用約定方式定義的中間件實際上是一個單例對象,而且它是在應用啟動時中由ApplicationServices創建的。由于FoobarMiddleware的構造函數中注入了FoobarDbContext對象,所以該對象自然也成了一個單例對象,這就意味著FoobarDbContext對象的生命周期會延續到當前應用程序被關閉的那一刻,造成的后果就是數據庫連接不能及時地被釋放。

using?Microsoft.EntityFrameworkCore;
using?System.ComponentModel.DataAnnotations;var?builder?=?WebApplication.CreateBuilder(args);
builder.Host.UseDefaultServiceProvider(options?=>?options.ValidateScopes?=?true);
builder.Services.AddDbContext<FoobarDbContext>(options?=>?options.UseSqlServer("{your?connection?string}"));
var?app?=?builder.Build();
app.UseMiddleware<FoobarMiddleware>();
app.Run();
...

在一個ASP.NET Core應用中,如果將服務的生命周期注冊為Scoped模式,我們希望服務實例真正采用基于請求的生命周期模式。我們可以通過啟用針對服務范圍的驗證來避免采用作為根容器的IServiceProvider對象來提供Scoped服務實例。針對服務范圍的檢驗開關可以調用IHostBuilder接口的UseDefaultServiceProvider擴展方法進行設置。如果我們采用上面的方式開啟針對服務范圍驗證,啟動該程序之后會出現圖4所示的異常。由于此驗證會影響性能,所以默認情況下此開關只有在“Development”環境下才會被開啟。

cd484e11e5dd0753b4ee912f049fc8e0.png
圖4 針對Scoped服務的驗證

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282901.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282901.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282901.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android之 RecyclerView,CardView 詳解和相對應的上拉刷新下拉加載

為什么80%的碼農都做不了架構師&#xff1f;>>> 隨著 Google 推出了全新的設計語言 Material Design&#xff0c;還迎來了新的 Android 支持庫 v7&#xff0c;其中就包含了 Material Design 設計語言中關于 Card 卡片概念的實現 —— CardView。RecyclerView也是谷…

php獲取郵箱內容嗎,php正則驗證email郵箱及抽取內容中email的例子

1&#xff0c;php正則驗證email格式&#xff1a;復制代碼 代碼示例:if (ereg(“/^[a-z]([a-z0-9]*[-_\.]?[a-z0-9])*([a-z0-9]*[-_]?[a-z0-9])[\.][a-z]{2,3}([\.][a-z]{2})?$/i; ”,$email)){echo “Your email address is correct!”;}else{echo “Please try again!”;}?…

Java——Arrays類操作數組的工具類

JDK中提供了一個專門用于操作數組的工具類&#xff0c;即 Arrays 類&#xff0c;位于 Java。util 包中。該類提供了一系列方法來操作數組&#xff0c;如排序、復制、比較、填充等&#xff0c;用戶直接調用這些方法即可&#xff0c;不需要自己編碼實現&#xff0c;降低了開發難度…

PropertiesUtil 獲取文件屬性值

有時候不要把一些屬性值寫死在代碼中&#xff0c;而是寫在配置在文件中&#xff0c;方便更改 PropertiesUtil工具類&#xff1a;讀取key-value形式的配置文件&#xff0c;根據key獲得value值 1、測試類 public class Test{private static PropertiesUtil propertiesUtil new …

CORS——跨域請求那些事兒

【本期嘉賓介紹】睿得&#xff0c;具有多年研發、運維、安全等IT相關從業經歷。目前從事CDN、存儲、視頻直播點播的技術支持。喜愛鉆研&#xff0c;喜愛編碼&#xff0c;喜愛分享。 在日常的項目開發時會不可避免的需要進行跨域操作&#xff0c;而在實際進行跨域請求時&#xf…

oracle 數據執行計劃,Oracle里常見的執行計劃

本文介紹了Oracle數據庫里常見的執行計劃&#xff0c;使用的Oracle數據庫版本為11.2.0.1。1、與表訪問相關的執行計劃Oracle數據庫里與表訪問有關的兩種方法&#xff1a;全表掃描和ROWID掃描。反映在執行計劃上&#xff0c;與全表掃描對應的執行計劃中的關鍵字是“TABLE ACCESS…

.NET MAUI實戰 Dispatcher

詳細內容這一期分享的內容非常簡單&#xff0c;在之前使用過WPF的開發者對MVVM開發模式下ViewModel中后臺線程轉UI線程并不陌生使用Appplication.Current.Dispatcher。那么在.NET MAUI中也有同樣的機制&#xff0c;存在于.NET MAUI Shell對象中。那么什么是Shell&#xff1f;官…

GDB 配置

GDB 配置 使用 GDB 擴展來配置 GDB 事實上我還是覺得原生的 GDB 就挺好&#xff0c;速度快&#xff0c;需要查看什么執行命令就可以。 GDB DashBoard https://github.com/cyrus-and/gdb-dashboard $sudo mkdir -m 777 ~/gdbinit; cd ~/gdbinit $git clone https://github.com/c…

Oracle區分中文和英文,oracle中中英文段落劃分實現

oracle中關于中文占用字節數&#xff0c;不同的數據庫有不同的情況&#xff0c;有的占用兩個字節、有的占用三個字節&#xff0c;現在測試環境的數據庫中文占用三個字節&#xff0c;要實現由中英文組成的段落字符串&#xff0c;按照每行占用多少字節重新分段&#xff0c;具體應…

未來哪些行業值得加入?

閱讀本文大概需要5分鐘。這個問題很多讀者都問過&#xff0c;基本上每隔幾篇原創就會有人留言問&#xff0c;還有公眾號后臺和知乎私聊。之前在一次留言中我承諾專門開一篇文章來聊聊這個話題&#xff0c;今天想著要兌現這個諾言了。為啥最近會存在這個問題呢&#xff0c;原因其…

虛擬機網絡配置詳解(NAT、橋接、Hostonly)

VirtualBox中有四種網絡連接方式: NATBridged AdapterInternalHost-only AdapterVMWare中有三種&#xff0c;其實它跟VMWare的網絡連接方式都是一樣的概念&#xff0c;只是比VMWare多了Internal方式 在介紹四種工作模式之前&#xff0c;先說下虛擬網卡&#xff0c;虛擬機安裝好…

Oracle收款核銷了怎么撤銷,21應收收款-核銷取消或核銷調整

注&#xff1a;本課程不包含學習下載資料目標人群&#xff1a;1、Oracle ERP/EBS初級顧問和技術顧問&#xff1b; 1、Oracle ERP/EBS用戶熟練學習ERP系統的基本設置功能&#xff1b; 2、Oracle ERP/EBS財務初級顧問的學習&#xff1b; 3、其他對Oracle ERP/EBS有興趣的想轉行如…

微軟宣布正式開源 Azure IoT Edge 邊緣計算服務

開發四年只會寫業務代碼&#xff0c;分布式高并發都不會還做程序員&#xff1f; 微軟宣布&#xff0c;去年年底公開預覽的 Azure IoT Edge 邊緣計算服務已進入官方版&#xff0c;并通過 GitHub 將其開源。Azure IoT Edge 主要將基于云的分析和定制的業務邏輯轉移到邊緣設備&a…

Windows下安裝BeautifulSoup

電腦首先要安裝好了python&#xff0c;我安裝的是2.7。 下面就是bs4的安裝過程了: 1.去官網下載BeautifulSoup4 2017.02.10目前最新版本&#xff1a;Beautiful Soup 4.3.2 2.解壓文件 將下載得到的壓縮包解壓到任意文件夾&#xff0c;路徑不含中文 3.打開cmd命令提示符 winr&am…

BZOJ1578: [Usaco2009 Feb]Stock Market 股票市場

S<50只股票D<10天的價格給出&#xff0c;求第一天開始用n<200000元最后能得到的最大錢數&#xff0c;保證答案<500000。 做D次完全背包即可&#xff0c;每次做完把dp數組清空。 1 #include<cstdio>2 #include<cstring>3 #include<algorithm>4 #i…

OC如何跳到系統設置里的各種設置界面

當 iOS系統版本 < iOS7時 , 只能跳轉到 系統設置頁面 &#xff0c;樓主試了下&#xff0c;非真機是沒有任何效果的 當iOS系統版本 < iOS 10.0 時 NSURL *url [NSURL URLWithString:"prefs:rootLOCATION_SERVICES"]; if( [[UIApplication sharedApplication]can…

oracle 注冊程序,oracle 靜態注冊

1. 最近在裝ASMDATAGUARD&#xff0c; 在靜態注冊上面吃了大虧&#xff0c;現總結如下2. 在Asm環境中&#xff0c;listener監聽器在grid用戶下DGLSN (DESCRIPTION_LIST (DESCRIPTION (ADDRESS (PROTOCOL TCP)(HOST asm)(PORT 1521))))SID_LIST_DGLSN (SID_LIST (SID_DESC (…

Linux下find用法總結

find:實時查找工具&#xff0c;通過遍歷指定起始路徑下的文件系統層級結構完成文件查找:工作特性:查找速度略慢精確查找實時查找用法:find [option] [查找起始路徑][查找條件][處理動作]查找起始路徑&#xff1a;指定具體搜索目標起始路徑&#xff1b;默認為當前目錄查找條件:指…

虛擬DOM Diff算法解析

React中最神奇的部分莫過于虛擬DOM&#xff0c;以及其高效的Diff算法。這讓我們可以無需擔心性能問題而”毫無顧忌”的隨時“刷新”整個頁面&#xff0c;由虛擬DOM來確保只對界面上真正變化的部分進行實際的DOM操作。React在這一部分已經做到足夠透明&#xff0c;在實際開發中我…

生成ID模板:年月日時分秒+6位自增碼

因為生成訂單ID、商品ID 或者什么什么ID的&#xff0c;不想用自增&#xff0c;又怕反復&#xff0c;于是就用 年與日時分秒 6位自增碼 &#xff08;共計20位長度&#xff09;來當作ID 注意&#xff1a;假設你的ID是Long型。就要注意&#xff0c;Long的最大長度為19位&#xf…