~ Case of the Kobito, Markdown Editor
for YAPC Hackathon!
@mizchi / Koutaro Chikuba, Increments Inc
About
- Node.js / Frontend Engineer
- Single Page Application Specialist
- Kobito for Windows Developper
- Increments Inc (Providing qiita.com / Qiita:Team)
- Sorry, my English is not so good.
YAPC::Asia 2015 Hackathon | Peatix の発表資料
ここで喋ることは一昨日急に決まったので(YAPC回るし)スライド作る時間なかった。ゆるして。
発表中に @benogle 氏に何度か質問しながら進行します。
前提知識
- 基本は @benogle 氏が喋ってくれたでしょう
- Electronには沢山の拡張があります。迷ったらelectron/docs at master · atom/electron を読むこと
Kobito とは
Electron採用理由
- Web系の会社がWPFに習熟したエンジニア雇うのは難しい
- nodeの豊富なライブラリ資産
- 環境固定でエッジなAPIでコードを書ける
- IndexedDb
- WebComponents
- ServiceWorker
- (WebAssembly)
- Chrome以外の動作確認をサボれて最高
Electronでの開発に必要な足腰
-
発生した問題がnode/ブラウザのどちらかの問題か特定するだけの知識
- Electronのバグ!っていう前にChromeのバグを確認
- Issues - chromium - An open-source project to help move the web forward. - Google Project Hosting
- Electronのバグ!っていう前に各npmパッケージのIssueを確認
- Electronの質問です!って言いながらブラウザ/nodeの質問しにくる人多すぎて、関係者はイライラしていると思う!!!!!
- あるとよい: nodeでの開発経験
- nodeできることならなんでもできる
- nodeで足りない機能はC++とV8 BridgeでAPI書く必要
- だいたいある
- node-libusbでusbを直接読んでIoTっぽいことをしてる人もいる
- あるとよい: ブラウザフロントエンドの経験
- Single Page Applicationのノウハウ
- ユーザーはElectronだからUIがHTMLでレスポンスが悪い、なんてのを許容しない
- => 必然的にSPA
- (業務アプリだと気にしないかも)
- HTML = GUI Toolkit
- Single Page Applicationのノウハウ
- 確かにElectronの環境に依存したバグは存在する
- node/browser対応のjsで
if (typeof global === 'object') {...}
としてしまっているケース- 適当にforkしたりパッチ当てたりして対応(これは難しくない)
- 一部のnativeモジュールのビルド(一時期sqlite3落ちてて困った)
- webview tag is buggy
- node/browser対応のjsで
(今どんなバグがあります?)
- @benogle「AutoUpdaterが不穏」
Electronを使う場合、同じ目的に対して複数の回答がありうる
- これどうやって実現するの?
- HTTP Request の例
- XMLHTTPRequest / Fetch API
require('http')
- ※ クロスドメイン制約は起動オプションで変更できる
- modal window
- Electron組み込みdialog モジュール
- DOMでcssでposition: absolute
- 場合によっては
input<type='file' .../>
- nodeのbuffer と browser の file オブジェクトの変換はちょっと面倒
- ストレージ
- IndexedDb / localStorage
- HTML5 File API
-
require('fs')
や sqlite3のORM- ネイティブモジュールを使うと各環境でnpm install しなおす必要があったので自分はIndexedDbを使った。楽だし。
- HTTP Request の例
Electron本体の構成を読む
- コミットログみると開発者はzcbenz氏ほぼ一人
- ドキュメンテーションは複数人
- ほとんど C++(chromium-srcへのパッチ)
- node v8 とのバインディングで少しだけJavaScript(coffee)
- Electron では node v8 と Chromium のメインループの透過的な統合処理が主
- ChromiumのStandalone化だけなら atom/brightray 側を読む
祝: Atom の Web Components 導入、ついでに Atom Shell の話。 - steps to phantasien
そこで Atom Shell は Node.js のイベントループを二つのフェーズにわけ、 一部の処理だけを別スレッドに移すことで二つのイベントループを単一スレッドに押し込んだ。 具体的には libuv でファイルハンドルを待ちブロックする部分だけを専用のスレッドに追い出す。 そしてハンドルがシグナルされブロックから抜けた直後にメインスレッドへ処理を戻して 続きの処理、つまり JavaScript のコードを動かす。
要するに別スレッドで epoll_wait() して続きはメインスレッドで引き取る。 そしてメインスレッドの処理が終わったらふたたび別のスレッドでハンドルを待つ。
何が起こるかというと、実は透過的に見えててもクライアントからブラウザプロセスを使っててエラーを起こした場合ハングする。
- 「今度zcbenzが東京に引っ越してくるよ」
- 「Electron開発チームが一人しかいないんで、3~4人に増やしたい」(雇用の気配がする)
Electronと似たような構成の環境
- nwjs/nw.js
- セキュリティサンドボックスモデルが違う
- あるインスタンスがnode由来のオブジェクトか、ブラウザ由来のオブジェクトかを厳密に区別する
- Electronは区別せずシンプルで楽観的
- 構成的に複数ウィンドウは出せない
- adobe/brackets-shell (no node.js)
- bracketsというエディタのシェル
-
CEF3-based application shell for Brackets.
- ソース公開されてないけど、たぶんVivaldi も似たような構成なんじゃないか?
リリースしての辛み
App Store問題
- App Storeに出せない
- Mac App Store · Issue #249 · atom/electron
- nodeの一部がMacOSXのサンドボックス違反があるっぽい
- nw.js と共通の問題
- パッチはあるけど大変 Electron for the Mac App Store
There will probably be a 2nd version of Electron OS X posted for future releases that is MAS compatible
とのこと。(質問: future っていつ頃?)
- @benogle「現状手が足りてない中、いろいろなプライオリティがある。いつとは言えないがやる」
ウイルス誤検知があった
- リリース後、最初一部のウイルス検知ソフトウェアで引っかかった(symantec)
- 問い合わせて問題がないことを確認して解除してもらった
(この件きいたことある?)
- @benogle「自分は携わってないが、たしかそんな話があったと聞いている」
Electron を自前でビルドする必要があるか?
- resedit.exeでプロセス名やアイコンを変えれる、とあったが自分はできなかった
- ついでにいろいろイジって回りたかった(IndexedDbの最大サイズとか)
適当に作るだけなら不要
@benogle「electron-packagerなら大丈夫だよ」
Electronのビルドでの苦悩
- 定期的にHEADが落ちてる
- テスト通ってても特定プラットフォーム(Windowsでだけ)壊れてたりする
- node / io.js のゴタゴタでリポジトリのurl変わって落ちたりもした
- Issue建てて壊れてんぞーって言うと直る(こともある)
- webview tag まわりは壊れがち
- electron/web-view-tag.md at master · atom/electron
- webview tag 自体は Chrome拡張用の機能で、Electron側で機能を追加している webview Tag - Google Chrome
- webviewタグを使った場合、必ずビルドごとに挙動を確認すること!
- 他にも秘孔あるかも
(怪しいモジュールどこかありますか。たとえばElectronじゃなくてChromeの問題だけどshadowRootが頻繁に描画に失敗するのは知ってる)
Electronアプリ開発において参考になる実装
-
atom/atom
- bootstrap ~ window生成 ~ apmのplugin読み込み までは難しくない(コード量は多いが)
- 編集バッファのコードを読むのは難しい(コードハイライター + DOM職人芸的な最適化)
- 困ったらwindow.atomインスタンスのAPI探す atom/atom.coffee at master · atom/atom
-
benogle/curve-app
- 一昨日の発表で知った
- 昨日というよりAtom開発者ならではのビルド環境
- package.json の devDependencies を真似たい
(他におすすめのビルド構成ある?)
- @benogle「エコシステムとして細かいタスクに分割しようという動きがある。今だとelectron-packager とか electron-rebuild が良い」
Atom の編集はStandaloneで動かないのか?
- テキストオブジェクトのモデルはpure jsで独立している atom/text-buffer
- 質問した
- エディタを抜き出すためにリファクタしていいか質問した Standalone TextEditor · Issue #5001 · atom/atom
- ブラウザで動かないモジュールを使ってる(Highlighting の正規表現に Oniguruma) からそのままじゃ動かない
- 今はまだやるな時期が悪い
- 自分は結局CodeMirrorを拡張した
(これ予定ありますか)
- @benogle 「やりたいが手が足りない」
Isomorphic(Universal) JavaScript
- どんな環境でも動くJavaScript、という考え方
- Pure EcmaScript Implementation
- 強制的にView触れないので、DDDにおけるdomain層の設計ギプスと考えればよい
- 環境ごとの制約を満たせばいつでもいろんなプラットフォームで展開できる
- 手元に複数の実装がある
- Electron - Mac / Win / Linux(Debian)
- npm版(electron-prebuilt)
- ブラウザ版 (Chrome / Firefox / Edge で動作確認)
- Chrome Packaged Apps
- 必要になったタイミングで必要な物をリリース可能
- E2Eテストでselenium組み込みが面倒なときにブラウザ版で走らせたりした
- 手元に複数の実装がある
TIPS
IndexedDb
- IndexedDbの普通のブラウザの上限は5MB程度だが、Electronでは増やしている
- Electron
-
~/Library/Application\ Support/<app-name>/IndexedDb
に実体
-
- サーバーサイドのDBと違っていつアップデートされるか不明でクライアントマイグレーションをしないといけない
- mizchi/stone-skin
- 自作のIndexedDbWrapper
- IndexedDbマイグレーション機能含む
remote
レンダラプロセスからブラウザプロセスを操作するには、たぶん正しいやり方は require('ipc') してpubsubする。ただ結構面倒臭い。
main.jsの
var BrowserWindow = require('browser-window')
は、クライントでの
var BrowserWindow = require('remote').require('browser-window')
と同じ。その気になればブラウザ側から別ウィンドウの生成まで可能。
プロダクションでbrowserify
- kobito は production では browserify を使うようにしている
- 小サイズ化(87MB -> 1.8MB)
- (ちょっとした難読化)
- browserify できないモジュールは無視する
- ポリフィルできない fs, http 等
- Electron プリインの app, browser-window, ipc ...
- global.require('...') と書いて解決(BK気味)
- 開発時はbrowserifyの時間をスキップしたかったんでそのまま
- いまは 差分ビルドできる watchify あるんで問題ないかも
asar
- (たぶん) atom shell archiver
- tar のようにファイルを連結する(だけ)
- Electronのfsモジュールは中からは foo.asar/index.html と中のパスを透過的に扱える
- fsにはその為のパッチが当たっており、
require('original-fs')
で元のfsを使うことも可能
KobitoのUpdaterの実装
electron/auto-updater.md at master · atom/electron
This module has only been implemented for OS X.
とのことなので自分で作った。
main.js
versions/
- 1.1.0.asar
- index.html
- etc...
- 1.0.0.asar
- index.html
- etc...
起動時は semverが一番大きい物を起動。起動後にバージョンサーバーを確認して、存在しなければ asar をダウンロードして再起動。
Electron本体の更新を伴うバージョンアップはメジャーアップデートでやるとする(年1~2回の予定)
(質問したが諸事情で省略)
その他諸々
atom/Electron/docs 以下を穴があくほど読め!以上!
知見はQiitaのElectronタグにでも投稿してくれ!