Microsoft's official plan at MAUI is to use Blazor for development, but most web projects on the current market are built using Vue, React and other technologies. If we cannot bypass the accumulated technologies, it is not realistic to rewrite the entire project with Blazor.
Vue is the current popular web framework. Simply put, it is a template engine that uses the two major features of "template" and "binding" to achieve mvvm mode development of web pages. The. NET MAUI framework allows you to embed Vue applications into Web containers. Cross-platform hybrid development can be achieved.
For example, in a medical industry project, I have used this hybrid development method to generate applications. Vue code can run across platforms without making any changes:

If you have a set of websites developed by Vue, you can follow this article and try to move values to your mobile devices such as iPhone, Android, and tablets.
The core work of hybrid development is to build interoperability between the web and. net. We will leverage the following features of the Blazor engine:
- Unified management of resources
- Injection of js code
- js calls C#code
- C#calls js code
如果你还不了解混合开发的概念,请回看上一章节[MAUI] 混合开发概念_jevonsflash 的专栏-CSDN 博客
The whole work is divided into MAUI part, Vue part and hybrid transformation.
MAUI Part
Create a Maui App project:

You can also create a Maui Blazor App project called MatoProject, but this template is mainly developed around Blazor, and we don't need some functions and have to delete a lot of files.
After the creation is completed, edit MatoProject.csproj and add. Razor to the end of Sdk. VS will automatically install the Microsoft.AspNetCore.Components. WebView.Maui dependent package (be careful not to add this package manually by NuGet, otherwise the program will not run)


Create a wwwroot folder in the project directory after installation is complete

This folder will be the root directory of the mixed development web section. This name cannot be defined casually. Let's see why:
Open the file Microsoft.AspNetCore.Components. WebView. Maui.targets:

我们可以看到构建项目时,这个库会将 wwwroot 文件夹里的内容作为 Maui 资源(MauiAsset)类型设置标签,编译器则会根据 MauiAsset 标签将这些内容打包进各个平台的资源文件夹,具体的 Maui 资源类型可以参考这个文章.NET MAUI – Manage App Resources – Developer Thoughts (egvijayanand.in) ,
Open MauiProgram.cs to register the BlazorMauiWebView component in builder, and use the extension method AddBlazorWebView () in the service to add services related to Blazor.
using Microsoft.Maui;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Controls.Compatibility;
using Microsoft.Maui.Controls.Hosting;
using Microsoft.AspNetCore.Components.WebView.Maui;
using Microsoft.Extensions.DependencyInjection;
namespace MatoProject
{
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.RegisterBlazorMauiWebView()
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
builder.Services.AddBlazorWebView();
return builder.Build();
}
}
}
Open MainPage.xaml and edit the main page of the native application:
Create a BlazorWebView control to cover the screen, and set HostPage as the homepage of the Web section, index.html
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MatoProject.MainPage"
xmlns:b="clr-namespace:Microsoft.AspNetCore.Components.WebView.Maui;assembly=Microsoft.AspNetCore.Components.WebView.Maui"
BackgroundColor="{DynamicResource SecondaryColor}">
<Grid>
<b:BlazorWebView HostPage="wwwroot/index.html">
<b:BlazorWebView.RootComponents>
<b:RootComponent Selector="#blazorapp" x:Name="MainWebView" ComponentType="{x:Type local:Index}/>
</b:BlazorWebView.RootComponents>
</b:BlazorWebView>
</Grid>
</ContentPage>
Create_import. razor
@using System.Net.Http @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 MatoProject
Vue part
Now that we have established a natively developed web container, we need to deal with the Vue project:
cd to the project directory and use vue-cli to create a blank Vue project:

