20200325のvue.jsに関する記事は4件です。

Nuxt.jsをServerlessでデプロイするにあたってぶつかった4つの壁

概要

今回の内容は下記の技術解説記事になります。
爆速でLambda@edgeにNuxt.jsを構築できるテンプレートを作った

AWSへのデプロイを自動化

今回デプロイ作業を自動化するにあたって下記の2種類候補がありました。
1. Serverless Frameworkを使う方法
2. Serverless Componentsを使う方法

開発初期段階ではServerless Frameworkを使っていたのですが、
とある理由でServerless Componentsを使う方針に切り替えました。

なぜServerless Componentsに切り替えたのか

まず1つ目の大きな理由ですが、serverless.ymlを書くのが面倒すぎる!!!!!!!
ちょっとしたLambdaをデプロイするだけならまだマシですが、
CloudFrontやS3を絡めた少し複雑な環境になるとserverless.ymlの内容がめちゃくちゃ長文になり、
見通しも悪すぎて何を書いてるか分からなくなってきます。

それに比べてServerless Componentsymlの代わりにjsを使って
プログラマティックに記述出来るので、ものすごくメンテナンスがしやすいです。

あと.envAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYを記述するだけでデプロイ実行出来るのも地味に便利です。

ただ1つだけServerless Componentsにも欠点があります。
ドキュメントが少なすぎる!!!!!!!!
日本語の解説記事もですが、英語での解説記事も殆どありません。
僕はServerlessのレポジトリに上がっている物を参考に今回のテンプレを作成しました。

Lambda@EdgeへExpressをデプロイする方法

今回はこちらの記事で紹介されているaws-serverless-express-edgeを使用しました。

しかし、いざデプロイして見るとhostHeader.startsWith is not a functionというエラーが出てレスポンスが返ってきません。
エラーが出ている箇所のコードを見ると、CloudFrontからのリクエストからHeaderを取り出す方法が間違っていました。
AWSのドキュメントを見てみるとheadersの中身が下記のような構造になっています。

"user-agent": [{
  "key": "User-Agent",
  "value": "curl/7.66.0"
}]

上記の形式に合わせて修正した物がこちらになります。
aws-serverless-express-edgeはもうメンテナンスされていないようだったので、今回はフォークして該当箇所を修正しました。

デプロイ速度の改善

デプロイで一番時間の掛かる部分はnode_moduleの更新です。
本番環境で使用しない物は出来るだけdevDependenciesに追いやるのがベストですが、
本来であればbuildModulesで使用するライブラリはdevDependenciesに入れるべきなのですが、Lambda実行時にエラーが出てしまします。
Nuxt.jsをExpressの上に乗せて動かしている事による弊害です。

対処

今回はローカルで実行するコマンドに対して、IS_LOCAL=trueというenvをセットするようにし、
nuxt.config.tsに下記の関数を定義する事により対処しました。

nuxt.config.ts
function requireBuildModule(module: string): string {
  return process.env.IS_LOCAL === 'true' ? module : '';
}

使用方法は下記の通りです。

nuxt.config.ts
buildModules: [
  // Doc: https://github.com/nuxt/typescript
  requireBuildModule('@nuxt/typescript-build'),
  // Doc: https://axios.nuxtjs.org/usage
  requireBuildModule('@nuxtjs/axios'),
  // Doc: https://github.com/nuxt-community/dotenv-module
  requireBuildModule('@nuxtjs/dotenv'),
  // Doc: https://tailwindcss.com/
  requireBuildModule('@nuxtjs/tailwindcss'),
  // Doc: https://pwa.nuxtjs.org
  requireBuildModule('@nuxtjs/pwa'),
  // Doc: https://github.com/Developmint/nuxt-webfontloader
  requireBuildModule('nuxt-webfontloader'),
].filter(Boolean),

Lambdaから実行する際にはIS_LOCALが定義されていないので、上記のbuildModuleは全て空文字の配列になります。
そのまま空文字を残したままにしてしまうとエラーが出てしまうので、最後にfilter(Boolean)で空文字を消しています。
これによりLambdaから実行する際にはbuildModuleは読み込まれなくなる為、依存ライブラリをほぼ全てdevDependenciesへ移行することが可能になります。

Build時にModuleもBundleするように変更する

