背景
接了一个项目需要开发一个功能简单的桌面端应用,主要包含的功能有 内置数据,本地化操作数据,对数据进行CRUD操作。
 效果展示如下:
 
 
技术选型:
-  开发桌面端有如下几种技术方案:** Electron:使用HTML、CSS和JS构建跨平台的桌面应用程序,基于Chromium和Node.js。 NW.js:(也称node-webkit)类似于Electron。 React Native:使用React和JS。 Flutter:使用Dart编程语言,可构建高度定制的桌面应用程序。 本文这里选择Electron。
-  “vue”: “^3.2.47”, 
 “vue-router”: “^4.1.6”,
 “ant-design-vue”: “^4.0.0”,
 “lowdb”: “^1.0.0”, //本地化存储数据插件
项目搭建
本项目使用Vue Cli 创建Vue项目,命令如下:
npm i @vue/cli -g
vue create desk
cd desk
vue add electron-builder
npm run electron:serve
运行之后效果:
 
 项目目录如下:
 和vue项目目录差不多,差异的是electron主进程的代码是放在background.js中。
 
项目难点:
一.Electron 实现对数据进行本地持久化存储方案选择——lowdb
 
Electron 是一个用于构建跨平台桌面应用程序的开源框架,它允许你使用前端技术(HTML、CSS 和 JavaScript)创建桌面应用程序。如果你想在 Electron 应用程序中对数据进行本地持久化存储,你可以使用以下方法:
-  使用 Electron 的内置 API: Electron 提供了一些内置的 API,可以让你轻松地进行本地数据持久化存储,最常见的是使用 localStorage和sessionStorage,类似于在浏览器中的用法。这种方法适合于存储少量数据,例如配置信息或用户首选项。示例: // 本地存储数据 localStorage.setItem('key', 'value'); // 从本地获取数据 const data = localStorage.getItem('key'); // 删除数据 localStorage.removeItem('key');
-  使用 Node.js 模块: Electron 可以使用 Node.js 模块,这意味着你可以使用 Node.js 提供的文件系统和其他模块来进行更高级的本地数据持久化存储。最常见的方式是使用 fs模块来读写文件。示例: const fs = require('fs'); // 写入数据到本地文件 fs.writeFile('data.txt', 'Hello, Electron!', (err) => { if (err) throw err; console.log('数据已写入文件'); }); // 从本地文件读取数据 fs.readFile('data.txt', 'utf8', (err, data) => { if (err) throw err; console.log('从文件中读取的数据:', data); });
-  使用数据库: 如果你需要存储大量结构化数据,你可以考虑使用一种数据库系统,如 SQLite、IndexedDB 或 LevelDB。SQLite 特别适合桌面应用程序,因为它是一个嵌入式数据库引擎,可以轻松地集成到 Electron 应用中。 示例(使用 SQLite): const sqlite3 = require('sqlite3').verbose(); const db = new sqlite3.Database('mydatabase.db'); // 创建表并插入数据 db.serialize(() => { db.run('CREATE TABLE IF NOT EXISTS users (id INT, name TEXT)'); db.run('INSERT INTO users VALUES (1, "John")'); }); // 查询数据 db.all('SELECT * FROM users', (err, rows) => { if (err) throw err; console.log('查询结果:', rows); }); // 关闭数据库连接 db.close();
以上是在 Electron 应用程序中进行本地持久化存储的一些常见方法。
本项目使用
lowdb,lowdb是一个轻量级的本地JSON数据库,适合小型项目和简单数据持久化需求。
二.lowdb的基本使用规则:
 