This can be built according to Vue's programming preferences. For example, I chose the 2.0 project, which supports Typescript, es6 class naming method, etc., and will eventually be packaged into static resources through webpack, so it doesn't matter.
Create src/api/fooService.ts and create the following function:
The window ['DotNet'] object will be an interactive operation object injected in MAUI Blazor
export async function GetAll(data) {
var result = null;
await window["DotNet"]
.invokeMethodAsync("MatoProject", "GetFoo")
.then((data) => {
console.log("DotNet method return the value:" + data);
result = data;
});
return result;
}
export async function Add(data) {
var result = null;
await window["DotNet"]
.invokeMethodAsync("MatoProject", "Add", data)
.then((data) => {
console.log("DotNet method return the value:" + data);
result = data;
});
return result;
}
Open Home.vue Editor:
This is the main page of the Web. We need three buttons and related functions to test the interaction between js and C#.
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png" />
<div>
<h3>foo:</h3>
<button @click="getFoo">click to get foo</button>
<br />
<span>{{ foo }}</span>
</div>
<div>
<h3>bar:</h3>
<span>{{ bar }}</span>
</div>
<div>
<button @click="add">click here to add</button>
<span>click count:{{ cnt }}</span>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import HelloWorld from "@/components/HelloWorld.vue"; // @ is an alias to /src
import { GetAll, Add } from "@/api/fooService";
@Component({
components: {
HelloWorld,
},
})
export default class Home extends Vue {
foo: string = "";
bar: string = "";
cnt: number = 0;
async created() {
window["postBar"] = this.postBar;
}
async add() {
this.cnt = await Add({ a: this.cnt, b: 1 });
}
async getFoo() {
var foo = await GetAll(null);
this.foo = foo;
}
async postBar(data) {
this.bar = data;
console.log("DotNet invocked the function with param:" + data);
return this.bar;
}
}
</script>
So far, a simple Vue project has been completed
Run the packaging command:
PS D:\Project\maui-vue-hybirddev\hybird-host> yarn build
Copy everything in the dist directory to the wwwroot folder.
mixed transformation
This is the focus of hybrid development, transforming the MAUI project to adapt to Vue
Open wwwroot/index.js and rewrite it to:
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<link rel="icon" href="favicon.ico" />
<title>hybird-host</title>
<link href="js/about.dc8b0f2b.js" rel="prefetch" />
<link href="css/app.03043124.css" rel="preload" as="style" />
<link
href="js/app.b6b5425b.js"
rel="preload"
as="script"
crossorigin="anonymous"
/>
<link
href="js/chunk-vendors.cf6d8f84.js"
rel="preload"
as="script"
crossorigin="anonymous"
/>
<link href="css/app.03043124.css" rel="stylesheet" />
</head>
<body>
<div id="blazorapp">Loading...</div>
<script src="_framework/blazor.webview.js" autostart="false"></script>
</body>
</html>
-
- Note that only rewrite the body part completely, do not change the link tag content of the head, and only add crosssorigin ="anonymous" after js to solve the cross-domain problem. **
Create the Index.razor file:
@using Microsoft.Maui.Controls @inject IJSRuntime JSRuntime @implements
IDisposable
<noscript
><strong
>We're sorry but CareAtHome doesn't work properly without JavaScript
enabled. Please enable it to continue.</strong
></noscript
>
<div id="app"></div>
@code { [JSInvokable] public static Task<string>
GetFoo() { return Task.FromResult("this is foo call C# method from js"); }
[JSInvokable] public static Task<int>
Add(AddInput addInput) { return Task.FromResult(addInput.a + addInput.b); }
public async void Post(object o, EventArgs a) { await
JSRuntime.InvokeAsync<string
>("postBar", "this is bar call js method from C#"); } protected override
async Task OnAfterRenderAsync(bool firstRender) { ((App.Current as
App).MainPage as MainPage).OnPostBar += this.Post; try { if (firstRender)
{ await JSRuntime.InvokeAsync<IJSObjectReference
>("import", "./js/chunk-vendors.cf6d8f84.js", new { crossorigin =
"anonymous" }); await JSRuntime.InvokeAsync<IJSObjectReference
>("import", "./js/app.b6b5425b.js", new { crossorigin = "anonymous"
}); } } catch (Exception ex) { Console.WriteLine(ex); } } public void
Dispose() { (Application.Current.MainPage as MainPage).OnPostBar -=
this.Post; } }</IJSObjectReference
></IJSObjectReference
></string
></int
></string
>
Note that the following two statements need to correspond to the actual file name generated by the package and add cross-domain tags
await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/chunk-vendors.cf6d8f84.js", new { crossorigin = "anonymous" });
await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./js/app.b6b5425b.js", new { crossorigin = "anonymous" });
MainPage.xaml Create a button and set the method to trigger the event:
<button
Text="Post Bar To WebView"
HorizontalOptions="Center"
VerticalOptions="End"
HeightRequest="40"
Clicked="PostBar_Clicked"
></button>
CodeBehind:
using System;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Essentials;
namespace MatoProject
{
public partial class MainPage : ContentPage
{
public event EventHandler<EventArgs> OnPostBar;
int count = 0;
public MainPage()
{
InitializeComponent();
}
private async void PostBar_Clicked(object sender, EventArgs args)
{
OnPostBar?.Invoke(this, args);
}
}
}
At this point, all the code work has been completed, and you can choose a Windows or Android emulator to run the program on your PC

Operation effect:

If running on the windows platform, the native control uses the Edge WebView 2 renderer to load the page. Pressing f12 will call the native debugging tool, and you can see the print here.

Now, some people may ask why such a technical architecture is used? There may be better hybrid development technologies such as Ionic, React Native, and Uni-app. First of all, there is no denying that these technologies all have their own characteristics and advantages, but when you have a mature Xamarin framework, you can easily migrate to MAUI, use EFCore to achieve data persistence or integrate the Abp framework to configure dependency injection, global events, localization and other commonly used functions in mobile development (another article will teach you how to move Abp into MAUI). Xamarin is a device abstraction layer, and the WebView provided is also highly compatible with H5.
Of course, the main reason is rapid development. Your code accumulation is valuable, and less code modification is the king. If you are writing Web code using the React technology stack, maybe React Native is your best choice. ** There is no optimal technology, only the technology that suits you best. **
-
- Code warehouse: **