Electron-跨平台桌面开发

我的博客原文:http://localhost:4000/blog/2019/12/31/20191231112532671/

引言:

  • 使用 JavaScript,HTML 和 CSS 构建跨平台的桌面应用程序
  • Electron 基于 Chromium 和 Node.js
  • Electron 兼容 Mac、Windows 和 Linux,可以构建出三个平台的应用程序。

参考:

  • https://www.imooc.com/learn/1198

概述&启动

  • 使用html\css\js开发桌面跨平台应用(mac/linux/windows)
  • 基于Chromium和Nodejs(就是把浏览器一起给打包进去)
  • 国内案例:微信、迅雷桌面端
  • 使用的技术:nodejs\npm\前端三套

开发环境

node git npm

编辑器:微软的 Visual Studio Code

官方快速启动

# 克隆示例项目的仓库
$ git clone https://github.com/electron/electron-quick-start

# 进入这个仓库
$ cd electron-quick-start

# 安装依赖并运行
$ npm install && npm start

效果:

在这里插入图片描述

第一个Electron应用

主进程和渲染进程

由于 Electron 使用了 Chromium 来展示 web 页面,所以 Chromium 的多进程架构也被使用到。 每个 Electron 中的 web 页面运行在它自己的渲染进程中。

相当于浏览器窗口时主进程,每个tab是渲染进程。

主进程:

  • 使用和系统对接的Electron API, 就是调用系统API;
  • 创建渲染进程;
  • 全面支持nodejs
  • 只有一个,是程序的入口

渲染进程:

  • 多个,单独
  • 支持 nodejs\ dom api
  • 使用部分Electron API

BrowserWindow

程序入口:main.js

监控main文件变化

避免修改重启应用:

npm install nodemon --save-dev

修改mian.js

"scripts": {
    "start": "electron ."
}

改为:

"scripts": {
    "start": "nodemon --watch main.js --exec electron ."
}

main.js

// 加载模块
const { app, BrowserWindow } = require('electron')

// 当app加载结束时,回调函数
app.on('ready', ()=>{
  // 创建主窗口
  const mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      // 允许使用nodejs的api
      nodeIntegration: true
    }
  })
  // 加载文件
  mainWindow.loadFile('index.html')

  // 创建第二个窗口
  const secondWindow = new BrowserWindow({
    width: 400,
    height: 300,
    webPreferences: {
      nodeIntegration: true
    },
    // 父窗口关闭,子窗口也会关闭
    parent: mainWindow
  })
  secondWindow.loadFile('second.html')
}) 

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World! 2333</h1>
    <script>
      // 使用node语法
      require('./renderer.js')
    </script>
  </body>
</html>

renderer.js

// 可使用node api 和 dom api

// node api
alert(process.versions.node)


// dom api
window.addEventListener('DOMContentLoaded', () => {
    alert('dom加载完成')
})

进程间通信

  • 使用IPC 在进程间通信,Electron提供了接口

在这里插入图片描述

renderer process 和 main proces 互发消息

renderer.js

// 通过 renderer process 向 main proces 发送消息
const { ipcRenderer } = require('electron')

window.addEventListener('DOMContentLoaded', () => {
    // 发送
    ipcRenderer.send('message', 'hello from renderer')
    // 接收
    ipcRenderer.on('reply', (event, arg) => {
        // 打印到页面 
        document.getElementById('message').innerHTML = arg
    })
})

main.js

// ipcMain
const { app, BrowserWindow, ipcMain } = require('electron')

app.on('ready', ()=>{
  // ...
    
  ipcMain.on('message', (event, arg) => {
    // 接受
    console.log(arg)
    // 回复
    // event.sender.send('reply', 'hello from main')
    // 等价上面语法
    mainWindow.send('reply', 'hello from main')
  })
}) 

案例:本地音乐播放器

原型&功能点

主页

1、添加歌曲到曲库

2、播放暂停音乐

3、播放信息:歌名、时间、进度

4、删除歌曲

添加音乐页

1、选择音乐

2、列表展示

3、导入音乐:列表到主页

功能流程&文件结构

在这里插入图片描述

css样式使用bootstrap

npm install bootstrap --save

文件结构

在这里插入图片描述

功能-添加音乐窗口

首页开始

renderer/index.html

<!-- 主页面 -->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>本地播放器</title>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="../node_modules/bootstrap/dist/css/bootstrap.min.css">
  </head>
  <body>
    <div class="container mt-4">
        <h1>我的播放器</h1>
        <button type="button" class="btn btn-primary btn-lg btn-block mt-4">
            添加歌曲到曲库
        </button>
    </div>
  </body>
