Blazor WASM 实现人民币大写转换器

DotNetCore实战

共 6937字,需浏览 14分钟

 ·

2021-04-13 13:45

导语

.NET 5 正式发布已经有一段时间了,其中 Blazor 技术是该版本的亮点之一。作为微软技术的被坑者,年少的我曾经以为 SilverLight 能血虐 Flash,Zune 能团灭 iPod,WP 能吊打 iPhone,UWP 能统一全平台…… 可是后…… 最终步入大龄程序员的我发现,只有陪伴了我将近 20 年的ASP.NET 还没有完蛋。于是我这两天花了点时间,尝试将我的一个 UWP 小工具用 Blazor 重写,分享给大家。

无法抢救的 UWP


人民币大写转换器” 是我年少无知时开发的小工具之一,它的主要功能有:

- 将数字金额转化为大写中文

- 复制结果

- 使用中文语音朗读结果

- 显示参照表

可惜 UWP 不论是充满 Bug 的 SDK,Runtime,还是微软的龟速更新与混乱的规划,都已经无可救药了,是时候给应用找个新家了。

Blazor


Blazor 是 .NET Core 时代微软推出的用于 Web 应用开发的新框架,它可以运行在服务器端,也可使用 WASM 运行在客户端,即浏览器中。

对我来说,这个技术最吸引人的,就是 WASM。像我这种已经30多岁,学不了新东西的 .NET 程序员,根本搞不定 Angular、Vue、React 这些花里胡哨的框架,而 Blazor WASM 是把 .NET 运行时搬到了浏览器端,和 SilverLight 类似,但这次是以WASM标准的形式运行,不需要安装插件,并且也能跨平台。

于是我可以继续使用熟悉的 .NET 和 C# 开发 SPA Web 应用。更重要的是,既然是原汁原味的 .NET,就可以很方便的重用以前的代码,以及现成的成千上万个 NuGet 包,而不用像一个新发明的框架那样从0开始积累生态。

我 996 了 2 小时,成功将“人民币大写转换器”重写到 Blazor WASM,效果如下:

Demo:https://rmbcap.azurewebsites.net/
源代码:https://github.com/EdiWang/RMBCapitalization-Blazor

由于篇幅关系,本文不叙述重写的每处细节,只参数关键点。其他细节大家可到 GitHub 阅读源代码了解。

创建 Blazor WASM 工程

我们可以使用 Visual Studio 2019 创建 Blazor WASM 工程。

选择 Blazor WebAssembly App 就可以了

工程结构

一个 Blazor WASM 项目的典型结构如上图。Program.cs 包含应用如何启动与承载的逻辑。

wwwroot 中的文件为纯 HTML/CSS/JS 文件,不包含.NET的逻辑。其中 index.html 为承载应用的默认页面,和 Angular 等 SPA 框架非常类似,它将会把应用页面加载到 <div id="app"> 中。

MainLayout.razor 是整个应用的布局页面,如果你有多个页面和视图,那么通常这里会放 Header,Footer 等内容。

Index.razor 为应用的默认主页。我这个应用只有一个页面,所以一切逻辑都在这里实现就可以了。

可重用的代码

人民币大写的转换类与框架和平台无关,因此完全可以直接复制到Blazor工程里用,即 RMBConverter.cs。

UWP 应用的视图通常采用 MVVM 模式开发,这些逻辑可以很方便的迁移到 Blazor。

Index.razor

就像写 MVC 的 cshtml 一样,使用熟悉的 Razor 语法,就能绑定数据和事件。

对于 input,简单的双向数据绑定可以直接用 @bind="属性" 实现。但我这个应用里要求用户一边输入金额一边进行实时计算,所以只能写成事件绑定。

<div>

    <h3> @Result </h3>

</div>

<div>

    <div>

        <input type="text" @bind-value="InputAmount" @bind-value:event="oninput" />

    </div>

    <div>

        <button @onclick="CopyResult">复制</button>

        <button @onclick="ReadAloud">朗读</button>

        <button @onclick="Clear">清除</button>

    </div>

</div>

对于有参数的事件处理函数,要注意它和正常 C# 写事件一样,是个 Lambda 表达式,如果放在循环里的话要注意变量的值是在循环里被修改。

下面的代码必须使用 var num = i 来存储 i 的值,如果直接使用 KeyPadClicked(i),那么 i 一定永远等于1。

<div class="row">

    @for (int i = 1; i <= 9; i++)

    {

var num = i;

        <div class="col-4">

   <button class="btn btn-light key" @onclick="() => KeyPadClicked(num.ToString())">@i</button>

        </div>

    }