Nuxt.jsはビルド時にassetsやら.vueファイルやらがBundleされて出力されますが、
moduleに定義されている物はBundle対象から除外されます。
例) @nuxtjs/tailwindcss等

その為、ビルド自体は成功するのですが実際にデプロイするとどこかしらでエラーが出てしまいます。

対処

実はドキュメントにも記載されていない、実際にNuxt.jsのソースコードを見ないと気づかない隠し設定が存在します。

nuxt.config.ts
build: {
  standalone: true,
  extend (config: WebpackConfiguration, ctx: any) {
  },
},

上記のstandalone: trueを追記する事により、ビルド時にmoduleに定義されている物も一緒にBundleされるようになります。
これ気づくのに結構時間掛かりました。tailwindcss諦めようかと思ってたくらい

まとめ

軽い気持ちでテンプレ作ろうと思ったらめちゃくちゃ時間掛かった
つらい

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

社内利用ツールを2020年らしいプロセスで作ってみる(Vue.js + Electron + SQLite3) Day3

ここまでの流れ

とあるSIerにおいて、社内システムで案件、要員、収益などの登録、参照は出来るものの、計画立案時にアドホックに変えながら計数を見るような機能がなくて困っているため、休暇中にツールを作ってみる試みです。

  1. ツール作成の目的と動作環境を検討する -> Day1
  2. アーキテクチャをいったん検討する -> Day1
  3. ユースケースを洗い出す -> Day2
  4. 画面イメージのスケッチを作成する -> Day2
  5. ドメインモデルを検討する(用語の定義、振る舞いの定義) -> Day3
  6. ユースケース、画面イメージのワイヤフレーム、ドメインモデルを何周か見直す -> Day3, Day4
  7. 当初決めた目的が果たせるか利用者目線を検証する(コンセプトの検証)
  8. 単純なCRUDアプリでアーキテクチャを検証する(実現方式の検証)
  9. コアになるユースケース1本だけプロトタイプ作成(MVP)
  10. 本格的に準備して開発を始める(テスト駆動のイテレーション開始)

【Qiita】社内利用ツールを2020年らしいプロセスで作ってみる(Vue.js + Electron + SQLite3) Day1
【Qiita】社内利用ツールを2020年らしいプロセスで作ってみる(Vue.js + Electron + SQLite3) Day2

開発日記(Day3)

ドメインモデルを検討する

draw.ioで、新規ファイルを作成します。
ユースケース図のときと同じように、GitHubでissueを切ってbranchを作って、作成したbranchのuml/を保存先に指定します。

エンティティと関連の洗い出し

最初は属性などの細かいことを考えずに、エンティティと関連だけを考えていきますが、後で属性や振る舞い(関数)も書き込んでいくので、"Class" 部品を詳細を畳んだ状態で使うことにしました。
集約ルートに該当するルートエンティティは "Interface 2" 部品を使って、Interface->集約ルートとして書きたかったのですが、畳めないので全て "Class" で統一することにします。

書き始めると分からなくなるもので、以下の記事からとにかくいろいろ読みました。
【はてなブログ】日本語のドメイン駆動設計 (Domain-Driven Design - DDD) 関連記事まとめ

特に参考にしたのが、やはりというべきか増田さんの資料です。
【SlideShare】3週連続DDDその1 ドメイン駆動設計の基本を理解する

インクリメンタルに育てるというところで、44Pからのモデル成長過程が資料に落ちているのが見つかったのはこれだけです。これが初版の心理的なハードルをとても下げてくれました。

domainModel_v01.png

プルリク作ってmasterにマージをして、3点セットが以下の状態になりました。

  • ユースケース図: draw.ioで書いたsvgファイルがGitHubのmasterにマージ済
  • 画面スケッチ: 紙とペンで書いたスケッチがノートにある
  • ドメインモデル図: draw.ioで書いた概念レベルのsvgファイルがGitHubのmasterにマージ済

ユースケース、画面イメージのワイヤフレーム、ドメインモデルの見直し

コンセプト検証の目線で要素の過不足を確認

3つを並べてみると、登場する要素が異なっているのに気付きました。
要素が多い、詳細化が進んでいる順で、画面イメージ>ユースケース>ドメインモデルの順でした。

