Skip to content

Commit 71ee1ab

Browse files
perf(Tab): improve create tab item from url performance (#4445)
* fix(tab): 创建TabItem时不需要渲染TabItem组件 * 添加复现demo * 更新复现demo * refactor: 增加不渲染设置 * refactor: 重构代码 * refactor: 更新私有变量 * chore: 增加拼写字典配置 * refactor: 精简代码 * refactor: 更改参数生效生命周期 * refactor: 增加部分 Tab 参数 * doc: 更新文档 * doc: 更新示例 * refactor: 增加部分 Tab 参数 * test: 增加单元测试 * refactor: 精简代码 * doc: 更新文档 * chore: 移除测试工程 * revert: 撤销代码更改 * chore: bump version 8.10.3-beta01 * test: 更新单元测试 * test: 更新单元测试 * test: 更新单元测试 --------- Co-authored-by: Argo-AsicoTech <[email protected]>
1 parent 14871c4 commit 71ee1ab

19 files changed

+35
-701
lines changed

exclusion.dic

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,4 @@ scrlang
104104
Validata
105105
Validatable
106106
noselect
107+
Urls

src/BootstrapBlazor/BootstrapBlazor.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk.Razor">
22

33
<PropertyGroup>
4-
<Version>8.10.2</Version>
4+
<Version>8.10.3-beta01</Version>
55
</PropertyGroup>
66

77
<ItemGroup>

src/BootstrapBlazor/Components/DateTimePicker/TimePickerSetting.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ public class TimePickerSetting : ComponentBase
3939
/// <summary>
4040
/// <inheritdoc/>
4141
/// </summary>
42-
protected override void OnInitialized()
42+
protected override void OnParametersSet()
4343
{
44-
base.OnInitialized();
44+
base.OnParametersSet();
4545

4646
if (Option != null)
4747
{

src/BootstrapBlazor/Components/Tab/BootstrapBlazorAuthorizeView.cs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
namespace BootstrapBlazor.Components;
1212

1313
/// <summary>
14-
///
14+
/// BootstrapBlazorAuthorizeView 组件
1515
/// </summary>
1616
public class BootstrapBlazorAuthorizeView : ComponentBase
1717
{
@@ -49,16 +49,14 @@ public class BootstrapBlazorAuthorizeView : ComponentBase
4949
[Inject]
5050
private IAuthorizationService? AuthorizationService { get; set; }
5151

52-
#if NET6_0_OR_GREATER
5352
[Inject]
5453
[NotNull]
5554
private NavigationManager? NavigationManager { get; set; }
56-
#endif
5755

5856
private bool Authorized { get; set; }
5957

6058
/// <summary>
61-
/// OnInitializedAsync 方法
59+
/// <inheritdoc/>
6260
/// </summary>
6361
/// <returns></returns>
6462
protected override async Task OnInitializedAsync()
@@ -68,7 +66,7 @@ protected override async Task OnInitializedAsync()
6866
}
6967

7068
/// <summary>
71-
/// BuildRenderTree 方法
69+
/// <inheritdoc/>
7270
/// </summary>
7371
/// <param name="builder"></param>
7472
protected override void BuildRenderTree(RenderTreeBuilder builder)
@@ -82,17 +80,14 @@ protected override void BuildRenderTree(RenderTreeBuilder builder)
8280
{
8381
builder.AddAttribute(index++, kv.Key, kv.Value);
8482
}
85-
#if NET6_0_OR_GREATER
8683
BuildQueryParameters();
87-
#endif
8884
builder.CloseComponent();
8985
}
9086
else
9187
{
9288
builder.AddContent(0, NotAuthorized);
9389
}
9490

95-
#if NET6_0_OR_GREATER
9691
void BuildQueryParameters()
9792
{
9893
var queryParameterSupplier = QueryParameterValueSupplier.ForType(Type);
@@ -106,6 +101,5 @@ void BuildQueryParameters()
106101
queryParameterSupplier.RenderParametersFromQueryString(builder, query);
107102
}
108103
}
109-
#endif
110104
}
111105
}