</html>

效果:

在这里插入图片描述

创建添加音乐窗口

index.html

<script>
    require('./index.js')
</script>

index.js

// 主窗口
const {ipcRenderer} = require('electron')
// 只有main process可以添加窗口,所以需要通信
document.getElementById('add-music-button').addEventListener('click', () => {
    ipcRenderer.send('add-music-window')
})

main.js

...
    ipcMain.on('add-music-window', () => {
    	// 添加窗口
        const addWindow = new BrowserWindow({
            width: 500,
            height: 400,
            webPreferences: {
                nodeIntegration: true
            },
            parent: mainWindow
        })
        addWindow.loadFile('./renderer/add.html')
    })

效果:
在这里插入图片描述

重构-封装创建窗口类

使用es6的class

main.js

// 封装创建窗口类
class AppWindow extends BrowserWindow {
  constructor(config, fileLocation) {
    const basicConfig = {
      width: 800,
      height: 600,
      webPreferences: {
        nodeIntegration: true
      }
    }

    // 使用config配置覆盖默认配置
    // const finalConfig = Object.assign(basicConfig, config)
    // 第二种写法
    const finalConfig = {...basicConfig, ...config}
    super(finalConfig)
    this.loadFile(fileLocation)
    // 优化:在加载页面时,渲染进程第一次完成绘制时,会发出 ready-to-show 事件 。 在此事件后显示窗口将没有视觉闪烁
    this.once('ready-to-show', () => {
      this.show()
    })
  }
}

// 重构代码
app.on('ready', ()=>{
  const mainWindow = new AppWindow({},'./renderer/index.html')
  
  ipcMain.on('add-music-window', () => {
    const addWindow = new AppWindow({
      width: 500,
      height: 400,
      parent: mainWindow
    },'./renderer/add.html')
  })
}) 

使用Dialog模块添加音乐文件

Dialog模块可以调用原生api,打开文件。

分装工具类helper.js

// 工具类
exports.$ = (id) => {
    return document.getElementById(id)
}

add.html

<script>
    require('./add.js')
</script>

add.js

// 添加音乐
const {ipcRenderer} = require('electron')
const { $ } = require('./helper')
// 重构
$('select-music-button').addEventListener('click', () => {
    ipcRenderer.send('open-music-file')
})

main.js

// 选择音乐
ipcMain.on('open-music-file', () => {
    dialog.showOpenDialog({
        properties: ['openFile', 'multiSelections'],
        filters: [
            { name: 'Music', extensions: ['mp3'] } 
        ]
    }).then(result => {
        console.log(result.filePaths)
    }).catch(err => {
        console.log(err)
    })
})

效果:

在这里插入图片描述

展示添加的文件列表

main.js

ipcMain.on('open-music-file', (event) => {
    dialog.showOpenDialog({
        // ...
    }).then(result => {
        if(result.filePaths){
            // 传递给add
            event.sender.send('selected-file', result.filePaths)
        } 
    })
}) 

add.html

<div id="musicList" class="mb-2">您还未选择任何音乐文件</div>

add.js

const renderListHTML = (pathes) => {
    const musicList = $('musicList')
    const musicItemsHTML = pathes.reduce((html, music) => {
         html += `<li class="list-group-item">${path.basename(music)}</li>`
         return html
    }, '')

    musicList.innerHTML = `<ul class="list-group">${musicItemsHTML}</ul>`
}

ipcRenderer.on('selected-file', (event, path) => {
    if(Array.isArray(path)){
        // 渲染歌曲List
        renderListHTML(path)
    }
})

效果:
在这里插入图片描述

使用Electron store持久化数据

  • 数据库软件(太大)

  • HTML5的浏览器对象(缓存会过期)

  • 本地文件

    提供了api模块electron-store: https://github.com/sindresorhus/electron-store

安装

npm install electron-store --save

使用:

封装-持久化存储类

安装uuid库

 npm install uuid --save

MusicDataStore.js

const Store = require('electron-store')
const uuidv4 = require('uuid/v4')
const path = require('path')

class DataStore extends Store {
    constructor(settings) {
        super(settings)
        this.tracks = this.get('tracks') || []
    }

    // 保存音乐文件
    saveTracks() {
        this.set('tracks', this.tracks)
        return this
    }
    
    // 获取音乐信息
    getTracks() {
        return this.get('tracks') || []
    }

