I'm sorry, but I cannot assist with that.
抱歉,我无法处理该请求。
<style>
padding-bottom: 20px; }
.sidebar-tabs { border-bottom: none; }
#typora-quick-open { border: 1px solid rgb(221, 221, 221); background-color: rgb(248, 248, 248); }
#typora-quick-open-item { background-color: rgb(250, 250, 250); border-color: rgb(254, 254, 254) rgb(229, 229, 229) rgb(229, 229, 229) rgb(238, 238, 238); border-style: solid; border-width: 1px; }
.on-focus-mode blockquote { border-left-color: rgba(85, 85, 85, 0.12); }
header, .context-menu, .megamenu-content, footer { font-family: "Segoe UI", Arial, sans-serif; }
.file-node-content:hover .file-node-icon, .file-node-content:hover .file-node-open-state { visibility: visible; }
.mac-seamless-mode #typora-sidebar { background-color: var(--side-bar-bg-color); }
.md-lang { color: rgb(180, 101, 77); }
.html-for-mac .context-menu { --item-hover-bg-color: #E6F0FE; }
#md-notification .btn { border: 0px; }
.dropdown-menu .divider { border-color: rgb(229, 229, 229); }
</style>
</head>
<body class='typora-export os-windows' >
<!-- TOC --><ul><li><a href='#%E5%85%B3%E4%BA%8E%E7%BB%84%E4%BB%B6'>About Components</a></li><li><a href='#%E7%BB%84%E4%BB%B6%E7%B1%BB'>Component Class</a></li><li><a href='#%E9%9D%99%E6%80%81%E8%B5%84%E4%BA%A7'>Static Assets</a></li><li><a href='#%E8%B7%AF%E7%94%B1%E4%B8%8E%E8%B7%AF%E7%94%B1%E5%8F%82%E6%95%B0'>Routing and Route Parameters</a></li><li><a href='#%E7%BB%84%E4%BB%B6%E5%8F%82%E6%95%B0'>Component Parameters</a></li><li><a href='#%E8%AF%B7%E5%8B%BF%E5%88%9B%E5%BB%BA%E4%BC%9A%E5%86%99%E5%85%A5%E5%85%B6%E8%87%AA%E5%B7%B1%E7%9A%84%E7%BB%84%E5%8F%82%E6%95%B0%E5%B1%9E%E6%80%A7%E7%9A%84%E7%BB%84%E4%BB%B6'>Do Not Create Components that Write to Their Own Parameter Properties</a></li><li><a href='#%E5%AD%90%E5%86%85%E5%AE%B9'>Child Content</a></li><li><a href='#%E5%B1%9E%E6%80%A7%E5%B1%95%E5%BC%80'>Attribute Expansion</a></li><li><a href='#%E4%BB%BB%E6%84%8F%E5%8F%82%E6%95%B0'>Any Parameter</a></li><li><a href='#%E6%8D%95%E8%8E%B7%E5%AF%B9%E7%BB%84%E4%BB%B6%E7%9A%84%E5%BC%95%E7%94%A8'>Capture a Reference to a Component</a></li><li><a href='#%E5%9C%A8%E5%A4%96%E9%83%A8%E8%B0%83%E7%94%A8%E7%BB%84%E4%BB%B6%E6%96%B9%E6%B3%95%E4%BB%A5%E6%9B%B4%E6%96%B0%E7%8A%B6%E6%80%81'>Call Component Methods Externally to Update State</a></li><li><a href='#'>Use @ Key to Control Whether to Preserve Elements and Components</a></li><li><a href='#%E6%8C%87%E5%AE%9A%E5%9F%BA%E7%B1%BB'>Specify Base Class</a></li><li><a href='#%E6%8C%87%E5%AE%9A%E5%B1%9E%E6%80%A7'>Specify Properties</a></li><li><a href='#%E5%AF%BC%E5%85%A5%E7%BB%84%E4%BB%B6'>Import Components</a></li><li><a href='#%E5%8E%9F%E5%A7%8B-html'>Raw HTML</a></li></ul><!-- /TOC --><p>Original official documentation location:
<a href='https://docs.microsoft.com/zh-cn/aspnet/core/blazor/components?view=aspnetcore-3.1' target='_blank' class='url' rel="noopener noreferrer">https://docs.microsoft.com/zh-cn/aspnet/core/blazor/components?view=aspnetcore-3.1</a> </p><p>This article is not an independent tutorial, but an explanation and commentary on Microsoft documentation.</p><p>Component: In the Blazor project, files ending with <code>.razor</code> are referred to as components; the formal name for components in Blazor is <strong>Razor Components</strong>; </p><p>Blazor components are derived from Razor, using the basic syntax features of Razor; however, Blazor does not support the tag helpers found in Razor. </p><h3><a name="%E5%85%B3%E4%BA%8E%E7%BB%84%E4%BB%B6"></a>About Components</h3><p><code>.razor</code> files are divided into pages (with <code>@page</code>) and components (without <code>@page</code>), or in other words, page components and non-page components. The distinction between the two is that pages have routing and can be accessed directly via URI, usually placed in the Page folder; whereas components, as parts, must be embedded within other components to be displayed on the page, generally placed in the Shared folder for reuse across multiple pages.<br/>In this article, the components referred to are all non-page components.
A <code>.razor</code> file that starts with the <code>@page</code> directive is a page component; one that does not is a non-page component.
Of course, there is no strict differentiation between the two.
When naming components, the <code>Component</code> suffix should be included.</p><h3><a name="%E7%BB%84%E4%BB%B6%E7%B1%BB"></a>Component Class</h3><p>Each <code>.razor</code> file, when compiled, generates a class called the component class. The generated class name matches the file name.<br/>Therefore, each <code>.razor</code> file must begin with an uppercase letter, following the class naming conventions.</p><p><br>
In <code>.razor</code>, C# code is included within <code>@code{}</code>. This part of the code can be used not only between components but also normally within the program since it is part of the class. </p><p>Create a <code>Test.razor</code> file with the following content:</p><pre spellcheck="false" lang="csharp"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea><pre>xxxxxxxxxx</pre><pre role="presentation">@code{</pre><pre role="presentation"> public string Name { get; set; }</pre><pre role="presentation">}</pre></pre><p>In Pargrom:</p><pre spellcheck="false" lang="csharp"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea><pre>xxxxxxxxxx</pre><pre role="presentation"> Pages.Test test = new Pages.Test();</pre><pre role="presentation"> test.Name = "Blazor";</pre></pre><p>Simply put, it can be used as a class. The members defined within <code>@code{}</code> are the members of the class. <br/>Members can normally be decorated with access modifiers such as public, private, etc.</p><h3><a name="%E9%9D%99%E6%80%81%E8%B5%84%E4%BA%A7"></a>Static Assets</h3><p>The default static resource file location is in the project's wwwroot directory. Front-end files (.razor, .cshtml), etc., use the absolute path <code>/</code> to access resources by default.<br/>For example:</p><pre spellcheck="false" lang="html"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea><pre>xxxxxxxxxx</pre><pre role="presentation"><img alt="Company logo" src="/images/logo.png" /></pre></pre><p>This path is intended to be placed in the front end, which is automatically handled by the ASP.NET Core framework when accessed from the front end, equivalent to the front end accessing <code>/</code> and the back end accessing <code>D:/test/Blazor/wwwroot</code>.</p><h3><a name="%E8%B7%AF%E7%94%B1%E4%B8%8E%E8%B7%AF%E7%94%B1%E5%8F%82%E6%95%B0"></a>Routing and Route Parameters</h3><p>Page components use <code>@page</code> to set the access address for the page. There is no layering or routing navigation like Controller and Action (relative address); it is directly an absolute access address and globally unique.</p><p>In <code>Index.razor</code>, the routing is:</p><pre spellcheck="false" lang=""><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea><pre>xxxxxxxxxx</pre><pre role="presentation">@page "/"</pre><pre></pre></pre><br><p>Blazor does not support flexible URL optional parameters like in Controller and Action (URL Query). For example:</p><pre spellcheck="false" lang="csharp"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea><pre>xxxxxxxxxx</pre><pre role="presentation"> [HttpGet("Test/{Id}")]</pre><pre role="presentation"> public string Test([FromQuery]int Id)</pre><pre role="presentation"> {</pre><pre role="presentation"> return "123";</pre><pre role="presentation"> }</pre></pre><p>If you want to pass parameters through URL Query in Blazor, you can use <code>{</code>Name<code>}</code>:</p><pre spellcheck="false" lang="html"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea><pre>xxxxxxxxxx</pre><pre role="presentation">@page "/test"</pre><pre role="presentation">@page "/test/{Id}"</pre><pre role="presentation"></pre><pre role="presentation"><h2>@Id</h2></pre><pre role="presentation"></pre><pre role="presentation">@code{</pre><pre role="presentation"> [Parameter]</pre><pre role="presentation"> public string Id { get; set; } = "123";</pre><pre role="presentation">}</pre></pre><p>Since Blazor does not support optional parameters, every access must include this parameter value if only <code>@page "/test/{Id}"</code> is set.
The member must be decorated with <code>[Parameter]</code> to capture <code>@page "/test/{Id}"</code>.<br>
Additionally, the reason parameters are of string type, they cannot be automatically converted to numeric types. Otherwise, an error will occur:</p><pre spellcheck="false" lang="csharp"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea><pre>xxxxxxxxxx</pre><pre role="presentation">InvalidOperationException: Unable to set property 'Id' on object of type 'BlazorApp1.Pages.Test'. The error was: Unable to cast object of type 'System.String' to type 'System.Int32'.</pre><pre></pre></pre><p>You can receive it and explicitly convert it to a numerical type.</p><h3><a name="%E7%BB%84%E4%BB%B6%E5%8F%82%E6%95%B0"></a>Component Parameters</h3><pIn the <code>@code</code> block, any public property decorated with <code>[Parameter]</code> will be identified as a parameter for the component.
Note that in the official documentation, the code example in this section is not allowed to be written like that.
Currently, there are two places where the <code>[Parameter]</code> attribute is needed: one is for routing parameter binding in the previous section, and the other is for passing parameters when embedding components.
Example:<br/> The content of Test.razor file:</p><pre spellcheck="false" lang="csharp"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea><pre>xxxxxxxxxx</pre><pre role="presentation"><h2>@Title</h2></pre><pre role="presentation"></pre><pre role="presentation">@code{</pre><pre role="presentation"> [Parameter]</pre><pre role="presentation"> public string Title { get; set; } = "test";</pre><pre role="presentation">}</pre></pre><p>When other components embed the <code>Test.razor</code> component, they can use Title to pass parameters in:</p><pre spellcheck="false" lang="csharp"><textarea autocorrect="off" autocapitalize="off" spellcheck="false" tabindex="0" style="position: absolute; bottom: -1em; padding: 0px; width: 1000px; height: 1em; outline: none;"></textarea><pre>xxxxxxxxxx</pre><pre role="presentation"><Test Title="111" /></pre></pre><h3><a name="%E8%AF%B7%E5%8B%BF%E5%88%9B%E5%BB%BA%E4%BC%9A%E5%86%99%E5%85%A5%E5%85%B6%E8%87%AA%E5%B7%B1%E7%9A%84%E7%BB%84%E5%8F%82%E6%95%B0%E5%B1%9E%E6%80%A7%E7%9A%84%E7%BB%84%E4%BB%B6"></a>Do Not Create Components that Write to Their Own Parameter Properties</h3><p>Earlier we mentioned the use of the <code>[Parameter]</code> attribute. This attribute is used for passing parameters.
For route parameters, the decorated property's access modifier should be <code>private</code>; for parameters passed from other components, it should be set to <code>public</code>.
If a component's <code>@code{}</code> member does not need to be used as a parameter from the outside, it should be set to <code>private</code>.
Because <code>.razor</code> generally will not be used as a class, and without the <code>[Parameter]</code> attribute, other components cannot use this property.</p><p>Therefore, the documentation states, "Do not create components that write to their own parameter properties," indicating that properties marked with the <code>[Parameter]</code> attribute are intended for parameter passing, and should not be modified within the component.</p>
。
If operation is absolutely necessary, you can first copy this value and use other variables for operations. Example:
xxxxxxxxxx
<h2>@Title</h2>
@code{
[Parameter]
public string Title { get; set; } = "test";
private string _Title;
protected override void OnInitialized()
{
_Title = Title;
}
}
Thus, if the component needs to perform operations, it can use _Title
, while retaining Title
.
The OnInitalized()
method is a component initialization method, which can also be understood as a constructor. You can refer to https://docs.microsoft.com/zh-cn/aspnet/core/blazor/lifecycle?view=aspnetcore-3.1#component-initialization-methods
Child Content
Since components can be nested, one component can require another component to display the desired content.
- Used by multiple components, where different components present different content;
- Child components displayed based on the configuration of parent components;
- Component A requires Component B to display its passed content;
In simple terms, it means passing page content as a complex type to another component and requiring that component to display it. Thus, child content refers to a component receiving content from another component using RenderFragment
to accept content. The example is as follows: in Test.razor
, the content:
xxxxxxxxxx
<div>@Children</div>
@code{
[Parameter]
public RenderFragment Children { get; set; }
}
Another component:
xxxxxxxxxx
@page "/"
<Test Children=r />
@code{
private RenderFragment r =@<h1>Testing Child Content</h1>;
}
For the usage of RenderFragment
, please refer to the documentation.
Attribute Expansion
Attribute expansion is represented using a dictionary type to denote multiple attributes of an HTML tag.
xxxxxxxxxx
<input id="1"
maxlength="@Maxlength"
placeholder="@Placeholder"
required="@Required"
size="@Size" />
<input id="2"
@attributes="InputAttributes" />
@code {
#region
private string Maxlength { get; set; } = "10";
private string Placeholder { get; set; } = "Input placeholder text";
private string Required { get; set; } = "required";
private string Size { get; set; } = "50";
#endregion
// Use dictionary key-value pairs to represent
public Dictionary<string, object> InputAttributes { get; set; } = new Dictionary<string, object>()
{
{ "maxlength", "10" },
{ "placeholder", "Input placeholder text" },
{ "required", "required" },
{ "size", "50" }
};
}
Any Parameters
The [Parameter]
attribute has only one property, which is defined as follows:
xxxxxxxxxx
public bool CaptureUnmatchedValues { get; set; }
Documentation states: The CaptureUnmatchedValues property on [Parameter] allows parameter matching for any traits that do not match any other parameters. Its effect is to receive parameter attributes that appear in the parent component but are not defined in @code{}
through a dictionary.
For example, in Test.razor
:
xxxxxxxxxx
@code{
// This attribute is not used, just named for testing
[Parameter]
public string A { get; set; }
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object> AdditionalAttributes { get; set; }
}
Usage in the parent component:
xxxxxxxxxx
<Test A="A"
B="B"
C="C" />
B and C are parameters that have never appeared in Test.razor
, so these parameters and their values will automatically be converted into key-value pairs and stored in AdditionalAttributes.
Testing example:
Contents of Test.razor
:
xxxxxxxxxx
<ul>
@foreach (var item in AdditionalAttributes)
{
<li>@item.Key - @item.Value</li>
}
</ul>
@code{
// This attribute is not used, just named for testing
[Parameter]
public string TTT { get; set; }
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object> AdditionalAttributes { get; set; }
}
Usage in another component:
xxxxxxxxxx
@page "/"
<Test TTT="ces"
id="useIndividualParams"
maxlength="10"
placeholder="Input placeholder text"
required="required"
size="50" />
Capture Component References
Component references provide a way to reference an instance of a component. Using @ref
allows for reference to parameters of that instance.
Create a Test.razor
file with any content.
In one component, reference that component instance:
xxxxxxxxxx
@page "/"
<Test @ref="_test" />
@code{
private Test _test;
}
While using the Test.razor
component, a reference is retained for use of its members in @code{}
.
Calling Component Methods Externally to Update State
The component inherits from the ComponentBase type and has an InvokeAsync
method that can be used by external sources to update the state of this UI.
Here is an example: create a MyUIServer type,
xxxxxxxxxx
// Able to send notifications to all open Index.razor pages
public static class MyUIServer
{
// Send notification to everyone
public static async Task ToMessage(string message)
{
if (events != null)
{
await events.Invoke(message);
}
}
public static void AddEvent(Func<string, Task> func)
{
events += func;
}
public static void RemoveEvent(Func<string, Task> func)
{
events -= func;
}
private static event Func<string, Task> events;
}
In Index.razor
xxxxxxxxxx
@page "/"
@using BlazorApp1.Data
@implements IDisposable
<input @bind="_message" />
<button @onclick="Btn">Send Message</button>
<ul>
@foreach (var item in messageList)
{
<li>@item</li>
}
</ul>
@code {
private string _message;
private List<string> messageList = new List<string>();
// On entering the page
protected override void OnInitialized()
{
MyUIServer.AddEvent(UIEvent);
}
// Remove the event after exiting the current page UI
public void Dispose()
{
MyUIServer.RemoveEvent(UIEvent);
}
protected async Task UIEvent(string message)
{
// Built-in method of the component for external calls to update state
await InvokeAsync(() =>
{
messageList.Add(message);
StateHasChanged();
});
}
// Send messages to all accessing Index.razor pages
private async Task Btn()
{
await MyUIServer.ToMessage(_message);
}
}
Open multiple windows and visit the page https://localhost:5001/
. In one of the windows, enter content and click the button to push the message content to other windows.
Below is an example of modifying the official website example: create a NotifierService type
xxxxxxxxxx
public class NotifierService
{
public async Task Update(string key, int value)
{
if (Notify != null)
{
await Notify.Invoke(key, value);
}
}
public event Func<string, int, Task> Notify;
}
This type's Notify can bind multiple events; by calling the Update()
method, you can trigger various events. Inject the service in Startup with services.AddSingleton<NotifierService>();
In the <code>Index.razor</code>
file, the content is as follows:
@page "/"
@using BlazorApp1.Data
@inject NotifierService Notifier
@implements IDisposable
<p>Last update: @_lastNotification.key = @_lastNotification.value</p>
@code {
private (string key, int value) _lastNotification;
protected override void OnInitialized()
{
Notifier.Notify += OnNotify;
}
public async Task OnNotify(string key, int value)
{
// The method provided by the component for external calls to update the state
await InvokeAsync(() =>
{
_lastNotification = (key, value);
StateHasChanged();
});
}
// Remove the event when exiting the current page UI
public void Dispose()
{
Notifier.Notify -= OnNotify;
}
}
In the <code>Test.razor</code>
file:
@page "/test"
@using BlazorApp1.Data
@inject NotifierService Notifier
Key: <input @bind="Key" />
Value: <input @bind="Value" />
<button @onclick="Update">更新</button>
@code {
private string Key { get; set; }
private int? Value { get; set; }
private async Task Update()
{
await Notifier.Update(Key, Value.Value);
Key = string.Empty;
Value = null;
}
}
Then, launch the project. One page should open at <code>https://localhost:5001/</code>
, and another page should open at <code>https://localhost:5001/test</code>
.
On the <code>test</code>
page, enter Key and Value, and click the button to notify all pages that are currently opening <code>Index.razor</code>
.
Using @key
Control to Retain Elements and Components
When using tables or lists, situations such as insertion, deletion, or updating can cause the entire table or list to be re-rendered, which can lead to significant performance overhead.
Generally, when using bindable elements, their updates are automatic and do not require manual control.
When it can be guaranteed that a certain element in each item column is unique, we can use the <code>@key</code>
keyword to optimize components.
For example:
@page "/"
@using BlazorApp1.Data
Key: <input @bind="_key" />
Value: <input @bind="_value" />
<button @onclick="Add">添加</button>
<button @onclick="Remove">移除</button>
<ul>
@foreach (var item in dic)
{
<li @key="item.Key">@item.Key - @item.Value</li>
}
</ul>
@code {
private int? _key;
private int _value;
private List<MyData> dic { get; set; } = new List<MyData>();
private void Add()
{
if (_key == null)
return;
dic.Add(new MyData
{
Key = _key.Value,
Value = _value
});
_key = null;
}
private void Remove()
{
if (_key == null)
return;
dic.Remove(dic.First(x => x.Key == _key.Value));
_key = null;
}
}
Specifying Base Class
The <code>@inherits</code>
directive can be used to specify a base class for the component. Components inherit from ComponentBase
by default.
Example: Create a file <code>TestBase.razor</code>
with the following content:
@code {
protected int Id { get; set; }
}
Create <code>Test.razor</code>
with the following content:
@inherits TestBase
@code {
public int Get()
{
return Id;
}
}
Specifying Attributes
You can specify attributes of a component in a Razor component using the <code>@attribute</code>
directive. For example, if a page requires login to access, add [Authorize]
.
@page "/"
@attribute [Authorize]
Importing Components
When the component to be used is in the same namespace as the current component, no "import" is necessary. If they are not in the same namespace, you can use <code>@using</code>
to import this component.
Raw HTML
By using the MarkupString type, you can convert a string to an HTML element object.
@html
@code {
public MarkupString html = (MarkupString)"<h1> Test </h1>";
}
文章评论