Jasper Ji

平常心

无法启动问题

安装的教程主要参考官方文档,主要记录下安装遇到的问题,首先因为域名没有下来,使用的是IP,另外端口也是自定义的。

sudo docker run --detach \
  --publish 47.xxx.xxx.xx:8929:80 \
  --name gitlab \
  --restart always \
  --volume $GITLAB_HOME/config:/etc/gitlab \
  --volume $GITLAB_HOME/logs:/var/log/gitlab \
  --volume $GITLAB_HOME/data:/var/opt/gitlab \
  --shm-size 256m \
  gitlab/gitlab-ce:15.1.2-ce.0

这个命令会提示bind: cannot assign requested address,所以就只改端口。

sudo docker run --detach \
  --publish 8929:80 \
  --name gitlab \
  --restart always \
  --volume $GITLAB_HOME/config:/etc/gitlab \
  --volume $GITLAB_HOME/logs:/var/log/gitlab \
  --volume $GITLAB_HOME/data:/var/opt/gitlab \
  --shm-size 256m \
  gitlab/gitlab-ce:15.1.2-ce.0

IP在gitlab.rb配置中修改

external_url 'http://47.xxx.xxx.xx' # 注意不需要加端口

修改完毕后,我就直接执行Docker命令重启了,访问网页直接显示502的界面,然后CPU暴涨,直接卡死,只能控制台重启机器。我以为是机器配置的问题,毕竟是2核4G的,于是我又换了一个低一点的版本13,试了下,发现依旧卡死。最后参考这篇文章docker 搭建gitlab后,出现502的处理方案之一

docker exec gitlab gitlab-ctl reconfigure

Dcoker启动后先不要访问网页,等一会CPU降下去后再执行这个命令。

初始化密码问题

之前安装的是13,密码在第一次访问时会显示重置密码的网页,但是安装的15,发现没有这个重置密码的,只有一个登录,原来从14开始初始密码放置在/etc/gitlab/initial_root_password,找到后直接登录后再重置。

安装Gitlab也算有好多次了,之前主要是原生的安装并没有用过Docker,另外吐槽下这玩意,动不动就CPU爆满,真的不是很友好。

参考

Linux初装gitlab初始默认密码

缘起

早些时候有使用React Native开发了第一版的App,后来又用Flutter重新开发了。Flutter的开发和用户体验确实比RN要好,我们的App其实一直没有使用,用的是小程序,功能的频繁变更,如果是App的话,可能用户手机上依旧安装的是老的版本,当然也可以通过版本控制来让用强制更新,从而可以避免老版本的问题。另外就是热更新,RN的最大优势就是热更新吧,如果不考虑热更新我肯定还是Flutter优先,RN的热更新主要以微软的CodePush,刚出来的时候免费的,目前已经变成AppCenter,需要付费了。另外React Native 中文网的Pushy,不过没有用过。后来偶然发现code-push-server这个开源的项目,最近才想到应该试下这个方案。

CodePush Server

这个项目的初衷是因为微软的CodePush在国内太慢了,另外官方的CodePush Server是没有开源的,只有React Native CodePush是开源的。理论上是可以通过React Native CodePush 反推出CodePush Server逻辑的,但是不知道CodePush Server的这个项目是怎么来的,不过能用就好。

不过这个项目在3年前就已经停止更新了,另外React Native CodePush则一直在更新,所以实际会有不少的问题。我用的是Docker搭建的CodePush Server服务,这个很简陋没有什么管理的界面,都是用React Native CodePush命令行来管理的。

依赖

package.json中的依赖:"react-native-code-push": "~5.6.0",一定要使用小版本,因为5.7以上的版本就会报错。

屏蔽自动生成

react-native-code-push插件会自动在/android/app/build/generated/rncli/src/main/java/com/facebook/react/PackageList.java文件中生成CodePush的类型,但是如果使用CodePush Server的话,就需要手动的更改CodePush的Server路径,而不是微软的,需要的项目下生成react-native-config.js,屏蔽自动生成。

module.exports = {
    dependencies: {
        'react-native-code-push': {
            platforms: {
                android: null, // disable Android platform, other platforms will still autolink
            },
        },
    },
};
Gradle 配置

需要变更app/build.gradle

apply from: "../../node_modules/react-native/react.gradle"
apply from: "../../node_modules/react-native-code-push/android/codepush.gradle" // 新增此段
常用命令

热更新是分平台,所以需要创建对应的安卓以及iOS的。

