# Electron 桌面应用
本章节以 Mac OS X下 Hosts 文件管理 App 为例。
技术栈包含:
* Electron (仅 OS X 平台)
* Webpack 2
* Vue 2
* Vuex
* Vue-Router
* Vue-Material (UI组件)
* Babel
* ESLint
* Yarn(包管理)
## 初始化项目
* 安装所需的各种 NPM 包。
* 引入 ESLint、Babel 配置文件。
## 配置 Vue2 + Webpack2
需要注意`Webpack` 2.x.x 版本与 1.x.x 版本发生了不少改动。可以参考迁移文档: 进行学习。
### 坑1: extract-text-webpack-plugin
该插件 NPM 最新版本为1.0.1,不支持 Webpack2,所以需要通过安装 RC3 版本来获得对应支持。
```
yarn add --dev extract-text-webpack-plugin@2.0.0-rc3
或
npm i --save-dev extract-text-webpack-plugin@2.0.0-rc3
```
### 坑2:babel-plugin-transform-runtime 与 extract-text-webpack-plugin 插件有冲突
会导致 extract-text 插件报错崩溃。
解决方法,修改 `.babelrc`:
```json
{
"presets": [
"latest"
],
"plugins": [
[
"transform-runtime",
{
"helpers": false,
"polyfill": false,
"regenerator": true
}
]
],
"compact": true
}
```
其中 设置`compact`属性还能阻止 500kb 限制的警告。
### 坑3:postcss-loader 与 extract-text-webpack-plugin 插件有冲突
目前无解,去掉了`postcss-loader`的使用。
下一阶段尝试。
### 坑4:在 Vue 中使用 Electron
首先,需要在 webpack config 中设置:
```yaml
target: 'electron'
```
向 Electron 注册插件:
```js
import Vue from 'vue';
import electron from 'electron';
Vue.use({
install: (vue) => {
vue.prototype.$electron = electron;
}
});
```
前端向 IPC 发送消息:
```js
this.$electron.ipcRenderer.send('resizePreferencesWindow', { width: 400, height: 300 });
```
## Electron 配置
### 设置 App 开机自启
使用插件:
```js
import AutoLaunch from 'auto-launch';
const hostsAutoLauncher = new AutoLaunch({
name: 'Hosts.js',
path: '/Applications/Hosts.js.app'
});
exports.isEnabled = async () => {
const result = await hostsAutoLauncher.isEnabled();
return result;
};
exports.enable = () => {
hostsAutoLauncher.enable();
};
exports.disable = () => {
hostsAutoLauncher.disable();
};
```
### 添加 Electron Vue DevTools
可以直接通过插件实现,地址:
devtools.js:
```js
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer';
module.exports = installExtension(VUEJS_DEVTOOLS);
```
窗口加载:
```js
if (process.env.NODE_ENV === 'development') {
// 开发模式加载 devtools
const devtools = require('../lib/devtools');
devtools.then(() => { // 注意调用方式
this.preferencesWindow.loadURL('http://localhost:3000/build/app.html');
});
} else {
this.preferencesWindow.loadURL(`file://${path.join(__dirname, '../../build/app.html')}`);
}
```
## 设计核心模块
### Hosts 分级列表
特点:
* 支持顶级项目
* 支持二级目录项目
* 目录不含有启动状态,目录下的子项目有
* 支持排序
```js
[
{
name: 'Default',
order: 2,
id: 'default',
type: 'item',
enabled: true
},
{
namename: 'Folder',
order: 1,
id: 'xxx1',
type: 'folder',
children: [
{
name: 'Sub Item 2',
order: 2,
id: 'xxxs2',
type: 'item',
enabled: false
},
{
name: 'Sub Item 1',
order: 1,
id: 'xxxs1',
type: 'item',
enabled: true
},
{
name: 'Sub Item 4',
order: 4,
id: 'xxxs4',
type: 'item',
enabled: true
},
{
name: 'Sub Item 3',
order: 3,
id: 'xxxs3',
type: 'item',
enabled: true
}
]
}
]
```
### 排序算法
1. 对顶级项目进行排序
2. 对子菜单项目进行排序
```js
const reorderItems = arr => arr.sort((x, y) => x.order - y.order > 0 ? 1 : -1).map((i, iIndex) => {
i.order = iIndex + 1;
if (i.type === 'folder') {
i.children = i.children.sort((x, y) => x.order - y.order > 0 ? 1 : -1).map((j, jIndex) => {
j.order = jIndex + 1;
return j;
});
}
return i;
});
```
### 删除元素算法
1. 默认项禁止删除
2. 编程过程中的异常捕获,实际操作中不会发生
3. 子菜单超过两个项目禁止删除
```js
const deleteItem = (arr, id, pid = '') => {
if(id==='default') return false;
let index;
if (pid === '') {
index = arr.findIndex((x) => x.id === id);
// 异常捕获
if (index === -1) return false;
// 子菜单超过两个项目禁止删除
if (typeof arr[index].children === 'object' && arr[index].children.length > 1) return false;
arr.splice(index, 1);
return reorderItems(arr);
}
index = arr.findIndex((x) => x.id === pid);
// 异常捕获
if (index === -1) return false;
arr[index].children = deleteItem(arr[index].children, id);
return reorderItems(arr);
};
```
### 添加元素算法
1. 不能建立二级目录
2. 编程过程中的异常捕获,实际操作中不会发生
```js
const uuid = require('uuid');
const insertItem = (arr, name, pid = '', type = 'item') => {
if (type === 'folder' && pid !== '') return false;
const item = {
name,
order: -1,
id: uuid.v4(),
};
if (type === 'item') {
item.enabled = false;
} else {
item.children = [];
}
if (pid === '') {
item.order = arr.length;
arr.push(item);
} else {
const index = arr.findIndex((x) => x.id === pid);
// 异常捕获
if (index === -1) return false;
item.order = arr[index].length;
arr[index].children.push(item);
}
return reorderItems(arr);
};
```
### 位置调整算法
#### 上移
#### 下移
### 优化
* 以 class 形式封装
* 抛出简单的外部接口
---
Hosts.js项目源码: