随笔 - 5  文章 - 0  评论 - 1  阅读 - 3582

Electron中使用bytenode保护nodejs代码实践

网上也看了不少把node javascript转换为bytecode的文章,但是实操起来总有些问题,特别是对preload.js部分怎么把preload.js转换为bytecode,说得不那么详尽;我把我自己实践过程详细的描述一下,希望可以帮到有需要的朋友;

1.我是在一个开源项目上简单修改一下,https://gitee.com/chiugi/vue3-electron-serialport

Home.vue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/* eslint-disable no-bitwise */
<template>
  <div>
    <el-form>
      <el-form-item>
         渲染进程
      </el-form-item>
    </el-form>
  </div>
</template>
 
<script>
/* eslint-disable */
import {
  reactive, ref, onUnmounted, watch, computed, nextTick,
} from 'vue';
import funtest1 from '../funtest1'
const toHexString = bytes =>
  bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')+' ';
funtest1('call by Home');
const getForm = () => {   
     
  return {
   
  };
}
 
export default {
  name: 'Home'
  setup() {
    return {
      ...getForm(),     
    };
  },
  methods: {
  }
};
</script>

 background.js

复制代码
import {
  app, protocol, BrowserWindow, session, ipcMain,
} from 'electron';
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib';
// import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer';
const path = require('path');

const isDevelopment = process.env.NODE_ENV !== 'production';

app.allowRendererProcessReuse = false;
// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
  { scheme: 'app', privileges: { secure: true, standard: true } },
]);


// 测试 Node API
// const bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
// const buf = Buffer.from(bytes);
// console.log('backgroud,buf', buf);

async function createWindow() {
  // Create the browser window.
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      contextIsolation: true,
      preload: path.join(__dirname, '/preload.js'),
    },
  });

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    await win.loadURL(process.env.WEBPACK_DEV_SERVER_URL);
    if (!process.env.IS_TEST) win.webContents.openDevTools();
  } else {
    createProtocol('app');
    // Load the index.html when not in development
    win.loadURL('app://./index.html');
  }
}

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (BrowserWindow.getAllWindows().length === 0) createWindow();
});

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    // try {
    //   await installExtension(VUEJS_DEVTOOLS);
    //   session.defaultSession.loadExtension(
    //     path.resolve(__dirname, '../../vue-devtools/shells/chrome'), // 这个是刚刚build好的插件目录
    //   );
    // } catch (e) {
    //   console.error('Vue Devtools failed to install:', e.toString());
    // }

    // 记得预先安装 npm install vue-devtools
    const ses = session.fromPartition('persist:name');
    try {
      // The path to the extension in 'loadExtension' must be absolute
      await ses.loadExtension(path.resolve('node_modules/vue-devtools/vender'));
    } catch (e) {
      console.error('Vue Devtools failed to install:', e.toString());
    }
  }
  createWindow();
});

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
  if (process.platform === 'win32') {
    process.on('message', (data) => {
      if (data === 'graceful-exit') {
        app.quit();
      }
    });
  } else {
    process.on('SIGTERM', () => {
      app.quit();
    });
  }
}
复制代码

preload.js

const funtest1 = require('./funtest1');
const bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const buf = Buffer.from(bytes);
console.log('preload,buf', buf);
funtest1('call by preload');

funtest1.js

复制代码
function stringToUint8Array(str){
  var arr = [];
  for (var i = 0, j = str.length; i < j; ++i) {
    arr.push(str.charCodeAt(i));
  }
 
  var tmpUint8Array = new Uint8Array(arr);
  return tmpUint8Array
}

function funtest1(str) {
  console.log("str",str);
  const buf = Buffer.from(str);
  //const buf = stringToUint8Array(str);
  console.log("buf:",buf);
}

module.exports = funtest1;
复制代码

执行

npm run electron:serve

在console里面看日志,可以发现那个 Buffer.from 返回的对象在preload.js 里面跟在 Home.vue里面返回的对象类型是不一样的,在 Node.js 中,Buffer 类是随 Node 内核一起发布的核心库,在普通的Web里面的Javascript环境是调用不了的,

我这个是使用 Electron13.6.2版本,在渲染进程里面能使用Buffer.from,感觉应该跟早期版本渲染进程可以直接调用Nodejs API有关系,虽然新版在渲染进程中不能通过require调用Node API,但是调用Node核心库好像没有问题;我说这个跟下面的内容是有关系的,先做个铺垫;

下面来说说如何使用bytenode;先安装

npm install --save bytenode

修改下package.json,在  scripts 里面增加

"pack": "vue-cli-service electron:build --skipBundle"

