• 【Blazor】在ASP.NET Core中使用Blazor组件


    前言

    Blazor正式版的发布已经有一段时间了,.NET社区的各路高手也创建了一个又一个的Blazor组件库,其中就包括了我和其他小伙伴一起参与的AntDesign组件库,于上周终于发布了第一个版本0.1.0,共计完成了59个常用组件,那么今天就来聊一聊如何在ASP.NET Core MVC项目中使用这些Blazor组件吧

    环境搭建

    .NET Core SDK 3.0.301

    Vistual Studio 2019.16.6.3

    调用Blazor组件

    创建ASP.NET Core MVC项目,如果想要在已有的项目上使用AntDesign,需要确保Target Framework是netcoreapp3.1,然后在Nuget中搜索并安装AntDesign 0.1.0版本。

    修改Startup.cs

    在ConfigureServices方法中添加

    1 // add for balzor
    2 services.AddServerSideBlazor();
    3 // add for AntDesign
    4 services.AddAntDesign();

    在Configure方法中添加

    1 app.UseEndpoints(endpoints =>
    2 {
    3     endpoints.MapControllerRoute(
    4     name: "default",
    5     pattern: "{controller=Home}/{action=Index}/{id?}");
    6     // add for blazor
    7     endpoints.MapBlazorHub();
    8 });

    修改./Views/Shared/_Layout.cshtml

    在head区域添加

    1 <!--add for AntDesign-->
    2 <link href="/_content/AntDesign/css/ant-design-blazor.css" rel="stylesheet">
    3 <base href="/" />

    在script区域添加

    1 <!--add for blazor-->
    2 <script src="~/_framework/blazor.server.js"></script>

    这里我们需要利用一个中间层,否则直接在View里添加组件会有很多限制,不太方便

    创建一个razor文件./Components/HelloWorld.razor

     1 @using AntDesign
     2  
     3 <Button type="primary" OnClick="(e)=>OnClick(e)">@_content</Button>
     4  
     5 @code{
     6     private string _content = "Primay";
     7     private void OnClick(Microsoft.AspNetCore.Components.Web.MouseEventArgs args)
     8     {
     9         _content += "*";
    10     }
    11 }

    最后在View中添加刚刚新建的中间组件

    修改./Views/Home/Index.cshtml,添加代码

    1 <component type="typeof(HelloWorld)" render-mode="ServerPrerendered" />

    Build & Run

    这时候主页应该会出现一个ant-design风格的button,点击后button的内容会变为Priamary*,每点击一次就多一个*,效果如下

     

    小结

    一般来说,在MVC项目中,先将界面需要使用的组件组合在一起,然后整体包装在一个中间组件(HelloWolrd.razor)中,最后在调用的View中展示中间组件。可以理解为组件库为我们提供了各种各样的零件,中间层将这些零件(以及原生HTML标签)组合成一个产品,最后在View中展示产品。

    创建一个播放器组件

    首先我们创建好需要用到的JavaScript脚本

    Nuget安装Microsoft.TypeScript.MSBuild

    创建文件main.ts

     1 interface Window {
     2     Music: any;
     3 }
     4  
     5 function Play(element, flag) {
     6     var dom = document.querySelector(element);
     7     if (flag) {
     8         dom.play();
     9     }
    10     else {
    11         dom.pause();
    12     }
    13 }
    14  
    15 function GetMusicTime(element) {
    16     var dom = document.querySelector(element);
    17     let obj = {
    18         currentTime: dom.currentTime,
    19         duration: dom.duration
    20     }
    21     let json = JSON.stringify(obj);
    22  
    23     return json
    24 }
    25  
    26 function SetMusicTime(element, time) {
    27     var dom = document.querySelector(element);
    28     dom.currentTime = time;
    29 }
    30  
    31 window.Music = {
    32     print: Print,
    33     play: Play,
    34     getMusicTime: GetMusicTime,
    35     setMusicTime: SetMusicTime
    36 }

    创建文件tsconfig.json

    {
      "compileOnSave": true,
      "compilerOptions": {
        "noImplicitAny": false,
        "noEmitOnError": true,
        "removeComments": false,
        "sourceMap": false,
        "target": "es2015",
        "outDir": "wwwroot/js"
      },
      "files": [ "main.ts" ],
      "exclude": [
        "node_modules",
        "wwwroot"
      ]
    }

    创建文件夹./wwwroot/music/

    放入几首你喜欢的音乐,但要注意支持的文件格式

    <audio> can be used to play sound files in the following formats:

    .mp3: Supported by all modern browsers.
    .wav: Not supported by Internet Explorer.
    .ogg: Not supported by Internet Explorer and Safari.
     

    创建./Components/MusicPlayer.razor

     1 @namespace SoBrian.MVC.Components
     2 @inherits AntDesign.AntDomComponentBase
     3  
     4 <audio id="audio" preload="auto" src="@_currentSrc"></audio>
     5 <div>
     6     <AntDesign.Row Justify="center" Align="middle">
     7         <AntDesign.Col Span="4">
     8             <p>@System.IO.Path.GetFileNameWithoutExtension(_currentSrc)</p>
     9         </AntDesign.Col>
    10         <AntDesign.Col Span="4">
    11             <AntDesign.Space>
    12                 <AntDesign.SpaceItem>
    13                     <AntDesign.Button Type="primary" Shape="circle" Icon="left" OnClick="OnLast" />
    14                 </AntDesign.SpaceItem>
    15                 <AntDesign.SpaceItem>
    16                     <AntDesign.Button Type="primary" Shape="circle" Icon="@PlayPauseIcon" Size="large" OnClick="OnPlayPause" />
    17                 </AntDesign.SpaceItem>
    18                 <AntDesign.SpaceItem>
    19                     <AntDesign.Button Type="primary" Shape="circle" Icon="right" OnClick="OnNext" />
    20                 </AntDesign.SpaceItem>
    21             </AntDesign.Space>
    22         </AntDesign.Col>
    23         <AntDesign.Col Span="9">
    24             <AntDesign.Slider Value="@_currentTimeSlide" OnAfterChange="OnSliderChange" />
    25         </AntDesign.Col>
    26         <AntDesign.Col Span="3">
    27             <p>@($"{_currentTime.ToString("mm\:ss")} / {_duration.ToString("mm\:ss")}")</p>
    28         </AntDesign.Col>
    29     </AntDesign.Row>
    30 </div>

    创建./Components/MusicPlayer.razor.cs

      1 public partial class MusicPlayer : AntDomComponentBase
      2 {
      3     private bool _isPlaying = false;
      4     private bool _canPlayFlag = false;
      5     private string _currentSrc;
      6     private List<string> _musicList = new List<string>
      7     {
      8         "music/周杰伦 - 兰亭序.mp3",
      9         "music/周杰伦 - 告白气球.mp3",
     10         "music/周杰伦 - 听妈妈的话.mp3",
     11         "music/周杰伦 - 园游会.mp3",
     12         "music/周杰伦 - 夜曲.mp3",
     13         "music/周杰伦 - 夜的第七章.mp3",
     14         "music/周杰伦 - 搁浅.mp3"
     15     };
     16     private Timer _timer;
     17     private double _currentTimeSlide = 0;
     18     private TimeSpan _currentTime = new TimeSpan(0);
     19     private TimeSpan _duration = new TimeSpan(0);
     20     private string PlayPauseIcon { get => _isPlaying ? "pause" : "caret-right"; }
     21     private Action _afterCanPlay;
     22     [Inject]
     23     private DomEventService DomEventService { get; set; }
     24  
     25     protected override void OnInitialized()
     26     {
     27         base.OnInitialized();
     28  
     29         _currentSrc = _musicList[0];
     30         _afterCanPlay = async () =>
     31         {
     32             // do not use _isPlaying, this delegate will be triggered when user clicked play button
     33             if (_canPlayFlag)
     34             {
     35                 try
     36                 {
     37                     await JsInvokeAsync("Music.play", "#audio", true);
     38                     _canPlayFlag = false;
     39                 }
     40                 catch (Exception ex)
     41                 {
     42                 }
     43             }
     44         };
     45     }
     46  
     47     protected override Task OnFirstAfterRenderAsync()
     48     {
     49         // cannot listen to dom events in OnInitialized while render-mode is ServerPrerendered
     50         DomEventService.AddEventListener<JsonElement>("#audio", "timeupdate", OnTimeUpdate);
     51         DomEventService.AddEventListener<JsonElement>("#audio", "canplay", OnCanPlay);
     52         DomEventService.AddEventListener<JsonElement>("#audio", "play", OnPlay);
     53         DomEventService.AddEventListener<JsonElement>("#audio", "pause", OnPause);
     54         DomEventService.AddEventListener<JsonElement>("#audio", "ended", OnEnd);
     55         return base.OnFirstAfterRenderAsync();
     56     }
     57  
     58         #region Audio EventHandlers
     59  
     60         private async void OnPlayPause(MouseEventArgs args)
     61         {
     62             try
     63             {
     64                 await JsInvokeAsync("Music.play", "#audio", !_isPlaying);
     65         }
     66             catch (Exception ex)
     67             {
     68             }
     69         }
     70  
     71     private async void OnCanPlay(JsonElement jsonElement)
     72     {
     73         try
     74         {
     75             string json = await JsInvokeAsync<string>("Music.getMusicTime", "#audio");
     76             jsonElement = JsonDocument.Parse(json).RootElement;
     77             _duration = TimeSpan.FromSeconds(jsonElement.GetProperty("duration").GetDouble());
     78  
     79             _afterCanPlay();
     80         }
     81         catch (Exception)
     82         {
     83         }
     84     }
     85  
     86     private void OnPlay(JsonElement jsonElement)
     87     {
     88         _isPlaying = true;
     89     }
     90  
     91     private async void OnLast(MouseEventArgs args)
     92     {
     93         _canPlayFlag = true;
     94         int index = _musicList.IndexOf(_currentSrc);
     95         index = index == 0 ? _musicList.Count - 1 : index - 1;
     96         _currentSrc = _musicList[index];
     97     }
     98  
     99     private async void OnNext(MouseEventArgs args)
    100     {
    101         _canPlayFlag = true;
    102         int index = _musicList.IndexOf(_currentSrc);
    103         index = index == _musicList.Count - 1 ? 0 : index + 1;
    104         _currentSrc = _musicList[index];
    105     }
    106  
    107     private void OnPause(JsonElement jsonElement)
    108     {
    109         _isPlaying = false;
    110         StateHasChanged();
    111     }
    112  
    113         private void OnEnd(JsonElement jsonElement)
    114     {
    115         _isPlaying = false;
    116         StateHasChanged();
    117  
    118         OnNext(new MouseEventArgs());
    119     }
    120  
    121     private async void OnTimeUpdate(JsonElement jsonElement)
    122     {
    123         // do not use the timestamp from timeupdate event, which is the total time the audio has been working
    124         // use the currentTime property from audio element
    125         string json = await JsInvokeAsync<string>("Music.getMusicTime", "#audio");
    126         jsonElement = JsonDocument.Parse(json).RootElement;
    127         _currentTime = TimeSpan.FromSeconds(jsonElement.GetProperty("currentTime").GetDouble());
    128         _currentTimeSlide = _currentTime / _duration * 100;
    129  
    130         StateHasChanged();
    131     }
    132  
    133     #endregion
    134  
    135     private async void OnSliderChange(OneOf<double, (double, double)> value)
    136     {
    137         _currentTime = value.AsT0 * _duration / 100;
    138         _currentTimeSlide = _currentTime / _duration * 100;
    139         await JsInvokeAsync("Music.setMusicTime", "#audio", _currentTime.TotalSeconds);
    140     }
    141 }

    创建./Controllers/MusicController.cs

    1 public class MusicController : Controller
    2 {
    3     public IActionResult Index(string name)
    4     {
    5         return View();
    6     }
    7 }

    创建./Views/Music/Index.cshtml

    1 <component type="typeof(MusicPlayer)" render-mode="Server" />

    修改./Views/Shared/_Layout.cshtml,添加以下代码

    1 <li class="nav-item">
    2     <a class="nav-link text-dark" asp-area="" asp-controller="Music" asp-action="Index">Music</a>
    3 </li>

    Build & Run

    点击菜单栏的Music,效果如下

    总结

    WebAssembly并不是JavaScript的替代品,Blazor当然也不是,在开发Blazor组件的过程中,大部分情况下,仍然要通过TypeScript / JavaScript来与DOM进行交互,比如在这个播放器的案例中,还是需要JavaScript来调用audio的play,pause等方法。但是在View层面使用播放器这个组件时,我们几乎可以不再关心JavaScript的开发。这让前端的开发更类似于开发WPF的XAML界面。事实上,社区里也有这样的项目,致力于提供一种类WPF界面开发的组件库。

    同时,也希望大家能多多关注国内小伙伴们共同参与开发的AntDesign,作为最热门的Blazor组件库之一,在今年的MS Build大会上也获得了微软官方的认可。虽然目前组件还有不少BUG和性能问题,但是在社区的努力下,相信它会越来越好,让我们一起为.NET生态添砖加瓦!

    社区组件库:

    https://github.com/ant-design-blazor/ant-design-blazor

    https://github.com/ArgoZhang/BootstrapBlazor

    参考:

    https://catswhocode.com/html-audio-tag

    https://www.w3schools.com/TAGS/tag_audio.asp

  • 相关阅读:
    php Windows系统 wamp集成环境下redis的使用
    IO流文件拷贝
    IO流框架
    Map集合
    泛型
    Deque(队列)
    List接口
    Iterator接口(迭代器)
    Java中的异常详解
    Java中的正则表达式
  • 原文地址:https://www.cnblogs.com/brian-ding/p/13255073.html
Copyright © 2020-2023  润新知