在《屠龙少年终成恶龙,前端转产品的我给前端挖了个坑》这篇文章里,有讲到我是如何把我们的前端带坑里去。同时评论区有一条评论 你那个demo.exe是用什么实现的?可以直接套壳现有的系统? 看起来好像是在坑外徘徊不定若有所思的样子。因此我打算写本文把他踹坑里去,能踹一个是一个。
不想用 electron 和 tauri ?那我们一起来写个像 electron 的垃圾玩意吧~ 我们的目标是:前端程序员无需会三方语言就可独立完成桌面程序,创建托盘程序和服务、读写文件、处理进程、剪贴板这些都没有问题,预计整体体积不超过1M。
我当前已经使用 nodejs 开发一个命令行程序,这个程序的工具方式是,从网络上获取动态的配置,然后读取这个配置进行启动。启动后就能去做其他额外的事情了,而不需要管这个程序。因为这个程序只是一个辅助工具。
但是目前有一些痛点:
每次启动的时候我都得先找到项目目录,然后运行 node xxx.js,然后启动一个黑框框,然后我再最小化这个框。启动步骤相当麻烦并且有一个不用管的窗口在任务栏,相当碍眼。
有很多方法可以处理启动问题,比如 pm2/快捷链接/全局安装/制作 PKG 安装包等。但各自有各自的问题,这里不一一列举。
对于有一个黑框需要最小化到任务栏问题,我尝试过使用 node child_process 的 detached=false, windowsHide=true 等参数配合 pm2 都是没有用的,黑框还是会弹出。 假设有用,我要的也不仅如此。
我觉得这个工具不错,我想要把这个工具发给别人使用,虽然这个工具是 nodejs 写的,但我不希望别人还要去学习安装 nodejs 环境。虽然这个工具是命令行启动,并支持参数配置,但我希望像常规程序一样,别人点击一个图标就能启动,可以从界面上配置参数。可以在界面上看到程序的实时日志,最小化之后,变成一个小图标在任务栏,不占空间不碍眼。
那么问题来了,因为我经常用 html/css/js 画界面,对很多前端组件库比较熟悉,所以我打算用前端写界面。但 js 是跑在浏览器里的,读取不了保存在电脑里的配置文件,更实现不了托盘图标功能,也运行不了 node 程序。
据我所知,像这种想使用前端语言开发界面,又需要与操作系统进行交互的功能,有不少方案。下面是我对他们的调研结果:
名称 | 前端 | 后端 | 体积 MB | 内存 MB | 放弃原因 | 备注 |
nodegui | chromium | nodejs | 100 | 100 | 体积大 | |
miniblink49 | Chromium | nodejs | ? | ? | 体积大 | 仅支持 window |
NW.js | Chromium | nodejs | 100 | 100 | 体积大 | |
electron | Chromium | nodejs | 100 | 100 | 体积大 | |
Wails | webview | go | 8M | ? | 需其他语言 | |
Tauri | webview | rust | 1 | ? | 需其他语言 | |
Qt | 可选 | C++ | 30 | ? | 需其他语言 | |
wpf | 可选 | C# | ? | ? | 需其他语言 | 仅支持 window |
Muon | Chromium | go | 42 | 26 | 需其他语言 | |
Sciter | Sciter | QuickJS | 5 | ? | 与普通浏览器和 nodejs 可能有差异 | |
gluon | 浏览器 | nodejs | 1 | 80 | 生态小,例如没有找到托盘图标实现方式 | |
neutralino | 浏览器 | API | 2M | 60 | api 不多 |
当前大家比较火有 electron 和 tauri。四年前我使用过 electron 做过一个桌面划词程序,由于涉及到系统操作,所以需要安装 node-gyp/pytohn/visual studio 等依赖来进行本地编译,能否操作成功与 electron/node/node-ffi 等版本兼容性有很大的关系,安装过程和 electron 的体积都给我留下了不好的印象,另外 electron 里的主进程、渲染进程、通信的一些使用上的差异,也让我觉得不那么便利,所以我放弃 electron 。
接下来就是 tauri,它由于不打包 nodejs 和 chromium ,所以体积较小。但我看他官网上的 demo,就连启动都 rust 代码。
虽然代码没几行,但我也是相当拒绝:说好的只使用前端语言就能写桌面程序呢?
所以我放弃了 tauri 。原因是我真想找一个不使用三方语言就能做桌面程序的工具。我发现 neutralino 比较贴近我的需求,但它当前还很年轻,很多 api 和示例都没有。这相当于如果遇到了操作系统层面上的问题,只要他不提供 api 我就没法操作,因为我不会写原生代码,所以又放弃了 neutralino 。
所以就自己做一个吧。
准备使用当前了解的一个语言做一个基于 webview 的工具,我们暂且叫 main。它加载好前端页面,并向前端页面注入 api 并连接上 websockets 。如果前端有什么对系统操作的诉求,告诉 main 即可,由 main 完成,对于前端而言,就像调用一个普通的 js 方法一样,传参、处理结果、完事。
语言名为 aardio ,由于“各种原因”这里不做过多叙述。后面文档中统一称其为语言。
那么为什么都去搞一个语言了不搞 rust 这些?有几点考虑:
程序的整体架构是这样的:
下面这个图片演示了启动程序时,有一个绿色的进度条,然后进入界面。
目前已过可行性验证阶段,给客户做了一个文件管理系统程序,类似一个网盘,页面由前端完成,然后文件的下载、预览、同步这些交给 main 提供的 api。
下面这个图片演示了在 web 中关闭程序。
对于自己的话,做了一个 ai 助手,对接的开源 ai-ui,已发给同事使用,也没有问题。做了一个文章开关提到的助手程序,自己使用。
再次演示一下透明窗口,上面的启动时的进度条也是使用透明窗口完成的。
演示自定义窗口标题和托盘。
程序启动时的进度条也是使用 html 实现的
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>loading...</title>
<style>
body,
html {
height: 100%;
overflow: hidden;
}
body {
display: flex;
align-items: center;
justify-content: center;
}
@property --progress {
syntax: '<percentage>';
inherits: false;
initial-value: 0%;
}
.g-progress {
margin: auto;
width: 240px;
height: 10px;
border-radius: 25px;
background: linear-gradient(90deg, #0f0, #0ff var(--progress), transparent 0);
border: 1px solid #eee;
transition: .3s --progress;
}
</style>
</head>
<body>
<div class="box">
<div id="progress" class="g-progress progress-bar-striped" style="--progress: 10%"></div>
</div>
</body>
</html>
官方示例中给到的 webview 交互示例通过 external 注入到页面的 window 上,通过此方法能让 js 中的数据和 main 进行类型转换(比如 js 里传一个 number,那么到 main 里也是 number),还提供了一些可以直接启用 main 里对象方法的操作。好用是好用,但是与 nodejs 交互的时候,没有这种自动转换的功能,而且示例中的 node 服务连接很慢。
为了让 main 支持 webview 和 nodejs,并且使用方法统一,并且加快启动速度,查了一些资料,发生像这种跨语言通信通常都是使用 rpc 协议完成的,有 json-rpc/http-rpc/rpc-ws 等,为了实时性更强,我选择了 websockets 这种方式, 我 npm 社区中发现有 www.npmjs.com/package/rpc… 这个包可用,还兼容 node 和浏览器,尝试过后选择了它,这解决了跨语言通信问题。
另一个问题是,mian 中有很多方法是现成的。比如以下代码在 main 中可以使用:
// 有一个 winform 对象
winform.hitMax() // 最大化
winform.show() // 显示窗口
winform.hwnd // 获取窗口句柄
winform.hitCaption() // 拖动窗口
winform.text="title" // 设置窗口标题
// ... 上百个现在的方法和属性
如果我们要为 js 提供 api, 我们是每个属性和方法都得去写吗?这又麻烦,代码又还臃肿。
经过一波挣扎,我想起了使用代理这种方式去实现,还是 js。
const obj=new Proxy(
{},
{
get: function (target, key, receiver) {
console.log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console.log(`setting ${key}!`);
return Reflect.set(target, key, value, receiver);
},
}
);
根据 proxy,我们可以实现拦截到某个对象的方法调用和属性访问、设置等。再加上深层代理的话,像 winform.process.close() 这种有任何层方法属性都没有问题。
同时,在 main 中我们有这样的代码,来处理 proxy 拦截到的每个 key path:
我们把拦截到的 path ,比如在 js 里写 winform.process.close(true) 的时候,我们把拦截到的 winform.process.close 和参数 true 通过 rpc-ws 提供的 call 方法传给 main,这时候 main 根据 path 去动态调用函数并把参数传进去。我们把执行结果又丢给 call 方法返回给我们的 js 即可。
那么问题又来了,既然都实现了在 js 里调用方法和访问属性都像在写 main 中的代码一样,那真的就能不能以 js 的形式去写 main 的代码呢?看了一天的教程,发现这水很深啊,约等于创造一门语言,怕了怕了,逃。
但是思路着要有吧?好的:
如果简单一些呢,我们依然可以使用 proxy,实现操作符的拦截,从而实现一些简单的加减乘除的操作。然这没什么用啊,我们要实现的是比如用 js 里对 winform 对象进行遍历之前,我们就要做一个生成器之类的东西,在生成器的每一步里,去获取 main 里的遍历结果。感觉上好像能实现,实际我也不知道我在说什么。但是就算实现了,像这种遍历器,频繁的语言交互应该会消耗大量时间,感觉应该得不偿失。
所以在 js 里获得 main 中语言的编写体验,就不实现啦。如果我们真的要在 js 里写另一种语言,我们开放一个类似 js 的 eval 的功能。它可以向 main 传原生代码和参数。
// 创建目录
const dir=`C:/my/`
await ws.call(`run`, [
`
fsys.createDir(arg)
`, dir])
例如上面这段代码,直接传送目录参数 C:/my/ 到 arg,使用原生语言 fsys.createDir(arg) 去执行。
计划一:使用 main 去做更多的桌面 app,以此促进 main 的完善。
计划二:为某个当前成熟的 ui 框架制定一套 css 皮肤,例如 win7 皮肤 ,例如 element-ui 样子很 web,但应用了这个皮肤之后,整体页面风格和控件都看起来就像原生 win7 桌面程序一样。
计划三:尽快完成 api 的封装和文档,让前端朋友只调用指定的 js api 即可完成托盘、进程、剪贴板、IO等系统操作。我们封装的 api 尽量向 neutralino 靠近,做到最小成本的迁移。等它成熟后,可以迁入,没成熟之前我们也能自己用着。
可以帮我们封装 api,这需要你了解 main 的语言;可以用 main 来做些小工具尝试一下,这就是最好的帮助;可以做操作系统风格皮肤,等你做好了,electron 和 tauri 他们都能用,因为他只是 css;或者可以点个 star https://github.com/wll8/sys-shim
好了,饼画了,牛吹了,坑挖了,我要去玩了。
作者:程序媛李李李李李蕾
链接:https://juejin.cn/post/7304538151480803366
了解一样东西,我认为最重要的莫过于了解Ta的过去,Ta的现状,以及Ta的未来.学习CSS也不外乎如此.
通过网络学习一样新东西,最大的困惑就是----网上的文章,写得非常好.但就是碎片化,不系统.而且还有个情况就是:有些问题,大神们觉得很简单,不屑一提或者毫无意识的内容或知识,对于那些刚入门的人因为不知道,也没人点播,有可能会在这个问题上迷茫很久.这一点我体会颇深.
经验分享:入门阶段不要管好那么多细节,记忆之类的东西,尽快地入门才是最重要的.其实入门,无非就是对所学内容形成一套概念,知其然,所以然.大概的就成了.
一开始不要在W3C网站上学习,因为内容太多,虽然很系统,但是我一点都不相信你会有耐心全部看下来!
一开始学习,首先找些小的实战项目看看,小编上方推荐的裙里就有免费的网课做小项目的课堂,里面讲的非常细,一开始可能有一些不是很懂,但是你只要坚持听完一节网课,学习到老师们的代码风格和做项目的思路,对你的帮助是非常大的。
我觉得最好的方式是实践出来的。如果你没有一个网站或者一个服务器等等之类的。你可以用Github去做一个自己的博客,虽然是静态的但是也是够的。
可以按下面的步骤来学习,一个网站正常情况下由三部分组成:
1. Head 顶部导航
2. Content 内容
3. Footer 顶部导航
对于Head来说有不同的玩法,而首先你需要学会的是ul li a 这三个基础的HTML,而对于大部分前端程序员来说。要手动写一个水平导航可以说是一个入门的标志,还有竖直的导航,浮动的导航等等。
接着你还会遇到的问题是如何处理底部的footer,让他一直在最下面的。而不会乱跑,
大概需要的过程
1. 学会分析一个网页的结构
2. 理解每部分的原理
3. 手动写CSS
小编就分享到这里了,关注小编,可以看小编的更多优质文章。
取一个网页,我们需要了解一下网页的结构,如果想要深入学习,建议看一下《网页设计与制作》,这本书讲述较为详细,推荐阅读。
网页的三大组成部分——HTML、CSS、JavaScript,如果把网页比作一个人的话,HTML相当于骨架,JavaScript相当于肌肉,而CSS相当于皮肤,三者结合起来形成一个完整的网页。
网页基本结构
HTML(Hyter Markup Language 超文本标记语言),主要是通过HTML标记对网页中的文本、图片、声音等内容进行描述。HTML提供了很多种标记,如段落标记(p标签)、图片标记(img标签)、视频标记(video标签)等,网页中需要定义什么内容,就可以用相应的标记描述。
首页源代码
从图可见,网页内容是通过HTML标记(图中带有“<>”的符号)描述的,整个网页由各种标签嵌套而成。
HTML其实是一个纯文本文件,只是网页的一个骨架,只有HTML的网页其实并不美观,为了让网页看起来更好看,我们需要借助CSS。
CSS(Cascading Style Sheets 层叠样式表),“层叠”指的当HTML中引用了数个样式文件,并且样式发生冲突时,浏览器能依据层叠顺序处理,“样式”指的是网页文字、图片等的大小、颜色、排列等格式。
body {
font: 12px/1.14 SF Pro Display,PingFang SC,Hiragino Sans GB,Microsoft YaHei,WenQuanYi Micro Hei,Helvetica Neue,Arial,sans-serif;
-webkit-font-smoothing: antialiased;
color: #333;
outline: 0;
}
font、color、outline即样式设置
CSS的位置很灵活,即可以嵌入在HTML文档中,也可以时一个单独的外部文件,如果是独立的文件,则必须以.css为扩展名,使用link标签引入文件。上图CSS显示为内嵌方式,一般集中放在HTML文档头部(<head>标签内)。
JavaScript,简称JS,是一种脚本语言,可使网页具有交互性(HTML和CSS制作的网页是静态网页),js脚本语言使得用户与信息是一种实时、动态、交互的页面功能,如页面效果切换、动画效果、页面游戏等,它还可以控制cookies,包括创建和修改等。
JavaScript通常也是以单独文件形式加载,后缀为js,在HTML中通过script标签引入,如<scrip src="jquery-2.1.0.js" type="text/javascript"></script>。
我们可以把互联网看作一张大网,而爬虫(即网络爬虫)就是在网上爬行的蜘蛛,把网的节点比作一个个网页,爬虫爬到一个节点就相当于访问了该页面,获取了其信息,节点之间的线就像与网页与网页之间的链接关系,蜘蛛可顺着节点连接继续爬行到下一个节点,即通过一个网页爬取另一个网页,这样整个网的节点都可以被爬取到了。
那么爬虫的基本过程可以简单概述为:获取网页——提取信息——保存数据。
静态网页:用HTML代码编写的页面,每个网页都有一个固定的URL,加载速度快,编写简单,但可维护性差、交互性差,不能根据URL灵活多变地显示内容。
动态网页:以数据库技术为基础,可以大大降低网站维护的工作量,它可以动态解析URL参数的变化,关联数据库并动态呈现不同的页面内容,可以实现用户登录与注册功能。
*请认真填写需求信息,我们会在24小时内与您取得联系。