在 vue.config.js里面的增加

 electronBuilder: {
      preload: 'src/preload.js',
     builderOptions: {
     ...

这样build的时候才会把 preload.js 打包进去;

执行打包

npm run electron:build

在 dist_electron\win-unpacked\resources\app 目录下可以看到,跟   dist_electron\bundled  是一样的

 

下面的使用bytenode的方法是学习自 https://gitee.com/qjh_2413/vue-electron-bytenode.git

1.在bundled里面把 backgroud.js 重命名为 backgroud.src.js,把 preload.js重命名为 preload.src.js,新建 background.bytenode.js和 preload.bytenode.js

background.bytenode.js

复制代码
'use strict';

const bytenode = require('bytenode');
const fs = require('fs');
const v8 = require('v8');
const path = require('path');

v8.setFlagsFromString('--no-lazy');

if (!fs.existsSync(path.join(__dirname, './background.jsc'))) {
  bytenode.compileFile(path.join(__dirname, './background.src.js'),path.join(__dirname,  './background.jsc'));
}

require(path.join(__dirname,'./background.jsc'));
复制代码

preload.bytenode.js

复制代码
'use strict';

const bytenode = require('bytenode');
const fs = require('fs');
const v8 = require('v8');
const path = require('path');

v8.setFlagsFromString('--no-lazy');

if (!fs.existsSync(path.join(__dirname, './preload.jsc'))) {
  bytenode.compileFile(path.join(__dirname, './preload.src.js'),path.join(__dirname,  './preload.jsc'));
}

require(path.join(__dirname,'./preload.jsc'));
复制代码

把 background.bytenode.js 复制一份为 backgroud.js,把 preload.bytenode.js复制为 preload.js,

网上有说用 electron .\background.js ,但是经常有人碰到错误,其实就是因为你调用的electron版本跟当前项目的版本不一致;你可以直接使用 .\node_modules\electron\dist\electron.exe .\dist_electron\bundled\background.js   ,用当前项目的electron来执行background.js;

另一种更简单,在 dist_electron\win-unpacked\resources\app 执行跟上面一样的步骤(electronbuilder里面的 asar 先设为 false),然后执行 dist_electron\win-unpacked\(项目).exe,然后把 backgroud.jsc,background.js,preload.jsc,preload.js复制到 bundled 目录里面;

然后执行打包

npm run pack

但是你会发现

 preload.js里面调用 Buffer.from 报错,因为 preload.js 生成preload.jsc时是在渲染进程执行的,普通的浏览器javascript环境是调用不了 Buffer.from 这个 Nodejs API的。要解决这个问题,把生成 preload.jsc这个步骤放到 background.js执行,

修改 background.js

复制代码
'use strict';

const bytenode = require('bytenode');
const fs = require('fs');
const v8 = require('v8');
const path = require('path');

v8.setFlagsFromString('--no-lazy');

if (!fs.existsSync(path.join(__dirname, './background.jsc'))) {
  bytenode.compileFile(path.join(__dirname, './background.src.js'),path.join(__dirname,  './background.jsc'));
}

if (!fs.existsSync(path.join(__dirname, './preload.jsc'))) {
  bytenode.compileFile(path.join(__dirname, './preload.src.js'),path.join(__dirname,  './preload.jsc'));
}

require(path.join(__dirname,'./background.jsc'));
复制代码

然后在 preload.js里面把生成 preload.jsc 这段代码注释掉,你发现可以正确执行,调用Buffer.from 也不报错了;

我也实验了一下 在 preload.js 里面生成 background.jsc,

修改 preload.js

复制代码
'use strict';

const bytenode = require('bytenode');
const fs = require('fs');
const v8 = require('v8');
const path = require('path');

v8.setFlagsFromString('--no-lazy');

if (!fs.existsSync(path.join(__dirname, './preload.jsc'))) {
  bytenode.compileFile(path.join(__dirname, './preload.src.js'),path.join(__dirname,  './preload.jsc'));
}

if (!fs.existsSync(path.join(__dirname, './background.jsc'))) {
  bytenode.compileFile(path.join(__dirname, './background.src.js'),path.join(__dirname,  './background.jsc'));
}

require(path.join(__dirname,'./preload.jsc'));
复制代码

我说下结果吧,如果 background.js 里面不调用 Buffer.from,生成的 background.jsc执行没有报错,background.js里面通过require引用Nodejs API都没有问题,如果我在background.js加上

 const bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 const buf = Buffer.from(bytes);
 console.log('backgroud,buf', buf);

启动就会报错,

应用启动不起来;

 总结:background.js,preload.js这些要转换成bytecode还是在主线程里面执行比较好;

 

最新更新:把bytenode 编译放入打包流程,请看项目:https://gitee.com/cfqdream/vue3-electron-bytenode.git

posted on   东八泰  阅读(1923)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· 单线程的Redis速度为什么快?
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示