分类
桌面开发

使用 Electron 开发桌面应用

这个标题比较大,先挖个坑,日后再填。

忘记哪里看来的:nw(原 node-webkit)的作者从 Intel 离职后,无法继续维护 nw,此时 Github 向他抛来橄榄枝,请他去做 Electron(主要是为 Atom 做基础),于是便有了 Electron。

这两个东西虽然基础架构不一样,不过大体上都是 V8 + Chromium,实现使用 JavaScript 构建系统交互,使用 Web 提供 UI。简单对比了一下我觉得明显 Electron 好多了嘛,所以选择用它来开发桌面应用。

经过几天摸索,开发出来一个应用。不过太小,可分享的东西不多,所以先记几条 Tips。

封装 Electron 应用

打包工具

Electron 构建了一套完整的环境,只需要替换里面的 Web 部分就可以发布。这样最大的好处是每次发布的时候只需要简单压缩一下网页部分,放到包里,不用构建整套系统,对开发机的要求大大降低。

坏处就是,对于我们墙内用户而言,安装 Electron 必须用小水管拉一个将近 100M 的包回来,简直痛苦的要死。更别提后面如果享用 [electron-packager] 之类的工具封装的话还要再下一遍……每到这个时候,都要给病魔加油,愿他早日战胜方校长。

这里建议使用淘宝的 cnpm 镜像,速度会好很多。不过它似乎和 npm 有点冲突。

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

至于 electron-packager,因为它使用 [electron-download] 下载 Electron 运行包,可以按照提示,修改下载的源,走淘宝镜像,这样也会快很多:

npm config set ELECTRON_MIRROR https://npm.taobao.org/mirrors/electron/

环境选择

至少在我这里,在 Mac 下只能打包 Mac 应用;在 Windows 下只能打包 Windows 应用。所以需要多平台的话,请准备多台开发机。

分类
js

Gulp 中顺序执行任务

书接上文,Chrome 插件中无法直接使用 Handlebars 处理模板。两种方案,一是利用沙箱,将 .eval() 放在独立的环境中执行,好处是其它跨域的操作也能这样处理,坏处是写起来麻烦。另一种则是利用 Handlebars 的“预编译”功能,将模板提前编译好,直接在代码中引用。好处是写起来更顺畅,并且从发布插件的角度来看,早晚都要这样做。

我决定选用后者,于是我需要把模板提取出来进行预编译,然后我准备写个 Gulp 任务来搞定这个。

从页面中提取模板不算太难,写个正则就好,这里提前约定,模板使用 <script type="x-handlebars-template"> 标签包裹。

gulp.task('template', function () {
  let promise = new Promise((resolve, reject) => {
    fs.readFile('popup.html', (err, content) => {
      if (err) reject(err);
      resolve(content);
    }
  });
  promise.then((content) => {
    content.replace(/<script([^>]*)>([\S\s]+?)<\/script>/g, (match, attr, template) {
      // 具体处理模板,略掉了
    });
  })
});

模板编译之后,还需要用 Webpack 打包才能使用,于是要增加 Webpack 的任务。这个比较简单,参考它的 文档 即可。不过很明显,必须等全部模板预编译完成才能打包。之前我已经试过用 run sequence 顺序执行任务,不过这里我用到 Promise,情况似乎有变。

稍微 Google 一下,得知要能顺序执行任务有三种选择:

使用 callback

gulp.task('task', function (callback) {
  setTimeout(function () {
    // 任务执行完成后,调用 callback
    callback();
  }, 5000);
});

返回 stream

这个好像是标准做法,也是我之前做的。

gulp.task('task', function (callback) {
   returen gulp.src('*.js')
     .pipe(uglify())
     .pipe(gulp.desc('dist/');
});

返回 Promise 对象

gulp.task('task', function () {
  return new Promise(resolve, reject) => {
    resolve();
  });
});

既然支持 Promise 那就好办了,在 .then() 前面加一个 return 就好。


完整代码

参考文章

Handling Sync Tasks with Gulp JS