[MAUI]深入了解.NET MAUI Blazor与Vue的混合开发

news2024/11/16 6:59:54

文章目录

    • Vue在混合开发中的特点
    • 创建MAUI项目
    • 创建Vue应用
    • 使用element-ui组件库
    • JavaScript和原生代码的交互
      • 传递根组件参数
      • 从设备调用Javascript代码
      • 从Vue页面调用原生代码
    • 读取设备信息
    • 项目地址

.NET MAUI结合Vue的混合开发可以使用更加熟悉的Vue的语法代替Blazor语法,你现有项目不必重写。之前写过一篇 [MAUI] 在.NET MAUI中结合Vue实现混合开发 ,其中介绍了如何创建一个vue应用并将其打包至MAUI项目,这种方式依赖vue-cli创建和打包静态站点,好处是可以使用Node.js 的构建但MAUI仅仅作为容器。开发应用需要一个独立的host项目

这次用集成的方式。将vue作为MAUI的一部分,这样就可以在MAUI项目中直接使用vue了。

在这里插入图片描述

Vue在混合开发中的特点

首先要说的是,Vue框架是渐进性的,所谓渐进性,就是Vue不会强求你使用所有的框架特性,你可以根据需要逐步使用。

同样地,element-ui也可以通过引入样式和组件库,配合Vue使用

因此我们不需要Vue Router、Vuex、Vue CLI、单文件组件这些高级特性,仅仅引入Vue.js即可使用Vue模板语法。我们将利用Blazor引擎的如下功能:

  • 组件化开发
  • 静态资源管理
  • js代码的注入
  • js调用C#代码
  • C#调用js代码

由.NET MAUI提供的功能:

  • 路由管理
  • 状态管理

由Vue提供模板语法,事件处理,计算属性/侦听器等,以及Element-UI提供交互组件。

创建MAUI项目

创建一个MAUI项目,这里使用的是Visual Studio 2022 17.7.3,创建一个Blazor MAUI App项目命名MAUI-Vue-Hybriddev-Integrated,选择Android和iOS作为目标平台,选择.NET 7.0作为目标框架。

在这里插入图片描述

从Vue官网下载最新的Vue.js

在这里插入图片描述

将其放置在wwwroot目录下,然后在index.html中引入
在这里插入图片描述

    <script src="lib/vuejs/vue.js"></script>

创建Vue应用

在Views目录下创建 HomePage.xaml作为Vue应用的容器,在页面中创建<BlazorWebView>视图元素,并设置HostPagewwwroot/index.html,这样就可以在MAUI中使用Vue了。

<BlazorWebView x:Name="blazorWebView"
               HostPage="wwwroot/index.html">
    <BlazorWebView.RootComponents>
        <RootComponent Selector="#app"
                       x:Name="rootComponent"
                       ComponentType="{x:Type views:HomePageWeb}" />
    </BlazorWebView.RootComponents>
</BlazorWebView>

每个BlazorWebView控件包含根组件(RootComponent)定义,ComponentType是在应用程序启动时加载页面时的类型,该类型需要继承自Microsoft.AspNetCore.Components.IComponent,由于我们的导航是由MAUI处理的,因此我们不需要使用Blazor路由,直接使用Razor组件

在Views目录下创建HomePageWeb.razor,这是Vue应用页面相当于Vue的单文件组件,这里可以使用Vue的模板语法,而不是Blazor的Razor语法。
在这里插入图片描述

我们在HomePageWeb.razor中写下Vue官方文档中Hello Vue示例代码


<div id="vue-app">
    {{ message }}
</div>


<script type="text/javascript">
    var app = new Vue({
        el: '#vue-app',
        data: {
            message: 'Hello Vue!',
        }
    })

</script>

注意:Vue的根元素名称不要跟Blazor的根元素名称相同,否则会报错。

在这里插入图片描述

此时更改JavaScript里的内容,你会发现Blazor页面不会热加载。

请勿将 <script> 标记置于 Razor 组件文件 (.razor) 中,因为 <script> 标记无法由Blazor 动态更新。

于是需要将script部分代码放置在外部,此时有两种方案,一个是放在wwwroot/js目录下,然后在wwwroot/index.html中引入,还有一种是使用并置的js文件,这种方式是所谓的"CodeBehind",因为更利于组织代码,这里我们使用并置的js文件。

创建一个HomePageWeb.razor.js文件,将script部分代码放置在其中,然后在HomePageWeb.razor中引入