-  安装和导入: 在您的Electron项目中,首先确保安装了 lowdb和lodash依赖:npm install lowdb lodash然后在您的JavaScript文件中导入 lowdb和lodash:const low = require('lowdb'); const FileSync = require('lowdb/adapters/FileSync'); const adapter = new FileSync('db.json'); // 指定数据文件 const db = low(adapter);
-  初始化数据库: 在初始化 lowdb时,您可以使用defaults方法来指定初始数据和默认值:db.defaults({ users: [] }).write();这将创建名为 users的初始数据对象,如果数据文件中不存在该对象,将使用默认值。
-  插入数据: 使用 push方法将数据插入到数据库中:db.get('users') .push({ id: 1, name: 'Alice' }) .write();
-  查询数据: 使用 get方法和value方法查询数据:const users = db.get('users').value();
-  更新数据: 使用 find方法和assign方法来更新数据:db.get('users') .find({ id: 1 }) .assign({ name: 'Alicia' }) .write();
-  删除数据: 使用 remove方法来删除数据:db.get('users') .remove({ id: 1 }) .write();
-  链式操作: lowdb支持链式操作,您可以在一个语句中执行多个操作,例如插入、查询和更新数据:db.get('users') .push({ id: 2, name: 'Bob' }) .find({ id: 2 }) .assign({ name: 'Bobby' }) .write();
-  使用过滤器: 您可以使用 filter方法来过滤数据:const filteredUsers = db.get('users') .filter(user => user.name.includes('B')) .value();
了解更多详细信息可看lowdb的文档。
三. 读取本地数据
- background.js文件中实现本地数据读取代码如下:
import LodashId from 'lodash-id'
import FileSync from 'lowdb/adapters/FileSync'
import fs from 'fs-extra'
import localDb from './local-db.json'
const APP = process.type === 'renderer' ? remote.app : app
const STORE_PATH = APP.getPath('userData')  //获取electron应用的用户目录
// 判断路径是否存在,若不存在,就创建一个新的
if (process.type !== 'renderer') {
  if (!fs.pathExistsSync(STORE_PATH)) {
    fs.mkdirpSync(STORE_PATH)
  }
}
const adapter = new FileSync(path.join(STORE_PATH, 'localData.json'))
let db = Datastore(adapter) // lowdb接管该文件数据
db._.mixin(LodashId)  //LodashId自动生成id
db.defaults(localDb).write() // 一定要显式调用write方法将数据存入JSON
四. 对数据进行操作,就涉及到主进程与渲染进程之间进行通信
- background.js文件中监听渲染进程的请求代码如下:
// 监听渲染进程的请求
ipcMain.on('ipc-getSurfaceData', (event) => {
  // 执行数据库写入操作
  try {
    const data = db.get('SurfaceData').value()
    // 读取成功的响应给渲染进程
    event.sender.send('getSurface', { success: true, res: data });
  } catch (error) {
    // 发送写入失败的响应给渲染进程
    return event.sender.send('getSurface', { success: false, error: error.message });
  }
});
- 渲染进程 src/api/service.js 发送请求给主进程执行数据库写入操作如下:
 这里有点类似mock 模拟发送请求,不过这里是向主进程发送请求,因为只有主进程才有对本地数据进行增删改查的操作。
export function getSurfaceData() {
  // 发送请求给主进程执行数据库写入操作
  ipcRenderer.send('ipc-getSurfaceData');
  return new Promise((resolve, reject) => {
    // 监听来自主进程的响应
    ipcRenderer.on('getSurface', (event, res) => {
      if (res.success) {
        resolve({
          code: 200,
          data: res.res
        })
      } else {
        return reject({
          code: 400,
          message: res.error
        })
      }
    });
  })
}
- vue文件下调用渲染进程的方法,代码如下:
const getSurface = async () => {
  const res = await getSurfaceData()
  if (res.code == 200) {
    SurfaceData.value = res.data
    innerSurfaceData.value = SurfaceData.value.filter(
      (item) => item.type == 'inner'
    )
    outSurfaceData.value = SurfaceData.value.filter((item) => item.type == 'out')
  } else {
    message.error(res.message)
  }
}
以上就是使用lowdb对本地数据进行操作的具体实现。
五. 打包相关配置
在什么系统下打包就会生成相应系统下的桌面端,也可以执行命令去打包相应的桌面端
page.json 添加如下打包命令:
“electron:build:win32”: “vue-cli-service electron:build --mode --win --ia32”,
“electron:build:win64”: “vue-cli-service electron:build --mode --win --x64”,
“electron:build:mac”: “vue-cli-service electron:build --mode --mac”,
- vue.config.js 文件代码如下:
module.exports = {
  lintOnSave: true,
  productionSourceMap: false,
  pluginOptions: {
    electronBuilder: {
      nodeIntegration: true,
      chainWebpackMainProcess: (config) => {
        config.output.filename((file) => {
          if (file.chunk.name === 'index') {
            return 'background.js';
          } else {
            return '[name].js';
          }
        });
      },
      builderOptions: {
        appId: 'com.electron.htc',//包名
        buildVersion: '20230726',
        productName: 'xxx软件',//生成exe的名字
        copyright: 'Copyright © 2023- year',
        icon: 'static/favicon.ico',
        extraFiles: [
          {
            from: 'src/assets/favicon.ico',
            to: 'static/favicon.ico',
          },
        ],
        win: {
          icon: 'src/assets/favicon.ico',
          executableName: 'xxx软件',
          requestedExecutionLevel: 'requireAdministrator',
        },
        nsis: {
          oneClick: false,// 是否一键安装
          perMachine: true, //代表是否显示辅助安装程序的安装模式安装程序页面(选择按机器还是按用户)。true时代表始终按用户安装。
          artifactName:
            'xxx软件V${version}_Build${buildVersion}.${ext}',
          uninstallDisplayName: 'xxx软件 ${version}',
          allowToChangeInstallationDirectory: true, //是否允许修改安装目录
          createDesktopShortcut: true, // 是否创建桌面图标
          createStartMenuShortcut: true,// 是否创建开始菜单图标
          shortcutName: "xxx软件", // 快捷方式名称
          runAfterFinish: false,//是否安装完成后运行
          menuCategory: 'xxx有限公司',
        },
      },
    },
    // chainWebpackMainProcess: (config) => {
    //   config.output.filename('background.js');
    // }
  },
  css: {
    loaderOptions: {
      less: {
        lessOptions: {
          javascriptEnabled: true,
        },
      },
    },
  },
}
这就是我第一次开发桌面端的经历,搭建的项目复杂程度仅供小型桌面端使用。复杂一些的项目,就需要考虑主进程再去细分,一个background.js难以满足。详细的可以看electron的官方文档!!!



















