自学内容网 自学内容网

golang调用webview,webview2,go-webview2

go version go1.20 windows/amd64

先要了解一些第三方库

1、webview/webview

它是一个跨平台的轻量级的webview库,面向的是C/C++,使用它可以构建跨平台的GUI。webview就是浏览器内核,在不同操作系统上是不同的库,比如在windows上为webview2,所以webview与webview2不要搞混了。从功能上,它实现了Javascript与C/C++之间的相互调用,即bindings。具体实现原理应该与wails差不多。

在不同平台上的支持如下

PlatformTechnologies
LinuxGTK, WebKitGTK
macOSCocoa, WebKit
WindowsWindows API, WebView2

windows api 是windows系统提供的DLL(user32.dll),以此来构建windows上原生的GUI程序。

webview2是微软的Edge浏览器内核,用它来渲染前端页面,下载地址,所以这是两种不同的实现方式。而且webview2只支持windows系统。如果你的系统是Windows 10+,那么 WebView2 Runtime会默认安装了。

2、webview/webview_go

它使用Golang将webview/webview包装了一下,但是使用的是CGO,即webview/webview_go依赖于webview/webview,并使用CGO将依赖引入。

项目还不完善,我的本地系统x86_64-w64-mingw32下面找不到EventToken.h头文件,所以代码无法运行,Support for EventToken.h on mingw64 #45 依然没有解决。

3、jchv/go-webview2

它借鉴了webview/webview,使用Golang包装了webview2,不需要使用CGO,这是与webview_go的不同,当然此包只支持windows,因为它只是包装了webview2。在wails中使用的就是 jchv/go-webview2

它提供了一个示例:cmd/demo/main.go

import (
"log"

"github.com/jchv/go-webview2"
)

func main() {
w := webview2.NewWithOptions(webview2.WebViewOptions{
Debug:     true,
AutoFocus: true,
WindowOptions: webview2.WindowOptions{
Title:  "Minimal webview example",
Width:  800,
Height: 600,
IconId: 2, // icon resource id
Center: true,
},
})
if w == nil {
log.Fatalln("Failed to load webview.")
}
defer w.Destroy()
w.SetSize(800, 600, webview2.HintFixed)
w.Navigate("https://en.m.wikipedia.org/wiki/Main_Page")
w.Run()
}

在这里插入图片描述

在这里插入图片描述

go-webview2简要说明

webviewloader\module.go

先使用windows.NewLazyDLL("WebView2Loader")加载WebView2Loader.dll,如果没有找到,它会使用go-winloader库来加载go-webview2自带的WebView2Loader.dll文件(webviewloader/sdk/x64下)。

webview2的参考文档:https://learn.microsoft.com/zh-cn/microsoft-edge/webview2/

go-webview2基本已经封装的很好了,下面是一个WebView对象提供的功能。

// WebView is the interface for the webview.
type WebView interface {

// Run runs the main loop until it's terminated. After this function exits -
// you must destroy the webview.
Run()

// Terminate stops the main loop. It is safe to call this function from
// a background thread.
Terminate()

// Dispatch posts a function to be executed on the main thread. You normally
// do not need to call this function, unless you want to tweak the native
// window.
Dispatch(f func())

// Destroy destroys a webview and closes the native window.
Destroy()

// Window returns a native window handle pointer. When using GTK backend the
// pointer is GtkWindow pointer, when using Cocoa backend the pointer is
// NSWindow pointer, when using Win32 backend the pointer is HWND pointer.
Window() unsafe.Pointer

// SetTitle updates the title of the native window. Must be called from the UI
// thread.
SetTitle(title string)

// SetSize updates native window size. See Hint constants.
SetSize(w int, h int, hint Hint)

// Navigate navigates webview to the given URL. URL may be a data URI, i.e.
// "data:text/text,<html>...</html>". It is often ok not to url-encode it
// properly, webview will re-encode it for you.
Navigate(url string)

// SetHtml sets the webview HTML directly.
// The origin of the page is `about:blank`.
SetHtml(html string)

// Init injects JavaScript code at the initialization of the new page. Every
// time the webview will open a the new page - this initialization code will
// be executed. It is guaranteed that code is executed before window.onload.
Init(js string)

// Eval evaluates arbitrary JavaScript code. Evaluation happens asynchronously,
// also the result of the expression is ignored. Use RPC bindings if you want
// to receive notifications about the results of the evaluation.
Eval(js string)

// Bind binds a callback function so that it will appear under the given name
// as a global JavaScript function. Internally it uses webview_init().
// Callback receives a request string and a user-provided argument pointer.
// Request string is a JSON array of all the arguments passed to the
// JavaScript function.
//
// f must be a function
// f must return either value and error or just error
Bind(name string, f interface{}) error
}

Navigate方法的参数

  • 网络地址:https://en.m.wikipedia.org/wiki/Main_Page
  • 文件系统地址:file:///D:/dev/php/magook/trunk/server/go-webview/test.html,要使用绝对地址。当然,只要是浏览器能识别的文件都行,比如图片,网页,TXT文件等等。
  • 直接展示内容:就是将文件的内容复制进去,此时就需要指定内容是什么形式的,如果不指定就无法展示出来。格式为mimetype,content,其中 mimetype的常用的有data:image/gif, data:image/webp, data:image/jpeg, data:image/png, data:text/html, data:text/text,它会严格按照mimetype渲染,所以类型一定要对。
    但是不推荐这种用法,我发现它会将本行中#后面的内容当做注释舍弃掉,比如
......
let option_CrGrDQTOQuKK = {"color":["#5470c6","#91cc75","#fac858","#ee6666","#73c0de","#3ba272","#fc8452","#9a60b4","#ea7ccc"],"legend":{},"series":[{"name":"Category A","type":"bar","data":[{"value":235},{"value":41},{"value":90},{"value":256},{"value":202},{"value":40},{"value":11}]},{"name":"Category B","type":"bar","data":[{"value":277},{"value":69},{"value":158},{"value":274},{"value":116},{"value":43},{"value":233}]}],"title":{"text":"My first bar chart generated by go-echarts","subtext":"It's extremely easy to use, right?"},"toolbox":{},"tooltip":{},"xAxis":[{"data":["Mon","Tue","Wed","Thu","Fri","Sat","Sun"]}],"yAxis":[{}]}
......

你会发现这一行只剩下了let option_CrGrDQTOQuKK = {"color":[",肯定会影响结果的。所以,最好使用前两种方式。

Bind 方法,进行了绑定之后,js就可以调用name方法,而这个name方法在go中的实现逻辑就是 f。

func (w *webview) Bind(name string, f interface{}) error {
v := reflect.ValueOf(f)
if v.Kind() != reflect.Func {
return errors.New("only functions can be bound")
}
if n := v.Type().NumOut(); n > 2 {
return errors.New("function may only return a value or a value+error")
}
w.m.Lock()
w.bindings[name] = f
w.m.Unlock()

w.Init("(function() { var name = " + jsString(name) + ";" + `
var RPC = window._rpc = (window._rpc || {nextSeq: 1});
window[name] = function() {
  var seq = RPC.nextSeq++;
  var promise = new Promise(function(resolve, reject) {
RPC[seq] = {
  resolve: resolve,
  reject: reject,
};
  });
  window.external.invoke(JSON.stringify({
id: seq,
method: name,
params: Array.prototype.slice.call(arguments),
  }));
  return promise;
}
})()`)

return nil
}

它这里使用的是window.external.invoke()方法,即调用外部方法,因为它包装的是new Promise,所以使用 Promise 的机制来处理返回值。

在RPC调用方面,其内部机制还是postMessage()消息事件,更具体的可以阅读我关于wails的文章

如果 Debug = true,那么按F12可以打开调试。

我的代码如下:

package main

import (
"errors"
"log"

webview2 "github.com/jchv/go-webview2"
)

func main() {
w := webview2.NewWithOptions(webview2.WebViewOptions{
Debug:     true,
AutoFocus: true,
WindowOptions: webview2.WindowOptions{
Title:  "Minimal webview example",
Width:  800,
Height: 600,
IconId: 2, // icon resource id
Center: true,
},
})
if w == nil {
log.Fatalln("Failed to load webview.")
}
defer w.Destroy()

w.Bind("test_love", Great)

w.Navigate("file:///D:/dev/php/magook/trunk/server/go-webview/test.html")

w.Run()
}

func Great(param string) (result string, err error) {
if param == "I love you" {
return "I love you too", nil
} else if param == "I hate you" {
return "", errors.New("break up")
} else {
return "I don't know", nil
}
}

特别注意: Bind() 一定要在 Navigate() 之前调用,否则就是无效的!!

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>爱吗?</title>
</head>

<body>
    <div class="box">
        <input type="button" value="爱你" class="btn" onclick="love()">
        <input type="button" value="不爱了" class="btn" onclick="nolove()">
    </div>
</body>

</html>
<style>
    .box {
        margin-top: 100px;
        text-align: center;
    }

    .btn {
        display: inline-block;
        width: 100px;
        height: 50px;
        border-radius: 10px;
        border: 2px solid green;
        text-align: center;
    }
</style>
<script>

    function love() {
        window["test_love"]("I love you").then(result => {
            alert(result);
        }).catch(err => {
            alert(err);
        });
    }
    function nolove() {
        window["test_love"]("I hate you").then(result => {
            alert(result);
        }).catch(err => {
            alert(err);
        });
    }
</script>

打开调试,console.log(window),或者 console.log(window.test_love),就能看到你定义的方法已经被注册到了window对象上。

运行效果:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

由于wails引入了go-webview2,并做了扩展,所以直接使用wails框架即可。


原文地址:https://blog.csdn.net/raoxiaoya/article/details/143952304

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!