Electron 不错,但也不是完美的。

Electron 带来了很多优秀的桌面软件,但并不一定总是适合我们的需求。

多个选择总是好事!

我使用 Electron 遇到的一些麻烦

1、Electron 太大了!

2、每一个 Electron 写的软件都要重复地带一个 Electron …… 升级与分发都不方便。

3、Electron 不方便嵌入其他窗口界面,与其他语言、技术融合不易。

4、并不是所有桌面软件都需要 Electron 的跨平台特性。macOS , Linux 的桌面系统市场份额小于被遗忘的 Windows 8 ,如果软件只是在 Windows 平台运行,并且需要大量与专用系统 API 交互,跨平台反而是不必要的负担。

5、我曾经在 aardio 中封装了一个 electron 扩展库,然后我在写这个扩展库的时候,当时看到的还是 remote 真香 …… 然后我为这个扩展库写了个很大的 JS 文件就用到了 remote。可是等我写完没多久, 就看到 remote 被 Electron 抛弃了,remote 会慢一万倍 ,各种缺陷 ……

WebView2 的优势

1、WebView2 基于性能强悍的 Edge(Chromium) 内核。

2、调用 WebView2 生成的软件体积很小。所有基于 WebView2 的软件可以共享同一个 WebView2 组件。Win11 已经内置 WebView2 组件,其他操作系统也可以快速地自动安装 WebView2 。

3、WebView2 接口非常简洁,嵌入其他窗口界面也非常方便。

总结一句话就是:WebView2 简单、好用、生成软件体积小。

aardio 标准库中的 web.view 就是基于 WebView2。WebView2 的接口是如此简洁,所以我写的这个库也只有很少的代码。因为 aardio 可以将网页自动内嵌到独立 EXE 文件,就可以非常方便地生成独立 EXE 程序。

一个最简单的程序演示

下面我们用 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); //导出本地函数给网页 JavaScript
wb.external = {
getComputerName = function(){
return sys.getComputerName();
}
}
import sys; //写入网页 HTML
wb.html = /**
<html>
<head>
<script>
(async ()=>{
var n = await aardio.getComputerName();
alert(n);
})()
</script>
</head>
<body>
**/ //响应按钮事件
mainForm.btnCallJs.oncommand = function(id,event){
//调用 JS 函数
wb.xcall("document.write","测试")
} mainForm.show();
win.loopMessage();

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

入门

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

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

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

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

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

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

下面使用

wb.html = "<html></html>"

就可以写网页 HTML 代码了。

或者使用

wb.go("网址")

可以打开指定的网页。

使用

import wsock.tcp.simpleHttpServer;
wb.go("\res\index.html");

可以打开资源目录的网页,支持SPA 单页应用。资源目录可以嵌入 EXE 生成 独立 EXE 文件,放心不用多写其他代码。

添加下面的代码导出 external 对象给网页 JavaScript :

//导出本地函数给网页 JavaScript
wb.external = {
getComputerName = function(){
return sys.getComputerName();
}
} import sys;

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

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

注意在 aardio 中 /* 注释 */ 可以作为字符串赋值给其他变量,请参考: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 文件。

如何将网页显示在窗体的指定位置?并且支持自动缩放?

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

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

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

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

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

使用 wb.export 导出 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;
}
})

注意:

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 自动转换。

示例:网页 JavaScript 调用本地 Ping 命令

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

1、JavaScript 的异步函数太麻烦了,怎样把他搞成同步的,不用 await ,不用 async 。

2、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 = /**
<body style="white-space: pre;"><script>
doSomething = async() => {
var result = await aardio.ping('www.baidu.com');
document.body.insertAdjacentText('beforeend',result);
};
</script>
<button onclick="doSomething()">开始干活了</ping>
**/

就这么短短几句,一个简单的程序就完成了,请看运行效果:

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

