久しぶりのVue.jsに関する記事です。
今回は、以前記載させていただきました、JQueryとmark.jsでマークダウンのリアルタイムプレビューをつくるを書き換えたいと思います。
今までの記事はこちらになります。
JSFiddleを使用します。
設定方法などはVue.js入門その1〜基本文法〜をご参照ください。
JSFiddleの左側、External ResourcesでCDNの設定をできます。
下記のリンクより、marked.js
のリンクを取得し、設定します。
(特に修正するわけではないので、min.js
のリンクを取得しました。)
marked
HTMLの記述欄に記載して動くか確認してみます。
<div id="content"></div> <script> document.getElementById('content').innerHTML = marked('# Marked in browser\n\nRendered by **marked**.'); </script>
下図のように表示されるかと思います。
変換の部分は後回しにし、画面表示とVueインスタンスだけ作成します。
簡易的にするため、form
タグでは囲っていません。
v-modelを設定しておき、Vueインスタンスのinput
プロパティと双方向バインディングをさせます。
<div id='editor'> <textarea rows="10" cols="50" v-model='input'> </textarea> <div><!-- ここに変換後の文章を表示 --></div> </div>
var app = new Vue({ el: '#editor', data: { input: '', }, });
Vueインスタンスに、マークダウンからHTMLに変換して返す処理を追加します。
このとき、getter
としても機能させてくれるcomputed
を使用します。
var app = new Vue({ el: '#editor', data: { input: '', }, + computed: { + convertMarkdownToHtml: function() { + return marked(this.input); + } + }, });
やっていることはinput
プロパティをmarked
メソッドで変換して返しているだけです。
これを変換後の文章を表示するエリアに埋め込みます。
HTMLを書き換えるときは、v-htmlが便利です。
これで指定したプロパティ、もしくはメソッドで書き換えてくれます。
<div id='editor'> <textarea rows="10" cols="50" v-model='input' debounce='50'> </textarea> - <div><!-- ここに変換後の文章を表示 --></div> + <div v-html='convertMarkdownToHtml'></div> </div>
ここまでで、変換はうまくいっているかと思います。
ただ、公式ドキュメントにも下記の通り記載がありますが、ユーザーからの入力をそのまま表示させると危険そうです。
任意の HTML をあなたの Web サイト上で動的に描画することは、XSS攻撃を招くため大変危険です。v-htmlは信頼済みコンテンツのみに利用し、絶対にユーザの提供するコンテンツには使わないでください。
marked
メソッドは第二引数でオプションの設定ができます。
https://github.com/chjj/marked
例えば、サニタイズしない場合は、下記のようにスクリプトを実行できます。
ここで、第二引数にsanitize
オプションを入れておきます。
- return marked(this.input); + return marked(this.input, {sanitize: true});
タグをそのままエスケープしてくれるので、ひとまず安心です。
debounce
を利用して、一定時間の間に行われた処理をまとめて実施したいと思います。
Vue.jsの1系ではv-model
にdebounce
属性があったようですが、2系で削除されました。
v-modelのdebounce削除
ドキュメントを読むとlodash.js
が勧められていたので、そちらを使用したいと思います。lodash.js
はJavaScriptのユーティリティライブラリで、debounce
以外にも便利なメソッドがたくさんあるようです。
Lodash
今回はまたExternal Resourcesで読み込もうと思いますので、CDN版を利用します。
lodash.js
値が変わるたびに、debounce
のメソッドを呼びたいと思いますので、v-model
はやめて、v-bindとv-onを使用します。
- <textarea rows="10" cols="50" v-model='input'> + <textarea rows="10" cols="50" v-bind:value='input' v-on:input='updateValue'>
v-bind
では、textarea
の値とinput
プロパティを結びつけます。v-on
は特定のイベントが起きたときにメソッドを呼びます。v-on:input
で、textarea
の値が変化したら、関数を呼ぶようにします。
イベントリファレンス -input-
(関数はまだ実装していませんが、先に書いておきます。)
また、v-bind
、v-on
にはそれぞれ省略記法があるので、そちらで書き換えてみます。
- <textarea rows="10" cols="50" v-bind:value='input' v-on:input='updateValue'> + <textarea rows="10" cols="50" :value='input' @input='updateValue'>
lodash.js
の_.debounce
を利用して、プロパティの変更をまとめて実施するようにします。
_.debounceの引数はそれぞれ下記のようになります。
var app = new Vue({ el: '#editor', data: { input: '', }, computed: { convertMarkdownToHtml: function() { return marked(this.input, {sanitize: true}); } }, + methods: { + updateValue: _.debounce(function(eventObject) { + this.input = eventObject.target.value; + }, 500) + }, });
今回は0.5秒で変更するようにしました。
(主観ですが、1秒だとけっこう遅く感じます。)
0.5秒間隔でeventObject
から値を取得し、input
プロパティにセットします。
これで、処理がまとめて実施されるようになったのではないでしょうか?
サンプルは下記になります。
CSSの方はお好みでお願いします。