blazor wasm开发chrome插件

用blazor(Wasm)开发了一个chrome插件感觉效率挺高的,分享给大家

先简单介绍下WebAssembly的原理:

“WebAssembly是一种用于基于堆栈的虚拟机的二进制指令格式”

e9645237d0efc5285181313ca17e35d6.png
image

如上图,浏览器在执行js时是会经历 Parser转成语法树->Compiler转成字节码->JIT即时字节码解释执行

因为WebAssembly 模块已经被编译成一种 JavaScript 字节码形式,现代支持 WebAssembly 的 JavaScript 引擎可以在其 JIT 组件中可以直接解释执行!

mono团队把开源跨平台.NET运行时Mono(也是unity3d的运行时)编译成了WebAssembly ,那么开发的.net程序就可以通过这个运行时在浏览器中加载net程序执行。

近日vs2022发布了,blazor的功能得到进一步提升,

  • 支持AOT将.NET代码直接编译为WebAssembly字节码

  • 支持NativeFileReference添加c语言和rust等原生依赖(手动狗头)

进入正题

开发浏览器插件,常见的就是按照插件的这几块api来进行扩展

  • 右键菜单扩展

  • Backgroud(可以理解为每个插件都有一个后台一直运行的模块)

  • popup(浏览器右上角点击插件弹出的窗口模块)

  • contentScript(嵌入到你想要嵌入的网站内执行)

  • devtools(开发面板扩展模块)

首先基于这个大佬的模板搭建工程

https://github.com/mingyaulee/Blazor.BrowserExtension

基于模板的话会帮你引入哪些包

5185718b942265884f1312eeecff0ffe.png
image

我也躺了很多坑,看看我给大佬提的issue,和大佬一起成长

5c68eba64c03e79f2c12cafb7931eab2.pngf7c65fafce3cae83eb8e927dc386a90e.png738e6740f7eac8a66cbdfb7903e305b4.png

这里我总结一套非常高效的方案给大家:

  1. Backgroud用csharp写

  2. popup,option等的html不要用balzor写,balzor加载html没有任何优势

  3. contentScript用js写,内嵌到网站的,如果是balzor的话会初始化的时候卡1~2s左右,这个会严重影响体验

js和csharp交互

这里把BackGround(csharp开发)作为插件后端 html和js作为插件的前端的方式

右键菜单扩展

在BackGround里面写,包括响应事件

//选中跳转菜单
await WebExtensions.ContextMenus.Create(new WebExtensions.Net.Menus.CreateProperties
{Title = "测试菜单",Contexts = new List<ContextType>{ContextType.Selection},//data是选中的内容包装对象Onclick = async (data, tab) => { await test(data).ConfigureAwait(false); }
}, EmptyAction);
//非选中跳转菜单await WebExtensions.ContextMenus.Create(new WebExtensions.Net.Menus.CreateProperties
{Title = "跳转百度",Onclick = async (d, tab) => { await OpenUrl("https://www.baidu.com").ConfigureAwait(false); }
}, EmptyAction);

contentScript/popup等

用js写,有2种方式来和Backgroud通讯

1. 事件一来一回的方式

contentScript中发送消息给BackGround

