随着macos M1芯片的发布,当前的electron PC应用要适配arm64架构(虽然低版本electron应用也可以在arm64架构上面运行(转译)但是这严重影响电脑性能)。
mac上面编译node需要安装xcode、node-gyp、python,node。可以根据下面链接查看到当前支持了M1架构的应用具体版本号。https://isapplesiliconready.com/zh
nodelee@bijiewangluodeMac-mini m1_addon % npm -v
7.0.8
nodelee@bijiewangluodeMac-mini m1_addon % node -v
v15.1.0
nodelee@bijiewangluodeMac-mini m1_addon % node-gyp -v
v7.1.2
强烈建议使用napi实现addon,nan api适配性很差,不同版本的electron需要重新编译,并且有些高版本的electron还不支持。
addon.cc
#include
#include
#include
#include
#define NAPI_EXPERIMENTAL
#include
#define NAPI_DESC(name, func) \
napi_property_descriptor { name, 0, func, 0, 0, 0, napi_default, 0 }
#define CHECK(expr) \
{ \
if ((expr == napi_ok) == 0) \
{ \
fprintf(stderr, "[Err] %s:%d: %s\n", __FILE__, __LINE__, #expr); \
fflush(stderr); \
abort(); \
} \
}
napi_value get_value(napi_env env, napi_callback_info info)
{
napi_value ret;
CHECK(napi_create_int32(env, 1314, &ret));
return ret;
}
napi_value Init(napi_env env, napi_value exports)
{
napi_property_descriptor desc;
desc = NAPI_DESC("getValue", get_value);
CHECK(napi_define_properties(env, exports, 1, &desc));
return exports;
}
NAPI_MODULE(addon, Init)
binding.gyp
{
'targets': [
{
"target_name": "addon",
"sources": ["./addon.cc"]
}
]
}
index.js
var addon= require('./build/Release/addon.node');
console.log("addon get value is ", addon.getValue());
分别在intel和arm64 环境下编译
node-gyp rebuild --arch=arm64
node-gyp rebuild --arch=x64
查看intel x86上面编译的node架构
macdeMacBook-Pro:mac mac$ lipo -info addon_x64.node
Architectures in the fat file: addon_x64.node are: x86_64
查看arm64上面编译的node架构
nodelee@bijiewangluodeMac-mini Release % lipo -info addon_arm64.node
Non-fat file: addon_arm64.node is architecture: arm64
正常情况下两种架构的运行结果:
nodelee@bijiewangluodeMac-mini m1_addon % node index.js
addon get value is 1314
合并node
为了调用方便我们可以把不同架构的node合并成一个,加载的时候根据不同的架构加载不同的node。
lipo -create addon_arm64.node addon_x64.node -output addon.node
查看合并后的node架构
nodelee@bijiewangluodeMac-mini Release % lipo -info addon.node
Architectures in the fat file: addon.node are: x86_64 arm64
electron高版本为了安全性打包后contextIsolation默认为true
mian.js
const {
app,
BrowserWindow,
dialog,
ipcMain
} = require("electron");
const path = require("path");
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win;
function createWindow() {
// Create the browser window.
win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false, // is default value after Electron v5
contextIsolation: true, // protect against prototype pollution
// enableRemoteModule: false, // turn off remote
preload: path.join(__dirname, "./preload.js") // use a preload script
}
});
// Load app
win.loadFile(path.join(__dirname, "./index.html"));
}
app.on("ready", createWindow);
index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<script>
console.log("value is ", window.api.getValue()); //
script>
<body>
<h1>hello electronh1>
body>
html>
package.json
{
"name": "bjcast-demo",
"version": "1.0.18",
"description": "A minimal Electron application",
"main": "./main.js",
"scripts": {
"dev": "electron .",
"build:mac": "electron-builder --mac --arm64"
},
"build": {
"appId": "com.bjnet.demo",
"mac": {
"target": {
"target": "dir",
"arch": "arm64"
}
},
},
"author": "GitHub",
"license": "CC0-1.0",
"devDependencies": {
"electron": "^11.1.0",
"electron-builder": "^22.9.1"
}
}
preload.js
const {
contextBridge,
} = require("electron");
var addon = require('./addon.node');
contextBridge.exposeInMainWorld(
"api", {
getValue: () => {
return addon.getValue();
},
}
);
运行
npm run dev
打包
npm run build:mac