code-push release-react [应用名称]-android android -d Production # 打包生产版本

运行程序,注意热更新也是分为测试和生产版本的。

react-native run-android --variant=release  # 生成release版本,默认是Debug版本

总结

整个算是跑通了,从业务角度来看,热更新确实很棒。但实际情况就是如果不是大厂,那只能自己搭建CodePush Serser的服务了,不过考虑到这个开源的项目以及很久没人在维护了,所以实际上留下一个巨大坑,后续维护的难度还是有的。

同事的之前是用Mac本开发的,后来换成了小米的本,发现无法用真机和模拟器调试,执行adb devices 命令后直接卡死。网上搜索很多,多是说端口被占用的问题,但是试了好多次,发现端口没有被占用。当时也没有细想,后来冷静下来发现还是得从原理上来寻找问题的根源,于是一大早就把谷歌官网关于adb的文档重新看了下,原来只是使用,并没有深入的去理解原理。adb分为服务端和客户端,端口一般是5037,连接不上的问题实际上网络连接的问题。第一个想到的是防火墙,结果发现不是,后来觉得是不是代理软件的问题,于是就把代理软件关闭了,发现也没有解决问题。后来搜索发现了这篇文章如何解决adb卡死,命令不返回的问题,于是从微软官网下载了Process Explorer,发现果然是代理软件的问题,虽然之前虽然有关闭代理软件,因为adb连接的时候会默认加入代理,因为代理无法连接,所有adb的连接就一直不响应。

其实过去几年因为代理而产生的问题也是不少,但每次的还是会中招。另外发现现在的网络确实很厉害,很多问题只要一搜索大都能解决问题,而面对的这个问题的时候,我也陷入了这样的思维,不停的搜索,完全丧失了自己思考的能力。实际上当一个问题搜索很多遍后,依旧没有解,那么多半是一些只有在自己电脑上遇到的特殊问题。

Process Explorer,这个软件真的非常棒,主要还是免费提供的,直接从微软官网下载,我第一次从其他地方下载,没有发现问题而且显示患有问题,应该是一个老版本,后来从官网下载的就没有问题。因为一直使用Mac对于Win的使用经验还是停留在很多年前,发现要深度玩转Win。Sysinternals 套件真是宝藏库。

《苹果上的缺口:我与史蒂夫·乔布斯的生活回忆录》(The Bite in the Apple: A Memoir of My Life with Steve Jobs),很早之前听说乔布斯第一任,也是乔布斯第一个孩子的母亲,女友克里斯安·布伦南(ChrisannBrennan)写了一本《The Bite in the Apple》的书,我英语太菜,后来发现居然被翻译成中文版了,也是第一时间就购买了,与乔布斯有关的书很多,甚至连乔布斯经常光顾的餐厅的厨师都出书了,此书有点被忽视,人们大都了解成功后的乔布斯,虽然《乔布斯传》也曾描写创建苹果之前的乔布斯的一些事,不过还是有点不够细致,而此书刚好是一种补充。

18年的时候为了开发出海的应用,专门购买了一台二手的LG Nexus 5X,图的就是有谷歌服务,最近又有用到开发,发现一些问题,都是众所周知的原因了,好在都一一解决了,提前是要先安装adb,通过adb来设置。

连接WIFI成功后,提示无法访问互联网

实际上是可以访问网络,当然谷歌是访问不了的,网上搜索了下,主要是替换验证的网址了,以下方法亲测可用。

adb shell settings put global captive_portal_detection_enabled 1
adb shell settings put global captive_portal_mode 1
adb shell settings put global captive_portal_use_https 0
adb shell settings put global captive_portal_server connect.rom.miui.com
adb shell settings put global captive_portal_http_url http://connect.rom.miui.com/generate_204
adb shell settings put global captive_portal_https_url https://connect.rom.miui.com/generate_204

无法使用互联网时间

同样的也是无法谷歌提供的时间服务, 我用的阿里的NTP服务。

adb shell settings put global ntp_server ntp.aliyun.com

参考

开发者必备手机nexus 5x 开发环境预备

Android 系统时间不对有遇到的吗?

记事本这个小应用完全是为了学习Rust而产生的应用,今年4月6号创建了这个项目,主要是用业余时间弄下,刚好五一假期,我也就加把劲,算是弄出一个基本功能的东西。本来想写那些遇到的问题,可一旦动笔却不知道如何写起来,问题已经都解决了,新的问题依旧在路上。于是直接把项目上传到Github了,yew-notepad,有兴趣的可以自己拉下来看看,或许有点用。

