NodeJS安装升级
brew install node
brew update # cd $(brew --repo) #HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles'
brew upgrade
sudo npm cache clean -f #清除缓存
npm view npm versions #所有版本
npm view npm version #最新版本
npm view node versions
sudo npm install npm@latest -g #升级到最新版本的npm
sudo npm install npm@xx -g #升级到指定版本npm
npm list 清单
npm config set registry=https://registry.npmmirror.com
npm config set disturl=https://registry.npmmirror.com/-/binary/node
项目初始化
mkdir project_name
cd project_name
npm init #entry point main.js
#npm install --arch=ia32 --platform=win32
npm config set ELECTRON_MIRROR=http://npm.taobao.org/mirrors/electron/
npm install --save-dev electron
//vi package.json "scripts": {"start": "electron ."},
//touch main.js
//touch index.html or use nomadli.com/index.html
//touch preload.js
//vi main.js
const { app, BrowserWindow, Menu, ipcMain } = require('electron')
const path = require('path')
//let win: BrowserWindow | null = null;
const createWindow = () => {
//if (win !== null) {
// return;
//}
const win = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
//__dirname 当前js的目录, preload在启动进程前运行,因此preload.js还在主进程
preload: path.join(__dirname, 'preload.js'),
// 打开这选项,可以在渲染进程使用node
// 否则就只能在主进程使用node,所有消息靠进程间通信实现
// 但是electron官方非常不推荐,这样不太安全
nodeIntegration: true,
contextIsolation: false,
webSecurity: false
},
icon: '/path/to/icon.png',
frame: false // 设置无边框窗体
})
const menu = Menu.buildFromTemplate([
{
label: app.name,
submenu: [
{
//win.webContents == ipcRenderer.send
click: () => win.webContents.send('update-counter', 1),
label: 'Increment'
},
{
click: () => win.webContents.send('update-counter', -1),
label: 'Decrement'
}
]
}])
Menu.setApplicationMenu(menu)
win.loadFile('index.html')
//win.on('close', () => win = null);
win.webContents.openDevTools() // Open the DevTools.
}
app.whenReady().then(() => {
console.log("platform = " +process.platform)
console.log("electron version = " + process.versions['electron'])
console.log("node version = " + process.versions['node'])
console.log("chrome version = " + process.versions['chrome'])
ipcMain.on('set-title', (event, title) => {
const webContents = event.sender
const win = BrowserWindow.fromWebContents(webContents)
win.setTitle(title)
})
ipcMain.handle('dialog:openFile', async () => {
const { canceled, filePaths } = await dialog.showOpenDialog()
if (!canceled) {
return filePaths[0]
}
})
ipcMain.on('async-req', (event, arg) => {
console.log(arg)
event.reply('async-rps', 'pong')
})
ipcMain.on('sync-req', (event, arg) => {
console.log(arg)
event.returnValue = 'pong'
})
createWindow()
if (process.platform === 'darwin') {
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow()
}
})
}
})
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})
//app.on('activate', () => {
// createWindow();
//})
//vi preload.js 这里是render进程
const { contextBridge, ipcRenderer } = require('electron')
//mainword是NodeJS进程 render的js调用${nodejs.chrome()} window.nodejs.ping() window.nodejs.setTitle(title)
contextBridge.exposeInMainWorld('nodejs', {
node: () => process.versions.node,
chrome: () => process.versions.chrome,
electron: () => process.versions.electron,
send: ipcRenderer.send, //直接运行render发送进程通信
aping: () => ipcRenderer.send('async-req', 'ping'),
ping: () => {const r = ipcRenderer.sendSync('sync-req', 'ping') console.log(r)},
setTitle: (title) => ipcRenderer.send('set-title', title),
openFile: () => ipcRenderer.invoke('dialog:openFile'),
//window.nodejs.menuCounter((event, value) => {event.sender.send('counter-value', newValue)}) 其中event.sender.send 相当于ipcRenderer.send
menuCounter: (callback) => ipcRenderer.on('update-counter', callback)
})
ipcRenderer.on('async-rps', (_event, arg) => {
console.log(arg)
})
window.addEventListener('DOMContentLoaded', () => {
const item = document.getElementById(item_id)
if (item) {
item.innerText = "hello change world"
}
})
- 创建NodeJS工程、引入Electron模块、通过Electron的app声明周期调用chrome的显示.整体c/s模型
- app 模块生命周期
- BrowserWindow 窗口管理
- path NodeJS path 模块
- reload.js 用来在两个进程通信只能使用 Electron的染进程 Node.js的events、timers、url 和Polyfilled全局模块Buffer、process、clearImmediate、setImmediate
项目打包
npm install --save-dev @electron-forge/cli
npx electron-forge import
npm run make
//forge.config.js 中macos 签名
module.exports = {
packagerConfig: {
osxSign: {},
osxNotarize: {
tool: 'notarytool',
appleId: process.env.APPLE_ID,
appleIdPassword: process.env.APPLE_PASSWORD,
teamId: process.env.APPLE_TEAM_ID
}
}
}
//window 签名
module.exports = {
makers: [
{
name: '@electron-forge/maker-squirrel',
config: {
certificateFile: './cert.pfx',
certificatePassword: process.env.CERTIFICATE_PASSWORD,
iconUrl: 'https://url/to/icon.ico',
setupIcon: '/path/to/icon.ico',
//其它 都设置icon: '/path/to/icon.png'
}
},
//多平台
{
"name": "@electron-forge/maker-zip",
"platforms": ["darwin", "linux", "windows"],
"config": {}
}
]
}
// package.json 中添加多平台
"config": {
"forge": {
"makers": [
{
"name": "@electron-forge/maker-zip",
"platforms": ["darwin", "linux", "windows"],
"config": {}
}
]
}
},
macos .icns 1024x1024 Windows .ico 256x256 linux png 512x512
//window macos
module.exports = {
packagerConfig: {
icon: '/path/to/icon' // no file extension required
}
}
//linux
{
name: '@electron-forge/maker-deb',
config: {
options: {
icon: '/path/to/icon.png'
}
}
}
images/
├── icon.png
├── icon@2x.png
└── icon@3x.png
- forge 组合了electron-packager、 @electron/osx-sign、electron-winstaller 等
vscode 调试
{
"version": "0.2.0",
"compounds": [
{
"name": "Electron",
"configurations": ["NodeJS", "Renderer"],
"stopAll": true
}
],
"configurations": [
{
"name": "Renderer",
"port": 9222,
"request": "attach",
"type": "chrome",
"webRoot": "${workspaceFolder}"
},
{
"name": "NodeJS",
"type": "node",
"request": "launch",
"cwd": "${workspaceFolder}",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd"
},
"args": [".", "--remote-debugging-port=9222"],
"outputCapture": "std",
"console": "integratedTerminal"
}
]
}
问题
- 主进程是NodeJS 无法直接操作dom即无法访问渲染器上下文
- 使用预加载完成多进程通信preload.js
非前端 Electron+TypeScript+Webpack不带vue
npm init
#安装electron依赖库
npm install --save-dev electron
npm install --save-dev webpack webpack-cli
#electron需要单独打包主进程和渲染进程,共用配置需要抽取需要merge合并共用和私有
npm install --save-dev webpack-merge
npm install --save-dev webpack-node-externals 打包过程中排除node_modules
#webpack的loader
#解决css打包
npm install --save-dev css-loader style-loader
#解决native的node文件打包
npm install --save-dev node-loader
#解决typescript文件打包
npm install --save-dev ts-loader
#解决其它文件打包
npm install --save-dev file-loader
#wepack 打包html的plugin
npm install --save-dev html-webpack-plugin
npm install --save-dev typescript
#typescript类型库,node库和webpack库 @types/electron不需要, electron模块已经自带了
npm install --save-dev @types/node @types/webpack
#打包工具
npm install --save-dev @electron-forge/cli
#写代码
#创建tsconfig.json 或执行下面命令
tsc init
#编辑配置并执行调试
npm run webpack-main #执行主进程打包
npm run webpack-render #执行渲染进程打包
npm start
#打包
npm exec --package=@electron-forge/cli -c "electron-forge import"
npm run package
npm run make
//tsconfig.json
{
"compilerOptions": {
"target": "ES2018",
"module": "CommonJS",
"lib": [ "dom", "esnext" ],
"strict": true,
"noImplicitReturns": true,
"moduleResolution": "Node",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"allowJs": true,
"resolveJsonModule": true
},
"exclude": [ "node_modules" ],
"include": [ "src/main", "src/renderer" ]
}
//package.json scripts节添加:
"webpack-main": "webpack --config webpack.main.config.js",
"webpack-render": "webpack --config webpack.render.config.js"
//webpack.base.config.js
module.exports = {
module: {
rules: [
// typescript
{
test: /\.ts$/,
use: 'ts-loader',
exclude: /node_modules/
},
// css (style loader在前 css loader在后)
{
test:/\.css$/,
use: [ 'style-loader', 'css-loader'],
exclude: /node_modules/
},
// .node native (这里例子中用不到)
{
test: /\.node$/,
exclude: /node_modules/,
use: 'node-loader'
}
]
}
};
//webpack.main.config.js
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const nodeExternals = require('webpack-node-externals');
const webpackBaseConfig = require('./webpack.base.config');
module.exports = merge(webpackBaseConfig, {
mode: 'development',
target: 'node',
entry: path.join(__dirname, 'src/main/main.ts'),
output: {
// 注意输出目录
path: path.join(__dirname, 'dist/main'),
filename: 'main.js'
},
externals: [nodeExternals()],
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: 'development'
})
],
node: {
__dirname: false,
__filename: false
}
});
//webpack.render.config.js
const path = require('path');
const webpack = require('webpack');
const { merge } = require('webpack-merge');
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpackBaseConfig = require('./webpack.base.config');
module.exports = merge(webpackBaseConfig, {
mode: 'development',
target: 'electron-renderer',
entry: path.join(__dirname, 'src/render/render.ts'),
output: {
path: path.join(__dirname, 'dist/render'),
filename: 'render.js'
},
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: 'development'
}),
new HtmlWebpackPlugin({
inject: 'body',
scriptLoading: 'defer',
minify: false,
filename: 'index.html',
template: path.join(__dirname, 'src/render/index.html')
}),
],
});