Electron+vue-cli3開發跨平台桌面應用


一步一步搭建Electron+vue-cli3開發平台(親測有效)

Electron是一個基於Chromium和 Node.js,可以使用 HTML、CSS和JavaScript構建跨平台應用的技術框架,兼容 Mac、Windows 和 Linux。雖然B/S是目前開發的主流,但是C/S仍然有很大的市場需求。受限於瀏覽器的沙盒限制
[^沙盒(英語:sandbox,又譯為沙箱),計算機專業術語,在計算機安全領域中是一種安全機制,為運行中的程序提供的隔離環境。通常是作為一些來源不可信、具破壞力或無法判定程序意圖的程序提供實驗之用。]
網頁應用無法滿足某些場景下的使用需求,而桌面應用可以讀寫本地文件、調用更多系統資源,再加上Web開發的低成本、高效率的優勢,這種方式越來越受到開發者的喜愛。

這篇博客一步一步教你如何使用Electron和vue-cli3,在完全保留vue開發web應用的習慣下,搭建桌面應用。

這篇博客不涉及Electron和vue的開發教程,僅以實現兩者結合為目的,若要深入學習Electron和vue,請訪問官方:

Electron Vue Vue-cli Stylus

學習該框架之前,希望讀者需要提前掌握以下技能:

HTML、CSS、JavaScript Vue2.x Stylus

1.創建項目

1.1 安裝Nodejs

Node.js 中文網下載安裝

1.2 使用cnpm加速下載

npm有時下載速度很慢,可以安裝cnpm,從國內淘寶鏡像下載,執行以下命令:

$ npm install -g cnpm --registry=https://registry.npm.taobao.org

之后所有的npm命令都可以直接替換成cnpm使用了。

1.3 為什么不使用electron-vue搭建呢

Electron-vue官方中文文檔

由於SimulatedGREG/electron-vue已經很久沒有更新了,而且其生成的工程結構並不是vue-cli3。所以放棄使用。

1.4 安裝/升級vue-cli3

  • 先執行以下命令,確認下本地安裝的vue-cli版本(如果自己的電腦沒有安裝過vue,跳過前兩步到第三步吧!):

$ vue -V$ vue --version

在寫本篇博客時,我的vue是2.9.6版本。

  • 如果本地使用的是vue-cli2.x或者更早版本,可先執行下面命令全局卸載:

$ cnpm uninstall vue-cli -g

(1)vue-cli3使用了新的npm包名,與舊版本不一樣。

(2)在vue2.9.6下執行上面命令,控制台輸入vue -V仍然有輸出,解決方法:

​ a. 當然, 卸載命令還是要執行的: 全局卸載:npm uninstall vue-cli -g;

​ b. npmrc文件刪除掉

​ c. 檢索刪除vue-cli文件夾

​ 再試試: vue -V,就不顯示版本號了。

(3)可以忽略上述問題,直接安裝最新版本的vue-cli3即可(親測有效)。

  • 執行以下命令全局安裝vue-cli3:

$ cnpm install @vue/cli -g

  • 執行以下命令升級vue-cli3:

$ cnpm update @vue/cli -g

1.5 創建vue項目

  • 在指定目錄下,打開終端,執行以下命令,創建vue項目:

$ vue create electron-vue-demo

這里的項目名稱為electron-vue-demo(不能出現大寫字母),可根據自己的具體項目改變。

  • 創建命令執行后,在完成創建之前,會出現以下選項(如果熟悉此步驟可跳過本節內容):
Vue CLI v4.4.6
? Please pick a preset: (Use arrow keys)
  default (babel, eslint) 
> Manually select features 

選擇 Manually select features (自定義安裝)。


? Check the features needed for your project: (Press <space> to select, <a> to toggle all, 
<i> to invert selection)
>(*) Babel
 ( ) TypeScript
 ( ) Progressive Web App (PWA) Support
 (*) Router
 (*) Vuex
 (*) CSS Pre-processors
 (*) Linter / Formatter
 ( ) Unit Testing
 ( ) E2E Testing

這里選擇了常用的模塊,請根據實際需求進行選擇。


? Use history mode for router? 
(Requires proper server setup for index fallback in production) (Y/n)n

如果選擇了router,這里會詢問是否使用history模式。