目前用Rust写前端的资料很少,Yew资料更少,遇到问题基本上是反复的看官方的文档以及例子为主,很多时候总想抛开这些去寻找,但最后发现还是得认真的看文档和例子。另外就是看书吧,程序类的书籍我一般喜欢看纸质书,之前买的《Rust权威指南》,结合项目中遇到的问题期间又翻了翻,蛮有收获的。昨天又去省图借了《Rust程序设计》(Programming Rust),英文书名跟前者很像,出版也早一些,所以《Rust权威指南》为什么不是《Rust 程序设计语言》,估计是害怕读者搞混乱吧。这两本书各有千秋吧,我是拿来互补的。《Programming Rust》貌似已经出了第二版,我估计会考虑买本。

在线书籍

Rust 程序设计语言,其实就是我买的那本《Rust权威指南》,我觉得在线版的译名更合适。

通过例子学 Rust 中文版

尝试用Rust已经有点时间了,之前主要的问题是卡在IndexedDB的使用上,最后的代码是这样。

spawn_local(async move {
        let (tx, rx) = oneshot::channel::<IdbDatabase>();
        let window = web_sys::window().unwrap();
        let idb_factory = window.indexed_db().unwrap().unwrap();

        let open_request = idb_factory
            .open_with_u32(String::from("todo").as_str(), 1)
            .unwrap();

        let on_upgradeneeded = Closure::once(move |event: &Event| {
            let target = event.target().expect("Event should have a target; qed");
            let req = target
                .dyn_ref::<IdbRequest>()
                .expect("Event target is IdbRequest; qed");

            let result = req
                .result()
                .expect("IndexedDB.onsuccess should have a valid result; qed");
            assert!(result.is_instance_of::<IdbDatabase>());
            let db = IdbDatabase::from(result);
            let store:IdbObjectStore = db.create_object_store(&String::from("user")).unwrap();
            let _index = store.create_index_with_str(&String::from("name"), &String::from("name")).expect("create_index_with_str error");

        });
        open_request.set_onupgradeneeded(Some(on_upgradeneeded.as_ref().unchecked_ref()));
        on_upgradeneeded.forget();

        let on_success = Closure::once(move |event: &Event| {
            // Extract database handle from the event
            let target = event.target().expect("Event should have a target; qed");
            let req = target
                .dyn_ref::<IdbRequest>()
                .expect("Event target is IdbRequest; qed");

            let result = req
                .result()
                .expect("IndexedDB.onsuccess should have a valid result; qed");
            assert!(result.is_instance_of::<IdbDatabase>());

            let db = IdbDatabase::from(result);
            let _ = tx.send(db);
        });
        open_request.set_onsuccess(Some(on_success.as_ref().unchecked_ref()));
        on_success.forget();

        let db = rx.await.unwrap();
        let transaction = db.transaction_with_str_and_mode(&String::from("user"), IdbTransactionMode::Readwrite).expect("transaction_with_str error");
        let store = transaction.object_store(&String::from("user")).expect("store error");

        let name = JsValue::from_str(_content_element.value().as_str());
        let add_request = store.add_with_key(&name, &JsValue::from("name")).expect("add error");

        let on_add_error = Closure::once(move |event: &Event| {
            console::log_1(&String::from("写入数据失败").into());
            console::log_1(&event.into());
        });
        add_request.set_onerror(Some(on_add_error.as_ref().unchecked_ref()));
        on_add_error.forget();

        let on_add_success = Closure::once(move |event: &Event| {
            console::log_1(&String::from("写入数据成功").into());
        });
        add_request.set_onsuccess(Some(on_add_success.as_ref().unchecked_ref()));
        on_add_success.forget();

        console::log_1(&String::from("do").into());
 });

因为IndexedDB连接成功是个异步事件,db 只能在成功事件中才能拿到,最初因为对IndexedDB的使用不熟悉,这玩意在很早以前看HTML5的时候就看到过,不过一直没有用过。我在成功事件中调用create_object_store,结果给报错了,实际上创建数据库只能在onupgradeneeded事件中处理。

let myDatabase = MyDatabase::new();
myDatabase.add(String::from("jasper", String::from("name")));

早期的想法是把数据库的操作封装起来,但是连接是异步的,上面的add操作时数据库可能还没有连接成功,db等于为空。可能是之前做iOS的缘故,我一直想着数据库的操作应该是个单例,不过Rust的单例好像不那么简单,一直报错,所以这个想法就先放放了。

