网站关键词优化排名软件系统,上海培训机构,网站建设费用还是网络专业,淄博网站seoBlazor作为一个新兴的交互式 Web UI 的框架#xff0c;有其自身的优缺点#xff0c;如果现有的 JavaScript 组件能移植到 Blazor#xff0c;无疑让 Blazor 如虎添翼#xff0c;本文就介绍一下自己在开发 BulmaRazor 组件库的时#xff0c;封装现有的 JavaScript 组件的方法… Blazor作为一个新兴的交互式 Web UI 的框架有其自身的优缺点如果现有的 JavaScript 组件能移植到 Blazor无疑让 Blazor 如虎添翼本文就介绍一下自己在开发 BulmaRazor 组件库的时封装现有的 JavaScript 组件的方法文中以 TuiEditor 为例。开始首先找到现有 TuiEditor 的主页或者文档这一步很简单我们找到官网 https://ui.toast.com/tui-editor/ 分析一下组件的使用方法一般都是有样式文件有 JavaScript 文件有一个 options 对象来初始化一个主对象主对象上有方法和事件大概就是这些了我们先下载所需的文件然后一步一步处理。样式部分该组件需要两个样式 codemirror.min.css 和 toastui-editor.min.css 由于一个组件库不只这一个组件为了引用方便我们需要使用 BuildBundlerMinifier 合并文件不知道 BuildBundlerMinifier 的同学网上查一下。在网站的根目录需要有 BuildBundlerMinifier 所需的配置文件 bundleconfig.json对应的配置如下 {outputFileName: wwwroot/bulmarazor.min.css,inputFiles: [wwwroot/css/tuieditor/codemirror.min.css,wwwroot/css/tuieditor/toastui-editor.min.css]},
项目中很可能还有其他的样式文件一起合并就好了引用的时候我们只需要一个样式文件这里就是 bulmarazor.min.css。脚本部分tuieditor 的 JavaScript 文件只有一个当然一般 JavaScript 组件的脚本文件都是一个如果是普通的 web 开发的话直接引入就可以了但是在 Blazor 中有些麻烦需要使用 JavaScript 互操作互操作是指 C# 代码可调用到 JavaScript 代码而 JavaScript 代码也可调用到 C# 代码。C# 调用 JavaScript 代码有两种方法一种是使用 IJSRuntime 调用挂载到 window 对象上的方法另一种是使用模块隔离的方式调用这里我们需要模块隔离因为有以下优点导入的 JavaScript 不再污染全局命名空间。库和组件的使用者不需要引用相关的 JavaScript。关于 JavaScript 模块可以参考这里 这里 使用 JavaScript 模块依赖于 import 和 export而一般的 JavaScript 类库并不支持所以我们需要些一些导出的代码文件结构如下我们忽视红色标注先来看一下 toastui-editor-export.js 这个文件export function initEditor(options) {options.el document.getElementById(options.elid);let editor new toastui.Editor.factory(options);return editor;
}
toastui-editor-all.min.JavaScript 这个文件就是 JavaScript 组件文件我们不用去改它也不应该去改它因为后续升级了我们可以直接覆盖的toastui-editor-export.js 就是我们专门写的一个导出类库中所需功能的导出文件。为了引用方便我们还是需要合并一下就是图片示现的那样合并配置如下 {outputFileName: wwwroot/js/tuieditor.min.js,inputFiles: [wwwroot/jsplugin/tuieditor/toastui-editor-all.min.js,wwwroot/jsplugin/tuieditor/toastui-editor-export.js]}
现在我们使用隔离的方式引用 wwwroot/js/tuieditor.min.js 就可以了。当我们新建一个Razor组件项目的时候会带有调用的例子我们比猫画虎搞定using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;namespace BulmaRazor.Components
{public class BulmaRazorJsInterop : IAsyncDisposable{private readonly LazyTaskIJSObjectReference tuiEditorModuleTask;public BulmaRazorJsInterop(IJSRuntime jsRuntime){tuiEditorModuleTask new(() jsRuntime.InvokeAsyncIJSObjectReference(import, ./_content/BulmaRazor/js/tuieditor.min.js).AsTask());}public async ValueTaskIJSObjectReference TuiEditorInit(TuiEditorOptions options){var module await tuiEditorModuleTask.Value;return await module.InvokeAsyncIJSObjectReference(initEditor, options.ToParams());}public async ValueTask DisposeAsync(){if (tuiEditorModuleTask.IsValueCreated){var module await tuiEditorModuleTask.Value;await module.DisposeAsync();}}}
}
Blazor 组件部分组件文件是 TuiEditor.razorUI代码是非常简单的就是一个带有 id 属性的 div 容器id 很重要是我们互操作的基础这里我们使用GUID生成唯一的id。我们需要在 blazor 组件呈现之后调用 JavaScript 代码来初始化我们的 JavaScript 组件调用 JavaScript 代码之后返回了js 对象的引用editor注意editor和上述var module await tuiEditorModuleTask.Value; 中的 module 是一样的都是 JavaScript 对象引用。大致的代码如下inject BulmaRazorJsInterop JsInteropdiv idId/divcode {readonly string Id tuiEditor_ Guid.NewGuid().ToString(N);IJSObjectReference editor;[Parameter]public TuiEditorOptions Options { get; set; }protected override void OnInitialized(){if (Options null)Options new TuiEditorOptions();Options.elid Id;base.OnInitialized();}protected override async Task OnAfterRenderAsync(bool firstRender){await base.OnAfterRenderAsync(firstRender);editor await JsInterop.TuiEditorInit(Options);}
}
Options选项部分TuiEditor 组件中有个参数 TuiEditorOptions 是要对应 JavaScript 中的 options 参数的我们需要自己定义一个这里我们使用两个类来使用一个是针对 JavaScript 的 JsParams 类似字典的对象一个是针对使用者的 TuiEditorOptions 。JsParams 就是一个Dictionarystring,object为了方便我们过滤了空值 internal class JsParams:Dictionarystring,object{public void AddNotNull(string key, object value){if (value ! null){base.Add(key,value);}}}
TuiEditorOptions 类除了参数之外包含一个 ToParams() 的方法把自己转换成 JsParams:
public class TuiEditorOptions
{internal string elid { get; set; }/// summary/// Editors height style value. Height is applied as border-box ex) 300px, 100%, auto/// /summarypublic string Height { get; set; }/// summary/// 是否是查看器/// /summarypublic bool? Viewer { get; set; }//...其他参数internal JsParams ToParams(){JsParams ps new JsParams();var def BulmaRazorOptions.DefaultOptions.TuiEditorOptions;ps.AddNotNull(elid, elid);ps.AddNotNull(viewer,Viewer);ps.AddNotNull(height, Height ?? def.Height);//...其他参数return ps;}
}
有几个原因使用 JsParams null值可以不传递因为js的options一般都用默认值减少传输可以使用默认设置如上有个BulmaRazorOptions.DefaultOptions.TuiEditorOptions;可以灵活的手动处理参数上面例子没有提现出来不过组件写多了肯定会遇到这种情况对象的方法JavaScript 组件一般也会公开许多实例方法比如获得焦点设置内容获取内容等等在在前面我们一直保存了 JavaScript 组件实例的引用也就是在 TuiEditor 中的 editor 对象向公开哪些方法在 TuiEditor.razor 中添加就是了 public void Focus(){editor?.InvokeVoidAsync(focus);}public ValueTaskstring GetMarkdown(){return editor?.InvokeAsyncstring(getMarkdown) ?? new ValueTaskstring();}public void InsertText(string text){editor?.InvokeVoidAsync(insertText, text);}public ValueTaskbool IsViewer(){return editor?.InvokeAsyncbool(isViewer) ?? new ValueTaskbool(false);}//...其他需要的方法
对象事件JavaScript 组件对象有自己的事件在 JavaScript 中直接设置 JavaScript 函数就可以了但是并不能把 C# 方法或者委托传递给 js这里就需要用到 JavaScript 调用C#方法了。Blazor 框架中 JavaScript 只能调用静态方法而我们实际中是基于对象来写逻辑的所有我专门写了一个类来处理js的调用JSCallbackManager public static class JSCallbackManager{private static ConcurrentDictionarystring, Dictionarystring, Delegate eventHandlerDict new();public static void AddEventHandler(string objId, string eventKey, Delegate delegate){var eventHandlerList eventHandlerDict.GetOrAdd(objId, (key) new Dictionarystring, Delegate());eventHandlerList[eventKey] delegate;}public static void DisposeObject(string objId){if (eventHandlerDict.Remove(objId, out Dictionarystring, Delegate handlers)){handlers.Clear();}}[JSInvokable]public static object JSCallback(string objId, string eventKey){if (eventHandlerDict.TryGetValue(objId, out Dictionarystring, Delegate handlers)){if (handlers.TryGetValue(eventKey, out Delegate d)){var obj d.DynamicInvoke();return obj;}}return null;}}
我们使用一个嵌套的字典来保存了Blazor组件的回调委托每一个组件对象都有一个唯一的Id每一个组件类型都可以有不同名称的 JavaScript 事件回调。比如我们想订阅 JavaScript 组件实例的 load 事件我们需要改两个地方第一个是 toastui-editor-export.js 导出文件:export function initEditor(options) {options.el document.getElementById(options.elid);options.events {load: function () {DotNet.invokeMethodAsync(BulmaRazor, JSCallback, options.elid, load);}}let editor new toastui.Editor.factory(options);return editor;
}
JavaScript 的事件还是需要用 js来做然后在js方法内部调用 C# 方法。第二个是需要在 TuiEditor 中添加回调委托 [Parameter]public EventCallbackTuiEditor OnLoad { get; set; }protected override void OnInitialized(){if (Options null)Options new TuiEditorOptions();Options.elid Id;//这里添加回调委托并把js事件公开成了Blazor组件事件JSCallbackManager.AddEventHandler(Id, load, new FuncTask(() OnLoad.InvokeAsync(this)));base.OnInitialized();}protected override ValueTask DisposeAsync(bool disposing){//移除对象的所有回调委托JSCallbackManager.DisposeObject(Id);return base.DisposeAsync(disposing);}
这样我们就把 JavaScript 组件事件移植到了 Blazor 组件。修整经过上述不知组件基本移植完了但还不能很好的使用第一因为界面是 js在操作所以我们应该禁用 Blazor组件的渲染 protected override bool ShouldRender(){return false;}
在js的options中有个initialValue属性是初始化内容的我们改成Blazor的形式最好是可以绑定 [Parameter]public EventCallbackTuiEditor OnBlur { get; set; }protected override void OnInitialized(){if (Options null)Options new TuiEditorOptions();Options.InitialValue _value;Options.elid Id;//这里也是通过js事件触发JSCallbackManager.AddEventHandler(Id, blur, new FuncTask(async () {await setValue();await OnBlur.InvokeAsync(this);}));base.OnInitialized();}private string _value;[Parameter]public string Value{get { return _value; }set{_value value;SetMarkdown(value, true);}}[Parameter]public EventCallbackstring ValueChanged { get; set; }private async Task setValue(){_value await GetMarkdown();await ValueChanged.InvokeAsync(_value);}public void SetMarkdown(string markdown, bool cursorToEnd true){editor?.InvokeVoidAsync(setMarkdown, markdown, cursorToEnd);}
这样我们就可以使用 Blazor 绑定语法了TuiEditor bind-Valuemarkdown/TuiEditor
code{private string markdown # Init Title;
}
效果如下在线效果点击这里源代码BulmaRazor官网Gitee地址Github地址希望喜欢 Blazor 和 BulmaRazor 的朋友给个Star鼓励一下该项目从2021年的春节假期开始一个人做真心的累和耗时您的鼓励是我坚持下去的最大动力