vue-router 默認使用hash模式(即通過url#hash來跳轉頁面),使用URL的hash來模擬一個完整的 URL,當URL改變時,頁面不會重新加載。
如果使用history模式,URL就像正常的url,例如 http://yoursite.com/page,比較好看。但是還需要后台配置支持。

這里我們選擇n


? Pick a CSS pre-processor (PostCSS, Autoprefixer and CSS Modules are supported by default): 
(Use arrow keys)
  Sass/SCSS (with dart-sass) 
  Sass/SCSS (with node-sass) 
  Less 
> Stylus

選擇CSS預處理模塊,這里我們使用Stylus


? Pick a linter / formatter config: (Use arrow keys)
  ESLint with error prevention only 
  ESLint + Airbnb config 
> ESLint + Standard config 
  ESLint + Prettier 

選擇ESLint代碼格式檢查工具的配置,選擇ESLint + Standard config,標准配置。


? Pick additional lint features: 
(Press <space> to select, <a> to toggle all, <i> to invert selection)
>(*) Lint on save
 ( ) Lint and fix on commit

Line on save表示在保存代碼的時候,進行格式檢查。

Lint and fix on commit表示在$ git commit的時候自動糾正格式。

這里只選擇Lint on save


? Where do you prefer placing config for Babel, PostCSS, ESLint, etc.? 
  In dedicated config files 
> In package.json 

這里問把 babel,postcss,eslint 這些配置文件放哪?

In dedicated config files表示獨立文件。

In package.json表示放在package.json里。

這里選擇In package.json


? Save this as a preset for future projects? (y/N) N

是否為以后的項目保留這些設置?選擇N

然后耐心等待項目安裝完成。

1.6 自動安裝Electron

  • 執行以下命令,進入項目目錄:

$ cd electron-vue-demo

  • 然后執行以下命令:

$ vue add electron-builder

electron-builder是一個簡單又強大的庫。解決了打包這個棘手的問題,而且可以應對大部分的打包需求。

  • 接下來出現配置選項:
? Choose Electron Version (Use arrow keys)
  ^7.0.0 
  ^8.0.0 
> ^9.0.0

選擇Electron版本,我寫這篇博客的時候,選項是上面3個版本,選擇最新的即可。

然后耐心等待安裝完成。

1.7 手動安裝Electron

  • 修改package.json,添加以下7行:
  ...
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "electron:build": "vue-cli-service electron:build",	// add
    "electron:serve": "vue-cli-service electron:serve",	// add
    "postinstall": "electron-builder install-app-deps",	// add
    "postuninstall": "electron-builder install-app-deps"	// add
  },
  "main": "background.js",		// add
  "dependencies": {
    "core-js": "^3.6.5",
    "vue": "^2.6.11",
    "vue-router": "^3.2.0",
    "vuex": "^3.4.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.4.0",
    "@vue/cli-plugin-eslint": "~4.4.0",
    "@vue/cli-plugin-router": "~4.4.0",
    "@vue/cli-plugin-vuex": "~4.4.0",
    "@vue/cli-service": "~4.4.0",
    "@vue/eslint-config-standard": "^5.1.2",
    "babel-eslint": "^10.1.0",
    "electron": "^9.0.5",		// add
    "eslint": "^6.7.2",
    "eslint-plugin-import": "^2.20.2",
    "eslint-plugin-node": "^11.1.0",
    "eslint-plugin-promise": "^4.2.1",
    "eslint-plugin-standard": "^4.0.0",
    "eslint-plugin-vue": "^6.2.2",
    "stylus": "^0.54.7",
    "stylus-loader": "^3.0.2",
    "vue-cli-plugin-electron-builder": "~2.0.0-rc.3",		// add
    "vue-template-compiler": "^2.6.11"
  },
  ...
  • 新建src/background.js
'use strict'

import { app, protocol, BrowserWindow } from 'electron'
import {
  createProtocol,
  installVueDevtools
} from 'vue-cli-plugin-electron-builder/lib'
const isDevelopment = process.env.NODE_ENV !== 'production'

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win

// Scheme must be registered before the app is ready
protocol.registerSchemesAsPrivileged([
  { scheme: 'app', privileges: { secure: true, standard: true } }
])

function createWindow() {
  // Create the browser window.
  win = new BrowserWindow({
    width: 1000,
    height: 600,
    webPreferences: {
      /* Use pluginOptions.nodeIntegration, leave this alone
      See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration
      for more info
      nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
      */
      webSecurity: false,
      nodeIntegration: true
    }
  })

  if (process.env.WEBPACK_DEV_SERVER_URL) {
    // Load the url of the dev server if in development mode
    win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
    if (!process.env.IS_TEST) win.webContents.openDevTools()
  } else {
    createProtocol('app')
    // Load the index.html when not in development
    win.loadURL('app://./index.html')
  }

  win.on('closed', () => {
    win = null
  })
}

