源自 Atom-shell 的 Electron 目前是一個很火的項目。已經有不少開發者基於 Electron 開發出了各類各樣的桌面程序。在我看來,對於廣大前端開發者來講,最爲耳熟能詳的應該是 Atom 和 VS Code。在 Electron 的官網上可以看到更多有意思的項目。css
以前提到過,個人計劃之一就是玩一下 Electron,打造一個桌面工具。上個星期通過幾回摸索和調研肯定了這個項目的可行性以後,開始着手打造。近幾天慢慢的構建出基礎的項目前端結構。html
對於前端的技術選型已經沒有以前那麼糾結了,思考事後決定了:React。緣由很簡單,以前的一個小項目用的是 Vuejs 的一套體系,此次想換換口味。雖然以前我一直很不喜歡 React 那種模板和邏輯混合在一塊兒的方式,可是很喜歡 Redux 的處理方式,因此忍不住試試看,究竟是用 React 爽仍是 Vue 爽。前端
瞭解過 Electron 的應該都知道它的 main process 和 renderer process。main process 使用 BroswerWindow 實例建立 web page,每一個 BroswerWindow 實例在它本身的 renderer process 中運行 web page,每當 BroswerWindow 實例被銷燬時,其對應的 renderer process 也會被終止。main process 管理全部的 web page 及其對應的 renderer process 。vue
我以爲能夠這麼簡單地理解的:若是將 renderer process 負責管理渲染的 web 頁面所作的事情和瀏覽器相似,那麼 main process 則是包裹着這個「瀏覽器」的外殼,將「瀏覽器」中的代碼與系統底層聯繫在一塊兒。node
在實踐過程當中,我發現 main process 的文件不能使用 import(應該說是沒法使用 ES6 語法),可使用 babel 將使用 ES6 語法的代碼編譯成可執行的版本代碼。而 renderer process 的代碼則經過 webpack 打包 React 代碼。react
經過 gulp 和 babel 能夠很輕鬆地完成webpack
var path = require('path'); var gulp = require('gulp'); var babel = require("gulp-babel"); var ROOT_PATH = path.resolve(__dirname); var APP_PATH = path.resolve(ROOT_PATH, 'app'); // main process 的編譯 gulp.task('babel:electron-main', function () { return gulp.src([APP_PATH + '/main.js', APP_PATH + '/main/**/*.js'], { base: APP_PATH }) .pipe(babel()) .pipe(gulp.dest('dist')); });
gulp 與 babel 的配合使用的更多細節能夠參考 babel 和 gulp-babel。git
React 的 webpack 配置在這裏我就不重複了,處處都能找到。github
我發如今目前市面上Electron 的相關基礎教程中,簡單的介紹都是如此:web
// 安裝 npm install -g electron-prebuilt // 啓動 electron . //更好一點的是按照官方給出的 quick start npm start
可是這樣有一個很直接的問題:每次修改 main process 相關代碼以後須要重啓,修改了 renderer process 相關代碼以後須要手動刷新,這很影響開發體驗。
renderer process 的 hot load 很好處理,和前端開發相似,react 和 vue 都有相似的工具,直接將前端開發中使用的配置挪過來就好。而 main process 的自動化則須要另尋辦法,固然,也不難。使用 electron-connect 能夠很好的幫助咱們解決這個問題,在 gulp 中設置好task 以後而後在 renderer process 和 main process 中的插入一段代碼便可。
gulpfile.js
var gulp = require('gulp'); var gutil = require('gulp-util'); var electron = require('electron-connect').server.create(); gulp.task('watch:electron', function () { electron.start(); gulp.watch(['./app/src/main.js', './app/src/main/**/*.js'], electron.restart); gulp.watch(['./app/dist/**/*.{html,js,css}'], electron.reload); });
RendererProcess
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Hello World!</title> </head> <body> <!-- All of the Node.js APIs are available in this renderer process. --> <!--We are using node <script>document.write(process.versions.node)</script>,--> Chromium <script>document.write(process.versions.chrome)</script>, and Electron <script>document.write(process.versions.electron)</script>. and Node <script>document.write(process.version)</script>. <div id="example"></div> </body> <script> //建立 client require('electron-connect').client.create(); </script> </html>
MainProcess
'use strict'; var app = require('app'); var BrowserWindow = require('browser-window'); var client = require('electron-connect').client; app.on('ready', function () { var mainWindow = new BrowserWindow({ width: 400, height: 300 }); mainWindow.loadUrl('file://' + __dirname + '/index.html'); // Connect to server process client.create(mainWindow); });
細心的同窗可能會發現,在 watch 的 task 中,同時對 main process 和 renderer process 的代碼監聽,對應的操做是 restart 和 reload。reload 會刷新當前的頁面,在這裏 React 的 hot load 均可以不須要了。印象中好像 hot load 是不會整個刷新頁面的,回頭能夠試試。
至此,只須要在終端中執行
gulp watch:electron
就能達到開發過程當中 electron 自動 restart 和 reload 的目的了。若是想看詳細文檔能夠前往 這裏。
上述只是簡單的例子,更多時候須要根據項目的規劃作調整,一下是個人 gulpfile.js
var path = require('path'); var gulp = require('gulp'); var babel = require("gulp-babel"); var gutil = require('gulp-util'); var webpack = require('webpack'); var webpackConfig = require('./webpack.config.js'); var electron = require('electron-connect').server.create(); var ROOT_PATH = path.resolve(__dirname); var APP_PATH = path.resolve(ROOT_PATH, 'app'); // 開發 var webpackConfigDev = Object.create(webpackConfig); webpackConfigDev.devtool = 'eval-source-map'; webpackConfigDev.debug = true; var devCompiler = webpack(webpackConfigDev); // renderer process 的 webpack 編譯 gulp.task('webpack:build-dev', function () { devCompiler.run(function (err, status) { if (err) { throw new gutil.PluginError('webpack:build-dev', err); } gutil.log('[webpack:build-dev]', status.toString({ colors: true })); }); }); // main process 的編譯 gulp.task('babel:electron-main', function () { return gulp.src([APP_PATH + '/main.js', APP_PATH + '/main/**/*.js', APP_PATH + '/constant/*.js'], { base: APP_PATH }) .pipe(babel()) .pipe(gulp.dest('dist')); }); gulp.task('watch', ['babel:electron-main', 'webpack:build-dev'], function () { electron.start(); gulp.watch(['./app/main.js', './app/main/**/*.js'], ['babel:electron-main']); gulp.watch([APP_PATH + '/constant/*.js', './app/src/**/*.{html,js,css}'], ['webpack:build-dev']); gulp.watch(['./dist/main.js', './dist/main/**/*.js'], electron.restart); gulp.watch(['./dist/renderer/*.{html,js,css}', './dist/renderer/**/*.{html,js,css}'], electron.reload); }); gulp.task('dev', ['watch']);