# demo-electron-react **Repository Path**: topyangwang/electron-react ## Basic Information - **Project Name**: demo-electron-react - **Description**: electron + cra搭建的读取本地文件的demo - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-11-04 - **Last Updated**: 2023-02-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### electron + car 读取本地文件模版 #### 一、创建react项目 ```javascript npx create-react-app electron-react --template typescript yarn start ``` #### 二、创建Electron基本应用程序 Electron 官方提供了一个名为electron-quick-start 的项目, 可以 clone 下来当成模版使用:https://github.com/electron/electron-quick-start ##### 1. 安装 electron ```javascript yarn add electron -D ``` ##### 2. 修改package.json文件 ```javascript "main": "./src/main/main.js", // 配置启动文件 "scripts": { ... "main": "electron ." // electron 启动命令 "main-dev": "electron . dev", // 后面会根据传入的这个参数,在主进程中做一层判断 "dev": "yarn start & yarn main-dev", }, ``` ##### 3. 创建 electron 主进程的文件 `main.js`。 1. 拷贝electron-quick-start中的 `main.js` 到 `src/main/mian.js`。 2. 修改如下: ```javascript // 1. 引入node的 path 和url模块 const path = require('path') const url = require('url'); // 2. 获取在 package.json 中的命令脚本传入的参数,来判断是开发还是生产环境 const mode = process.argv[2]; // 3. 在createWindow函数中,创建了主窗体之后根据参数去加载不同的url到electron中: const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { // 网页功能设置 preload: path.join(__dirname, 'preload.js'), } }) if(mode === 'dev') { mainWindow.loadURL("http://localhost:3000/") } else { mainWindow.loadURL(url.format({ pathname: path.join(__dirname, '../../dist/index.html'), protocol: 'file:', slashes: true })) } ``` ##### 4. 添加 preload.js 文件 将quick-start工程中直接将这个文件copy过来即可。 ##### 5. 启动Electron ```javascript yarn main-dev ``` #### 三、读取本地文件 ##### 1. 给渲染进程注入electron API main.js ```javascript const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { // 网页功能设置 preload: path.join(__dirname, 'preload.js'), nodeIntegration: true, // 是否启用node集成 渲染进程的内容有访问node的能力 } }) ``` preload.js ```javascript const { contextBridge, ipcRenderer } = require('electron'); // 将 electron 变量挂载到 window 上 contextBridge.exposeInMainWorld('electron', { ipcRenderer: { sendMessage(channel, args) { ipcRenderer.send(channel, args); }, on(channel, func) { const subscription = (_event, ...args) => func(...args); ipcRenderer.on(channel, subscription); return () => { ipcRenderer.removeListener(channel, subscription); }; }, once(channel, func) { ipcRenderer.once(channel, (_event, ...args) => func(...args)); }, }, }); ``` ##### 2. 进程通信,读取文件 1. App.js:发送读取文件请求 ```javascript // 给主进程发送信息 const getUser = ()=> { // "getUser": channel名字; "获取用户列表": 数据 window.electron.ipcRenderer.sendMessage('getUser', '获取用户列表'); } ``` 2. 创建 `readFile.js` 文件、`mock/test.json` ```javascript // readFile.js const fs = require('fs') function readFile(path){ return new Promise((resolve, reject)=>{ fs.readFile(path, { flag: 'r', encoding:'utf-8' }, (err, data) => { if (err) { reject('出错啦'); } resolve(data); }); }) } module.exports = { readFile } // test.json [ { "name": "张三", "age": 18, "sex": "boy" }, { "name": "李四", "age": 47, "sex": "boy" } ] ``` 3. main.js:监听请求、读取文件、并返回内容 ```javascript const { ipcMain } = require('electron'); const { readFile } = require('./readFs'); // 监听 getUser 频道 ipcMain.on('getUser', (event, arg) => { let con = readFile(path.resolve(__dirname, '../../mock/test.json')); // 读取问文件内容 // "userList": channel名称, res: 数据 con.then(res => { event.sender.send('userList', res); // 发送文件信息 }).catch(err=> { event.sender.send('userList', []) }) // event.reply('userList', res); // event.returnValue = 'pong' }) ``` 4. App.js:监听请求,接收信息 ```javascript const listionUser = () => { // 监听 userList 频道 return window.electron.ipcRenderer.on('userList', (data: any)=> { console.log('userList', JSON.parse(data)); }) } useEffect(() => { listionUser(); }, []) ``` #### 四、打包发布: electron-builder ```javascript yarn add electron-builder -D // package.json "scripts": { "pack-win": "electron-builder --win --x64", "pack-mac": "electron-builder --mac", }, "build": { "productName": "测试项目", "appId": "test.app", "extends": null, "directories": { // 输出文件夹 "output": "build" }, // 此属性一定要将用到的文件全部包含进去 "files": [ "dist/**/*", "src/main/**/*", "mock/**/*", "package.json" ], "mac": { "target": [ "dmg", "zip" ], }, "win": { "target": "nsis", "icon": "icons/icon.png" } }, ``` 1. `yarn build` 2. `yarn pack-mac` build其他配置 ```javascript "build": { "productName":"xxxx",//项目名 这也是生成的exe文件的前缀名 "appId": "com.leon.xxxxx",//包名 "copyright":"xxxx",//版权 信息 "directories": { // 输出文件夹 "output": "build" }, "nsis": { "oneClick": false, // 是否一键安装 "allowElevation": true, // 允许请求提升。 如果为false,则用户必须使用提升的权限重新启动安装程序。 "allowToChangeInstallationDirectory": true, // 允许修改安装目录 "installerIcon": "./build/icons/aaa.ico",// 安装图标 "uninstallerIcon": "./build/icons/bbb.ico",//卸载图标 "installerHeaderIcon": "./build/icons/aaa.ico", // 安装时头部图标 "createDesktopShortcut": true, // 创建桌面图标 "createStartMenuShortcut": true,// 创建开始菜单图标 "shortcutName": "xxxx", // 图标名称 "include": "build/script/installer.nsh", // 包含的自定义nsis脚本 }, "publish": [ { "provider": "generic", // 服务器提供商 也可以是GitHub等等 "url": "http://xxxxx/" // 服务器地址 } ], } ``` #### 五、目录结构 ```javascript . ├── README.md ├── config-overrides.js ├── mock │   └── test.json ├── package.json ├── public │   ├── favicon.ico │   └── index.html ├── src │   ├── App.css │   ├── App.test.tsx │   ├── App.tsx │   ├── index.css │   ├── index.tsx │   └── main │      ├── main.js // 主程序入口 │      ├── preload.js │      └── readFs.js ├── tsconfig.json ├── types │   └── typing.d.ts └── yarn.lock ``` #### 六、electront详解 ##### Electron 架构和 Chromium 架构类似,也是具有1个主进程和多个渲染进程。 ###### 主进程 Electron 运行 package.json 的 main 脚本的进程被称为主进程 (主进程只有一个)。 Electron主进程的具体职责: 1. 主进程连接着操作系统和渲染进程,可以把她看做页面和计算机沟通的桥梁; 2. 进程间通信、窗口管理 4. 一些只能或适合在主进程做的事情(例如浏览器下载、全局快捷键处理、托盘、session); 5. 维护一些必要的全局状态。 ###### 渲染进程 渲染进程就是我们所熟悉前端环境了,只是载体改变了,从浏览器变成了window。 出于安全考虑,渲染进程是不能直接访问本地资源的,因此都需要在主进程完成。 Electron渲染进程主要特点: 1. Electron 使用了 Chromium 来展示 web 页面,所以 Chromium 的多进程架构也被使用到; 2. 每个web页面运行在它自己的渲染进程中(每个渲染进程都是相互独立的,并且只关心他们自己的网页); 3. 使用BrowserWindow类开启一个渲染进程并将这个实例运行在该进程中,当一个BrowserWindow实例被销毁后,相应的渲染进程也会被终止; 4. 渲染进程中不能调用原生资源,但是渲染进程中同样包含Node.js环境,所以可以引入Node.js。 > https://zhuanlan.zhihu.com/p/566977351