Qt for WebAssembly,是Qt在2018年发布的技术,于5.12加入到Qt,官方对此技术介绍如下:
https://www.qt.io/blog/2018/05/22/qt-for-webassembly
简单对说,这是一个让Qt程序可以直接跑在web中对一个方法,具体流程如下:
使用Emscripten作为platfrom静态编译Qt工程,把整个工程和Qt环境打包编译成wasm可执行文件,配合html套壳一起加载到浏览器中,然后浏览器会提供一个虚拟化环境运行wasm,程序运行起来后所有的图形结果通过一个canvas输出。
相比之前WebGL技术这样的远程运行技术,这一次WebAssembly是真的把Qt程序跑在了浏览器本地上,实现了性能,效果的保证。总体靠谱得多。
关于WebAssembly详细描述和资料,这里不再累述,请直接参考Qt官方文档:
如果你想真的通过WebAssembly开发程序,我也建议务必看完以下链接里所有资料,这会帮你节约很多时间
https://wiki.qt.io/Qt_for_WebAssembly
https://www.qt.io/cn/blog/2019/01/18/getting-started-qt-webassembly
https://www.youtube.com/watch?v=W3WC-VpKdGQ&t=1319s
作为一个面世2年的技术,这项技术已经被多位前辈介绍,但是大多都是草草带过没有涉及很多细节,因此本文会相对整体介绍,看完本文后可以构建一个比较满意的工程。
系统:Ubuntu 18.04 64bit
注:我是在虚拟机里面安装的是原版系统,并且进行了常规更新(update/upgrade),我建议为了避免奇怪的环境问题,尽量使用虚拟机安装环境,虽然Qt支持Windows和macOS下使用WebAssembly,但是我不建议在这两个系统下操作。
注2:请不要过多担心系统问题,因为编译后的产物例如wasm、html,都是跨平台的,无所谓是通过哪个系统编译出来的。
Qt源码:qt-everywhere-src-5.14.2.tar.xz
源码下载地址:http://download.qt.io/archive/qt/5.14/5.14.2/single/
sudo apt-get install vim git
sudo apt-get install gcc g++ make libgl1-mesa-dev libglu1-mesa-dev libssl1.0-dev
sudo apt-get install libssl-dev
sudo apt-get install libfontconfig1-dev libfreetype6-dev libx11-dev libxext-dev libxfixes-dev libxi-dev libxrender-dev libxcb1-dev libx11-xcb-dev libxcb-glx0-dev libxkbcommon-x11-dev libxcb-keysyms1-dev libxcb-image0-dev libxcb-shm0-dev libxcb-icccm4-dev libxcb-sync0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-randr0-dev libxcb-render-util0-dev
sudo apt-get install python
sudo apt-get install openjdk-8-jdk
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
git pull
./emsdk install sdk-fastcomp-1.38.30-64bit
./emsdk activate sdk-fastcomp-1.38.30-64bit
source ./emsdk_env.sh
em++ --version
注:Emscripten和Qt是有版本对应关系,版本不匹配可能导致编译失败以及运行时奇怪问题,具体对应关系请参考本文开头发的参考链接。我使用的Qt5.14.2
配套Emscripten1.38.27
或Emscripten1.38.30
注2:source ./emsdk_env.sh是每次打开终端后必须执行的,他会初始化Emscripten环境
xz -d ./qt-everywhere-src-5.14.2.tar.xz
tar -xvf ./qt-everywhere-src-5.14.2.tar
cd qt-everywhere-src-5.14.2
./configure -prefix ~/Develop/qt5.14.2_web -confirm-license -opensource -xplatform wasm-emscripten -nomake tools -nomake examples -nomake tests -skip qt3d -skip qtcharts -skip qtdatavis3d -skip qtdoc -skip qtgamepad -skip qtlocation -skip qtlottie -skip qtmacextras -skip qtmultimedia -skip qtnetworkauth -skip qtpurchasing -skip qtquick3d -skip qtremoteobjects -skip qtscxml -skip qtsensors -skip qtserialbus -skip qtserialport -skip qtspeech -skip qtsvg -skip qttools -skip qtvirtualkeyboard -skip qtwayland -skip qtwebchannel -skip qtwebengine -skip qtwebglplugin -skip qtwebview -skip qtwinextras -no-feature-accessibility -no-feature-appstore-compliant -no-feature-big_codecs -no-feature-calendarwidget -no-feature-colordialog -no-feature-d3d12 -no-feature-filesystemwatcher -no-feature-fontcombobox -no-feature-fontdialog -no-feature-ftp -no-feature-imageformat_xbm -no-feature-lcdnumber -no-feature-library -no-feature-multiprocess -no-feature-pdf -no-feature-printdialog -no-feature-printpreviewdialog -no-feature-printpreviewwidget -no-feature-qml-debug -no-feature-qml-devtools -no-feature-qml-worker-script -no-feature-qml-xml-http-request -no-feature-quick-designer -no-feature-sha3-fast -no-feature-sharedmemory -no-feature-socks5 -no-feature-udpsocket -no-feature-whatsthis -no-feature-wizard -no-feature-xml-schema -no-feature-xmlstreamwriter
make -j6
make install
注:我的配置参数特别长,因为我对Qt进行了裁剪,这个裁剪能缩减大约1mb的wasm文件体积,如果你觉得没必要可以去掉-no-feature
开头的那批参数
注2:相比编译桌面版Qt,WebAssembly版的Qt程序HTTPS请求依托浏览器环境,因此这里不需要配置ssl相关参数。也就是说编译时不需要配置ssl环境就可以用HTTPS
若一切成功,依次执行qmake -v
和em++ -v
应该可以看到以下信息
和编译普通Qt工程一样,切换到源码目录,直接执行这2个命令就行了
qmake
make -j3
但是因为WebAssembly和静态编译原因,编译会特别慢,一个简单的工程会需要编译几分钟,这里耐心等待就行了
编译好后,我们一共需要从工程目录中提取5个文件,如下:
注:我的工程名为test
qtloader.js
qtlogo.svg
test.html
test.js
test.wasm
其中test.js
和test.wasm
是对应Qt工程的主要产物,每次Qt工程重新编译后都要更新这2个文件,这两个文件也不建议进行二次修改,直接用就行了。
如果要对页面进行美化,直接修改test.html
就行了
因为不支持直接从文件打开,所以如果要对页面进行访问,最简单对方法就是用python开启一个web服务器
python3 -m http.server
然后在浏览器(例如Ubuntu自带对火狐)中直接打开http://127.0.0.1:8000/test.html
就能看到程序了
我这里有一个已经挂在网上的WebAssembly程序,可以直接打开看下效果
http://web.jasonserver.com:50003/test/test.html
截止上一步,一个简单的web程序就构建完成了,但是我们还有几步完善过程要走
目前Qt for WebAssembly这边有一个缺陷就是无法使用浏览器或者说客户宿主机的字体,简单的数字和英文Qt有处理可以显示但是中文全部是小方块。根据静态编译的思路我们也不太会把整个中文字体都打包到资源文件里,毕竟中文字体基本都10mb+,就算打包进去加载速度也会被大幅度拖慢。因此我们需要对字体进行裁剪,我使用对是一个在线裁剪工具,配合常用2000字汉字表。
https://font-subset.disidu.com
https://wenku.baidu.com/view/3d2bd02b453610661ed9f40a.html
注:如果百度不让你复制,一个最简单对破解方法就是打开页面后禁用页面JS,然后再复制,就行了
我裁剪了SourceHanSansCN-Medium
字体,裁剪前10mb+,裁剪后才300多kb,打包进资源文件完全可以接受
然后如果要在Qt里支持这个字体,在main函数里增加一行代码就行了
QFontDatabase::addApplicationFont( ":/SourceHanSansCN-Medium.subset.otf" );
为什么要压缩,因为wasm文件真的有点大。我这里一个简单工程编译出来就有18.2mb,这个对于一个web而言有点大了,在实际网络环境中可能需要10秒左右才能加载完成。
至于如何压缩,这个东西就有点坑了,最初我不熟悉web对压缩体系看了半天没看懂,只看官方说可以压缩却没说怎么压缩,最后才知道这个是WebServer自带功能,不需要开发者手动压缩
比如说我们可以使用nginx部署web,在nginx配置文件中配置开启glib压缩,我的配置如下:
gzip on;
gzip_min_length 32k;
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 3;
gzip_types text/plain text/css text/xml application/xml application/json text/javascript application/javascript application/octet-stream;
然后这个压缩就全自动的完成了。
大致流程就是浏览器在请求资源文件的时候会说明自己支持的压缩方式,然后服务器如果可以匹配到这个压缩方式就会压缩好数据再返回给浏览器。
可以看到开启glib压缩后,18.2mb下降到了7.3mb,还是比较可观的,缩减了几倍的网络传输消耗
如果追求更高压缩率可以使用brotli,本文不再介绍。注意这个压缩方法不是每个浏览器都支持,比如说Safari就不支持
ico这个东西很多地方都用得到,web这里需要一个favicon.ico建议还是准备下
ico建议弄多分辨率版本,推荐工具gfie
我在html中配置ico方式为:
现在Web已经可以做的和应用程序一样,在桌面有一个小图标,进入后只有本身的页面没有浏览器框架,这依托于PWA标准
具体参考如下:
https://developer.mozilla.org/zh-CN/docs/Web/Manifest
简单的说这里只要一个manifest.json
配置文件,我的配置如下:
{
"name": "Test",
"short_name": "Test",
"start_url": "test.html",
"display": "standalone",
"background_color": "#000e27",
"theme_color": "#000e27",
"description": "Qt for WebAssembly Test",
"icons": [{
"src": "favicon.ico",
"sizes": "256x256 64x64 48x48 32x32 24x24 16x16",
"type": "image/x-icon"
}],
"related_applications": [{
"platform": "web"
}]
}
虽然我们有了PWA,但是iOS这边还是有一些私有标准需要单独写html兼容,我的配置如下:
目前来说Qt for WebAssembly还是有一些问题,这作为一个成长中的框架和平台我们是接受的,在我测试中我注意到几个主要问题,还是有必要单独说下,这些问题理论上在将来都会慢慢解决,希望能尽快了。
目前多线程支持是几乎没有的。为什么说几乎没有是因为在一些组合下还是可以用多线程,但是限制特别多,例如要挑浏览器,以及对参数有特别要求。这对于web这样一个高兼容性的设计多少有些背道而驰。
我尝试了几个安卓手机,自带对浏览器以及一些第三方浏览器都不支持WebAssembly技术,因此根本无法打开页面。但是微信里面自带浏览器却可以打开。总的来说在安卓上兼容性比较差。
但是iOS没有这个问题,基本上所有浏览器都支持。
在桌面浏览器平台High-DPI缩放没有问题,但是在iOS上和部分安卓上缩放是异常的,具体的bug我已经反馈给Qt,可以通过Qt的bug系统来追踪这个问题修复情况。这个问题会导致在iOS下消耗大量无意义的资源进而导致卡顿。老设备上甚至无法正常加载页面。
https://bugreports.qt.io/browse/QTBUG-85662
注:示例工程也一起传到这里了,需要的可以直接去下载
我在Bug系统里也找到了这个反馈,截止发文还是没修复。因此目前程序中无法使用@2x和@3x的图片
https://bugreports.qt.io/browse/QTBUG-79378
若要部署到https到web server上面,可能会报错
Application exit (TypeError: undefined is not an object (evaluating'handle[name]'))
我不清楚这是Qt到bug还是什么原因,但是我看Qt的在线示例也有这个问题。要避免这个问题需要修改js文件,找到projectname.js里找到这一行
functionBody+=" var rv = handle[name]("argsList");\n";
改成
functionBody+=" if(handle){var rv = handle[name]("+argsList+");}\n";
这个问题也提交Qt了,可以到这里追踪问题修复
https://bugreports.qt.io/browse/QTBUG-85736