在这里插入图片描述

protected override async Task OnAfterRenderAsync(bool firstRender)
{

    if (firstRender)
    {
        await JSRuntime.InvokeAsync<IJSObjectReference>("import", "./Views/HomePageWeb.razor.js");
    }
}

发布应用后,框架会自动将脚本移动到 Web 根目录。 在此示例中,脚本被移动到./wwwroot/Views/HomePageWeb.razor.js

使用element-ui组件库

同样,我们在element-ui官方CDN下载样式文件和组件库,首先在index.html中引入样式和组件库

<link href="css/app.css" rel="stylesheet" />
...
<script src="lib/element-ui/index.js"></script>

然后在HomePageWeb.razor中使用组件

<div id="vue-app">
    {{ message }}
    <el-input v-model="input" placeholder="请输入内容"></el-input>
    <el-button @click="showDialog = true">提交</el-button>
    <el-dialog :visible.sync="showDialog" title="消息">
        <p>{{input}}</p>
        <p>提交成功</p>
    </el-dialog>
</div>

CodeBehind中引入组件

var app = new Vue({
    el: '#vue-app',
    data: {
        message: 'Hello Vue!',
        showDialog: false,
        input: 'text message from vue'
    }
})

运行效果如下:

在这里插入图片描述

在这里插入图片描述

JavaScript和原生代码的交互

Blazor组件中的代码可以通过注入IJSRuntime来调用JavaScript代码,JavaScript代码可以通过调用DotNet.invokeMethodAsync来调用C#代码。

传递根组件参数

如果被调用的代码位于其他类中,需要给这个Blazor组件传递实例,还记得刚才提及的根组件(RootComponent)吗?我们用它来传递这个实例,称之为根组件参数,详情请查看官方文档 在 ASP.NET Core Blazor Hybrid 中传递根组件参数

创建SecondPage.xaml,根据刚才的步骤创建一个BlazorWebView并注入vuejs代码
html部分创建一个el-dialog组件,当消息被接收时,显示对话框


@using Microsoft.Maui.Controls
@inject IJSRuntime JSRuntime

<div id="vue-app">
    {{ message }}
    <el-dialog :visible.sync="showDialog" title="Native device msg recived!">
        <p>{{msg}}</p>
    </el-dialog>
</div>

在@code代码段中创建SecondPage对象。


@code {

    [Parameter]
    public SecondPage SecondPage { get; set; }

    ...
}

回到SecondPage.xaml.cs,在构造函数中将自己传递给根组件参数

public SecondPage()
{
    InitializeComponent();
    rootComponent.Parameters =
        new Dictionary<string, object>
        {
            { "SecondPage", this }
        };
}


从设备调用Javascript代码

在SecondPage.xaml中,创建一个Post按钮,点击按钮后将文本框PostContentEntry的内容传递给Vue代码

<StackLayout Grid.Row="1">
    <Entry x:Name="PostContentEntry" Text="Hello,this is greetings from native device"></Entry>
    <Button Text="Post To Vue"
            HorizontalOptions="Center"
            VerticalOptions="End"
            HeightRequest="40"
            Clicked="Post_Clicked"></Button>

</StackLayout>

在这里插入图片描述

在SecondPage.razor.js中, 创建greet方法,用于接收从原生代码传递过来的参数,并显示在对话框中。

