カテゴリー: FrontEnd

Vue.js入門その5〜マークダウンのリアルタイムプレビューをつくる〜

はじめに

久しぶりのVue.jsに関する記事です。
今回は、以前記載させていただきました、JQueryとmark.jsでマークダウンのリアルタイムプレビューをつくるを書き換えたいと思います。

今までの記事はこちらになります。

環境構築

JSFiddleを使用します。
設定方法などはVue.js入門その1〜基本文法〜をご参照ください。

  • Vue.js: 2.2.1
  • marked (cdn.js): 0.3.6
  • lodash.js (cdn.js): 4.17.4

marked.jsの設定

CDNのロード

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タグでは囲っていません。

HTML

v-modelを設定しておき、Vueインスタンスのinputプロパティと双方向バインディングをさせます。

<div id='editor'>
    <textarea rows="10" cols="50" v-model='input'>
    </textarea>
    <div><!-- ここに変換後の文章を表示 --></div>
</div>

JS

var app = new Vue({
  el: '#editor',
  data: {
    input: '',
  },
});

HTMLに変換して返す

Vueインスタンスに、マークダウンからHTMLに変換して返す処理を追加します。
このとき、getterとしても機能させてくれるcomputedを使用します。

var app = new Vue({
  el: '#editor',
  data: {
    input: '',
  },
+ computed: {
+   convertMarkdownToHtml: function() {
+     return marked(this.input);
+   }
+ },
});

やっていることはinputプロパティをmarkedメソッドで変換して返しているだけです。
これを変換後の文章を表示するエリアに埋め込みます。

innerHTMLをしてくれるv-html

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-modeldebounce属性があったようですが、2系で削除されました。
v-modelのdebounce削除

ドキュメントを読むとlodash.jsが勧められていたので、そちらを使用したいと思います。
lodash.jsはJavaScriptのユーティリティライブラリで、debounce以外にも便利なメソッドがたくさんあるようです。
Lodash

今回はまたExternal Resourcesで読み込もうと思いますので、CDN版を利用します。
lodash.js

HTMLの書き換え

値が変わるたびに、debounceのメソッドを呼びたいと思いますので、v-modelはやめて、v-bindv-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-bindv-onにはそれぞれ省略記法があるので、そちらで書き換えてみます。

- <textarea rows="10" cols="50" v-bind:value='input' v-on:input='updateValue'>
+ <textarea rows="10" cols="50" :value='input' @input='updateValue'>

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の方はお好みでお願いします。

naoki85

シェア
執筆者:
naoki85
タグ: Vuejs

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

2週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前