Day2 では画面を作ることでユースケースの不足が出てきて喜んでいましたが、あれは完全に逆でした。
当初の機能としては作らなくてもいい機能(コンセプト検証には不要なはずの機能)が盛り込まれていました。

皮肉なことですが、最初に作ったユースケースが一番コンセプトを正確に表しているように感じたので、まずは画面とモデルをこれに合わせるように見直していくことにします。

スケッチのワイヤフレーム化

とうとうFigmaを使います。
Sketchなどのデザインツールを使ったことがある人からすると操作感などはハードルがかなり低いという評判のようですが、デザインすること自体がはじめての私には3日目のこれが最大の山場だった可能性が高いです。。

このあたりのページが大変参考になりました。先人の知恵には感謝感謝です。

【Webメディア/ferret】デザインツールFigma(フィグマ)の使い方とは?デザイナー以外の方でも有効活用できる

【個人ブログnote】Figmaの使い方、全力でまとめました。

この途中でネットが激混みで遅くなって休憩入りましたがどうやら戻りそうにないので、予定よりも進んでいませんが今日は終わることにします。

用語の定義

振る舞いの検討

Day4にむけて

とても中途半端に終わってしまっているので、画面のワイヤーフレーム化からいきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【SpecTest GUI】MonacoEditor + Vue.js/Electron

SpecTest GUI ヘの道(1)

誰向け?

  • VSCode で使われている Monaco Editor に興味ある人
  • Monaco Editor で Markdown Editor を Electron ベースで 作りたい人
  • SpecTest を 応援してくれる

尚、今回の結果は以下にコミットしてあります。

はじめに

SpecTest は私が欲しいと思っていた BDD を実現するための汎用フレームワーク。

  • SpecTest そのものについては ここ を参照してください。
  • リポジトリは ここ です。

今回はいつもと趣向を変えて、SpecTest GUI への道 と題して GUI 作っていきます。Kinx と両方並行して進めます。どっちかと言うとこっちがサイド・プロジェクト的な。

Markdown Editor ベース

私は普段 Virtual Studio Code にお世話になっているのだが、Markdown メモには Joplin を使っている。なので、Markdown エディタが別にあること自体に苦はないし、便利だと思う。色々 VSCode だけで行けるほうがいいとも思うが、ニッチに特化したツールはそれなりに存在意義があるし。

SpecTest も基本はマークダウンなので、Markdown Editor 的な何かがベースになると良いかなー。ということで、ちょっとした Markdown Editor ベースでいきます。結構長くなりそうなので、記事は分割します。今回は、簡易 Markdown Editor を作るところまで。最初は MavonEditor が良さそうだと思ったが、エディタ部分のフォントが変えられず、融通の利かなさでパスすることに。

簡単な Markdown Editor のサンプルにはなると思う。とは言っても今回の目玉、前々から使ってみたかった Monaco Editor を使うので意味はある。

そう、アレです。Virtual Studio Code で使われているアレです。実は、当初エディタは Atom を使おうと思っていたのだが、動作が重い...。そこで Visual Studio Code にしてみたところすこぶる軽快。しかし、どちらも Electron ベースだというではないか。 この違いは一体何だ?、と行きついたのが Monaco Editor。これは期待できる。

Monaco Editor の使い方や、Monaco Editor を使いたい人に参考になれば。

準備

node.js インストール

node.js は大体の人がインストール済みですかね。ここ からインストール。

vuecli インストール、プロジェクトの作成

以前は electron-vue を使っていたのですが、最近は vuecli + electron-builder らしいですね。今回はそれでいきます。

vuecli をインストール。既にインストール済みなら飛ばしてください。

$ npm install -g @vue/cli

プロジェクトの作成。プロジェクト名は spectest-gui にします。

$ vue create spectest-gui

Manually select features を選んで、Router と Vuex を選択します。私はいつもこれです。Router は使わないか...。あとは、TypeScript を使うかどうか。今時は使うべきかもしれないが、時間もないので慣れている JavaScript にしてしまう。

Vue CLI v4.2.3
? Please pick a preset: Manually select features
? Check the features needed for your project:
 (*) Babel
 ( ) TypeScript
 ( ) Progressive Web App (PWA) Support
 (*) Router