早期的时候甚至不知道如何用Rust设置回调,最后发现了这个库kvdb_web,代码也是参考了indexed_db.rs中的方法。这个库并没有演示的例子,所以只是参考了他打开数据库的方法。里面有用到futures::channel,但是实际使用老是报错。后来我忘了Rust写前端,实际上是运行在Wasm虚拟机的环境下,Rust的多线程则是一般的操作系统的环境下了。实际上要在Wasm使用多线程,得用另外一种方式。实际上是用Rust的代码调用JS了,JS是单线程运行的。所以整个思路虽然是用Rust在写,但实际思想的话得跟着JS来走。我想在后面的代码中拿到db,有点类似JS中使用await的方式。最后用了两个库,wasm-bindgen-futures主要使用了spawn_local这个东西,另外还有futures-channel这个库。

spawn_local(async {
	let (tx, rx) = oneshot::channel::<i32>();
	// 省略中间代码
	let on_success = Closure::once(move |event: &Event| {
                // Extract database handle from the event
                let target = event.target().expect("Event should have a target; qed");
                let req = target
                    .dyn_ref::<IdbRequest>()
                    .expect("Event target is IdbRequest; qed");

                let result = req
                    .result()
                    .expect("IndexedDB.onsuccess should have a valid result; qed");
                assert!(result.is_instance_of::<IdbDatabase>());

                let db = IdbDatabase::from(result);
                let _ = tx.send(db);
        });
    open_request.set_onsuccess(Some(on_success.as_ref().unchecked_ref()));
    on_success.forget();
   
    // 等于这块回一直阻塞,直到拿个值。
    let db = rx.await.unwrap();
    // 省略剩余代码
})

futures-channel这个库的用法跟Rust自带那个有点类似,不过只用这个是可以工作的。后面的问题基本上是IndexedDB的用法问题,最后终于可以把数据成功插入了,虽然再次插入数据时会有写入失败的问题,不过这个已经是后面的事了。

当我费了很多周折,成功写入时,也就是写这篇文章时候,突然间发现可以直接用下面的方式写,如果只是要写入数据的话。

let window = web_sys::window().unwrap();
let idb_factory = window.indexed_db().unwrap().unwrap();

let open_request = idb_factory
    .open_with_u32(String::from("todo").as_str(), 1)
    .unwrap();

let on_upgradeneeded = Closure::once(move |event: &Event| {
    let target = event.target().expect("Event should have a target; qed");
    let req = target
        .dyn_ref::<IdbRequest>()
        .expect("Event target is IdbRequest; qed");

    let result = req
        .result()
        .expect("IndexedDB.onsuccess should have a valid result; qed");
    assert!(result.is_instance_of::<IdbDatabase>());
    let db = IdbDatabase::from(result);
    let store: IdbObjectStore = db.create_object_store(&String::from("user")).unwrap();
    let _index = store
        .create_index_with_str(&String::from("name"), &String::from("name"))
        .expect("create_index_with_str error");
});
open_request.set_onupgradeneeded(Some(on_upgradeneeded.as_ref().unchecked_ref()));
on_upgradeneeded.forget();

let on_success = Closure::once(move |event: &Event| {
    // Extract database handle from the event
    let target = event.target().expect("Event should have a target; qed");
    let req = target
        .dyn_ref::<IdbRequest>()
        .expect("Event target is IdbRequest; qed");

    let result = req
        .result()
        .expect("IndexedDB.onsuccess should have a valid result; qed");
    assert!(result.is_instance_of::<IdbDatabase>());

    let db = IdbDatabase::from(result);
    let transaction = db
        .transaction_with_str_and_mode(&String::from("user"), IdbTransactionMode::Readwrite)
        .expect("transaction_with_str error");
    let store = transaction
        .object_store(&String::from("user"))
        .expect("store error");

    let name = JsValue::from_str(_content_element.value().as_str());
    let add_request = store
        .add_with_key(&name, &JsValue::from("name"))
        .expect("add error");

    let on_add_error = Closure::once(move |event: &Event| {
        console::log_1(&String::from("写入数据失败").into());
        console::log_1(&event.into());
    });
    add_request.set_onerror(Some(on_add_error.as_ref().unchecked_ref()));
    on_add_error.forget();

    let on_add_success = Closure::once(move |event: &Event| {
        console::log_1(&String::from("写入数据成功").into());
    });
    add_request.set_onsuccess(Some(on_add_success.as_ref().unchecked_ref()));
    on_add_success.forget();
});
open_request.set_onsuccess(Some(on_success.as_ref().unchecked_ref()));
on_success.forget();

