源自 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']);