>(*) Vuex
 ( ) CSS Pre-processors
 (*) Linter / Formatter
 ( ) Unit Testing
 ( ) E2E Testing

あとはデフォルト。

?  Successfully created project spectest-gui.
?  Get started with the following commands:

 $ cd spectest-gui
 $ npm run serve

上記が出れば成功。

electron-builder

プロジェクトが無事作成されたら、早速 electron-builder 入れてみましょう。

$ cd spectest-gui
$ vue add electron-builder

Choose Electron Version とか聞かれるので迷わず新しいのを。

? Choose Electron Version (Use arrow keys)
  ^4.0.0
  ^5.0.0
> ^6.0.0
✔  Successfully invoked generator for plugin: vue-cli-plugin-electron-builder

成功。

起動してみましょう。

$ npm run electron:serve

Vue

おぉ。

この辺で VSCode とかを立ち上げると、既に git リポジトリができていて、初版がコミットされており、既に変更があることが確認できます。必要に応じて git にコミットしておきましょう(たぶん git コマンドがないとできないと思うけど、git コマンドが無いと無視されるのかどうかとかは既に入っていたのでわかんない)。

$ git add .
$ git commit -m "added electron builder"

Vuetify

やはり流行に乗ってマテリアル・デザインで Vuetify を使います。好きなので。色々揃ってて良いですねえ。

$ vue add vuetify

以下が聞かれますが、とりあえずデフォルトで進めます。

? Choose a preset: (Use arrow keys)
> Default (recommended)
  Prototype (rapid development)
  Configure (advanced)
✔  Successfully invoked generator for plugin: vue-cli-plugin-vuetify
 vuetify  Discord community: https://community.vuetifyjs.com
 vuetify  Github: https://github.com/vuetifyjs/vuetify
 vuetify  Support Vuetify: https://github.com/sponsors/johnleider

成功したようですね。起動してみます。

$ npm run electron:serve

Vuetify

おぉ、変わった。

splitpanes / vue-monaco / marked / highlight.js

そしてお待ちかね、Monaco Editor の出番です。vue-monaco というパッケージがあります。ついでに今回、マルチペインで作業できるようにするつもりなので、splitpanes というライブラリを入れてしまいます。また、表示用に markedhighlight.js も入れます。さらに github-markdown-css も入れておきましょう。

$ npm install splitpanes
$ npm install vue-monaco
$ npm install marked
$ npm install highlight.js
$ npm install github-markdown-css

fontawesome

間違いなく fontawesome のアイコンは使います。入れておきます。

$ npm install @fortawesome/fontawesome-svg-core
$ npm install @fortawesome/free-solid-svg-icons
$ npm install @fortawesome/vue-fontawesome
$ npm install @fortawesome/free-brands-svg-icons
$ npm install @fortawesome/free-regular-svg-icons

色々入ったので、また変更が通知されているはずです。コミットしてしまいましょう。

$ git add .
$ git commit -m "added vuetify and some modules"

簡易 Markdown Editor

さて、Markdown Editor つくりますよ。

アイコンの設定

まず、fontawesome アイコンを使えるようにしてしまいましょう。src/main.js を開いて、import 文の最後の行の次あたりに以下の行を追加。

main.js
import { library } from '@fortawesome/fontawesome-svg-core'
import { fas } from '@fortawesome/free-solid-svg-icons'
import { fab } from '@fortawesome/free-brands-svg-icons'
import { far } from '@fortawesome/free-regular-svg-icons'
library.add(fas, far, fab)

初期画面の整理

ひとまず以下のような感じで構成してきます。

src/App.vue
   /components/MarkdownPane.vue
              /markdown/Editor.vue
              /markdown/VIewer.vue