//创建窗口
import win.ui;
var winform = win.form(text="Ping") //嵌入浏览器组件
import web.view;
var wb = web.view(winform); //导出 aardio 函数到 JavaScript
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 "恭喜,事做好了!"
}
}
import process.popen; //写入网页 HTML
wb.html = /**
<body style="white-space: pre;"><script>
doSomething = async() => { //异步有异步的好处,扬长避短是智慧,倒行逆施最累人。
var result = await aardio.ping('www.baidu.com');
document.body.insertAdjacentText('beforeend',result);
};
</script>
<button onclick="doSomething()">开始干活了</ping>
**/ //显示窗口
winform.show(); //启动界面消息循环
win.loopMessage();

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 = /**
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title></title>
<link rel="stylesheet" href="https://unpkg.com/xterm@4.13.0/css/xterm.css">
<script src="https://unpkg.com/xterm@4.13.0/lib/xterm.js"></script>
</head>
<body style="height:100vh;">
<script>
let term = new Terminal();
term.open(document.body);
term.write('\x1b[31m红色字体\x1b[37m测试')
</script>
</body>
</html>
**/ wb.xcall("term.write",'\e[32m绿色字体'); winform.show();
win.loopMessage();

无边框窗口:用网页实现窗口标题栏

「无边框窗口」指的是去掉独立窗体默认的边框与标题栏,然后由程序自行定制边框与标题栏。

aardio 做这事还是很容易的,首页在窗体属性中指定「边框」属性为 none。

这样直接运行后显示的窗体就没有边框和标题栏了( 按 Alt + F4 关闭窗口 )。

然后添加下面的代码就可以为窗体添加标题栏、标题栏按钮、阴影边框、并支持拖动边框缩放:

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

win.ui.simpleWindow 的源码很简单,参考其源码也可以自己编写新的库定制边框与标题栏。

这里我们不用上面的方法,而是用网页实现标题栏。

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

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

下面写个简单的例子,先看下运行效果:

WebView2 无边框窗口示例完整源码如下:

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 = /**
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style type="text/css">
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;
} </style>
<script type="text/javascript"> </script>
</head>
<body>
<div id="title-bar" >
<div class="caption" onmousedown="aardio.hitCaption()">按住这里调用 aardio.hitCaption() 拖动窗口 </div>
<div class="buttons">
<button id="min-btn" onclick="aardio.hitMin()">0</button>
<button id="max-btn" onclick="aardio.hitMax()">1</button>
<button id="close-btn" onclick="aardio.close()">r</button>
</div>
</div>
<div id="main">
1、请指定窗体「边框」属性为 none ,创建无边框窗口。<br />
2、调用 win.ui.shadow(winform) 创建阴影边框<br />
</div>
<script src="default.js"></script>
</body>
</html>
**/ //添加阴影边框
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();

以上源码来自 aardio 自带范例 > Web 界面 > web.view :

WebView2 + 前端工程

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

运行创建的范例工程会显示帮助:

这些熟悉前端的一看就懂,就不多说了。

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

而工程中的「网页」目录「内嵌资源」属性为 true —— 也就是说发布后的 EXE 文件会包含这个目录。

「网页」目录「本地构建」属性为 true —— 这指的是该目录下的文件会无条件添加到发布 EXE 文件中(不必添加到工程 )。

其他浏览器组件

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

请参考「 aardio 自带范例 > Web 界面」:

