Edited at

[ Electron + React ] 5分で作るMarkdown Editor

More than 1 year has passed since last update.


自己紹介

じゅんじゅんというニックネームで、関西を拠点に活動しているフロントエンドエンジニアです。

HAL大阪の2回生です👍 (2016.9.9現在)

よくstart up系イベントに行くので、大阪らへんの方は会いましょう!


作ったもの

今回は


  • ElectronとReactで作ること

しか決まってないので、リアルタイムプレビューの機能だけしかつけていません。

できたもの。

スクリーンショット 2016-09-09 1.24.12.png


今回作った経緯

今回は、いつもREADMEとかを書くときにsublime textのOmniMarkupPreviewerを使っていたんですが、全然動かない。。

なぜか分からないので、勉強を兼ねて作ることにしました。

今回使う技術は


  • Electron

  • React

ぐらいです。

Editor部分はAceを使いました。

Markdownのpreviewには、markedを使いました。

Electronに関しては、ほぼサンプルコードのままです。


環境

Node v5.1.1

Electron v1.3.5


開発

今回の構成は

- app.js(electron)

- index.html
- stylesheets
- markd.css
- style.css
- javascripts
- app.js
- bundle.js
- components
- App.jsx
- Editor.jsx
- Preview.jsx

という感じになっています。

まずはnpm initから始めましょう。

今回使ったモジュールは


  • react

  • react-dom

  • marked

  • react-ace

  • brace

  • electron-packager

開発に使うのは


  • watchify

  • babelify

  • babel-preset-react

  • babel-preset-es2015

react-aceはreactでaceを使うためのコンポーネントです。

watchifyは使い差分ビルドするためで、babelifyはES6に対応したBrowserify用のパッケージです。

babel-preset-reactは、babelのreactのトランスパイルに使うものです。

babel-preset-es2015というのがありますが、ES6用のものです。ES6で書かない場合は、省いて大丈夫です!

package.jsonにこれを追記してください。

  "main": "app.js",

"scripts": {
"start": "watchify -v -t babelify javascripts/app.js -o javascripts/bundle.js"
},

では、まず.babelrcを書きましょう。

{

"presets": ["react", "es2015"]
}

次に、htmlを編集します。


index.html

<body>

<div id="react"></div>
<script src="javascripts/bundle.js></script>
</body>

Reactのレンダリングするためのjavascripts/app.jsを編集しましょう。


App.js

import React from "react";

import { render } from "react-dom";

// componentのApp.jsxをインポート
import App from "./components/App.jsx";

render(
<App/>,
document.getElementById("react")
)


では、このインポートされたApp.jsxを書いていきます。


App.jsx

import React from "react";

// 後で作ります。
import Editor from "./Editor.jsx"
import Preview from "./Preview.jsx"

export default class App extends React.Component{

constructor(){
super();
this._edit = this._edit.bind(this); // ES6のthisバインドされない問題があるので.bind(this)
this.state = {
value: ""
}
}

render(){

return(
<div className="clearfix" id="app">
<Editor
edit={this._edit} // editorコンポーネントにeditしている間に呼ぶメソッドをpropsでわたします。
value={this.state.value}
/>
<Preview
data={this.state.value} // markedでhtmlにパースするときのデータ
/>
</div>
);
}

_edit(text){
this.setState({ value: text })
}

}


特に困ることもないと思います。


Editor.jsx

import React from "react";

import brace from 'brace';
import AceEditor from 'react-ace'; // aceのreact componentを使います。

import 'brace/mode/markdown'; // modeを選ぶ
import 'brace/theme/textmate'; // カラーテーマを選ぶ

export default class Editor extends React.Component{

constructor(props){
super(props)
this._onChange = this._onChange.bind(this);
}

render(){
return(
<AceEditor
mode="markdown" // mode
theme="textmate" // theme
width="45%"
height="100%"
name="editor" // id
tabSize={2}
showPrintMargin={false} // 真ん中らへんの線を消す
highlightActiveLine={false} // lineのハイライトを消す
editorProps={{$blockScrolling: true}}
onChange={this._onChange}
value={this.props.value}
/>
)
}

_onChange(text){
this.props.edit(text); // propsでわたってきたものを実行
}

}


上二つにでてくるthis._onChange = this._onChange.bind(this)などのものですが、これはES6で書いたときに、自動でthisをバインドしてくれないので自分で書くしかない場合の書き方です。

stage-0を使ってより簡単に記述することができます。

ReactをES6で開発時のbindの問題

//_onChange(){}

_onChange = () => {}

自作のメソッドをアロー関数をつかってかけばthisを追えるようになります。

react-aceのオプションなどはreact-aceを参照ください。


Preview.jsx

import React from "react";

import marked from "marked";

export default class Preview extends React.Component{

constructor(props){
super(props);
}

render(){
return(
<div id="preview"
dangerouslySetInnerHTML={{__html: marked(this.props.data)}} // reactは標準では、HTMLがエスケープされてしまうので、エスケープされないようにしないといけない。
></div>
)
}

}


これで全てコンポーネントを書き終えました。

$ npm startを実行してください。bundle.jsが出力されます。

そしたら

$ electron app.js

とすれば、アプリとしてこのエディタが立ちあがると思います。

おソースは全てgithubにあるので、興味あるひとはぜひみてください!

konojunya/md-preview-electron


最後に

React自体もそんなに難しくなく、markedもaceも余裕でしたね。

今後はexport機能とかをつけようと思います。

9/15 グランフロントでReactハンズオンします!大阪の人ぜひ!

2時間で学ぶReactハンズオン

Twitter => @konojuya

HP => Hello, My World