工作一直是做后端研发,近期使用CefSharp研究数据爬取时需要一些界面展示,但WinForm进行界面开发时过于繁琐,于是想通过CefSharp加载本地htm资源进行UI的一些开发,可以利用前端丰富的一些功能库快速的搭建UI。刚开始自己是基于Bootstrap以及JQuery进行界面的开发,但是复杂的界面单htm编写会非常庞大,不利于后期维护扩展,于是搜了下目前流行的前端框架,发现VUE3是很流行的,于是尝试在WinForm项目使用Vue3做界面。
搭建Vue3项目
本地安装node相关模块,然后使用Create Preset进行快速搭建
# 全局安装脚手架 npm install -g create-preset # 使用 `vue3-ts-vite` 模板创建一个名为 `test-vue3` 的项目 preset init test-vue3 --template vue3-ts-vite
CefSharp加载本地资源文件
创建CustomSchemeHandler的工厂类
因为cefsharp采用了工厂的设计模式,所以还需要实现一个对应的工厂类
using CefSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
public class MySchemeHandlerFactory : ISchemeHandlerFactory
{
public const string SchemeName = "lightjia";// 自定义Scheme的名称
private Dictionary<string, MemoryStream> resources = null;
public void MySchemeHandlerFactory()
{
string resDir = @"F://myvue";
if(resources == null)
{
resources = new Dictionary<string, MemoryStream>();
}
var dirInfo = new DirectoryInfo(resDir);
foreach (var fi in dirInfo.GetFiles())
{
using (FileStream fs = new FileStream(fi.FullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (BinaryReader br = new BinaryReader(fs))
{
var dateLen = Convert.ToInt32(fs.Length);
MemoryStream ms = new MemoryStream(dateLen);
ms.Write(br.ReadBytes(dateLen), 0, dateLen);
resources[fi.Name] = ms;
}
}
}
}
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
if (schemeName == SchemeName)
{
return new MySchemeHandler(resources);
}
else
{
//error handler
return null;
}
}
}
创建自定义的Scheme类
自定义的Scheme类MySchemeHandler
using CefSharp;
using CefSharp.Callback;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
public class MySchemeHandler : IResourceHandler
{
private Dictionary<string, MemoryStream> resources;
private string mimeType;
private MemoryStream memStream;
public MySchemeHandler(Dictionary<string, MemoryStream> res)
{
resources = res;
}
public void Cancel()
{
}
public void Dispose()
{
memStream = null;
}
public void GetResponseHeaders(IResponse response, out long responseLength, out string redirectUrl)
{
redirectUrl = null;
if (memStream != null)
{
response.MimeType = mimeType;
responseLength = memStream.Length;
response.StatusCode = (int)System.Net.HttpStatusCode.OK;
response.StatusText = "OK";
}
else
{
responseLength = 0;
response.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
response.StatusText = "NotFound";
}
}
public bool Open(IRequest request, out bool handleRequest, ICallback callback)
{
var uri = new Uri(request.Url);
var resourceName = uri.AbsolutePath.TrimEnd(new char[]{'/'});
if (resources.TryGetValue(resourceName, out memStream))
{
memStream.Seek(0, SeekOrigin.Begin);
handleRequest = false;
Task.Run(() =>
{
using (callback)
{
var fileExtension = Path.GetExtension(resourceName);
mimeType = ResourceHandler.GetMimeType(fileExtension);
callback.Continue();
}
});
return true;
}
else
{
mimeType = ResourceHandler.GetMimeType(".html");
memStream = null;
callback.Continue();
}
handleRequest = true;
return false;
}
public bool ProcessRequest(IRequest request, ICallback callback)
{
throw new NotImplementedException();
}
public bool Read(Stream dataOut, out int bytesRead, IResourceReadCallback callback)
{
callback.Dispose();
if (memStream == null)
{
bytesRead = 0;
return false;
}
var buffer = new byte[dataOut.Length];
bytesRead = memStream.Read(buffer, 0, buffer.Length);
dataOut.Write(buffer, 0, buffer.Length);
return bytesRead > 0;
}
public bool ReadResponse(Stream dataOut, out int bytesRead, ICallback callback)
{
throw new NotImplementedException();
}
public bool Skip(long bytesToSkip, out long bytesSkipped, IResourceSkipCallback callback)
{
throw new NotImplementedException();
}
}
vue编译后的资源与本地资源映射
import { createRouter, createWebHistory } from 'vue-router'
import routes from './routes'
import { websiteTitle } from '@/config'
const router = createRouter({
//history: createWebHistory(import.meta.env.BASE_URL),
//做资源映射
history: createWebHistory(
import.meta.env.DEV ? import.meta.env.BASE_URL : '/myvue/index.html'
),
routes,
})
router.afterEach((to) => {
const { title } = to.meta
document.title = title ? `${title} - ${websiteTitle}` : websiteTitle
})
export default router
然后var ChromeBrowser = new ChromiumWebBrowser("lightjia://domin/myvue/index.html")
就会加载本地的资源文件
然后就进行web界面的开发