\n \u003C/label>\n \u003Cbutton>search\u003C/button>\n \u003C/form>\n\u003C/search> \u003Csearch>: 一般検索要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Csearch> は HTML の要素で、文書やアプリケーションのうち、検索や絞り込み操作を行うことに関連する、フォームコントロールやその他のコンテンツの部分を表すコンテナーです。\u003Csearch> 要素は意味的に、要素の内容の目的が検索や絞り込み機能であることを示します。検索や絞り込み機能は、ウェブサイトやアプリケーション、現在のウェブページや文書、あるいはインターネット全体やそのサブセクションを対象とする可能性があります。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/search",{"id":140,"title":141,"titles":142,"content":143,"level":94},"/html-tags-memory-test#cite","\u003Ccite>",[112,122],"引用先を表示する際に使用する。 \u003Cp>次の引用は有名な文学作品からのものです:\u003C/p>\n\u003Cblockquote>\n \"生きるとは呼吸することではない。行動することだ。\"\n \u003Ccite>— ジャン=ジャック・ルソー, 『エミール』\u003C/cite>\n\u003C/blockquote> \u003Ccite>: 引用元要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Ccite> は HTML の要素で、引用された創作物のタイトルをマークアップするために使用します。この参照は、引用メタデータに関する利用場面に合わせた慣習に応じて省略形が用いられることがあります。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/cite",{"id":145,"title":146,"titles":147,"content":148,"level":94},"/html-tags-memory-test#abbr","\u003Cabbr>",[112,122],"略語を表す。 \u003Cp>\u003Cabbr>HTML\u003C/abbr>(HyperText Markup Language)\u003C/p> \u003Cabbr>: 略語要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cabbr> は HTML の要素で、略語や頭字語を表します。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/abbr",{"id":150,"title":151,"titles":152,"content":153,"level":94},"/html-tags-memory-test#b","\u003Cb>",[112,122],"ユーザーの注意を惹きたい場合に使用する。 ただし、\u003Cb>を太字にするという装飾目的で使用するべきではない。 単にテキストを太字にする場合は CSS の font-weight を使用し、重要な意味を持つテキストは \u003Cstrong>を使用する。 \u003Cb>: 注目付け要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cb> は HTML の要素で、要素の内容に読み手の注意を惹きたい場合で、他の特別な重要性が与えられないものに使用します。これは以前は太字要素と呼ばれており、ほとんどのブラウザーでは文字列を太字で描画していました。しかし、 \u003Cb> を文字列の装飾に使うべきではありません。太字の文字列を作成するには、 CSS の font-weight プロパティを使用し、特別な重要性を持つテキストを示すには \u003Cstrong> 要素を使用してください。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/b",{"id":155,"title":156,"titles":157,"content":158,"level":94},"/html-tags-memory-test#bdi","\u003Cbdi>",[112,122],"ブラウザーの書字方向アルゴリズムにこのテキストが周囲のテキストから独立しているものと扱うよう指示する。 \u003Cbdi>: 書字方向分離要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cbdi> は HTML の要素で、ブラウザーの書字方向アルゴリズムにこのテキストが周囲のテキストから独立しているものと扱うよう指示します。これは特に、ウェブサイトがテキストを動的に挿入するとき、挿入されるテキストの書字方向が不明な場合に便利です。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/bdi",{"id":160,"title":161,"titles":162,"content":163,"level":94},"/html-tags-memory-test#bdo","\u003Cbdo>",[112,122],"現在のテキストの書字方向を上書きし、中のテキストが異なる書字方向で描画されるようにする。 \u003Cbdo>: 双方向文字列上書き要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cbdo> は HTML の要素で、現在のテキストの書字方向を上書きし、中のテキストが異なる書字方向で描画されるようにします。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/bdo",{"id":165,"title":166,"titles":167,"content":168,"level":94},"/html-tags-memory-test#data","\u003Cdata>",[112,122],"商品などを並べる際にidのような識別子を付与する際に使用する。 日付を扱う場合は\u003Cdata>ではなく\u003Ctime>を使用する。 \u003Cul>\n \u003Cli>\u003Cdata value='1'>Apple\u003C/li>\n \u003Cli>\u003Cdata value='2'>Orange\u003C/li>\n \u003Cli>\u003Cdata value='3'>Strawberry\u003C/li>\n\u003C/ul> \u003Cdata>: データ要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cdata> は HTML の要素で、与えられたコンテンツの断片を機械可読な翻訳にリンクします。コンテンツが時刻または日付に関連するものであれば、\u003Ctime> 要素を使用する必要があります。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/data",{"id":170,"title":171,"titles":172,"content":173,"level":94},"/html-tags-memory-test#time","\u003Ctime>",[112,122],"時刻を表す。\ndatetime属性を一緒に使用する。 \u003Ctime>: (日付)時刻要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Ctime> は HTML の要素で、特定の時の区間を表します。datetime 属性を使用して、機械可読な形式の日付を記述することができ、検索エンジンの結果を改善したりリマインダーなどの独自機能に使用したりすることができます。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/time",{"id":175,"title":176,"titles":177,"content":178,"level":94},"/html-tags-memory-test#dfn","\u003Cdfn>",[112,122],"定義句や文の文脈の中で定義している用語を示す。 \u003Cdfn>: 定義要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cdfn> は HTML の要素で、定義句や文の文脈の中で定義している用語を示すために用いられます。祖先である \u003Cp> 要素、\u003Cdt>/\u003Cdd> の組み合わせ、または直近の \u003Csection> 要素が用語の定義とみなされます。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/dfn",{"id":180,"title":181,"titles":182,"content":183,"level":94},"/html-tags-memory-test#kbd","\u003Ckbd>",[112,122],"キーボード、音声入力、その他の入力端末からのユーザーによる文字入力を表す行内の文字列の区間を表します。 \u003Ckbd>: キーボード入力要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Ckbd> は HTML の要素で、キーボード、音声入力、その他の入力端末からのユーザーによる文字入力を表す行内の文字列の区間を表します。慣習的に、ユーザーエージェントは既定で \u003Ckbd> 要素の中身を等幅フォントで表示しますが、 HTML 標準で規定されているものではありません。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/kbd",{"id":185,"title":186,"titles":187,"content":188,"level":94},"/html-tags-memory-test#mark","\u003Cmark>",[112,122],"テキストにマーカーを引くことができる。 \u003Cp>aaaa\u003Cmark>bbb\u003C/mark>\u003C/p> \u003Cmark>: テキストマーク要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cmark> は HTML の要素で、周囲の文脈での関連性によって参照したり表記したりする目的でマーク付けされたり強調表示されたりするテキストを表します。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/mark",{"id":190,"title":191,"titles":192,"content":193,"level":94},"/html-tags-memory-test#q","\u003Cq>",[112,122],"文章中に含まれるインラインの引用。 \u003Cblockquote>と異なり、テキストサイズが短い場合に使用する。 \u003Cq>: インライン引用要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cq> は HTML の要素で、中に含まれるテキストが短いインラインの引用であることを示します。最近の多くのブラウザーでは、文字列を引用符で囲むように実装しています。この要素は、段落区切りをまたがない短い引用のためのものです。長文の引用には、 \u003Cblockquote> 要素を使用してください。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/q",{"id":195,"title":196,"titles":197,"content":198,"level":94},"/html-tags-memory-test#ruby","\u003Cruby>",[112,122],"テキストの上下左右に表示される小さな注釈を表す。 漢字の読み等を表現する際に使用する。 後述する\u003Crp>と\u003Crt>と併せて使用する。 \u003Cruby>田中\u003Crp>(\u003C/rp>\u003Crt>Tanaka\u003C/rt>\u003Crp>)\u003C/rp>\u003C/ruby> \u003Cruby>: ルビ注釈要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cruby> は HTML の要素で、ベーステキストの上、下、隣に描画される小さな注釈であり、よく東アジアの文字の発音を表すのに使われます。他の種類の注釈にも使われることがありますが、この使い方はあまり一般的ではありません。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/ruby",{"id":200,"title":201,"titles":202,"content":203,"level":94},"/html-tags-memory-test#rp","\u003Crp>",[112,122],"\u003Cruby>による注釈表示に対応していないブラウザで表示する代替の括弧を提供するための要素。 \u003Crp>: ルビの代替表示用括弧要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Crp> は HTML の要素で、\u003Cruby> 要素によるルビの表示に対応していないブラウザー向けの代替表示用の括弧を提供するために使用します。それぞれ 1 つの \u003Crp> 要素で、注釈の文字列を含む \u003Crt> 要素を囲む開き括弧と閉じ括弧をそれぞれ囲む必要があります。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/rp",{"id":205,"title":206,"titles":207,"content":208,"level":94},"/html-tags-memory-test#rt","\u003Crt>",[112,122],"注釈として表示するテキストを指定する。 \u003Crt>: ルビテキスト要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Crt> は HTML の要素で、ルビによる注釈(振り仮名)のルビテキストの部分を指定します。東アジアの組版において発音、翻訳、音写などの情報を提供するために使用します。 \u003Crt> 要素は常に \u003Cruby> 要素の中で使用されます。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/rt",{"id":210,"title":211,"titles":212,"content":213,"level":94},"/html-tags-memory-test#s","\u003Cs>",[112,122],"打ち消し線で対象の要素が既に適切ではない、正確ではないことを表現する。 \u003Cs>: 取り消し要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cs> は HTML の要素で、テキストを取り消し線または打ち消し線つきで描画します。\u003Cs> 要素はすでに適切または正確ではなくなった事柄を表現するために使用してください。しかし、文書の修正を示す場合、 \u003Cs> 要素は適切ではありません。この場合は \u003Cdel> と \u003Cins> の方が適しているので、こちらを使用してください。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/s",{"id":215,"title":216,"titles":217,"content":218,"level":94},"/html-tags-memory-test#del","\u003Cdel>",[112,122],"文章の中で削除されたテキスト。 \u003Cins>と併せて使用する。 \u003Cblockquote>\n \u003Cdel>const size = 100;\u003C/del>\n \u003Cins>const size = 300;\u003C/ins>\n\u003C/blockquote> \u003Cdel>: 削除済みテキスト要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cdel> は HTML の要素で、文書から削除された文字列の範囲を表します。これは例えば、「変更の追跡」や、ソースコードの差分情報を描画するときに使用することができます。\u003Cins> 要素は逆の目的に、文書に追加された文字列を示すために用いることができます。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/del",{"id":220,"title":221,"titles":222,"content":223,"level":94},"/html-tags-memory-test#ins","\u003Cins>",[112,122],"文章の中に追加されたテキスト。 \u003Cdel>と併せて使用する。 \u003Cblockquote>\n \u003Cdel>const size = 100;\u003C/del>\n \u003Cins>const size = 300;\u003C/ins>\n\u003C/blockquote> \u003Cins> - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cins> は HTML の要素で、文書に追加されたテキストの範囲を表します。同様に、 \u003Cdel> 要素を使用して文書から削除されたテキストの範囲を表すことができます。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/ins",{"id":225,"title":226,"titles":227,"content":228,"level":94},"/html-tags-memory-test#samp","\u003Csamp>",[112,122],"プログラムを実行した際のサンプル出力を表す。 \u003Csamp>: サンプル出力要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Csamp> は HTML の要素で、コンピュータープログラムからのサンプル出力を表すインラインのテキストを収めるために使用されます。内容は普通、ブラウザーの既定の等幅フォント(Courier や Lucida Console など)を使用して表示されます。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/samp",{"id":230,"title":231,"titles":232,"content":233,"level":94},"/html-tags-memory-test#sub","\u003Csub>",[112,122],"表記上の理由で下付き文字として表示するべきインラインテキスト。 \u003Csub>: 下付き文字要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Csub> は HTML の要素で、表記上の理由で下付き文字として表示するべきインラインテキストを示します。下付き文字は普通、小さめのテキストを使用してベースラインよりも低く表示されます。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/sub",{"id":235,"title":236,"titles":237,"content":238,"level":94},"/html-tags-memory-test#sup","\u003Csup>",[112,122],"表記上の理由で上付き文字として表示するべきインラインテキスト。 \u003Csup>: 上付き文字要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Csup> は HTML の要素で、表記上の理由で上付き文字として表示するべきインラインテキストを指定します。上付き文字は普通、小さめのテキストを使用して高いベースラインで表示されます。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/sup",{"id":240,"title":241,"titles":242,"content":243,"level":94},"/html-tags-memory-test#u","\u003Cu>",[112,122],"テキストに対して下線を付与したいだけの場合は CSS の text-decoration を使用する。 \u003Cu>: 非言語的注釈(下線)要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cu> は HTML の要素で、非言語的に注釈があることを示す方法で表示する行内テキストの区間を示します。これは既定で単純な実線の下線として表示されますが、 CSS を使用して変更することもできます。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/u",{"id":245,"title":246,"titles":247,"content":248,"level":94},"/html-tags-memory-test#var","\u003Cvar>",[112,122],"変数名や数式を表す。 \u003Cvar>: 変数要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cvar> は HTML の要素で、数式やプログラムコード内の変数の名前を表します。挙動はブラウザーに依存しますが、通常は現在のフォントのイタリック体を使って表示されます。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/var",{"id":250,"title":251,"titles":252,"content":253,"level":94},"/html-tags-memory-test#area","\u003Carea>",[112,122],"イメージマップの中でクリック可能な領域をあらかじめ定義する。 \u003Carea>: イメージマップ領域要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Carea> は HTML の要素で、イメージマップの中でクリック可能な領域をあらかじめ定義します。イメージマップでは、画像上の幾何学的な領域をハイパーテキストリンクと関連付けすることができます。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/area",{"id":255,"title":256,"titles":257,"content":258,"level":94},"/html-tags-memory-test#track","\u003Ctrack>",[112,122],"\u003Caudio>もしくは\u003Cvideo>の子要素として使用する。 \u003Ctrack>: 埋め込みテキストトラック要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Ctrack> は HTML の要素で、メディア要素 (\u003Caudio> および \u003Cvideo>) の子として使用します。この要素は自動的に処理される字幕など、時間指定されたテキストトラック (または時系列データ) を指定することができます。トラックは WebVTT 形式 (.vtt ファイル) を用います。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/track",{"id":260,"title":261,"titles":262,"content":263,"level":94},"/html-tags-memory-test#wbr","\u003Cwbr>",[112,122],"ブラウザの判断で改行しても良い場所を表す。 \u003Cwbr>: 改行可能要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cwbr> は HTML の要素で、改行可能位置 — テキスト内でブラウザーが任意で改行してよい位置を表しますが、この改行規則は必要のない場合は改行を行いません。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/wbr",{"id":265,"title":266,"titles":267,"content":268,"level":94},"/html-tags-memory-test#meter","\u003Cmeter>",[112,122],"メーターを表示する際に使用する。 \u003Cmeter>: HTML メーター要素 - HTML: ハイパーテキストマークアップ言語 | MDN\n \u003Cmeter> は HTML の要素で、既知の範囲内のスカラー値、または小数値を表します。\n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/meter",{"id":270,"title":271,"titles":272,"content":25,"level":26},"/html-tags-memory-test#実験的な機能","実験的な機能",[112],{"id":274,"title":275,"titles":276,"content":277,"level":94},"/html-tags-memory-test#portal","\u003Cportal>",[112,271],"別のページの内容を現在のページにプレビューとして埋め込む。 \u003Ciframe>よりも制限がある。 developer.mozilla.org\n \n \n \n https://developer.mozilla.org/ja/docs/Web/HTML/Element/portal",{"id":279,"title":107,"titles":280,"content":281,"level":26},"/html-tags-memory-test#まとめ",[112],"セマンティックな実装ができるように心がけていきます。 html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7hpK, html code.shiki .s7hpK{--shiki-default:#B31D28;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"id":283,"title":284,"titles":285,"content":286,"level":49},"/codebuild-token-error","CodeBuild で「authentication required for primary source」が発生した場合の対処法",[],"CodeBuild でトークンエラーになった場合の対処法を備忘録として残す。",{"id":288,"title":289,"titles":290,"content":291,"level":26},"/codebuild-token-error#エラー","エラー",[284],"「authentication required for primary source」というエラーが発生しました。",{"id":293,"title":13,"titles":294,"content":295,"level":26},"/codebuild-token-error#対処法",[284],"一度、CodeBuild と GitHub の接続を切断します。 その後、改めて CodeBuild と GitHub を接続しようとするとパーソナルアクセストークンを入力するフォームが表示されます。 有効なトークンを入力および保存をした上で再ビルドを実行すると成功しました。",{"id":297,"title":107,"titles":298,"content":299,"level":26},"/codebuild-token-error#まとめ",[284],"特にチーム開発をしている場合は、退職者のトークンを使用していないか注意しましょう。",{"id":301,"title":302,"titles":303,"content":304,"level":49},"/search-nuxt-content","NuxtContent の search で検索機能を実装する",[],"NuxtContent で作成したブログ内を検索する機能を実装する方法についてまとめる。",{"id":306,"title":307,"titles":308,"content":309,"level":26},"/search-nuxt-content#前提","前提",[302],"NuxtContent の検索機能である search は、2024年8月15日時点で実験的な機能です。 今後、仕様が変更されたり、機能そのものが外される可能性があります。 もし、search を使用する場合は nuxt.config.ts を以下のように設定する必要があります。 export default defineNuxtConfig({\n content: {\n experimental: {\n search: true\n }\n }\n})",{"id":311,"title":312,"titles":313,"content":314,"level":26},"/search-nuxt-content#検索機能の実装","検索機能の実装",[302],"今回、検索機能のロジックは composable として切り出して作成しました。 次のコードを記述するだけで検索機能を実装することができます。 export function useSearch() {\n const keyword = ref\u003Cstring>(\"\");\n\n const results = computedAsync(async () => {\n return await searchContent(keyword.value);\n }, null);\n\n return {\n keyword,\n results,\n };\n} computedAsync は、VueUse の composable です。 公式サイトの通りに進めると私の環境では検索結果がリアクティブに変更されなかったため、computedAsync を使用してリアクティブに検索結果を取得できるようにしました。",{"id":316,"title":317,"titles":318,"content":319,"level":26},"/search-nuxt-content#検索処理の呼び出し","検索処理の呼び出し",[302],"あとは作成した composable を呼び出し、検索キーワードを入力するフォームと検索結果を表示する要素を用意するだけです。 \u003Cscript lang=\"ts\" setup>\nconst { keyword, results } = useSearch();\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cinput v-model=\"keyword\" type=\"search\" placeholder=\"記事を検索する\" />\n \u003Cp>{{ results }}\u003C/p>\n\u003C/template>",{"id":321,"title":107,"titles":322,"content":323,"level":26},"/search-nuxt-content#まとめ",[302],"簡単に検索を実装することができるため、正式な機能としてリリースされることを期待したいです。 html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"id":325,"title":326,"titles":327,"content":328,"level":49},"/monorepo","pnpm の workspace を使ってみた",[],"pnpm の workspace を使って monorepo 構成のプロジェクト管理を体験してみた。",{"id":330,"title":331,"titles":332,"content":333,"level":26},"/monorepo#はじめに","はじめに",[326],"私はこれまでモノレポのプロダクトに携わったことがなく、モノレポ構成のイメージが持てずにいました。\nそこで、試しに自分でもモノレポ構成のリポジトリを作ってみようと思い、実際に試してみたので備忘録として残します。",{"id":335,"title":336,"titles":337,"content":338,"level":26},"/monorepo#実際に試してみる","実際に試してみる",[326],"今回、モノレポ構成の練習台に選ばれたのは Zenn の記事を管理しているリポジトリです。 zenn-contents/package.json at main · splendente/zenn-contents\n manage zenn articles. Contribute to splendente/zenn-contents development by creating an account on GitHub.\n \n \n https://github.com/splendente/zenn-contents/blob/main/package.json 私はリポジトリの中に Zenn の記事だけでなく、記事の中で扱ったサンプルコードを examples ディレクトリの中で管理しています。 具体的なディレクトリ構成は以下のとおりです。 /zenn-contents\n├─ articles\n│ └─ xxxx.md\n├─ books\n│ └─ xxxx.md\n├─ examples\n│ ├─ sampleA\n│ │ ├─ package.json\n│ │ └─ pnpm-lock.yaml\n│ └─ sampleB\n│ ├─ package.json\n│ └─ pnpm-lock.yaml\n├─ package.json\n└─ pnpm-lock.yaml この時、sampleA や sampleB の node_modules はルートディレクトリで pnpm install 等を実行しても作成されません。 ここで pnpm workspace の登場です。 pnpm-workspace.yaml をルートディレクトリに作成します。 内容は以下のように記述しました。 packages:\n - examples/** 最後に package.json を修正し、ルートディレクトリに配置されている package.json によって実行されるスクリプトで examples ディレクトリ配下の各プロジェクトを起動できるようにします。 \"scripts\": {\n ...省略\n \"examples:sampleA:dev\": \"pnpm run --filter=./examples/sampleA dev\",\n \"examples:sampleB:dev\": \"pnpm run --filter=./examples/sampleB dev\"\n}, --filter は -F という省略形もあるようです。",{"id":340,"title":107,"titles":341,"content":342,"level":26},"/monorepo#まとめ",[326],"手軽に pnpm の workspace を導入することができました。 個人的にはルートディレクトリでサブディレクトリのスクリプトを実行することができるという点が非常に良いと感じました。",{"id":344,"title":345,"titles":346,"content":347,"level":49},"/github-actions","GitHub Actions で issue を定期的に作成するワークフローを作ってみた",[],"定期的なタスクの対応漏れを防ぐ手段の1つとして GitHub Actions でワークフローを作成しました。",{"id":349,"title":331,"titles":350,"content":351,"level":26},"/github-actions#はじめに",[345],"Basic 認証を設けているサイトを運用していると、セキュリティの観点からパスワードを定期的に変更したいと考えることでしょう。 しかし、他の作業に気を取られているとついついパスワードの更新を忘れてしまいます。 そこで、定期的にパスワードを変更するというissueを作成するようにしました。",{"id":353,"title":354,"titles":355,"content":356,"level":26},"/github-actions#実際のコード","実際のコード",[345],"実行タイミングは cron にて設定します。 今回は3ヶ月毎に実行されるように設定しています。 name: update basic password\non:\n schedule:\n - cron: '0 0 1 */3 *'\n\njobs:\n create_issue:\n runs-on: ubuntu-latest\n permissions:\n issues: write\n steps:\n - name: Create Issue\n uses: actions/github-script@v6\n with:\n github-token: ${{ secrets.GITHUB_TOKEN }}\n script: |\n const issue = await github.rest.issues.create({\n owner: context.repo.owner,\n repo: context.repo.repo,\n title: 'Update basic password',\n body: `\n The basic password needs to be updated every 3 months.\n Please update the password in the password manager.\n `,\n labels: ['security']\n }); 正常に動作していると添付画像のような issue が作成されます。",{"id":358,"title":107,"titles":359,"content":360,"level":26},"/github-actions#まとめ",[345],"ワークフローがあることで定期的なタスクのリマインドも行うことができるようになり、とても便利だなと感じました。",{"id":362,"title":363,"titles":364,"content":365,"level":49},"/get-started-with-nest-js","NestJS に入門してみる",[],"NestJS についての個人的なメモ。",{"id":367,"title":368,"titles":369,"content":370,"level":26},"/get-started-with-nest-js#プロジェクトの構築","プロジェクトの構築",[363],"公式サイトでは @nestjs/cli をグローバルにインストールする手順だが、今回はグローバルを汚さないように以下のコマンドでプロジェクトを作成します。 $ npx @nestjs/cli new プロジェクト名",{"id":372,"title":373,"titles":374,"content":375,"level":26},"/get-started-with-nest-js#起動","起動",[363],"以下のコマンドでアプリを起動することができます。 pnpm start:dev",{"id":377,"title":378,"titles":379,"content":380,"level":26},"/get-started-with-nest-js#初期状態","初期状態",[363],"main.ts がエントリーファイルになっています。 NestFactory.create(AppModule)でNestJSのインスタンスを生成しています。 第一引数に渡している AppModule は、ルートモジュールです。 NestJS では1つ以上のモジュールが必要である。 ポート番号を変更し、フロントと被らないように変更します。 app.listen(process.env.PORT ?? 3001); http://localhost:3001 へアクセスし、「Hello World!」と返却されていることを確認します。 「Hello World!」を返却するメソッドは、app.service.ts 内の AppService という class の中に書かれていました。 export class AppService {\n getHello(): string {\n return 'Hello World!';\n }\n} 現時点で AppService の上に書かれている @Injectable() というデコレータがどのような役割をになっているのか理解できていません。 次に app.service.ts がどこで呼ばれているのかを確認します。 app.controller.ts と app.module.ts の2ファイルから呼ばれていることわかりました。 app.module.ts は app.service.ts だけではなく、app.controller.ts を呼び出していました。 先に app.controllers.ts を確認します。 ここで初めて気づきますが、NestJS はデコレーターを使用する場面が多いです!!! @Controller() というデコレータを定義することで app.controllers.ts で定義した class をコントローラーとして定義することができるようです。 また、引数にパスを設定することができます。 下記のように変更し、http://localhost:3001/users にアクセスすると先ほどと同じように「Hello World!」が返却されることを確認することができます。 @Controller('users') AppController という class の中では以下のようなメソッドが作成されていました。 @Get()\ngetHello(): string {\n return this.appService.getHello();\n} 基本的にコントローラーはルーティングの役割とサービスで定義したメソッドを呼び出す役割があるようです。 コントローラーでは詳細な機能の実装はせず、詳しい部分はサービスにお願いするイメージだと思います。 最後に app.module.ts を確認します。 @Module() というデコレータの引数にコントローラーとサービス、そして imports が設定されています。 @Module({\n imports: [],\n controllers: [AppController],\n providers: [AppService],\n})\nexport class AppModule {} NestJS ではこれまで確認してきた コントローラーとサービスで構築する機能の1つのモジュールという単位で呼ぶようです。 app.module.ts ではこのモジュールを定義するようです。 試しに Users というモジュールを作成しようと思います。 事前に先ほど追加した main.service.ts で定義した @Controller('users') を @Controller() に戻します。 users/index.service.ts を作成し、以下の内容を記述します。 import { Injectable } from '@nestjs/common';\nimport type { User } from './../types/index.d.ts';\n\n@Injectable()\nexport class UsersService {\n getUsers(): User[] {\n return [\n {\n id: 1,\n name: 'John',\n },\n ];\n }\n} 型情報はコントローラーでも使用するため、外部ファイルとして定義します。 type User = {\n id: number;\n name: string;\n}; 次にコントローラーを作成します。 import { Controller, Get } from '@nestjs/common';\nimport { UsersService } from './index.service';\nimport type { User } from './../types/index.d.ts';\n\n@Controller('users')\nexport class UsersController {\n constructor(private readonly usersService: UsersService) {}\n\n @Get()\n getUsers(): User[] {\n return this.usersService.getUsers();\n }\n} モジュールを作成します。 import { Module } from '@nestjs/common';\nimport { UsersController } from './index.controller';\nimport { UsersService } from './index.service';\n\n@Module({\n controllers: [UsersController],\n providers: [UsersService],\n})\nexport class UsersModule {} 最後に app.module.ts に作成したコントローラーとサービスを追記します。 別のモジュールを読み込む場合は imports に書くようです。 import { Module } from '@nestjs/common';\nimport { AppController } from './app.controller';\nimport { AppService } from './app.service';\nimport { UsersModule } from './users/index.module';\n\n@Module({\n imports: [UsersModule],\n controllers: [AppController],\n providers: [AppService],\n})\nexport class AppModule {} http://localhost:3001/usres にアクセスし、{\"id\":1,\"name\":\"John\"} というユーザーのデータが返却されていることを確認します。",{"id":382,"title":383,"titles":384,"content":385,"level":26},"/get-started-with-nest-js#環境変数の設定","環境変数の設定",[363],"NestJSで環境変数を扱うには@nestjs/configを使用します。 まずはインストールします。 pnpm add @nestjs/config 次にapp.module.tsのimportsに以下を追記します。 imports: [\n ConfigModule.forRoot({\n isGlobal: true, // グローバルに登録します\n }),\n], 次に.envファイルを用意し、環境変数を宣言します。 DATABASE_USER=test 最後に環境変数をserviceで呼び出してみます。 export class AppService {\n constructor(private configService: ConfigService) {}\n\n getHello(): string {\n return this.configService.get\u003Cstring>('DATABASE_USER');\n }\n} testという文字列が返ってくることを確認します。 html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"id":387,"title":388,"titles":389,"content":390,"level":49},"/overflow-clip","CSS の overflow: clip; について",[],"あまり聞き慣れないプロパティである overflow: clip; についてまとめる。",{"id":392,"title":331,"titles":393,"content":394,"level":26},"/overflow-clip#はじめに",[388],"overflow プロパティを使用する際に scroll や hidden 等を使う機会は多いですが、clip を使用する機会がなかったので調べた内容をまとめます。",{"id":396,"title":397,"titles":398,"content":399,"level":26},"/overflow-clip#overflow-clip-とは","overflow: clip; とは?",[388],"MDN を読みながら理解を深めようと思います。 overflow - CSS: カスケーディングスタイルシート | MDN\n overflow は CSS の一括指定プロパティで、コンテンツが要素のパディングボックスに収まらない(はみ出す)場合に、水平方向および垂直方向の望ましい動作を設定します。\n \n \n https://developer.mozilla.org/ja/docs/Web/CSS/overflow#clip 挙動について以下のように記載がありました。 溢れたコンテンツは、overflow-clip-margin プロパティを使用して定義された要素のはみ出しクリップ辺で切り取られます。 hidden と同じような挙動です。 ただし、溢れたコンテンツをブラウザがどのように認識するかという点で hidden と clip には違いがあるようです。 切り取られた領域の外側に溢れたコンテンツは表示されず、ユーザーエージェントはスクロールバーを追加せず、プログラムによるスクロールも行われません。新しい整形コンテキストは作成されません。 hidden によって切り取られたコンテンツは scrollTo() メソッド等を使用することでスクロールすることができますが、clip によって切り取られたコンテンツは scrollTo() メソッド等を使ってもスクロールすることができないようです。",{"id":401,"title":402,"titles":403,"content":404,"level":26},"/overflow-clip#単一方向におけるクリップ","単一方向におけるクリップ",[388],"overflow-clip-margin を使用することでどれだけ外にはみ出すことができるかを指定することができます。 overflow-clip-margin - CSS: カスケーディングスタイルシート | MDN\n overflow-clip-margin は CSS のプロパティで、 overflow: clip を持つ要素がクリップされる前に、要素の辺からどれだけ外側に描画できるかを指定します。 このプロパティで定義される境界は、ボックスのオーバーフロークリップ枠と呼ばれます。\n \n \n https://developer.mozilla.org/ja/docs/Web/CSS/overflow-clip-margin",{"id":406,"title":107,"titles":407,"content":408,"level":26},"/overflow-clip#まとめ",[388],"overflow: clip; は、溢れたコンテンツを切り取る CSS のプロパティで hidden とは異なり、プログラムによるスクロール操作ができない。",{"id":410,"title":411,"titles":412,"content":413,"level":49},"/nuxt-color-mode","@nuxtjs/color-mode でダークモードを導入する方法",[],"Nuxt で実行したアプリにダークモードを導入したのでまとめる。",{"id":415,"title":331,"titles":416,"content":417,"level":26},"/nuxt-color-mode#はじめに",[411],"Nuxt で作成したアプリケーションにダークモードを導入する際に便利なモジュールが @nuxtjs/color-mode です。 Nuxt Color Mode\n Dark and Light mode with auto detection made easy with Nuxt 🌗\n \n \n https://color-mode.nuxtjs.org/ 次のコマンドを実行し、アプリケーションに必要なモジュールを追加します。 npx nuxi module add color-mode nuxt.config.ts に以下のような設定が追加されていることを確認します。 export default defineNuxtConfig({\n modules: [\n '@nuxtjs/color-mode',\n ]\n})",{"id":419,"title":420,"titles":421,"content":422,"level":26},"/nuxt-color-mode#テーマの切り替えを実装する","テーマの切り替えを実装する",[411],"カラーのテーマを切り替えるためのボタンやセレクトボックスを用意します。 公式サイトにもコード例が載っています。 ライトテーマとダークテーマだけで良い場合は以下のようになります。 \u003Ctemplate>\n \u003Cselect v-model=\"$colorMode.preference\">\n \u003Coption value=\"light\">Light\u003C/option>\n \u003Coption value=\"dark\">Dark\u003C/option>\n \u003C/select>\n\u003C/template> 値を変更する場合に $colorMode の value ではなく、preference を変更する点に注意してください。 value を変更してもテーマの切り替えはできますが、preference の値が書きかわらないため、リロードしたりページ遷移すると変更前のテーマになってしまいます。",{"id":424,"title":425,"titles":426,"content":427,"level":26},"/nuxt-color-mode#css-変数を定義する","CSS 変数を定義する",[411],"今回は CSS で文字色や背景色を変えられるようにします。 main.css という CSS ファイルを用意します。 私は以下のような変数を定義しました。 下記を参考にご自身のアプリで使いたいテーマごとに色を設定してください。 html.light-mode{\n --primary-text-color: ライトモードで使用する文字色;\n --primary-bg-color: ライトモードでメインで使用する背景色;\n --secondary-bg-color: ライトモードでサブで使用する背景色;\n --primary-border-color: ライトモードで使用する枠線の色;\n}\n\nhtml.dark-mode {\n --primary-text-color: ダークモードで使用する文字色;\n --primary-bg-color: ダークモードでメインで使用する背景色;\n --secondary-bg-color: ダークモードでサブで使用する背景色;\n --primary-border-color: ダークモードで使用する枠線の色;\n}",{"id":429,"title":430,"titles":431,"content":432,"level":26},"/nuxt-color-mode#動作確認","動作確認",[411],"ライトモードを使用している場合です。 ダークモードを使用している場合です。",{"id":434,"title":435,"titles":436,"content":437,"level":26},"/nuxt-color-mode#storybook-を使用している場合のモック方法","Storybook を使用している場合のモック方法",[411],"Storybook で表示しようとすると @nuxtjs/color-mode の $colorMode が undefined となってしまい、エラーが表示されてしまいます。 そこで、下記のように $colorMode をモックすることで Storybook でも $colorMode を使ったコンポーネントを表示することができるようになります。 export default {\n render: () => ({\n components: { コンポーネント名 },\n setup() {\n const colorMode = {\n value: 'light',\n preference: 'light',\n unknown: false,\n forced: false,\n }\n const app = getCurrentInstance()?.appContext.app\n if (app) {\n app.config.globalProperties.$colorMode = colorMode\n }\n return {}\n },\n template: `\n \u003Cコンポーネント名 />\n `,\n }),\n}",{"id":439,"title":107,"titles":440,"content":441,"level":26},"/nuxt-color-mode#まとめ",[411],"@nuxtjs/color-mode を使用するとダークモードを簡単に導入することができました。 個人的にはダークモードにした場合の配色を考える方が大変でした。 html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"id":443,"title":444,"titles":445,"content":446,"level":49},"/prisma-error","npx prisma init でSupport for loading ES Module in require() is an experimental feature and might change at any time というエラーが出た場合の原因と対処法",[],"Prisma を使用した際に見慣れないエラーに遭遇したので備忘録として残す。",{"id":448,"title":449,"titles":450,"content":451,"level":26},"/prisma-error#エラーの内容","エラーの内容",[444],"npx prisma init を実行した際に以下のようなエラーメッセージが出てしまい、初期化に失敗してしまいました。 (node:30187) ExperimentalWarning: Support for loading ES Module in require() is an experimental feature and might change at any time\n(Use node --trace-warnings ... to show where the warning was created)\nError: (0 , KSe.isError) is not a function",{"id":453,"title":454,"titles":455,"content":456,"level":26},"/prisma-error#エラーの原因","エラーの原因",[444],"原因は、Node.js のバージョンでした。 私は v23.0.0 を使用していましたが、2024年12月22日の時点で Prisma が Node.js の v23 をサポートしていませんでした。",{"id":458,"title":459,"titles":460,"content":461,"level":26},"/prisma-error#エラーの対処法","エラーの対処法",[444],"v22.11.0 にバージョンを下げて再度 npx prisma init を実行すると無事に成功しました。 サポート状況は公式サイトに記載されているのでバージョンを変更する際に参考するようにしてください。",{"id":463,"title":464,"titles":465,"content":466,"level":49},"/husky-lint-staged","Husky と lint-staged の設定手順",[],"2024年3月26日時点の Husky と lint-staged の設定手順についてまとめる。",{"id":468,"title":469,"titles":470,"content":471,"level":26},"/husky-lint-staged#husky-と-lint-staged-をインストールする","Husky と lint-staged をインストールする",[464],"Husky と lint-staged を開発環境で使用するため、次のコマンドを実行し、devDependencies に各パッケージをインストールします。 npm install -D husky lint-staged",{"id":473,"title":474,"titles":475,"content":476,"level":26},"/husky-lint-staged#パッケージ情報","パッケージ情報",[464],"2024年3月26日時点での各パッケージの最新バージョンを使用します。 バージョンの詳細については以下の表を参照してください。 パッケージ名バージョンHusky9.0.11lint-staged15.2.2",{"id":478,"title":479,"titles":480,"content":481,"level":26},"/husky-lint-staged#husky-の初期化","Husky の初期化",[464],"Husky を初期化するために次のコマンドを実行をします。 npx husky init",{"id":483,"title":484,"titles":485,"content":486,"level":26},"/husky-lint-staged#pre-commit-の追加","pre-commit の追加",[464],"pre-commit で lint-staged が実行されるようにコマンドを .husky/pre-commit に追加します。 echo \"npx lint-staged\" > .husky/pre-commit",{"id":488,"title":489,"titles":490,"content":491,"level":26},"/husky-lint-staged#packagejson-を修正","package.json を修正",[464],"最後に package.json に lint-staged の設定を追加します。 {\n \"scripts\": {\n \"prepare\": \"husky\"\n },\n \"lint-staged\": {\n \"*\": \"npm run xxxx\"\n },\n}",{"id":493,"title":107,"titles":494,"content":495,"level":26},"/husky-lint-staged#まとめ",[464],"以上で Husky と lint-staged の設定を行うことができます。 Husky のバージョンが v9 に上がったタイミングで設定周りが変更になっているため、v8 より以前のバージョンとは設定手順が異なることに注意してください。 html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"id":497,"title":498,"titles":499,"content":500,"level":49},"/call-once-composable","Nuxt 3.15 で追加された callOnce のナビゲーションモードについて",[],"callOnceとはどんな composable なのか、そして追加されたナビゲーションモードはどんなモードなのかをまとめる。",{"id":502,"title":503,"titles":504,"content":505,"level":26},"/call-once-composable#callonce","callOnce",[498],"SSR もしくは CSR で処理を一度だけ実行することができる composable です。 Nuxt v3.9 から使えるようになりました。 ユースケースとしては、state の初期化を行う場合などです。 State Management · Get Started with Nuxt\n Nuxt provides powerful state management libraries and the useState composable to create a reactive and SSR-friendly shared state.\n \n \n https://nuxt.com/docs/getting-started/state-management#initializing-state",{"id":507,"title":508,"titles":509,"content":510,"level":26},"/call-once-composable#ナビゲーションモード","ナビゲーションモード",[498],"Nuxt 3.15 で新たに追加された機能です。 Nuxt 3.15 · Nuxt Blog\n Nuxt 3.15 is out - with Vite 6, better HMR and faster performance\n \n \n https://nuxt.com/blog/v3-15#️-navigation-mode-for-callonce 公式サイトにも記載がありますが、下記のように { mode: 'navigation' } を指定することでナビゲーションモードを有効にすることができます。 await callOnce(() => counter.value++, { mode: 'navigation' })",{"id":512,"title":513,"titles":514,"content":515,"level":26},"/call-once-composable#middleware-と-callonce-のナビゲーションモードの違いについて","middleware と callOnce のナビゲーションモードの違いについて",[498],"ページ遷移の度に実行することができるという説明を聞くと、middleware と役割が似ているように感じます。 大きな違いとして、処理が実行されるタイミングが異なります。 middleware で定義される処理はナビゲーションの前に実行されます。 そのため、middleware には、ログイン状態のチェックや閲覧権限のチェック等の処理を定義するのが向いています。 逆に callOnce のナビゲーションモードはナビゲーションのタイミングで実行されるため、タイミングとしては middleware よりも後に実行されます。 そのため、callOnce には、状態等のデータの初期化等の処理を定義するのが向いています。 html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":35,"title":6,"titles":517,"content":30,"level":49},[],{"id":519,"title":13,"titles":520,"content":521,"level":26},"/option-display-none#対処法",[6],"よくある対処法として、span 要素で option 要素を囲み、span 要素に対して display: none; を指定する方法があります。 しかし、select 要素の子要素として span 要素を指定することは HTML の仕様に従わないため、他の方法を模索するべきだと思います。 CSS で指定する方法は諦め、動的に要素を追加したり、削除したりする方法が良いと思います。",{"id":523,"title":524,"titles":525,"content":526,"level":49},"/fix-msw-github-pages-error","Storybook で「[MSW] Failed to register a Service Worker for scope ('https://username.github.io/') with script ('https://reponame.github.io/mockServiceWorker.js'): Service Worker script does not exist at the given path.」が発生した場合の対処法",[],"Storybook と Mock Service Worker を使ったプロジェクトを GitHub Pages にデプロイしたらエラーになった場合の対処法を備忘録として残す。",{"id":528,"title":289,"titles":529,"content":530,"level":26},"/fix-msw-github-pages-error#エラー",[524],"Storybook を GitHub Pages へデプロイした際に下記のようなエラーが表示されてしまいました。 [MSW] Failed to register a Service Worker for scope ('https://splendente.github.io/') with script ('https://splendente.github.io/mockServiceWorker.js'): Service Worker script does not exist at the given path. MSW(Mock Service Worker)のスクリプトファイルである mockServiceWorker.js が見つからないことが原因です。 GitHub Pages にデプロイすると、URLが https://{ユーザー名}.github.io/{リポジトリ名}/ となります。 そのため、mockServiceWorker.js のパスがルートに向いてるとエラーのようにファイルが見つけられないようです。",{"id":532,"title":533,"titles":534,"content":535,"level":26},"/fix-msw-github-pages-error#対象法","対象法",[524],"GitHub Pages では mockServiceWorker.js のパスを /リポジトリ名/mockServiceWorker.js と指定すると解消します。 .storybook/preview.(js or ts) を下記のように変更します。 let options = {};\nif (location.hostname === \"自分のユーザー名.github.io\") {\n options = {\n serviceWorker: {\n url: \"/リポジトリ名/mockServiceWorker.js\",\n },\n };\n}\n\n// Initialize MSW\ninitialize(options); html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":537,"title":538,"titles":539,"content":540,"level":49},"/nuxt-og-image","nuxt-og-imageで日本語フォントを指定する方法",[],"nuxt-og-imageでOGP画像を生成したら日本語が文字化けしてしまった時の対処法を備忘録として残す。 Fonts · Nuxt OG Image · Nuxt SEO\n Using custom fonts in your OG Images.\n \n \n https://nuxtseo.com/docs/og-image/guides/custom-fonts nuxt-og-image は、OGP 画像を簡単に作成することができる Nuxt のモジュールです。 ただし、注意点として英語以外の言語に対応していません。 そのため、私のように日本語でブログの記事を執筆している場合、 OGP 画像に表示させるテキストが文字化けしてしまいます。",{"id":542,"title":13,"titles":543,"content":544,"level":26},"/nuxt-og-image#対処法",[538],"公式ドキュメントの中に対処法が書いてありました。 日本語を使用する場合、以下のように nuxt.Config.ts を修正します。 export default defineNuxtConfig({\n ogImage: {\n fonts: [\n 'Noto+Sans+JP:400',\n ]\n }\n}) Nuxt Dev Tools で文字化けが解消されていることを確認します。 フォントは、${name}:${weight} の形式で Google フォントを指定することができます。 例えば、文字を太くしたい場合は下記のように指定します。 export default defineNuxtConfig({\n ogImage: {\n fonts: [\n 'Noto+Sans+JP:700', \n ]\n }\n})",{"id":546,"title":107,"titles":547,"content":548,"level":26},"/nuxt-og-image#まとめ",[538],"nuxt-og-image で英語以外のテキストを使用する場合は明示的にフォントを指定する必要がある",{"id":550,"title":551,"titles":552,"content":553,"level":26},"/nuxt-og-image#参考記事","参考記事",[538],"https://nuxtseo.com/docs/og-image/guides/non-english-localeshttps://nuxtseo.com/docs/og-image/guides/custom-fonts html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":555,"title":556,"titles":557,"content":558,"level":49},"/improve-storycap","storycapの実行時間を短縮する",[],"フロントエンドのテスト手法の1つであるVisual Regression Testのツールであるstorycapの実行時間を短縮するための情報についてまとめる。 Storybookを事前にBuildするテスト対象を絞る並列実行する",{"id":560,"title":561,"titles":562,"content":563,"level":49},"/get-started-with-elysiajs","ElysiaJSに入門してみる",[],"ElysiaJSについての個人的なメモ。 Bunのフレームワーク公式サイトExpressよりも21倍速い人間工学に基づいた人間のためのフレームワークEnd-to-Endで型セーフである",{"id":565,"title":566,"titles":567,"content":568,"level":26},"/get-started-with-elysiajs#bunのインストール","Bunのインストール",[561],"まだBunをインストールしたことがなかったため、公式サイトを参照し、下記のコマンドを実行します。 brew install oven-sh/bun/bun 2024年4月7日時点では、v1.1.2がインストールされました。",{"id":570,"title":571,"titles":572,"content":573,"level":26},"/get-started-with-elysiajs#elysiajsのインストール","ElysiaJSのインストール",[561],"bun create elysia アプリ名",{"id":575,"title":576,"titles":577,"content":578,"level":26},"/get-started-with-elysiajs#アプリの起動","アプリの起動",[561],"# To get started, run:\n\n cd アプリ名\n bun run src/index.ts インストール時に表示されたメッセージに従って下記を実行します。 bun run src/index.ts 上記では、メッセージに従いましたが、bun devでも起動できます。 localhost:3000 へアクセスし、「Hello Elysia」と表示されることを確認します。",{"id":580,"title":581,"titles":582,"content":583,"level":26},"/get-started-with-elysiajs#indextsを覗いてみる","index.tsを覗いてみる",[561],"import { Elysia } from \"elysia\";\n\nconst app = new Elysia().get(\"/\", () => \"Hello Elysia\").listen(3000);\n\nconsole.log(\n `🦊 Elysia is running at ${app.server?.hostname}:${app.server?.port}`\n); コンソールに「🦊 Elysia is running at localhost:3000」が表示されることを確認します。",{"id":585,"title":586,"titles":587,"content":588,"level":26},"/get-started-with-elysiajs#マニュアルのインストール","マニュアルのインストール",[561],"bun add elysia dependencyにelysiaが追加されるため、npmスクリプトを下記のように変更します。 \"scripts\": {\n \"dev\": \"bun run --watch src/index.ts\",\n \"build\": \"bun build src/index.ts\",\n \"start\": \"NODE_ENV=production bun src/index.ts\",\n \"test\": \"bun test\"\n}, testコマンドは、既に存在していため、追加ではなく更新しました。 devコマンドは、開発モードで起動し、オートリロードが効くbuildコマンドは、プロダクションビルドを実行するstartコマンドは、プロダクションサーバーで起動するtestコマンドは、テストを実行してくれる",{"id":590,"title":591,"titles":592,"content":593,"level":26},"/get-started-with-elysiajs#公式おすすめのディレクトリ構成","公式おすすめのディレクトリ構成",[561],"elysia-app\n ├── src\n │ ├── index.ts // エントリーポイント\n │ ├── setup.ts // [wip]調べる\n │ ├── controllers // [wip]調べる\n │ ├── libs // 共通処理のファイルを格納する\n │ ├── models // [wip]調べる\n │ └── types // 型情報を記載したファイルを格納する\n ├── test // テストファイルを格納する\n ├── bun.lockb\n ├── node_modules\n ├── package.json\n └── tsconfig.json html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"id":595,"title":596,"titles":597,"content":598,"level":49},"/get-started-with-hono","Honoに入門してみる",[],"Honoについての個人的なメモ。 公式サイト Honoは、CDNのエッジで動く高速なWebフレームワークです。 Cloudflareだけでなく、FastlyやAWS Lambda、Vercelなどでも動くようです。",{"id":600,"title":601,"titles":602,"content":603,"level":26},"/get-started-with-hono#honoのインストール","Honoのインストール",[596],"npmを使用してインストールする場合、下記のコマンドを実行します。 npm create hono@latest my-app プロジェクトを作成したら下記のコマンドを実行し、アプリを起動します。 npm run dev http://localhost:8787にアクセスし、「Hello Hono!」と表示されることを確認します。 html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":605,"title":606,"titles":607,"content":608,"level":49},"/first-oss-contribution","初めてOSSにコントリビュートしました",[],"OSSへのコントリビュートという実績を解除したのできっかけなどをまとめる。 本日、OSSへのコントリビュートという実績を解除しました。 docs: change tag from div to li element by splendente · Pull Request #2814 · vuejs/docs\n Description of Problem\nAs noticed in #2813 , I changed tag in example code in transition group page from div to li element.\nProposed Solution\nAdditional Information\n \n \n https://github.com/vuejs/docs/pull/2814",{"id":610,"title":611,"titles":612,"content":613,"level":26},"/first-oss-contribution#きっかけ","きっかけ",[606],"Vue.jsのTransitionとTransition Groupの違いを調査するため、公式ドキュメントを漁っていた時のことです。 サンプルコードでulタグ直下の要素がliタグではなく、divタグになっていることに気づいたことがきっかけです。",{"id":615,"title":616,"titles":617,"content":25,"level":26},"/first-oss-contribution#ossコントリビュートまでの軌跡","OSSコントリビュートまでの軌跡",[606],{"id":619,"title":620,"titles":621,"content":622,"level":94},"/first-oss-contribution#issueを作成する","issueを作成する",[606,616],"既に同じ問題に気づいている人がいる可能性があるため、Vue.jsの公式ドキュメントのリポジトリのissueを漁ってみました。 修正したい内容である「Transition Group」や「ul」、「list」等の単語を検索ワードとして使いつつ、issueを確認しましたが、重複するissueを見つけることができませんでした。 既にPull Requestが作成されている可能性もあるのでPull Requestも確認しました。 その後も調査しましたが、どこにも重複するissueやPull Requestを見つけられなかったため、issueを作成することにしました。 Google翻訳等を活用し、issueを作成後、わずか数分で「bug」と「contribution welcome」というタグをコアチームメンバーの方が設定してくれました。",{"id":624,"title":625,"titles":626,"content":627,"level":94},"/first-oss-contribution#pull-requestを作成する","Pull Requestを作成する",[606,616],"次に私はこのissueを自分が担当していいのかどうかわからず、路頭に迷いました。 「君が担当者だ!」とアサインされるのを待つべきか勝手にPRを送るってもいいのか...。 リポジトリに対する権限的にissueに対して自らをアサインすることができなかったため、ルールなどが記載されたドキュメントを探すことにしました。 README.mdなどの様々なドキュメントを漁りましたが、欲しい情報を見つけることはできませんでした。 他のissue等を覗きましたが、明確に担当者をアサインしているケースを見つけることができず、逆にissueを作成した人がそのままPull Requestを作成しているケースが多かったことと「contribution welcome」というタグを信じ、ダメ元でPull Requestを送ることにしました。 OSSへのコントリビュートが初めてだったため、fork等の慣れない作業は都度調べながら行いました。 修正内容は軽微だったため、すぐに修正を終え、Pull Requestを作成しました。",{"id":629,"title":630,"titles":631,"content":632,"level":94},"/first-oss-contribution#レビュー-merge","レビュー & Merge",[606,616],"Pull Request作成後、すぐに自分が作成したissueに反応してくださったコアメンバーの方がレビューおよびApproveをしてくださり、そのままMergeとなりました。 (初歩的なミスもカバーしてくれました。) 以上で初のOSSコントリビュートが無事に終了することができました。",{"id":634,"title":635,"titles":636,"content":637,"level":26},"/first-oss-contribution#ossコントリビュートを経験してみた感想","OSSコントリビュートを経験してみた感想",[606],"OSSと聞くと少し構えてしまうが、それよりもチャレンジしてみて良かったという気持ちが強いです。 簡単な修正でしたが、初めてVitePressを触ってみることができたりと得られた収穫が大きいです。 今回はいきなりPull Requestを送りましたが、リポジトリのルールはしっかり確認し、もし不安であればissueなどでコメントして事前に担当していいか確認するのが良いと思います。 これを機にOSS活動も頑張っていきたいです。",{"id":639,"title":640,"titles":641,"content":642,"level":49},"/difference-between-defineexpose-and-provide-and-inject","defineExpose と Provide & Inject の違いについて",[],"データなどを共有したり、公開することができる defineExpose と Provide & Inject の違いについてまとめる。",{"id":644,"title":645,"titles":646,"content":647,"level":26},"/difference-between-defineexpose-and-provide-and-inject#defineexpose-について","defineExpose について",[640],"defineExpose は、Vue.js のコンパイラーマクロで子コンポーネントの script setup 構文で定義されたプロパティを親コンポーネント、もしくは外部のスクリプトファイルに対して公開する機能です。 script setup 構文は、デフォルトでスコープが閉じているため、親コンポーネントや外部のスクリプトファイルは通常アクセスすることができません。 そのため、親コンポーネントから子コンポーネントの変数にアクセスしたい場合などは defineExpose を使用する必要があります。",{"id":649,"title":650,"titles":651,"content":652,"level":94},"/difference-between-defineexpose-and-provide-and-inject#使用例","使用例",[640,645],"defineExpose を使用した場合、子コンポーネントは下記のように書くことができます。 \u003Cscript setup lang=\"ts\">\nimport { ref } from \"vue\";\n\nconst count = ref\u003Cnumber>(0);\n\nconst increment = () => {\n count.value++;\n};\n\ndefineExpose({\n increment,\n});\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cdiv>\n \u003Cp>{{ count }}\u003C/p>\n \u003C/div>\n\u003C/template> count というリアクティブデータが定義されおり、increment という関数が実行されると数値が1ずつ増えるというシンプルなコンポーネントです。 公開したいプロパティを defineExpose 内に書くだけで外部に公開することができます。 increment という関数を外部に公開したい場合、下記のように書くことができます。 defineExpose({\n increment,\n}); defineExpose を使用した子コンポーネントを呼び出す親コンポーネントは下記のように書くことができます。 \u003Cscript setup lang=\"ts\">\nimport { ref } from \"vue\";\nimport Count from \"./Count.vue\"\n\nconst count = ref\u003CInstanceType\u003Ctypeof Count> | null>(null);\n\nconst execIncrement = () => {\n count.value?.increment();\n};\n\u003C/script>\n\n\u003Ctemplate>\n \u003CCount ref=\"count\" />\n \u003Cbutton type=\"button\" @click=\"execIncrement\">increment\u003C/button>\n\u003C/template> テンプレート参照を使用することで、子コンポーネントで defineExpose を使用して定義した increment という関数にアクセスすることができます。 上記の例では、increment と書かれたボタンをクリックすると子コンポーネントの count というリアクティブデータが1つずつ増えることを確認することができます。",{"id":654,"title":655,"titles":656,"content":25,"level":26},"/difference-between-defineexpose-and-provide-and-inject#provide-inject-について","Provide & Inject について",[640],{"id":658,"title":659,"titles":660,"content":661,"level":94},"/difference-between-defineexpose-and-provide-and-inject#provide","Provide",[640,655],"親または祖先にあたるコンポーネントから子または子孫にあたるコンポーネントに対してデータやメソッドを共有する機能です。 Provideは、defineExpose とは異なり、子また子孫にあたるコンポーネントから親または祖先にあたるコンポーネントに対してデータやメソッドを共有することはできません。 また、外部のスクリプトファイルに対してもデータやメソッドを共有することはできない点も defineExpose と異なる点です。",{"id":663,"title":664,"titles":665,"content":666,"level":94},"/difference-between-defineexpose-and-provide-and-inject#inject","Inject",[640,655],"子または子孫にあたるコンポーネントが親または祖先にあたるコンポーネントから共有されたデータやメソッドを受け取る機能です。",{"id":668,"title":650,"titles":669,"content":670,"level":94},"/difference-between-defineexpose-and-provide-and-inject#使用例-1",[640,655],"Provide と Inject を使用する場合、下記のように書くことができます。 Provide を定義する親コンポーネントです。 先ほどの defineExpose で使用した例を再利用しています。 注意点としては、defineExpose とは異なり、親コンポーネントであることです。 \u003Cscript setup lang=\"ts\">\nimport { ref, provide } from \"vue\";\nimport Count from \"./Count.vue\"\n\nconst count = ref\u003Cnumber>(0);\n\nconst increment = () => {\n count.value++;\n};\n\nprovide(\"increment\", increment);\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cp>{{ count }}\u003C/p>\n \u003CCount />\n\u003C/template> 親コンポーネントから Provide で共有された increment という関数を Inject で受け取るために子コンポーネントは下記のように書きます。 \u003Cscript setup lang=\"ts\">\nimport { inject } from \"vue\";\n\nconst increment = inject\u003C() => void>(\"increment\")!;\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cdiv>\n \u003Cbutton type=\"button\" @click=\"increment()\">increment\u003C/button>\n \u003C/div>\n\u003C/template> 子コンポーネントで定義した increment 書かれたボタンをクリックすると親コンポーネントの count というリアクティブデータが1つずつ増えることを確認することができます。 Provide と Injectは、子または子孫コンポーネントから親または祖先コンポーネントに対して情報を共有することはできません。 例えば、下記のコードはエラーになります。 子コンポーネントで定義した increment という関数を Provide で定義します。 \u003Cscript setup lang=\"ts\">\nimport { ref, provide } from \"vue\";\n\nconst count = ref(0);\n\nconst increment = () => {\n count.value++;\n};\n\nprovide(\"increment\", increment);\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cp>{{ count }}\u003C/p>\n\u003C/template> 次に親コンポーネントで Inject を使用して increment という関数にアクセスしようとしてみます。 \u003Cscript setup lang=\"ts\">\nimport { inject } from \"vue\";\nimport Count from \"./Count.vue\"\n\nconst increment = inject\u003C() => void>(\"increment\")!;\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cdiv>\n \u003CCount />\n \u003Cbutton type=\"button\" @click=\"increment()\">increment\u003C/button>\n \u003C/div>\n\u003C/template> 上記のコードを実行すると次のエラーが表示されるはずです。 Vue warn: injection \"increment\" not found.",{"id":672,"title":107,"titles":673,"content":674,"level":26},"/difference-between-defineexpose-and-provide-and-inject#まとめ",[640],"defineExpose を使用することで親または祖先コンポーネントだけでなく、外部のスクリプトファイルに対してもプロパティを公開することができます。 Provide は、親または祖先コンポーネントから子または子孫コンポーネントに対して情報を共有する機能であり、子または子孫コンポーネントから親または祖先コンポーネントに対して情報を共有することはできません。 Inject は、子または子孫にあたるコンポーネントが親または祖先にあたるコンポーネントから共有されたデータやメソッドを受け取るのみで外部にプロパティを公開したり、親または祖先コンポーネントに対して情報を共有することはできません。 html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"id":676,"title":677,"titles":678,"content":679,"level":49},"/run-workflow-with-secrets-in-dependabot","Dependabot で secrets を使用するワークフローを実行する方法",[],"Dependabot によってプルリクエストが作成された際に secrets を使用したワークフローが失敗してしまうときの対処法についてまとめる。",{"id":681,"title":682,"titles":683,"content":684,"level":26},"/run-workflow-with-secrets-in-dependabot#secrets-を使用したワークフローが失敗する","secrets を使用したワークフローが失敗する",[677],"先日、chromatic を GitHub Actions で実行するようなワークフローを追加しました。 自分で変更を加え、プッシュをした際やプルリクエストを作成した際に chromatic のワークフローが実行されることは確認していたのですが、Dependabot によってプルリクエストが作成された際にワークフローが失敗してしまいました。 実際に表示されたエラーメッセージを確認すると、chromatic にデプロイを実行する際に必要なトークンが取得できていないことが分かります。 ✖ Missing project token\n\nSign in to https://www.chromatic.com/start and create a new project,\nor find your project token on the Manage screen in an existing project.\nSet your project token as the CHROMATIC_PROJECT_TOKEN environment variable\nor pass the --project-token command line option. しかし、GitHub の secrets に CHROMATIC_PROJECT_TOKEN というトークン保存しているため、トークンを読み込めないという事象に戸惑いました。 調査を進める中で Dependabot は actions に設定されている secrets を読み込むことができないということがわかりました。",{"id":686,"title":687,"titles":688,"content":689,"level":26},"/run-workflow-with-secrets-in-dependabot#解決策","解決策",[677],"Dependabot secrets を使用することで解決することができます。 次の画像を参考に Settings の Security から Secrets and variables へアクセスし、「New repository secret」というボタンを押下することで chromatic の実行時に必要なトークンを設定することができます。 設定することができたら、失敗した chromatic のワークフローを再実行し、無事に成功することを確認します。",{"id":691,"title":692,"titles":693,"content":694,"level":49},"/toref-and-torefs","toRef と toRefs",[],"似た名前の toRef と toRefs の違いについて調べる。",{"id":696,"title":697,"titles":698,"content":699,"level":26},"/toref-and-torefs#toref-について","toRef について",[692],"toRef は、引数として与えられた値から ref を作成します。 例えば、props として受け取った値を変更したい場合があると思います。 props は、一方向のバインディングを形成します。 親コンポーネントで値が変更した場合、子コンポーネントで受け取った props の値は変更されますが、子コンポーネントで受け取った props の値をを変更することはできません。 次のコードはボタンをクリックしても値が変更されません。 \u003Cscript setup lang=\"ts\">\ndefineProps\u003C{ count: number }>();\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cbutton type=\"button\" @click=\"count++\">count is {{ count }}\u003C/button>\n\u003C/template> このような場合に toRef を使用することで受け取った props の値を変更することができます。 toRef を使用することで受け取った props から ref を作成することでボタンをクリックした際に値が変更されるようになります。 \u003Cscript setup lang=\"ts\">\nimport { toRef } from \"vue\";\n\nconst props = defineProps\u003C{ count: number }>();\n\nconst count = toRef(props.count);\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cbutton type=\"button\" @click=\"count++\">count is {{ count }}\u003C/button>\n\u003C/template> この時、toRef は新たに ref を定義するため、親で保持する値が変更されないことに注意してください。 次の親コンポーネントで定義された count の値は変更されません。 \u003Cscript setup lang=\"ts\">\nimport { ref } from \"vue\";\nimport Child from \"./components/Child.vue\";\n\nconst count = ref\u003Cnumber>(0);\n\u003C/script>\n\n\u003Ctemplate>\n \u003CChild :count=\"count\" />\n \u003Cp>count is {{ count }}\u003C/p>\n\u003C/template> また、readonly なリアクティブデータとして定義することもできます。 次のコードは先ほどとは異なり、ボタンをクリックしても count の値が変更されることはありません。 \u003Cscript setup lang=\"ts\">\nimport { toRef } from \"vue\";\n\nconst props = defineProps\u003C{ count: number }>();\n\nconst count = toRef(() => props.count);\n\u003C/script>\n\n\u003Ctemplate>\n \u003Cbutton type=\"button\" @click=\"count++\">count is {{ count }}\u003C/button>\n\u003C/template>",{"id":701,"title":702,"titles":703,"content":704,"level":26},"/toref-and-torefs#torefs-について","toRefs について",[692],"toRefs は、reactive によって宣言されたリアクティブなオブジェクトの各プロパティを toRef によって ref に変換します。 \u003Cscript setup lang=\"ts\">\nimport { reactive, toRefs } from \"vue\";\n\nconst man = reactive({\n age: 20,\n});\n\nconst stateAsRefs = toRefs(man);\n\nman.age++;\nconsole.log(man.age); // 21\nconsole.log(stateAsRefs.age.value); // 21\n\nstateAsRefs.age.value++;\nconsole.log(man.age); // 22\nconsole.log(stateAsRefs.age.value); // 22\n\u003C/script> リアクティビティを損なうことなく、個々の ref を生成することができるため、どちらかの値が変更されるともう片方の値も書き変わります。 toRefs を使用して ref を生成する場合、.value を使用してアクセスする必要があることに注意してください。",{"id":706,"title":107,"titles":707,"content":708,"level":26},"/toref-and-torefs#まとめ",[692],"toRef は引数から ref を生成し、toRefs はリアクティブなオブジェクトから toRef を使用して個別の ref を生成します。 html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",[710,712],{"title":498,"path":497,"stem":711,"description":500,"children":-1},"20.call-once-composable",{"title":524,"path":523,"stem":713,"description":526,"children":-1},"22.fix-msw-github-pages-error",["Reactive",715],{"$snuxt-i18n-meta":716,"$scolor-mode":717,"$ssite-config":720},{},{"preference":718,"value":718,"unknown":34,"forced":719},"system",false,{"_priority":721,"currentLocale":725,"defaultLocale":725,"env":726,"name":727,"url":728},{"name":722,"env":723,"url":722,"defaultLocale":724,"currentLocale":724},-3,-15,-2,"ja_JP","production","blog by Hikaru Kobayashi","https://www.hikaru-kobayashi.me",["Set"],["ShallowReactive",731],{"search-data":-1,"/option-display-none":-1,"pager":-1}]