src/BootstrapBlazor/Components/Tab/Route/IRouteTable.cs

Lines changed: 0 additions & 16 deletions
This file was deleted.

src/BootstrapBlazor/Components/Tab/Route/RouteConstraint.cs

Lines changed: 0 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -5,86 +5,6 @@
55
namespace Microsoft.AspNetCore.Components.Routing;
66

77
[ExcludeFromCodeCoverage]
8-
#if NET5_0
9-
internal abstract class RouteConstraint
10-
{
11-
// note: the things that prevent this cache from growing unbounded is that
12-
// we're the only caller to this code path, and the fact that there are only
13-
// 8 possible instances that we create.
14-
//
15-
// The values passed in here for parsing are always static text defined in route attributes.
16-
private static readonly ConcurrentDictionary<string, RouteConstraint> _cachedConstraints
17-
= new ConcurrentDictionary<string, RouteConstraint>();
18-
19-
public abstract bool Match(string pathSegment, out object? convertedValue);
20-
21-
public static RouteConstraint Parse(string template, string segment, string constraint)
22-
{
23-
if (string.IsNullOrEmpty(constraint))
24-
{
25-
throw new ArgumentException($"Malformed segment '{segment}' in route '{template}' contains an empty constraint.");
26-
}
27-
28-
if (_cachedConstraints.TryGetValue(constraint, out var cachedInstance))
29-
{
30-
return cachedInstance;
31-
}
32-
else
33-
{
34-
var newInstance = CreateRouteConstraint(constraint);
35-
if (newInstance != null)
36-
{
37-
// We've done to the work to create the constraint now, but it's possible
38-
// we're competing with another thread. GetOrAdd can ensure only a single
39-
// instance is returned so that any extra ones can be GC'ed.
40-
return _cachedConstraints.GetOrAdd(constraint, newInstance);
41-
}
42-
else
43-
{
44-
throw new ArgumentException($"Unsupported constraint '{constraint}' in route '{template}'.");
45-
}
46-
}
47-
}
48-
49-
/// <summary>
50-
/// Creates a structured RouteConstraint object given a string that contains
51-
/// the route constraint. A constraint is the place after the colon in a
52-
/// parameter definition, for example `{age:int?}`.
53-
/// </summary>
54-
/// <param name="constraint">String representation of the constraint</param>
55-
/// <returns>Type-specific RouteConstraint object</returns>
56-
private static RouteConstraint? CreateRouteConstraint(string constraint)
57-
{
58-
switch (constraint)
59-
{
60-
case "bool":
61-
return new TypeRouteConstraint<bool>(bool.TryParse);
62-
case "datetime":
63-
return new TypeRouteConstraint<DateTime>((string str, out DateTime result)
64-
=> DateTime.TryParse(str, CultureInfo.InvariantCulture, DateTimeStyles.None, out result));
65-
case "decimal":
66-
return new TypeRouteConstraint<decimal>((string str, out decimal result)
67-
=> decimal.TryParse(str, NumberStyles.Number, CultureInfo.InvariantCulture, out result));
68-
case "double":
69-
return new TypeRouteConstraint<double>((string str, out double result)
70-
=> double.TryParse(str, NumberStyles.Number, CultureInfo.InvariantCulture, out result));
71-
case "float":
72-
return new TypeRouteConstraint<float>((string str, out float result)
73-
=> float.TryParse(str, NumberStyles.Number, CultureInfo.InvariantCulture, out result));
74-
case "guid":
75-
return new TypeRouteConstraint<Guid>(Guid.TryParse);
76-
case "int":
77-
return new TypeRouteConstraint<int>((string str, out int result)
78-
=> int.TryParse(str, NumberStyles.Integer, CultureInfo.InvariantCulture, out result));
79-
case "long":
80-
return new TypeRouteConstraint<long>((string str, out long result)
81-
=> long.TryParse(str, NumberStyles.Integer, CultureInfo.InvariantCulture, out result));
82-
default:
83-
return null;
84-
}
85-
}
86-
}
87-
#else
888
internal static class RouteConstraint
899
{
9010
public static UrlValueConstraint Parse(string template, string segment, string constraint)
@@ -116,4 +36,3 @@ public static UrlValueConstraint Parse(string template, string segment, string c
11636
_ => null,
11737
};
11838
}
119-
#endif