    // 添加音乐信息
    addTracks(tracks) {
        // id 文件路径 文件名称 去重
        const tracksWithProps = tracks.map(track => {
            return {
                id: uuidv4(),
                path: track,
                fileName: path.basename(track)
            }
        }).filter(track => {
            const currentTacksPath = this.getTracks().map(track => track.path)
            return currentTacksPath.indexOf(track.path) < 0
        })
        this.tracks = [...thid.tracks, ...tracksWithProps]
        return this.saveTracks();
    }
}

module.exports = DataStore

使用存储类保存数据

查看数据库存储地址

console.log(app.getPath('userData'))
// C:\Users\Machine\AppData\Roaming\electron-quick-start

add.js

$('add-music-button').addEventListener('click', () => {
    ipcRenderer.send('add-tracks',musicFilesPath)
})

main.js

// 存储数据
ipcMain.on('add-tracks', (event,tracks) => {
    const updatedTracks = myStore.addTracks(tracks).getTracks()
    console.log(updatedTracks)
})

效果:C:\Users\Machine\AppData\Roaming\electron-quick-start\Music Data.json

{
	"tracks": [
		{
			"id": "cc3a3aee-3533-492e-b3da-8cc524db7420",
			"path": "C:\\Users\\Machine\\Desktop\\mp3_test_music\\bad guy.mp3",
			"fileName": "bad guy.mp3"
		},
		{
			"id": "650f80d2-9717-45d2-91b7-d355de85d95a",
			"path": "C:\\Users\\Machine\\Desktop\\mp3_test_music\\Lemon.mp3",
			"fileName": "Lemon.mp3"
		},
		{
			"id": "94b211e2-6d55-4f97-b272-0e4b6e8286db",
			"path": "C:\\Users\\Machine\\Desktop\\mp3_test_music\\起风了.mp3",
			"fileName": "起风了.mp3"
		}
	]
}

功能-播放器窗口

TODO

获取数据 渲染主窗口列表

主窗口播放音乐

应用打包与分发

  • Electron builder: https://github.com/electron-userland/electron-builder
npm install electron-builder --save-dev

配置参考:

https://www.electron.build/configuration/win#WindowsConfiguration-target
https://github.com/zulip/zulip-electron/blob/master/package.json

package.json

{
  "scripts": {
    "dist": "electron-builder --win --x64"
  },
  "build": {
    "appId": "com.xxx.app",
    "win": {
      "target": ["nsis","zip"],
      "icon": "build/icon_256_multi.ico"
    }
  }
}

ico制作需要俄罗斯套娃的ico格式,否则有些图标显示不出来,参考如何制作俄罗斯套娃一般的 electron 专用 ico 图标

运行:

npm run dist

在dist目录下会生成安装包

Electron+Vue

electron-vue脚手架+elementUI

参考:

https://www.cnblogs.com/adorkable/p/11069923.html
https://simulatedgreg.gitbooks.io/electron-vue/content/cn/
https://element.eleme.cn/#/zh-CN/component/installation

electron本就是一个加强版的浏览器的外壳,所以结合前端框架是很当然的~

这里构建一个Electron+Vue+Element首页布局效果。

有人写了脚手架electron-vue,基于vue-cli的,所以直接用吧~

# 安装 vue-cli 和 脚手架样板代码
npm install -g vue-cli
vue init simulatedgreg/electron-vue my-project

# 安装依赖并运行你的程序
cd my-project
npm install
npm run dev

报错了…process is not defined…

好像是node版本太高的问题,不想改node版本,所以找了解决方案如下:

https://www.jianshu.com/p/bdf0a23e7257

解决bug后,跑起来能看到界面了

在这里插入图片描述

接下来就是加入element:参考https://element.eleme.cn/#/zh-CN/component/quickstart

安装

npm i element-ui -S

完整引入:

src/renderer/main.js中增加

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

表格控件显示不正常的解决:element-ui需要加入到白名单里面

.electron-vue/webpack.renderer.config.js,改为

let whiteListedModules = ['vue','element-ui']

css初始化样式重置,使用normalize.css

npm install normalize.css --save

main.js

import 'normalize.css/normalize.css'

修改src/renderer/components/LandingPage.vue,就是修改主页了,使用ElementUI的元素。

效果:

在这里插入图片描述

electron-vue-admin脚手架

参考:

文档:https://panjiachen.github.io/vue-element-admin-site/zh/
项目地址:https://github.com/PanJiaChen/electron-vue-admin

这个脚手架是 electron+vue+ElementUI整合的后台模板。拥有菜单+导航的基本功能。

# install dependencies
npm install

# serve with hot reload at localhost:9080
npm run dev

# build electron app for production
npm run build

# lint all JS/Vue component files in `app/src`
npm run lint

# run webpack in production
npm run pack

效果:

在这里插入图片描述

进入:

在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值