总结

至此算是把IndexedDB的问题解决了,如果想把操作封装起来,那么可以用前面的方式。只是简单的操作的话,后面的代码也是可以的。到目前为止,基本上还没有写网页的东西,Yew的东西,也就只是搭了个架子时有用到,并没有深入的使用。后面得需要好好的深入下,Yew这个框架跟传统的还不太一样,用trunk serve启动后,实际会有一个WebSocket在前后端之间通讯。

再谈谈Rust学习的东西,实际上《Rust权威指南》这本书我只是看了个大概,而目前的实验项目中并没有使用Rust去写很多逻辑的代码,更多的是调用。用Rust写前端这个方式,起步会比一般的难点,主要还是牵涉的东西不只是Rust的问题,还有Wasm以及JS相关的东西,最重要的还是要明白他的运行环境是浏览器中Wasm虚拟机。不过后续如果有大量成熟的库,可以解决这些基础问题的话,只写网页的话,难度会小些。

虽然项目整体进展不大,也走了一些弯路,不过却也让我思考到了更多的东西。现在感觉Rust是未来全栈程序员必备的语言,这门语言是非常考验你是不是一个合格的程序员,因为他既有很底层的东西,比如内存概念,也有很现代的语法,但前提是你需要都懂,对于那些不是科班出身又没有好好学习计算机相关的东西人来说,这个确实有难度。

Python平时主要是处理数据用,在Mac上使用很方便,最近想把一个程序让下面人用,他们是Window环境,Python他们并不熟悉,所以直接把源文件丢过去,他们又得折腾一番。我就想到打包成exe,其实很早就有这样的想法,但一直没有做过。用的是PyInstaller这个库,还想着这么简单,结果一打包,发现并不是Window下的exe,原来是我理解的错误,以为可以直接打包成Window上执行的exe了,实际上PyInstaller只是在不同平台上将python打包成对应平台的应用了。难道我又得找台Windows,环境弄好,再打包一个?真麻烦,想着可以交叉编译吗?查了下PyInstaller以前好像可以,后来就去掉了。之前用过点Go语言,就是可以交叉编译的,一时间我尽有想用Go重写那个功能的想法,但是一对比还是Python方便。最后找了一个通过docker进行打包的方案docker-pyinstaller

直接这个命令就可以打包了,具体可以参考文档。

docker run -v "$(pwd):/src/" cdrx/pyinstaller-windows

但是有个问题,打包出来的应用一执行,提示一个依赖的库,没有找到。不对啊,文档中明明说把依赖写在requirements.txt文件里,我也写了的。

文档中的这段话,看着没有其他的操作,也没有相关的示例代码。

If the src folder has a requirements.txt file, the packages will be installed into the environment before PyInstaller runs.

折腾了好几个小时,最后找到了一篇日文的文章,看完后豁然开朗,原来是写法的问题,我对docker的一些东西不够熟吧。

docker run --rm -v "$(pwd):/src/" cdrx/pyinstaller-windows -c \
  "pip install -r requirements.txt && \
  pyinstaller main.py --onedir --onefile --clean && \
  mv dist/main.exe main.exe && \
  rm -rf __pycache__/ build/ dist/ main.spec"

最后打包后的exe就没有依赖缺失的问题了。不过打包成exe后,我最终还是改了下python的代码,比如os.path.dirname(__file__)是无法使用了,另外打开文件时提示UnicodeDecodeError: 'gbk' codec can't decode byte的错误,需要加encoding='utf-8'

参考

Docker環境のPyInstallerでキレイにExe化する

最近在看WebAssembly的东西,觉得Rust写基于WebAssembly的Web应用好像是不错的选择。另外Rust这门语言,很早就听说的了,后来也买了《Rust权威指南》这本书,只是简单的翻了翻,并没有做过什么项目。于是就起了个备忘录的简单项目。

Yew

有点类似React,用起来还是比较顺手了。目前版本迭代还是相当的快,我一开始用的0.19.0的版本,最后因为一个库的关系,直接选择了最新的版本,版本之间还时存在兼容问题。我打包后有2M,相比传统的还是比较大了,不过可能是是没有优化的结果。在代码里面写Html,主要的问题是格式化的问题,vscode里面一格式化代码后,html的代码就乱了。

问题