src/BootstrapBlazor/Components/Tab/Route/RouteContext.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@ public RouteContext(string path)
2323

2424
public string[] Segments { get; }
2525

26-
#if NET6_0_OR_GREATER
2726
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
28-
#endif
2927
public Type? Handler { get; set; }
3028

3129
public IReadOnlyDictionary<string, object>? Parameters { get; set; }

src/BootstrapBlazor/Components/Tab/Route/RouteEntry.cs

Lines changed: 2 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -9,144 +9,6 @@
99
namespace Microsoft.AspNetCore.Components.Routing;
1010

1111
[ExcludeFromCodeCoverage]
12-
#if NET5_0
13-
[DebuggerDisplay("Handler = {Handler}, Template = {Template}")]
14-
internal class RouteEntry
15-
{
16-
public RouteEntry(RouteTemplate template, Type handler, string[] unusedRouteParameterNames)
17-
{
18-
Template = template;
19-
UnusedRouteParameterNames = unusedRouteParameterNames;
20-
Handler = handler;
21-
}
22-
23-
public RouteTemplate Template { get; }
24-
25-
public string[] UnusedRouteParameterNames { get; }
26-
27-
public Type Handler { get; }
28-
29-
internal void Match(RouteContext context)
30-
{
31-
var pathIndex = 0;
32-
var templateIndex = 0;
33-
Dictionary<string, object> parameters = null;
34-
// We will iterate over the path segments and the template segments until we have consumed
35-
// one of them.
36-
// There are three cases we need to account here for:
37-
// * Path is shorter than template ->
38-
// * This can match only if we have t-p optional parameters at the end.
39-
// * Path and template have the same number of segments
40-
// * This can happen when the catch-all segment matches 1 segment
41-
// * This can happen when an optional parameter has been specified.
42-
// * This can happen when the route only contains literals and parameters.
43-
// * Path is longer than template -> This can only match if the parameter has a catch-all at the end.
44-
// * We still need to iterate over all the path segments if the catch-all is constrained.
45-
// * We still need to iterate over all the template/path segments before the catch-all
46-
while (pathIndex < context.Segments.Length && templateIndex < Template.Segments.Length)
47-
{
48-
var pathSegment = context.Segments[pathIndex];
49-
var templateSegment = Template.Segments[templateIndex];
50-
51-
var matches = templateSegment.Match(pathSegment, out var match);
52-
if (!matches)
53-
{
54-
// A constraint or literal didn't match
55-
return;
56-
}
57-
58-
if (!templateSegment.IsCatchAll)
59-
{
60-
// We were dealing with a literal or a parameter, so just advance both cursors.
61-
pathIndex++;
62-
templateIndex++;
63-
64-
if (templateSegment.IsParameter)
65-
{
66-
parameters ??= new(StringComparer.OrdinalIgnoreCase);
67-
parameters[templateSegment.Value] = match;
68-
}
69-
}
70-
else
71-
{
72-
if (templateSegment.Constraints.Length == 0)
73-
{
74-
75-
// Unconstrained catch all, we can stop early
76-
parameters ??= new(StringComparer.OrdinalIgnoreCase);
77-
parameters[templateSegment.Value] = string.Join('/', context.Segments, pathIndex, context.Segments.Length - pathIndex);
78-
79-
// Mark the remaining segments as consumed.
80-
pathIndex = context.Segments.Length;
81-
82-
// Catch-alls are always last.
83-
templateIndex++;
84-
85-
// We are done, so break out of the loop.
86-
break;
87-
}
88-
else
89-
{
90-
// For constrained catch-alls, we advance the path index but keep the template index on the catch-all.
91-
pathIndex++;
92-
if (pathIndex == context.Segments.Length)
93-
{
94-
parameters ??= new(StringComparer.OrdinalIgnoreCase);
95-
parameters[templateSegment.Value] = string.Join('/', context.Segments, templateIndex, context.Segments.Length - templateIndex);
96-
97-
// This is important to signal that we consumed the entire template.
98-
templateIndex++;
99-
}
100-
}
101-
}
102-
}
103-
104-
var hasRemainingOptionalSegments = templateIndex < Template.Segments.Length &&
105-
RemainingSegmentsAreOptional(pathIndex, Template.Segments);
106-
107-
if ((pathIndex == context.Segments.Length && templateIndex == Template.Segments.Length) || hasRemainingOptionalSegments)
108-
{
109-
if (hasRemainingOptionalSegments)
110-
{
111-
parameters ??= new Dictionary<string, object>(StringComparer.Ordinal);
112-
AddDefaultValues(parameters, templateIndex, Template.Segments);
113-
}
114-
if (UnusedRouteParameterNames?.Length > 0)
115-
{
116-
parameters ??= new Dictionary<string, object>(StringComparer.Ordinal);
117-
for (var i = 0; i < UnusedRouteParameterNames.Length; i++)
118-
{
119-
parameters[UnusedRouteParameterNames[i]] = null;
120-
}
121-
}
122-
context.Handler = Handler;
123-
context.Parameters = parameters;
124-
}
125-
}
126-
127-
private void AddDefaultValues(Dictionary<string, object> parameters, int templateIndex, TemplateSegment[] segments)
128-
{
129-
for (var i = templateIndex; i < segments.Length; i++)
130-
{
131-
var currentSegment = segments[i];
132-
parameters[currentSegment.Value] = null;
133-
}
134-
}
135-
136-
private bool RemainingSegmentsAreOptional(int index, TemplateSegment[] segments)
137-
{
138-
for (var i = index; index < segments.Length - 1; index++)
139-
{
140-
if (!segments[i].IsOptional)
141-
{
142-
return false;
143-
}
144-
}
145-
146-
return segments[^1].IsOptional || segments[^1].IsCatchAll;
147-
}
148-
}
149-
#else
15012
[DebuggerDisplay("Handler = {Handler}, Template = {Template}")]
15113
internal class RouteEntry
15214
{
@@ -284,4 +146,5 @@ private bool RemainingSegmentsAreOptional(int index, TemplateSegment[] segments)
284146
return segments[^1].IsOptional || segments[^1].IsCatchAll;
285147
}
286148
}
287-
#endif
149+
150+
#nullable restore warnings

src/BootstrapBlazor/Components/Tab/Route/RouteKey.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
namespace Microsoft.AspNetCore.Components.Routing;
77

8-
#if NET6_0_OR_GREATER
98
[ExcludeFromCodeCoverage]
109
internal readonly struct RouteKey : IEquatable<RouteKey>
1110
{
@@ -61,4 +60,3 @@ public override int GetHashCode()
6160
return HashCode.Combine(AppAssembly, AdditionalAssemblies.Count);
6261
}
6362
}
64-
#endif

src/BootstrapBlazor/Components/Tab/Route/RouteTable.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,7 @@
55
namespace Microsoft.AspNetCore.Components.Routing;
66

77
[ExcludeFromCodeCoverage]
8-
#if NET5_0
9-
internal class RouteTable : IRouteTable
10-
#else
118
internal class RouteTable
12-
#endif
139
{
1410
public RouteTable(RouteEntry[] routes)
1511
{

0 commit comments

Comments
 (0)