[MAUI] in NET MAUI combined with Vue to realize hybrid development

Posted by evilcoder on Wed, 19 Jan 2022 00:38:26 +0100

In MAUI, Microsoft's official solution is to use Blazor for development, but most Web projects in the current market are built using Vue, React and other technologies. If we can't bypass the accumulated technologies, it's unrealistic to rewrite the whole project with Blazor.

Vue is a popular web framework. In short, it is a set of template engine. It uses the two characteristics of "template" and "binding" to realize the development of web page mvvm mode. Use NET MAUI framework can embed Vue applications into web containers. Cross platform hybrid development can be realized.

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 website developed by Vue, you can try to move value into your mobile devices such as iPhone, Android and tablet according to this article.

The core work of hybrid development is to build Web and net, we will use the following functions of Blazor engine:

  • Unified management of resources
  • js code injection
  • js call C# code
  • C # calling js code

If you don't understand the concept of hybrid development, please go back to the previous chapter [MAUI] hybrid development concept_ jevonsflash column - CSDN bloghttps://blog.csdn.net/jevonsflash/article/details/121835547

The whole work is divided into MAUI part, Vue part and hybrid transformation.

MAUI section

To create a Maui App project:

You can also create a Maui Blazor App project named MatoProject, but this template is mainly developed around Blazor. We don't need some functions, so we have to delete many files.

Edit matoproject.com when creation is complete Csproj, add at the end of Sdk Razor, VS will automatically install Microsoft AspNetCore. Components. WebView. Maui depends on the package (be careful not to manually add this package to Nuget, otherwise the program cannot run)

After installation, create a wwwroot folder in the project directory

This folder will be the root directory of the mixed development Web part. This name cannot be defined casually. Let's see why:

Open Microsoft AspNetCore. Components. WebView. Maui. Targets this file:

We can see that when building a project, the library will label the contents in the wwwroot folder as the Maui asset type, and the compiler will package these contents into the resource folders of each platform according to the Maui asset tag. For specific Maui resource types, please refer to this article .NET MAUI – Manage App Resources – Developer Thoughts (egvijayanand.in) ,

Open mauipprogram CS register the BlazorMauiWebView component in the builder, and use the extension method AddBlazorWebView() in the service to add related Blazor services

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();
				.ConfigureFonts(fonts =>
					fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
			return builder.Build();

Open mainpage Xaml, edit the main page of the native application:

Create a BlazorWebView control to spread the screen, and set the HostPage as the home page index of the Web part html

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             BackgroundColor="{DynamicResource SecondaryColor}">

        <b:BlazorWebView HostPage="wwwroot/index.html">
                <b:RootComponent Selector="#blazorapp" x:Name="MainWebView" ComponentType="{x:Type local:Index}/>


Establish_ 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 section

Now that we have established the Web container for native development, we need to deal with the Vue project:

cd to the project directory and create a blank Vue project using Vue cli:

It can be built according to Vue's programming preferences. For example, I chose the 2.0 project, supported Typescript, es6 class naming, etc. they all have to be packaged into static resources through webpack, so it doesn't matter.

Create Src / API / fooservice TS, create the following function:

The window['DotNet '] object will be an interoperation object injected in Maui blazer

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 edit:

This is the main page of the Web. We need three buttons and related functions to test the interaction between js and C#.

  <div class="home">
    <img alt="Vue logo" src="../assets/logo.png" />
      <button @click="getFoo">click to get foo</button>
      <br />
      <span>{{ foo }}</span>
      <span>{{ bar }}</span>
      <button @click="add">click here to add</button>
      <span>click count:{{ cnt }}</span>
<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";

  components: {
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;

This completes a simple Vue project

Run the package command:

PS D:\Project\maui-vue-hybirddev\hybird-host> yarn build

Copy all contents 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 is rewritten as:

<!DOCTYPE html>
<html lang="">
    <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">
    <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">
    <div id="blazorapp">Loading...</div>
    <script src="_framework/blazor.webview.js" autostart="false"></script>

Note that only rewrite the body part, do not change the content of the link tag of the head, and only add crossrigin = "anonymous" after js to solve the cross domain problem.

Create 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 {

    public static Task<string> GetFoo()
        return Task.FromResult("this is foo call C# method from js");

    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;
            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)


    public void Dispose()
        (Application.Current.MainPage as MainPage).OnPostBar -= this.Post;


Note that the following two statements need to correspond to the actual file name generated by packaging and add a cross domain label

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 creates a button and sets the method to trigger the event:

<Button Text="Post Bar To WebView" HorizontalOptions="Center" VerticalOptions="End" HeightRequest="40" Clicked="PostBar_Clicked"></Button>


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()

		private async void PostBar_Clicked(object sender, EventArgs args)
			OnPostBar?.Invoke(this, args);

So far, all the code work has been completed. You can choose Windows or Android simulator to run the program on the PC

Operation effect:

If running on windows platform, the native control uses the Edge} WebView2 renderer to load the page. Press f12 to call the native debugging tool. You can see the print here

Now, someone may ask why such a technical architecture is used? Obviously, there may be better hybrid development technologies Ionic, React Native and uni app. First of all, it is undeniable that these technologies have their characteristics and advantages, but when you have a mature xamarin framework, you can easily migrate to MAUI, use EFCore to realize data persistence, or integrate the Abp framework to configure common mobile development functions such as dependency injection, global events, localization and so on (another article will teach you how to move Abp value into MAUI). Xamarin is a device abstraction layer, and the WebView provided also has good H5 compatibility.

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 with React technology stack, maybe React Native is your best choice. There is no best technology, only the most suitable technology for you.



Code warehouse:

jevonsflash/maui-vue-hybirddev: maui-vue-hybirddev (github.com)

jevonsflash/maui-vue-hybirddev (gitee.com)

Topics: .NET xamarin xaml