chrome.runtime.sendMessage("消息体", function () { });
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {//处理backgroup发来的消息});

BackGround注册事件用来接收js发过来的消息

//注册事件接收js过来的消息
await WebExtensions.Runtime.OnMessage.AddListener(OnReceivedCommand);//处理事件
private bool OnReceivedCommand(object obj, MessageSender sender, Action action){Console.WriteLine("OnCommand:" + key + $",from TabId:{sender.Tab.Id}");//处理完成后发送事件给js那边await WebExtensions.Tabs.SendMessage(sender.Tab.Id.Value, "处理完成了", new SendMessageOptions());
}

2. 长连接方式

js端

var port = chrome.extension.connect({name: "test"
});port.onMessage.addListener(function (msg) {console.log(msg);
});$('#test').click(e => {port.postMessage('发消息');
});

csharp端

await WebExtensions.Runtime.OnConnect.AddListener(port =>
{Console.WriteLine(port.Name + "---》connection");port.OnMessage.AddListener(new DelegateMethod(async (msg) =>{//处理消息}));});

目前这种方式有一个需要优化,就是无法在csharp端主动推送消息给js端 给大佬提了issue了,相信很快可以fix https://github.com/mingyaulee/WebExtensions.Net/issues/14

配置/存储相关

有两种方法:

1. chrome.storage.local

这里我封装了一个类专门操作

public class ChromLocalStorage
{private readonly IWebExtensionsApi _webExtensionsApi;private readonly IJSRuntime _jsRuntime;public ChromLocalStorage(IWebExtensionsApi webExtensionsApi, IJSRuntime JsRuntime){_webExtensionsApi = webExtensionsApi;_jsRuntime = JsRuntime;}/// <summary>/// 调用chrom.storage.local set 把 key 和 value设置进去/// key返回/// </summary>/// <param name="value"></param>/// <param name="existKey"></param>/// <returns></returns>public async Task<string> localSet(string value,string existKey  = null){var key = existKey ?? "key_" + DateTime.Now.ToString("yyyyMMddHHmmss");byte[] bytes = Encoding.UTF8.GetBytes(value);var encode = Convert.ToBase64String(bytes);var jss = "var " + key + " = {'" + key + "':'" + encode + "'}";await _jsRuntime.InvokeVoidAsync("eval", jss);object data2 = await _jsRuntime.InvokeAsync<object>("eval", key);await _jsRuntime.InvokeVoidAsync("chrome.storage.local.set", data2);Console.WriteLine($"call chrome.storage.local.set,key:{key},value:{value},base64Value:{encode}");return key;}public async Task<string> localSet<T>(T value){if (value is string s){return await localSet(s,null);}//转成jsonstringvar serialize = JsonSerializer.Serialize(value);return await localSet(serialize,null);}public async Task<T> localGet<T>(string key){var data = await localGet(key);T deserialize = JsonSerializer.Deserialize<T>(data);return deserialize;}public async Task<string> localGet(string key,bool remove=true){try{var local = await _webExtensionsApi.Storage.GetLocal();var getData = await local.Get(new StorageAreaGetKeys(key));var data = getData.ToString();if (string.IsNullOrEmpty(data)){return string.Empty;}var value = data.Split(new string[] { ":\"" }, StringSplitOptions.None)[1].Split(new string[] { "\"" }, StringSplitOptions.None)[0];var str = Convert.FromBase64String(value);var bastStr = Encoding.UTF8.GetString(str);//Console.WriteLine($"call chrome.storage.local.get,key:{key},value:{bastStr},base64Value:{value}");if (remove) await local.Remove(new StorageAreaRemoveKeys(key));return bastStr;}catch (Exception e){return "";}}public async Task localRemove(string key){var local = await _webExtensionsApi.Storage.GetLocal();await local.Remove(new StorageAreaRemoveKeys(key));}
}

2. 6.0推出的新技术:采用EFCore + Sqlite

需要用到native的库 https://github.com/SteveSandersonMS/BlazeOrbital/blob/main/BlazeOrbital/ManufacturingHub/Data/e_sqlite3.o

下载下来后放入工程中,然后引入

8283d232060d588910f53c8416a4db9e.png
image

这里还有一个关键

https://github.com/SteveSandersonMS/BlazeOrbital/blob/main/BlazeOrbital/ManufacturingHub/wwwroot/dbstorage.js

下载这个js后放入工程中,这个js是将sqlite和本地的indexdb进行同步的

//EF的DbContext
public class ClientSideDbContext : DbContext
{//定义你要存储的表模型public DbSet<Part> Parts { get; set; } = default!;public ClientSideDbContext(DbContextOptions<ClientSideDbContext> options): base(options){}protected override void OnModelCreating(ModelBuilder modelBuilder){base.OnModelCreating(modelBuilder);//设置你的表的索引等modelBuilder.Entity<Part>().HasIndex(x => x.Id);modelBuilder.Entity<Part>().HasIndex(x => x.Name);modelBuilder.Entity<Part>().Property(x => x.Name).UseCollation("nocase");}
}//sqlite的初始化以及获取DBContext的方法封装
public class DataSynchronizer
{public const string SqliteDbFilename = "app.db";private readonly Task firstTimeSetupTask;private readonly IDbContextFactory<ClientSideDbContext> dbContextFactory;public DataSynchronizer(IJSRuntime js, IDbContextFactory<ClientSideDbContext> dbContextFactory){this.dbContextFactory = dbContextFactory;firstTimeSetupTask = FirstTimeSetupAsync(js);}public async Task<ClientSideDbContext> GetPreparedDbContextAsync(){await firstTimeSetupTask;return await dbContextFactory.CreateDbContextAsync();}private async Task FirstTimeSetupAsync(IJSRuntime js){//只加载一次 让sqlite和indexdb同步var module = await js.InvokeAsync<IJSObjectReference>("import", "./js/dbstorage.js");if (RuntimeInformation.IsOSPlatform(OSPlatform.Create("browser"))){await module.InvokeVoidAsync("synchronizeFileWithIndexedDb", SqliteDbFilename);}using var db = await dbContextFactory.CreateDbContextAsync();await db.Database.EnsureCreatedAsync();}}
0ecb4bb7f362c9ba6304b1a68f1fa503.png
image

在Program.cs进行注册9c527d069b5d2fd54038e56862755164.png

那么你就可以在Backgroud里面注入并在初始化方法中拿到db上下文

[Inject] public DataSynchronizer DataSynchronizer { get; set; }//db上下文
private ClientSideDbContext db;protected override async Task OnInitializedAsync()
{await base.OnInitializedAsync();db = await DataSynchronizer.GetPreparedDbContextAsync();
}

推荐用新的方式,EF写起来更爽更高效,拿到db上下文 就可以很简单的操作插件里面所有用到存储配置等!

这种方式比较适合了解.net生态的人,结合.net的一些库还可以实现很多好玩的功能

  • excel导出

  • 二维码生成

  • ajax拦截,转发等


我是正东,开发chrome插件其实很简单,这种方式对于我来说比较高效 哈哈

欢迎白嫖 顺手点个赞吧!f30efb4a8d40fe90007cc392ad52f6ac.pngf7af4a35c58bde6b4a17d4f7fe16be77.png6634a44c2c95ba49a2c8610d6e4b4482.png

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

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

相关文章

求偶的两种方法

import java.util.Scanner;public class Testif {public static void main(String[] args) {// TODO 自动生成的方法存根Scanner scan new Scanner(System.in);System.out.println("请输入一个整数&#xff1a;");long number scan.nextLong(36);String check (numbe…

idea zookeeper的使用_学习ZooKeeper源码,就从这篇开始吧

【ZooKeeper系列】1.ZooKeeper单机版、伪集群和集群环境搭建【ZooKeeper系列】2.用Java实现ZooKeeper API的调用在系列的前两篇文章中&#xff0c;介绍了ZooKeeper环境的搭建(包括单机版、伪集群和集群)&#xff0c;对创建、删除、修改节点等场景用命令行的方式进行了测试&…

base target=_self是什么意思

<base target_blank>//在空白页打开 <base target_parent>//在当前页的上一页(父亲)打开 <base target_search>//在浏览器地址栏打开 <base target_self>//在当前页打开 <base target_top>//在最初(祖先)页打开 base 是地址 tar…

都才40出头,近一个月已有至少5名优秀青年学者英年早逝!健康不容忽视

全世界只有3.14 % 的人关注了爆炸吧知识近年来&#xff0c;在工作、生活的层层重压下&#xff0c;正处于事业上升期的科研人员因病逝世的消息屡屡传出。据不完全统计&#xff0c;仅仅今年10月份至今&#xff0c;短短一个多月时间里&#xff0c;已有至少5位优秀青年学者倒下。他…

基于 Spring Security 的开源统一角色访问控制系统 URACS

URACS Java语言开发的统一角色访问控制系统(Unified Role Access Control System)&#xff0c;基于Spring Security 3实现的权限控制系统 程序框架版本说明&#xff1a;Spring MVC 3.0.6 Spring Security 3.1.3 Hibernate 3.6.10 运行演示例子&#xff1a; 例子使用的是MySQL…

Chatopera 王海良:做好开源客服系统

| 作者&#xff1a;王海良| 编辑&#xff1a;刘雪洁| 设计&#xff1a;马丽娜| 责编&#xff1a;王玥敏做好开源客服系统&#xff0c;是我最近的心心念念的事情之一。我想讲的故事&#xff0c;就包括春松客服&#xff0c;一个开源的客服系统。篇章有限&#xff0c;我想从三点来…

Android之ANR异常及解决方法

ANR (Application Not Responding)   ANR定义:在Android上,如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框。用户可以选择“等待”而让程序继续运行,也可以选择“强制…

如何写出无法维护的代码

If builders built buildings the way programmers write programs, then the first woodpecker that came along would destroy civilization. &#xff08;如果建筑师盖房子就像程序员写程序一样&#xff0c;那么&#xff0c;第一只到来的啄木鸟就能毁掉我们的文明&#xff0…

mysql_contents_将MySQLhelpcontents的内容有层次的输出方法推荐

经常会遇到这种情况&#xff0c;在一个不能上网的环境通过MySQL客户端登录数据库&#xff0c;想执行一个操作&#xff0c;却忘了操作的具体语法&#xff0c;各种不方便。其实&#xff0c;MySQL数据库内置了帮助文档&#xff0c;通过help contents即可查看。如下所示&#xff1a…

Android类参考---Fragment(一)

1. 继承关系 java.lang.Object |__android.app.Fragment 实现接口&#xff1a;ComponentCallbacks2 View.OnCreateContextMenuListener 引入版本&#xff1a;API Level 11 已知的子类&#xff1a; DialogFragment、ListFragment、PreferenceFragment、WebViewFragment 2. 类概要…

点开那些优秀的硕博士们的朋友圈,他们都有这些特点!

全世界只有3.14 % 的人关注了爆炸吧知识很多同学都会有这种感觉&#xff0c;读了硕士博士后&#xff0c;兴趣会突然间发生很大变化&#xff0c;发朋友圈也会不一样了。例如&#xff0c;合格的学术研究者&#xff0c;要快速、全面的获取各种最新文献和学界动态&#xff1b;还要持…

对程序员职业的一些建议

&#xff08;转载自Bcwhy编程十万个为什么&#xff09;  从四年前被CSDN采访后职业规化就像软件工程”&#xff09;&#xff0c;经常会有网友&#xff08;尤其是刚毕业的&#xff09;写邮件来问我一些程序员职业生涯的一些问题&#xff0c;至到今天。比如&#xff0c;国企还是…

如何高效的将 DataReader 转成 ListT ?

咨询区 Anthony&#xff1a;我在使用第三方工具包&#xff0c;它返回了一个 DataReader&#xff0c;为了能更方便的使用&#xff0c;我希望有一种快捷方法能够将它转成 List<T>&#xff0c;除了一行一行的迭代赋值之外还有其他好的方式吗&#xff1f;回答区 pim&#xff…

Android之如何成为Android高手

成为Android高手一般分为六个阶段&#xff1a; 第一阶段&#xff1a;熟练掌握Java SE&#xff0c;尤其是对其内部类、线程、并发、网络编程等需要深入研究&#xff1b;熟练掌握基于HTTP协议的编程&#xff0c;清楚POST和GET等请求方式流程和细节&#xff1b;能够进行基本的Java…

java foreach 跳过本次循环_【Java】对foreach循环的思考

阿里java开发手册已经发表&#xff0c;很多都值得认真研究思考&#xff0c;看到零度的思考题&#xff0c;没忍住研究了一下。在这里插入图片描述首先&#xff0c;看一下给出的反例的执行结果。如果是"1"&#xff0c;最后list中的元素为["2"]如果把"1&…

地球上这10个奇幻景观,带你踏入外太空

全世界只有3.14 % 的人关注了爆炸吧知识大蓝洞大蓝洞是灯塔礁的一部分&#xff0c;位于洪都拉斯伯利兹城陆地大约100公里之遥&#xff0c;是一个较大的完美环状海洋深洞&#xff0c;是当今世界最吸引人的潜水地点之一。305米的口径&#xff0c;123米的洞深&#xff0c;洞口呈现…

闲谈简单设计(KISS)疑惑

忙碌了一年了项目又到了交付了&#xff0c;虽然项目能成功上线&#xff08;因为还有维护支持的团队&#xff09;。但是个人从技术上看&#xff0c;这是一个不那么成功的项目&#xff0c;因为后期艰难的修复bug,添加feature。这与简单设计有什么关系呢&#xff1f;在某模块开发起…

OSChina 周六乱弹 —— 有人骂你神经病怎么办?

2019独角兽企业重金招聘Python工程师标准>>> 周六了&#xff0c;大家有没有在认真加班呢&#xff1f;其实咱们程序员的生活真的不容易 熊大信了熊二的话&#xff1a;程序员的人生 码代码不容易&#xff0c;咱们还是去抢银行吧 sunny_chan&#xff1a;一天老师让同学…

手把手教你学Dapr - 6. 发布订阅

介绍发布/订阅模式允许微服务使用消息相互通信。生产者或发布者在不知道哪个应用程序将接收它们的情况下向主题发送消息。这涉及将它们写入输入通道。同样&#xff0c;消费者或订阅者订阅该主题并接收其消息&#xff0c;而不知道是什么服务产生了这些消息。这涉及从输出通道接收…

Android之AndroidManifest.xml文件解析和权限集合

一、关于AndroidManifest.xml AndroidManifest.xml 是每个android程序中必须的文件。它位于整个项目的根目录&#xff0c;描述了package中暴露的组件&#xff08;activities, services, 等等&#xff09;&#xff0c;他们各自的实现类&#xff0c;各种能被处理的数据和启动位置…