// Quit when all windows are closed.
app.on('window-all-closed', () => {
  // On macOS it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', () => {
  // On macOS it's common to re-create a window in the app when the
  // dock icon is clicked and there are no other windows open.
  if (win === null) {
    createWindow()
  }
})

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', async () => {
  if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
      await installVueDevtools()
    } catch (e) {
      console.error('Vue Devtools failed to install:', e.toString())
    }
  }
  createWindow()
})

// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
  if (process.platform === 'win32') {
    process.on('message', (data) => {
      if (data === 'graceful-exit') {
        app.quit()
      }
    })
  } else {
    process.on('SIGTERM', () => {
      app.quit()
    })
  }
}

以上代碼是1.6小節使用自動化方式安裝后生成的。

  • 安裝依賴包

在項目目錄下執行以下命令,安裝全部依賴包:

$ cnpm install

1.8 編譯並啟動APP

  • 執行以下命令,開始編譯APP,並啟動開發環境APP:

$ npm run electron:serve$ yarn electron:serve

首次啟動可能會等待很久,加載完后會自動打開APP,等待即可。

  • 編譯成功后,就會出現開發環境的APP了,如下圖(Win10啟動界面):

APP

  • 當回到控制台,發現控制台的信息:
INFO  Launching Electron...
Failed to fetch extension, trying 4 more times
Failed to fetch extension, trying 3 more times
Failed to fetch extension, trying 2 more times
Failed to fetch extension, trying 1 more times
Failed to fetch extension, trying 0 more times
Vue Devtools failed to install: Error: net::ERR_CONNECTION_TIMED_OUT

這是因為在請求安裝vuejs devtools插件。需要FQ才能安裝成功。可以忽略上面的問題,耐心等待5次請求失敗后會自動跳過,上面的成功界面即證實跳過依然編譯成功。

依然有解決方案:

注釋掉src/background.js中的以下代碼就行了:

/*
  if (isDevelopment && !process.env.IS_TEST) {
    // Install Vue Devtools
    try {
        await installVueDevtools();
    } catch (e) {
        console.error("Vue Devtools failed to install:", e.toString());
    }
  }
*/

2.配置項目

2.1 配置ESLint代碼格式檢查工具

ESlint可以高效的檢查代碼格式,讓參與項目的所有工程師都能保持統一的代碼風格。其檢測精度甚至可以精確到是否多一個空格或者少一個空格。代碼格式的統一對提高團隊的協同開發效率有很大的幫助,特別是對有代碼潔癖的工程師。

