一、WebView2

通常来说,选择Web应用还是原生应用来开发是一种在通用性和本地权限之前的权衡。WebApp兼容的范围很广,而且Web前端代码基于浏览器天生跨平台,而且前端框架多好开发。而原生应用有很大本地权限,可以进行各种文件和操作系统接口的调用。WebView可以结合这两者的优点进行开发。

WebView2允许你在本地App里面嵌入web相关的技术(例如HTML,CSS和JavaScript)。WebView2控件使用微软的Edge作为渲染引擎,你可以嵌入一部分或者整个App都用WebView来做。微软还有一个类似的出得更早框架是CEFSharp,这个但是下载依赖要稍繁琐,就不过多介绍。

1.1 优势

WebView2具有如下的一些优势:

  • 可以运用Web的生态和技术库:Web前端的各种库,工具都可以直接套用来开发界面。
    开发得更
  • Windows 7,8,10都支持。
  • 本地应用API:可以调用完整的本地API。
  • 代码复用:代码库中的前端代码可以跨平台重用。
  • 微软支持:这个插件这是有微软持续更新的GA版本,支持.NET FrameWork 4.7.2以后的版本、.NET Core、Win32 C/C++,.NET 5,.NET 6,WinUI 2.0/3.0。
  • 带内核更新的部署:依赖于定期更新和修复漏洞的Choromium内核。
  • 固定版本内核部署:可以选一个固定的Choromium版本部署WebView2。
  • 增量支持:可以逐步将网页逐个添加或替换到你的应用中。

1.2 示例

下面,我们使用 aardio 调用 web.view (WebView2)写一个最简单的程序。

import win.ui;/*DSG{{*/mainForm = win.form(text="WebView2")mainForm.add(btnCallJs={cls="button";text="调用 JS 函数";left=461;top=395;right=726;bottom=449;note="点这里调用 JavaScript  函数";z=1};custom={cls="custom";left=17;top=21;right=730;bottom=356;z=2})/*}}*///创建浏览器组件import web.view;var wb = web.view(mainForm.custom);//导出本地函数给网页 JavaScriptwb.external = {getComputerName = function(){return sys.getComputerName();}}import sys;//写入网页 HTMLwb.html = /**         (async ()=>{     var n = await aardio.getComputerName();    alert(n);    })()    **///响应按钮事件mainForm.btnCallJs.oncommand = function(id,event){//调用 JS 函数wb.xcall("document.write","测试")}mainForm.show();win.loopMessage();

上面就是一个完整程序的源代码,可以一键生成独立 EXE 文件。

二、快速入门

2.1 基本使用

首先,点选 【aardio 主菜单】-> 【新建工程】-> 【窗口程序】->【空白工程】,然后点击【创建工程】。

如果熟悉网页前端开发,也可以点击 【新建工程】-> 【Web 界面】-> 【 WebView2】来创建工程。

双击工程入口代码 main.aardio 打开主窗口,自【界面控件】中拖一个【调用 JS 函数】的按钮上去,再拖一个 custom 控件到窗体上: 用来嵌入网页。

然后,切换到代码视图,添加以下代码创建网页浏览器。

import web.view;var wb = web.view(mainForm.custom);

web.view 的第 1 个参数指定要嵌入 WebView2 的窗口对象,该参数可以是 mainForm.custom 这样的控件窗口,也可以是 mainForm 这样的窗体对象。

然后,就可以开始写网页 HTML 代码了。

wb.html = ""//打开一个网址wb.go("网址")wb.go("/res/index.html")

可以打开资源目录的网页,资源目录可以嵌入 EXE 生成 独立 EXE 文件,放心不用多写一句代码。

我们可以添加下面的代码导出 external 对象给网页 JavaScript。

wb.external = {getComputerName = function(){return sys.getComputerName();}}import sys;

在网页 JavaScript 里可以调用上面导出的 external 对象,不过在 JavaScript 里要用 aardio 这个名字表示 external 对象,代码如下。

wb.html = /**         (async ()=>{     var n = await aardio.getComputerName();    alert(n);    })()    **/

注意在 aardio 中 /* 注释 */ 可以作为字符串赋值给其他变量。要注意所有 aardio 对象在 JavaScript 中都是异步 Promise 对象。如上在 async 函数体内可以愉快地使用 await 调用 aardio 函数。

接着,我们在窗体设计视图双击【调用 JS 函数】按钮,这会切换到代码视图,并自动添加以下回调函数。

mainForm.btnCallJs.oncommand = function(id,event){}

用户点击按钮时就会调用上面的函数。接着,小改一下添加 aardio 代码调用 JavaScript 函数。

mainForm.btnCallJs.oncommand = function(id,event){//调用 JS 函数wb.xcall("document.write","测试")}

可以在 aardio 中点击【运行】按钮直接运行代码,也可以点击【发布】按钮直接生成 EXE 文件。

2.2 自动缩放

web.view() 构造函数的第 1 个嵌入窗口参数可以是 win.form 对象(独立窗口),也可以是 custom, static 这样的普通控件对象。例如前面的例子就是将 WebView2 嵌入 custom 控件。

import web.view;var wb = web.view(mainForm.custom);

aardio 中的所有控件都可以非常方便的支持自动缩放。只要简单地在窗体设计器中选定控件,然后在【属性】面板设置【固定边距】、【自适应大小】这些属性就可以。

另外一个更简单的方法,是在窗体设计器上点右键,然后在弹出菜单中点击【九宫格缩放布局】—— aardio 将会自动设置所有控件的自适应缩放属性。

至于网页内容自适应排版很简单,不需要在 aardio 中编写代码。

2.3 导出 aardio 函数到 Javascript

我们介绍过使用 external 导出 aardio 函数到网页 JavaScript 。我们还可以用 wb.export 导出 aardio 函数,先看例子:

import web.view;var wb = web.view(mainForm.custom);wb.export({alert = function(msg){winform.msgbox(msg) };nativeAdd = function(a,b){ return a + b; }})

不过,导出 aardio 函数到 Javascript时有以下几点需要注意:

  1. wb.export() 导出的是 JavaScript 全局函数。
  2. wb.export() 导出的函数在 JavaScript 中同样是异步 Promise 对象。
  3. wb.export() 导出的 Javascript 全局函数, 使用 JSON 自动转换调用参数和返回值,可以更好的兼容只能支持纯aardio 对象 / 纯 JavaScript 对象的代码。
  4. wb.export() 导出的函数内部禁止调用 wb.doScript 或 wb.eval 执行Javascript 。

wb.external 内部是调用 wb.exportHostObject() 导出 aardio 对象,中间不需要经过 JSON 自动转换。

2.4 调用本地 Ping 命令

我经常被问到几个类似的问题:

  • JavaScript 的异步函数太麻烦了,怎样把他搞成同步的,不用 await ,不用 async 。
  • JavaScript 的异步函数太好用了,怎样在 aardio 中也这样搞,如何在 aardio 里 await 。

其实同步有同步的优势,异步有异步的好处,扬长避短是智慧,倒行逆施最累人。下面我们一起来写一个在 WebView2 中调用本地 Ping 命令的小程序体验一下。
首先,创建窗口。

import win.ui;var winform = win.form(text="Ping")

接着,基于窗口创建 WebView2 浏览器组件。

import web.view;var wb = web.view(winform);

然后,使用 external 对象导出 JavaScript 可以调用的本地函数。

import process.popen;wb.external = {  ping = function(domain){        var prcs = process.popen("ping "+ domain);    for( all,out,err in prcs.each() ){        wb.invoke("document.body.insertAdjacentText",'beforeend',all);     }        return "恭喜,事做好了!"  } }

在 JavaScript 里用 aardio.ping() 就可以直接调用上面的 external.ping() 函数了,下面在网页里写 JavaScript 来调用 aardio 函数。

wb.html = /**doSomething = async() => {    var result = await aardio.ping('www.baidu.com');    document.body.insertAdjacentText('beforeend',result);};


上面程序的完整 aardio 源代码如下:

//创建窗口import win.ui;var winform = win.form(text="Ping")//嵌入浏览器组件import web.view;var wb = web.view(winform);//导出 aardio 函数到 JavaScriptwb.external = {ping = function(domain){//同步有同步的优势,扬长避短是智慧,倒行逆施最累人。 var prcs = process.popen("ping "+ domain);for( all,out,err in prcs.each() ){    wb.invoke("document.body.insertAdjacentText",'beforeend',all); }return "恭喜,事做好了!"} }import process.popen;//写入网页 HTMLwb.html = /**doSomething = async() => {//异步有异步的好处,扬长避短是智慧,倒行逆施最累人。  var result = await aardio.ping('www.baidu.com');  document.body.insertAdjacentText('beforeend',result);};

2.5 aardio 调用 JS 函数

在 aardio 中可以使用 wb.doScript() , wb.eval() , wb.xcall() 等函数调用网页 JavaScript ,下面看一个在 aardio 中调用 xterm.js 的简单例子。

import win.ui;var winform = win.form(text="xterm")import web.view;var wb = web.view(winform);wb.html = /**                let term  = new Terminal();    term.open(document.body);    term.write('\x1b[31m红色字体\x1b[37m测试')  **/wb.xcall("term.write",'\e[32m绿色字体');winform.show();win.loopMessage();

2.6 用网页实现窗口标题栏

【无边框窗口】指的是去掉独立窗体默认的边框与标题栏,然后由程序自行定制边框与标题栏。aardio 实现的方式很简单,只需要首页在窗体属性中指定【边框】属性为 none。

然后,直接运行后显示的窗体就没有边框和标题栏了( 按 Alt + F4 关闭窗口 )。并且,添加下面的代码就可以为窗体添加标题栏、标题栏按钮、阴影边框、并支持拖动边框缩放。

import win.ui.simpleWindow;win.ui.simpleWindow(winform);

接下来,我们使用网页实现标题栏。我们知道网页绘制一个标题栏与标题栏按钮很简单,难点在于怎么在网页里控制窗口。我们先学习几个专用于无边框窗口的 aardio 函数:

winform.hitMax() //模拟点击最大化按钮winform.hitMin() //模拟点击最小化按钮winform.hitClose() //模拟点击关闭按钮winform.hitCaption() //拖动标题栏

下面,我们通过一个简单的例子,先看下运行效果。

实现的源码如下:

import win.ui;/*DSG{{*/var winform = win.form(text="无边框窗口";right=759;bottom=469;bgcolor=16777215;border="none")winform.add()/*}}*/import web.view;var wb = web.view(winform); //导出为 Javascript 中的 aardio 对象wb.external = { close = function(){winform.close();};hitCaption = function(){winform.hitCaption();};hitMin = function(){winform.hitMin();};hitMax = function(){return winform.hitMax();};}wb.html = /**    html {    margin: 0px;    padding: 0px; background-color: #202020; }#title-bar {height: 32px;padding: 0px;    margin: 0px;}#title-bar .caption {position: fixed;top: 0px;left: 0px;width: 100%;padding-left: 10px;color: #ADADAD;line-height: 32px;font-size: 14px;cursor: default;user-select:none;}#title-bar .buttons {position: fixed;top: 1px;right: 1px;}#title-bar button {font: 14px Marlett ;color: #F5F5F5;background-color: transparent;border: none;height: 28px;width: 28px;  } #title-bar button:hover {background-color: #FF4500;}#title-bar button:active {background-color: #B0451E;color: #C5C5C5;}#main {padding: 12px;color: #C0C0C0;}                               按住这里调用 aardio.hitCaption() 拖动窗口                                                 1、请指定窗体「边框」属性为 none ,创建无边框窗口。
2、调用 win.ui.shadow(winform) 创建阴影边框
**/ //添加阴影边框import win.ui.shadow;win.ui.shadow(winform);//设置窗口缩放范围import win.ui.minmax;win.ui.minmax(winform);//切换最大化、还原按钮winform.adjust = function( cx,cy,wParam ) {if( wParam == 0x2/*_SIZE_MAXIMIZED*/ ){ wb.doScript(`document.getElementById("max-btn").innerText="2";`)}elseif( wParam == 0x0/*_SIZE_RESTORED*/ ){wb.doScript(`document.getElementById("max-btn").innerText="1";`)} };winform.show();win.loopMessage();

三、WebView2前端工程开发

3.1 工程化开发

如果熟悉网页前端开发,也可以点击 【新建工程】->【Web 界面】->【WebView2 】创建工程。


注意,WebView2 默认工程的【网页源码】这个目录的【内嵌资源】属性设置为 false:也就是说发布后的 EXE 文件不会包含这个目录。

3.2 其他浏览器组件

aardio 中的浏览器组件非常多,用法与 web.view 基本都类似。aardio 甚至可以调用操作系统已安装的 Chrome,Edge 等浏览器写软件界面。