window.app = new Vue({
    el: '#vue-app',
    data: {
        message: 'Vue Native interop',
        showDialog: false,
        msg: ''
    },
    methods: {
        greet: function (content) {
            this.msg = content;
            this.showDialog = true;
        }

    },

在SecondPage.xaml.cs中,创建一个OnPost事件,当Post按钮被点击时触发该事件


public event EventHandler<OnPostEventArgs> OnPost;

private void Post_Clicked(object sender, EventArgs args)
{
    OnPost?.Invoke(this, new OnPostEventArgs(this.PostContentEntry.Text));
}


在SecondPage.razor中,订阅OnPost事件,当事件被触发时,调用greet方法,将参数传递给JavaScript代码


public async void Recived(object o, OnPostEventArgs args)
{
    await JSRuntime.InvokeAsync<string>("window.app.greet", args.Content);
}

protected override async Task OnAfterRenderAsync(bool firstRender)
{
    try
    {
        if (firstRender)
        {
            SecondPage.OnPost += this.Recived;

            await JSRuntime.InvokeAsync<IJSObjectReference>(
"import", "./Views/SecondPageWeb.razor.js");

        }


    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }

}

在页面销毁时,要取消订阅事件,避免内存泄漏。


@implements IDisposable

...

public void Dispose()
{
    SecondPage.OnPost -= this.Recived;
}


运行效果如下

在这里插入图片描述

从Vue页面调用原生代码

原生代码指的是.NET MAUI平台的C#代码,比如要在设备上显示一个弹窗,需要调用Page.DisplayAlert方法,它隶属于Microsoft.Maui.Controls命名空间,属于MAUI组件库的一部分。

因此需要将MAUI类型的对象通过引用传递给JavaScript调用,调用方式是通过将对象实例包装在 DotNetObjectReference 中传递给JavaScript。使用该对象的invokeMethodAsync从 JS 调用 .NET 实例方法。详情请查看官方文档 JavaScript 函数调用 .NET 方法

在@code代码段中,界面加载时创建DotNetObjectReference对象

@code {
    private DotNetObjectReference<SecondPageWeb>? objRef;


    protected override void OnInitialized()
    {
        objRef = DotNetObjectReference.Create(this);
    }

页面加载完成时,将DotNetObjectReference对象传递给JavaScript代码


protected override async Task OnAfterRenderAsync(bool firstRender)
{
    try
    {
        if (firstRender)
        {
            SecondPage.OnPost += this.Recived;

            await JSRuntime.InvokeAsync<IJSObjectReference>(
"import", "./Views/SecondPageWeb.razor.js");
            await JSRuntime.InvokeAsync<string>("window.initObjRef", this.objRef);

        }


    }
    catch (Exception ex)
    {
        Console.WriteLine(ex);
    }

}

window.app = new Vue({
    
    ...

    data: {
        ...
        objRef: null
    },
    
})
window.initObjRef = function (objRef) {
    window.app.objRef = objRef;
}

在SecondPage.razor中,创建el-input组件和el-button组件,当按钮被点击时,调用Post方法,将文本框的内容传递给原生代码

<div id="vue-app">
    {{ message }}
    <el-input v-model="input" placeholder="请输入内容"></el-input>
    <el-button @click="post">Post To Native</el-button>
    <el-dialog :visible.sync="showDialog" title="Native device msg recived!">
        <p>{{msg}}</p>
    </el-dialog>
</div>

按钮和对话框的显示逻辑与之前相同,不再赘述。

在这里插入图片描述

在SecondPage.razor中,创建Post方法,方法被调用时,将触发MAUI组件库的原生代码

[JSInvokable]
public async Task Post(string content)
{
    await SecondPage.DisplayAlert("Vue msg recived!", content, "Got it!");

}

vue绑定的函数中,调用DotNet.invokeMethodAsync将文本框的内容传递给原生代码


window.app = new Vue({
    el: '#vue-app',
    data: {
        message: 'Vue Native interop',
        showDialog: false,
        msg: '',
        input: 'Hi, I am a text message from Vue',
        deviceDisplay: null,
        objRef: null
    },
    methods: {
        greet: function (content) {
            this.msg = content;
            this.showDialog = true;
        },
        post: function () {
            this.objRef.invokeMethodAsync('Post', this.input);
        }


    }
})

运行效果如下

在这里插入图片描述

读取设备信息

可以使用Vue的watch属性监听数据变化,当MAUI对象加载完成时,调用原生代码,读取设备信息

<div id="vue-app">

    ...

    <p>Device Display</p>
    <p>{{deviceDisplay}}</p>
</div>

CodeBehind代码如下:

watch: {
    objRef: async function (newObjRef, oldObjRef) {
        if (newObjRef) {
            var deviceDisplay = await this.objRef.invokeMethodAsync('ReadDeviceDisplay');
            console.warn(deviceDisplay);
            this.deviceDisplay = deviceDisplay;
        }

    }
},

原生代码如下:


[JSInvokable]
public async Task<string> ReadDeviceDisplay()
{
    return await Task.FromResult(SecondPage.ReadDeviceDisplay());

}

在ReadDeviceDisplay方法中,我们读取设备分辨率、屏幕密度、屏幕方向、屏幕旋转、刷新率等信息

public string ReadDeviceDisplay()
{
    System.Text.StringBuilder sb = new System.Text.StringBuilder();

    sb.AppendLine($"Pixel width: {DeviceDisplay.Current.MainDisplayInfo.Width} / Pixel Height: {DeviceDisplay.Current.MainDisplayInfo.Height}");
    sb.AppendLine($"Density: {DeviceDisplay.Current.MainDisplayInfo.Density}");
    sb.AppendLine($"Orientation: {DeviceDisplay.Current.MainDisplayInfo.Orientation}");
    sb.AppendLine($"Rotation: {DeviceDisplay.Current.MainDisplayInfo.Rotation}");
    sb.AppendLine($"Refresh Rate: {DeviceDisplay.Current.MainDisplayInfo.RefreshRate}");

    var text = sb.ToString();
    return text;
}

当页面加载时,会在HTML页面上显示设备信息

在这里插入图片描述

项目地址

Github:maui-vue-hybirddev

关注我,学习更多.NET MAUI开发知识!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1106827.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cypress安装使用

node.js 安装使用Cypress总是会看见node.js&#xff0c;那就先看看node.js是什么。JavaScript以前运行需要在浏览器中&#xff08;浏览器内置解释器&#xff09;&#xff0c;通过node.js框架内置v8引擎&#xff08;也就是可以执行js脚本所需的工具&#xff09;&#xff0c;这样…

金翅擘海|人大女王金融硕士庞雪雨:行学之道,在自律、在勤勉、在止于至善

庞雪雨 中国人民大学-加拿大女王大学金融硕士2022-2023级行业高管班 光大保德信资产管理有限公司董事总经理 当我进入到人大校园的那一刻&#xff0c;映入眼帘的是明德楼&#xff0c;由此我想到了《大学》&#xff0c;大学开篇中讲到&#xff0c;大学之道&#xff0c;在明明德&…

如何理解Linux字符设备驱动?

我们学习编程的时候&#xff0c;一般都会从hello程序开始。同样的&#xff0c;学习Linux驱动&#xff0c;我们也是从最简单的hello驱动学起。 一、驱动层和应用层 看一下STM32裸机开发与嵌入式Linux开发的一些区别&#xff1a; 嵌入式Linux的开发方式与STM32裸机开发的方式有…

【数值分析】1 - 误差及有关概念

文章目录 一、误差的背景介绍1.1 误差的来源与分类1.2 误差的传播与积累1.3 例题1.3.1 公式一1.3.2 公式二1.3.3 总结 二、误差与有效数字2.1 绝对误差与绝对误差限2.2 相对误差和相对误差限 三、有效数字3.1 有效数字的定义和标准浮点式3.1.1 例题 3.2 有效数字与相对误差的关…

【一文清晰】单元测试到底是什么?应该怎么做?

我是java程序员出身&#xff0c;后来因为工作原因转到到了测试开发岗位。测试开发工作很多年后&#xff0c;现在是一名自由职业者 1、什么是单元测试 2、该怎么做单元测试 一、什么是单元测试&#xff1f; 单元测试&#xff08;unit testing&#xff09;&#xff0c;是指对软件…

4款文件恢复工具推荐,恢复数据就靠它们!

“想问问大家在使用电脑时有什么好的文件恢复工具推荐吗&#xff1f;最近好像有点粗心&#xff0c;经常误删文件&#xff0c;非常需要一个有用的恢复工具&#xff0c;希望大家给些意见&#xff01;” 在日常工作或学习中&#xff0c;由于各种原因造成的文件丢失是很正常的情况。…

什么是模拟芯片,模拟芯片都有哪些测试指标?

模拟芯片又称处理模拟信号的集成电路 模拟集成电路主要是指由电容、电阻、晶体管等组成的模拟电路集成在一起用来处理模拟信号的集成电路。有许多的模拟集成电路&#xff0c;如运算放大器、模拟乘法器、锁相环、电源管理芯片等。 模拟集成电路的主要构成电路有&#xff1a;放…

打造属于自己的vue图标库

hfex-icon图标库 Install npm i -D hfex-icon主要提供2种使用方式 方式一 通过svg图标资源&#xff0c;借助unplugin-icons库将svg图标文件生成vue组件&#xff0c;然后通过vue组件的引入方式在vue中使用 unplugin-icons 兼容vue2和vue3 在vue.config.js的plugins中配置…

“轻松实现文件夹批量重命名:使用顺序编号批量改名“

你是否曾经需要大量修改文件夹名称&#xff1f;或者需要为文件夹添加有序编号以便于管理&#xff1f;下面就教你一个简单的方法&#xff0c;轻松实现文件夹批量重命名&#xff0c;使用顺序编号批量改名。 首先我们要进入文件批量改名高手主页面&#xff0c;并在板块栏里选择“文…

java springboot VUE粮食经销系统开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 springboot VUE 粮食经销系统是一套完善的完整信息管理类型系统&#xff0c;结合springboot框架和VUE完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用springboot框架&#xff08;MVC模式开发&#xff09; &#xff0c;系统具有完整的源代码和数…

kafka、zookeeper、flink测试环境、docker

1、kafka环境单点 根据官网版本说明(3.6.0)发布&#xff0c;zookeeper依旧在使用状态&#xff0c;预期在4.0.0大版本的时候彻底抛弃zookeeper使用KRaft(Apache Kafka)官方并给出了zk迁移KR的文档 2、使用docker启动单点kafka 1、首先将kafka启动命令&#xff0c;存储为.servi…

LLC 三相移相PWM产生原理分析

LLC 三相移相PWM产生原理分析 void MX_PWM_Stop(void) {//----------------------使用停止函数后会导致移相角度为60度---------------------------------------------------------------- #if 1 //------Tim1 PWM定时器初始化------------------ HAL_TIM_OC_Stop(&htim1…

最新Google play开发者账号注册要求、单位账号邓白氏编码问题及身份验证解决思路!

随着Google play商店的蓬勃发展&#xff0c;越来越多的开发者在上面上传应用&#xff0c;但部分开发者为了获得更多的收益&#xff0c;试图通过多种方式绕过谷歌限制&#xff0c;无视谷歌规则。 为了维持Google play的生态环境&#xff0c;谷歌采取了多种方式和方法去应对这些…

决策树oo

决策树学习的算法通常是一个递归地选择最优特征(选择方法的不同&#xff0c;对应着不同的算法)&#xff0c;并根据该特征对训练数据进行分割&#xff0c;使得各个子数据集有一个最好的分类的过程。这一过程对应着对特征空间的划分&#xff0c;也对应着决策树的构建 步骤&#…

git切换远程仓库源步骤

git切换远程仓库源步骤&#xff1a; 第一步&#xff1a;git remote -v 查看当前远程仓库源&#xff1a; 第二步&#xff1a;git remote rm origin删除远程仓库源&#xff1b; 第三步&#xff1a;git remote add origin http://newURL.git 添加新的远程仓库源地址&#xff1b…

Operator 开发实践 四 (WebHook)

1. WebHook介绍 我们知道访问Kubernetes API有好几种方式&#xff0c;比如使用kubectl命令、使用client-go之类的开发库、直接通过REST请求等。不管是一个使用kubectl的真人用户&#xff0c;还是一个Service Account&#xff0c;都可以通过API访问认证&#xff0c;这个过程官网…

AI机器视觉多场景应用迸发检测活力,引领食品及包装行业新发展

随着食品安全意识的广泛传播&#xff0c;人们对食品质量和安全的要求越来越高&#xff0c;众多食品包装厂商加速产线数智化转型&#xff0c;迫切需要高效、准确且智能化的检测技术。 在现代食品及包装行业的自动化生产中&#xff0c;涉及到各种各样的识别、检测、测量等环节&a…

LabVIEW中管理大型数据

LabVIEW中管理大数据 LabVIEW的最大优势之一是自动内存管理。这种内存管理允许用户轻松创建字符串、数组和集群&#xff0c;而无需C/C用户经常担心。但是&#xff0c;这种内存管理设计为绝对安全&#xff0c;因此数据被非常频繁地复制。这通常不会造成任何问题&#xff0c;但是…

Linux下使用openssl制作证书

openssl是一个功能丰富且自包含的开源安全工具箱。它提供的主要功能有&#xff1a;SSL协议实现(包括SSLv2、SSLv3和TLSv1)、大量软算法(对称/非对称/摘要)、大数运算、非对称算法密钥生成、ASN.1编解码库、证书请求(PKCS10)编解码、数字证书编解码、CRL编解码、OCSP协议、数字证…

Go语言和Python语言哪个比较好?

目录 1、性能 2、开发效率和易用性 3、社区支持 4、语法 5、其他因素 总结 Go语言和Python语言都是非常优秀的编程语言&#xff0c;它们各自具有不同的优势和适用场景。在选择哪种语言更适合您的项目时&#xff0c;需要考虑多个方面&#xff0c;包括性能、开发效率、可读…