多年前,我在开发ASP.NET MVC时曾使用Blazor构建了一个烘手器的网站,并成功将其优化至百度搜索排名第一。那时的技术选择让我惊叹Blazor的简易性。然而,随着时光流逝,这似乎成为一个失败的尝试。
随后,前后端分离成为主流,我转向使用类似Vue的技术。同时,我不再过多关注SEO,因为百度权重不再像从前那样关键。这就如同现在的女生可能不再在意男友是否是厂里的优秀工作者或是否获得五一奖等,而更注重实际的经济基础。在这个时候,追求财务收益变得更为实际,其他的言辞已经显得无关痛痒。
最近,由于公司项目需要iOS开发,但又缺乏iOS程序员,于是我寻找了解决方案。我先尝试了Flutter,成功完成了一个项目。考虑到工作经验的积累,我思考是否找一个工具可以一劳永逸地解决所有问题。于是,我在业余时间学习了Unity和Godot。当我发现了MAUI时,我仿佛找到了职业生涯的一根救命稻草。.NET不仅可以用于开发Web应用、编写RESTful服务,而且Unity、Godot等游戏引擎也都支持C#。而MAUI更是能够实现跨平台开发,现在还发现了MAUI Blazor,简直就像是购买了一瓶白酒,里面还意外中了一张奖券,全都是额外赠送的好处。
我已经厌倦了在开发过程中不同语言的切换,以及不同工具的安装。Visual Studio就像中国厨师用来切菜的利器,一把就能解决各种问题。这种一体化的开发环境让我的工作更加高效和愉快。
第一次新建项目,项目名叫mauiApp 没有想到这名字跟maui的关键字重复,花了好大时间发现这一个问题。
以下定义了start?
Router.razor
<Router AppAssembly="@typeof(MauiProgram).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Components.Layout.MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
</Router>
_imports.razor 这里是引用的模块,后面要引用到
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using MauiAppBlazorapp
@using MauiAppBlazorapp.Components
wwwroot下的index.html感觉是一个WEB服务一样,这里的/表示把 定义的路由页当首页。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<title>第一个Mauiblazor项目</title>
<base href="/" />
<link rel="stylesheet" href="css/bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="css/app.css" />
<link rel="stylesheet" href="MauiAppBlazorapp.styles.css" />
<link rel="icon" type="image/png" href="favicon.png" />
</head>
<body>
<div class="status-bar-safe-area"></div>
<div id="app">Loading...</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webview.js" autostart="false"></script>
</body>
</html>
layout的页面模板 这时引入菜单
@inherits LayoutComponentBase
<div class="page">
<div class="sidebar">
<NavMenu />
</div>
<main>
<div class="top-row px-4">
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
</div>
<article class="content px-4">
@Body
</article>
</main>
</div>
菜单NavMenu.razor
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">MauiAppBlazorapp</a>
</div>
</div>
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="weather">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
</NavLink>
</div>
</nav>
</div>
pages下面有三个子页,通过分析NavMenu.razor 则点击菜单跳转到href,跳转到三个页面中的一个。下面是html javascript,看语法象typescript .
@page "/counter"
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
``@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
@page "/weather"
<h1>Weather</h1>
<p>This component demonstrates showing data.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
// Simulate asynchronous loading to demonstrate a loading indicator
await Task.Delay(500);
var startDate = DateOnly.FromDateTime(DateTime.Now);
var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" };
forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = summaries[Random.Shared.Next(summaries.Length)]
}).ToArray();
}
private class WeatherForecast
{
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}
在pages里加入一个Login.razor
@page "/"
<h1>LOGIN!</h1>
Welcome to LOGIN.
<div style="text-align: center; margin-top: 20px;">
<h1>LOGIN!</h1>
<p>Welcome to LOGIN.</p>
<div style="margin-top: 20px;">
<label class="col-form-label">用户名</label>
<input type="text" id="username" @bind="adminusername" class="form-control" />
</div>
<div style="margin-top: 20px;">
<label class="col-form-label">密码</label>
<input type="password" id="password" @bind="adminpassword" class="form-control" />
</div>
<div style="margin-top: 20px;">
<button @onclick="AuthenticateCredentials" id="login" style="background-color: darkred;" class="btn btn-primary btn-medium">Login</button>
</div>
@if (!string.IsNullOrWhiteSpace(message))
{
<div class="alert alert-danger" role="alert">
@message
</div>
}
</div>
@code {
public string adminusername { get; set; }
public string adminpassword { get; set; }
public string message { get; set; }
public void AuthenticateCredentials()
{
if (adminusername == "myusername" && adminpassword == "mypassword")
{
message = "登录成功!";
// 这里可以添加其他登录成功的逻辑
}
else
{
message = "登录失败,请检查用户名和密码!";
// 这里可以添加其他登录失败的逻辑
}
}
}
再修改一下Navmenu.razor 改一下Home的跳转为/home:
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">演示</a>
</div>
</div>
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="/" Match="NavLinkMatch.All">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="home" aria-hidden="true">
<span class="bi bi-house-door-fill-nav-menu" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="bi bi-plus-square-fill-nav-menu" aria-hidden="true"></span> Counter
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="weather">
<span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Weather
</NavLink>
</div>
</nav>
</div>