</div>

<div class="row">

    <div class="col-8">

 <button class="btn btn-light key" @onclick='() => KeyPadClicked("0")'>0</button>

    </div>

    <div class="col">

 <button class="btn btn-light key" @onclick='() => KeyPadClicked(".")'>.</button>

    </div>

</div>

和 XAML 的 MVVM 以及 Angular 稍有不同的是,处理逻辑不是在 code behind 文件里写的,而是在 razor 页面本身写。如果能写成 Index.razor.cs 就干净了。

@code {


    private string _inputAmount;


    public string InputAmount

    {

        get => _inputAmount;

        set

        {

            _inputAmount = value;


            // 验证和处理逻辑...

            Result = string.IsNullOrWhiteSpace(_inputAmount) ?

                    string.Empty :

                    RMBConverter.GetCapitalizedRmb(InputAmount);

        }

    }


    public string Result { get; set; }


    private async Task CopyResult()

    {

        // ...

    }


    private async Task ReadAloud()

    {

        // ...

    }


    private void Clear()

    {

        InputAmount = string.Empty;

    }


    private void KeyPadClicked(string value)

    {

        InputAmount += value switch

        {

            "0" when InputAmount != "0" => 0,

            "." when !InputAmount.Contains(".") => ".",

            _ => value

        };

    }

}

需要重新实现的功能

复制文字

在 UWP 中,复制可以调用 Windows 的 Clipboard API 来完成。但是在浏览器端,没有 Windows 的 API,Blazor 也没有封装剪切板 API,因此我们只能借用 JS 来完成。

index.html

window.clipboardCopy = {

    copyText: function (text) {

        navigator.clipboard.writeText(text).then(function () {

            console.log(text);

        })

            .catch(function (error) {

                alert(error);

            });

    }

};

Index.razor

使用依赖注入,引入 IJSRuntime 的实例。这是 Blazor 用于和 JavaScript 交互的接口。

@inject IJSRuntime JavaScriptRuntime

然后就可以调用 JS 进行复制

private async Task CopyResult()

{

    if (!string.IsNullOrWhiteSpace(Result))

    {

        await JavaScriptRuntime.InvokeVoidAsync("clipboardCopy.copyText", Result);

    }

}

朗读

类似的,在 UWP 里,朗读使用的是 Windows 的 SpeechSynthesizer API。浏览器端也有个类似的 SpeechSynthesisUtterance。

index.html

window.readAloud = {

    readText: function (text) {

        let utterance = new SpeechSynthesisUtterance(text);

        utterance.lang = 'zh-CN';

        speechSynthesis.speak(utterance);

    }

}

Index.razor

private async Task ReadAloud()

{

    if (!string.IsNullOrWhiteSpace(Result))

    {

        await JavaScriptRuntime.InvokeVoidAsync("readAloud.readText", Result);

    }

}

本地应用


目前 Blazor WASM 还没有本地应用的官方支持,必须打开浏览器才能使用,现有的版本只能使用PWA完成一部分本地应用化操作。但在今年即将发布的 .NET 6 版本中,Blazor 会迎来官方最纯正的本地应用支持。只要不出自 SilverLight、Zune、WP、WinRT、UWP 团队之手,就不会被坑!

现存的问题


Blazor WASM 虽然看着香,但目前有一些痛点还有待解决。

首先,框架本身的体积依然较大,由于众所周知而不可描述的原因,如果服务器部署在海外,那么我国网络加载 Blazor 应用会比较慢。

另外,不是所有版本的浏览器都可以跑 WASM,尤其是手机端。Blazor 的兼容性相比 Angular,Vue,React 等,还有些差距。

总结


使用 Blazor WASM 开发 Web 应用能够让 .NET 程序员充分利用既有的知识和技能快速上手,结合 Web 的强大生态 与 .NET 的高效生产力,成就不凡。而 UWP 只能哭晕在厕所也没人听见……


往期精彩回顾




【推荐】.NET Core开发实战视频课程 ★★★

.NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划

【.NET Core微服务实战-统一身份认证】开篇及目录索引

Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南)

.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了

10个小技巧助您写出高性能的ASP.NET Core代码

用abp vNext快速开发Quartz.NET定时任务管理界面

在ASP.NET Core中创建基于Quartz.NET托管服务轻松实现作业调度

现身说法:实际业务出发分析百亿数据量下的多表查询优化

关于C#异步编程你应该了解的几点建议

C#异步编程看这篇就够了

浏览 46
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报