在項目根目錄下創建.eslintrc.js (注意文件名前面有個.

請粘貼以下代碼:

module.exports = {
  root: true,
  env: {
    node: true
  },
  'extends': [
    'plugin:vue/essential',
    '@vue/standard'
  ],
  rules: {
    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
    // 不檢測語句末尾的分號
    'semi': ['off', 'always'],
    // 強制縮進為2個空格
    'indent': ['error', 2],
    // 關閉函數名稱跟括號之間的空格檢測
    'space-before-function-paren': 0,
    // 忽略大括號內的空格
    'object-curly-spacing': 0
  },
  parserOptions: {
    parser: 'babel-eslint'
  }
}

這里說明下關於indent縮進的配置,要配合項目根目錄下的.editorconfig

[*.{js,jsx,ts,tsx,vue}]
indent_style = space   <--這里定義縮進類型是空格還是tab
indent_size = 2        <--這里需要與.eslintrc.js的indent對應
trim_trailing_whitespace = true
insert_final_newline = true

.editorconfig 用於IDE自動格式化代碼
.eslintrc.js 用於ESlint檢測

更多配置可參閱 ESLint教程

2.2 配置vue

在項目目錄下創建vue.config.js,粘貼以下代碼:

const path = require('path');

function resolve (dir) {
  return path.join(__dirname, dir);
}

module.exports = {
  publicPath: './',
  devServer: {
    // can be overwritten by process.env.HOST
    host: '0.0.0.0',  
    port: 8080
  },
  chainWebpack: config => {
    config.resolve.alias
      .set('@', resolve('src'))
      .set('src', resolve('src'))
      .set('common', resolve('src/common'))
      .set('components', resolve('src/components'));
  }
};

devServer 用於設置開發環境的服務,這里表示在本地8080端口啟動web服務。

chainWebpack 我們給項目目錄起了"別名(alias)",在代碼中,我們可以直接用“別名”訪問資源,省去了每次輸入完整相對路徑的麻煩。

在js代碼中可直接使用別名,例如:
@/common/js/xxx.js 等價於 src/common/js/xxx.js
common/js/xxx.js 等價於 src/common/js/xxx.js

在css或者html中使用別名,需要在別名前加“~”,例如:
@import "~common/stylus/font.styl"

3.項目基本設定

3.1 主進程和渲染進程簡介

在開始下面的步驟之前,很有必要簡單了解下Electron的應用架構。

  • 主進程

Electron 運行 package.json 的 main 腳本(background.js)的進程被稱為主進程。 在主進程中運行的腳本通過創建web頁面來展示用戶界面。 一個 Electron 應用總是有且只有一個主進程。

  • 渲染進程

由於 Electron 使用了 Chromium 來展示 web 頁面,所以 Chromium 的多進程架構也被使用到。 每個 Electron 中的 web 頁面運行在它自己的渲染進程中。

在普通的瀏覽器中,web頁面通常在一個沙盒環境中運行,不被允許去接觸原生的資源。 然而 Electron 的用戶在 Node.js 的 API 支持下可以在頁面中和操作系統進行一些底層交互。

  • 主進程和渲染進程的關系

主進程使用 BrowserWindow 實例創建頁面。 每個 BrowserWindow 實例都在自己的渲染進程里運行頁面。 當一個 BrowserWindow 實例被銷毀后,相應的渲染進程也會被終止。

主進程管理所有的web頁面和它們對應的渲染進程。 每個渲染進程都是獨立的,它只關心它所運行的 web 頁面。

具體可參閱官方文檔

3.2 APP窗口大小

修改background.js:

  win = new BrowserWindow({
    width: 1000,
    height: 600,
    webPreferences: {
      /* Use pluginOptions.nodeIntegration, leave this alone
      See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration
      for more info
      */
      nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
    }
  })

3.3 取消跨域限制

修改background.js:

  win = new BrowserWindow({
    width: 1000,
    height: 600,
    webPreferences: {
      /* Use pluginOptions.nodeIntegration, leave this alone
      See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration
      for more info
      nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION
      */
      webSecurity: false,
      nodeIntegration: true
    }
  })

3.4 取消菜單欄

在我們生成的桌面APP中,我們可以看到默認的菜單欄。

在windows中,菜單欄在APP窗口內的頂部;在macOS中,菜單欄位於電腦屏幕頂部。

為了方便項目將來也能直接生成純web應用,盡量把APP的全部功能都做到渲染進程里,這里我們取消菜單欄。

由於macOS的特殊性,頂部菜單欄無法刪除,所以我們針對macOS特殊處理,把菜單欄只保留“關於”和“退出”。

修改background.js

'use strict'
// 添加Menu組件
import { app, protocol, BrowserWindow, Menu } from 'electron'
...
function createWindow() {
  // Create the browser window.
  ...

  win.on('closed', () => {
    win = null
  })
  
  createMenu()
}

// 設置菜單欄
function createMenu() {
  // darwin表示macOS,針對macOS的設置
  if (process.platform === 'darwin') {
    const template = [{
      label: 'App Demo',
      submenu: [
        {role: 'about'},
        {
          role: 'quit'
        }]
    }]
    const menu = Menu.buildFromTemplate(template)
    Menu.setApplicationMenu(menu)
  } else {
    // windows及linux系統
    Menu.setApplicationMenu(null)
  }
}

改變后的APP界面樣式:

APP

更多關於菜單欄設置,請參閱:Electron官方API

3.5 設置APP窗口圖標

准備windows和macOS兩版圖標:

Platform File Type Specification
Windows app.ico 最小尺寸:256x256
MacOS app.png或app.icns 最小尺寸:512x512

把圖標文件放到public/目錄下,項目結構如下:

|- /dist_electron
   |...
|- /public
   |- app.icns
   |- app.ico
   |- app.png
   |- favicon.ico
   |- index.html
|- /src
   |...
|- .editorconfig    
|- .eslintrc.js
|- .gitignore
|- babel.config.js
|- package.json
|- package-lock.json
|- README.md

可以順便把favicon.ico也修改一下,但是在桌面版APP上是用不到的。如果以后生成純web項目才會用到。

修改background.js,讓APP窗口應用圖標:

function createWindow() {
  // Create the browser window.
  win = new BrowserWindow({
    ...
    
    // eslint-disable-next-line no-undef
    icon: `${__static}/app.ico`
  })

  ...
}

這里的${__static}對應的是public目錄

現在,Windows系統上可以看到開發環境的APP窗口圖標已經生效了。

MacOS圖標請參照4.1章節,並且需要在build后才能生效。

3.6 設置APP窗口標題欄名稱

修改public/index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <!-- 把'<%= htmlWebpackPlugin.options.title %>'改為App Demo -->
    <title>My App</title>
  </head>
  <body>
    ...
  </body>
</html>

4.打包

這里我們已經集成了electron-builder工具,可以參閱官方文檔


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM