tag:crieit.net,2005:https://crieit.net/users/naoki0719/feed naoki0719の投稿 - Crieit Crieitでユーザーnaoki0719による最近の投稿 2020-06-01T21:53:26+09:00 https://crieit.net/users/naoki0719/feed tag:crieit.net,2005:PublicArticle/15917 2020-06-01T21:52:34+09:00 2020-06-01T21:53:26+09:00 https://crieit.net/posts/nuxt-content-5ed4fa12e42ad @nuxt/contentで画像を最適化する <p>先日導入した@nuxt/content で画像をどうやって最適化するか模索して、nuxt-optimized-images が良さそうだったので紹介します。</p> <h2 id="今回使用した主な環境"><a href="#%E4%BB%8A%E5%9B%9E%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%9F%E4%B8%BB%E3%81%AA%E7%92%B0%E5%A2%83">今回使用した主な環境</a></h2> <ul> <li>@aceforth/nuxt-optimized-images: 1.0.1</li> <li>@nuxt/content: 1.2.0</li> <li>nuxt: 2.12.2</li> </ul> <h2 id="nuxt-optimized-images のインストール"><a href="#nuxt-optimized-images+%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">nuxt-optimized-images のインストール</a></h2> <p>こちらは<a target="_blank" rel="nofollow noopener" href="https://aceforth.com/docs/nuxt-optimized-images/">公式ドキュメント</a>に従って、インストールしました。</p> <p>今回は png 画像だけできれば良かったので、最適化に使うパッケージは 1 つだけにしています。</p> <pre><code class="sh">yarn add -D @aceforth/nuxt-optimized-images imagemin-pngquant </code></pre> <p>もし他の種類も最適化したい場合は、追加でインストールします。</p> <p>詳細は<a target="_blank" rel="nofollow noopener" href="https://aceforth.com/docs/nuxt-optimized-images/#optimization-packages">公式ドキュメント</a>で確認してください。</p> <h3 id="nuxt の設定に追加する"><a href="#nuxt+%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%81%AB%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B">nuxt の設定に追加する</a></h3> <p>次に nuxt.config.ts にモジュールを追加します。</p> <pre><code class="ts">{ buildModules: [ '@aceforth/nuxt-optimized-images', ], optimizedImages: { // モジュールのオプション optimizeImages: true } } </code></pre> <h2 id="マークダウンで画像を表示する"><a href="#%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%80%E3%82%A6%E3%83%B3%E3%81%A7%E7%94%BB%E5%83%8F%E3%82%92%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B">マークダウンで画像を表示する</a></h2> <p> 一般的なマークダウン記法であれば<code>![画像](/images/sample.png)</code>のようにすれば表示することができます。</p> <p>しかし、今回のパッケージで最適化するには別の方法で行います。</p> <h3 id="components に画像読み込み用のコンポーネントを追加"><a href="#components+%E3%81%AB%E7%94%BB%E5%83%8F%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF%E7%94%A8%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%82%92%E8%BF%BD%E5%8A%A0">components に画像読み込み用のコンポーネントを追加</a></h3> <pre><code class="vue"><!-- ~/components/ImageLoader.vue --> <template> <img :src="require(`~/assets/images/${this.file}`)" alt="" /> </template> <script lang="ts"> import { Vue, Component, Prop } from 'nuxt-property-decorator' @Component export default class ImageLoader extends Vue { @Prop({ required: true }) public file!: string } </script> </code></pre> <p>props で使用する画像ファイル名を指定するようにしました。</p> <p>このように、require('画像パス')とすることで画像の最適化が自動的に行われるようになります。</p> <h3 id="マークダウンを呼び出すコンポーネントを修正"><a href="#%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%80%E3%82%A6%E3%83%B3%E3%82%92%E5%91%BC%E3%81%B3%E5%87%BA%E3%81%99%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%82%92%E4%BF%AE%E6%AD%A3">マークダウンを呼び出すコンポーネントを修正</a></h3> <pre><code class="vue"><!-- ~/pages/index.md --> <script lang="ts"> import { Vue, Component } from 'nuxt-property-decorator' import { ImageLoader } from '~/components/ImageLoader.vue' @Component({ components: { ImageLoader } }) export default class Index extends Vue {} </script> </code></pre> <p>マークダウンを処理するコンポーネントに先ほど作成した、ImageLoader.vue を追加しておきます。</p> <h3 id="マークダウンファイルで使う"><a href="#%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%80%E3%82%A6%E3%83%B3%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%A7%E4%BD%BF%E3%81%86">マークダウンファイルで使う</a></h3> <pre><code class="md">--- title: '画像を表示する` --- ## ここに画像を表示します <image-loader file="ファイル名" /> </code></pre> <p>これで<code>nuxt generate</code>することでハッシュ化された、ファイルが出力されるようになります。</p> <h2 id="画像のリサイズ"><a href="#%E7%94%BB%E5%83%8F%E3%81%AE%E3%83%AA%E3%82%B5%E3%82%A4%E3%82%BA">画像のリサイズ</a></h2> <p>画像のリサイズはさらにパッケージの追加が必要です。</p> <pre><code class="sh">yarn add -D responsive-loader sharp </code></pre> <p>sharp は jimp でもいいようですが、jimp は遅いみたいです。</p> <h3 id="ImageLoader.vue を変更"><a href="#ImageLoader.vue+%E3%82%92%E5%A4%89%E6%9B%B4">ImageLoader.vue を変更</a></h3> <p>リサイズのアクションは require するときに、サイズを決めなくてはいけません。</p> <p>size を動的にしたくて props を追加してやってみたのですが、うまくいかなかったので決め打ちで設定することにしました。</p> <p>以下のように ImageLoader.vue を変更します。</p> <pre><code class="vue"><template> <img :src="resizeImage.src" alt="" /> </template> <script lang="ts"> import { Vue, Component, Prop } from 'nuxt-property-decorator' @Component export default class ImageLoader extends Vue { @Prop({ required: true }) public file!: string public resizeImage = require(`~/assets/images/${this.file}?resize&size=600`) } </script> </code></pre> <h3 id="マークダウンで画像のサイズを指定する"><a href="#%E3%83%9E%E3%83%BC%E3%82%AF%E3%83%80%E3%82%A6%E3%83%B3%E3%81%A7%E7%94%BB%E5%83%8F%E3%81%AE%E3%82%B5%E3%82%A4%E3%82%BA%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B">マークダウンで画像のサイズを指定する</a></h3> <pre><code class="md">--- title: '画像を表示する` --- ## ここに画像を表示します <image-loader file="ファイル名" /> </code></pre> <p>これで任意のサイズ、今回は<code>width: 400px</code>にリサイズされた画像を出力できるようになりました。</p> <h2 id="終わりに"><a href="#%E7%B5%82%E3%82%8F%E3%82%8A%E3%81%AB">終わりに</a></h2> <p>マークダウンに vue コンポーネントが混じってしまうのが気持ち悪いですが、これで画像も自由なサイズで表示できました。</p> <p>次回はレスポンシブの表示にも対応していきたいです。</p> naoki0719 tag:crieit.net,2005:PublicArticle/15916 2020-05-31T20:37:48+09:00 2020-06-05T13:29:34+09:00 https://crieit.net/posts/nuxt-content @nuxt/contentでブログを構築してみました <p>これまで nuxt と microCMS を組み合わせて JAMStack なブログを Netlify で公開していましたが、コンテンツ自体も一緒に管理できたほうが効率的かつ外部サービスに依存したくないと考え、作り直しのため markdown-it を採用つもりでした。</p> <p>しかし<a target="_blank" rel="nofollow noopener" href="https://content.nuxtjs.org/">@nuxt/content</a> が公開されたことを知り、早速導入をしてみました。</p> <h2 id="@nuxt/content の機能について"><a href="#%40nuxt%2Fcontent+%E3%81%AE%E6%A9%9F%E8%83%BD%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6">@nuxt/content の機能について</a></h2> <ul> <li>ホットリロード</li> <li>Markdown の Vue コンポーネント</li> <li>全文検索</li> <li><code>nuxt generate</code>で静的サイトの生成</li> <li>MongoDB のような QueryBuilder</li> <li>PrismJS によるマークダウンファイルのコードブロックをスタイリングする</li> <li>目次の生成</li> <li>Markdown、CSV、YAML、JSON を処理する</li> <li>hooks による拡張</li> <li>types による Typescript のサポート (v1.0.1)</li> </ul> <h2 id="インストール時の環境"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%99%82%E3%81%AE%E7%92%B0%E5%A2%83">インストール時の環境</a></h2> <ul> <li>MacOS Catalina 10.15.4</li> <li>yarn 1.22.4</li> <li>NuxtJS 2.12.2 (TypeScript)</li> <li>@nuxt/content v1.2.0</li> </ul> <h2 id="インストール"><a href="#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">インストール</a></h2> <p>まずはパッケージをインストールします。</p> <p><code>yarn add @nuxt/content</code></p> <p>次に nuxt.config.ts に以下を追加します。</p> <pre><code># nuxt.config.ts { modules: [ '@nuxt/content', ], content: { // デフォルト以外の設定 } } </code></pre> <h2 id="TypeScript の対応"><a href="#TypeScript+%E3%81%AE%E5%AF%BE%E5%BF%9C">TypeScript の対応</a></h2> <p><code>tsconfig.json</code>に以下を追加します。</p> <pre><code class="json">{ "compilerOptions": { "types": ["@nuxt/types", "@nuxt/content"] } } </code></pre> <p>これを追加することで Context を拡張して、後に出てくる<code>$content</code>の参照が行われるようにします。</p> <h2 id="コンテンツの管理"><a href="#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84%E3%81%AE%E7%AE%A1%E7%90%86">コンテンツの管理</a></h2> <p>コンテンツの管理はデフォルトでは<code>~/content</code>以下で行います。</p> <p>それぞれのファイルはサブディレクトリでも管理することができます。</p> <p>今回は<code>~/content/articles/sample.md</code>として作成してみます。</p> <pre><code class="md">--- title: '日本語タイトルを使えます' date: 2020-05-23 tags: [Markdown, NuxtJS] --- # 記事タイトル コンテンツの内容 </code></pre> <p><code>@nuxt/content</code>で slug は、ファイル名の拡張子を除く部分となっています。</p> <p>YAML Front Matte の機能として記事のメタデータを管理することができ、<code>tags</code>のように配列で記述できます。</p> <p>これらのメタデータはクエリを使って絞り込みに利用できます。</p> <p>注意点として<code>title</code>を<code>title: 1</code>のように文字列以外を入力すると、文字列の関数がないことでエラーが発生します。この場合はシングルクォートで括るようにすれば回避できます。</p> <h2 id="コンテンツの表示"><a href="#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%B3%E3%83%84%E3%81%AE%E8%A1%A8%E7%A4%BA">コンテンツの表示</a></h2> <p><code>asyncData</code>を使ってサーバーサイドで記事を取得します。</p> <pre><code class="vue"><template> <div> <div><span>{</span><span>{</span> page.title <span>}</span><span>}</span></div> <div><span>{</span><span>{</span> page.date <span>}</span><span>}</span></div> <nuxt-content :document="page" /> </div> </template> <script lang="ts"> import { Vue, Component } from 'nuxt-property-decorator' import { Context } from '@nuxt/types' @Component export default class Sample extends Vue { async asyncData({ $content, params, error }: Context) { const page = await $content('articles/sample').fetch() return { page } } } </script> </code></pre> <p><code>$content('articles/sample')</code>とすると、<code>content/articles/sample</code>を記事として取得します。</p> <p>複数の記事をまとめて取得するには<code>$content('articles')</code>として、<code>content/articles</code>以下にある全ての記事を配列で取得することができます。</p> <p>サーバーサイドで取得後、<code>page</code>変数には記事ファイルのメタデータとコンテンツが読み込まれ、<br /> <code><nuxt-content :document="page"></code>で HTML にマークアップされた状態で表示されます。</p> <h2 id="コードブロックのテーマを変更する"><a href="#%E3%82%B3%E3%83%BC%E3%83%89%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E3%81%AE%E3%83%86%E3%83%BC%E3%83%9E%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%99%E3%82%8B">コードブロックのテーマを変更する</a></h2> <p><code>@nuxt/content</code>でコードブロックのハイライトは<code>prismjs</code>で行われているので、<code>prism-themes</code>をインストールすれば好きなテーマへ変更することができます。</p> <pre><code class="shell">yarn add prism-themes </code></pre> <p>nuxt.config.ts でテーマの変更を設定します。</p> <pre><code class="ts"># nuxt.config.ts { modules: [ '@nuxt/content', ], content: { markdown: { prism: { // ここに使いたいcssのテーマを設定します theme: 'prism-themes/themes/prism-vsc-dark-plus.css' } } } } </code></pre> <h2 id="タグの検索をする"><a href="#%E3%82%BF%E3%82%B0%E3%81%AE%E6%A4%9C%E7%B4%A2%E3%82%92%E3%81%99%E3%82%8B">タグの検索をする</a></h2> <p>メタデータが配列の場合は、単純な where では取得できません。</p> <p><code>@nuxt/content</code>のクエリは<a target="_blank" rel="nofollow noopener" href="https://github.com/techfort/LokiJS/wiki">LokiJS</a>を使っているので、公式マニュアルを参照して<code>$contains</code>を使えばいいことがわかりました。</p> <p>具体的には以下の方法で、特定のタグを持つ記事を取得することができます。</p> <pre><code class="ts">await $content('articles') .where({ tags: { $contains: 'Markdown' } }) .fetch() </code></pre> <h2 id="サブディレクトリの記事も取得するには"><a href="#%E3%82%B5%E3%83%96%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%AE%E8%A8%98%E4%BA%8B%E3%82%82%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B%E3%81%AB%E3%81%AF">サブディレクトリの記事も取得するには</a></h2> <p>v1.3.0で実装されました。TypeScriptを使っている場合は<code>$content</code>の引数に問題があるので、v1.3.1を使いましょう。</p> <p>これでフラットな記事管理から解放されます。</p> <pre><code class="ts">const articles = $content('articles', { deep: true }) </code></pre> <h2 id="終わりに"><a href="#%E7%B5%82%E3%82%8F%E3%82%8A%E3%81%AB">終わりに</a></h2> <p>開発速度が速く、どんどん機能追加がされていて、シンプルで使いやすい印象です。</p> <p>実際に導入までの作業も簡単で、公式ドキュメントを見ながら行うことができました。</p> <p>目的としていた記事の一元管理を短時間で構築することができました。今後ますます注目されそうな気がします。</p> naoki0719