放弃 Electron,拥抱 WebView2!JavaScript 快速开发独立 EXE 程序的更多相关文章

  1. aardio + .NET 快速开发独立 EXE 程序,可防 ILSpy 反编译

    简介 aardio 可以非常方便地调用 .NET( 不需要任何复杂的步骤 ). .NET 在 aardio 中很好用,系统自带 .NET 组件以及各种开源 .NET 组件在 aardio 用户中也很受 ...

  2. aardio + PowerShell 可视化快速开发独立 EXE 桌面程序

    aardio 可以方便地调用 PowerShell ,PowerShell 中也可以自由调用 aardio 对象与函数.不用带上体积很大的System.Management.Automation.dl ...

  3. aardio + PHP 可视化快速开发独立 EXE 桌面程序

    aardio 支持与很多编程语言混合开发.网络上大家分享的 aardio + Python 混合开发的文章很多,aardio + PHP 的文章却很少. 其实 aardio 与 PHP 混合开发是真的 ...

  4. Bootstrap 是一个用于快速开发 Web 应用程序和网站的前端框架

    Bootstrap 是一个用于快速开发 Web 应用程序和网站的前端框架.Bootstrap 是基于 HTML.CSS.JAVASCRIPT 的. 历史 Bootstrap 是由 Twitter 的 ...

  5. 快速开发微信小程序

    image.png 最近婷主在做微信小程序.自己的微信公众号也需要添加点料,乘着这次放假,把微信小程序研究了下.虽然没有做什么很强大的功能,不过好歹自己的公众号也有了微信小程序.够用即可. 1.需要先 ...

  6. AAuto 快速开发win32小程序

    AAuto编程语言 AAuto是专用于桌面软件快速开发的新一代混合型编程语言 -  具有动态语言轻便.灵活.快速开发的特性,而且又可以同时支持静态类型开发,象静态语言那样使用.AAuto可以直接支持原 ...

  7. 基于flink快速开发实时TopN程序

    TopN 是统计报表和大屏非常常见的功能,主要用来实时计算排行榜.流式的TopN可以使业务方在内存中按照某个统计指标(如出现次数)计算排名并快速出发出更新后的排行榜. 我们以统计词频为例展示一下如何快 ...

  8. 如何进行Flink项目构建,快速开发Flink应用程序?

    项目模板 Flink应用项目可以使用Maven或SBT来构建项目,Flink针对这些构建工具提供了相应项目模板. Maven模板命令如下,我们只需要根据提示输入应用项目的groupId.artifac ...

  9. 本地json文件的编辑器,node-webkit开发的exe程序

    首发:个人博客,更新&纠错&回复 在昨天的dota契合度计算器中,用到了dota英雄数据和dota玩家数据这两个数据库,为了便于网页应用使用,这两个数据库的存储格式是json,即her ...

随机推荐

  1. RPA应用场景-考勤审批

    场景概述 考勤审批 所涉系统名称 考勤系统,微信 人工操作(时间/次) 5分钟 所涉人工数量 43 操作频率 不定时 场景流程 1.客户领导长期出差,又不想对考勤系统做深度开发: 2.员工请假后,领导 ...

  2. 《Stepwise Metric Promotion for Unsupervised Video Person Re-identification》 ICCV 2017

    Motivation: 这是ICCV 17年做无监督视频ReID的一篇文章.这篇文章简单来说基于两个Motivation. 在不同地方或者同一地方间隔较长时间得到的tracklet往往包含的人物是不同 ...

  3. 使用Runnable和Callable接口实现多线程的区别

    使用Runnable和Callable接口实现多线程的区别 先看两种实现方式的步骤: 1.实现Runnable接口 public class ThreadDemo{ public static voi ...

  4. nexus org.sonatype.nexus.bootstrap.jetty.JettyServer - Start failed

    INFO [jetty-main-1] *SYSTEM org.sonatype.nexus.bootstrap.jetty.JettyServer - Runningjvm 1 | 2020-04- ...

  5. 10分钟带你进入Swagger的世界,快来看一看吧

    什么是Swagger? 如下引用swagger官方的解释 Swagger is a powerful yet easy-to-use suite of API developer tools for ...

  6. Blazor快速实现扫雷(MineSweeper)

    如何快速的实现一个扫雷呢,最好的办法不是从头写,而是移植一个已经写好的! Blazor出来时间也不短了,作为一个.net开发者就用它来作吧.Blazor给我的感觉像是Angular和React的结合体 ...

  7. 「APIO2010」巡逻 题解

    来源 LCA 个人评价:lca求路径,让我发现了自己不会算树的直径(但是本人似乎没有用lca求) 1 题面 「APIO2010」巡逻 大意:有一个有n个节点的树,每条边权为1,一每天要从1号点开始,遍 ...

  8. 0202年,您真的需要Thrift这样一个RPC微服务框架来拯救一下传统HTTP接口(api)了

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_104 目前市面上类似Django的drf框架基于json的http接口解决方案大行其道,人们也热衷于在接口不多.系统与系统交互较少 ...

  9. Vue 自定义事件 && 组件通信

    1 App.vue 2 <template> 3 <!-- 4 组件的自定义事件: 5 1.一种组件间通信的方式,使用于:子组件===>父组件 6 2.使用场景:A是父组件,B ...

  10. Vue 引出声明周期 && 组件的基本使用

    1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="UTF-8" /> 5 & ...