https://nodejs.org/en/download/ https://nodejs.org/dist/latest-v20.x/node-v20.18.2-x64.msi nvm安装 https://github.com/coreybutler/nvm-windows/releases nvm install lts nvm list C:\Users\itoracle_17068343192>nvm use 22.13.1 Now using node v22.13.1 (64-bit) C:\Users\itoracle_17068343192>node -v v22.13.1 C:\Users\itoracle_17068343192>npm -v 10.9.2 将npm的版本降到8.5.5这个解决兼容python,让node可以兼容python3 npm install -g npm@8.5.5 npm install -g yarn 安装指定版本 nvm install v20.9.0 |
二进制版本 mkdir -p ~/wks/nodejs/app cd ~/wks/nodejs/app/ wget https://nodejs.org/dist/v20.9.0/node-v20.9.0-linux-x64.tar.xz tar -xvf node-v20.9.0-linux-x64.tar.xz cd node-v20.9.0-linux-x64/ https://nodejs.org/en/download/ 环境变量 export NODEWKS=/home/xt/wks/nodejs export NODE_BASE=$NODEWKS/app/node-v20.9.0-linux-x64/ export PATH=$NODE_PATH/.bin:$NODE_BASE/bin:$PATH export NODE_GLOBAL=$NODEWKS/app/global export PATH=$NODE_GLOBAL/bin:$PATH 下面的设置会替代NODE_PATH的作用 mkdir -p $NODEWKS/app/{global,cache} npm config set prefix "$NODEWKS/app/global" npm config set cache "$NODEWKS/app/cache" NODE_GLOBAL就是上面global的路径,将全局安装的包加到PATH路径中 which node /home/xt/wks/nodejs/app/node-v20.9.0-linux-x64/bin/node $ npm -v 10.1.0 将npm的版本降到8.5.5这个解决兼容python,让node可以兼容python3 npm install -g npm@8.5.5 全局安装会安装到global目录 yarn npm install -g yarn 下面的根据需要安装,不需要就不用设置 yarn config set -g registry http://mirrors.cloud.tencent.com/npm/ yarn config set -g sass_binary_site http://mirrors.cloud.tencent.com/npm/node-sass/ which yarn /home/xt/wks/nodejs/app/global/bin/yarn yarn常用命令 安装包: yarn install // 安装package.json里所有包,并将包即它所有依赖项保存进yarn.lock yarn install --flat //安装一个包的单一版本 yarn install --force //强制重新下载所有包 yarn install --production //只安装dependencies里的包 yarn install --no-lockfile //不读取或生成yarn.lock yarn install --pure-lockfile //不生成yarn.lock 添加包(会更新package.json 和 yarn.lock): yarn add [package] //在当前项目中添加一个依赖包,会自动更新到package.json 和 yarn.lock文件中 yarn add [package]@[version] //安装指定版本,这里指的是主要版本,如果需要精确到小版本,使用 -E参数 yarn add [package]@[tag] //安装某个tag (比如beta,next或者latest) 不指定依赖类型默认安装到dependencies里,你可以指定依赖类型: yarn add --dev/-D //加到devDependencies yarn add --peer/-P //加到peerDependencies yarn add --optional /-O //加到optionalDependencies 默认安装包的主要版本里的最新版本,下面两个命令可以指定版本: yarn add --exact /-E //安装包的精确版本。例如: yarn add foo@1.2.3会接受1.9.1版,但是yarn add foo@1.2.3 --exact直接说1.2.3版 yarn add --title /-T //安装包的次要版本里的最新版。例如:yarn add foo@1.2.3 --title 会接受1.2.9,但不接受1.3.0 yarn install npm run start |
|
|
|
https://www.electronjs.org/zh/docs/latest/tutorial/quick-start mkdir qsbiji && cd qsbiji yarn init yarn add --dev electron 最后,您希望能够执行 Electron 如下所示, 在您的 package.json配置文件中的scripts字段下增加一条start命令: { "scripts": { "start": "electron ." } } yarn start |
ubuntu electron-packager打包 npm install --save-dev electron-packager ./node_modules/.bin/electron-packager . 73biji --platform=linux --arch=x64 vscode 环境中可以运行,出了该环境无法在ubuntu桌面运行 xt@ai:~/wks$ cd /home/xt/wks/nodejs/qsbiji/73biji-linux-x64 xt@ai:~/wks/nodejs/qsbiji/73biji-linux-x64$ ls 73biji chrome_crashpad_handler libEGL.so libvk_swiftshader.so LICENSES.chromium.html resources.pak version chrome_100_percent.pak chrome-sandbox libffmpeg.so libvulkan.so.1 locales snapshot_blob.bin vk_swiftshader_icd.json chrome_200_percent.pak icudtl.dat libGLESv2.so LICENSE resources v8_context_snapshot.bin xt@ai:~/wks/nodejs/qsbiji/73biji-linux-x64$ ./73biji https://webpack.js.org/ |
windows打包 electron-squirrel-startup是windows平台上的依赖包 首先要创建nodejs项目并init 注意Init时,描述,作者等提示写的内容不能为空 init完毕后,初始化一个简单的项目,配置index.js,index.html, 可以run之后,再进行下面的打包 npm install --save-dev @electron-forge/cli npx electron-forge import npm run make 打包demo:soft/windows/kaifa/73biji.rar |
https://www.electronforge.io/cli#make npm install --save-dev @electron-forge/cli npx create-electron-app@latest my-app https://www.electronforge.io/ npx create-electron-app@latest my-app npx create-electron-app@latest 73biji --template=webpack cd my-app npm install --save-dev @electron-forge/cli npx electron-forge init --template=webpack 如果有yarn.lock文件要先删除这个文件 npx electron-forge import npm start npm install --save-dev @electron-forge/publisher-github maker配置 // If your config is only in package.json: // Only showing the relevant configuration for brevity { "config": { "forge": { "makers": [ { "name": "@electron-forge/maker-zip", "platforms": ["darwin", "linux"], // optional "config": { // Config here } } ] } } } npm run make npx electron-forge import https://www.electronforge.io/cli#package npx electron-forge package --arch="x64" --platform="linux" npm install --save-dev @electron-forge/cli npx electron-forge init --template=webpack npx electron-forge import |
|
https://www.electronjs.org/zh/docs/latest/tutorial/tutorial-prerequisites Electron中的 ES 模块 (ESM) https://www.electronjs.org/zh/docs/latest/tutorial/esm 使用预加载脚本来增强渲染器:Electron modules及Polyfilled 的全局模块 BrowserWindow 的预加载脚本运行在具有 HTML DOM 和 Node.js、Electron API 的有限子集访问权限的环境中。 ::: info 预加载脚本沙盒化 从 Electron 20 开始,预加载脚本默认 沙盒化 ,不再拥有完整 Node.js 环境的访问权。 实际上,这意味着你只拥有一个 polyfilled 的 require 函数,这个函数只能访问一组有限的 API。 app:控制应用程序的事件生命周期。 https://www.electronjs.org/zh/docs/latest/api/app process https://www.electronjs.org/zh/docs/latest/api/process buffer https://nodejs.org/api/buffer.html clearImmediate https://nodejs.org/api/timers.html#timers_clearimmediate_immediate setImmediate https://nodejs.org/api/timers.html#timers_setimmediate_callback_args NodeJs events https://nodejs.org/api/events.html timers https://nodejs.org/api/timers.html url https://nodejs.org/api/url.html |
webPreferences加载JS // Create the browser window. const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: {//为了将脚本附在渲染进程上,在 BrowserWindow 构造器中使用 webPreferences.preload 传入脚本的路径。 preload: path.join(__dirname, 'preload.js') } }) 为了将脚本附在渲染进程上,在 BrowserWindow 构造器中使用 webPreferences.preload 传入脚本的路径。 nodejs/73biji/preload.js中配置全局变量 const { contextBridge } = require('electron') contextBridge.exposeInMainWorld('versions', { node: () => process.versions.node, chrome: () => process.versions.chrome, electron: () => process.versions.electron // 除函数之外,我们也可以暴露变量 }) 之后可以在页面或页面中的JS直接使用 const information = document.getElementById('info') information.innerText = `本应用正在使用 Chrome (v${versions.chrome()}), Node.js (v${versions.node()}), 和 Electron (v${versions.electron()})` |
渲染:就是Html加载展示的过程 Electron模块 就是 渲染进程模块, - 即Html在Electron中渲染 - 多数情况下,html是在浏览器中渲染的,可以将Electron看作一个迷你浏览器 - 所以Electron的进程也是渲染进程 主进程 - 项目是一个Nodejs项目,最初的入口,或者最开始的入口是一个js文件 - 通常是main.js/index.js,这就是主进程 在主进程中初始化了Electron模型,并将静态资源也传入了进去 //__dirname 字符串指向当前正在执行脚本的路径 (在本例中,它指向你的项目的根文件夹)。 //path.join API 将多个路径联结在一起,创建一个跨平台的路径字符串。 const createWindow = () => { // Create the browser window. const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: {//为了将脚本附在渲染进程上,在 BrowserWindow 构造器中使用 webPreferences.preload 传入脚本的路径。 preload: path.join(__dirname, 'preload.js') } }) // 加载 index.html mainWindow.loadFile('index.html') // 打开开发工具 // mainWindow.webContents.openDevTools() } 效率进程 Process-specific module aliases (TypeScript) Electron's npm package also exports subpaths that contain a subset of Electron's TypeScript type definitions. electron/main includes types for all main process modules. electron/renderer includes types for all renderer process modules. electron/common includes types for modules that can run in main and renderer processes. These aliases have no impact on runtime, but can be used for typechecking and autocomplete. const { app } = require('electron/main') const { shell } = require('electron/common') |
nodejs项目有一部分是js开发,这部分js在main.js中加载,即在主进程中加载 现在页面在Electron渲染进程中, 要想在页面中访问main.js主进程中的方法,可以进行以下设置 main.js:引入 ipcMain const { app, BrowserWindow, ipcMain } = require('electron/main') const path = require('node:path') const createWindow = () => { const win = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js') } }) win.loadFile('index.html') } app.whenReady().then(() => { ipcMain.handle('ping', () => 'pong') createWindow() }) 渲染进程中在preload.js通过ipcRenderer调用main.js主进程的方法 const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld('versions', { node: () => process.versions.node, chrome: () => process.versions.chrome, electron: () => process.versions.electron, ping: () => ipcRenderer.invoke('ping') // 除函数之外,我们也可以暴露变量 }) renderer.js中调用 const func = async () => { const response = await window.versions.ping() console.log(response) // 打印 'pong' } func() |
window-all-closed事件 // quitting the app when no windows are open on non-macOS platforms app.on('window-all-closed', () => { if (process.platform !== 'darwin') app.quit() }) app事件 The main process also controls your application's lifecycle through Electron's app module. This module provides a large set of events and methods that you can use to add custom application behavior (for instance, programmatically quitting your application, modifying the application dock, or showing an About panel). 其他事件参考 https://www.electronjs.org/zh/docs/latest/api/app |
https://www.electronjs.org/zh/docs/latest/tutorial/ipc main.js:主进程通过ipcMain.on定义一个事件 ipcMain.on('set-title', (event, title) => { const webContents = event.sender console.log("webContents") console.log(webContents) const win = BrowserWindow.fromWebContents(webContents) win.setTitle(title) }) // 加载 index.html mainWindow.loadFile('index.html') 主进程的日志会打印在IDE控制台 渲染器预加载脚本preload.js设置electronAPI.setTitle与主进程中的set-title绑定 const { contextBridge, ipcRenderer } = require('electron/renderer') contextBridge.exposeInMainWorld('electronAPI', { setTitle: (title) => ipcRenderer.send('set-title', title) }) 渲染器页面调用渲染器的方法 const setButton = document.getElementById('btn') const titleInput = document.getElementById('title') setButton.addEventListener('click', () => { const title = titleInput.value window.electronAPI.setTitle(title) }) |
main.js const { app, BrowserWindow, ipcMain, dialog } = require('electron/main') const path = require('node:path') async function handleFileOpen () { const { canceled, filePaths } = await dialog.showOpenDialog() if (!canceled) { return filePaths[0] } } function createWindow () { const mainWindow = new BrowserWindow({ webPreferences: { preload: path.join(__dirname, 'preload.js') } }) mainWindow.loadFile('index.html') } app.whenReady().then(() => { ipcMain.handle('dialog:openFile', handleFileOpen) createWindow() app.on('activate', function () { if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) app.on('window-all-closed', function () { if (process.platform !== 'darwin') app.quit() }) 使用 ipcMain.handle 监听事件 在主进程中,我们将创建一个 handleFileOpen() 函数, 它调用 dialog.showOpenDialog 并返回用户选择的文件路径值。 每当渲染器进程通过 dialog:openFile 通道发送 ipcRender.invoke 消息时,此函数被用作一个回调。 然后,返回值将作为一个 Promise 返回到最初的 invoke 调用。 preload.js const { contextBridge, ipcRenderer } = require('electron/renderer') contextBridge.exposeInMainWorld('electronAPI', { openFile: () => ipcRenderer.invoke('dialog:openFile') }) renderer.js const btn = document.getElementById('btn') const filePathElement = document.getElementById('filePath') btn.addEventListener('click', async () => { const filePath = await window.electronAPI.openFile() filePathElement.innerText = filePath }) 出于保留历史的目地,会有一些其他的方法。我们建议尽可能使用 ipcRenderer.invoke 。 |
main.js中添加菜单 const menu = Menu.buildFromTemplate([ { label: app.name, submenu: [ { click: () => mainWindow.webContents.send('update-counter', 1), label: 'Increment' }, { click: () => mainWindow.webContents.send('update-counter', -1), label: 'Decrement' } ] } ]) Menu.setApplicationMenu(menu) click 处理函数通过 update-counter 通道向渲染器进程发送消息(1 或 -1)。 click: () => mainWindow.webContents.send('update-counter', -1) preload.js const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld('electronAPI', { onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (_event, value) => callback(value)) }) renderer.js const counter = document.getElementById('counter') window.electronAPI.onUpdateCounter((value) => { const oldValue = Number(counter.innerText) const newValue = oldValue + value counter.innerText = newValue.toString() }) 回调 从 ipcRenderer.on 回调中将回复发送回主进程。 preload.js const { contextBridge, ipcRenderer } = require('electron') contextBridge.exposeInMainWorld('electronAPI', { onUpdateCounter: (callback) => ipcRenderer.on('update-counter', (_event, value) => callback(value)), counterValue: (value) => ipcRenderer.send('counter-value', value) }) renderer.js const counter = document.getElementById('counter') window.electronAPI.onUpdateCounter((value) => { const oldValue = Number(counter.innerText) const newValue = oldValue + value counter.innerText = newValue.toString() window.electronAPI.counterValue(newValue) }) main.js // ... ipcMain.on('counter-value', (_event, value) => { console.log(value) // will print value to Node console }) // ... |
https://www.electronjs.org/zh/docs/latest/tutorial/message-ports |
|
Electron 应用程序的结构非常相似。 作为应用开发者,你将控制两种类型的进程:主进程 和 渲染器进程。 这类似于上文所述的 Chrome 的浏览器和渲染器进程。 主进程 每个 Electron 应用都有一个单一的主进程,作为应用程序的入口点。 主进程在 Node.js 环境中运行,这意味着它具有 require 模块和使用所有 Node.js API 的能力。 窗口管理 The main process' primary purpose is to create and manage application windows with the BrowserWindow module BrowserWindow 类的每个实例创建一个应用程序窗口,且在单独的渲染器进程中加载一个网页。 You can interact with this web content from the main process using the window's webContents object. const { BrowserWindow } = require('electron') const win = new BrowserWindow({ width: 800, height: 1500 }) win.loadURL('https://www.bing.com') const contents = win.webContents console.log(contents) Note: A renderer process is also created for web embeds such as the BrowserView module. 嵌入式网页内容也可访问 webContents 对象。 由于 BrowserWindow 模块是一个 EventEmitter, 所以您也可以为各种用户事件 ( 例如,最小化 或 最大化您的窗口 ) 添加处理程序。 当一个 BrowserWindow 实例被销毁时,与其相应的渲染器进程也会被终止。 |
|
|
|
|