Yew本身提供的文档还可以,有问题看看文档也能解决。主要的问题是wey_sys的一些库的使用,比如我想使用IndexedDB这个东西,其实JS版的文档很好看懂,Rust版的就有点不知所措,主要还是我对Rust了解不够,再加上这些库并没有特别全的文档,所以折腾半天。比如设置IndexedDB回调的问题,参数显示js_sys::Function,我想着那就构造个Function出来,结果翻遍文档没有看到如何构造的例子,最后解决方案是闭包转Function的方式,一开始也是报错,提示闭包已久被销毁了,后来发现官方的例子有点问题,最后修复了。

另外因为要在几个页面间共享一些数据,想着写个单例吧,发现不那么简单,找了个lazy_static这样库,提示*mut u8` cannot be shared between threads safely这样的错误,最终还是停了下来。

总结

Rust的学习曲线比较陡,主要是很多语法不同于传统的C体系,所以感觉原来的经验无法施展,而且沿着经验的思路反而会比较绕,发现还是得按Rust的方式解决问题。

上次去省图偶然看到了《WebAssembly 实战》一书,之前就有关注到,于是就借了回来,刚好趁清明假期看看。之前有写过一篇《初试WebAssembly》,本身Rust语言不熟,虽然我买了本《Rust权威指南》,但一直没有什么项目可以练手,基本上没有进展。《WebAssembly 实战》一书是基于C/C++的,相对来说读起来障碍不大。实际我也跑了一个例子试了下,当然如果真拿C/C++来写Web应用的话,估计会很奔溃了。接着我又看了好几个关于WebAssembly的视频演讲,发现有了新的想法。

推出WebAssembly这个东西主要是因为现有的优化已经无法满足性能的进一步提升了,语言肯定不能大改了,JS这么用的广。记得早些年使用Flash的时候,那会是在嵌入式上跑AS2,性能的问题真的是让我当时无语。后来又推出了AS3性能又大幅的提升,但是很长一段时间,我们公司的引擎没有升级,所以我基本上还是用AS2。虽然现在的Web开发没有像当年那样,毕竟开放的环境还是不一样。回到正题,如果单纯的写WebAssembly的话,好像也没有这个必要,而且据说和JS的调用开销不小,所以到底什么时候用,是个问题。反过来想要是哪天整个的Web可以大部分都用WebAssembly来写的话,那就好了,不过目前还不能直接操作Dom,这是一个很大的问题,理论和JS是相互调用的,我看到Rust有个web_sys的库,可以操作dom,不过我想应该也是自己实现的吧。目前看到几个Rust的例子,是直接用WebAssembly来写整个的Web应用的,比如Yew这个Rust的框架允许你构建基于WebAssembly的应用。另外微软的Blazor,允许使用C#来编写基于WebAssembly的单页应用。还有Go的Vugu。

所以如果和JS混着用,那么实际上是把WebAssembly作为一个计算密集型的一个解决方案吧,实际开发中大部分的Web应用都是些轻客户端,主要还是界面相关的操作了,其实大部分情况下很难想到用WebAssembly了,这也是我最早尝试时的感想。但是如果未来解决了类似调DOM的成本比较高的的问题,那么有没有可能Web语言变成Rust为主,为什么是Rust呢?首先WebAssembly本身没有垃圾回收,内存是手动处理了。Rust呢?标榜的是一个没有垃圾回收的安全语言,与C/C++相比实际上是语言层面上处理内存管理的问题。这么一想Rust与WebAssembly真是绝配,当然其他的语言也可以编译成WebAssembly,但对于垃圾回收的语言,是比要实现一个垃圾回收的运行时环境,这样性能上就会有折扣。

总结

想来想去,最终落到Rust这个语言上了,或者说WebAssembly本身的设计这种在底层改变的东西无疑为未来提供了各种改变的可能性,即使不是Rust也许会是其他的语言。C/C++用户可以让现有的库移植到Web上使用,另外Web应用一定是现在这样的轻客户端的类型吗?WebAssembly的引入确实提供另一种可能。另外一定要用JS来写Web应用吗?过去是没有办法,但是底层的改变让未来可以使用统一基于WebAssembly的语言来写Web成为了可能,而不像过去所有新的语言实际上都得编译成JS。基于WebAssembly的框架,有微软这样的大厂,也有基于Go的,某种程度这是一场大厂利好的事,但坏处是当有各种的语言可以写Web时,会不会有点分裂,毕竟只学一个JS,大家共用资源,而用不同的语言有学习成本。

0%