網上也看了不少把node javascript轉換為bytecode的文章,但是實操起來總有些問題,特別是對preload.js部分怎么把preload.js轉換為bytecode,說得不那么詳盡;我把我自己實踐過程詳細的描述一下,希望可以幫到有需要的朋友;
1.我是在一個開源項目上簡單修改一下,https://gitee.com/chiugi/vue3-electron-serialport
Home.vue
/* 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