トップレベル(src/App.vue

Vuetify の <v-app><v-app-bar><v-content> を配置し、<v-content>MarkdownPane を配置します。全体を書くと以下の通り。基本、あったものをざっくり消して、HelloWorldMarkdownPane に変える。

App.vue
<template>
  <v-app>
    <v-app-bar app ref="appbar">
      <v-app-bar-nav-icon></v-app-bar-nav-icon>
      <v-toolbar-title>SpecTest GUI</v-toolbar-title>
    </v-app-bar>

    <v-content>
      <MarkdownPane />
    </v-content>
  </v-app>
</template>

<script>
import MarkdownPane from './components/MarkdownPane';

export default {
  name: 'App',

  components: {
    MarkdownPane,
  },

  data: () => ({
    //
  }),
};
</script>

src/components/MarkdownPane.vue が無いので、まだ起動できない。

ペイン分割(src/components/MarkdownPane.vue

中身のない src/components/MarkdownPane.vue を作ります。splitpanes で左右にペイン分割する。

MarkdownPane.vue
<template>
  <splitpanes class="default-theme" :style="{ height: '100%', overflow: 'hidden' }">   
    <pane class="pane-editor" ref="epane" size="55">
    </pane>
    <pane class="pane-view" ref="vpane">
    </pane>
  </splitpanes>
</template>

<script>
import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'

export default {
  name: 'MarkdownPane',

  components: {
    Splitpanes, Pane,
  },
};
</script>

これで一応動くようにはなる。まだエディタ組み込んでませんが、ペイン分割の動作を確認できます。

Panes

こんな感じ。真ん中のスプリッタでぐりぐりと動かせます。

Window の大きさ調整

Electron を使っているとだいたいそうなのだが、ウィンドウのリサイズに追随してくれないコンポーネントが結構ある。なので、ウィンドウ・サイズを Vuex のストアに確保しておき、各コンポーネントで参照できるようにしておく。具体的には、App.vue にハンドラを設置。その際、上部のアプリケーションバーのサイズを含めないようにあらかじめ引き算しておく。軽く 3 引いているのは、誤差でスクロールバーが出たり変な感じになることがあったので気持ち少なめに程度の意味。

まず、store でウィンドウサイズを保存するように修正。

src/store/index.js
export default new Vuex.Store({
  state: {
    windowSize: { width: 0, height: 0 },
  },
  mutations: {
    setWindowSize (state, appbar) {
      state.windowSize.width = window.innerWidth
      state.windowSize.height = window.innerHeight - appbar.clientHeight - 3
    },
  },
  ...

次に App.vue の <v-app-bar> タグに ref をつけ、バーの高さを渡せるようにした上で、

App.vue
    <v-app-bar app ref="appbar">

methodshandleResize を追加し、ハンドラとして呼ばれるように mountedbeforeDestroy でリスナーに登録し、ウィンドウサイズを store にセットをする。

  methods: {
    handleResize: function() {
      this.$store.commit('setWindowSize', this.$refs.appbar.$el)
    },
  },

  mounted: function () {
    window.addEventListener('resize', this.handleResize)
    this.$store.commit('setWindowSize', this.$refs.appbar.$el)
  },

  beforeDestroy: function () {
    window.removeEventListener('resize', this.handleResize)
  },

テキストデータ共有

テキストエディタで編集したデータは、marked で変換されて表示される。なので、編集ドキュメント自体も store で管理。statecode として追加し、upadteCode でアップデートできるようにしておく。さっきのと合わせるとこんな感じ

src/store/index.js
export default new Vuex.Store({
  state: {
    windowSize: { width: 0, height: 0 },
    code: "",
  },
  mutations: {
    setWindowSize (state, appbar) {
      state.windowSize.width = window.innerWidth
      state.windowSize.height = window.innerHeight - appbar.clientHeight - 3
    },
    updateCode (state, code) {
      state.code = code;
    }
  },
  ...

Markdown エディタの配置

お待ちかねの MonacoEditor を組み込む時間です。

まず、src/components/markdown/Editor.vue を作ります。あらかじめ設定しているのは以下の点。

  • MonacoEditor は自分でレイアウトに追随してくれないので、リサイズで再レイアウトさせる必要がある。そのための仕組みを入れておく。
  • resize 自体は親コンポーネントから受け取る。
  • code はローカルに編集するので this に持たせておき、変更をウォッチして store にコピーする。
  • 縦横サイズはウィンドウサイズをもとに設定してやらないとうまくレイアウトできない。Pane で各ペインの高さをそろえて、その高さに Editor を合わせる。
  • @editorWillMount イベントを受け取って、monaco インスタンスを確保しておく。これは後で使う。
  • fontSize は一応設定値として持っておく。
  • エディタが見やすいように、上下に少しマージンを入れておく。

全部入れると次のようになる。

src/components/markdown/Editor.vue
<template>
  <MonacoEditor ref="editor" v-model="code" language="markdown" class="mdeditor" :style="{ width: width, height: height }"
    :options="{ scrollBeyondLastLine: false, wordWrap: 'on', fontSize: fontSize }"
    @editorWillMount="onEditorWillMount"
  />
</template>

<script>
import MonacoEditor from 'vue-monaco'

export default {
  name: 'MarkdownEditor',
  components: { MonacoEditor },

  data: () => ({
    code: '',
    monaco: null,
    fontSize: 12,
    clientWidth: 1,
    clientHeight: 1,
  }),

  methods: {
    onEditorWillMount: function(monaco) {
      this.monaco = monaco
    },
    resize: function(el) {
      this.clientWidth = el.clientWidth - 1;
      this.clientHeight = el.clientHeight - 3;
      this.$nextTick(() => {
        this.$refs.editor.getEditor().layout()
      })
    },
  },

  computed: {
    width() {
      return this.clientWidth + 'px'
    },
    height() {
      return this.clientHeight + 'px'
    },
  },

  watch: {
    code() {
      this.$store.commit('updateCode', this.code)
    },
  },
};
</script>

<style scoped>
.mdeditor {
  margin-top: 6px;
  margin-bottom: 8px;
}
</style>

また、splitpanes でペインサイズが変わったときも追随してくれない。なので、その仕組みを入れておく。修正すると次のようになる。MarkdownEditor のメソッドを呼ぶので ref をつけておく。

src/components/MarkdownPane.vue
<template>
  <splitpanes class="default-theme" :style="{ height: height, overflow: 'hidden' }" @resized="resizedPane($event)">
    <pane class="pane-editor" ref="epane" size="55">
      <MarkdownEditor ref="editor" />
    </pane>
    <pane class="pane-view" ref="vpane">
    </pane>
  </splitpanes>
</template>

<script>
import MarkdownEditor from './markdown/Editor'
import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'

export default {
  name: 'MarkdownPane',

  components: {
    MarkdownEditor, Splitpanes, Pane,
  },

  methods: {
    resizedPane: function() {
      this.$refs.editor.resize(this.$refs.epane.$el)
    }
  },

  computed: {
    height () {
      return (this.$store.state.windowSize.height - 1) + "px"
    },
  }
};
</script>

リサイズも通知されるように、App.vuehandleResize で通知するように修正。mounted のときも通知しておく。App.vue も全部載せると以下のようになる。

src/App.vue
<template>
  <v-app>
    <v-app-bar app ref="appbar" height="56">
      <v-app-bar-nav-icon></v-app-bar-nav-icon>
      <v-toolbar-title>SpecTest GUI</v-toolbar-title>
    </v-app-bar>

    <v-content>
      <MarkdownPane ref="pane" />
    </v-content>
  </v-app>
</template>

<script>
import MarkdownPane from './components/MarkdownPane';

export default {
  name: 'App',

  components: {
    MarkdownPane,
  },

  data: () => ({
    //
  }),

  methods: {
    handleResize: function() {
      this.$store.commit('setWindowSize', this.$refs.appbar.$el)
      this.$refs.pane.resizedPane()
    },
  },

  mounted: function () {
    window.addEventListener('resize', this.handleResize)
    this.$store.commit('setWindowSize', this.$refs.appbar.$el)
    this.$nextTick(() => {
      this.$refs.pane.resizedPane()
    })
  },

  beforeDestroy: function () {
    window.removeEventListener('resize', this.handleResize)
  },
};
</script>
...

ここまでできると、エディタが使える。

Editor

やった!

VSCode と同じだ。検索も置換もできる。Minimap も付いてる。なんて素晴らしい。

Markdown ビューワの配置

編集できるだけではイマイチですね。ちゃんと HTML に変換して表示しましょう。src/components/markdown/Viewer.vue を作ります。ここでは試行錯誤した結果のみ先に見せます。

src/components/markdown/Viewer.vue
<template>
  <div id="viewer" class="mdviewer" :style="{ width: width, height: height, overflow: 'auto' }" ref="viewer">
    <div v-html="markdown" class="mdviewer-body markdown-body" />
  </div>
</template>

<script>
import marked from 'marked';
import hljs from 'highlight.js';
import 'highlight.js/styles/github-gist.css'
import 'github-markdown-css/github-markdown.css'

export default {
  name: 'MarkdownViewer',

  created: function () {
    marked.setOptions({
      langPrefix: '',
      highlight: function(code, lang) {
        var l = lang.split(':');
        return hljs.highlightAuto(code, [l[0]]).value
      }
    });
  },

  data: () => ({
    clientWidth: 1,
    clientHeight: 1,
  }),

  methods: {
    resize: function(el) {
      this.clientWidth = el.clientWidth - 1;
      this.clientHeight = el.clientHeight - 3;
    },
  },

  computed: {
    markdown: function () {
      return marked(this.$store.state.code) + '<br />'
    },
    width() {
      return this.clientWidth + 'px'
    },
    height() {
      return this.clientHeight + 'px'
    },
  }
}
</script>

<style>
.mdviewer {
  margin-top: 6px;
  margin-bottom: 8px;
}
.mdviewer-body {
  padding: 8px;
}

code {
  font-size: 85% !important;
  padding: 0px !important;
}
pre>code {
  width: 100%;
  padding: 0.5em;
  color: inherit !important;
  -webkit-box-shadow: inset 0 0px 0 #ffffff !important;
  box-shadow: inset 0 0px 0 #ffffff !important;
}
pre>code:after, pre>code:before {
  content: "" !important;
  letter-spacing: 0px !important;
}
</style>

marked で変換する際の highlight.js の設定を created で行い、エディタと同じようにサイズを調整できるようにして、どうも Vuetify が悪さしているっぽいスタイルを調整(グローバルスコープで !important とか使っているが、こうしないと回避できなかった...他に影響あったら考える)。

文書自体は store に変更があると自動的に computed して変換後の文書が表示される。賢いねー。

また、src/component/MarkdownPane.vue に登録してリサイズされるようにメソッドを追加。splitpanesdefault-theme が邪魔な感じなので消してしまう。全体は以下の通り。

src/component/MarkdownPane.vue
<template>
  <splitpanes :style="{ height: height, overflow: 'hidden' }" @resized="resizedPane($event)">
    <pane class="pane-editor" ref="epane" size="55">
      <MarkdownEditor ref="editor" />
    </pane>
    <pane class="pane-view" ref="vpane">
      <MarkdownViewer ref="viewer" />
    </pane>
  </splitpanes>
</template>

<script>
import MarkdownEditor from './markdown/Editor'
import MarkdownViewer from './markdown/Viewer'
import { Splitpanes, Pane } from 'splitpanes'
import 'splitpanes/dist/splitpanes.css'

export default {
  name: 'MarkdownPane',

  components: {
    MarkdownEditor, MarkdownViewer, Splitpanes, Pane,
  },

  methods: {
    resizedPane: function() {
      this.$nextTick(() => {
        this.$refs.editor.resize(this.$refs.epane.$el)
        this.$refs.viewer.resize(this.$refs.vpane.$el)
      })
    }
  },

  computed: {
    height () {
      return (this.$store.state.windowSize.height - 1) + "px"
    },
  }
};
</script>

Viewer

おぉー。エディタだ。

おわりに

ここまでで簡単な Markdown Editor ができました。保存とかできないけど。あと、やはりスクロールは連動してほしい。

ということで、次回は スクロール連動 を実現させます。

ここまでの結果は、以下にコミットしてあります。

SpecTest そのものに関しては以下を参照してください。

ではまた次回。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

yarn devをしても動作しない(セキュリティソフト問題)

vue.jsのCLIの操作に慣れるため、Qiitaに投稿されている諸先輩方の記事を参考にアプリケーションを生成、
動作させようとしましたが、「yarn dev」コマンドを入力してもブラウザに表示されませんでした。
結論から申し上げますと、PCに導入しているセキュリティーソフト「ESET」の設定において、有効にしているHTTPプロトコルに問題がありました。

まずはESETのアプリを立ち上げ、左のサイドメニューから、
 設定ボタン > 「Webアクセス保護」の設定ボタン をクリックし、HTTPプロトコルで使用するポート欄に記載のアドレス

80,8080,3128

から先頭の「80,」を削除し、ウィンドウを閉じると変更を保存するか聞かれるので保存をクリック。
以上で、問題が解決しました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む