blazor wasm开发chrome插件
用blazor(Wasm)开发了一个chrome插件感觉效率挺高的,分享给大家
先简单介绍下WebAssembly的原理:
“WebAssembly是一种用于基于堆栈的虚拟机的二进制指令格式”
如上图,浏览器在执行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
基于模板的话会帮你引入哪些包
我也躺了很多坑,看看我给大佬提的issue,和大佬一起成长

这里我总结一套非常高效的方案给大家:
Backgroud用csharp写 popup,option等的html不要用balzor写,balzor加载html没有任何优势 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);
}
//转成jsonstring
var 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
下载下来后放入工程中,然后引入
这里还有一个关键
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();
}
}
在Program.cs进行注册 
那么你就可以在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拦截,转发等
关注公众号一起学习

blazor wasm开发chrome插件的更多相关文章
- 使用 Vuejs 开发 chrome 插件的注意事项
使用 Vuejs 开发 chrome 插件 chrome 插件的开发其实并不难,web开发者可以使用 html, css, javascript 轻松的开发实用的 chrome 插件. 一个好的 ch ...
- 使用Vuejs 开发chrome 插件的注意事项
chrome 插件的开发其实并不难,web开发者可以使用 html, css, javascript 轻松的开发实用的 chrome 插件. 一个好的 chrome 插件可以提高我们的开发效率,甚至方 ...
- 自己开发chrome插件生成二维码
摘要: 最近在开发微信项目时,需要在微信调试,所以经常会在微信中输入本地服务地址,输入起来特别麻烦,所以自己就想了想微信中的扫一扫,然后开发了这款chrome插件,将当前url生成二维码,用微信扫一扫 ...
- 开发chrome插件(扩展)
官方文档 https://developer.chrome.com/extensions/getstarted.html [干货]Chrome插件(扩展)开发全攻略 http://blog.haoji ...
- 替代Infinity绝佳的自主开发chrome插件
最近闲来无事在好朋(da)友(shen)的帮助下开发一个chrome插件,目的是为了替换infinity主页插件, 当然在此也推荐一波infinity确实不错,界面和易用性都是非常好用的水准了. 主页 ...
- 开发Chrome插件,实现网站自动登录
近期被一个事情困扰着,我们采购了一款软件,里面有一个数据大屏页,当登录过期后,数据就会保持原状,不再更新.和供应商反馈了很多次,都无法彻底解决数据显示的问题,没办法,自己周末在家研究,网站自动登录的事 ...
- 试着开发chrome插件
我的第一个chrome插件,是app形式的 代码如下 创建一个文件: 1.manifest.json { "version": "1.0", "man ...
- 使用Python开发chrome插件
本文由 伯乐在线 - xianhu 翻译,Daetalus 校稿.未经许可,禁止转载!英文出处:pythonspot.com.欢迎加入翻译小组. 谷歌Chrome插件是使用HTML.JavaScrip ...
- Web前端开发Chrome插件
参考:http://www.cnblogs.com/sosoft/p/3490481.html 越来越多的前端开发人员喜欢在Chrome里开发调试代码,Chrome有许多优秀的插件可以帮助前端开发人员 ...
随机推荐
- 洛谷P7078 [CSP-S2020] 贪吃蛇 题解
比赛里能做出这题的人真的非常厉害,至少他的智商和蛇一样足够聪明. 首先有一个结论: 当前最强的蛇吃了最弱的蛇之后,如果没有变成最弱的蛇,他一定会选择吃! 证明: 假设当前最强的蛇叫石老板. 如果下一条 ...
- linux性能优化基础——iommu相关配置
此篇文档介绍了IOMMU相关的信息: https://blog.chaosjohn.com/Check-VT-D-or-IOMMU-under-Linux.html iommu和vt-d都是io半虚拟 ...
- django 1.11.16之环境搭建
django版本:django1.11.16 windows环境 python 3.6.3 !!!可先安装虚拟环境在进行环境搭建 1.安装django:pip install django= ...
- [转载]CSS3实现文本垂直排列
最近的一个项目中要使文字垂直排列,也就是运用了CSS的writing-mode属性. writing-mode最初时ie中支持的一个属性,后来在CSS3中增添了这一新的属性,所以在ie中和其他浏览器中 ...
- 初学python-day5 流程控制
流程控制 一.判断语句 1.if语句(单一条件分支) 结构: if 表达式: 为真的时候执行的语句 概述:当程序运行到if语句时候,首页要计算表达式的值,判断真假,如果表达式的职位为真,则执行i ...
- Vue3学习(六)之使用Vue3进行数据绑定及显示列表数据
一.写在前面 说来还是比较惭愧的,从周二开始事就比较多,周三还电脑坏了,然后修电脑等等一些杂事,忙的团团转,因为周二.周三自己走的过多了,导致不敢直腰,周四卧床一天. 之前都听说<陈情令> ...
- 【UE4 C++】Slate 初探: Editor UI 与 Game UI
概述 名词区分 Slate Slate 是完全自定义.与平台无关的UI框架 应用 可用于编辑器UI,编辑器的大部分界面都是使用 Slate 构建的 可做为游戏UI 可作为独立应用开发 只能 C++ 开 ...
- 【数据结构与算法Python版学习笔记】树——平衡二叉搜索树(AVL树)
定义 能够在key插入时一直保持平衡的二叉查找树: AVL树 利用AVL树实现ADT Map, 基本上与BST的实现相同,不同之处仅在于二叉树的生成与维护过程 平衡因子 AVL树的实现中, 需要对每个 ...
- 第四次Scrum Metting
日期:2021年4月29日 会议主要内容概述:交代近两日工作,进一步细化上次讨论细节,代码合并. 一.进度情况## 组员 负责 两日内已完成的工作 后两日计划完成的工作 工作中遇到的困难 徐宇龙 后端 ...
- 2021.6.17考试总结[NOIP模拟8]
T1 星际旅行 其实就是求两条只走一遍的边的方案数. 考场上第一眼就感觉不可做,后来画了几个图,发现好像只要两个边是相连的就可以只走一遍,居然还真拿了30.. 其实是一道欧拉路的题,把每条非自环的边看 ...