Nodejs环境

 
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 ### 安装和使用 ```bash nvm install lts nvm list ``` ### 切换版本 ```bash 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 版管理 将npm的版本降到8.5.5这个解决兼容python,让node可以兼容python3: ```bash npm install -g npm@8.5.5 npm install -g yarn ```
## 安装指定版本 ```bash nvm install v20.9.0 ```
## 二进制版本 ```bash 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/
## 环境变量 ```bash 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的作用: ```bash 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 常用命令 ### 安装包 ```bash 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) ```bash yarn add [package] # 在当前项目中添加一个依赖包 yarn add [package]@[version] # 安装指定版本(主要版本) yarn add [package]@[tag] # 安装某个tag (比如beta,next或者latest) ``` ### 依赖类型 不指定依赖类型默认安装到dependencies里,你可以指定依赖类型: ```bash yarn add --dev/-D # 加到devDependencies yarn add --peer/-P # 加到peerDependencies yarn add --optional/-O # 加到optionalDependencies ``` ### 版本控制 默认安装包的主要版本里的最新版本,下面两个命令可以指定版本: ```bash yarn add --exact/-E # 安装包的精确版本,如yarn add foo@1.2.3 --exact直接装1.2.3版 yarn add --tilde/-T # 安装包的次要版本里的最新版,如yarn add foo@1.2.3 --tilde会接受1.2.9,但不接受1.3.0 ```

 
yarn install 
npm run start

## Windows 问题解决 ### PowerShell 脚本执行策略错误 错误信息:`npm : 无法加载文件 C:\nvm4w\nodejs\npm.ps1,因为在此系统上禁止运行脚本` ### 解决方案 #### 1. 以管理员身份启动 PowerShell 右键点击 Windows 开始菜单,选择「Windows PowerShell(管理员)」,确保拥有系统权限。 #### 2. 查看和修改执行策略 ```powershell PS C:\Users\itora> Get-ExecutionPolicy Restricted PS C:\Users\itora> Set-ExecutionPolicy RemoteSigned -Scope CurrentUser PS C:\Users\itora> Get-ExecutionPolicy RemoteSigned ``` #### 3. 参数说明 - **RemoteSigned**:允许本地未签名脚本运行,但远程脚本需签名(推荐安全配置) - **-Scope CurrentUser**:仅对当前用户生效,避免全局策略调整
重新运行 npm 命令,关闭当前 PowerShell 窗口,重新启动普通权限的终端(无需管理员),再次执行 npm 相关操作。 **安全性提示**:调整策略后需确保运行的脚本来源可信。若需恢复默认策略,可执行: ```powershell Set-ExecutionPolicy Restricted -Scope CurrentUser ```
## Electron 镜像源配置 ### 方法1:使用国内镜像源(推荐) #### 设置yarn镜像源 ```bash # 设置淘宝镜像源 yarn config set registry https://registry.npmmirror.com # 设置Electron专用镜像 yarn config set electron_mirror https://npmmirror.com/mirrors/electron/ # 验证配置 yarn config list ``` 然后重新安装: ```bash yarn add electron --dev ``` ### 方法2:临时使用镜像源 ```bash # 临时设置环境变量 ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ yarn add electron --dev ``` ### 方法3:配置.npmrc文件 在项目根目录创建或修改 `.npmrc` 文件: ```ini registry=https://registry.npmmirror.com electron_mirror=https://npmmirror.com/mirrors/electron/ electron_custom_dir=version ``` ### 方法4:使用cnpm(备选方案) ```bash # 安装cnpm npm install -g cnpm --registry=https://registry.npmmirror.com # 使用cnpm安装 cnpm add electron --dev ``` ### 方法5:完整的一键设置脚本 ```bash #!/bin/bash # 设置yarn和electron镜像 yarn config set registry https://registry.npmmirror.com yarn config set electron_mirror https://npmmirror.com/mirrors/electron/ yarn config set sass_binary_site https://npmmirror.com/mirrors/node-sass/ yarn config set phantomjs_cdnurl https://npmmirror.com/mirrors/phantomjs/ echo "镜像源配置完成!现在可以快速安装electron了" echo "运行: yarn add electron --dev" ``` ### 方法6:验证安装 ```bash # 检查当前yarn配置 yarn config list ``` # 检查electron是否安装成功 npx electron --version 7. 常用国内镜像源 淘宝镜像: https://registry.npmmirror.com 华为镜像: https://mirrors.huaweicloud.com/repository/npm/ 腾讯镜像: https://mirrors.cloud.tencent.com/npm/ ### 方法8:离线安装 如果仍然很慢,可以使用离线安装: ```bash # 先下载electron二进制文件到本地缓存目录 # macOS/Linux: ~/.cache/electron # Windows: %USERPROFILE%\AppData\Local\electron\Cache ```
Electron项目搭建
## Electron 快速开始 参考文档:https://www.electronjs.org/zh/docs/latest/tutorial/quick-start ### 初始化项目 ```bash mkdir 73biji && cd 73biji yarn init yarn add --dev electron ``` ### 配置 package.json 在您的 `package.json` 配置文件中的 `scripts` 字段下增加一条 `start` 命令: ```json { "scripts": { "start": "electron ." } } ``` ### 启动应用 ```bash yarn start ```
## Ubuntu 系统依赖 在 Ubuntu 上运行 Electron 应用前,需要安装以下系统依赖: ```bash sudo apt update sudo apt install libgbm1 sudo apt install libatk1.0-0 # 主包 sudo apt install libatk-bridge2.0-0 # 如果还提示缺 bridge sudo apt install libgtk-3-0 libgtk2.0-0 \ libgdk-pixbuf2.0-0 libglib2.0-0 \ libx11-xcb1 libxcomposite1 \ libxcursor1 libxdamage1 \ libxrandr2 libxtst6 libnss3 \ libasound2 libdbus-1-3 ``` ### Ubuntu electron-packager 打包 ```bash npm install --save-dev electron-packager ./node_modules/.bin/electron-packager . 73biji --platform=linux --arch=x64 ``` **注意**:vscode 环境中可以运行,出了该环境无法在ubuntu桌面运行 ### 打包后的目录结构 ```bash 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 `electron-squirrel-startup` 是 windows 平台上的依赖包。 ### 打包步骤 #### 1. 创建项目 首先要创建 nodejs 项目并 init。 **注意**:Init 时,描述、作者等提示写的内容不能为空。 #### 2. 初始化简单项目 配置 index.js、index.html,可以 run 之后,再进行下面的打包。 #### 3. 使用 Electron Forge 打包 ```bash npm install --save-dev @electron-forge/cli npx electron-forge import npm run make ``` ### 打包示例 打包demo:`soft/windows/kaifa/73biji.rar`

## Electron Forge 快速开始 官方文档:https://www.electronforge.io/cli#make ### 安装和创建项目 ```bash npm install --save-dev @electron-forge/cli npx create-electron-app@latest my-app ```

## Electron Forge 官方文档:https://www.electronforge.io/ ### 快速开始 ```bash npx create-electron-app@latest my-app npx create-electron-app@latest 73biji --template=webpack ``` ### 初始化项目 ```bash cd my-app npm install --save-dev @electron-forge/cli npx electron-forge init --template=webpack ``` **注意**:如果有 yarn.lock 文件要先删除这个文件。 ### 导入和启动 ```bash npx electron-forge import npm start ``` ### GitHub 发布 ```bash npm install --save-dev @electron-forge/publisher-github ``` ### Maker 配置 ```javascript // 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 } } ] } } } ``` ### 打包命令 ```bash npm run make ``` ### Package 命令 参考文档:https://www.electronforge.io/cli#package ```bash npx electron-forge package --arch="x64" --platform="linux" ``` ### 完整流程示例 ```bash npm install --save-dev @electron-forge/cli npx electron-forge init --template=webpack npx electron-forge import ```
基本架构

## Electron 基本架构教程 官方文档: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 的有限子集访问权限的环境中。 #### 预加载脚本沙盒化 从 Electron 20 开始,预加载脚本默认沙盒化,不再拥有完整 Node.js 环境的访问权。 实际上,这意味着你只拥有一个 polyfilled 的 require 函数,这个函数只能访问一组有限的 API。 #### 可用的 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 #### Node.js 模块 **events** https://nodejs.org/api/events.html **timers** https://nodejs.org/api/timers.html **url** https://nodejs.org/api/url.html
## webPreferences 加载 JS ### Preload 脚本 为了将脚本附在渲染进程上,在 BrowserWindow 构造器中使用 webPreferences.preload 传入脚本的路径。 ```javascript // Create the browser window. const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { // 为了将脚本附在渲染进程上,在 BrowserWindow 构造器中使用 webPreferences.preload 传入脚本的路径。 preload: path.join(__dirname, 'preload.js') } }) ``` ### 配置全局变量 在 `nodejs/73biji/preload.js` 中配置全局变量: ```javascript const { contextBridge } = require('electron') contextBridge.exposeInMainWorld('versions', { node: () => process.versions.node, chrome: () => process.versions.chrome, electron: () => process.versions.electron // 除函数之外,我们也可以暴露变量 }) ``` ### 在页面中使用 之后可以在页面或页面中的 JS 直接使用: ```javascript const information = document.getElementById('info') information.innerText = `本应用正在使用 Chrome` ```
## 渲染进程与主进程 ### 渲染进程 渲染:就是Html加载展示的过程 Electron模块就是渲染进程模块: - 即Html在Electron中渲染 - 多数情况下,html是在浏览器中渲染的,可以将Electron看作一个迷你浏览器 - 所以Electron的进程也是渲染进程 ### 主进程 - 项目是一个Nodejs项目,最初的入口,或者最开始的入口是一个js文件 - 通常是main.js/index.js,这就是主进程 ### 在主进程中初始化Electron模块 在主进程中初始化了Electron模型,并将静态资源也传入了进去: ```javascript // __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() } ``` ### 效率进程(Utility Process) 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. ### 导入示例 ```javascript 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 实例被销毁时,与其相应的渲染器进程也会被终止。
    

 

    

 


 

  

 


参考