[{"data":1,"prerenderedAt":12904},["ShallowReactive",2],{"tag-nuxt-1":3},{"count":4,"content":5},9,[6,3848,4905,6694,8739,10426,11222,11314,11421],{"id":7,"title":8,"body":9,"category":3835,"createdAt":3837,"description":8,"extension":3838,"index":3839,"meta":3840,"navigation":621,"path":3841,"publish":621,"seo":3842,"series":3839,"seriesTitle":3839,"stem":3843,"tag":3844,"thumbnail":3846,"updatedAt":3837,"__hash__":3847},"articles\u002Farticles\u002Flaravel-nuxt-jwt-spa.md","laravel 6 + Nuxt.js で作るJWT認証つきSPA構築",{"type":10,"value":11,"toc":3798},"minimark",[12,16,19,22,25,33,38,41,47,50,66,69,73,85,402,425,447,451,454,463,482,486,489,493,496,503,509,515,519,522,529,536,542,546,557,561,564,570,587,590,593,599,605,684,691,697,700,703,706,712,715,721,727,731,988,992,1002,1137,1140,1146,1150,1157,1202,1205,1211,1214,1221,1227,1232,1637,1640,1644,1653,1657,1660,1667,1670,1673,1680,1684,1687,1690,1695,1701,1705,1731,1735,1741,1759,1844,1858,1862,1871,1874,1877,1883,1887,1893,1899,1902,1908,2221,2224,2235,2242,2263,2270,2273,2276,2391,2404,2410,2422,2425,2428,2431,2434,2441,3635,3646,3652,3655,3658,3661,3664,3667,3670,3673,3676,3703,3707,3710,3716,3725,3728,3731,3735,3738,3766,3769,3772,3794],[13,14,15],"p",{},"こんにちはjunです。laravel をAPIサーバーとして扱いフロントはNuxt.jsのSPAで構築するプロジェクトがありました。認証を実装したり初期のセッティングでそこそこ詰まったのでメモがてら記事にします。",[13,17,18],{},"この手の記事にありがちな「開発サーバーまでの段階しかやらない」ではなくSPAをビルドしてDocker上の仮想的に作成したサーバー環境で実際に一つのアプリケーションとして動かすとこまでやってみます。",[13,20,21],{},"独立したフロントエンド とバックをどうやってつなげて、どんなサーバー構成にすればいいのか？という視点で見ていただければ幸いです。また私も昨日にようやく自分の頭で纏った程度なので、この手のアプリケーション作成がﾁｮｯﾄﾃﾞｷﾙ人はぜひアドバイスやコメントお願いします。",[13,23,24],{},"Dockerを用いた環境構築概要から話しています。環境がありLravelとNuxtのAPI連携について知りたい人は **「Larvel にJWT認証機能を追加する」**から読みはじめてください。",[13,26,27,28,32],{},"使用環境\ndocker：19.03.13\nmaxOS Catalina：10.15.5\ncomposer：1.10.6\nNode.js：12.19.0\n",[29,30,31],"strong",{},"Laravel：6.20","\nNuxt.js：2.14.6",[34,35,37],"h2",{"id":36},"dockerで仮想環境を構築","Dockerで仮想環境を構築",[13,39,40],{},"私が今回構築するサーバー構成は以下の図の感じです。",[42,43],"image-render",{":src":44,":width":45,":center":46},"'_mix\u002Flaravel-nuxt.jpeg'","'500px'","true",[13,48,49],{},"webサーバーではバーチャルホスト を使って、80・443などの通常のHTTP通信に対してはNuxt.jsのビルドファイルを返します。そして8000ポートにはLaravelのプロジェクトをおいて、APIサーバーとして使用します。",[13,51,52,53,57,58,61,62,65],{},"Nuxt.jsはLaravelから発行されたトークンを用いてAPIにアクセスしてデータを引っ張ります。実際の運用ではドメインが付与されるので、例えば",[54,55,56],"code",{},"service.com","として、",[54,59,60],{},"http(s):\u002F\u002Fservice.com","はNuxt.jsへ、そしてNuxt.jsは",[54,63,64],{},"https:\u002F\u002Fapi.service.com","へAPIを飛ばします。",[13,67,68],{},"バーチャルホスト の設定をすることで公開側とAPIをconfレベルで分けることができます。つまりDockerのwebサーバーイメージには、ApacheまたはNginxの設定ファイルをボリュームして、それぞれのドキュメントルート を指定できるような環境があれば大丈夫です。（無理に私のイメージとかに合わせなくても大丈夫ということです。）",[70,71,72],"h3",{"id":72},"docker-composeは以下のような感じ",[13,74,75,80,81,84],{},[76,77,79],"a",{"href":78},"\u002Farticles\u002Fbuild-lamp-with-docker","こちら","記事で作成したイメージを使用します。cenotsから構築したLAMP環境です。",[54,82,83],{},"centos_apache","イメージを用いてweb側をビルドしています。",[86,87,93],"pre",{"className":88,"code":89,"filename":90,"language":91,"meta":92,"style":92},"language-yml shiki shiki-themes material-theme-ocean","version: '3'\nservices: \n  web_1:\n    image: centos_apache:1.0\n    depends_on: \n      - db\n    volumes: \n      - .\u002Fhtml\u002F:\u002Fvar\u002Fwww\u002Fhtml\u002F\n      - .\u002Fweb_1\u002Fhttpd.conf:\u002Fetc\u002Fhttpd\u002Fconf\u002Fhttpd.conf\n      - \u002Fsys\u002Ffs\u002Fcgroup:\u002Fsys\u002Ffs\u002Fcgroup:ro\n    ports: \n      - \"9000:80\"\n      - \"3000:3000\"\n      - \"8000:8000\"\n    privileged: true\n    command: \u002Fsbin\u002Finit\n  db:\n    image: mysql:5.7\n    environment:\n      MYSQL_DATABASE: trend_system\n      MYSQL_USER: trend\n      MYSQL_PASSWORD: trendtrend\n      MYSQL_ROOT_PASSWORD: rootroot\n    ports: \n      - \"3306:3306\"\n    volumes: \n      - laravel_data:\u002Fvar\u002Flib\u002Fmysql\nvolumes: \n  laravel_data: {}\n","docker-compose.yml","yml","",[54,94,95,118,130,139,150,160,169,179,187,194,202,212,226,238,250,262,273,281,291,299,310,321,332,343,352,364,373,381,391],{"__ignoreMap":92},[96,97,100,104,108,111,115],"span",{"class":98,"line":99},"line",1,[96,101,103],{"class":102},"s-wAU","version",[96,105,107],{"class":106},"sAklC",":",[96,109,110],{"class":106}," '",[96,112,114],{"class":113},"sfyAc","3",[96,116,117],{"class":106},"'\n",[96,119,121,124,126],{"class":98,"line":120},2,[96,122,123],{"class":102},"services",[96,125,107],{"class":106},[96,127,129],{"class":128},"s0W1g"," \n",[96,131,133,136],{"class":98,"line":132},3,[96,134,135],{"class":102},"  web_1",[96,137,138],{"class":106},":\n",[96,140,142,145,147],{"class":98,"line":141},4,[96,143,144],{"class":102},"    image",[96,146,107],{"class":106},[96,148,149],{"class":113}," centos_apache:1.0\n",[96,151,153,156,158],{"class":98,"line":152},5,[96,154,155],{"class":102},"    depends_on",[96,157,107],{"class":106},[96,159,129],{"class":128},[96,161,163,166],{"class":98,"line":162},6,[96,164,165],{"class":106},"      -",[96,167,168],{"class":113}," db\n",[96,170,172,175,177],{"class":98,"line":171},7,[96,173,174],{"class":102},"    volumes",[96,176,107],{"class":106},[96,178,129],{"class":128},[96,180,182,184],{"class":98,"line":181},8,[96,183,165],{"class":106},[96,185,186],{"class":113}," .\u002Fhtml\u002F:\u002Fvar\u002Fwww\u002Fhtml\u002F\n",[96,188,189,191],{"class":98,"line":4},[96,190,165],{"class":106},[96,192,193],{"class":113}," .\u002Fweb_1\u002Fhttpd.conf:\u002Fetc\u002Fhttpd\u002Fconf\u002Fhttpd.conf\n",[96,195,197,199],{"class":98,"line":196},10,[96,198,165],{"class":106},[96,200,201],{"class":113}," \u002Fsys\u002Ffs\u002Fcgroup:\u002Fsys\u002Ffs\u002Fcgroup:ro\n",[96,203,205,208,210],{"class":98,"line":204},11,[96,206,207],{"class":102},"    ports",[96,209,107],{"class":106},[96,211,129],{"class":128},[96,213,215,217,220,223],{"class":98,"line":214},12,[96,216,165],{"class":106},[96,218,219],{"class":106}," \"",[96,221,222],{"class":113},"9000:80",[96,224,225],{"class":106},"\"\n",[96,227,229,231,233,236],{"class":98,"line":228},13,[96,230,165],{"class":106},[96,232,219],{"class":106},[96,234,235],{"class":113},"3000:3000",[96,237,225],{"class":106},[96,239,241,243,245,248],{"class":98,"line":240},14,[96,242,165],{"class":106},[96,244,219],{"class":106},[96,246,247],{"class":113},"8000:8000",[96,249,225],{"class":106},[96,251,253,256,258],{"class":98,"line":252},15,[96,254,255],{"class":102},"    privileged",[96,257,107],{"class":106},[96,259,261],{"class":260},"sbqyR"," true\n",[96,263,265,268,270],{"class":98,"line":264},16,[96,266,267],{"class":102},"    command",[96,269,107],{"class":106},[96,271,272],{"class":113}," \u002Fsbin\u002Finit\n",[96,274,276,279],{"class":98,"line":275},17,[96,277,278],{"class":102},"  db",[96,280,138],{"class":106},[96,282,284,286,288],{"class":98,"line":283},18,[96,285,144],{"class":102},[96,287,107],{"class":106},[96,289,290],{"class":113}," mysql:5.7\n",[96,292,294,297],{"class":98,"line":293},19,[96,295,296],{"class":102},"    environment",[96,298,138],{"class":106},[96,300,302,305,307],{"class":98,"line":301},20,[96,303,304],{"class":102},"      MYSQL_DATABASE",[96,306,107],{"class":106},[96,308,309],{"class":113}," trend_system\n",[96,311,313,316,318],{"class":98,"line":312},21,[96,314,315],{"class":102},"      MYSQL_USER",[96,317,107],{"class":106},[96,319,320],{"class":113}," trend\n",[96,322,324,327,329],{"class":98,"line":323},22,[96,325,326],{"class":102},"      MYSQL_PASSWORD",[96,328,107],{"class":106},[96,330,331],{"class":113}," trendtrend\n",[96,333,335,338,340],{"class":98,"line":334},23,[96,336,337],{"class":102},"      MYSQL_ROOT_PASSWORD",[96,339,107],{"class":106},[96,341,342],{"class":113}," rootroot\n",[96,344,346,348,350],{"class":98,"line":345},24,[96,347,207],{"class":102},[96,349,107],{"class":106},[96,351,129],{"class":128},[96,353,355,357,359,362],{"class":98,"line":354},25,[96,356,165],{"class":106},[96,358,219],{"class":106},[96,360,361],{"class":113},"3306:3306",[96,363,225],{"class":106},[96,365,367,369,371],{"class":98,"line":366},26,[96,368,174],{"class":102},[96,370,107],{"class":106},[96,372,129],{"class":128},[96,374,376,378],{"class":98,"line":375},27,[96,377,165],{"class":106},[96,379,380],{"class":113}," laravel_data:\u002Fvar\u002Flib\u002Fmysql\n",[96,382,384,387,389],{"class":98,"line":383},28,[96,385,386],{"class":102},"volumes",[96,388,107],{"class":106},[96,390,129],{"class":128},[96,392,394,397,399],{"class":98,"line":393},29,[96,395,396],{"class":102},"  laravel_data",[96,398,107],{"class":106},[96,400,401],{"class":106}," {}\n",[13,403,404,405,408,409,412,413,416,417,420,421,424],{},"ホストマシンの",[54,406,407],{},"localhost:9000","にアクセスするとコンテナの",[54,410,411],{},"localhost:80","につながります。Nuxt.jsの開発サーバーポートとなる",[54,414,415],{},"3000","はホストとコンテナ一緒にしています。多分使わないのですが ",[54,418,419],{},"php artisan serve","で立てるLaravel開発サーバーポート",[54,422,423],{},"8000","も一応用意しておきます。",[13,426,427,428,431,432,435,436,439,440,442,443,446],{},"ホスト側には",[54,429,430],{},"html","ディレクトリに",[54,433,434],{},"frontend","、",[54,437,438],{},"laravel","と言ったディレクトリがあります。Nuxt、Laravelのソースがそれぞれに配置されています。コンテナ起動時にはその",[54,441,430],{},"ディレクトリはコンテナの",[54,444,445],{},"\u002Fvar\u002Fwww\u002Fhtml","にボリュームされます。",[70,448,450],{"id":449},"httpdconfは以下の感じ","httpd.confは以下の感じ",[13,452,453],{},"Nginx兄貴達にはすみませんが、とりあえず以下のようなバーチャルホスト 設定が立てられば大丈夫です。",[86,455,461],{"className":456,"code":458,"filename":459,"language":460,"meta":92},[457],"language-text","Listen 8000\n\u003CDirectory \"\u002Fvar\u002Fwww\u002Fhtml\u002Ffrontend\u002Fdist\">\n    Options Indexes FollowSymLinks\n    AllowOverride All\n    Require all granted\n\u003C\u002FDirectory>\n\n\u003CVirtualHost *:80>\n    ServerName example.com\n    DocumentRoot \u002Fvar\u002Fwww\u002Fhtml\u002Ffrontend\u002Fdist\n\u003C\u002FVirtualHost>\n\n\u003CVirtualHost *:8000>\n    ServerName example.com\n    DocumentRoot \u002Fvar\u002Fwww\u002Fhtml\u002Flaravel\u002Fpublic\n\u003C\u002FVirtualHost>\n","httpd.conf","text",[54,462,458],{"__ignoreMap":92},[13,464,465,466,469,470,473,474,477,478,481],{},"forntendはnuxtのプロジェクトディレクトリ、laravelはLaravelのプロジェクトディレクトリ名です。80でリクエストが来たらnuxtのビルドした",[54,467,468],{},"index.html","がある",[54,471,472],{},"\u002Fvar\u002Fwww\u002Fhtml\u002Ffrontend\u002Fdist","へ飛ばされます。一応後でブラウザからはアクセスできないようにしますが、8000できたリクエストは",[54,475,476],{},"\u002Fvar\u002Fwww\u002Fhtml\u002Flaravel\u002Fpublic","の",[54,479,480],{},"index.php","がキャッチします。",[70,483,485],{"id":484},"ぶっちゃけdockerなくてもとりあえず行けます","ぶっちゃけDockerなくてもとりあえず行けます",[13,487,488],{},"初心者の人は「？」となるかもしれません。しかしDBがあり、ホストマシン上でnuxtプロジェクトとLaravelお互いの開発サーバ同士で連絡が取れれば、見出しの内容は実装可能です。ビルドまでしたり総合的に確かめたい場合にDockerを使用してください。",[34,490,492],{"id":491},"laravelの設定をする","Laravelの設定をする",[70,494,495],{"id":495},"コンテナを起動",[13,497,498,499,502],{},"docker-compose.ymlがあるディレクトリで",[54,500,501],{},"docker-compose up -d"," をします。特に問題なければコンテナーが起動するはずです。起動したら",[86,504,507],{"className":505,"code":506,"language":460},[457],"docker exec -it {web側のコンテナ名} \u002Fbin\u002Fbash\n",[54,508,506],{"__ignoreMap":92},[13,510,511,512,514],{},"をしてコンテナに入りましょう。",[54,513,445],{},"にいくとマウントしたLaravelプロジェクトとNuxt.jsプロジェクトがいるはずです。",[70,516,518],{"id":517},"laravel-のenvファイルを変更","laravel のenvファイルを変更",[13,520,521],{},"マイグレーションをしたいのでlaravelのenvを設定します。",[86,523,527],{"className":524,"code":525,"filename":526,"language":460,"meta":92},[457],"DB_CONNECTION=mysql\nDB_HOST=db \u002F\u002F docker-compose.yml で定義したDBのコンテナ\nDB_PORT=3306\nDB_DATABASE=laravel_test\nDB_USERNAME=root\nDB_PASSWORD=rootroot\n",".env",[54,528,525],{"__ignoreMap":92},[13,530,531,532,535],{},"docker-composeのおかげでこの設定であればつながります。コンテナの中",[54,533,534],{},"\u002Fvar\u002Fwww\u002Fhtmm\u002Flaravel","にてマイグレーションを実行します。",[86,537,540],{"className":538,"code":539,"language":460},[457],"$ php artisan migrate\n",[54,541,539],{"__ignoreMap":92},[34,543,545],{"id":544},"larvel-にjwt認証機能を追加する","Larvel にJWT認証機能を追加する",[13,547,548,549,552,553,556],{},"ここからが本番です。ちなみに私が使用している",[29,550,551],{},"Laravel はバージョン6.2.0","です。バージョンによって設定が異なったりします。この ",[29,554,555],{},"記事は2020年12月時点でLravel 8 は出ているけど、LTSなどの都合でLravel 6を使用するという状況で書いています。"," sライブラリなどはあえてバージョンを指定したりしていますので注意してください。",[70,558,560],{"id":559},"jwtライブラリをインストール","JWTライブラリをインストール",[13,562,563],{},"Laravelには標準でJTWが入っていないのでライブラリをインストールします。",[86,565,568],{"className":566,"code":567,"language":460},[457],"composer require tymon\u002Fjwt-auth:1.0.0-rc.5\n",[54,569,567],{"__ignoreMap":92},[13,571,572,575,576,579,580,586],{},[54,573,574],{},"tymon\u002Fjwt-auth"," というライブラリを入れますが、Laravel 6 の場合はバージョンに",[54,577,578],{},":1.0.0-rc.5","を指定しないとエラーになります。以下は",[76,581,585],{"href":582,"rel":583},"https:\u002F\u002Fjwt-auth.readthedocs.io\u002Fen\u002Fdevelop\u002F",[584],"nofollow","このライブラリのドキュメント","通りの実装です。",[70,588,589],{"id":589},"設定を一部変更",[13,591,592],{},"ライブラリのインストールをすればJWTは使えるようになります。しかし今回の構築では一部変更しなければならない箇所があったので、設定ファイルを生成します。",[86,594,597],{"className":595,"code":596,"language":460},[457],"php artisan vendor:publish --provider=\"Tymon\\JWTAuth\\Providers\\LaravelServiceProvider\"\n",[54,598,596],{"__ignoreMap":92},[13,600,601,604],{},[54,602,603],{},"config\u002Fjwt.php","というファイルが生成されたはずです。このファイルにおいて以下の部分を書き換えます。",[86,606,610],{"className":607,"code":608,"language":609,"meta":92,"style":92},"language-php shiki shiki-themes material-theme-ocean","'providers' => [\n\n\u002F*\n|--------------------------------------------------------------------------\n| JWT Provider\n|--------------------------------------------------------------------------\n|\n| Specify the provider that is used to create and decode the tokens.\n|\n*\u002F\n\n\u002F\u002F 'jwt' => Tymon\\JWTAuth\\Providers\\JWT\\Lcobucci::class,\n'jwt' => Tymon\\JWTAuth\\Providers\\JWT\\Namshi::class,\n\n]\n","php",[54,611,612,617,623,628,633,638,642,647,652,656,661,665,670,675,679],{"__ignoreMap":92},[96,613,614],{"class":98,"line":99},[96,615,616],{},"'providers' => [\n",[96,618,619],{"class":98,"line":120},[96,620,622],{"emptyLinePlaceholder":621},true,"\n",[96,624,625],{"class":98,"line":132},[96,626,627],{},"\u002F*\n",[96,629,630],{"class":98,"line":141},[96,631,632],{},"|--------------------------------------------------------------------------\n",[96,634,635],{"class":98,"line":152},[96,636,637],{},"| JWT Provider\n",[96,639,640],{"class":98,"line":162},[96,641,632],{},[96,643,644],{"class":98,"line":171},[96,645,646],{},"|\n",[96,648,649],{"class":98,"line":181},[96,650,651],{},"| Specify the provider that is used to create and decode the tokens.\n",[96,653,654],{"class":98,"line":4},[96,655,646],{},[96,657,658],{"class":98,"line":196},[96,659,660],{},"*\u002F\n",[96,662,663],{"class":98,"line":204},[96,664,622],{"emptyLinePlaceholder":621},[96,666,667],{"class":98,"line":214},[96,668,669],{},"\u002F\u002F 'jwt' => Tymon\\JWTAuth\\Providers\\JWT\\Lcobucci::class,\n",[96,671,672],{"class":98,"line":228},[96,673,674],{},"'jwt' => Tymon\\JWTAuth\\Providers\\JWT\\Namshi::class,\n",[96,676,677],{"class":98,"line":240},[96,678,622],{"emptyLinePlaceholder":621},[96,680,681],{"class":98,"line":252},[96,682,683],{},"]\n",[13,685,686,687,690],{},"JWTのプロバイダーを",[54,688,689],{},"Tymon\\JWTAuth\\Providers\\JWT\\Namshi::class","に変更します。というのも標準の設定でいくと",[86,692,695],{"className":693,"code":694,"language":460},[457],"Tymon\\JWTAuth\\Exceptions\\JWTException: Could not create token: Implicit conversion of keys from strings is deprecated. Please use InMemory or LocalFileReference classes. \n",[54,696,694],{"__ignoreMap":92},[13,698,699],{},"というエラーが生じます。JWTトークンを生成する処理に非推奨な部分があるそうで、プロバイダを変更する必要がありました。参考記事にあるgithub issueがお世話になりました。",[70,701,702],{"id":702},"シークレットを生成",[13,704,705],{},"JWTトークンを生成するためのシークレットを作成します。",[86,707,710],{"className":708,"code":709,"language":460},[457],"php artisan jwt:secret\n",[54,711,709],{"__ignoreMap":92},[13,713,714],{},"するとenvファイルの下の方に",[86,716,719],{"className":717,"code":718,"language":460},[457],"JWT_SECRET=ve1b******\n",[54,720,718],{"__ignoreMap":92},[13,722,723,724,726],{},"というものが生成されます。ちなみにこのシークレットは絶対に外部に漏れてはいけません。間違ってgithubとかに上げないように ",[54,725,526],{},"ファイルはgitignoreしましょう。",[70,728,730],{"id":729},"userモデルにメソッドを追加","Userモデルにメソッドを追加",[86,732,735],{"className":607,"code":733,"filename":734,"language":609,"meta":92,"style":92},"\u003C?php\n\nnamespace App;\n\nuse Illuminate\\Contracts\\Auth\\MustVerifyEmail;\nuse Illuminate\\Foundation\\Auth\\User as Authenticatable;\nuse Illuminate\\Notifications\\Notifiable;\nuse Tymon\\JWTAuth\\Contracts\\JWTSubject; \u002F\u002F追加\n\nclass User extends Authenticatable implements JWTSubject \u002F\u002F追加\n{\n    use Notifiable;\n\n    \u002F**\n     * The attributes that are mass assignable.\n     *\n     * @var array\n     *\u002F\n    protected $fillable = [\n        'name', 'email', 'password',\n    ];\n\n    \u002F**\n     * The attributes that should be hidden for arrays.\n     *\n     * @var array\n     *\u002F\n    protected $hidden = [\n        'password', 'remember_token',\n    ];\n\n    \u002F**\n     * The attributes that should be cast to native types.\n     *\n     * @var array\n     *\u002F\n    protected $casts = [\n        'email_verified_at' => 'datetime',\n    ];\n\n    public function getJWTIdentifier()　 \u002F\u002F追加\n    {\n        return $this->getKey();\n    }\n\n    public function getJWTCustomClaims()　 \u002F\u002F追加\n    {\n        return [];\n    }\n}\n","app\u002FUser.php",[54,736,737,742,746,751,755,760,765,770,775,779,784,789,794,798,803,808,813,818,823,828,833,838,842,846,851,855,859,863,868,873,878,883,888,894,899,904,909,915,921,926,931,937,943,949,955,960,966,971,977,982],{"__ignoreMap":92},[96,738,739],{"class":98,"line":99},[96,740,741],{},"\u003C?php\n",[96,743,744],{"class":98,"line":120},[96,745,622],{"emptyLinePlaceholder":621},[96,747,748],{"class":98,"line":132},[96,749,750],{},"namespace App;\n",[96,752,753],{"class":98,"line":141},[96,754,622],{"emptyLinePlaceholder":621},[96,756,757],{"class":98,"line":152},[96,758,759],{},"use Illuminate\\Contracts\\Auth\\MustVerifyEmail;\n",[96,761,762],{"class":98,"line":162},[96,763,764],{},"use Illuminate\\Foundation\\Auth\\User as Authenticatable;\n",[96,766,767],{"class":98,"line":171},[96,768,769],{},"use Illuminate\\Notifications\\Notifiable;\n",[96,771,772],{"class":98,"line":181},[96,773,774],{},"use Tymon\\JWTAuth\\Contracts\\JWTSubject; \u002F\u002F追加\n",[96,776,777],{"class":98,"line":4},[96,778,622],{"emptyLinePlaceholder":621},[96,780,781],{"class":98,"line":196},[96,782,783],{},"class User extends Authenticatable implements JWTSubject \u002F\u002F追加\n",[96,785,786],{"class":98,"line":204},[96,787,788],{},"{\n",[96,790,791],{"class":98,"line":214},[96,792,793],{},"    use Notifiable;\n",[96,795,796],{"class":98,"line":228},[96,797,622],{"emptyLinePlaceholder":621},[96,799,800],{"class":98,"line":240},[96,801,802],{},"    \u002F**\n",[96,804,805],{"class":98,"line":252},[96,806,807],{},"     * The attributes that are mass assignable.\n",[96,809,810],{"class":98,"line":264},[96,811,812],{},"     *\n",[96,814,815],{"class":98,"line":275},[96,816,817],{},"     * @var array\n",[96,819,820],{"class":98,"line":283},[96,821,822],{},"     *\u002F\n",[96,824,825],{"class":98,"line":293},[96,826,827],{},"    protected $fillable = [\n",[96,829,830],{"class":98,"line":301},[96,831,832],{},"        'name', 'email', 'password',\n",[96,834,835],{"class":98,"line":312},[96,836,837],{},"    ];\n",[96,839,840],{"class":98,"line":323},[96,841,622],{"emptyLinePlaceholder":621},[96,843,844],{"class":98,"line":334},[96,845,802],{},[96,847,848],{"class":98,"line":345},[96,849,850],{},"     * The attributes that should be hidden for arrays.\n",[96,852,853],{"class":98,"line":354},[96,854,812],{},[96,856,857],{"class":98,"line":366},[96,858,817],{},[96,860,861],{"class":98,"line":375},[96,862,822],{},[96,864,865],{"class":98,"line":383},[96,866,867],{},"    protected $hidden = [\n",[96,869,870],{"class":98,"line":393},[96,871,872],{},"        'password', 'remember_token',\n",[96,874,876],{"class":98,"line":875},30,[96,877,837],{},[96,879,881],{"class":98,"line":880},31,[96,882,622],{"emptyLinePlaceholder":621},[96,884,886],{"class":98,"line":885},32,[96,887,802],{},[96,889,891],{"class":98,"line":890},33,[96,892,893],{},"     * The attributes that should be cast to native types.\n",[96,895,897],{"class":98,"line":896},34,[96,898,812],{},[96,900,902],{"class":98,"line":901},35,[96,903,817],{},[96,905,907],{"class":98,"line":906},36,[96,908,822],{},[96,910,912],{"class":98,"line":911},37,[96,913,914],{},"    protected $casts = [\n",[96,916,918],{"class":98,"line":917},38,[96,919,920],{},"        'email_verified_at' => 'datetime',\n",[96,922,924],{"class":98,"line":923},39,[96,925,837],{},[96,927,929],{"class":98,"line":928},40,[96,930,622],{"emptyLinePlaceholder":621},[96,932,934],{"class":98,"line":933},41,[96,935,936],{},"    public function getJWTIdentifier()　 \u002F\u002F追加\n",[96,938,940],{"class":98,"line":939},42,[96,941,942],{},"    {\n",[96,944,946],{"class":98,"line":945},43,[96,947,948],{},"        return $this->getKey();\n",[96,950,952],{"class":98,"line":951},44,[96,953,954],{},"    }\n",[96,956,958],{"class":98,"line":957},45,[96,959,622],{"emptyLinePlaceholder":621},[96,961,963],{"class":98,"line":962},46,[96,964,965],{},"    public function getJWTCustomClaims()　 \u002F\u002F追加\n",[96,967,969],{"class":98,"line":968},47,[96,970,942],{},[96,972,974],{"class":98,"line":973},48,[96,975,976],{},"        return [];\n",[96,978,980],{"class":98,"line":979},49,[96,981,954],{},[96,983,985],{"class":98,"line":984},50,[96,986,987],{},"}\n",[70,989,991],{"id":990},"authphpの認証ドライバを変更","auth.phpの認証ドライバを変更",[13,993,994,997,998,1001],{},[54,995,996],{},"config\u002Fauth.php","にて",[54,999,1000],{},"Authentication","認証ドライバを変更します。",[86,1003,1005],{"className":607,"code":1004,"filename":996,"language":609,"meta":92,"style":92},"    \u002F*\n    |--------------------------------------------------------------------------\n    | Authentication Defaults\n    |--------------------------------------------------------------------------\n    |\n    | This option controls the default authentication \"guard\" and password\n    | reset options for your application. You may change these defaults\n    | as required, but they're a perfect start for most applications.\n    |\n    *\u002F\n\n    'defaults' => [\n        'guard' => 'api', \u002F\u002F変更\n        'passwords' => 'users',\n    ],\n...\n    'guards' => [\n        'web' => [\n            'driver' => 'session',\n            'provider' => 'users',\n        ],\n\n        'api' => [\n            'driver' => 'jwt', \u002F\u002F変更\n            'provider' => 'users',\n            'hash' => false,\n",[54,1006,1007,1012,1017,1022,1026,1031,1036,1041,1046,1050,1055,1059,1064,1072,1077,1082,1087,1092,1097,1102,1107,1112,1116,1121,1128,1132],{"__ignoreMap":92},[96,1008,1009],{"class":98,"line":99},[96,1010,1011],{},"    \u002F*\n",[96,1013,1014],{"class":98,"line":120},[96,1015,1016],{},"    |--------------------------------------------------------------------------\n",[96,1018,1019],{"class":98,"line":132},[96,1020,1021],{},"    | Authentication Defaults\n",[96,1023,1024],{"class":98,"line":141},[96,1025,1016],{},[96,1027,1028],{"class":98,"line":152},[96,1029,1030],{},"    |\n",[96,1032,1033],{"class":98,"line":162},[96,1034,1035],{},"    | This option controls the default authentication \"guard\" and password\n",[96,1037,1038],{"class":98,"line":171},[96,1039,1040],{},"    | reset options for your application. You may change these defaults\n",[96,1042,1043],{"class":98,"line":181},[96,1044,1045],{},"    | as required, but they're a perfect start for most applications.\n",[96,1047,1048],{"class":98,"line":4},[96,1049,1030],{},[96,1051,1052],{"class":98,"line":196},[96,1053,1054],{},"    *\u002F\n",[96,1056,1057],{"class":98,"line":204},[96,1058,622],{"emptyLinePlaceholder":621},[96,1060,1061],{"class":98,"line":214},[96,1062,1063],{},"    'defaults' => [\n",[96,1065,1066,1069],{"class":98,"line":228},[96,1067,1068],{},"        'guard' => 'api',",[96,1070,1071],{}," \u002F\u002F変更\n",[96,1073,1074],{"class":98,"line":240},[96,1075,1076],{},"        'passwords' => 'users',\n",[96,1078,1079],{"class":98,"line":252},[96,1080,1081],{},"    ],\n",[96,1083,1084],{"class":98,"line":264},[96,1085,1086],{},"...\n",[96,1088,1089],{"class":98,"line":275},[96,1090,1091],{},"    'guards' => [\n",[96,1093,1094],{"class":98,"line":283},[96,1095,1096],{},"        'web' => [\n",[96,1098,1099],{"class":98,"line":293},[96,1100,1101],{},"            'driver' => 'session',\n",[96,1103,1104],{"class":98,"line":301},[96,1105,1106],{},"            'provider' => 'users',\n",[96,1108,1109],{"class":98,"line":312},[96,1110,1111],{},"        ],\n",[96,1113,1114],{"class":98,"line":323},[96,1115,622],{"emptyLinePlaceholder":621},[96,1117,1118],{"class":98,"line":334},[96,1119,1120],{},"        'api' => [\n",[96,1122,1123,1126],{"class":98,"line":345},[96,1124,1125],{},"            'driver' => 'jwt',",[96,1127,1071],{},[96,1129,1130],{"class":98,"line":354},[96,1131,1106],{},[96,1133,1134],{"class":98,"line":366},[96,1135,1136],{},"            'hash' => false,\n",[13,1138,1139],{},"configはキャッシュされていますので、configをいじった後はキャッシュクリアをしましょう。",[86,1141,1144],{"className":1142,"code":1143,"language":460},[457],"php artisan config:clear\n",[54,1145,1143],{"__ignoreMap":92},[70,1147,1149],{"id":1148},"jwt認証用のルートを作成","JWT認証用のルートを作成",[13,1151,1152,1153,1156],{},"認証のルートを作成します。",[54,1154,1155],{},"route\u002Fapi.php","に以下のように記述します。",[86,1158,1160],{"className":607,"code":1159,"filename":1155,"language":609,"meta":92,"style":92},"Route::prefix('v1')->group(function(){\n    Route::group(['middleware' => 'api', 'prefix' => 'auth'], function ($router) {\n        Route::post('login', 'AuthController@login')->name('login');;\n        Route::post('logout', 'AuthController@logout');\n        Route::post('refresh', 'AuthController@refresh');\n        Route::get('me', 'AuthController@me');\n    });\n});\n",[54,1161,1162,1167,1172,1177,1182,1187,1192,1197],{"__ignoreMap":92},[96,1163,1164],{"class":98,"line":99},[96,1165,1166],{},"Route::prefix('v1')->group(function(){\n",[96,1168,1169],{"class":98,"line":120},[96,1170,1171],{},"    Route::group(['middleware' => 'api', 'prefix' => 'auth'], function ($router) {\n",[96,1173,1174],{"class":98,"line":132},[96,1175,1176],{},"        Route::post('login', 'AuthController@login')->name('login');;\n",[96,1178,1179],{"class":98,"line":141},[96,1180,1181],{},"        Route::post('logout', 'AuthController@logout');\n",[96,1183,1184],{"class":98,"line":152},[96,1185,1186],{},"        Route::post('refresh', 'AuthController@refresh');\n",[96,1188,1189],{"class":98,"line":162},[96,1190,1191],{},"        Route::get('me', 'AuthController@me');\n",[96,1193,1194],{"class":98,"line":171},[96,1195,1196],{},"    });\n",[96,1198,1199],{"class":98,"line":181},[96,1200,1201],{},"});\n",[13,1203,1204],{},"ルートの設定は運用によって変わると思いますが、APIはバージョンで予め分けておくと将来の拡張性が高まります。一回ぽっきりのサービスでも実装して損はないと思います。上記のルートでは以下のような設定になります。",[86,1206,1209],{"className":1207,"code":1208,"language":460},[457],"$ php artisan route:list\n+--------+----------+---------------------+-------+---------------------------------------------+--------------+\n| Domain | Method   | URI                 | Name  | Action                                      | Middleware   |\n+--------+----------+---------------------+-------+---------------------------------------------+--------------+\n|        | POST     | api\u002Fv1\u002Fauth\u002Flogin   | login | App\\Http\\Controllers\\AuthController@login   | api          |\n|        | POST     | api\u002Fv1\u002Fauth\u002Flogout  |       | App\\Http\\Controllers\\AuthController@logout  | api,auth:api |\n|        | GET|HEAD | api\u002Fv1\u002Fauth\u002Fme      |       | App\\Http\\Controllers\\AuthController@me      | api,auth:api |\n|        | POST     | api\u002Fv1\u002Fauth\u002Frefresh |       | App\\Http\\Controllers\\AuthController@refresh | api,auth:api |\n+--------+----------+---------------------+-------+---------------------------------------------+--------------+\n",[54,1210,1208],{"__ignoreMap":92},[70,1212,1213],{"id":1213},"認証ルートに対するコントローラを作成",[13,1215,1216,1217,1220],{},"それぞれのルートは",[54,1218,1219],{},"AuthController.php","につなげる予定ですので、そのコントローラーを作成します。",[86,1222,1225],{"className":1223,"code":1224,"language":460},[457],"php artisan make:controller AuthController\n",[54,1226,1224],{"__ignoreMap":92},[13,1228,1229,1231],{},[54,1230,1219],{},"は以下のような設定になります。",[86,1233,1236],{"className":607,"code":1234,"filename":1235,"language":609,"meta":92,"style":92},"\u003C?php\n\nnamespace App\\Http\\Controllers;\n\nuse Illuminate\\Http\\Request;\nuse Illuminate\\Support\\Facades\\Auth;\n\nclass AuthController extends Controller\n{\n    \u002F**\n     * Create a new AuthController instance.\n     *\n     * @return void\n     *\u002F\n    public function __construct()\n    {\n        $this->middleware('auth:api', ['except' => ['login']]);\n    }\n\n    \u002F**\n     * Get a JWT via given credentials.\n     *\n     * @return \\Illuminate\\Http\\JsonResponse\n     *\u002F\n    public function login()\n    {\n        $credentials = request(['email', 'password']);\n\n        if (! $token = auth()->attempt($credentials)) {\n            return response()->json(['error' => 'Unauthorized'], 401);\n        }\n\n        return $this->respondWithToken($token);\n    }\n\n    \u002F**\n     * Get the authenticated User.\n     *\n     * @return \\Illuminate\\Http\\JsonResponse\n     *\u002F\n    public function me()\n    {\n        return response()->json(auth()->user());\n    }\n\n    \u002F**\n     * Log the user out (Invalidate the token).\n     *\n     * @return \\Illuminate\\Http\\JsonResponse\n     *\u002F\n    public function logout()\n    {\n        auth()->logout();\n\n        return response()->json(['message' => 'Successfully logged out']);\n    }\n\n    \u002F**\n     * Refresh a token.\n     *\n     * @return \\Illuminate\\Http\\JsonResponse\n     *\u002F\n    public function refresh()\n    {\n        return $this->respondWithToken(auth()->refresh());\n    }\n\n    \u002F**\n     * Get the token array structure.\n     *\n     * @param  string $token\n     *\n     * @return \\Illuminate\\Http\\JsonResponse\n     *\u002F\n    protected function respondWithToken($token)\n    {\n        return response()->json([\n            'access_token' => $token,\n            'token_type' => 'bearer',\n            'expires_in' => auth()->factory()->getTTL() * 60\n        ]);\n    }\n}\n","app\u002FHttp\u002FControllers\u002FAuthController.php",[54,1237,1238,1242,1246,1251,1255,1260,1265,1269,1274,1278,1282,1287,1291,1296,1300,1305,1309,1314,1318,1322,1326,1331,1335,1340,1344,1349,1353,1358,1362,1367,1372,1377,1381,1386,1390,1394,1398,1403,1407,1411,1415,1420,1424,1429,1433,1437,1441,1446,1450,1454,1458,1464,1469,1475,1480,1486,1491,1496,1501,1507,1512,1517,1522,1528,1533,1539,1544,1549,1554,1560,1565,1571,1576,1581,1586,1592,1597,1603,1609,1615,1621,1627,1632],{"__ignoreMap":92},[96,1239,1240],{"class":98,"line":99},[96,1241,741],{},[96,1243,1244],{"class":98,"line":120},[96,1245,622],{"emptyLinePlaceholder":621},[96,1247,1248],{"class":98,"line":132},[96,1249,1250],{},"namespace App\\Http\\Controllers;\n",[96,1252,1253],{"class":98,"line":141},[96,1254,622],{"emptyLinePlaceholder":621},[96,1256,1257],{"class":98,"line":152},[96,1258,1259],{},"use Illuminate\\Http\\Request;\n",[96,1261,1262],{"class":98,"line":162},[96,1263,1264],{},"use Illuminate\\Support\\Facades\\Auth;\n",[96,1266,1267],{"class":98,"line":171},[96,1268,622],{"emptyLinePlaceholder":621},[96,1270,1271],{"class":98,"line":181},[96,1272,1273],{},"class AuthController extends Controller\n",[96,1275,1276],{"class":98,"line":4},[96,1277,788],{},[96,1279,1280],{"class":98,"line":196},[96,1281,802],{},[96,1283,1284],{"class":98,"line":204},[96,1285,1286],{},"     * Create a new AuthController instance.\n",[96,1288,1289],{"class":98,"line":214},[96,1290,812],{},[96,1292,1293],{"class":98,"line":228},[96,1294,1295],{},"     * @return void\n",[96,1297,1298],{"class":98,"line":240},[96,1299,822],{},[96,1301,1302],{"class":98,"line":252},[96,1303,1304],{},"    public function __construct()\n",[96,1306,1307],{"class":98,"line":264},[96,1308,942],{},[96,1310,1311],{"class":98,"line":275},[96,1312,1313],{},"        $this->middleware('auth:api', ['except' => ['login']]);\n",[96,1315,1316],{"class":98,"line":283},[96,1317,954],{},[96,1319,1320],{"class":98,"line":293},[96,1321,622],{"emptyLinePlaceholder":621},[96,1323,1324],{"class":98,"line":301},[96,1325,802],{},[96,1327,1328],{"class":98,"line":312},[96,1329,1330],{},"     * Get a JWT via given credentials.\n",[96,1332,1333],{"class":98,"line":323},[96,1334,812],{},[96,1336,1337],{"class":98,"line":334},[96,1338,1339],{},"     * @return \\Illuminate\\Http\\JsonResponse\n",[96,1341,1342],{"class":98,"line":345},[96,1343,822],{},[96,1345,1346],{"class":98,"line":354},[96,1347,1348],{},"    public function login()\n",[96,1350,1351],{"class":98,"line":366},[96,1352,942],{},[96,1354,1355],{"class":98,"line":375},[96,1356,1357],{},"        $credentials = request(['email', 'password']);\n",[96,1359,1360],{"class":98,"line":383},[96,1361,622],{"emptyLinePlaceholder":621},[96,1363,1364],{"class":98,"line":393},[96,1365,1366],{},"        if (! $token = auth()->attempt($credentials)) {\n",[96,1368,1369],{"class":98,"line":875},[96,1370,1371],{},"            return response()->json(['error' => 'Unauthorized'], 401);\n",[96,1373,1374],{"class":98,"line":880},[96,1375,1376],{},"        }\n",[96,1378,1379],{"class":98,"line":885},[96,1380,622],{"emptyLinePlaceholder":621},[96,1382,1383],{"class":98,"line":890},[96,1384,1385],{},"        return $this->respondWithToken($token);\n",[96,1387,1388],{"class":98,"line":896},[96,1389,954],{},[96,1391,1392],{"class":98,"line":901},[96,1393,622],{"emptyLinePlaceholder":621},[96,1395,1396],{"class":98,"line":906},[96,1397,802],{},[96,1399,1400],{"class":98,"line":911},[96,1401,1402],{},"     * Get the authenticated User.\n",[96,1404,1405],{"class":98,"line":917},[96,1406,812],{},[96,1408,1409],{"class":98,"line":923},[96,1410,1339],{},[96,1412,1413],{"class":98,"line":928},[96,1414,822],{},[96,1416,1417],{"class":98,"line":933},[96,1418,1419],{},"    public function me()\n",[96,1421,1422],{"class":98,"line":939},[96,1423,942],{},[96,1425,1426],{"class":98,"line":945},[96,1427,1428],{},"        return response()->json(auth()->user());\n",[96,1430,1431],{"class":98,"line":951},[96,1432,954],{},[96,1434,1435],{"class":98,"line":957},[96,1436,622],{"emptyLinePlaceholder":621},[96,1438,1439],{"class":98,"line":962},[96,1440,802],{},[96,1442,1443],{"class":98,"line":968},[96,1444,1445],{},"     * Log the user out (Invalidate the token).\n",[96,1447,1448],{"class":98,"line":973},[96,1449,812],{},[96,1451,1452],{"class":98,"line":979},[96,1453,1339],{},[96,1455,1456],{"class":98,"line":984},[96,1457,822],{},[96,1459,1461],{"class":98,"line":1460},51,[96,1462,1463],{},"    public function logout()\n",[96,1465,1467],{"class":98,"line":1466},52,[96,1468,942],{},[96,1470,1472],{"class":98,"line":1471},53,[96,1473,1474],{},"        auth()->logout();\n",[96,1476,1478],{"class":98,"line":1477},54,[96,1479,622],{"emptyLinePlaceholder":621},[96,1481,1483],{"class":98,"line":1482},55,[96,1484,1485],{},"        return response()->json(['message' => 'Successfully logged out']);\n",[96,1487,1489],{"class":98,"line":1488},56,[96,1490,954],{},[96,1492,1494],{"class":98,"line":1493},57,[96,1495,622],{"emptyLinePlaceholder":621},[96,1497,1499],{"class":98,"line":1498},58,[96,1500,802],{},[96,1502,1504],{"class":98,"line":1503},59,[96,1505,1506],{},"     * Refresh a token.\n",[96,1508,1510],{"class":98,"line":1509},60,[96,1511,812],{},[96,1513,1515],{"class":98,"line":1514},61,[96,1516,1339],{},[96,1518,1520],{"class":98,"line":1519},62,[96,1521,822],{},[96,1523,1525],{"class":98,"line":1524},63,[96,1526,1527],{},"    public function refresh()\n",[96,1529,1531],{"class":98,"line":1530},64,[96,1532,942],{},[96,1534,1536],{"class":98,"line":1535},65,[96,1537,1538],{},"        return $this->respondWithToken(auth()->refresh());\n",[96,1540,1542],{"class":98,"line":1541},66,[96,1543,954],{},[96,1545,1547],{"class":98,"line":1546},67,[96,1548,622],{"emptyLinePlaceholder":621},[96,1550,1552],{"class":98,"line":1551},68,[96,1553,802],{},[96,1555,1557],{"class":98,"line":1556},69,[96,1558,1559],{},"     * Get the token array structure.\n",[96,1561,1563],{"class":98,"line":1562},70,[96,1564,812],{},[96,1566,1568],{"class":98,"line":1567},71,[96,1569,1570],{},"     * @param  string $token\n",[96,1572,1574],{"class":98,"line":1573},72,[96,1575,812],{},[96,1577,1579],{"class":98,"line":1578},73,[96,1580,1339],{},[96,1582,1584],{"class":98,"line":1583},74,[96,1585,822],{},[96,1587,1589],{"class":98,"line":1588},75,[96,1590,1591],{},"    protected function respondWithToken($token)\n",[96,1593,1595],{"class":98,"line":1594},76,[96,1596,942],{},[96,1598,1600],{"class":98,"line":1599},77,[96,1601,1602],{},"        return response()->json([\n",[96,1604,1606],{"class":98,"line":1605},78,[96,1607,1608],{},"            'access_token' => $token,\n",[96,1610,1612],{"class":98,"line":1611},79,[96,1613,1614],{},"            'token_type' => 'bearer',\n",[96,1616,1618],{"class":98,"line":1617},80,[96,1619,1620],{},"            'expires_in' => auth()->factory()->getTTL() * 60\n",[96,1622,1624],{"class":98,"line":1623},81,[96,1625,1626],{},"        ]);\n",[96,1628,1630],{"class":98,"line":1629},82,[96,1631,954],{},[96,1633,1635],{"class":98,"line":1634},83,[96,1636,987],{},[13,1638,1639],{},"これでJWT認証に必要なLarvel側の設定が終了しました。",[34,1641,1643],{"id":1642},"talend-api-testerでapiをチェック","Talend API TesterでAPIをチェック",[13,1645,1646,1647,1652],{},"きちんとAPIが存在しているかを確かめるために、chrome拡張の",[76,1648,1651],{"href":1649,"rel":1650},"https:\u002F\u002Fchrome.google.com\u002Fwebstore\u002Fdetail\u002Ftalend-api-tester-free-ed\u002Faejoelaoggembcahagimdiliamlcdmfm",[584],"Talend API Tester","を用いてチェックします。予めシーダーで作成した仮ユーザーデータを用いて以下のように入力してみます。",[42,1654],{":src":1655,":width":1656},"'_mix\u002Fsch-2020-12-05-14.17.54-768x353.png'","'100%'",[42,1658],{":src":1659,":width":1656},"'_mix\u002Fsch-2020-12-05-14.20.21-768x318.png'",[13,1661,1662,1663,1666],{},"JSONでアクセストークン が帰ってきました。このアクセストークン をリクエストヘッダに入れてリクエストすることで認証が必要なルートにアクセスできるようになります。試しに ",[54,1664,1665],{},"\u002Fapi\u002Fv1\u002Fauth\u002Fme","という現在のユーザー情報を確かめるルートにアクセスしてみます。",[13,1668,1669],{},"HEADERSにAuthorizationというものを追加し、先ほどのトークンを入れています。トークン値は「bearer トークン」という形です。",[42,1671],{":src":1672,":width":1656},"'_mix\u002Fsch-2020-12-05-14.23.34-768x224.png'",[13,1674,1675,1676,1679],{},"ユーザー情報が返ってきましたね。",[54,1677,1678],{},"Authcontroller@me","で定義した返り値がきちんとえられました。ちなみに認証情報がない場合はHTTP 401を返します。",[34,1681,1683],{"id":1682},"nuxtjsからapiを呼ぶ","Nuxt.jsからAPIを呼ぶ",[70,1685,1686],{"id":1686},"前準備",[13,1688,1689],{},"Nuxt.jsの構築に移る前にCORS対策をします。APIサーバーはブラウザからのアクセスを禁止して、XMLHttpRequest（XHR）のみのリクエストを許可するようにします。そしてXHRにはCORSという制約があります。これがあるとローカル以外からのXHRを用いたAPIアクセスが制限されます。特にNuxtを3000ポートで開発している時でもAPIサーバーと通信できるように準備しておきます。",[1691,1692,1694],"h4",{"id":1693},"cors設定のライブラリをインストール","CORS設定のライブラリをインストール",[86,1696,1699],{"className":1697,"code":1698,"language":460},[457],"composer require fruitcake\u002Flaravel-cors\n",[54,1700,1698],{"__ignoreMap":92},[1691,1702,1704],{"id":1703},"apphttpkernelphpのミドルウェア-に追加","app\u002FHttp\u002FKernel.phpのミドルウェア に追加",[86,1706,1709],{"className":607,"code":1707,"filename":1708,"language":609,"meta":92,"style":92},"protected $middleware = [\n    \u002F\u002F ...\n    \\Fruitcake\\Cors\\HandleCors::class,\n];\n","app\u002FHttp\u002FKernel.php",[54,1710,1711,1716,1721,1726],{"__ignoreMap":92},[96,1712,1713],{"class":98,"line":99},[96,1714,1715],{},"protected $middleware = [\n",[96,1717,1718],{"class":98,"line":120},[96,1719,1720],{},"    \u002F\u002F ...\n",[96,1722,1723],{"class":98,"line":132},[96,1724,1725],{},"    \\Fruitcake\\Cors\\HandleCors::class,\n",[96,1727,1728],{"class":98,"line":141},[96,1729,1730],{},"];\n",[1691,1732,1734],{"id":1733},"cors設定ファイルを出力して一部変更","CORS設定ファイルを出力して一部変更",[86,1736,1739],{"className":1737,"code":1738,"language":460},[457],"php artisan vendor:publish --tag=\"cors\"\n",[54,1740,1738],{"__ignoreMap":92},[13,1742,1743,1744,1747,1748,1751,1752,1755,1756,1758],{},"先ほどの",[54,1745,1746],{},"jwt.php","のように",[54,1749,1750],{},"cors.php","が",[54,1753,1754],{},"config\u002F","配下に出現します。その",[54,1757,1750],{},"を以下のように変更します。",[86,1760,1763],{"className":607,"code":1761,"filename":1762,"language":609,"meta":92,"style":92},"...\n    \u002F*\n     * You can enable CORS for 1 or multiple paths.\n     * Example: ['api\u002F*']\n     *\u002F\n    'paths' => ['api\u002F*'],\n...\n    \u002F*\n     * Matches the request origin. `['*']` allows all origins. Wildcards can be used, eg `*.mydomain.com`\n     *\u002F\n    'allowed_origins' => [\n          'http:\u002F\u002Flocalhost',\n          'http:\u002F\u002Flocalhost:3000',\u002F\u002F nuxt\n          'http:\u002F\u002Flocalhost:9000' \u002F\u002F dockerの9000->80\n        ],\n\n...\n","confog\u002Fcors.php",[54,1764,1765,1769,1773,1778,1783,1787,1792,1796,1800,1805,1809,1814,1819,1824,1832,1836,1840],{"__ignoreMap":92},[96,1766,1767],{"class":98,"line":99},[96,1768,1086],{},[96,1770,1771],{"class":98,"line":120},[96,1772,1011],{},[96,1774,1775],{"class":98,"line":132},[96,1776,1777],{},"     * You can enable CORS for 1 or multiple paths.\n",[96,1779,1780],{"class":98,"line":141},[96,1781,1782],{},"     * Example: ['api\u002F*']\n",[96,1784,1785],{"class":98,"line":152},[96,1786,822],{},[96,1788,1789],{"class":98,"line":162},[96,1790,1791],{},"    'paths' => ['api\u002F*'],\n",[96,1793,1794],{"class":98,"line":171},[96,1795,1086],{},[96,1797,1798],{"class":98,"line":181},[96,1799,1011],{},[96,1801,1802],{"class":98,"line":4},[96,1803,1804],{},"     * Matches the request origin. `['*']` allows all origins. Wildcards can be used, eg `*.mydomain.com`\n",[96,1806,1807],{"class":98,"line":196},[96,1808,822],{},[96,1810,1811],{"class":98,"line":204},[96,1812,1813],{},"    'allowed_origins' => [\n",[96,1815,1816],{"class":98,"line":214},[96,1817,1818],{},"          'http:\u002F\u002Flocalhost',\n",[96,1820,1821],{"class":98,"line":228},[96,1822,1823],{},"          'http:\u002F\u002Flocalhost:3000',\u002F\u002F nuxt\n",[96,1825,1826,1829],{"class":98,"line":240},[96,1827,1828],{},"          'http:\u002F\u002Flocalhost:9000'",[96,1830,1831],{}," \u002F\u002F dockerの9000->80\n",[96,1833,1834],{"class":98,"line":252},[96,1835,1111],{},[96,1837,1838],{"class":98,"line":264},[96,1839,622],{"emptyLinePlaceholder":621},[96,1841,1842],{"class":98,"line":275},[96,1843,1086],{},[13,1845,1846,1849,1850,1853,1854,1857],{},[54,1847,1848],{},"api\u002F"," ルート配下に対してcors設定を行い、そして",[54,1851,1852],{},"localhost","及び",[54,1855,1856],{},"3000ポート","からの接続を許可しました。",[70,1859,1861],{"id":1860},"nuxt-auth-モジュールを用いて認証系の機能を整える","nuxt auth モジュールを用いて認証系の機能を整える",[13,1863,1864,1865,1870],{},"nuxt.jsの構築は飛ばします。私の環境では nuxt+bootstrap+axiosで始めます。認証系をpluginで構築してもいいのですが、なかなか大変ですのでnuxt authモジュールを使用します。このモジュールがあれば認証つきのNuxtアプリを簡単に作成できます。",[76,1866,1869],{"href":1867,"rel":1868},"https:\u002F\u002Fdev.auth.nuxtjs.org\u002F",[584],"本家のドキュメント","に習いながら進めましょう。",[1691,1872,1873],{"id":1873},"モジュールのインストール",[13,1875,1876],{},"nuxt.jsのプロジェクトルートに移動いして以下のモジュールをインストールします。",[86,1878,1881],{"className":1879,"code":1880,"language":460},[457],"npm install @nuxtjs\u002Fauth-next @nuxtjs\u002Faxios\n",[54,1882,1880],{"__ignoreMap":92},[1691,1884,1886],{"id":1885},"nuxtconfigjsでの設定","nuxt.config.jsでの設定",[13,1888,1889,1892],{},[54,1890,1891],{},"nuxt.config.js","でまずモジュールの読み込みをします。",[86,1894,1897],{"className":1895,"code":1896,"language":460},[457],"...javascript[nuxt.config.js]\n\u002F\u002F Modules (https:\u002F\u002Fgo.nuxtjs.dev\u002Fconfig-modules)\n  modules: [\n    '@nuxtjs\u002Faxios',\n    '@nuxtjs\u002Fauth'\n  ],\n...\n",[54,1898,1896],{"__ignoreMap":92},[13,1900,1901],{},"これでauthモジュールとaxiosモジュールが使えます。authモジュールはstoreを使用しますので、storeにindex.jsがない場合は空でもいいので作っていきます。",[13,1903,1904,1905,1907],{},"そしてauthモジュールの設定を",[54,1906,1891],{},"で行います。",[86,1909,1913],{"className":1910,"code":1911,"filename":1891,"language":1912,"meta":92,"style":92},"language-javascript shiki shiki-themes material-theme-ocean","...\nauth:{\n    localStorage: false,\n    strategies:{\n      local:{\n        tokenType:'bearer',\n        endpoints:{\n          login:{\n            url:'\u002Fauth\u002Flogin',\n            method:'post',\n            propertyName:'access_token'\n          },\n          logout:{\n            url:'\u002Fauth\u002Flogout',\n            method:'post',\n          },\n          user:{\n            url:'\u002Fauth\u002Fme',\n            method:'get',\n            propertyName:false\n          }\n        }\n      },\n      redirect: {\n        login: '\u002Flogin',\n        logout: '\u002F',\n        callback: '\u002Flogin',\n        home: '\u002Fhome'\n      }\n}\n...\n","javascript",[54,1914,1915,1919,1928,1941,1948,1955,1972,1979,1986,2002,2018,2032,2037,2044,2059,2073,2077,2084,2099,2114,2123,2128,2132,2137,2147,2163,2179,2194,2208,2213,2217],{"__ignoreMap":92},[96,1916,1917],{"class":98,"line":99},[96,1918,1086],{"class":106},[96,1920,1921,1925],{"class":98,"line":120},[96,1922,1924],{"class":1923},"s5Dmg","auth",[96,1926,1927],{"class":106},":{\n",[96,1929,1930,1933,1935,1938],{"class":98,"line":132},[96,1931,1932],{"class":1923},"    localStorage",[96,1934,107],{"class":106},[96,1936,1937],{"class":260}," false",[96,1939,1940],{"class":106},",\n",[96,1942,1943,1946],{"class":98,"line":141},[96,1944,1945],{"class":1923},"    strategies",[96,1947,1927],{"class":106},[96,1949,1950,1953],{"class":98,"line":152},[96,1951,1952],{"class":1923},"      local",[96,1954,1927],{"class":106},[96,1956,1957,1960,1962,1965,1968,1970],{"class":98,"line":162},[96,1958,1959],{"class":1923},"        tokenType",[96,1961,107],{"class":106},[96,1963,1964],{"class":106},"'",[96,1966,1967],{"class":113},"bearer",[96,1969,1964],{"class":106},[96,1971,1940],{"class":106},[96,1973,1974,1977],{"class":98,"line":171},[96,1975,1976],{"class":1923},"        endpoints",[96,1978,1927],{"class":106},[96,1980,1981,1984],{"class":98,"line":181},[96,1982,1983],{"class":1923},"          login",[96,1985,1927],{"class":106},[96,1987,1988,1991,1993,1995,1998,2000],{"class":98,"line":4},[96,1989,1990],{"class":1923},"            url",[96,1992,107],{"class":106},[96,1994,1964],{"class":106},[96,1996,1997],{"class":113},"\u002Fauth\u002Flogin",[96,1999,1964],{"class":106},[96,2001,1940],{"class":106},[96,2003,2004,2007,2009,2011,2014,2016],{"class":98,"line":196},[96,2005,2006],{"class":1923},"            method",[96,2008,107],{"class":106},[96,2010,1964],{"class":106},[96,2012,2013],{"class":113},"post",[96,2015,1964],{"class":106},[96,2017,1940],{"class":106},[96,2019,2020,2023,2025,2027,2030],{"class":98,"line":204},[96,2021,2022],{"class":1923},"            propertyName",[96,2024,107],{"class":106},[96,2026,1964],{"class":106},[96,2028,2029],{"class":113},"access_token",[96,2031,117],{"class":106},[96,2033,2034],{"class":98,"line":214},[96,2035,2036],{"class":106},"          },\n",[96,2038,2039,2042],{"class":98,"line":228},[96,2040,2041],{"class":1923},"          logout",[96,2043,1927],{"class":106},[96,2045,2046,2048,2050,2052,2055,2057],{"class":98,"line":240},[96,2047,1990],{"class":1923},[96,2049,107],{"class":106},[96,2051,1964],{"class":106},[96,2053,2054],{"class":113},"\u002Fauth\u002Flogout",[96,2056,1964],{"class":106},[96,2058,1940],{"class":106},[96,2060,2061,2063,2065,2067,2069,2071],{"class":98,"line":252},[96,2062,2006],{"class":1923},[96,2064,107],{"class":106},[96,2066,1964],{"class":106},[96,2068,2013],{"class":113},[96,2070,1964],{"class":106},[96,2072,1940],{"class":106},[96,2074,2075],{"class":98,"line":264},[96,2076,2036],{"class":106},[96,2078,2079,2082],{"class":98,"line":275},[96,2080,2081],{"class":1923},"          user",[96,2083,1927],{"class":106},[96,2085,2086,2088,2090,2092,2095,2097],{"class":98,"line":283},[96,2087,1990],{"class":1923},[96,2089,107],{"class":106},[96,2091,1964],{"class":106},[96,2093,2094],{"class":113},"\u002Fauth\u002Fme",[96,2096,1964],{"class":106},[96,2098,1940],{"class":106},[96,2100,2101,2103,2105,2107,2110,2112],{"class":98,"line":293},[96,2102,2006],{"class":1923},[96,2104,107],{"class":106},[96,2106,1964],{"class":106},[96,2108,2109],{"class":113},"get",[96,2111,1964],{"class":106},[96,2113,1940],{"class":106},[96,2115,2116,2118,2120],{"class":98,"line":301},[96,2117,2022],{"class":1923},[96,2119,107],{"class":106},[96,2121,2122],{"class":260},"false\n",[96,2124,2125],{"class":98,"line":312},[96,2126,2127],{"class":106},"          }\n",[96,2129,2130],{"class":98,"line":323},[96,2131,1376],{"class":106},[96,2133,2134],{"class":98,"line":334},[96,2135,2136],{"class":106},"      },\n",[96,2138,2139,2142,2144],{"class":98,"line":345},[96,2140,2141],{"class":1923},"      redirect",[96,2143,107],{"class":106},[96,2145,2146],{"class":106}," {\n",[96,2148,2149,2152,2154,2156,2159,2161],{"class":98,"line":354},[96,2150,2151],{"class":1923},"        login",[96,2153,107],{"class":106},[96,2155,110],{"class":106},[96,2157,2158],{"class":113},"\u002Flogin",[96,2160,1964],{"class":106},[96,2162,1940],{"class":106},[96,2164,2165,2168,2170,2172,2175,2177],{"class":98,"line":366},[96,2166,2167],{"class":1923},"        logout",[96,2169,107],{"class":106},[96,2171,110],{"class":106},[96,2173,2174],{"class":113},"\u002F",[96,2176,1964],{"class":106},[96,2178,1940],{"class":106},[96,2180,2181,2184,2186,2188,2190,2192],{"class":98,"line":375},[96,2182,2183],{"class":1923},"        callback",[96,2185,107],{"class":106},[96,2187,110],{"class":106},[96,2189,2158],{"class":113},[96,2191,1964],{"class":106},[96,2193,1940],{"class":106},[96,2195,2196,2199,2201,2203,2206],{"class":98,"line":383},[96,2197,2198],{"class":1923},"        home",[96,2200,107],{"class":106},[96,2202,110],{"class":106},[96,2204,2205],{"class":113},"\u002Fhome",[96,2207,117],{"class":106},[96,2209,2210],{"class":98,"line":393},[96,2211,2212],{"class":106},"      }\n",[96,2214,2215],{"class":98,"line":875},[96,2216,987],{"class":106},[96,2218,2219],{"class":98,"line":880},[96,2220,1086],{"class":106},[13,2222,2223],{},"ここでは予めauthモジュールで使用するログイン用のルートを指定したり、使用する通信パターンを定義します。",[13,2225,2226,2227,2230,2231,2234],{},"JWTトークンをローカルストレージに入れておくのは危ないらしいので、",[54,2228,2229],{},"localStorage: false","としておきます。そして",[54,2232,2233],{},"strategies","の中で通信パターンやルートの定義を行います。",[13,2236,2237,2238,2241],{},"今は",[54,2239,2240],{},"local","という通信パターンしかありませんが、outhとかapi2とか他のapiサーバーに対しての通信パターンを複数定義できます。",[13,2243,2244,2247,2248,2251,2252,2255,2256,2258,2259,2262],{},[54,2245,2246],{},"tokenType","で先ほどの",[54,2249,2250],{},"beare","を指定しておきます。こうすると自動的に",[54,2253,2254],{},"authorization","ヘッダーに",[54,2257,2250],{},"という文字を追加してくれます。",[54,2260,2261],{},"endopoints","でそれぞれのログイン（login）、ログアウト（logout）、ユーザー確認（user）、それぞれのルートを指定します。",[13,2264,2265,2266,2269],{},"指定しないとauthモジュールのデフォルトのURLでアクセスしてしまいます。特に",[54,2267,2268],{},"user","はページが読み込みされた際に、トークンをサーバーに送ってログイン状態かどうかをNuxt.jsに伝える機能があります。ここをキチンと設定しないとNuxt側で現在ログインが必要なページが開けないなどの状態に陥ります。",[1691,2271,2272],{"id":2272},"axiosの設定",[13,2274,2275],{},"最後にaxiosの設定をします。",[86,2277,2279],{"className":1910,"code":2278,"filename":1891,"language":1912,"meta":92,"style":92},"const ENV = require('dotenv').config().parsed;\nexport default {\n... \nenv:ENV,\naxios: {\n    baseURL: ENV.API_BASE_URL,\n  },\n...\n}\n",[54,2280,2281,2327,2338,2345,2352,2361,2378,2383,2387],{"__ignoreMap":92},[96,2282,2283,2287,2290,2293,2297,2300,2302,2305,2307,2310,2313,2316,2319,2321,2324],{"class":98,"line":99},[96,2284,2286],{"class":2285},"sJ14y","const",[96,2288,2289],{"class":128}," ENV ",[96,2291,2292],{"class":106},"=",[96,2294,2296],{"class":2295},"sdLwU"," require",[96,2298,2299],{"class":128},"(",[96,2301,1964],{"class":106},[96,2303,2304],{"class":113},"dotenv",[96,2306,1964],{"class":106},[96,2308,2309],{"class":128},")",[96,2311,2312],{"class":106},".",[96,2314,2315],{"class":2295},"config",[96,2317,2318],{"class":128},"()",[96,2320,2312],{"class":106},[96,2322,2323],{"class":128},"parsed",[96,2325,2326],{"class":106},";\n",[96,2328,2329,2333,2336],{"class":98,"line":120},[96,2330,2332],{"class":2331},"s6cf3","export",[96,2334,2335],{"class":2331}," default",[96,2337,2146],{"class":106},[96,2339,2340,2343],{"class":98,"line":132},[96,2341,2342],{"class":106},"...",[96,2344,129],{"class":128},[96,2346,2347,2350],{"class":98,"line":141},[96,2348,2349],{"class":128},"env:ENV",[96,2351,1940],{"class":106},[96,2353,2354,2357,2359],{"class":98,"line":152},[96,2355,2356],{"class":102},"axios",[96,2358,107],{"class":106},[96,2360,2146],{"class":106},[96,2362,2363,2366,2368,2371,2373,2376],{"class":98,"line":162},[96,2364,2365],{"class":102},"    baseURL",[96,2367,107],{"class":106},[96,2369,2370],{"class":128}," ENV",[96,2372,2312],{"class":106},[96,2374,2375],{"class":128},"API_BASE_URL",[96,2377,1940],{"class":106},[96,2379,2380],{"class":98,"line":171},[96,2381,2382],{"class":106},"  },\n",[96,2384,2385],{"class":98,"line":181},[96,2386,1086],{"class":106},[96,2388,2389],{"class":98,"line":4},[96,2390,987],{"class":106},[13,2392,2393,2394,2396,2397,2400,2401,2403],{},"nuxtのdotenvモジュールを用いて",[54,2395,526],{},"ファイルから",[54,2398,2399],{},"baseURL","つまりAPIのアクセス先ルートを指定します。",[54,2402,526],{},"は以下のようになっています。",[86,2405,2408],{"className":2406,"code":2407,"filename":526,"language":460,"meta":92},[457],"API_BASE_URL=http:\u002F\u002Flocalhost:8000\u002Fapi\u002Fv1\n",[54,2409,2407],{"__ignoreMap":92},[13,2411,2412,2413,2415,2416,2418,2419,2421],{},"今はローカルの開発環境でやっていますが本番は",[54,2414,1852],{},"ではなくドメインになったりします。環境ごとに異なる値は",[54,2417,526],{},"に記述して環境ごとに",[54,2420,526],{},"ファイルを作成してそれをインポートします。",[70,2423,2424],{"id":2424},"ログインフォームを整える",[13,2426,2427],{},"デフォルトのページとかを削除してログインフォームとかを用意します。今回はbootstrapで構築しました。このようにヘッダーがあり、「ログイン」をクリックすると入力モーダルが現れます。",[42,2429],{":src":2430,":width":1656},"'_mix\u002Fsch-2020-12-05-15.07.38-768x389.png'",[42,2432],{":src":2433,":width":1656},"'_mix\u002Fsch-2020-12-05-15.09.41-768x529.png'",[13,2435,2436,2437,2440],{},"この送信をクリックするとnuxt authの関数、",[54,2438,2439],{},"loginwith()","が実行されます。コンポーネントレベルですが以下のコードになっています。",[86,2442,2447],{"className":2443,"code":2444,"filename":2445,"language":2446,"meta":92,"style":92},"language-vue shiki shiki-themes material-theme-ocean","\u003Ctemplate>\n    \u003Cb-modal\n      id=\"login-modal\"\n      ref=\"modal\"\n      title=\"ログイン情報を入力してください。\"\n      ok-only\n      :okTitle=\"'送信'\"\n      @show=\"resetModal\"\n      @hidden=\"resetModal\"\n      @ok=\"handleOk\"\n    >\n      \u003Cform @submit.stop.prevent=\"handleSubmit\">\n        \u003Cb-alert v-if=\"loginErrMes\" show variant=\"danger\">{{loginErrMes}}\u003C\u002Fb-alert>\n        \u003Cb-form-group\n          :state=\"emailState\"\n          label=\"メールアドレス\"\n          type=\"email\"\n          label-for=\"name-input\"\n          invalid-feedback=\"メールアドレスは入力必須です。\"\n        >\n          \u003Cb-form-input\n            id=\"login-email-input\"\n            v-model=\"email\"\n            :state=\"emailState\"\n            required\n          \u002F>\n        \u003C\u002Fb-form-group>\n\n        \u003Cb-form-group\n          :state=\"passState\"\n          label=\"パスワード\"\n          label-for=\"name-input\"\n          invalid-feedback=\"パスワードは入力必須です。\"\n          autocomplete=\"username\"\n        >\n          \u003Cb-form-input\n            id=\"login-password-input\"\n            v-model=\"pass\"\n            type=\"password\"\n            :state=\"passState\"\n            autocomplete=\"current-password\"\n            required\n          \u002F>\n        \u003C\u002Fb-form-group>\n      \u003C\u002Fform>\n    \u003C\u002Fb-modal>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nimport { required, minLength, between } from 'vuelidate\u002Flib\u002Fvalidators'\nexport default {\n    name:'loginModal',\n    data(){\n        return{\n            email:'',\n            pass:'',\n            emailState:null,\n            passState:null,\n            loginErrMes:null,\n        }\n    },\n    validations:{\n      email:{\n        required,\n      },\n      pass:{\n        required\n      },\n    },\n    methods:{\n        checkFormHasError(){\n            this.emailState = !this.$v.email.$invalid;\n            this.passState = !this.$v.pass.$invalid;\n            return this.$v.$invalid;\n        },\n        resetModal(){\n            this.email='';\n            this.pass='';\n            this.emailState=null;\n            this.passState=null;\n            this.loginErrMes=null;\n        },\n        handleOk(bvModalEvt){\n            bvModalEvt.preventDefault()\n            this.handleSubmit();\n        },\n        async handleSubmit() {\n            if(this.checkFormHasError()) return;\n\n            try{\n              await this.$auth.loginWith('local', { data:{\n                email:this.email,\n                password:this.pass\n              }})\n              this.resetModal();\n              this.$store.dispatch('message\u002FsetFlashMessage',{\n                content:'ログインしました。',\n                messageType:'success'\n              })\n              this.$bvModal.hide('login-modal')\n            }catch(error){\n              this.loginErrMes='パスワードまたはメールアドレスが異なります。';\n            }\n        }\n    },\n}\n","component\u002FloginModal","vue",[54,2448,2449,2460,2468,2483,2497,2511,2516,2530,2544,2557,2571,2576,2598,2646,2653,2667,2681,2695,2709,2723,2728,2736,2750,2763,2776,2781,2786,2796,2800,2806,2819,2832,2844,2857,2871,2875,2881,2894,2907,2921,2933,2947,2951,2955,2963,2972,2982,2990,2994,3003,3038,3046,3062,3070,3077,3089,3100,3108,3115,3122,3126,3131,3138,3145,3152,3156,3163,3168,3172,3176,3183,3190,3217,3239,3255,3260,3267,3279,3291,3300,3308,3316,3320,3334,3348,3359,3364,3377,3399,3404,3412,3445,3458,3469,3478,3490,3515,3532,3547,3555,3578,3596,3614,3620,3625,3630],{"__ignoreMap":92},[96,2450,2451,2454,2457],{"class":98,"line":99},[96,2452,2453],{"class":106},"\u003C",[96,2455,2456],{"class":102},"template",[96,2458,2459],{"class":106},">\n",[96,2461,2462,2465],{"class":98,"line":120},[96,2463,2464],{"class":106},"    \u003C",[96,2466,2467],{"class":102},"b-modal\n",[96,2469,2470,2473,2475,2478,2481],{"class":98,"line":132},[96,2471,2472],{"class":2285},"      id",[96,2474,2292],{"class":106},[96,2476,2477],{"class":106},"\"",[96,2479,2480],{"class":113},"login-modal",[96,2482,225],{"class":106},[96,2484,2485,2488,2490,2492,2495],{"class":98,"line":141},[96,2486,2487],{"class":2285},"      ref",[96,2489,2292],{"class":106},[96,2491,2477],{"class":106},[96,2493,2494],{"class":113},"modal",[96,2496,225],{"class":106},[96,2498,2499,2502,2504,2506,2509],{"class":98,"line":152},[96,2500,2501],{"class":2285},"      title",[96,2503,2292],{"class":106},[96,2505,2477],{"class":106},[96,2507,2508],{"class":113},"ログイン情報を入力してください。",[96,2510,225],{"class":106},[96,2512,2513],{"class":98,"line":162},[96,2514,2515],{"class":2285},"      ok-only\n",[96,2517,2518,2521,2523,2525,2528],{"class":98,"line":171},[96,2519,2520],{"class":2285},"      :okTitle",[96,2522,2292],{"class":106},[96,2524,2477],{"class":106},[96,2526,2527],{"class":113},"'送信'",[96,2529,225],{"class":106},[96,2531,2532,2535,2537,2539,2542],{"class":98,"line":181},[96,2533,2534],{"class":2285},"      @show",[96,2536,2292],{"class":106},[96,2538,2477],{"class":106},[96,2540,2541],{"class":113},"resetModal",[96,2543,225],{"class":106},[96,2545,2546,2549,2551,2553,2555],{"class":98,"line":4},[96,2547,2548],{"class":2285},"      @hidden",[96,2550,2292],{"class":106},[96,2552,2477],{"class":106},[96,2554,2541],{"class":113},[96,2556,225],{"class":106},[96,2558,2559,2562,2564,2566,2569],{"class":98,"line":196},[96,2560,2561],{"class":2285},"      @ok",[96,2563,2292],{"class":106},[96,2565,2477],{"class":106},[96,2567,2568],{"class":113},"handleOk",[96,2570,225],{"class":106},[96,2572,2573],{"class":98,"line":204},[96,2574,2575],{"class":106},"    >\n",[96,2577,2578,2581,2584,2587,2589,2591,2594,2596],{"class":98,"line":214},[96,2579,2580],{"class":106},"      \u003C",[96,2582,2583],{"class":102},"form",[96,2585,2586],{"class":2285}," @submit.stop.prevent",[96,2588,2292],{"class":106},[96,2590,2477],{"class":106},[96,2592,2593],{"class":113},"handleSubmit",[96,2595,2477],{"class":106},[96,2597,2459],{"class":106},[96,2599,2600,2603,2606,2609,2611,2613,2616,2618,2621,2624,2626,2628,2631,2633,2636,2639,2642,2644],{"class":98,"line":228},[96,2601,2602],{"class":106},"        \u003C",[96,2604,2605],{"class":102},"b-alert",[96,2607,2608],{"class":2285}," v-if",[96,2610,2292],{"class":106},[96,2612,2477],{"class":106},[96,2614,2615],{"class":113},"loginErrMes",[96,2617,2477],{"class":106},[96,2619,2620],{"class":2285}," show",[96,2622,2623],{"class":2285}," variant",[96,2625,2292],{"class":106},[96,2627,2477],{"class":106},[96,2629,2630],{"class":113},"danger",[96,2632,2477],{"class":106},[96,2634,2635],{"class":106},">",[96,2637,2638],{"class":128},"{{loginErrMes}}",[96,2640,2641],{"class":106},"\u003C\u002F",[96,2643,2605],{"class":102},[96,2645,2459],{"class":106},[96,2647,2648,2650],{"class":98,"line":240},[96,2649,2602],{"class":106},[96,2651,2652],{"class":102},"b-form-group\n",[96,2654,2655,2658,2660,2662,2665],{"class":98,"line":252},[96,2656,2657],{"class":2285},"          :state",[96,2659,2292],{"class":106},[96,2661,2477],{"class":106},[96,2663,2664],{"class":113},"emailState",[96,2666,225],{"class":106},[96,2668,2669,2672,2674,2676,2679],{"class":98,"line":264},[96,2670,2671],{"class":2285},"          label",[96,2673,2292],{"class":106},[96,2675,2477],{"class":106},[96,2677,2678],{"class":113},"メールアドレス",[96,2680,225],{"class":106},[96,2682,2683,2686,2688,2690,2693],{"class":98,"line":275},[96,2684,2685],{"class":2285},"          type",[96,2687,2292],{"class":106},[96,2689,2477],{"class":106},[96,2691,2692],{"class":113},"email",[96,2694,225],{"class":106},[96,2696,2697,2700,2702,2704,2707],{"class":98,"line":283},[96,2698,2699],{"class":2285},"          label-for",[96,2701,2292],{"class":106},[96,2703,2477],{"class":106},[96,2705,2706],{"class":113},"name-input",[96,2708,225],{"class":106},[96,2710,2711,2714,2716,2718,2721],{"class":98,"line":293},[96,2712,2713],{"class":2285},"          invalid-feedback",[96,2715,2292],{"class":106},[96,2717,2477],{"class":106},[96,2719,2720],{"class":113},"メールアドレスは入力必須です。",[96,2722,225],{"class":106},[96,2724,2725],{"class":98,"line":301},[96,2726,2727],{"class":106},"        >\n",[96,2729,2730,2733],{"class":98,"line":312},[96,2731,2732],{"class":106},"          \u003C",[96,2734,2735],{"class":102},"b-form-input\n",[96,2737,2738,2741,2743,2745,2748],{"class":98,"line":323},[96,2739,2740],{"class":2285},"            id",[96,2742,2292],{"class":106},[96,2744,2477],{"class":106},[96,2746,2747],{"class":113},"login-email-input",[96,2749,225],{"class":106},[96,2751,2752,2755,2757,2759,2761],{"class":98,"line":334},[96,2753,2754],{"class":2285},"            v-model",[96,2756,2292],{"class":106},[96,2758,2477],{"class":106},[96,2760,2692],{"class":113},[96,2762,225],{"class":106},[96,2764,2765,2768,2770,2772,2774],{"class":98,"line":345},[96,2766,2767],{"class":2285},"            :state",[96,2769,2292],{"class":106},[96,2771,2477],{"class":106},[96,2773,2664],{"class":113},[96,2775,225],{"class":106},[96,2777,2778],{"class":98,"line":354},[96,2779,2780],{"class":2285},"            required\n",[96,2782,2783],{"class":98,"line":366},[96,2784,2785],{"class":106},"          \u002F>\n",[96,2787,2788,2791,2794],{"class":98,"line":375},[96,2789,2790],{"class":106},"        \u003C\u002F",[96,2792,2793],{"class":102},"b-form-group",[96,2795,2459],{"class":106},[96,2797,2798],{"class":98,"line":383},[96,2799,622],{"emptyLinePlaceholder":621},[96,2801,2802,2804],{"class":98,"line":393},[96,2803,2602],{"class":106},[96,2805,2652],{"class":102},[96,2807,2808,2810,2812,2814,2817],{"class":98,"line":875},[96,2809,2657],{"class":2285},[96,2811,2292],{"class":106},[96,2813,2477],{"class":106},[96,2815,2816],{"class":113},"passState",[96,2818,225],{"class":106},[96,2820,2821,2823,2825,2827,2830],{"class":98,"line":880},[96,2822,2671],{"class":2285},[96,2824,2292],{"class":106},[96,2826,2477],{"class":106},[96,2828,2829],{"class":113},"パスワード",[96,2831,225],{"class":106},[96,2833,2834,2836,2838,2840,2842],{"class":98,"line":885},[96,2835,2699],{"class":2285},[96,2837,2292],{"class":106},[96,2839,2477],{"class":106},[96,2841,2706],{"class":113},[96,2843,225],{"class":106},[96,2845,2846,2848,2850,2852,2855],{"class":98,"line":890},[96,2847,2713],{"class":2285},[96,2849,2292],{"class":106},[96,2851,2477],{"class":106},[96,2853,2854],{"class":113},"パスワードは入力必須です。",[96,2856,225],{"class":106},[96,2858,2859,2862,2864,2866,2869],{"class":98,"line":896},[96,2860,2861],{"class":2285},"          autocomplete",[96,2863,2292],{"class":106},[96,2865,2477],{"class":106},[96,2867,2868],{"class":113},"username",[96,2870,225],{"class":106},[96,2872,2873],{"class":98,"line":901},[96,2874,2727],{"class":106},[96,2876,2877,2879],{"class":98,"line":906},[96,2878,2732],{"class":106},[96,2880,2735],{"class":102},[96,2882,2883,2885,2887,2889,2892],{"class":98,"line":911},[96,2884,2740],{"class":2285},[96,2886,2292],{"class":106},[96,2888,2477],{"class":106},[96,2890,2891],{"class":113},"login-password-input",[96,2893,225],{"class":106},[96,2895,2896,2898,2900,2902,2905],{"class":98,"line":917},[96,2897,2754],{"class":2285},[96,2899,2292],{"class":106},[96,2901,2477],{"class":106},[96,2903,2904],{"class":113},"pass",[96,2906,225],{"class":106},[96,2908,2909,2912,2914,2916,2919],{"class":98,"line":923},[96,2910,2911],{"class":2285},"            type",[96,2913,2292],{"class":106},[96,2915,2477],{"class":106},[96,2917,2918],{"class":113},"password",[96,2920,225],{"class":106},[96,2922,2923,2925,2927,2929,2931],{"class":98,"line":928},[96,2924,2767],{"class":2285},[96,2926,2292],{"class":106},[96,2928,2477],{"class":106},[96,2930,2816],{"class":113},[96,2932,225],{"class":106},[96,2934,2935,2938,2940,2942,2945],{"class":98,"line":933},[96,2936,2937],{"class":2285},"            autocomplete",[96,2939,2292],{"class":106},[96,2941,2477],{"class":106},[96,2943,2944],{"class":113},"current-password",[96,2946,225],{"class":106},[96,2948,2949],{"class":98,"line":939},[96,2950,2780],{"class":2285},[96,2952,2953],{"class":98,"line":945},[96,2954,2785],{"class":106},[96,2956,2957,2959,2961],{"class":98,"line":951},[96,2958,2790],{"class":106},[96,2960,2793],{"class":102},[96,2962,2459],{"class":106},[96,2964,2965,2968,2970],{"class":98,"line":957},[96,2966,2967],{"class":106},"      \u003C\u002F",[96,2969,2583],{"class":102},[96,2971,2459],{"class":106},[96,2973,2974,2977,2980],{"class":98,"line":962},[96,2975,2976],{"class":106},"    \u003C\u002F",[96,2978,2979],{"class":102},"b-modal",[96,2981,2459],{"class":106},[96,2983,2984,2986,2988],{"class":98,"line":968},[96,2985,2641],{"class":106},[96,2987,2456],{"class":102},[96,2989,2459],{"class":106},[96,2991,2992],{"class":98,"line":973},[96,2993,622],{"emptyLinePlaceholder":621},[96,2995,2996,2998,3001],{"class":98,"line":979},[96,2997,2453],{"class":106},[96,2999,3000],{"class":102},"script",[96,3002,2459],{"class":106},[96,3004,3005,3008,3011,3014,3017,3020,3022,3025,3028,3031,3033,3036],{"class":98,"line":984},[96,3006,3007],{"class":2331},"import",[96,3009,3010],{"class":106}," {",[96,3012,3013],{"class":128}," required",[96,3015,3016],{"class":106},",",[96,3018,3019],{"class":128}," minLength",[96,3021,3016],{"class":106},[96,3023,3024],{"class":128}," between",[96,3026,3027],{"class":106}," }",[96,3029,3030],{"class":2331}," from",[96,3032,110],{"class":106},[96,3034,3035],{"class":113},"vuelidate\u002Flib\u002Fvalidators",[96,3037,117],{"class":106},[96,3039,3040,3042,3044],{"class":98,"line":1460},[96,3041,2332],{"class":2331},[96,3043,2335],{"class":2331},[96,3045,2146],{"class":106},[96,3047,3048,3051,3053,3055,3058,3060],{"class":98,"line":1466},[96,3049,3050],{"class":102},"    name",[96,3052,107],{"class":106},[96,3054,1964],{"class":106},[96,3056,3057],{"class":113},"loginModal",[96,3059,1964],{"class":106},[96,3061,1940],{"class":106},[96,3063,3064,3067],{"class":98,"line":1471},[96,3065,3066],{"class":102},"    data",[96,3068,3069],{"class":106},"(){\n",[96,3071,3072,3075],{"class":98,"line":1477},[96,3073,3074],{"class":2331},"        return",[96,3076,788],{"class":106},[96,3078,3079,3082,3084,3087],{"class":98,"line":1482},[96,3080,3081],{"class":102},"            email",[96,3083,107],{"class":106},[96,3085,3086],{"class":106},"''",[96,3088,1940],{"class":106},[96,3090,3091,3094,3096,3098],{"class":98,"line":1488},[96,3092,3093],{"class":102},"            pass",[96,3095,107],{"class":106},[96,3097,3086],{"class":106},[96,3099,1940],{"class":106},[96,3101,3102,3105],{"class":98,"line":1493},[96,3103,3104],{"class":102},"            emailState",[96,3106,3107],{"class":106},":null,\n",[96,3109,3110,3113],{"class":98,"line":1498},[96,3111,3112],{"class":102},"            passState",[96,3114,3107],{"class":106},[96,3116,3117,3120],{"class":98,"line":1503},[96,3118,3119],{"class":102},"            loginErrMes",[96,3121,3107],{"class":106},[96,3123,3124],{"class":98,"line":1509},[96,3125,1376],{"class":106},[96,3127,3128],{"class":98,"line":1514},[96,3129,3130],{"class":106},"    },\n",[96,3132,3133,3136],{"class":98,"line":1519},[96,3134,3135],{"class":102},"    validations",[96,3137,1927],{"class":106},[96,3139,3140,3143],{"class":98,"line":1524},[96,3141,3142],{"class":102},"      email",[96,3144,1927],{"class":106},[96,3146,3147,3150],{"class":98,"line":1530},[96,3148,3149],{"class":128},"        required",[96,3151,1940],{"class":106},[96,3153,3154],{"class":98,"line":1535},[96,3155,2136],{"class":106},[96,3157,3158,3161],{"class":98,"line":1541},[96,3159,3160],{"class":102},"      pass",[96,3162,1927],{"class":106},[96,3164,3165],{"class":98,"line":1546},[96,3166,3167],{"class":128},"        required\n",[96,3169,3170],{"class":98,"line":1551},[96,3171,2136],{"class":106},[96,3173,3174],{"class":98,"line":1556},[96,3175,3130],{"class":106},[96,3177,3178,3181],{"class":98,"line":1562},[96,3179,3180],{"class":102},"    methods",[96,3182,1927],{"class":106},[96,3184,3185,3188],{"class":98,"line":1567},[96,3186,3187],{"class":102},"        checkFormHasError",[96,3189,3069],{"class":106},[96,3191,3192,3195,3197,3200,3203,3206,3208,3210,3212,3215],{"class":98,"line":1573},[96,3193,3194],{"class":106},"            this.",[96,3196,2664],{"class":128},[96,3198,3199],{"class":106}," =",[96,3201,3202],{"class":106}," !this.",[96,3204,3205],{"class":128},"$v",[96,3207,2312],{"class":106},[96,3209,2692],{"class":128},[96,3211,2312],{"class":106},[96,3213,3214],{"class":128},"$invalid",[96,3216,2326],{"class":106},[96,3218,3219,3221,3223,3225,3227,3229,3231,3233,3235,3237],{"class":98,"line":1578},[96,3220,3194],{"class":106},[96,3222,2816],{"class":128},[96,3224,3199],{"class":106},[96,3226,3202],{"class":106},[96,3228,3205],{"class":128},[96,3230,2312],{"class":106},[96,3232,2904],{"class":128},[96,3234,2312],{"class":106},[96,3236,3214],{"class":128},[96,3238,2326],{"class":106},[96,3240,3241,3244,3247,3249,3251,3253],{"class":98,"line":1583},[96,3242,3243],{"class":2331},"            return",[96,3245,3246],{"class":106}," this.",[96,3248,3205],{"class":128},[96,3250,2312],{"class":106},[96,3252,3214],{"class":128},[96,3254,2326],{"class":106},[96,3256,3257],{"class":98,"line":1588},[96,3258,3259],{"class":106},"        },\n",[96,3261,3262,3265],{"class":98,"line":1594},[96,3263,3264],{"class":102},"        resetModal",[96,3266,3069],{"class":106},[96,3268,3269,3271,3273,3275,3277],{"class":98,"line":1599},[96,3270,3194],{"class":106},[96,3272,2692],{"class":128},[96,3274,2292],{"class":106},[96,3276,3086],{"class":106},[96,3278,2326],{"class":106},[96,3280,3281,3283,3285,3287,3289],{"class":98,"line":1605},[96,3282,3194],{"class":106},[96,3284,2904],{"class":128},[96,3286,2292],{"class":106},[96,3288,3086],{"class":106},[96,3290,2326],{"class":106},[96,3292,3293,3295,3297],{"class":98,"line":1611},[96,3294,3194],{"class":106},[96,3296,2664],{"class":128},[96,3298,3299],{"class":106},"=null;\n",[96,3301,3302,3304,3306],{"class":98,"line":1617},[96,3303,3194],{"class":106},[96,3305,2816],{"class":128},[96,3307,3299],{"class":106},[96,3309,3310,3312,3314],{"class":98,"line":1623},[96,3311,3194],{"class":106},[96,3313,2615],{"class":128},[96,3315,3299],{"class":106},[96,3317,3318],{"class":98,"line":1629},[96,3319,3259],{"class":106},[96,3321,3322,3325,3327,3331],{"class":98,"line":1634},[96,3323,3324],{"class":102},"        handleOk",[96,3326,2299],{"class":106},[96,3328,3330],{"class":3329},"s7ZW3","bvModalEvt",[96,3332,3333],{"class":106},"){\n",[96,3335,3337,3340,3342,3345],{"class":98,"line":3336},84,[96,3338,3339],{"class":128},"            bvModalEvt",[96,3341,2312],{"class":106},[96,3343,3344],{"class":2295},"preventDefault",[96,3346,3347],{"class":102},"()\n",[96,3349,3351,3353,3355,3357],{"class":98,"line":3350},85,[96,3352,3194],{"class":106},[96,3354,2593],{"class":2295},[96,3356,2318],{"class":102},[96,3358,2326],{"class":106},[96,3360,3362],{"class":98,"line":3361},86,[96,3363,3259],{"class":106},[96,3365,3367,3370,3373,3375],{"class":98,"line":3366},87,[96,3368,3369],{"class":2285},"        async",[96,3371,3372],{"class":102}," handleSubmit",[96,3374,2318],{"class":106},[96,3376,2146],{"class":106},[96,3378,3380,3383,3385,3388,3391,3394,3397],{"class":98,"line":3379},88,[96,3381,3382],{"class":2331},"            if",[96,3384,2299],{"class":102},[96,3386,3387],{"class":106},"this.",[96,3389,3390],{"class":2295},"checkFormHasError",[96,3392,3393],{"class":102},"()) ",[96,3395,3396],{"class":2331},"return",[96,3398,2326],{"class":106},[96,3400,3402],{"class":98,"line":3401},89,[96,3403,622],{"emptyLinePlaceholder":621},[96,3405,3407,3410],{"class":98,"line":3406},90,[96,3408,3409],{"class":2331},"            try",[96,3411,788],{"class":106},[96,3413,3415,3418,3420,3423,3425,3428,3430,3432,3434,3436,3438,3440,3443],{"class":98,"line":3414},91,[96,3416,3417],{"class":2331},"              await",[96,3419,3246],{"class":106},[96,3421,3422],{"class":128},"$auth",[96,3424,2312],{"class":106},[96,3426,3427],{"class":2295},"loginWith",[96,3429,2299],{"class":102},[96,3431,1964],{"class":106},[96,3433,2240],{"class":113},[96,3435,1964],{"class":106},[96,3437,3016],{"class":106},[96,3439,3010],{"class":106},[96,3441,3442],{"class":102}," data",[96,3444,1927],{"class":106},[96,3446,3448,3451,3454,3456],{"class":98,"line":3447},92,[96,3449,3450],{"class":102},"                email",[96,3452,3453],{"class":106},":this.",[96,3455,2692],{"class":128},[96,3457,1940],{"class":106},[96,3459,3461,3464,3466],{"class":98,"line":3460},93,[96,3462,3463],{"class":102},"                password",[96,3465,3453],{"class":106},[96,3467,3468],{"class":128},"pass\n",[96,3470,3472,3475],{"class":98,"line":3471},94,[96,3473,3474],{"class":106},"              }}",[96,3476,3477],{"class":102},")\n",[96,3479,3481,3484,3486,3488],{"class":98,"line":3480},95,[96,3482,3483],{"class":106},"              this.",[96,3485,2541],{"class":2295},[96,3487,2318],{"class":102},[96,3489,2326],{"class":106},[96,3491,3493,3495,3498,3500,3503,3505,3507,3510,3512],{"class":98,"line":3492},96,[96,3494,3483],{"class":106},[96,3496,3497],{"class":128},"$store",[96,3499,2312],{"class":106},[96,3501,3502],{"class":2295},"dispatch",[96,3504,2299],{"class":102},[96,3506,1964],{"class":106},[96,3508,3509],{"class":113},"message\u002FsetFlashMessage",[96,3511,1964],{"class":106},[96,3513,3514],{"class":106},",{\n",[96,3516,3518,3521,3523,3525,3528,3530],{"class":98,"line":3517},97,[96,3519,3520],{"class":102},"                content",[96,3522,107],{"class":106},[96,3524,1964],{"class":106},[96,3526,3527],{"class":113},"ログインしました。",[96,3529,1964],{"class":106},[96,3531,1940],{"class":106},[96,3533,3535,3538,3540,3542,3545],{"class":98,"line":3534},98,[96,3536,3537],{"class":102},"                messageType",[96,3539,107],{"class":106},[96,3541,1964],{"class":106},[96,3543,3544],{"class":113},"success",[96,3546,117],{"class":106},[96,3548,3550,3553],{"class":98,"line":3549},99,[96,3551,3552],{"class":106},"              }",[96,3554,3477],{"class":102},[96,3556,3558,3560,3563,3565,3568,3570,3572,3574,3576],{"class":98,"line":3557},100,[96,3559,3483],{"class":106},[96,3561,3562],{"class":128},"$bvModal",[96,3564,2312],{"class":106},[96,3566,3567],{"class":2295},"hide",[96,3569,2299],{"class":102},[96,3571,1964],{"class":106},[96,3573,2480],{"class":113},[96,3575,1964],{"class":106},[96,3577,3477],{"class":102},[96,3579,3581,3584,3587,3589,3592,3594],{"class":98,"line":3580},101,[96,3582,3583],{"class":106},"            }",[96,3585,3586],{"class":2331},"catch",[96,3588,2299],{"class":102},[96,3590,3591],{"class":128},"error",[96,3593,2309],{"class":102},[96,3595,788],{"class":106},[96,3597,3599,3601,3603,3605,3607,3610,3612],{"class":98,"line":3598},102,[96,3600,3483],{"class":106},[96,3602,2615],{"class":128},[96,3604,2292],{"class":106},[96,3606,1964],{"class":106},[96,3608,3609],{"class":113},"パスワードまたはメールアドレスが異なります。",[96,3611,1964],{"class":106},[96,3613,2326],{"class":106},[96,3615,3617],{"class":98,"line":3616},103,[96,3618,3619],{"class":106},"            }\n",[96,3621,3623],{"class":98,"line":3622},104,[96,3624,1376],{"class":106},[96,3626,3628],{"class":98,"line":3627},105,[96,3629,3130],{"class":106},[96,3631,3633],{"class":98,"line":3632},106,[96,3634,987],{"class":106},[13,3636,3637,3638,3641,3642,3645],{},"このアプリでは後でいろいろフォームとかある予定なので",[54,3639,3640],{},"vuelidate","というバリデーションライブラリを入れています。このログインフォーム程度であれば必要ありませんけど。ログイン処理をしているのは下の方にある",[54,3643,3644],{},"async handleSubmit()","です。入力値が正規値であれば実行されます。",[13,3647,3648,3649,3651],{},"nuxt authは",[54,3650,1891],{},"で定義した設定を元にログインのルートにデータをPOSTします。超便利です。",[42,3653],{":src":3654,":width":1656},"'_mix\u002Fsch-2020-12-05-15.14.45-768x518.png'",[13,3656,3657],{},"先ほどテストで入れたようにシーダーのアドレスとパスワードを入れて送信します。",[42,3659],{":src":3660,":width":1656},"'_mix\u002Fsch-2020-12-05-15.17.31-768x81.png'",[13,3662,3663],{},"ネットワークを見てみるとキチンと8000ポートで待機しているlaravelへ送信されています。レスポンスが200で成功しています。クッキーを見てみるとトークンが保存されているのが分かります。",[42,3665],{":src":3666,":width":1656},"'_mix\u002Fsch-2020-12-05-15.16.35-768x71.png'",[13,3668,3669],{},"そしてユーザー情報があるか＝ログインしているかで「ログイン」を「ログアウト」を制御しています。",[42,3671],{":src":3672,":width":1656},"'_mix\u002Fsch-2020-12-05-15.22.55-768x168.png'",[13,3674,3675],{},"authモジュールを入れている場合は以下のコードでログインしているか、ユーザーの情報を取得することができます。",[86,3677,3679],{"className":1910,"code":3678,"language":1912,"meta":92,"style":92},"this.$auth.user\nthis.$auth.loggedIn\n",[54,3680,3681,3692],{"__ignoreMap":92},[96,3682,3683,3685,3687,3689],{"class":98,"line":99},[96,3684,3387],{"class":106},[96,3686,3422],{"class":128},[96,3688,2312],{"class":106},[96,3690,3691],{"class":128},"user\n",[96,3693,3694,3696,3698,3700],{"class":98,"line":120},[96,3695,3387],{"class":106},[96,3697,3422],{"class":128},[96,3699,2312],{"class":106},[96,3701,3702],{"class":128},"loggedIn\n",[34,3704,3706],{"id":3705},"nuxtのspaをビルドする","NuxtのSPAをビルドする",[13,3708,3709],{},"Nuxtは3000ポートの開発サーバで見ていたので、今度はキチンとビルドしてユーザー視点でアクセスしてみましょう。",[86,3711,3714],{"className":3712,"code":3713,"language":460},[457],"npm run build\n",[54,3715,3713],{"__ignoreMap":92},[13,3717,3718,3719,3721,3722,3724],{},"distファイルが出されたのを確認し",[54,3720,407],{},"へアクセスします。私の環境ではホストの",[54,3723,407],{},"はコンテナのlocalhost:80につながります。最初の設定の通り、ドキュメントルート はdist配下に通じているのでindex.htmlが返されます。ログインが成功し、ネットワークでもAPIが送信されているのが分かります。",[42,3726],{":src":3727,":width":1656},"'_mix\u002Fsch-2020-12-05-15.34.55-768x213.png'",[13,3729,3730],{},"後はどんどんAPIルートを作成して、nuxtからはaxiosを用いてトークン付きリクエストを送れば認証ルートにアクセスすることができます。これでJWT認証つきのSPAアプリの設定が完了しました。",[34,3732,3734],{"id":3733},"以上","以上！",[13,3736,3737],{},"以上がLravel6とNuxt.jsで構築するJWT認証つきSPAの構築です。サーバーの構成は人によって様々ですがバーチャルホスト で公開側ページとAPIを分けてしまうのが簡単な気がします。とにかくトークン認証を用いたSPAを構築する際には",[3739,3740,3741,3745,3748,3751,3754,3757,3760,3763],"ul",{},[3742,3743,3744],"li",{},"Nuxt側とAPI側でサーバーを分ける",[3742,3746,3747],{},"laravelにJWT認証ライブラリを入れる",[3742,3749,3750],{},"ログイン用ルートを整える",[3742,3752,3753],{},"CORSの設定を行う",[3742,3755,3756],{},"NuxtからのログインルートにPOSTリクエストを送る",[3742,3758,3759],{},"トークンをブラウザのクッキーなどに保存",[3742,3761,3762],{},"認証ルートにはリクエストヘッダにBeare＋トークンでリクエストをする",[3742,3764,3765],{},"おのつど、またはページがリロードされたら \u002Fapi\u002Fauth\u002Fmeでトークンが有効かを確かめてNuxt側に認証状態を知らせる。",[13,3767,3768],{},"以上のまとめを意識すればおおよそのNuxt+API認証はわかってくると思います。ReactとかNext.jsなどもこの部分のエッセンスは同じなのでぜひ応用してください。",[34,3770,3771],{"id":3771},"参考記事",[3739,3773,3774,3781,3788],{},[3742,3775,3776],{},[76,3777,3780],{"href":3778,"rel":3779},"https:\u002F\u002Fpgmemo.tokyo\u002Fdata\u002Farchives\u002F1703.html",[584],"Laravel6 に jwt-auth をインストールしSPAからログインする（バックエンド Laravel編）",[3742,3782,3783],{},[76,3784,3787],{"href":3785,"rel":3786},"https:\u002F\u002Fgithub.com\u002Ftymondesigns\u002Fjwt-auth\u002Fissues\u002F2059",[584],"Tymon\\JWTAuth\\Exceptions\\JWTException: Could not create token: Implicit conversion of keys from strings is deprecated. Please use InMemory or LocalFileReference classes. ",[3742,3789,3790],{},[76,3791,3793],{"href":1867,"rel":3792},[584],"Nuxt auth introduction",[3795,3796,3797],"style",{},"html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}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 pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}",{"title":92,"searchDepth":132,"depth":132,"links":3799},[3800,3805,3809,3818,3819,3832,3833,3834],{"id":36,"depth":120,"text":37,"children":3801},[3802,3803,3804],{"id":72,"depth":132,"text":72},{"id":449,"depth":132,"text":450},{"id":484,"depth":132,"text":485},{"id":491,"depth":120,"text":492,"children":3806},[3807,3808],{"id":495,"depth":132,"text":495},{"id":517,"depth":132,"text":518},{"id":544,"depth":120,"text":545,"children":3810},[3811,3812,3813,3814,3815,3816,3817],{"id":559,"depth":132,"text":560},{"id":589,"depth":132,"text":589},{"id":702,"depth":132,"text":702},{"id":729,"depth":132,"text":730},{"id":990,"depth":132,"text":991},{"id":1148,"depth":132,"text":1149},{"id":1213,"depth":132,"text":1213},{"id":1642,"depth":120,"text":1643},{"id":1682,"depth":120,"text":1683,"children":3820},[3821,3826,3831],{"id":1686,"depth":132,"text":1686,"children":3822},[3823,3824,3825],{"id":1693,"depth":141,"text":1694},{"id":1703,"depth":141,"text":1704},{"id":1733,"depth":141,"text":1734},{"id":1860,"depth":132,"text":1861,"children":3827},[3828,3829,3830],{"id":1873,"depth":141,"text":1873},{"id":1885,"depth":141,"text":1886},{"id":2272,"depth":141,"text":2272},{"id":2424,"depth":132,"text":2424},{"id":3705,"depth":120,"text":3706},{"id":3733,"depth":120,"text":3734},{"id":3771,"depth":120,"text":3771},[3836],"devstack","2025-09-05","md",null,{},"\u002Farticles\u002Flaravel-nuxt-jwt-spa",{"title":8,"description":8},"articles\u002Flaravel-nuxt-jwt-spa",[438,3845],"nuxt","_mix\u002Flaravel-nuxt.jpeg","PoGyyswEs82YuxYkbUxf6PSHnouWa2M9g-hxfKYHTNE",{"id":3849,"title":3850,"body":3851,"category":4894,"createdAt":4895,"description":4896,"extension":3838,"index":3839,"meta":4897,"navigation":621,"path":4898,"publish":621,"seo":4899,"series":3839,"seriesTitle":3839,"stem":4900,"tag":4901,"thumbnail":4903,"updatedAt":3839,"__hash__":4904},"articles\u002Farticles\u002Fnuxt-auth-middleware.md","Nuxt.jsのSSR・SPA時のフロント側の認証ビューを自前で実装する",{"type":10,"value":3852,"toc":4882},[3853,3856,3859,3862,3873,3876,3879,3882,3885,3888,3891,3894,3906,3910,3913,3958,3961,4021,4024,4031,4112,4115,4118,4220,4226,4259,4263,4268,4275,4372,4482,4486,4493,4497,4506,4509,4685,4688,4692,4699,4859,4862,4865,4868,4879],[13,3854,3855],{},"こんにちはjunです。webアプリを作成する時は大体、認証機能をつけることが多いです。LaravelやDjangoなどでは一発で行けますが、Nuxt.js、Vue.jsを使用したSPA、Node.jsでのSSRコンテンツを作成する場合は一捻り必要です。",[13,3857,3858],{},"Nuxt.jsなどで作成するアプリはバックエンドと独立し、都度バックエンドへのAPI通信の際にトークンを渡したりすることで認証が必要なAPIへアクセスしています。",[13,3860,3861],{},"ビュー側の処理でも",[3739,3863,3864,3867,3870],{},[3742,3865,3866],{},"ログインしているユーザーはこのように表示",[3742,3868,3869],{},"このルートはログインしているユーザーのみアクセス可能",[3742,3871,3872],{},"未ログインユーザーはログイン画面移動",[13,3874,3875],{},"といった処理が行われることが多いです。この記事では上記のような認証を用いたビューの表現、ルーターの設定をNuxt.jsでどう行うかの解説をしたいと思います。SPA（クライアントサイドレンダリング）とSSR（サーバーサイドレンダリング）の２パターンを実装します。ちなみにnuxt-authなどのライブラリは使用しません。",[13,3877,3878],{},"また、ログインメソッドの処理やリクエストヘッダにどうこうするとか、バックエンド側の処理については今回は解説しません。あくまでフロント側（Nuxt.js側）の表示やロジックに認証が必要な場合にどう実装するかについて解説するのみです。",[34,3880,3881],{"id":3881},"大まかな処理",[13,3883,3884],{},"まずこの実装を行うにあたりJWTやクッキーなどなんらかの認証トークンが取得できていること、またそれらの値を使用できることを前提として進めます。",[13,3886,3887],{},"LaravelやDjangoなどではリクエストを送る際にヘッダーにセッションIDを含むクッキーを送信することで、サーバー側でログインによるビューやロジックの分岐を行っています。",[13,3889,3890],{},"しかしNuxt.jsでSPAの場合はコンテンツをクライアント側で生成し、またSSRの場合はnode.jsのサーバーで行われます。すなわちセッションやユーザー情報を保存しているAPIサーバーから「どうにかしてNuxt.js側にユーザー情報を渡す」必要があります。",[13,3892,3893],{},"これから実装する内容はSSR、SPAどちらも以下の通りです。",[3895,3896,3897,3900,3903],"ol",{},[3742,3898,3899],{},"Nuxt.js側でユーザーの情報を保存する。",[3742,3901,3902],{},"ログインの是非はユーザー情報の有無で判断する。",[3742,3904,3905],{},"何かしらのタイミングでユーザー情報を取得するAPIを都度発行する。",[34,3907,3909],{"id":3908},"storeの調整","Storeの調整",[13,3911,3912],{},"ではまずStoreにて以下のようにuserステートを作成します。",[86,3914,3917],{"className":1910,"code":3915,"filename":3916,"language":1912,"meta":92,"style":92},"export const state = () => ({\n    user:null,\n});\n","store\u002Findex.js",[54,3918,3919,3942,3949],{"__ignoreMap":92},[96,3920,3921,3923,3926,3929,3931,3934,3937,3940],{"class":98,"line":99},[96,3922,2332],{"class":2331},[96,3924,3925],{"class":2285}," const",[96,3927,3928],{"class":128}," state ",[96,3930,2292],{"class":106},[96,3932,3933],{"class":106}," ()",[96,3935,3936],{"class":2285}," =>",[96,3938,3939],{"class":128}," (",[96,3941,788],{"class":106},[96,3943,3944,3947],{"class":98,"line":120},[96,3945,3946],{"class":102},"    user",[96,3948,3107],{"class":106},[96,3950,3951,3954,3956],{"class":98,"line":132},[96,3952,3953],{"class":106},"}",[96,3955,2309],{"class":128},[96,3957,2326],{"class":106},[13,3959,3960],{},"デフォルトではnullにしておきます。このuserステートがnullでなく、ユーザー情報のオブジェクトである場合をログイン状態とします。mutationなどでこのステートに値をセットできるように作っておきます。",[86,3962,3964],{"className":1910,"code":3963,"filename":3916,"language":1912,"meta":92,"style":92},"export const mutations = {\n    setUser(state,{user}){\n            state.user = user;\n    }\n}\n",[54,3965,3966,3979,3997,4013,4017],{"__ignoreMap":92},[96,3967,3968,3970,3972,3975,3977],{"class":98,"line":99},[96,3969,2332],{"class":2331},[96,3971,3925],{"class":2285},[96,3973,3974],{"class":128}," mutations ",[96,3976,2292],{"class":106},[96,3978,2146],{"class":106},[96,3980,3981,3984,3986,3989,3992,3994],{"class":98,"line":120},[96,3982,3983],{"class":102},"    setUser",[96,3985,2299],{"class":106},[96,3987,3988],{"class":3329},"state",[96,3990,3991],{"class":106},",{",[96,3993,2268],{"class":3329},[96,3995,3996],{"class":106},"}){\n",[96,3998,3999,4002,4004,4006,4008,4011],{"class":98,"line":132},[96,4000,4001],{"class":128},"            state",[96,4003,2312],{"class":106},[96,4005,2268],{"class":128},[96,4007,3199],{"class":106},[96,4009,4010],{"class":128}," user",[96,4012,2326],{"class":106},[96,4014,4015],{"class":98,"line":141},[96,4016,954],{"class":106},[96,4018,4019],{"class":98,"line":152},[96,4020,987],{"class":106},[34,4022,4023],{"id":4023},"ミドルェアの作成",[13,4025,4026,4027,4030],{},"次に「ログインしたユーザーのみがアクセス可能なページ」を実装できるようにミドルウェアを実装します。",[54,4028,4029],{},"middleware\u002Fauth.js","を作成します。",[86,4032,4034],{"className":1910,"code":4033,"filename":4029,"language":1912,"meta":92,"style":92},"export default function ({ store, redirect }) {\n    if (!store.state.user) {\n        redirect('\u002Flogin');\n    }\n}\n",[54,4035,4036,4061,4087,4104,4108],{"__ignoreMap":92},[96,4037,4038,4040,4042,4045,4048,4051,4053,4056,4059],{"class":98,"line":99},[96,4039,2332],{"class":2331},[96,4041,2335],{"class":2331},[96,4043,4044],{"class":2285}," function",[96,4046,4047],{"class":106}," ({",[96,4049,4050],{"class":3329}," store",[96,4052,3016],{"class":106},[96,4054,4055],{"class":3329}," redirect",[96,4057,4058],{"class":106}," })",[96,4060,2146],{"class":106},[96,4062,4063,4066,4068,4071,4074,4076,4078,4080,4082,4085],{"class":98,"line":120},[96,4064,4065],{"class":2331},"    if",[96,4067,3939],{"class":102},[96,4069,4070],{"class":106},"!",[96,4072,4073],{"class":128},"store",[96,4075,2312],{"class":106},[96,4077,3988],{"class":128},[96,4079,2312],{"class":106},[96,4081,2268],{"class":128},[96,4083,4084],{"class":102},") ",[96,4086,788],{"class":106},[96,4088,4089,4092,4094,4096,4098,4100,4102],{"class":98,"line":132},[96,4090,4091],{"class":2295},"        redirect",[96,4093,2299],{"class":102},[96,4095,1964],{"class":106},[96,4097,2158],{"class":113},[96,4099,1964],{"class":106},[96,4101,2309],{"class":102},[96,4103,2326],{"class":106},[96,4105,4106],{"class":98,"line":141},[96,4107,954],{"class":106},[96,4109,4110],{"class":98,"line":152},[96,4111,987],{"class":106},[13,4113,4114],{},"単純にStoreのUserステートがnullかどうかでログインページに飛ばすようにしています。",[13,4116,4117],{},"ページコンポーネントでは以下のようにしてミドルウェアを有効にします。",[86,4119,4122],{"className":2443,"code":4120,"filename":4121,"language":2446,"meta":92,"style":92},"\u003Ctemplate>\n    \u003Cdiv>\n        auth\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\u003Cscript>\nexport default {\n    name:\"home\",\n    middleware:\"auth\",\n}\n\u003C\u002Fscript>\n","pages\u002Fauth\u002Findex.vue",[54,4123,4124,4132,4141,4146,4154,4162,4170,4178,4193,4208,4212],{"__ignoreMap":92},[96,4125,4126,4128,4130],{"class":98,"line":99},[96,4127,2453],{"class":106},[96,4129,2456],{"class":102},[96,4131,2459],{"class":106},[96,4133,4134,4136,4139],{"class":98,"line":120},[96,4135,2464],{"class":106},[96,4137,4138],{"class":102},"div",[96,4140,2459],{"class":106},[96,4142,4143],{"class":98,"line":132},[96,4144,4145],{"class":128},"        auth\n",[96,4147,4148,4150,4152],{"class":98,"line":141},[96,4149,2976],{"class":106},[96,4151,4138],{"class":102},[96,4153,2459],{"class":106},[96,4155,4156,4158,4160],{"class":98,"line":152},[96,4157,2641],{"class":106},[96,4159,2456],{"class":102},[96,4161,2459],{"class":106},[96,4163,4164,4166,4168],{"class":98,"line":162},[96,4165,2453],{"class":106},[96,4167,3000],{"class":102},[96,4169,2459],{"class":106},[96,4171,4172,4174,4176],{"class":98,"line":171},[96,4173,2332],{"class":2331},[96,4175,2335],{"class":2331},[96,4177,2146],{"class":106},[96,4179,4180,4182,4184,4186,4189,4191],{"class":98,"line":181},[96,4181,3050],{"class":102},[96,4183,107],{"class":106},[96,4185,2477],{"class":106},[96,4187,4188],{"class":113},"home",[96,4190,2477],{"class":106},[96,4192,1940],{"class":106},[96,4194,4195,4198,4200,4202,4204,4206],{"class":98,"line":4},[96,4196,4197],{"class":102},"    middleware",[96,4199,107],{"class":106},[96,4201,2477],{"class":106},[96,4203,1924],{"class":113},[96,4205,2477],{"class":106},[96,4207,1940],{"class":106},[96,4209,4210],{"class":98,"line":196},[96,4211,987],{"class":106},[96,4213,4214,4216,4218],{"class":98,"line":204},[96,4215,2641],{"class":106},[96,4217,3000],{"class":102},[96,4219,2459],{"class":106},[13,4221,4222,4223,4225],{},"こうすることでログインが必要なページを実装することができます。または",[54,4224,1891],{}," にて以下のように設定することで全てのページに認証ミドルウェアを適用できます。",[86,4227,4229],{"className":1910,"code":4228,"filename":1891,"language":1912,"meta":92,"style":92},"router: {\n    middleware: 'auth',\n},\n",[54,4230,4231,4240,4254],{"__ignoreMap":92},[96,4232,4233,4236,4238],{"class":98,"line":99},[96,4234,4235],{"class":1923},"router",[96,4237,107],{"class":106},[96,4239,2146],{"class":106},[96,4241,4242,4244,4246,4248,4250,4252],{"class":98,"line":120},[96,4243,4197],{"class":1923},[96,4245,107],{"class":106},[96,4247,110],{"class":106},[96,4249,1924],{"class":113},[96,4251,1964],{"class":106},[96,4253,1940],{"class":106},[96,4255,4256],{"class":98,"line":132},[96,4257,4258],{"class":106},"},\n",[70,4260,4262],{"id":4261},"特定のページディレクトリを除く場合","特定のページ、ディレクトリを除く場合",[13,4264,4265,4267],{},[54,4266,1891],{}," にてグローバルな認証ミドルウェアを実装できますが、未ログインでもアクセス可能なページや、未ログインでないと閲覧できないページ（ログインページなど）では不便です。",[13,4269,4270,4271,4274],{},"個別にミドルウェアを作成してページごとに設定してオーバーライドすることも可能ですが、私はよく以下のように",[54,4272,4273],{},"auth.js","実装しています。",[86,4276,4278],{"className":1910,"code":4277,"filename":4029,"language":1912,"meta":92,"style":92},"\u002F\u002F 特定のページの認証を外す\nexport default function ({ store, redirect }) {\n    if (!store.state.user && route.fullPath !== '\u002Flogin') {\n        redirect('\u002Flogin');\n    }\n}\n",[54,4279,4280,4286,4306,4348,4364,4368],{"__ignoreMap":92},[96,4281,4282],{"class":98,"line":99},[96,4283,4285],{"class":4284},"sC9rS","\u002F\u002F 特定のページの認証を外す\n",[96,4287,4288,4290,4292,4294,4296,4298,4300,4302,4304],{"class":98,"line":120},[96,4289,2332],{"class":2331},[96,4291,2335],{"class":2331},[96,4293,4044],{"class":2285},[96,4295,4047],{"class":106},[96,4297,4050],{"class":3329},[96,4299,3016],{"class":106},[96,4301,4055],{"class":3329},[96,4303,4058],{"class":106},[96,4305,2146],{"class":106},[96,4307,4308,4310,4312,4314,4316,4318,4320,4322,4324,4327,4330,4332,4335,4338,4340,4342,4344,4346],{"class":98,"line":132},[96,4309,4065],{"class":2331},[96,4311,3939],{"class":102},[96,4313,4070],{"class":106},[96,4315,4073],{"class":128},[96,4317,2312],{"class":106},[96,4319,3988],{"class":128},[96,4321,2312],{"class":106},[96,4323,2268],{"class":128},[96,4325,4326],{"class":106}," &&",[96,4328,4329],{"class":128}," route",[96,4331,2312],{"class":106},[96,4333,4334],{"class":128},"fullPath",[96,4336,4337],{"class":106}," !==",[96,4339,110],{"class":106},[96,4341,2158],{"class":113},[96,4343,1964],{"class":106},[96,4345,4084],{"class":102},[96,4347,788],{"class":106},[96,4349,4350,4352,4354,4356,4358,4360,4362],{"class":98,"line":141},[96,4351,4091],{"class":2295},[96,4353,2299],{"class":102},[96,4355,1964],{"class":106},[96,4357,2158],{"class":113},[96,4359,1964],{"class":106},[96,4361,2309],{"class":102},[96,4363,2326],{"class":106},[96,4365,4366],{"class":98,"line":152},[96,4367,954],{"class":106},[96,4369,4370],{"class":98,"line":162},[96,4371,987],{"class":106},[86,4373,4375],{"className":1910,"code":4374,"filename":4029,"language":1912,"meta":92,"style":92},"\u002F\u002F 特定のディレクトリ配下を外す\nexport default function ({ store, redirect }) {\n    if (!store.state.user && route.fullPath.indexOf('\u002Fpublic') != -1) {\n        redirect('\u002Flogin');\n    }\n}\n",[54,4376,4377,4382,4402,4458,4474,4478],{"__ignoreMap":92},[96,4378,4379],{"class":98,"line":99},[96,4380,4381],{"class":4284},"\u002F\u002F 特定のディレクトリ配下を外す\n",[96,4383,4384,4386,4388,4390,4392,4394,4396,4398,4400],{"class":98,"line":120},[96,4385,2332],{"class":2331},[96,4387,2335],{"class":2331},[96,4389,4044],{"class":2285},[96,4391,4047],{"class":106},[96,4393,4050],{"class":3329},[96,4395,3016],{"class":106},[96,4397,4055],{"class":3329},[96,4399,4058],{"class":106},[96,4401,2146],{"class":106},[96,4403,4404,4406,4408,4410,4412,4414,4416,4418,4420,4422,4424,4426,4428,4430,4433,4435,4437,4440,4442,4444,4447,4450,4454,4456],{"class":98,"line":132},[96,4405,4065],{"class":2331},[96,4407,3939],{"class":102},[96,4409,4070],{"class":106},[96,4411,4073],{"class":128},[96,4413,2312],{"class":106},[96,4415,3988],{"class":128},[96,4417,2312],{"class":106},[96,4419,2268],{"class":128},[96,4421,4326],{"class":106},[96,4423,4329],{"class":128},[96,4425,2312],{"class":106},[96,4427,4334],{"class":128},[96,4429,2312],{"class":106},[96,4431,4432],{"class":2295},"indexOf",[96,4434,2299],{"class":102},[96,4436,1964],{"class":106},[96,4438,4439],{"class":113},"\u002Fpublic",[96,4441,1964],{"class":106},[96,4443,4084],{"class":102},[96,4445,4446],{"class":106},"!=",[96,4448,4449],{"class":106}," -",[96,4451,4453],{"class":4452},"sx098","1",[96,4455,4084],{"class":102},[96,4457,788],{"class":106},[96,4459,4460,4462,4464,4466,4468,4470,4472],{"class":98,"line":141},[96,4461,4091],{"class":2295},[96,4463,2299],{"class":102},[96,4465,1964],{"class":106},[96,4467,2158],{"class":113},[96,4469,1964],{"class":106},[96,4471,2309],{"class":102},[96,4473,2326],{"class":106},[96,4475,4476],{"class":98,"line":152},[96,4477,954],{"class":106},[96,4479,4480],{"class":98,"line":162},[96,4481,987],{"class":106},[34,4483,4485],{"id":4484},"meリクエストを初期処理にいれる","meリクエストを初期処理にいれる。",[13,4487,4488,4489,4492],{},"storeとミドルェアの準備ができたので、APIサーバーに通信をしてユーザー情報を取得するメソッドを作成しておきます。ここではmeリクエストと呼ぶことにします。今回はaxiosで",[54,4490,4491],{},"https:\u002F\u002Fapi.example.com\u002Fauth\u002Fme","へトークンと一緒にリクエストするとユーザー情報のJSONがレスポンスとして得られるとします。期限切れやトークンがない場合や間違っている場合などは401がレスポンスとして戻ります。",[70,4494,4496],{"id":4495},"spa","SPA",[13,4498,4499,4500,4505],{},"SPAの場合は",[76,4501,4504],{"href":4502,"rel":4503},"https:\u002F\u002Fgithub.com\u002Fpotato4d\u002Fnuxt-client-init-module",[584],"nuxt-client-init-moduleというライブラリ"," を使用するとスムーズです。SSRの場合はNuxtServiceInitという初期処理を実装できるフックがあるのですが、SPAの場合はそれがありあません。Pluginでできなくもないのですが、nuxt-client-init-moduleを使用するとうまくいきやすいです。",[13,4507,4508],{},"上記のライブラリをインストールしてstoreにてmeリクエストをします。",[86,4510,4513],{"className":1910,"code":4511,"filename":4512,"language":1912,"meta":92,"style":92},"export const actions = {\n  async nuxtClientInit({ commit }, context) {\n    await this.$axios.get('https:\u002F\u002Fapi.example.com\u002Fauth\u002Fme')\n    .then(async res=>{\n        commit('setUser', {user:res.data});\n    })\n    .catch(err=>{\n        console.error(err)\n    })\n  }\n}\n","store\u002Findex.js]",[54,4514,4515,4528,4552,4576,4597,4633,4640,4655,4670,4676,4681],{"__ignoreMap":92},[96,4516,4517,4519,4521,4524,4526],{"class":98,"line":99},[96,4518,2332],{"class":2331},[96,4520,3925],{"class":2285},[96,4522,4523],{"class":128}," actions ",[96,4525,2292],{"class":106},[96,4527,2146],{"class":106},[96,4529,4530,4533,4536,4539,4542,4545,4548,4550],{"class":98,"line":120},[96,4531,4532],{"class":2285},"  async",[96,4534,4535],{"class":102}," nuxtClientInit",[96,4537,4538],{"class":106},"({",[96,4540,4541],{"class":3329}," commit",[96,4543,4544],{"class":106}," },",[96,4546,4547],{"class":3329}," context",[96,4549,2309],{"class":106},[96,4551,2146],{"class":106},[96,4553,4554,4557,4559,4562,4564,4566,4568,4570,4572,4574],{"class":98,"line":132},[96,4555,4556],{"class":2331},"    await",[96,4558,3246],{"class":106},[96,4560,4561],{"class":128},"$axios",[96,4563,2312],{"class":106},[96,4565,2109],{"class":2295},[96,4567,2299],{"class":102},[96,4569,1964],{"class":106},[96,4571,4491],{"class":113},[96,4573,1964],{"class":106},[96,4575,3477],{"class":102},[96,4577,4578,4581,4584,4586,4589,4592,4595],{"class":98,"line":141},[96,4579,4580],{"class":106},"    .",[96,4582,4583],{"class":2295},"then",[96,4585,2299],{"class":102},[96,4587,4588],{"class":2285},"async",[96,4590,4591],{"class":3329}," res",[96,4593,4594],{"class":2285},"=>",[96,4596,788],{"class":106},[96,4598,4599,4602,4604,4606,4609,4611,4613,4615,4617,4619,4622,4624,4627,4629,4631],{"class":98,"line":152},[96,4600,4601],{"class":2295},"        commit",[96,4603,2299],{"class":102},[96,4605,1964],{"class":106},[96,4607,4608],{"class":113},"setUser",[96,4610,1964],{"class":106},[96,4612,3016],{"class":106},[96,4614,3010],{"class":106},[96,4616,2268],{"class":102},[96,4618,107],{"class":106},[96,4620,4621],{"class":128},"res",[96,4623,2312],{"class":106},[96,4625,4626],{"class":128},"data",[96,4628,3953],{"class":106},[96,4630,2309],{"class":102},[96,4632,2326],{"class":106},[96,4634,4635,4638],{"class":98,"line":162},[96,4636,4637],{"class":106},"    }",[96,4639,3477],{"class":102},[96,4641,4642,4644,4646,4648,4651,4653],{"class":98,"line":171},[96,4643,4580],{"class":106},[96,4645,3586],{"class":2295},[96,4647,2299],{"class":102},[96,4649,4650],{"class":3329},"err",[96,4652,4594],{"class":2285},[96,4654,788],{"class":106},[96,4656,4657,4660,4662,4664,4666,4668],{"class":98,"line":181},[96,4658,4659],{"class":128},"        console",[96,4661,2312],{"class":106},[96,4663,3591],{"class":2295},[96,4665,2299],{"class":102},[96,4667,4650],{"class":128},[96,4669,3477],{"class":102},[96,4671,4672,4674],{"class":98,"line":4},[96,4673,4637],{"class":106},[96,4675,3477],{"class":102},[96,4677,4678],{"class":98,"line":196},[96,4679,4680],{"class":106},"  }\n",[96,4682,4683],{"class":98,"line":204},[96,4684,987],{"class":106},[13,4686,4687],{},"nuxtClientInitを使用することでページ側の初期化処理より前にユーザー情報を取得できます。",[70,4689,4691],{"id":4690},"ssr","SSR",[13,4693,4694,4695,4698],{},"SSRの場合は今度は",[54,4696,4697],{},"NuxtServiceInit","に変更するだけです。これは特にライブラリは必要なく、SSRであれば利用できます。",[86,4700,4702],{"className":1910,"code":4701,"filename":4512,"language":1912,"meta":92,"style":92},"export const actions = {\n  async NuxtServiceInit({ commit }, context) {\n    \u002F\u002F サーバーサイドでトークンを入れるなどが必要な場合は適宜入れください。\n     await this.$axios.get('https:\u002F\u002Fapi.example.com\u002Fauth\u002Fme')\n    .then(async res=>{\n        commit('setUser', {user:res.data});\n    })\n    .catch(err=>{\n        console.error(err)\n    })\n  }\n}\n",[54,4703,4704,4716,4735,4740,4763,4779,4811,4817,4831,4845,4851,4855],{"__ignoreMap":92},[96,4705,4706,4708,4710,4712,4714],{"class":98,"line":99},[96,4707,2332],{"class":2331},[96,4709,3925],{"class":2285},[96,4711,4523],{"class":128},[96,4713,2292],{"class":106},[96,4715,2146],{"class":106},[96,4717,4718,4720,4723,4725,4727,4729,4731,4733],{"class":98,"line":120},[96,4719,4532],{"class":2285},[96,4721,4722],{"class":102}," NuxtServiceInit",[96,4724,4538],{"class":106},[96,4726,4541],{"class":3329},[96,4728,4544],{"class":106},[96,4730,4547],{"class":3329},[96,4732,2309],{"class":106},[96,4734,2146],{"class":106},[96,4736,4737],{"class":98,"line":132},[96,4738,4739],{"class":4284},"    \u002F\u002F サーバーサイドでトークンを入れるなどが必要な場合は適宜入れください。\n",[96,4741,4742,4745,4747,4749,4751,4753,4755,4757,4759,4761],{"class":98,"line":141},[96,4743,4744],{"class":2331},"     await",[96,4746,3246],{"class":106},[96,4748,4561],{"class":128},[96,4750,2312],{"class":106},[96,4752,2109],{"class":2295},[96,4754,2299],{"class":102},[96,4756,1964],{"class":106},[96,4758,4491],{"class":113},[96,4760,1964],{"class":106},[96,4762,3477],{"class":102},[96,4764,4765,4767,4769,4771,4773,4775,4777],{"class":98,"line":152},[96,4766,4580],{"class":106},[96,4768,4583],{"class":2295},[96,4770,2299],{"class":102},[96,4772,4588],{"class":2285},[96,4774,4591],{"class":3329},[96,4776,4594],{"class":2285},[96,4778,788],{"class":106},[96,4780,4781,4783,4785,4787,4789,4791,4793,4795,4797,4799,4801,4803,4805,4807,4809],{"class":98,"line":162},[96,4782,4601],{"class":2295},[96,4784,2299],{"class":102},[96,4786,1964],{"class":106},[96,4788,4608],{"class":113},[96,4790,1964],{"class":106},[96,4792,3016],{"class":106},[96,4794,3010],{"class":106},[96,4796,2268],{"class":102},[96,4798,107],{"class":106},[96,4800,4621],{"class":128},[96,4802,2312],{"class":106},[96,4804,4626],{"class":128},[96,4806,3953],{"class":106},[96,4808,2309],{"class":102},[96,4810,2326],{"class":106},[96,4812,4813,4815],{"class":98,"line":171},[96,4814,4637],{"class":106},[96,4816,3477],{"class":102},[96,4818,4819,4821,4823,4825,4827,4829],{"class":98,"line":181},[96,4820,4580],{"class":106},[96,4822,3586],{"class":2295},[96,4824,2299],{"class":102},[96,4826,4650],{"class":3329},[96,4828,4594],{"class":2285},[96,4830,788],{"class":106},[96,4832,4833,4835,4837,4839,4841,4843],{"class":98,"line":4},[96,4834,4659],{"class":128},[96,4836,2312],{"class":106},[96,4838,3591],{"class":2295},[96,4840,2299],{"class":102},[96,4842,4650],{"class":128},[96,4844,3477],{"class":102},[96,4846,4847,4849],{"class":98,"line":196},[96,4848,4637],{"class":106},[96,4850,3477],{"class":102},[96,4852,4853],{"class":98,"line":204},[96,4854,4680],{"class":106},[96,4856,4857],{"class":98,"line":214},[96,4858,987],{"class":106},[13,4860,4861],{},"NuxtServiceInitというサーバーサイドで上記のリクエストを行って、storeにユーザー情報をいれてくれます。401が来てもcatchしてくれ、ユーザー情報はnullのままになります。",[34,4863,4864],{"id":4864},"完了",[13,4866,4867],{},"エッセンスは以下の通りです。",[3895,4869,4870,4873,4876],{},[3742,4871,4872],{},"初期処理にユーザー情報を取得するAPIをリクエスト",[3742,4874,4875],{},"認証済みであればstoreのuserにユーザー情報のオブジェクトが入る",[3742,4877,4878],{},"ミドルウェアやstoreの情報を使用して認証による分岐を行う",[3795,4880,4881],{},"html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}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 pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}",{"title":92,"searchDepth":132,"depth":132,"links":4883},[4884,4885,4886,4889,4893],{"id":3881,"depth":120,"text":3881},{"id":3908,"depth":120,"text":3909},{"id":4023,"depth":120,"text":4023,"children":4887},[4888],{"id":4261,"depth":132,"text":4262},{"id":4484,"depth":120,"text":4485,"children":4890},[4891,4892],{"id":4495,"depth":132,"text":4496},{"id":4690,"depth":132,"text":4691},{"id":4864,"depth":120,"text":4864},[3836],"2022-05-19","フロント側の認証機能を実装します",{},"\u002Farticles\u002Fnuxt-auth-middleware",{"title":3850,"description":4896},"articles\u002Fnuxt-auth-middleware",[4902,3845],"js","_common\u002Fnuxt.jpg","MWiO6wSm6JBqm1_bIaWiFdYFIqDsWKtpAvMLHvfsPGg",{"id":4906,"title":4907,"body":4908,"category":6682,"createdAt":6683,"description":6684,"extension":3838,"index":141,"meta":6685,"navigation":621,"path":6686,"publish":621,"seo":6687,"series":6688,"seriesTitle":6689,"stem":6690,"tag":6691,"thumbnail":6692,"updatedAt":3839,"__hash__":6693},"series\u002Fseries\u002Fnuxt-content-blog-4.md","Nuxt Content × SSG で作る静的ブログ。４：カテゴリーとタグ機能",{"type":10,"value":4909,"toc":6673},[4910,4922,4925,4928,4931,4934,4938,4941,5055,5064,5071,5075,5082,5752,5758,6125,6132,6180,6183,6187,6190,6197,6204,6210,6217,6221,6228,6612,6627,6630,6633,6646,6650,6653,6667,6670],[13,4911,4912,4913,4917,4918,4921],{},"こんにちはjunです。",[76,4914,4916],{"href":4915},"\u002Fseries\u002Fnuxt-content-blog\u002F3","前回の記事","は詳細ページの実装と静的書き出しを行いました。今回の記事では ",[29,4919,4920],{},"カテゴリーとタグ機能","　について解説します。",[34,4923,4924],{"id":4924},"カテゴリーとタグについて",[13,4926,4927],{},"まず最初にカテゴリーとタグ機能について念のため定義しておきます。基本的にはwordpressのものと概念は似ています。カテゴリーは基本的に記事に一つだけ設定して大まかな分類を行います。タグは記事に対して複数個設定できます。",[13,4929,4930],{},"機能としては上記の通りですが、記事がどんなコンテンツであるか整理されてリスト化されることでサイト訪問者がコンテンツを探しやすくなります。実際私のサイトもわかりやすくありませんか笑？",[13,4932,4933],{},"そのためブログを作るのならば、このカテゴリーとタグの機能はかなり重要です。それでは早速解説します。",[34,4935,4937],{"id":4936},"各記事にカテゴリータグを設定する","各記事にカテゴリー・タグを設定する",[13,4939,4940],{},"Nuxt Contentはマークダウンで原稿を作成します。マークダウン上部には以下のような記事のメタデータを記述します。",[86,4942,4944],{"className":88,"code":4943,"language":91,"meta":92,"style":92},"---\ntitle: Nuxt Content × SSG で作る静的ブログ。４：カテゴリーとタグ機能\ndescription: Nuxt Content × SSG で作る静的ブログ。カテゴリーとタグ機能\ncategory: [devstack]\ntag: [js,nuxt]\nseries: nuxt-content-blog\nseriesTitle: Nuxt Content × SSG で作る静的ブログ。\nindex: 4\npublish: true\nthumbnail:  _mix\u002Flogo-dark.jpg\n---\n",[54,4945,4946,4951,4961,4971,4985,5002,5012,5022,5032,5041,5051],{"__ignoreMap":92},[96,4947,4948],{"class":98,"line":99},[96,4949,4950],{"class":1923},"---\n",[96,4952,4953,4956,4958],{"class":98,"line":120},[96,4954,4955],{"class":102},"title",[96,4957,107],{"class":106},[96,4959,4960],{"class":113}," Nuxt Content × SSG で作る静的ブログ。４：カテゴリーとタグ機能\n",[96,4962,4963,4966,4968],{"class":98,"line":132},[96,4964,4965],{"class":102},"description",[96,4967,107],{"class":106},[96,4969,4970],{"class":113}," Nuxt Content × SSG で作る静的ブログ。カテゴリーとタグ機能\n",[96,4972,4973,4976,4978,4981,4983],{"class":98,"line":141},[96,4974,4975],{"class":102},"category",[96,4977,107],{"class":106},[96,4979,4980],{"class":106}," [",[96,4982,3836],{"class":113},[96,4984,683],{"class":106},[96,4986,4987,4990,4992,4994,4996,4998,5000],{"class":98,"line":152},[96,4988,4989],{"class":102},"tag",[96,4991,107],{"class":106},[96,4993,4980],{"class":106},[96,4995,4902],{"class":113},[96,4997,3016],{"class":106},[96,4999,3845],{"class":113},[96,5001,683],{"class":106},[96,5003,5004,5007,5009],{"class":98,"line":162},[96,5005,5006],{"class":102},"series",[96,5008,107],{"class":106},[96,5010,5011],{"class":113}," nuxt-content-blog\n",[96,5013,5014,5017,5019],{"class":98,"line":171},[96,5015,5016],{"class":102},"seriesTitle",[96,5018,107],{"class":106},[96,5020,5021],{"class":113}," Nuxt Content × SSG で作る静的ブログ。\n",[96,5023,5024,5027,5029],{"class":98,"line":181},[96,5025,5026],{"class":102},"index",[96,5028,107],{"class":106},[96,5030,5031],{"class":4452}," 4\n",[96,5033,5034,5037,5039],{"class":98,"line":4},[96,5035,5036],{"class":102},"publish",[96,5038,107],{"class":106},[96,5040,261],{"class":260},[96,5042,5043,5046,5048],{"class":98,"line":196},[96,5044,5045],{"class":102},"thumbnail",[96,5047,107],{"class":106},[96,5049,5050],{"class":113},"  _mix\u002Flogo-dark.jpg\n",[96,5052,5053],{"class":98,"line":204},[96,5054,4950],{"class":1923},[13,5056,5057,5058,435,5060,5063],{},"ここで設定する値は基本的に自由につけることができます。そしてこの例の",[54,5059,4975],{},[54,5061,5062],{},"tags","に注目してみてください。各記事のカテゴリーとタグはここで定義されています。そして配列形式で設定します。このようにメタデータに配列で設定することで記事のカテゴリー・タグを配列で取得できます。配列なので複数個設定することができます。",[13,5065,5066,5067,5070],{},"この時気をつけたいのが、例えば",[54,5068,5069],{},"[開発スタック,メモ]","と日本語（ラベル）を入力するのでなく、プログラム的なキー・スラグで入力した方が良いです。理由は後述します。",[34,5072,5074],{"id":5073},"カテゴリータグ設定ファイルを作成する","カテゴリー、タグ設定ファイルを作成する",[13,5076,5077,5078,5081],{},"プロジェクトルートに",[54,5079,5080],{},"taxonomy.js","というようなカテゴリー、タグを管理するファイルを作成します。中身は以下のようになっています。",[86,5083,5085],{"className":1910,"code":5084,"filename":5080,"language":1912,"meta":92,"style":92},"module.exports = {\n    category:[\n        {text:'技術スタック',slug:'devstack'},\n        {text:'プログラミング学習',slug:'learning'},\n        {text:'メモ',slug:'ministack'},\n    ],\n    tags:[\n        {text:'HTML',slug:'html'},\n        {text:'CSS',slug:'css'},\n        {text:'Javascript',slug:'js'},\n        {text:'jquery',slug:'jquery'},\n        {text:'Vue.js',slug:'vue'},\n        {text:'Nuxt.js',slug:'nuxt'},\n        {text:'webpack',slug:'webpack'},\n        {text:'PHP',slug:'php'},\n        {text:'Laravel',slug:'laravel'},\n        {text:'Python',slug:'python'},\n        {text:'Django',slug:'django'},\n        {text:'HeadlessCMS',slug:'headlesscms'},\n        {text:'wordpress',slug:'wordpress'},\n        {text:'concrete5',slug:'concrete5'},\n        {text:'ZOOM',slug:'zoom'},\n        {text:'インフラ',slug:'infrastructure'},\n        {text:'ネットワーク',slug:'network'},\n        {text:'Docker',slug:'docker'},\n    ]\n}\n",[54,5086,5087,5096,5106,5137,5167,5197,5204,5213,5242,5272,5301,5330,5359,5388,5417,5446,5475,5505,5535,5565,5594,5623,5653,5683,5713,5743,5748],{"__ignoreMap":92},[96,5088,5089,5092,5094],{"class":98,"line":99},[96,5090,5091],{"class":106},"module.exports",[96,5093,3199],{"class":106},[96,5095,2146],{"class":106},[96,5097,5098,5101,5103],{"class":98,"line":120},[96,5099,5100],{"class":102},"    category",[96,5102,107],{"class":106},[96,5104,5105],{"class":128},"[\n",[96,5107,5108,5111,5113,5115,5117,5120,5122,5124,5127,5129,5131,5133,5135],{"class":98,"line":132},[96,5109,5110],{"class":106},"        {",[96,5112,460],{"class":102},[96,5114,107],{"class":106},[96,5116,1964],{"class":106},[96,5118,5119],{"class":113},"技術スタック",[96,5121,1964],{"class":106},[96,5123,3016],{"class":106},[96,5125,5126],{"class":102},"slug",[96,5128,107],{"class":106},[96,5130,1964],{"class":106},[96,5132,3836],{"class":113},[96,5134,1964],{"class":106},[96,5136,4258],{"class":106},[96,5138,5139,5141,5143,5145,5147,5150,5152,5154,5156,5158,5160,5163,5165],{"class":98,"line":141},[96,5140,5110],{"class":106},[96,5142,460],{"class":102},[96,5144,107],{"class":106},[96,5146,1964],{"class":106},[96,5148,5149],{"class":113},"プログラミング学習",[96,5151,1964],{"class":106},[96,5153,3016],{"class":106},[96,5155,5126],{"class":102},[96,5157,107],{"class":106},[96,5159,1964],{"class":106},[96,5161,5162],{"class":113},"learning",[96,5164,1964],{"class":106},[96,5166,4258],{"class":106},[96,5168,5169,5171,5173,5175,5177,5180,5182,5184,5186,5188,5190,5193,5195],{"class":98,"line":152},[96,5170,5110],{"class":106},[96,5172,460],{"class":102},[96,5174,107],{"class":106},[96,5176,1964],{"class":106},[96,5178,5179],{"class":113},"メモ",[96,5181,1964],{"class":106},[96,5183,3016],{"class":106},[96,5185,5126],{"class":102},[96,5187,107],{"class":106},[96,5189,1964],{"class":106},[96,5191,5192],{"class":113},"ministack",[96,5194,1964],{"class":106},[96,5196,4258],{"class":106},[96,5198,5199,5202],{"class":98,"line":162},[96,5200,5201],{"class":128},"    ]",[96,5203,1940],{"class":106},[96,5205,5206,5209,5211],{"class":98,"line":171},[96,5207,5208],{"class":102},"    tags",[96,5210,107],{"class":106},[96,5212,5105],{"class":128},[96,5214,5215,5217,5219,5221,5223,5226,5228,5230,5232,5234,5236,5238,5240],{"class":98,"line":181},[96,5216,5110],{"class":106},[96,5218,460],{"class":102},[96,5220,107],{"class":106},[96,5222,1964],{"class":106},[96,5224,5225],{"class":113},"HTML",[96,5227,1964],{"class":106},[96,5229,3016],{"class":106},[96,5231,5126],{"class":102},[96,5233,107],{"class":106},[96,5235,1964],{"class":106},[96,5237,430],{"class":113},[96,5239,1964],{"class":106},[96,5241,4258],{"class":106},[96,5243,5244,5246,5248,5250,5252,5255,5257,5259,5261,5263,5265,5268,5270],{"class":98,"line":4},[96,5245,5110],{"class":106},[96,5247,460],{"class":102},[96,5249,107],{"class":106},[96,5251,1964],{"class":106},[96,5253,5254],{"class":113},"CSS",[96,5256,1964],{"class":106},[96,5258,3016],{"class":106},[96,5260,5126],{"class":102},[96,5262,107],{"class":106},[96,5264,1964],{"class":106},[96,5266,5267],{"class":113},"css",[96,5269,1964],{"class":106},[96,5271,4258],{"class":106},[96,5273,5274,5276,5278,5280,5282,5285,5287,5289,5291,5293,5295,5297,5299],{"class":98,"line":196},[96,5275,5110],{"class":106},[96,5277,460],{"class":102},[96,5279,107],{"class":106},[96,5281,1964],{"class":106},[96,5283,5284],{"class":113},"Javascript",[96,5286,1964],{"class":106},[96,5288,3016],{"class":106},[96,5290,5126],{"class":102},[96,5292,107],{"class":106},[96,5294,1964],{"class":106},[96,5296,4902],{"class":113},[96,5298,1964],{"class":106},[96,5300,4258],{"class":106},[96,5302,5303,5305,5307,5309,5311,5314,5316,5318,5320,5322,5324,5326,5328],{"class":98,"line":204},[96,5304,5110],{"class":106},[96,5306,460],{"class":102},[96,5308,107],{"class":106},[96,5310,1964],{"class":106},[96,5312,5313],{"class":113},"jquery",[96,5315,1964],{"class":106},[96,5317,3016],{"class":106},[96,5319,5126],{"class":102},[96,5321,107],{"class":106},[96,5323,1964],{"class":106},[96,5325,5313],{"class":113},[96,5327,1964],{"class":106},[96,5329,4258],{"class":106},[96,5331,5332,5334,5336,5338,5340,5343,5345,5347,5349,5351,5353,5355,5357],{"class":98,"line":214},[96,5333,5110],{"class":106},[96,5335,460],{"class":102},[96,5337,107],{"class":106},[96,5339,1964],{"class":106},[96,5341,5342],{"class":113},"Vue.js",[96,5344,1964],{"class":106},[96,5346,3016],{"class":106},[96,5348,5126],{"class":102},[96,5350,107],{"class":106},[96,5352,1964],{"class":106},[96,5354,2446],{"class":113},[96,5356,1964],{"class":106},[96,5358,4258],{"class":106},[96,5360,5361,5363,5365,5367,5369,5372,5374,5376,5378,5380,5382,5384,5386],{"class":98,"line":228},[96,5362,5110],{"class":106},[96,5364,460],{"class":102},[96,5366,107],{"class":106},[96,5368,1964],{"class":106},[96,5370,5371],{"class":113},"Nuxt.js",[96,5373,1964],{"class":106},[96,5375,3016],{"class":106},[96,5377,5126],{"class":102},[96,5379,107],{"class":106},[96,5381,1964],{"class":106},[96,5383,3845],{"class":113},[96,5385,1964],{"class":106},[96,5387,4258],{"class":106},[96,5389,5390,5392,5394,5396,5398,5401,5403,5405,5407,5409,5411,5413,5415],{"class":98,"line":240},[96,5391,5110],{"class":106},[96,5393,460],{"class":102},[96,5395,107],{"class":106},[96,5397,1964],{"class":106},[96,5399,5400],{"class":113},"webpack",[96,5402,1964],{"class":106},[96,5404,3016],{"class":106},[96,5406,5126],{"class":102},[96,5408,107],{"class":106},[96,5410,1964],{"class":106},[96,5412,5400],{"class":113},[96,5414,1964],{"class":106},[96,5416,4258],{"class":106},[96,5418,5419,5421,5423,5425,5427,5430,5432,5434,5436,5438,5440,5442,5444],{"class":98,"line":252},[96,5420,5110],{"class":106},[96,5422,460],{"class":102},[96,5424,107],{"class":106},[96,5426,1964],{"class":106},[96,5428,5429],{"class":113},"PHP",[96,5431,1964],{"class":106},[96,5433,3016],{"class":106},[96,5435,5126],{"class":102},[96,5437,107],{"class":106},[96,5439,1964],{"class":106},[96,5441,609],{"class":113},[96,5443,1964],{"class":106},[96,5445,4258],{"class":106},[96,5447,5448,5450,5452,5454,5456,5459,5461,5463,5465,5467,5469,5471,5473],{"class":98,"line":264},[96,5449,5110],{"class":106},[96,5451,460],{"class":102},[96,5453,107],{"class":106},[96,5455,1964],{"class":106},[96,5457,5458],{"class":113},"Laravel",[96,5460,1964],{"class":106},[96,5462,3016],{"class":106},[96,5464,5126],{"class":102},[96,5466,107],{"class":106},[96,5468,1964],{"class":106},[96,5470,438],{"class":113},[96,5472,1964],{"class":106},[96,5474,4258],{"class":106},[96,5476,5477,5479,5481,5483,5485,5488,5490,5492,5494,5496,5498,5501,5503],{"class":98,"line":275},[96,5478,5110],{"class":106},[96,5480,460],{"class":102},[96,5482,107],{"class":106},[96,5484,1964],{"class":106},[96,5486,5487],{"class":113},"Python",[96,5489,1964],{"class":106},[96,5491,3016],{"class":106},[96,5493,5126],{"class":102},[96,5495,107],{"class":106},[96,5497,1964],{"class":106},[96,5499,5500],{"class":113},"python",[96,5502,1964],{"class":106},[96,5504,4258],{"class":106},[96,5506,5507,5509,5511,5513,5515,5518,5520,5522,5524,5526,5528,5531,5533],{"class":98,"line":283},[96,5508,5110],{"class":106},[96,5510,460],{"class":102},[96,5512,107],{"class":106},[96,5514,1964],{"class":106},[96,5516,5517],{"class":113},"Django",[96,5519,1964],{"class":106},[96,5521,3016],{"class":106},[96,5523,5126],{"class":102},[96,5525,107],{"class":106},[96,5527,1964],{"class":106},[96,5529,5530],{"class":113},"django",[96,5532,1964],{"class":106},[96,5534,4258],{"class":106},[96,5536,5537,5539,5541,5543,5545,5548,5550,5552,5554,5556,5558,5561,5563],{"class":98,"line":293},[96,5538,5110],{"class":106},[96,5540,460],{"class":102},[96,5542,107],{"class":106},[96,5544,1964],{"class":106},[96,5546,5547],{"class":113},"HeadlessCMS",[96,5549,1964],{"class":106},[96,5551,3016],{"class":106},[96,5553,5126],{"class":102},[96,5555,107],{"class":106},[96,5557,1964],{"class":106},[96,5559,5560],{"class":113},"headlesscms",[96,5562,1964],{"class":106},[96,5564,4258],{"class":106},[96,5566,5567,5569,5571,5573,5575,5578,5580,5582,5584,5586,5588,5590,5592],{"class":98,"line":301},[96,5568,5110],{"class":106},[96,5570,460],{"class":102},[96,5572,107],{"class":106},[96,5574,1964],{"class":106},[96,5576,5577],{"class":113},"wordpress",[96,5579,1964],{"class":106},[96,5581,3016],{"class":106},[96,5583,5126],{"class":102},[96,5585,107],{"class":106},[96,5587,1964],{"class":106},[96,5589,5577],{"class":113},[96,5591,1964],{"class":106},[96,5593,4258],{"class":106},[96,5595,5596,5598,5600,5602,5604,5607,5609,5611,5613,5615,5617,5619,5621],{"class":98,"line":312},[96,5597,5110],{"class":106},[96,5599,460],{"class":102},[96,5601,107],{"class":106},[96,5603,1964],{"class":106},[96,5605,5606],{"class":113},"concrete5",[96,5608,1964],{"class":106},[96,5610,3016],{"class":106},[96,5612,5126],{"class":102},[96,5614,107],{"class":106},[96,5616,1964],{"class":106},[96,5618,5606],{"class":113},[96,5620,1964],{"class":106},[96,5622,4258],{"class":106},[96,5624,5625,5627,5629,5631,5633,5636,5638,5640,5642,5644,5646,5649,5651],{"class":98,"line":323},[96,5626,5110],{"class":106},[96,5628,460],{"class":102},[96,5630,107],{"class":106},[96,5632,1964],{"class":106},[96,5634,5635],{"class":113},"ZOOM",[96,5637,1964],{"class":106},[96,5639,3016],{"class":106},[96,5641,5126],{"class":102},[96,5643,107],{"class":106},[96,5645,1964],{"class":106},[96,5647,5648],{"class":113},"zoom",[96,5650,1964],{"class":106},[96,5652,4258],{"class":106},[96,5654,5655,5657,5659,5661,5663,5666,5668,5670,5672,5674,5676,5679,5681],{"class":98,"line":334},[96,5656,5110],{"class":106},[96,5658,460],{"class":102},[96,5660,107],{"class":106},[96,5662,1964],{"class":106},[96,5664,5665],{"class":113},"インフラ",[96,5667,1964],{"class":106},[96,5669,3016],{"class":106},[96,5671,5126],{"class":102},[96,5673,107],{"class":106},[96,5675,1964],{"class":106},[96,5677,5678],{"class":113},"infrastructure",[96,5680,1964],{"class":106},[96,5682,4258],{"class":106},[96,5684,5685,5687,5689,5691,5693,5696,5698,5700,5702,5704,5706,5709,5711],{"class":98,"line":345},[96,5686,5110],{"class":106},[96,5688,460],{"class":102},[96,5690,107],{"class":106},[96,5692,1964],{"class":106},[96,5694,5695],{"class":113},"ネットワーク",[96,5697,1964],{"class":106},[96,5699,3016],{"class":106},[96,5701,5126],{"class":102},[96,5703,107],{"class":106},[96,5705,1964],{"class":106},[96,5707,5708],{"class":113},"network",[96,5710,1964],{"class":106},[96,5712,4258],{"class":106},[96,5714,5715,5717,5719,5721,5723,5726,5728,5730,5732,5734,5736,5739,5741],{"class":98,"line":354},[96,5716,5110],{"class":106},[96,5718,460],{"class":102},[96,5720,107],{"class":106},[96,5722,1964],{"class":106},[96,5724,5725],{"class":113},"Docker",[96,5727,1964],{"class":106},[96,5729,3016],{"class":106},[96,5731,5126],{"class":102},[96,5733,107],{"class":106},[96,5735,1964],{"class":106},[96,5737,5738],{"class":113},"docker",[96,5740,1964],{"class":106},[96,5742,4258],{"class":106},[96,5744,5745],{"class":98,"line":366},[96,5746,5747],{"class":128},"    ]\n",[96,5749,5750],{"class":98,"line":375},[96,5751,987],{"class":106},[13,5753,5754,5757],{},[54,5755,5756],{},"module export"," としているのは静的書き出しの際にこのファイルを使用するからです。そしてNuxtで制御しやすいようにStoreに入れておきます。このように管理する時、キー（スラグ）とラベル（カテゴリ名）を分けてオブジェクトにしておいた方が良いです。キーを使用することで重複を避けたり、そのままURLの一部として使用することも可能です。",[86,5759,5761],{"className":1910,"code":5760,"filename":3916,"language":1912,"meta":92,"style":92},"import taxonomy from '..\u002Ftaxonomy';\nexport const state = () => ({\n    category:[\n        ...taxonomy.category\n    ],\n    tags:[\n        ...taxonomy.tags\n    ]\n  })\n\nexport const getters ={\n    getTagTextBySlug(state){\n        return (slug)=>{\n            const idx = state.tags.findIndex(tag=>{\n                return tag.slug === slug;\n            })\n            return (idx > -1)?state.tags[idx].text:undefined;\n        }\n    },\n    getCategoryTextBySlug(state){\n        return (slug)=>{\n            const idx = state.category.findIndex(tag=>{\n                return tag.slug === slug;\n            })\n            return (idx > -1)?state.category[idx].text:undefined;\n        }\n    }\n}\n",[54,5762,5763,5782,5800,5808,5821,5827,5835,5846,5850,5857,5861,5873,5884,5898,5928,5948,5954,5996,6000,6004,6015,6029,6055,6071,6077,6113,6117,6121],{"__ignoreMap":92},[96,5764,5765,5767,5770,5773,5775,5778,5780],{"class":98,"line":99},[96,5766,3007],{"class":2331},[96,5768,5769],{"class":128}," taxonomy ",[96,5771,5772],{"class":2331},"from",[96,5774,110],{"class":106},[96,5776,5777],{"class":113},"..\u002Ftaxonomy",[96,5779,1964],{"class":106},[96,5781,2326],{"class":106},[96,5783,5784,5786,5788,5790,5792,5794,5796,5798],{"class":98,"line":120},[96,5785,2332],{"class":2331},[96,5787,3925],{"class":2285},[96,5789,3928],{"class":128},[96,5791,2292],{"class":106},[96,5793,3933],{"class":106},[96,5795,3936],{"class":2285},[96,5797,3939],{"class":128},[96,5799,788],{"class":106},[96,5801,5802,5804,5806],{"class":98,"line":132},[96,5803,5100],{"class":102},[96,5805,107],{"class":106},[96,5807,5105],{"class":128},[96,5809,5810,5813,5816,5818],{"class":98,"line":141},[96,5811,5812],{"class":106},"        ...",[96,5814,5815],{"class":128},"taxonomy",[96,5817,2312],{"class":106},[96,5819,5820],{"class":128},"category\n",[96,5822,5823,5825],{"class":98,"line":152},[96,5824,5201],{"class":128},[96,5826,1940],{"class":106},[96,5828,5829,5831,5833],{"class":98,"line":162},[96,5830,5208],{"class":102},[96,5832,107],{"class":106},[96,5834,5105],{"class":128},[96,5836,5837,5839,5841,5843],{"class":98,"line":171},[96,5838,5812],{"class":106},[96,5840,5815],{"class":128},[96,5842,2312],{"class":106},[96,5844,5845],{"class":128},"tags\n",[96,5847,5848],{"class":98,"line":181},[96,5849,5747],{"class":128},[96,5851,5852,5855],{"class":98,"line":4},[96,5853,5854],{"class":106},"  }",[96,5856,3477],{"class":128},[96,5858,5859],{"class":98,"line":196},[96,5860,622],{"emptyLinePlaceholder":621},[96,5862,5863,5865,5867,5870],{"class":98,"line":204},[96,5864,2332],{"class":2331},[96,5866,3925],{"class":2285},[96,5868,5869],{"class":128}," getters ",[96,5871,5872],{"class":106},"={\n",[96,5874,5875,5878,5880,5882],{"class":98,"line":214},[96,5876,5877],{"class":102},"    getTagTextBySlug",[96,5879,2299],{"class":106},[96,5881,3988],{"class":3329},[96,5883,3333],{"class":106},[96,5885,5886,5888,5890,5892,5894,5896],{"class":98,"line":228},[96,5887,3074],{"class":2331},[96,5889,3939],{"class":106},[96,5891,5126],{"class":3329},[96,5893,2309],{"class":106},[96,5895,4594],{"class":2285},[96,5897,788],{"class":106},[96,5899,5900,5903,5906,5908,5911,5913,5915,5917,5920,5922,5924,5926],{"class":98,"line":240},[96,5901,5902],{"class":2285},"            const",[96,5904,5905],{"class":128}," idx",[96,5907,3199],{"class":106},[96,5909,5910],{"class":128}," state",[96,5912,2312],{"class":106},[96,5914,5062],{"class":128},[96,5916,2312],{"class":106},[96,5918,5919],{"class":2295},"findIndex",[96,5921,2299],{"class":102},[96,5923,4989],{"class":3329},[96,5925,4594],{"class":2285},[96,5927,788],{"class":106},[96,5929,5930,5933,5936,5938,5940,5943,5946],{"class":98,"line":252},[96,5931,5932],{"class":2331},"                return",[96,5934,5935],{"class":128}," tag",[96,5937,2312],{"class":106},[96,5939,5126],{"class":128},[96,5941,5942],{"class":106}," ===",[96,5944,5945],{"class":128}," slug",[96,5947,2326],{"class":106},[96,5949,5950,5952],{"class":98,"line":264},[96,5951,3583],{"class":106},[96,5953,3477],{"class":102},[96,5955,5956,5958,5960,5963,5966,5968,5970,5972,5975,5977,5979,5981,5984,5986,5989,5991,5993],{"class":98,"line":275},[96,5957,3243],{"class":2331},[96,5959,3939],{"class":102},[96,5961,5962],{"class":128},"idx",[96,5964,5965],{"class":106}," >",[96,5967,4449],{"class":106},[96,5969,4453],{"class":4452},[96,5971,2309],{"class":102},[96,5973,5974],{"class":106},"?",[96,5976,3988],{"class":128},[96,5978,2312],{"class":106},[96,5980,5062],{"class":128},[96,5982,5983],{"class":102},"[",[96,5985,5962],{"class":128},[96,5987,5988],{"class":102},"]",[96,5990,2312],{"class":106},[96,5992,460],{"class":128},[96,5994,5995],{"class":106},":undefined;\n",[96,5997,5998],{"class":98,"line":283},[96,5999,1376],{"class":106},[96,6001,6002],{"class":98,"line":293},[96,6003,3130],{"class":106},[96,6005,6006,6009,6011,6013],{"class":98,"line":301},[96,6007,6008],{"class":102},"    getCategoryTextBySlug",[96,6010,2299],{"class":106},[96,6012,3988],{"class":3329},[96,6014,3333],{"class":106},[96,6016,6017,6019,6021,6023,6025,6027],{"class":98,"line":312},[96,6018,3074],{"class":2331},[96,6020,3939],{"class":106},[96,6022,5126],{"class":3329},[96,6024,2309],{"class":106},[96,6026,4594],{"class":2285},[96,6028,788],{"class":106},[96,6030,6031,6033,6035,6037,6039,6041,6043,6045,6047,6049,6051,6053],{"class":98,"line":323},[96,6032,5902],{"class":2285},[96,6034,5905],{"class":128},[96,6036,3199],{"class":106},[96,6038,5910],{"class":128},[96,6040,2312],{"class":106},[96,6042,4975],{"class":128},[96,6044,2312],{"class":106},[96,6046,5919],{"class":2295},[96,6048,2299],{"class":102},[96,6050,4989],{"class":3329},[96,6052,4594],{"class":2285},[96,6054,788],{"class":106},[96,6056,6057,6059,6061,6063,6065,6067,6069],{"class":98,"line":334},[96,6058,5932],{"class":2331},[96,6060,5935],{"class":128},[96,6062,2312],{"class":106},[96,6064,5126],{"class":128},[96,6066,5942],{"class":106},[96,6068,5945],{"class":128},[96,6070,2326],{"class":106},[96,6072,6073,6075],{"class":98,"line":345},[96,6074,3583],{"class":106},[96,6076,3477],{"class":102},[96,6078,6079,6081,6083,6085,6087,6089,6091,6093,6095,6097,6099,6101,6103,6105,6107,6109,6111],{"class":98,"line":354},[96,6080,3243],{"class":2331},[96,6082,3939],{"class":102},[96,6084,5962],{"class":128},[96,6086,5965],{"class":106},[96,6088,4449],{"class":106},[96,6090,4453],{"class":4452},[96,6092,2309],{"class":102},[96,6094,5974],{"class":106},[96,6096,3988],{"class":128},[96,6098,2312],{"class":106},[96,6100,4975],{"class":128},[96,6102,5983],{"class":102},[96,6104,5962],{"class":128},[96,6106,5988],{"class":102},[96,6108,2312],{"class":106},[96,6110,460],{"class":128},[96,6112,5995],{"class":106},[96,6114,6115],{"class":98,"line":366},[96,6116,1376],{"class":106},[96,6118,6119],{"class":98,"line":375},[96,6120,954],{"class":106},[96,6122,6123],{"class":98,"line":383},[96,6124,987],{"class":106},[13,6126,6127,6128,6131],{},"カテゴリー、タグはサイドメニューにあるようにバッチで表示しています。その時は以下のような",[54,6129,6130],{},"v-for","のコンポーネントで表示しています。",[86,6133,6135],{"className":2443,"code":6134,"language":2446,"meta":92,"style":92},"\u003Cdiv class=\"p-badge-container\">\n    \u003Cnuxt-link class=\"c-tag-badge\" v-for=\"(t,index) in $store.state.category\" :key=\"'tag-'+index\" :to=\"'\u002Ftag\u002F'+t\">\n        \u003Cspan>{{t.text}}\u003C\u002Fspan>\n    \u003C\u002Fnuxt-link>\n\u003C\u002Fdiv>\n",[54,6136,6137,6157,6162,6167,6172],{"__ignoreMap":92},[96,6138,6139,6141,6143,6146,6148,6150,6153,6155],{"class":98,"line":99},[96,6140,2453],{"class":106},[96,6142,4138],{"class":102},[96,6144,6145],{"class":2285}," class",[96,6147,2292],{"class":106},[96,6149,2477],{"class":106},[96,6151,6152],{"class":113},"p-badge-container",[96,6154,2477],{"class":106},[96,6156,2459],{"class":106},[96,6158,6159],{"class":98,"line":120},[96,6160,6161],{"class":128},"    \u003Cnuxt-link class=\"c-tag-badge\" v-for=\"(t,index) in $store.state.category\" :key=\"'tag-'+index\" :to=\"'\u002Ftag\u002F'+t\">\n",[96,6163,6164],{"class":98,"line":132},[96,6165,6166],{"class":128},"        \u003Cspan>{{t.text}}\u003C\u002Fspan>\n",[96,6168,6169],{"class":98,"line":141},[96,6170,6171],{"class":128},"    \u003C\u002Fnuxt-link>\n",[96,6173,6174,6176,6178],{"class":98,"line":152},[96,6175,2641],{"class":106},[96,6177,4138],{"class":102},[96,6179,2459],{"class":106},[13,6181,6182],{},"カテゴリー、タグを管理、使用、設定ができるようになりましたので、次はカテゴリー・タグ一覧を作成していきましょう。",[34,6184,6186],{"id":6185},"カテゴリータグ一覧ページのためのpages構成","カテゴリー、タグ一覧ページのためのpages構成",[13,6188,6189],{},"カテゴリーとタグ一覧ページの構成は互いに同じなので、タグ一覧だけ解説します。",[13,6191,6192,6193,6196],{},"タグ一覧はそのタグのバッチをクリックすると閲覧できるページです。そのタグがついた記事を全て見ることが可能です。そして",[54,6194,6195],{},"\u002Ftag\u002F{tag_key}","というURLでルーティングされています。",[13,6198,6199,6200,6203],{},"そのようなルーティングを行うために以下のように",[54,6201,6202],{},"pages\u002F","を設定します。",[86,6205,6208],{"className":6206,"code":6207,"language":460},[457],"pages\n└── tag\n    └── _slug\n        ├── index.vue\n        └── page\n            └── _id.vue\n",[54,6209,6207],{"__ignoreMap":92},[13,6211,6212,6213,6216],{},"こうすうることで",[54,6214,6215],{},"{tag_key}","という動的ルートに対応できます。またページングのためのページコンポーネントも作成しておきます。",[34,6218,6220],{"id":6219},"特定のタグを持つ投稿のみを取得する","特定のタグを持つ投稿のみを取得する。",[13,6222,6223,6224,6227],{},"それでは",[54,6225,6226],{},"\u002Ftag\u002F_slug\u002Findex.vue","の中身を見ていきましょう。",[86,6229,6232],{"className":1910,"code":6230,"filename":6231,"language":1912,"meta":92,"style":92},"export default {\n    async asyncData({ store,$content, params }) {\n        const count = await $content({ deep: true }).only('title').where({ tag:{ $contains:params.slug}}).fetch();\n\n        const content = await $content({ deep: true })\n        .only(['title','description','thumbnail','path','category','tag','updatedAt','series','index'])\n        .sortBy('createdAt', 'desc')\n        .where({tag:{ $contains:params.slug}}) \u002F\u002F ここ\n        .skip(0).limit(store.state.indexPerPage)\n        .fetch();\n\n        return {\n`        content,\n        count:count.length`\n        }\n    },\n}\n","pages\u002Ftag\u002F_slug\u002Findex.vue",[54,6233,6234,6242,6268,6356,6360,6387,6472,6499,6530,6564,6574,6578,6584,6592,6600,6604,6608],{"__ignoreMap":92},[96,6235,6236,6238,6240],{"class":98,"line":99},[96,6237,2332],{"class":2331},[96,6239,2335],{"class":2331},[96,6241,2146],{"class":106},[96,6243,6244,6247,6250,6252,6254,6256,6259,6261,6264,6266],{"class":98,"line":120},[96,6245,6246],{"class":2285},"    async",[96,6248,6249],{"class":102}," asyncData",[96,6251,4538],{"class":106},[96,6253,4050],{"class":3329},[96,6255,3016],{"class":106},[96,6257,6258],{"class":3329},"$content",[96,6260,3016],{"class":106},[96,6262,6263],{"class":3329}," params",[96,6265,4058],{"class":106},[96,6267,2146],{"class":106},[96,6269,6270,6273,6276,6278,6281,6284,6286,6289,6292,6294,6297,6299,6301,6303,6306,6308,6310,6312,6314,6316,6318,6321,6323,6325,6327,6330,6333,6335,6338,6340,6342,6345,6347,6349,6352,6354],{"class":98,"line":132},[96,6271,6272],{"class":2285},"        const",[96,6274,6275],{"class":128}," count",[96,6277,3199],{"class":106},[96,6279,6280],{"class":2331}," await",[96,6282,6283],{"class":2295}," $content",[96,6285,2299],{"class":102},[96,6287,6288],{"class":106},"{",[96,6290,6291],{"class":102}," deep",[96,6293,107],{"class":106},[96,6295,6296],{"class":260}," true",[96,6298,3027],{"class":106},[96,6300,2309],{"class":102},[96,6302,2312],{"class":106},[96,6304,6305],{"class":2295},"only",[96,6307,2299],{"class":102},[96,6309,1964],{"class":106},[96,6311,4955],{"class":113},[96,6313,1964],{"class":106},[96,6315,2309],{"class":102},[96,6317,2312],{"class":106},[96,6319,6320],{"class":2295},"where",[96,6322,2299],{"class":102},[96,6324,6288],{"class":106},[96,6326,5935],{"class":102},[96,6328,6329],{"class":106},":{",[96,6331,6332],{"class":102}," $contains",[96,6334,107],{"class":106},[96,6336,6337],{"class":128},"params",[96,6339,2312],{"class":106},[96,6341,5126],{"class":128},[96,6343,6344],{"class":106},"}}",[96,6346,2309],{"class":102},[96,6348,2312],{"class":106},[96,6350,6351],{"class":2295},"fetch",[96,6353,2318],{"class":102},[96,6355,2326],{"class":106},[96,6357,6358],{"class":98,"line":141},[96,6359,622],{"emptyLinePlaceholder":621},[96,6361,6362,6364,6367,6369,6371,6373,6375,6377,6379,6381,6383,6385],{"class":98,"line":152},[96,6363,6272],{"class":2285},[96,6365,6366],{"class":128}," content",[96,6368,3199],{"class":106},[96,6370,6280],{"class":2331},[96,6372,6283],{"class":2295},[96,6374,2299],{"class":102},[96,6376,6288],{"class":106},[96,6378,6291],{"class":102},[96,6380,107],{"class":106},[96,6382,6296],{"class":260},[96,6384,3027],{"class":106},[96,6386,3477],{"class":102},[96,6388,6389,6392,6394,6397,6399,6401,6403,6405,6407,6409,6411,6413,6415,6417,6419,6421,6423,6426,6428,6430,6432,6434,6436,6438,6440,6442,6444,6446,6448,6451,6453,6455,6457,6459,6461,6463,6465,6467,6469],{"class":98,"line":162},[96,6390,6391],{"class":106},"        .",[96,6393,6305],{"class":2295},[96,6395,6396],{"class":102},"([",[96,6398,1964],{"class":106},[96,6400,4955],{"class":113},[96,6402,1964],{"class":106},[96,6404,3016],{"class":106},[96,6406,1964],{"class":106},[96,6408,4965],{"class":113},[96,6410,1964],{"class":106},[96,6412,3016],{"class":106},[96,6414,1964],{"class":106},[96,6416,5045],{"class":113},[96,6418,1964],{"class":106},[96,6420,3016],{"class":106},[96,6422,1964],{"class":106},[96,6424,6425],{"class":113},"path",[96,6427,1964],{"class":106},[96,6429,3016],{"class":106},[96,6431,1964],{"class":106},[96,6433,4975],{"class":113},[96,6435,1964],{"class":106},[96,6437,3016],{"class":106},[96,6439,1964],{"class":106},[96,6441,4989],{"class":113},[96,6443,1964],{"class":106},[96,6445,3016],{"class":106},[96,6447,1964],{"class":106},[96,6449,6450],{"class":113},"updatedAt",[96,6452,1964],{"class":106},[96,6454,3016],{"class":106},[96,6456,1964],{"class":106},[96,6458,5006],{"class":113},[96,6460,1964],{"class":106},[96,6462,3016],{"class":106},[96,6464,1964],{"class":106},[96,6466,5026],{"class":113},[96,6468,1964],{"class":106},[96,6470,6471],{"class":102},"])\n",[96,6473,6474,6476,6479,6481,6483,6486,6488,6490,6492,6495,6497],{"class":98,"line":171},[96,6475,6391],{"class":106},[96,6477,6478],{"class":2295},"sortBy",[96,6480,2299],{"class":102},[96,6482,1964],{"class":106},[96,6484,6485],{"class":113},"createdAt",[96,6487,1964],{"class":106},[96,6489,3016],{"class":106},[96,6491,110],{"class":106},[96,6493,6494],{"class":113},"desc",[96,6496,1964],{"class":106},[96,6498,3477],{"class":102},[96,6500,6501,6503,6505,6507,6509,6511,6513,6515,6517,6519,6521,6523,6525,6527],{"class":98,"line":181},[96,6502,6391],{"class":106},[96,6504,6320],{"class":2295},[96,6506,2299],{"class":102},[96,6508,6288],{"class":106},[96,6510,4989],{"class":102},[96,6512,6329],{"class":106},[96,6514,6332],{"class":102},[96,6516,107],{"class":106},[96,6518,6337],{"class":128},[96,6520,2312],{"class":106},[96,6522,5126],{"class":128},[96,6524,6344],{"class":106},[96,6526,4084],{"class":102},[96,6528,6529],{"class":4284},"\u002F\u002F ここ\n",[96,6531,6532,6534,6537,6539,6542,6544,6546,6549,6551,6553,6555,6557,6559,6562],{"class":98,"line":4},[96,6533,6391],{"class":106},[96,6535,6536],{"class":2295},"skip",[96,6538,2299],{"class":102},[96,6540,6541],{"class":4452},"0",[96,6543,2309],{"class":102},[96,6545,2312],{"class":106},[96,6547,6548],{"class":2295},"limit",[96,6550,2299],{"class":102},[96,6552,4073],{"class":128},[96,6554,2312],{"class":106},[96,6556,3988],{"class":128},[96,6558,2312],{"class":106},[96,6560,6561],{"class":128},"indexPerPage",[96,6563,3477],{"class":102},[96,6565,6566,6568,6570,6572],{"class":98,"line":196},[96,6567,6391],{"class":106},[96,6569,6351],{"class":2295},[96,6571,2318],{"class":102},[96,6573,2326],{"class":106},[96,6575,6576],{"class":98,"line":204},[96,6577,622],{"emptyLinePlaceholder":621},[96,6579,6580,6582],{"class":98,"line":214},[96,6581,3074],{"class":2331},[96,6583,2146],{"class":106},[96,6585,6586,6589],{"class":98,"line":228},[96,6587,6588],{"class":106},"`",[96,6590,6591],{"class":102},"        content,\n",[96,6593,6594,6597],{"class":98,"line":240},[96,6595,6596],{"class":102},"        count:count.length",[96,6598,6599],{"class":106},"`\n",[96,6601,6602],{"class":98,"line":252},[96,6603,1376],{"class":102},[96,6605,6606],{"class":98,"line":264},[96,6607,3130],{"class":102},[96,6609,6610],{"class":98,"line":275},[96,6611,987],{"class":102},[13,6613,6614,6615,6618,6619,6622,6623,6626],{},"まずしっかりと記事のデータとして",[54,6616,6617],{},"'tag'","を指定して、",[54,6620,6621],{},".where({tag:{ $contains:params.slug}})","を使用します。",[54,6624,6625],{},"params.slug","からURLのスラグを取得して、それが配列内に含まれているかを確かめています。その条件に合致した投稿、すなわち目的のタグを持っている記事のみを取得することができます。",[13,6628,6629],{},"後は一覧ページの実装で説明した通りにテンプレートを記述するなり、ページングを実装すれば完了です。",[34,6631,6632],{"id":6632},"静的書き出しの時はどうなるのか",[13,6634,6635,6636,6639,6640,6642,6643,6645],{},"このカテゴリーのルートはNuxtが知らないので、nuxt.config.jsで追加ルートとして記述しておく必要があります。ただし、私のサイトのようにサイドメニューにずっとリンクがある場合はとくに対策はしなくてもいいです。Nuxtの静的書き出しは",[54,6637,6638],{},"nuxt-link","を辿ってルートを解決しているそうで、自動的にタグ・カテゴリーのルートを書き出してくれました。なので",[54,6641,6638],{},"でタグのリンクを貼って、",[54,6644,6202],{}," の構成が正しければ特に心配ありません。",[34,6647,6649],{"id":6648},"以上でカテゴリータグ機能を実装","以上でカテゴリー。タグ機能を実装",[13,6651,6652],{},"前回までの記事詳細→一覧の作成と似ていましたが、",[3739,6654,6655,6658,6664],{},[3742,6656,6657],{},"記事に新しいメタデータを設定",[3742,6659,6660,6663],{},[54,6661,6662],{},"where()","で特定のタグ・カテゴリーを取得する",[3742,6665,6666],{},"store・外部ファイルで管理する",[13,6668,6669],{},"という点で少し違っていました。今回はカテゴリー・タグといったものですが使い方によってはいろんな分類方法があると思います。ぜひチャレンジしてみてください。次回は最後で、ブログとしてデプロイする前に設定すべきこと、アナリティクスやアドセンスの貼り方について解説します。",[3795,6671,6672],{},"html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}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 pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}",{"title":92,"searchDepth":132,"depth":132,"links":6674},[6675,6676,6677,6678,6679,6680,6681],{"id":4924,"depth":120,"text":4924},{"id":4936,"depth":120,"text":4937},{"id":5073,"depth":120,"text":5074},{"id":6185,"depth":120,"text":6186},{"id":6219,"depth":120,"text":6220},{"id":6632,"depth":120,"text":6632},{"id":6648,"depth":120,"text":6649},[3836],"2021-05-09","Nuxt Content × SSG で作る静的ブログ。カテゴリーとタグ機能",{},"\u002Fseries\u002Fnuxt-content-blog-4",{"title":4907,"description":6684},"nuxt-content-blog","Nuxt Content × SSG で作る静的ブログ。","series\u002Fnuxt-content-blog-4",[4902,3845],"_mix\u002Flogo-dark.jpg","SRildu2cH6w8PGbcMmWirBzP0VR8lSIzk3eFtii40vY",{"id":6695,"title":6696,"body":6697,"category":8730,"createdAt":8731,"description":8732,"extension":3838,"index":132,"meta":8733,"navigation":621,"path":8734,"publish":621,"seo":8735,"series":6688,"seriesTitle":6689,"stem":8736,"tag":8737,"thumbnail":6692,"updatedAt":3839,"__hash__":8738},"series\u002Fseries\u002Fnuxt-content-blog-3.md","Nuxt Content × SSG で作る静的ブログ。３：記事一覧ページとページング実装",{"type":10,"value":6698,"toc":8717},[6699,6705,6713,6716,6719,6722,6725,6731,6746,6752,6761,6764,6773,7105,7130,7144,7158,7172,7175,7178,7187,7193,7203,8366,8369,8373,8393,8396,8452,8457,8460,8463,8471,8474,8658,8663,8666,8671,8682,8685,8689,8699,8705,8708,8711,8714],[13,6700,4912,6701,6704],{},[76,6702,4916],{"href":6703},"\u002Fseries\u002Fnuxt-content-blog\u002F2","は詳細ページの実装と静的書き出しを行いました。今回の記事では",[3739,6706,6707,6710],{},[3742,6708,6709],{},"記事一覧ページの作成",[3742,6711,6712],{},"ページング機能",[13,6714,6715],{},"について解説していきます。それでは早速いきましょう。",[34,6717,6718],{"id":6718},"articlesの一覧ページを作る",[70,6720,6721],{"id":6721},"pagesディレクトリを設定する",[13,6723,6724],{},"詳細ページのルーティングを作るために以前は以下のようなディレクトリの設定をしました。",[86,6726,6729],{"className":6727,"code":6728,"language":460},[457],"├── pages\n│   ├── articles\n│   　   ├── _slug.vue\n",[54,6730,6728],{"__ignoreMap":92},[13,6732,6733,6734,6737,6738,6741,6742,6745],{},"この場合、",[54,6735,6736],{},"\u002Farticles\u002F{sulg}","というURLが有効になります。一覧ページは",[54,6739,6740],{},"\u002Farticles\u002F","というルートで",[54,6743,6744],{},"content\u002Farticles\u002F","配下の原稿が一覧となって見れるページです。しかし上記の設定では表示されないので以下のようにします。",[86,6747,6750],{"className":6748,"code":6749,"language":460},[457],"├── pages\n│   ├── articles\n│   　   ├── index.vue #追加\n│   　   ├── _slug.vue\n",[54,6751,6749],{"__ignoreMap":92},[13,6753,6754,6757,6758,6760],{},[54,6755,6756],{},"index.vue","というものを足しました。このファイルは",[54,6759,6740],{},"というルートに対応しています。ディレクトリの設定は以上となります。",[70,6762,6763],{"id":6763},"一覧のデータを取得してレンダーする",[13,6765,6223,6766,6768,6769,6772],{},[54,6767,6756],{},"にソースを書いていきましょう。詳細ページでは特定のパスに対応するコンテンツを取得していましたが、一覧ページでは",[54,6770,6771],{},"articles","のコンテンツを15件ほど取得するようにしましょう。とりあえずソースを載せます。",[86,6774,6777],{"className":2443,"code":6775,"filename":6776,"language":2446,"meta":92,"style":92},"\u003Ctemplate>\n    \u003Cdiv class=\"\">\n        \u003Ch1>記事一覧\u003C\u002Fh1>\n        \u003Cul>\n        \u003Cli v-for=\"(c,index) in content\" :key=\"index\">\n            \u003Cnuxt-link :to=\"c.path\">{{c.title}}\u003C\u002Fnuxt-link>\n        \u003C\u002Fli>\n        \u003C\u002Ful>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  async asyncData({ store,$content, params }) {\n    const content = await $content('articles')\n    .only(['title','path'])\n    .sortBy('createdAt', 'desc')\n    .skip(0).limit(15)\n    .fetch();\n\n    return {\n      content,\n    }\n  }\n}\n\u003C\u002Fscript>\n","page\u002Farticles\u002Findex.vue",[54,6778,6779,6787,6802,6820,6828,6859,6889,6897,6905,6913,6921,6925,6933,6941,6963,6986,7010,7034,7057,7067,7071,7078,7085,7089,7093,7097],{"__ignoreMap":92},[96,6780,6781,6783,6785],{"class":98,"line":99},[96,6782,2453],{"class":106},[96,6784,2456],{"class":102},[96,6786,2459],{"class":106},[96,6788,6789,6791,6793,6795,6797,6800],{"class":98,"line":120},[96,6790,2464],{"class":106},[96,6792,4138],{"class":102},[96,6794,6145],{"class":2285},[96,6796,2292],{"class":106},[96,6798,6799],{"class":106},"\"\"",[96,6801,2459],{"class":106},[96,6803,6804,6806,6809,6811,6814,6816,6818],{"class":98,"line":132},[96,6805,2602],{"class":106},[96,6807,6808],{"class":102},"h1",[96,6810,2635],{"class":106},[96,6812,6813],{"class":128},"記事一覧",[96,6815,2641],{"class":106},[96,6817,6808],{"class":102},[96,6819,2459],{"class":106},[96,6821,6822,6824,6826],{"class":98,"line":141},[96,6823,2602],{"class":106},[96,6825,3739],{"class":102},[96,6827,2459],{"class":106},[96,6829,6830,6832,6834,6837,6839,6841,6844,6846,6849,6851,6853,6855,6857],{"class":98,"line":152},[96,6831,2602],{"class":106},[96,6833,3742],{"class":102},[96,6835,6836],{"class":2285}," v-for",[96,6838,2292],{"class":106},[96,6840,2477],{"class":106},[96,6842,6843],{"class":113},"(c,index) in content",[96,6845,2477],{"class":106},[96,6847,6848],{"class":2285}," :key",[96,6850,2292],{"class":106},[96,6852,2477],{"class":106},[96,6854,5026],{"class":113},[96,6856,2477],{"class":106},[96,6858,2459],{"class":106},[96,6860,6861,6864,6866,6869,6871,6873,6876,6878,6880,6883,6885,6887],{"class":98,"line":162},[96,6862,6863],{"class":106},"            \u003C",[96,6865,6638],{"class":102},[96,6867,6868],{"class":2285}," :to",[96,6870,2292],{"class":106},[96,6872,2477],{"class":106},[96,6874,6875],{"class":113},"c.path",[96,6877,2477],{"class":106},[96,6879,2635],{"class":106},[96,6881,6882],{"class":128},"{{c.title}}",[96,6884,2641],{"class":106},[96,6886,6638],{"class":102},[96,6888,2459],{"class":106},[96,6890,6891,6893,6895],{"class":98,"line":171},[96,6892,2790],{"class":106},[96,6894,3742],{"class":102},[96,6896,2459],{"class":106},[96,6898,6899,6901,6903],{"class":98,"line":181},[96,6900,2790],{"class":106},[96,6902,3739],{"class":102},[96,6904,2459],{"class":106},[96,6906,6907,6909,6911],{"class":98,"line":4},[96,6908,2976],{"class":106},[96,6910,4138],{"class":102},[96,6912,2459],{"class":106},[96,6914,6915,6917,6919],{"class":98,"line":196},[96,6916,2641],{"class":106},[96,6918,2456],{"class":102},[96,6920,2459],{"class":106},[96,6922,6923],{"class":98,"line":204},[96,6924,622],{"emptyLinePlaceholder":621},[96,6926,6927,6929,6931],{"class":98,"line":214},[96,6928,2453],{"class":106},[96,6930,3000],{"class":102},[96,6932,2459],{"class":106},[96,6934,6935,6937,6939],{"class":98,"line":228},[96,6936,2332],{"class":2331},[96,6938,2335],{"class":2331},[96,6940,2146],{"class":106},[96,6942,6943,6945,6947,6949,6951,6953,6955,6957,6959,6961],{"class":98,"line":240},[96,6944,4532],{"class":2285},[96,6946,6249],{"class":102},[96,6948,4538],{"class":106},[96,6950,4050],{"class":3329},[96,6952,3016],{"class":106},[96,6954,6258],{"class":3329},[96,6956,3016],{"class":106},[96,6958,6263],{"class":3329},[96,6960,4058],{"class":106},[96,6962,2146],{"class":106},[96,6964,6965,6968,6970,6972,6974,6976,6978,6980,6982,6984],{"class":98,"line":252},[96,6966,6967],{"class":2285},"    const",[96,6969,6366],{"class":128},[96,6971,3199],{"class":106},[96,6973,6280],{"class":2331},[96,6975,6283],{"class":2295},[96,6977,2299],{"class":102},[96,6979,1964],{"class":106},[96,6981,6771],{"class":113},[96,6983,1964],{"class":106},[96,6985,3477],{"class":102},[96,6987,6988,6990,6992,6994,6996,6998,7000,7002,7004,7006,7008],{"class":98,"line":264},[96,6989,4580],{"class":106},[96,6991,6305],{"class":2295},[96,6993,6396],{"class":102},[96,6995,1964],{"class":106},[96,6997,4955],{"class":113},[96,6999,1964],{"class":106},[96,7001,3016],{"class":106},[96,7003,1964],{"class":106},[96,7005,6425],{"class":113},[96,7007,1964],{"class":106},[96,7009,6471],{"class":102},[96,7011,7012,7014,7016,7018,7020,7022,7024,7026,7028,7030,7032],{"class":98,"line":275},[96,7013,4580],{"class":106},[96,7015,6478],{"class":2295},[96,7017,2299],{"class":102},[96,7019,1964],{"class":106},[96,7021,6485],{"class":113},[96,7023,1964],{"class":106},[96,7025,3016],{"class":106},[96,7027,110],{"class":106},[96,7029,6494],{"class":113},[96,7031,1964],{"class":106},[96,7033,3477],{"class":102},[96,7035,7036,7038,7040,7042,7044,7046,7048,7050,7052,7055],{"class":98,"line":283},[96,7037,4580],{"class":106},[96,7039,6536],{"class":2295},[96,7041,2299],{"class":102},[96,7043,6541],{"class":4452},[96,7045,2309],{"class":102},[96,7047,2312],{"class":106},[96,7049,6548],{"class":2295},[96,7051,2299],{"class":102},[96,7053,7054],{"class":4452},"15",[96,7056,3477],{"class":102},[96,7058,7059,7061,7063,7065],{"class":98,"line":293},[96,7060,4580],{"class":106},[96,7062,6351],{"class":2295},[96,7064,2318],{"class":102},[96,7066,2326],{"class":106},[96,7068,7069],{"class":98,"line":301},[96,7070,622],{"emptyLinePlaceholder":621},[96,7072,7073,7076],{"class":98,"line":312},[96,7074,7075],{"class":2331},"    return",[96,7077,2146],{"class":106},[96,7079,7080,7083],{"class":98,"line":323},[96,7081,7082],{"class":128},"      content",[96,7084,1940],{"class":106},[96,7086,7087],{"class":98,"line":334},[96,7088,954],{"class":106},[96,7090,7091],{"class":98,"line":345},[96,7092,4680],{"class":106},[96,7094,7095],{"class":98,"line":354},[96,7096,987],{"class":106},[96,7098,7099,7101,7103],{"class":98,"line":366},[96,7100,2641],{"class":106},[96,7102,3000],{"class":102},[96,7104,2459],{"class":106},[13,7106,7107,7108,7111,7112,7114,7115,7118,7119,7122,7123,7126,7127,7129],{},"まずは",[54,7109,7110],{},"$content('articles')","で",[54,7113,6771],{},"配下のコンテンツを読む指定をします。そして",[54,7116,7117],{},".only(['title','path'])","を使用することでtitleとpathのみのデータを取得することができます。この",[54,7120,7121],{},"only()","を指定しない場合、",[54,7124,7125],{},"body","プロパティという原稿内容も取得してしまいます。原稿がボリューミーなほど取得コストが大きくなり、静的書き出しなどにも影響されます。そのため一覧などでは",[54,7128,7121],{},"を使用して必要最低限のプロパティを使用した方がいいです。",[13,7131,7132,7135,7136,7139,7140,7143],{},[54,7133,7134],{},"sortBy()","にて特定プロパティでソートし、後のページングで使いますが",[54,7137,7138],{},".skip(0).limit(15)","にて15件の記事を取得します。",[54,7141,7142],{},"asyncData()","内で記事を取得して、それをリストで出力します。",[4138,7145,7149,7152,7153,7157],{"className":7146},[7147,7148],"alert","alert-info",[54,7150,7151],{},"$content()","に対して加えることが可能なメソッドは",[76,7154,79],{"href":7155,"target":7156},"https:\u002F\u002Fcontent.nuxtjs.org\u002Fja\u002Ffetching","_blank","で確認できます。\n",[13,7159,7160,7161,6622,7164,7167,7168,7171],{},"Nuxt.jsで内部リンクを作成する時は",[54,7162,7163],{},"\u003Cnuxt-link>",[54,7165,7166],{},"to","に",[54,7169,7170],{},"content.path","を指定することで詳細ページに移動できるようになります。",[13,7173,7174],{},"一覧ページはこれぐらいで実装できます。今は最初から15件しか取得しないので、大量にある時はページングができるようにしましょう。",[34,7176,7177],{"id":7177},"ページングを実装する",[13,7179,7180,7181,7184,7185,6203],{},"私のサイトでは",[54,7182,7183],{},"\u002Farticles\u002Fpage\u002F2","の様なルートで対応しています。この様なルートを設定する場合は以下のように",[54,7186,6202],{},[86,7188,7191],{"className":7189,"code":7190,"language":460},[457],"├── pages\n│   ├── articles\n│   　   ├── index.vue \n│   　   ├── _slug.vue\n│   　   ├── pages          #追加\n│   　        ├──_id.vue    #追加\n",[54,7192,7190],{"__ignoreMap":92},[13,7194,7195,7198,7199,7202],{},[54,7196,7197],{},"_id.vue","を作成することで",[54,7200,7201],{},"\u002Farticles\u002Fpage\u002F{n}","という動的ルートが作成されます。そこでは以下のように設定します。",[86,7204,7207],{"className":2443,"code":7205,"filename":7206,"language":2446,"meta":92,"style":92},"\u003Ctemplate>\n    \u003Cdiv class=\"\">\n        \u003Ch1>記事一覧\u003C\u002Fh1>\n        \u003Cul>\n            \u003Cli v-for=\"(c,index) in content\" :key=\"index\">\n                \u003Cnuxt-link :to=\"c.path\">{{c.title}}\u003C\u002Fnuxt-link>\n            \u003C\u002Fli>\n        \u003C\u002Ful>\n        \u003Cul class=\"p-pagenation-container\">\n            \u003Cli class=\"c-pagenation-unit\" v-for=\"(pg) in num\" :key=\"pg.num\">\n                \u003Cnuxt-link v-if=\"pg.pg\" :to=\"'\u002Farticles\u002Fpage\u002F'+pg.num\" :class=\"(current == pg.num)?'is-current':''\">\n                    {{pg.num}}\n                \u003C\u002Fnuxt-link>\n                \u003Cspan v-else>\n                    {{pg.num}}\n                \u003C\u002Fspan>\n            \u003C\u002Fli>\n        \u003C\u002Ful>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n    validate({ redirect,params }) {\n        if(\u002F[0-9]+\u002F.test(params.id)) return true;\n        return redirect('\u002Farticles')\n    },\n    async asyncData({ store,$content, params }) {\n        const count = await $content('articles').only('title').fetch();\n        const current = params.id;\n        if(current > Math.ceil( count.length \u002F store.state.indexPerPage)) redirect('\u002Farticles');\n\n        const from = store.state.indexPerPage * (params.id - 1);\n        const to = store.state.indexPerPage * params.id;\n\n        const content = await $content('articles')\n        .only(['title','path'])\n        .sortBy('createdAt', 'desc')\n        .skip(from).limit(to)\n        .fetch();\n\n        return {\n            content,\n            current,\n            count:count.length\n        }\n    },\n    computed:{\n        max(){\n            return Math.ceil( this.$route.params.id \u002F 15);\n        },\n        num(){\n            let tmp = [];\n            for(let n=1; n\u003C=this.max;n++){\n                if(n == 1 || n == this.max){\n                    tmp.push({pg:true,num:n});\n                    continue;\n                }\n                if((this.current - 2 \u003C= n) && (n \u003C= this.current + 2)){\n                    tmp.push({pg:true,num:n})\n                    continue;\n                }\n                if((this.current - 2 - 1 == n) || (n == this.current + 2 + 1) ){\n                   tmp.push({pg:false,num:\"...\"})\n                    continue;\n                }\n            }\n            return tmp;\n        }\n    },\n}\n\u003C\u002Fscript>\n","page\u002Farticles\u002Fpage\u002F_id.vue",[54,7208,7209,7217,7231,7247,7255,7283,7310,7319,7327,7346,7387,7429,7434,7443,7454,7458,7466,7474,7482,7490,7498,7502,7510,7518,7535,7574,7591,7595,7617,7661,7678,7738,7742,7780,7809,7813,7835,7859,7883,7905,7915,7919,7925,7932,7939,7953,7957,7961,7968,7975,8009,8013,8020,8035,8075,8104,8140,8147,8152,8198,8228,8234,8238,8288,8324,8330,8334,8338,8346,8350,8354,8358],{"__ignoreMap":92},[96,7210,7211,7213,7215],{"class":98,"line":99},[96,7212,2453],{"class":106},[96,7214,2456],{"class":102},[96,7216,2459],{"class":106},[96,7218,7219,7221,7223,7225,7227,7229],{"class":98,"line":120},[96,7220,2464],{"class":106},[96,7222,4138],{"class":102},[96,7224,6145],{"class":2285},[96,7226,2292],{"class":106},[96,7228,6799],{"class":106},[96,7230,2459],{"class":106},[96,7232,7233,7235,7237,7239,7241,7243,7245],{"class":98,"line":132},[96,7234,2602],{"class":106},[96,7236,6808],{"class":102},[96,7238,2635],{"class":106},[96,7240,6813],{"class":128},[96,7242,2641],{"class":106},[96,7244,6808],{"class":102},[96,7246,2459],{"class":106},[96,7248,7249,7251,7253],{"class":98,"line":141},[96,7250,2602],{"class":106},[96,7252,3739],{"class":102},[96,7254,2459],{"class":106},[96,7256,7257,7259,7261,7263,7265,7267,7269,7271,7273,7275,7277,7279,7281],{"class":98,"line":152},[96,7258,6863],{"class":106},[96,7260,3742],{"class":102},[96,7262,6836],{"class":2285},[96,7264,2292],{"class":106},[96,7266,2477],{"class":106},[96,7268,6843],{"class":113},[96,7270,2477],{"class":106},[96,7272,6848],{"class":2285},[96,7274,2292],{"class":106},[96,7276,2477],{"class":106},[96,7278,5026],{"class":113},[96,7280,2477],{"class":106},[96,7282,2459],{"class":106},[96,7284,7285,7288,7290,7292,7294,7296,7298,7300,7302,7304,7306,7308],{"class":98,"line":162},[96,7286,7287],{"class":106},"                \u003C",[96,7289,6638],{"class":102},[96,7291,6868],{"class":2285},[96,7293,2292],{"class":106},[96,7295,2477],{"class":106},[96,7297,6875],{"class":113},[96,7299,2477],{"class":106},[96,7301,2635],{"class":106},[96,7303,6882],{"class":128},[96,7305,2641],{"class":106},[96,7307,6638],{"class":102},[96,7309,2459],{"class":106},[96,7311,7312,7315,7317],{"class":98,"line":171},[96,7313,7314],{"class":106},"            \u003C\u002F",[96,7316,3742],{"class":102},[96,7318,2459],{"class":106},[96,7320,7321,7323,7325],{"class":98,"line":181},[96,7322,2790],{"class":106},[96,7324,3739],{"class":102},[96,7326,2459],{"class":106},[96,7328,7329,7331,7333,7335,7337,7339,7342,7344],{"class":98,"line":4},[96,7330,2602],{"class":106},[96,7332,3739],{"class":102},[96,7334,6145],{"class":2285},[96,7336,2292],{"class":106},[96,7338,2477],{"class":106},[96,7340,7341],{"class":113},"p-pagenation-container",[96,7343,2477],{"class":106},[96,7345,2459],{"class":106},[96,7347,7348,7350,7352,7354,7356,7358,7361,7363,7365,7367,7369,7372,7374,7376,7378,7380,7383,7385],{"class":98,"line":196},[96,7349,6863],{"class":106},[96,7351,3742],{"class":102},[96,7353,6145],{"class":2285},[96,7355,2292],{"class":106},[96,7357,2477],{"class":106},[96,7359,7360],{"class":113},"c-pagenation-unit",[96,7362,2477],{"class":106},[96,7364,6836],{"class":2285},[96,7366,2292],{"class":106},[96,7368,2477],{"class":106},[96,7370,7371],{"class":113},"(pg) in num",[96,7373,2477],{"class":106},[96,7375,6848],{"class":2285},[96,7377,2292],{"class":106},[96,7379,2477],{"class":106},[96,7381,7382],{"class":113},"pg.num",[96,7384,2477],{"class":106},[96,7386,2459],{"class":106},[96,7388,7389,7391,7393,7395,7397,7399,7402,7404,7406,7408,7410,7413,7415,7418,7420,7422,7425,7427],{"class":98,"line":204},[96,7390,7287],{"class":106},[96,7392,6638],{"class":102},[96,7394,2608],{"class":2285},[96,7396,2292],{"class":106},[96,7398,2477],{"class":106},[96,7400,7401],{"class":113},"pg.pg",[96,7403,2477],{"class":106},[96,7405,6868],{"class":2285},[96,7407,2292],{"class":106},[96,7409,2477],{"class":106},[96,7411,7412],{"class":113},"'\u002Farticles\u002Fpage\u002F'+pg.num",[96,7414,2477],{"class":106},[96,7416,7417],{"class":2285}," :class",[96,7419,2292],{"class":106},[96,7421,2477],{"class":106},[96,7423,7424],{"class":113},"(current == pg.num)?'is-current':''",[96,7426,2477],{"class":106},[96,7428,2459],{"class":106},[96,7430,7431],{"class":98,"line":214},[96,7432,7433],{"class":128},"                    {{pg.num}}\n",[96,7435,7436,7439,7441],{"class":98,"line":228},[96,7437,7438],{"class":106},"                \u003C\u002F",[96,7440,6638],{"class":102},[96,7442,2459],{"class":106},[96,7444,7445,7447,7449,7452],{"class":98,"line":240},[96,7446,7287],{"class":106},[96,7448,96],{"class":102},[96,7450,7451],{"class":2285}," v-else",[96,7453,2459],{"class":106},[96,7455,7456],{"class":98,"line":252},[96,7457,7433],{"class":128},[96,7459,7460,7462,7464],{"class":98,"line":264},[96,7461,7438],{"class":106},[96,7463,96],{"class":102},[96,7465,2459],{"class":106},[96,7467,7468,7470,7472],{"class":98,"line":275},[96,7469,7314],{"class":106},[96,7471,3742],{"class":102},[96,7473,2459],{"class":106},[96,7475,7476,7478,7480],{"class":98,"line":283},[96,7477,2790],{"class":106},[96,7479,3739],{"class":102},[96,7481,2459],{"class":106},[96,7483,7484,7486,7488],{"class":98,"line":293},[96,7485,2976],{"class":106},[96,7487,4138],{"class":102},[96,7489,2459],{"class":106},[96,7491,7492,7494,7496],{"class":98,"line":301},[96,7493,2641],{"class":106},[96,7495,2456],{"class":102},[96,7497,2459],{"class":106},[96,7499,7500],{"class":98,"line":312},[96,7501,622],{"emptyLinePlaceholder":621},[96,7503,7504,7506,7508],{"class":98,"line":323},[96,7505,2453],{"class":106},[96,7507,3000],{"class":102},[96,7509,2459],{"class":106},[96,7511,7512,7514,7516],{"class":98,"line":334},[96,7513,2332],{"class":2331},[96,7515,2335],{"class":2331},[96,7517,2146],{"class":106},[96,7519,7520,7523,7525,7527,7529,7531,7533],{"class":98,"line":345},[96,7521,7522],{"class":102},"    validate",[96,7524,4538],{"class":106},[96,7526,4055],{"class":3329},[96,7528,3016],{"class":106},[96,7530,6337],{"class":3329},[96,7532,4058],{"class":106},[96,7534,2146],{"class":106},[96,7536,7537,7540,7542,7545,7548,7551,7553,7556,7558,7560,7562,7565,7568,7570,7572],{"class":98,"line":354},[96,7538,7539],{"class":2331},"        if",[96,7541,2299],{"class":102},[96,7543,7544],{"class":106},"\u002F[",[96,7546,7547],{"class":113},"0-9",[96,7549,7550],{"class":106},"]+\u002F",[96,7552,2312],{"class":106},[96,7554,7555],{"class":2295},"test",[96,7557,2299],{"class":102},[96,7559,6337],{"class":128},[96,7561,2312],{"class":106},[96,7563,7564],{"class":128},"id",[96,7566,7567],{"class":102},")) ",[96,7569,3396],{"class":2331},[96,7571,6296],{"class":260},[96,7573,2326],{"class":106},[96,7575,7576,7578,7580,7582,7584,7587,7589],{"class":98,"line":366},[96,7577,3074],{"class":2331},[96,7579,4055],{"class":2295},[96,7581,2299],{"class":102},[96,7583,1964],{"class":106},[96,7585,7586],{"class":113},"\u002Farticles",[96,7588,1964],{"class":106},[96,7590,3477],{"class":102},[96,7592,7593],{"class":98,"line":375},[96,7594,3130],{"class":106},[96,7596,7597,7599,7601,7603,7605,7607,7609,7611,7613,7615],{"class":98,"line":383},[96,7598,6246],{"class":2285},[96,7600,6249],{"class":102},[96,7602,4538],{"class":106},[96,7604,4050],{"class":3329},[96,7606,3016],{"class":106},[96,7608,6258],{"class":3329},[96,7610,3016],{"class":106},[96,7612,6263],{"class":3329},[96,7614,4058],{"class":106},[96,7616,2146],{"class":106},[96,7618,7619,7621,7623,7625,7627,7629,7631,7633,7635,7637,7639,7641,7643,7645,7647,7649,7651,7653,7655,7657,7659],{"class":98,"line":393},[96,7620,6272],{"class":2285},[96,7622,6275],{"class":128},[96,7624,3199],{"class":106},[96,7626,6280],{"class":2331},[96,7628,6283],{"class":2295},[96,7630,2299],{"class":102},[96,7632,1964],{"class":106},[96,7634,6771],{"class":113},[96,7636,1964],{"class":106},[96,7638,2309],{"class":102},[96,7640,2312],{"class":106},[96,7642,6305],{"class":2295},[96,7644,2299],{"class":102},[96,7646,1964],{"class":106},[96,7648,4955],{"class":113},[96,7650,1964],{"class":106},[96,7652,2309],{"class":102},[96,7654,2312],{"class":106},[96,7656,6351],{"class":2295},[96,7658,2318],{"class":102},[96,7660,2326],{"class":106},[96,7662,7663,7665,7668,7670,7672,7674,7676],{"class":98,"line":875},[96,7664,6272],{"class":2285},[96,7666,7667],{"class":128}," current",[96,7669,3199],{"class":106},[96,7671,6263],{"class":128},[96,7673,2312],{"class":106},[96,7675,7564],{"class":128},[96,7677,2326],{"class":106},[96,7679,7680,7682,7684,7687,7689,7692,7694,7697,7700,7703,7705,7708,7711,7713,7715,7717,7719,7721,7723,7726,7728,7730,7732,7734,7736],{"class":98,"line":880},[96,7681,7539],{"class":2331},[96,7683,2299],{"class":102},[96,7685,7686],{"class":128},"current",[96,7688,5965],{"class":106},[96,7690,7691],{"class":128}," Math",[96,7693,2312],{"class":106},[96,7695,7696],{"class":2295},"ceil",[96,7698,7699],{"class":102},"( ",[96,7701,7702],{"class":128},"count",[96,7704,2312],{"class":106},[96,7706,7707],{"class":128},"length",[96,7709,7710],{"class":106}," \u002F",[96,7712,4050],{"class":128},[96,7714,2312],{"class":106},[96,7716,3988],{"class":128},[96,7718,2312],{"class":106},[96,7720,6561],{"class":128},[96,7722,7567],{"class":102},[96,7724,7725],{"class":2295},"redirect",[96,7727,2299],{"class":102},[96,7729,1964],{"class":106},[96,7731,7586],{"class":113},[96,7733,1964],{"class":106},[96,7735,2309],{"class":102},[96,7737,2326],{"class":106},[96,7739,7740],{"class":98,"line":885},[96,7741,622],{"emptyLinePlaceholder":621},[96,7743,7744,7746,7748,7750,7752,7754,7756,7758,7760,7763,7765,7767,7769,7771,7773,7776,7778],{"class":98,"line":890},[96,7745,6272],{"class":2285},[96,7747,3030],{"class":128},[96,7749,3199],{"class":106},[96,7751,4050],{"class":128},[96,7753,2312],{"class":106},[96,7755,3988],{"class":128},[96,7757,2312],{"class":106},[96,7759,6561],{"class":128},[96,7761,7762],{"class":106}," *",[96,7764,3939],{"class":102},[96,7766,6337],{"class":128},[96,7768,2312],{"class":106},[96,7770,7564],{"class":128},[96,7772,4449],{"class":106},[96,7774,7775],{"class":4452}," 1",[96,7777,2309],{"class":102},[96,7779,2326],{"class":106},[96,7781,7782,7784,7787,7789,7791,7793,7795,7797,7799,7801,7803,7805,7807],{"class":98,"line":896},[96,7783,6272],{"class":2285},[96,7785,7786],{"class":128}," to",[96,7788,3199],{"class":106},[96,7790,4050],{"class":128},[96,7792,2312],{"class":106},[96,7794,3988],{"class":128},[96,7796,2312],{"class":106},[96,7798,6561],{"class":128},[96,7800,7762],{"class":106},[96,7802,6263],{"class":128},[96,7804,2312],{"class":106},[96,7806,7564],{"class":128},[96,7808,2326],{"class":106},[96,7810,7811],{"class":98,"line":901},[96,7812,622],{"emptyLinePlaceholder":621},[96,7814,7815,7817,7819,7821,7823,7825,7827,7829,7831,7833],{"class":98,"line":906},[96,7816,6272],{"class":2285},[96,7818,6366],{"class":128},[96,7820,3199],{"class":106},[96,7822,6280],{"class":2331},[96,7824,6283],{"class":2295},[96,7826,2299],{"class":102},[96,7828,1964],{"class":106},[96,7830,6771],{"class":113},[96,7832,1964],{"class":106},[96,7834,3477],{"class":102},[96,7836,7837,7839,7841,7843,7845,7847,7849,7851,7853,7855,7857],{"class":98,"line":911},[96,7838,6391],{"class":106},[96,7840,6305],{"class":2295},[96,7842,6396],{"class":102},[96,7844,1964],{"class":106},[96,7846,4955],{"class":113},[96,7848,1964],{"class":106},[96,7850,3016],{"class":106},[96,7852,1964],{"class":106},[96,7854,6425],{"class":113},[96,7856,1964],{"class":106},[96,7858,6471],{"class":102},[96,7860,7861,7863,7865,7867,7869,7871,7873,7875,7877,7879,7881],{"class":98,"line":917},[96,7862,6391],{"class":106},[96,7864,6478],{"class":2295},[96,7866,2299],{"class":102},[96,7868,1964],{"class":106},[96,7870,6485],{"class":113},[96,7872,1964],{"class":106},[96,7874,3016],{"class":106},[96,7876,110],{"class":106},[96,7878,6494],{"class":113},[96,7880,1964],{"class":106},[96,7882,3477],{"class":102},[96,7884,7885,7887,7889,7891,7893,7895,7897,7899,7901,7903],{"class":98,"line":923},[96,7886,6391],{"class":106},[96,7888,6536],{"class":2295},[96,7890,2299],{"class":102},[96,7892,5772],{"class":128},[96,7894,2309],{"class":102},[96,7896,2312],{"class":106},[96,7898,6548],{"class":2295},[96,7900,2299],{"class":102},[96,7902,7166],{"class":128},[96,7904,3477],{"class":102},[96,7906,7907,7909,7911,7913],{"class":98,"line":928},[96,7908,6391],{"class":106},[96,7910,6351],{"class":2295},[96,7912,2318],{"class":102},[96,7914,2326],{"class":106},[96,7916,7917],{"class":98,"line":933},[96,7918,622],{"emptyLinePlaceholder":621},[96,7920,7921,7923],{"class":98,"line":939},[96,7922,3074],{"class":2331},[96,7924,2146],{"class":106},[96,7926,7927,7930],{"class":98,"line":945},[96,7928,7929],{"class":128},"            content",[96,7931,1940],{"class":106},[96,7933,7934,7937],{"class":98,"line":951},[96,7935,7936],{"class":128},"            current",[96,7938,1940],{"class":106},[96,7940,7941,7944,7946,7948,7950],{"class":98,"line":957},[96,7942,7943],{"class":102},"            count",[96,7945,107],{"class":106},[96,7947,7702],{"class":128},[96,7949,2312],{"class":106},[96,7951,7952],{"class":128},"length\n",[96,7954,7955],{"class":98,"line":962},[96,7956,1376],{"class":106},[96,7958,7959],{"class":98,"line":968},[96,7960,3130],{"class":106},[96,7962,7963,7966],{"class":98,"line":973},[96,7964,7965],{"class":102},"    computed",[96,7967,1927],{"class":106},[96,7969,7970,7973],{"class":98,"line":979},[96,7971,7972],{"class":102},"        max",[96,7974,3069],{"class":106},[96,7976,7977,7979,7981,7983,7985,7987,7989,7992,7994,7996,7998,8000,8002,8005,8007],{"class":98,"line":984},[96,7978,3243],{"class":2331},[96,7980,7691],{"class":128},[96,7982,2312],{"class":106},[96,7984,7696],{"class":2295},[96,7986,7699],{"class":102},[96,7988,3387],{"class":106},[96,7990,7991],{"class":128},"$route",[96,7993,2312],{"class":106},[96,7995,6337],{"class":128},[96,7997,2312],{"class":106},[96,7999,7564],{"class":128},[96,8001,7710],{"class":106},[96,8003,8004],{"class":4452}," 15",[96,8006,2309],{"class":102},[96,8008,2326],{"class":106},[96,8010,8011],{"class":98,"line":1460},[96,8012,3259],{"class":106},[96,8014,8015,8018],{"class":98,"line":1466},[96,8016,8017],{"class":102},"        num",[96,8019,3069],{"class":106},[96,8021,8022,8025,8028,8030,8033],{"class":98,"line":1471},[96,8023,8024],{"class":2285},"            let",[96,8026,8027],{"class":128}," tmp",[96,8029,3199],{"class":106},[96,8031,8032],{"class":102}," []",[96,8034,2326],{"class":106},[96,8036,8037,8040,8042,8045,8048,8050,8052,8055,8057,8060,8063,8065,8068,8071,8073],{"class":98,"line":1477},[96,8038,8039],{"class":2331},"            for",[96,8041,2299],{"class":102},[96,8043,8044],{"class":2285},"let",[96,8046,8047],{"class":128}," n",[96,8049,2292],{"class":106},[96,8051,4453],{"class":4452},[96,8053,8054],{"class":106},";",[96,8056,8047],{"class":128},[96,8058,8059],{"class":106},"\u003C=this.",[96,8061,8062],{"class":128},"max",[96,8064,8054],{"class":106},[96,8066,8067],{"class":128},"n",[96,8069,8070],{"class":106},"++",[96,8072,2309],{"class":102},[96,8074,788],{"class":106},[96,8076,8077,8080,8082,8084,8087,8089,8092,8094,8096,8098,8100,8102],{"class":98,"line":1482},[96,8078,8079],{"class":2331},"                if",[96,8081,2299],{"class":102},[96,8083,8067],{"class":128},[96,8085,8086],{"class":106}," ==",[96,8088,7775],{"class":4452},[96,8090,8091],{"class":106}," ||",[96,8093,8047],{"class":128},[96,8095,8086],{"class":106},[96,8097,3246],{"class":106},[96,8099,8062],{"class":128},[96,8101,2309],{"class":102},[96,8103,788],{"class":106},[96,8105,8106,8109,8111,8114,8116,8118,8121,8123,8125,8127,8130,8132,8134,8136,8138],{"class":98,"line":1488},[96,8107,8108],{"class":128},"                    tmp",[96,8110,2312],{"class":106},[96,8112,8113],{"class":2295},"push",[96,8115,2299],{"class":102},[96,8117,6288],{"class":106},[96,8119,8120],{"class":102},"pg",[96,8122,107],{"class":106},[96,8124,46],{"class":260},[96,8126,3016],{"class":106},[96,8128,8129],{"class":102},"num",[96,8131,107],{"class":106},[96,8133,8067],{"class":128},[96,8135,3953],{"class":106},[96,8137,2309],{"class":102},[96,8139,2326],{"class":106},[96,8141,8142,8145],{"class":98,"line":1493},[96,8143,8144],{"class":2331},"                    continue",[96,8146,2326],{"class":106},[96,8148,8149],{"class":98,"line":1498},[96,8150,8151],{"class":106},"                }\n",[96,8153,8154,8156,8159,8161,8163,8165,8168,8171,8173,8175,8178,8180,8182,8184,8186,8188,8191,8193,8196],{"class":98,"line":1503},[96,8155,8079],{"class":2331},[96,8157,8158],{"class":102},"((",[96,8160,3387],{"class":106},[96,8162,7686],{"class":128},[96,8164,4449],{"class":106},[96,8166,8167],{"class":4452}," 2",[96,8169,8170],{"class":106}," \u003C=",[96,8172,8047],{"class":128},[96,8174,4084],{"class":102},[96,8176,8177],{"class":106},"&&",[96,8179,3939],{"class":102},[96,8181,8067],{"class":128},[96,8183,8170],{"class":106},[96,8185,3246],{"class":106},[96,8187,7686],{"class":128},[96,8189,8190],{"class":106}," +",[96,8192,8167],{"class":4452},[96,8194,8195],{"class":102},"))",[96,8197,788],{"class":106},[96,8199,8200,8202,8204,8206,8208,8210,8212,8214,8216,8218,8220,8222,8224,8226],{"class":98,"line":1509},[96,8201,8108],{"class":128},[96,8203,2312],{"class":106},[96,8205,8113],{"class":2295},[96,8207,2299],{"class":102},[96,8209,6288],{"class":106},[96,8211,8120],{"class":102},[96,8213,107],{"class":106},[96,8215,46],{"class":260},[96,8217,3016],{"class":106},[96,8219,8129],{"class":102},[96,8221,107],{"class":106},[96,8223,8067],{"class":128},[96,8225,3953],{"class":106},[96,8227,3477],{"class":102},[96,8229,8230,8232],{"class":98,"line":1514},[96,8231,8144],{"class":2331},[96,8233,2326],{"class":106},[96,8235,8236],{"class":98,"line":1519},[96,8237,8151],{"class":106},[96,8239,8240,8242,8244,8246,8248,8250,8252,8254,8256,8258,8260,8262,8265,8267,8269,8271,8273,8275,8277,8279,8281,8283,8286],{"class":98,"line":1524},[96,8241,8079],{"class":2331},[96,8243,8158],{"class":102},[96,8245,3387],{"class":106},[96,8247,7686],{"class":128},[96,8249,4449],{"class":106},[96,8251,8167],{"class":4452},[96,8253,4449],{"class":106},[96,8255,7775],{"class":4452},[96,8257,8086],{"class":106},[96,8259,8047],{"class":128},[96,8261,4084],{"class":102},[96,8263,8264],{"class":106},"||",[96,8266,3939],{"class":102},[96,8268,8067],{"class":128},[96,8270,8086],{"class":106},[96,8272,3246],{"class":106},[96,8274,7686],{"class":128},[96,8276,8190],{"class":106},[96,8278,8167],{"class":4452},[96,8280,8190],{"class":106},[96,8282,7775],{"class":4452},[96,8284,8285],{"class":102},") )",[96,8287,788],{"class":106},[96,8289,8290,8293,8295,8297,8299,8301,8303,8305,8308,8310,8312,8314,8316,8318,8320,8322],{"class":98,"line":1530},[96,8291,8292],{"class":128},"                   tmp",[96,8294,2312],{"class":106},[96,8296,8113],{"class":2295},[96,8298,2299],{"class":102},[96,8300,6288],{"class":106},[96,8302,8120],{"class":102},[96,8304,107],{"class":106},[96,8306,8307],{"class":260},"false",[96,8309,3016],{"class":106},[96,8311,8129],{"class":102},[96,8313,107],{"class":106},[96,8315,2477],{"class":106},[96,8317,2342],{"class":113},[96,8319,2477],{"class":106},[96,8321,3953],{"class":106},[96,8323,3477],{"class":102},[96,8325,8326,8328],{"class":98,"line":1535},[96,8327,8144],{"class":2331},[96,8329,2326],{"class":106},[96,8331,8332],{"class":98,"line":1541},[96,8333,8151],{"class":106},[96,8335,8336],{"class":98,"line":1546},[96,8337,3619],{"class":106},[96,8339,8340,8342,8344],{"class":98,"line":1551},[96,8341,3243],{"class":2331},[96,8343,8027],{"class":128},[96,8345,2326],{"class":106},[96,8347,8348],{"class":98,"line":1556},[96,8349,1376],{"class":106},[96,8351,8352],{"class":98,"line":1562},[96,8353,3130],{"class":106},[96,8355,8356],{"class":98,"line":1567},[96,8357,987],{"class":106},[96,8359,8360,8362,8364],{"class":98,"line":1573},[96,8361,2641],{"class":106},[96,8363,3000],{"class":102},[96,8365,2459],{"class":106},[13,8367,8368],{},"詳細を解説します。",[70,8370,8372],{"id":8371},"idの値とページ数をチェック","{id}の値とページ数をチェック",[13,8374,8375,8378,8379,8382,8383,8386,8387,7111,8390,8392],{},[54,8376,8377],{},"\u002Farciles\u002Fpage\u002F{id}","において",[54,8380,8381],{},"{id}","が数値のみ許可するようにします。そこでNuxt SSRでは",[54,8384,8385],{},"validate()","というものを使用できます。",[54,8388,8389],{},"params.id",[54,8391,8381],{},"の値が取得できますので、そこで数値であることを確認します。もしそうでなければ、１ページ目にリダイレクトさせます。",[13,8394,8395],{},"数値であっても提供するページを超えた数を指定されては困ります。その時のために{id}のページ数が存在するかをチェックしておきます。もし存在しなければ１ページ目にリダイレクトさせます。",[86,8397,8399],{"className":1910,"code":8398,"language":1912,"meta":92,"style":92},"if(current > Math.ceil( count.length \u002F store.state.indexPerPage)) redirect('\u002Farticles');\n",[54,8400,8401],{"__ignoreMap":92},[96,8402,8403,8406,8409,8411,8413,8415,8417,8420,8422,8425,8427,8429,8431,8433,8435,8438,8440,8442,8444,8446,8448,8450],{"class":98,"line":99},[96,8404,8405],{"class":2331},"if",[96,8407,8408],{"class":128},"(current ",[96,8410,2635],{"class":106},[96,8412,7691],{"class":128},[96,8414,2312],{"class":106},[96,8416,7696],{"class":2295},[96,8418,8419],{"class":128},"( count",[96,8421,2312],{"class":106},[96,8423,8424],{"class":128},"length ",[96,8426,2174],{"class":106},[96,8428,4050],{"class":128},[96,8430,2312],{"class":106},[96,8432,3988],{"class":128},[96,8434,2312],{"class":106},[96,8436,8437],{"class":128},"indexPerPage)) ",[96,8439,7725],{"class":2295},[96,8441,2299],{"class":128},[96,8443,1964],{"class":106},[96,8445,7586],{"class":113},[96,8447,1964],{"class":106},[96,8449,2309],{"class":128},[96,8451,2326],{"class":106},[4138,8453,8456],{"className":8454},[7147,8455],"alert-warning","\nSSRであれば上記の設定は必須ですが、静的書き出しでは正直入りません。なぜなら静的書き出し時にはこのページング分だけのルートしか提供されないためです。静的HTMLで提供する時は存在しないルートにアクセスした時、404のページにリダイレクトさせておくといいです。\n",[70,8458,8459],{"id":8459},"ページングに必要な値を取得する",[13,8461,8462],{},"これはページングを実装するために必要なロジックのな話になるので、一部省略しますが必要な値は",[3739,8464,8465,8468],{},[3742,8466,8467],{},"何件目から(from)",[3742,8469,8470],{},"何件目まで(to)",[13,8472,8473],{},"が必要となります。ソースでは以下のように使用しています。",[86,8475,8477],{"className":1910,"code":8476,"language":1912,"meta":92,"style":92},"const from = store.state.indexPerPage * (params.id - 1);     \u002F\u002F何件目から\nconst to = store.state.indexPerPage * params.id;             \u002F\u002F何件目まで\n\nconst content = await $content('articles')\n.only(['title','path'])\n.sortBy('createdAt', 'desc')\n.skip(from).limit(to)   \u002F\u002F ページング取得\n.fetch();\n",[54,8478,8479,8522,8554,8558,8581,8605,8629,8648],{"__ignoreMap":92},[96,8480,8481,8483,8486,8488,8490,8492,8494,8496,8499,8502,8505,8507,8510,8513,8515,8517,8519],{"class":98,"line":99},[96,8482,2286],{"class":2285},[96,8484,8485],{"class":128}," from ",[96,8487,2292],{"class":106},[96,8489,4050],{"class":128},[96,8491,2312],{"class":106},[96,8493,3988],{"class":128},[96,8495,2312],{"class":106},[96,8497,8498],{"class":128},"indexPerPage ",[96,8500,8501],{"class":106},"*",[96,8503,8504],{"class":128}," (params",[96,8506,2312],{"class":106},[96,8508,8509],{"class":128},"id ",[96,8511,8512],{"class":106},"-",[96,8514,7775],{"class":4452},[96,8516,2309],{"class":128},[96,8518,8054],{"class":106},[96,8520,8521],{"class":4284},"     \u002F\u002F何件目から\n",[96,8523,8524,8526,8529,8531,8533,8535,8537,8539,8541,8543,8545,8547,8549,8551],{"class":98,"line":120},[96,8525,2286],{"class":2285},[96,8527,8528],{"class":128}," to ",[96,8530,2292],{"class":106},[96,8532,4050],{"class":128},[96,8534,2312],{"class":106},[96,8536,3988],{"class":128},[96,8538,2312],{"class":106},[96,8540,8498],{"class":128},[96,8542,8501],{"class":106},[96,8544,6263],{"class":128},[96,8546,2312],{"class":106},[96,8548,7564],{"class":128},[96,8550,8054],{"class":106},[96,8552,8553],{"class":4284},"             \u002F\u002F何件目まで\n",[96,8555,8556],{"class":98,"line":132},[96,8557,622],{"emptyLinePlaceholder":621},[96,8559,8560,8562,8565,8567,8569,8571,8573,8575,8577,8579],{"class":98,"line":141},[96,8561,2286],{"class":2285},[96,8563,8564],{"class":128}," content ",[96,8566,2292],{"class":106},[96,8568,6280],{"class":2331},[96,8570,6283],{"class":2295},[96,8572,2299],{"class":128},[96,8574,1964],{"class":106},[96,8576,6771],{"class":113},[96,8578,1964],{"class":106},[96,8580,3477],{"class":128},[96,8582,8583,8585,8587,8589,8591,8593,8595,8597,8599,8601,8603],{"class":98,"line":152},[96,8584,2312],{"class":106},[96,8586,6305],{"class":2295},[96,8588,6396],{"class":128},[96,8590,1964],{"class":106},[96,8592,4955],{"class":113},[96,8594,1964],{"class":106},[96,8596,3016],{"class":106},[96,8598,1964],{"class":106},[96,8600,6425],{"class":113},[96,8602,1964],{"class":106},[96,8604,6471],{"class":128},[96,8606,8607,8609,8611,8613,8615,8617,8619,8621,8623,8625,8627],{"class":98,"line":162},[96,8608,2312],{"class":106},[96,8610,6478],{"class":2295},[96,8612,2299],{"class":128},[96,8614,1964],{"class":106},[96,8616,6485],{"class":113},[96,8618,1964],{"class":106},[96,8620,3016],{"class":106},[96,8622,110],{"class":106},[96,8624,6494],{"class":113},[96,8626,1964],{"class":106},[96,8628,3477],{"class":128},[96,8630,8631,8633,8635,8638,8640,8642,8645],{"class":98,"line":171},[96,8632,2312],{"class":106},[96,8634,6536],{"class":2295},[96,8636,8637],{"class":128},"(from)",[96,8639,2312],{"class":106},[96,8641,6548],{"class":2295},[96,8643,8644],{"class":128},"(to)   ",[96,8646,8647],{"class":4284},"\u002F\u002F ページング取得\n",[96,8649,8650,8652,8654,8656],{"class":98,"line":181},[96,8651,2312],{"class":106},[96,8653,6351],{"class":2295},[96,8655,2318],{"class":128},[96,8657,2326],{"class":106},[13,8659,8660,8662],{},[54,8661,8389],{},"が現在のページ数となっていますので、それを参考にしてページングによるコンテンツ取得をします。",[70,8664,8665],{"id":8665},"最大ページと表示ページ範囲を設定してレンダーする",[13,8667,8668,8670],{},[54,8669,7142],{},"での設定は以上でOKです。params.idから現在ページ数を用いてページングのレンダーを構築します。私のページングでは",[3739,8672,8673,8676,8679],{},[3742,8674,8675],{},"１ページ目と最後のページは常に表示",[3742,8677,8678],{},"現在ページから2ページ分だけ表示",[3742,8680,8681],{},"範囲外のページは「...」で表示する",[13,8683,8684],{},"という仕様で実装されています。詳細な仕組みは上記のソースをみてください。必要分のページのリンクを作成してページングは完成です。",[34,8686,8688],{"id":8687},"静的書き出しの際には特に気にせず大丈夫","静的書き出しの際には特に気にせず大丈夫。",[13,8690,8691,8692,8695,8696,8698],{},"ページングの設定して次は静的書き出しを行います。ただし前回のようにルートをnuxtに伝えるということは不要です。どうやら",[54,8693,8694],{},"\u002Farticles\u002Findex","を書き出す時にページングの",[54,8697,6638],{},"を辿ってルートを解決してくれているそうです。実際の書き出しでも",[86,8700,8703],{"className":8701,"code":8702,"language":460},[457],"✔ Generated route \"\u002Farticles\u002Fpage\n✔ Generated route \"\u002Farticles\u002Fpage\u002F1\" \n✔ Generated route \"\u002Farticles\u002Fpage\u002F2\" \n",[54,8704,8702],{"__ignoreMap":92},[13,8706,8707],{},"以上のようなログが確認できました。",[34,8709,8710],{"id":8710},"次回は",[13,8712,8713],{},"以上で一覧ページの作成とページングが実装終了しました。ページングとページリストはコンポーネント化しておいた方が後々の開発が楽になります。次回はカテゴリーとタグ機能の解説を行います。",[3795,8715,8716],{},"html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}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 pre.shiki code .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}",{"title":92,"searchDepth":132,"depth":132,"links":8718},[8719,8723,8728,8729],{"id":6718,"depth":120,"text":6718,"children":8720},[8721,8722],{"id":6721,"depth":132,"text":6721},{"id":6763,"depth":132,"text":6763},{"id":7177,"depth":120,"text":7177,"children":8724},[8725,8726,8727],{"id":8371,"depth":132,"text":8372},{"id":8459,"depth":132,"text":8459},{"id":8665,"depth":132,"text":8665},{"id":8687,"depth":120,"text":8688},{"id":8710,"depth":120,"text":8710},[3836],"2021-05-06","Nuxt Content × SSG で作る静的ブログ。記事一覧ページとページング機能ついて説明。",{},"\u002Fseries\u002Fnuxt-content-blog-3",{"title":6696,"description":8732},"series\u002Fnuxt-content-blog-3",[4902,3845],"A6PomFr-zdk_yRRjUN1XT-Qhy330_OwrjwFmZaj63C8",{"id":8740,"title":8741,"body":8742,"category":10417,"createdAt":10418,"description":10419,"extension":3838,"index":120,"meta":10420,"navigation":621,"path":10421,"publish":621,"seo":10422,"series":6688,"seriesTitle":6689,"stem":10423,"tag":10424,"thumbnail":6692,"updatedAt":3839,"__hash__":10425},"series\u002Fseries\u002Fnuxt-content-blog-2.md","Nuxt Content × SSG で作る静的ブログ。２：基本的な記事項目のレンダリングと静的書き出し",{"type":10,"value":8743,"toc":10401},[8744,8750,8761,8763,8766,8769,8781,8792,8798,8815,8819,8825,8830,8851,8855,8861,8994,9008,9013,9030,9033,9042,9045,9048,9059,9070,9117,9130,9133,9377,9384,9401,9408,9412,9415,9435,9439,9442,9445,9451,9457,9514,9517,9520,9531,9537,9540,9543,9546,10136,10139,10154,10318,10334,10337,10342,10348,10355,10361,10367,10373,10382,10385,10398],[13,8745,4912,8746,8749],{},[76,8747,4916],{"href":8748},"\u002Fseries\u002Fnuxt-content-blog\u002F1","は概念と基本的なコンテンツの作成・取得・表示について解説しました。今回の記事では",[3739,8751,8752,8755,8758],{},[3742,8753,8754],{},"nuxt.jsにおけるpage構成の設定",[3742,8756,8757],{},"目次生成、画像のレンダリング、どんな項目をレンダリングできるのか？",[3742,8759,8760],{},"静的書き出しの設定",[13,8762,6715],{},[34,8764,8765],{"id":8765},"サイトのルーティングを考えてpagesの構成を設定する",[70,8767,8768],{"id":8768},"何をどう設定すればいいか",[13,8770,8771,8772,8774,8775,8777,8778,8780],{},"Nuxt.jsは",[54,8773,6202],{},"というディレクトリ配下にファイルを作ることで、vue-routerの設定が自動で行われURLが生成されます。私のブログでは",[54,8776,6736],{},"というURLで対象の記事を呼び出します。そのURLが有効になるには",[54,8779,6202],{},"ディレクトリの構築をする必要があります。",[13,8782,8783,8784,8787,8788,8791],{},"一方、Nuxt Contentで任意の記事データを取得してみると",[54,8785,8786],{},"path:","というプロパティがあります。それは",[54,8789,8790],{},"content\u002F","ディレクトリをルートとしたパスになっています。以下のような構成の場合、",[86,8793,8796],{"className":8794,"code":8795,"language":460},[457],"├── content\n│   ├── articles\n│   │   ├── aaaaaa.md\n",[54,8797,8795],{"__ignoreMap":92},[13,8799,8800,8803,8804,8807,8808,8811,8812,8814],{},[54,8801,8802],{},"aaaaaa.md","のデータのパスは",[54,8805,8806],{},"\u002Farticles\u002Faaaaaa","となります。つまり",[54,8809,8810],{},"https:\u002F\u002Fexample.com\u002Farticles\u002Faaaaaa","というルートに対して、",[54,8813,8802],{},"が呼び出されるように設定します。",[70,8816,8818],{"id":8817},"ひとまず以下のようにpageを作成","ひとまず以下のようにpage\u002Fを作成",[13,8820,8821,8822,8824],{},"ひとまず以下のように",[54,8823,6202],{},"配下に作成してください。",[86,8826,8828],{"className":8827,"code":6728,"language":460},[457],[54,8829,6728],{"__ignoreMap":92},[13,8831,8832,8835,8836,435,8841,8843,8844,7111,8847,8850],{},[54,8833,8834],{},"_slug.vue","といものを作ることで",[76,8837,8840],{"href":8838,"rel":8839},"https:\u002F\u002Fja.nuxtjs.org\u002Fdocs\u002F2.x\u002Fdirectory-structure\u002Fpages",[584],"公式にある通り",[54,8842,6736],{},"のルートに対して",[54,8845,8846],{},"params.sulg",[54,8848,8849],{},"{sulg}","の値を取得できるようになります。",[70,8852,8854],{"id":8853},"paramssulgで指定されたsulgのファイルfetchする","params.sulgで指定されたsulgのファイルfetchする",[13,8856,6223,8857,8860],{},[54,8858,8859],{},"_sulg.vue","で以下のように記述します。",[86,8862,8865],{"className":2443,"code":8863,"filename":8864,"language":2446,"meta":92,"style":92},"\u003Ctemplate>\n  \u003Carticle>\n    \u003Cnuxt-content :document=\"content\" \u002F>\n  \u003C\u002Farticle>\n\u003C\u002Ftemplate>\n\u003Cscirpt>\nexport default {\n  async asyncData({ $content, params,redirect }) {\n    const content = await $content('articles').where({path:\"\u002Farticles\u002F\"+params.slug}).fetch();\n    if(content.length > 0){\n      return {\n        content:content[0],\n      }\n    }else{\n      redirect('\u002Farticles')\n    }\n  }\n}\n\u003C\u002Fscript>\n\n","\u002Farticles\u002F_sulg.vue",[54,8866,8867,8875,8885,8907,8916,8924,8933,8938,8943,8948,8953,8958,8963,8967,8972,8977,8981,8985,8989],{"__ignoreMap":92},[96,8868,8869,8871,8873],{"class":98,"line":99},[96,8870,2453],{"class":106},[96,8872,2456],{"class":102},[96,8874,2459],{"class":106},[96,8876,8877,8880,8883],{"class":98,"line":120},[96,8878,8879],{"class":106},"  \u003C",[96,8881,8882],{"class":102},"article",[96,8884,2459],{"class":106},[96,8886,8887,8889,8892,8895,8897,8899,8902,8904],{"class":98,"line":132},[96,8888,2464],{"class":106},[96,8890,8891],{"class":102},"nuxt-content",[96,8893,8894],{"class":2285}," :document",[96,8896,2292],{"class":106},[96,8898,2477],{"class":106},[96,8900,8901],{"class":113},"content",[96,8903,2477],{"class":106},[96,8905,8906],{"class":106}," \u002F>\n",[96,8908,8909,8912,8914],{"class":98,"line":141},[96,8910,8911],{"class":106},"  \u003C\u002F",[96,8913,8882],{"class":102},[96,8915,2459],{"class":106},[96,8917,8918,8920,8922],{"class":98,"line":152},[96,8919,2641],{"class":106},[96,8921,2456],{"class":102},[96,8923,2459],{"class":106},[96,8925,8926,8928,8931],{"class":98,"line":162},[96,8927,2453],{"class":106},[96,8929,8930],{"class":102},"scirpt",[96,8932,2459],{"class":106},[96,8934,8935],{"class":98,"line":171},[96,8936,8937],{"class":128},"export default {\n",[96,8939,8940],{"class":98,"line":181},[96,8941,8942],{"class":128},"  async asyncData({ $content, params,redirect }) {\n",[96,8944,8945],{"class":98,"line":4},[96,8946,8947],{"class":128},"    const content = await $content('articles').where({path:\"\u002Farticles\u002F\"+params.slug}).fetch();\n",[96,8949,8950],{"class":98,"line":196},[96,8951,8952],{"class":128},"    if(content.length > 0){\n",[96,8954,8955],{"class":98,"line":204},[96,8956,8957],{"class":128},"      return {\n",[96,8959,8960],{"class":98,"line":214},[96,8961,8962],{"class":128},"        content:content[0],\n",[96,8964,8965],{"class":98,"line":228},[96,8966,2212],{"class":128},[96,8968,8969],{"class":98,"line":240},[96,8970,8971],{"class":128},"    }else{\n",[96,8973,8974],{"class":98,"line":252},[96,8975,8976],{"class":128},"      redirect('\u002Farticles')\n",[96,8978,8979],{"class":98,"line":264},[96,8980,954],{"class":128},[96,8982,8983],{"class":98,"line":275},[96,8984,4680],{"class":128},[96,8986,8987],{"class":98,"line":283},[96,8988,987],{"class":128},[96,8990,8991],{"class":98,"line":293},[96,8992,8993],{"class":128},"\u003C\u002Fscript>\n",[13,8995,8996,8998,8999,9001,9002,7111,9004,9007],{},[54,8997,7142],{},"中で",[54,9000,7151],{},"をフェッチします。そして",[54,9003,6662],{},[54,9005,9006],{},"\"\u002Farticles\u002F\"+params.slug","に一致するコンテンツを引っ張るようにします。",[13,9009,9010,9012],{},[54,9011,6320],{},"クエリを使用すると配列で結果が返るので、あれば一致した結果、なければ一覧ページにリダイレクトするようにします。以上でNuxtにおけるpagesディレクトリの設定は完了です。",[4138,9014,9016,9018,9019,9022,9023,9025,9026],{"className":9015},[7147,7148],[54,9017,7142],{},"はSSRの時の",[54,9020,9021],{},"page\u002F","配下のファイルで使用できます。サーバーサイドで処理される箇所であり、そこで$contentをfetchします。静的書き出しを行うと、",[54,9024,7142],{},"内の処理は書き出し中に実行されます。\n",[76,9027,9029],{"href":9028,"target":7156},"https:\u002F\u002Fnuxtjs.org\u002Fdocs\u002F2.x\u002Ffeatures\u002Fdata-fetching","公式：Data Fetching",[34,9031,9032],{"id":9032},"コンテンツの作成",[13,9034,9035,9036,9041],{},"ブログ記事は単に文章だけでなく、太字、リンク、画像、見出し、目次が大体必要になります。文章の修飾はマークダウンの記述を行えば問題ありません。マークダウン記法は今回は解説しません。",[76,9037,9040],{"href":9038,"rel":9039},"https:\u002F\u002Fqiita.com\u002Fkamorits\u002Fitems\u002F6f342da395ad57468ae3",[584],"こちらの記事","がお世話になりました。",[70,9043,9044],{"id":9044},"画像をレンダリングする場合",[13,9046,9047],{},"マークダウンで画像を表示する場合は基本的に以下のように記述します。",[86,9049,9053],{"className":9050,"code":9051,"language":9052,"meta":92,"style":92},"language-markdown shiki shiki-themes material-theme-ocean","![image alt text](\u002Fimage\u002Fsample.jpg)\n","markdown",[54,9054,9055],{"__ignoreMap":92},[96,9056,9057],{"class":98,"line":99},[96,9058,9051],{},[13,9060,9061,9062,9065,9066,9069],{},"Nuxtの場合は画像を",[54,9063,9064],{},"asset","あたりに入れておき、",[54,9067,9068],{},"require('~\u002Fasset\u002Fsample.jpg')","という感じで依存性を解決できます。しかしマークダウンでは以下のパターンで画像パスの解決ができません。",[86,9071,9073],{"className":9050,"code":9072,"language":9052,"meta":92,"style":92},"\n\u002F\u002F パターン１\n![image alt text](~\u002Fasset\u002Fsample.jpg)\n\n\u002F\u002F パターン２\n![image alt text](\u002Fasset\u002Fsample.jpg)\n\n\u002F\u002F パターン３\n![image alt text](require(~\u002Fasset\u002Fsample.jpg))\n\n",[54,9074,9075,9079,9084,9089,9093,9098,9103,9107,9112],{"__ignoreMap":92},[96,9076,9077],{"class":98,"line":99},[96,9078,622],{"emptyLinePlaceholder":621},[96,9080,9081],{"class":98,"line":120},[96,9082,9083],{},"\u002F\u002F パターン１\n",[96,9085,9086],{"class":98,"line":132},[96,9087,9088],{},"![image alt text](~\u002Fasset\u002Fsample.jpg)\n",[96,9090,9091],{"class":98,"line":141},[96,9092,622],{"emptyLinePlaceholder":621},[96,9094,9095],{"class":98,"line":152},[96,9096,9097],{},"\u002F\u002F パターン２\n",[96,9099,9100],{"class":98,"line":162},[96,9101,9102],{},"![image alt text](\u002Fasset\u002Fsample.jpg)\n",[96,9104,9105],{"class":98,"line":171},[96,9106,622],{"emptyLinePlaceholder":621},[96,9108,9109],{"class":98,"line":181},[96,9110,9111],{},"\u002F\u002F パターン３\n",[96,9113,9114],{"class":98,"line":4},[96,9115,9116],{},"![image alt text](require(~\u002Fasset\u002Fsample.jpg))\n",[13,9118,9119,9120,9123,9124,9129],{},"画像のレンダリングは",[54,9121,9122],{},"\"@nuxt\u002Fcontent\": \"^1.14.0\"","時点で特段に対応されておらず、ドキュメントにも書かれていませんでした。",[76,9125,9128],{"href":9126,"rel":9127},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fcontent\u002Fissues\u002F106",[584],"Githubのissueでも報告されているよう","に議論となっています。",[13,9131,9132],{},"一応解決策としてはマークダウンファイルそのものにVueコンポーネントを書いてしまうことです。最初に画像レンダリング用のコンポーネントを作成します。",[86,9134,9137],{"className":2443,"code":9135,"filename":9136,"language":2446,"meta":92,"style":92},"\u003Ctemplate>\n  \u003Cimg :src=\"imgSrc()\" :alt=\"alt\"\u002F>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  props: {\n    src: {\n      type: String,\n      required: true\n    },\n    alt: {\n      type: String,\n    },\n  },\n  methods: {\n    imgSrc() {\n      try {\n        return require(`~\u002Fassets\u002Fimage\u002F${this.src}`)\n      } catch (error) {\n        return null\n      }\n    }\n  },\n}\n\u003C\u002Fscript>\n","components\u002FimageRender.vue",[54,9138,9139,9147,9181,9189,9193,9201,9209,9218,9227,9239,9248,9252,9261,9271,9275,9279,9288,9297,9304,9330,9346,9353,9357,9361,9365,9369],{"__ignoreMap":92},[96,9140,9141,9143,9145],{"class":98,"line":99},[96,9142,2453],{"class":106},[96,9144,2456],{"class":102},[96,9146,2459],{"class":106},[96,9148,9149,9151,9154,9157,9159,9161,9164,9166,9169,9171,9173,9176,9178],{"class":98,"line":120},[96,9150,8879],{"class":106},[96,9152,9153],{"class":102},"img",[96,9155,9156],{"class":2285}," :src",[96,9158,2292],{"class":106},[96,9160,2477],{"class":106},[96,9162,9163],{"class":113},"imgSrc()",[96,9165,2477],{"class":106},[96,9167,9168],{"class":2285}," :alt",[96,9170,2292],{"class":106},[96,9172,2477],{"class":106},[96,9174,9175],{"class":113},"alt",[96,9177,2477],{"class":106},[96,9179,9180],{"class":106},"\u002F>\n",[96,9182,9183,9185,9187],{"class":98,"line":132},[96,9184,2641],{"class":106},[96,9186,2456],{"class":102},[96,9188,2459],{"class":106},[96,9190,9191],{"class":98,"line":141},[96,9192,622],{"emptyLinePlaceholder":621},[96,9194,9195,9197,9199],{"class":98,"line":152},[96,9196,2453],{"class":106},[96,9198,3000],{"class":102},[96,9200,2459],{"class":106},[96,9202,9203,9205,9207],{"class":98,"line":162},[96,9204,2332],{"class":2331},[96,9206,2335],{"class":2331},[96,9208,2146],{"class":106},[96,9210,9211,9214,9216],{"class":98,"line":171},[96,9212,9213],{"class":102},"  props",[96,9215,107],{"class":106},[96,9217,2146],{"class":106},[96,9219,9220,9223,9225],{"class":98,"line":181},[96,9221,9222],{"class":102},"    src",[96,9224,107],{"class":106},[96,9226,2146],{"class":106},[96,9228,9229,9232,9234,9237],{"class":98,"line":4},[96,9230,9231],{"class":102},"      type",[96,9233,107],{"class":106},[96,9235,9236],{"class":128}," String",[96,9238,1940],{"class":106},[96,9240,9241,9244,9246],{"class":98,"line":196},[96,9242,9243],{"class":102},"      required",[96,9245,107],{"class":106},[96,9247,261],{"class":260},[96,9249,9250],{"class":98,"line":204},[96,9251,3130],{"class":106},[96,9253,9254,9257,9259],{"class":98,"line":214},[96,9255,9256],{"class":102},"    alt",[96,9258,107],{"class":106},[96,9260,2146],{"class":106},[96,9262,9263,9265,9267,9269],{"class":98,"line":228},[96,9264,9231],{"class":102},[96,9266,107],{"class":106},[96,9268,9236],{"class":128},[96,9270,1940],{"class":106},[96,9272,9273],{"class":98,"line":240},[96,9274,3130],{"class":106},[96,9276,9277],{"class":98,"line":252},[96,9278,2382],{"class":106},[96,9280,9281,9284,9286],{"class":98,"line":264},[96,9282,9283],{"class":102},"  methods",[96,9285,107],{"class":106},[96,9287,2146],{"class":106},[96,9289,9290,9293,9295],{"class":98,"line":275},[96,9291,9292],{"class":102},"    imgSrc",[96,9294,2318],{"class":106},[96,9296,2146],{"class":106},[96,9298,9299,9302],{"class":98,"line":283},[96,9300,9301],{"class":2331},"      try",[96,9303,2146],{"class":106},[96,9305,9306,9308,9310,9312,9314,9317,9320,9322,9325,9328],{"class":98,"line":293},[96,9307,3074],{"class":2331},[96,9309,2296],{"class":2295},[96,9311,2299],{"class":102},[96,9313,6588],{"class":106},[96,9315,9316],{"class":113},"~\u002Fassets\u002Fimage\u002F",[96,9318,9319],{"class":106},"${",[96,9321,3387],{"class":106},[96,9323,9324],{"class":128},"src",[96,9326,9327],{"class":106},"}`",[96,9329,3477],{"class":102},[96,9331,9332,9335,9338,9340,9342,9344],{"class":98,"line":301},[96,9333,9334],{"class":106},"      }",[96,9336,9337],{"class":2331}," catch",[96,9339,3939],{"class":102},[96,9341,3591],{"class":128},[96,9343,4084],{"class":102},[96,9345,788],{"class":106},[96,9347,9348,9350],{"class":98,"line":312},[96,9349,3074],{"class":2331},[96,9351,9352],{"class":106}," null\n",[96,9354,9355],{"class":98,"line":323},[96,9356,2212],{"class":106},[96,9358,9359],{"class":98,"line":334},[96,9360,954],{"class":106},[96,9362,9363],{"class":98,"line":345},[96,9364,2382],{"class":106},[96,9366,9367],{"class":98,"line":354},[96,9368,987],{"class":106},[96,9370,9371,9373,9375],{"class":98,"line":366},[96,9372,2641],{"class":106},[96,9374,3000],{"class":102},[96,9376,2459],{"class":106},[13,9378,9379,9380,9383],{},"上記のコンポーネントを ",[29,9381,9382],{},"マークダウン"," に記述します。",[86,9385,9389],{"className":9386,"code":9387,"language":9388,"meta":92,"style":92},"language-sample.md shiki shiki-themes material-theme-ocean","以下のようにします。\n\u003CimageRender src=\"sample.jpg\"\u002F>\n","sample.md",[54,9390,9391,9396],{"__ignoreMap":92},[96,9392,9393],{"class":98,"line":99},[96,9394,9395],{},"以下のようにします。\n",[96,9397,9398],{"class":98,"line":120},[96,9399,9400],{},"\u003CimageRender src=\"sample.jpg\"\u002F>\n",[13,9402,9403,9404,9407],{},"上記の記述はNuxt Contentがåいい感じにvueコンポーネントとして扱ってくれます。パスの解決はコンポーネント内の",[54,9405,9406],{},"require()","が行います。あまりかっこいい方法ではありませんが、一応これで画像をレンダリングできます。",[70,9409,9411],{"id":9410},"html要素をレンダリングする","HTML要素をレンダリングする",[13,9413,9414],{},"Nuxt Contentはマークダウン内のHTMLをHTMLとして扱ってくれますので、カスタムな要素を記述できます。",[86,9416,9418],{"className":9386,"code":9417,"language":9388,"meta":92,"style":92},"\u003Cdiv class=\"alert alert-warning\">\nこのアラートもこのようにHTMLをマークダウンに書いています！\n\u003C\u002Fdiv>\n",[54,9419,9420,9425,9430],{"__ignoreMap":92},[96,9421,9422],{"class":98,"line":99},[96,9423,9424],{},"\u003Cdiv class=\"alert alert-warning\">\n",[96,9426,9427],{"class":98,"line":120},[96,9428,9429],{},"このアラートもこのようにHTMLをマークダウンに書いています！\n",[96,9431,9432],{"class":98,"line":132},[96,9433,9434],{},"\u003C\u002Fdiv>\n",[4138,9436,9438],{"className":9437},[7147,8455],"\nこのアラートもこのようにHTMLをマークダウンに書いています！\n",[70,9440,9441],{"id":9441},"シンタックスハイライトを有効にする",[13,9443,9444],{},"Nuxt Contentは一応開発者向けなのか、簡単にコードブロックに対してシンタックスハイライトを有効にすることができます。prismJSを使用するのでまずはインストールします。",[86,9446,9449],{"className":9447,"code":9448,"language":460},[457],"npm install prism-themes\n\n",[54,9450,9448],{"__ignoreMap":92},[13,9452,9453,9454,9456],{},"そして",[54,9455,1891],{},"で以下のようにテーマのCSSを設定します。",[86,9458,9460],{"className":1910,"code":9459,"filename":1891,"language":1912,"meta":92,"style":92},"content: {\n    markdown: {\n      prism: {\n        theme: 'prism-themes\u002Fthemes\u002Fprism-material-oceanic.css'\n      }\n    }\n  },\n",[54,9461,9462,9470,9479,9488,9502,9506,9510],{"__ignoreMap":92},[96,9463,9464,9466,9468],{"class":98,"line":99},[96,9465,8901],{"class":1923},[96,9467,107],{"class":106},[96,9469,2146],{"class":106},[96,9471,9472,9475,9477],{"class":98,"line":120},[96,9473,9474],{"class":1923},"    markdown",[96,9476,107],{"class":106},[96,9478,2146],{"class":106},[96,9480,9481,9484,9486],{"class":98,"line":132},[96,9482,9483],{"class":1923},"      prism",[96,9485,107],{"class":106},[96,9487,2146],{"class":106},[96,9489,9490,9493,9495,9497,9500],{"class":98,"line":141},[96,9491,9492],{"class":1923},"        theme",[96,9494,107],{"class":106},[96,9496,110],{"class":106},[96,9498,9499],{"class":113},"prism-themes\u002Fthemes\u002Fprism-material-oceanic.css",[96,9501,117],{"class":106},[96,9503,9504],{"class":98,"line":152},[96,9505,2212],{"class":106},[96,9507,9508],{"class":98,"line":162},[96,9509,954],{"class":106},[96,9511,9512],{"class":98,"line":171},[96,9513,2382],{"class":106},[13,9515,9516],{},"以上でコードブロック内にてシンタックスハイライトが有効になります。",[70,9518,9519],{"id":9519},"目次の作成",[13,9521,9522,9523,9526,9527,9530],{},"目次の作成、もとい見出しのデータの取得は簡単です。",[54,9524,9525],{},"const content = $content().fetch()","で取得した ",[54,9528,9529],{},"content.toc","で見出しのデータが取得できます。",[86,9532,9535],{"className":9533,"code":9534,"language":460},[457],"toc:Array[8]\n    0:Object\n        depth:2\n        id:\"サイトのルーティングを考えてpagesの構成を設定する\"\n        text:\"サイトのルーティングを考えてpagesの構成を設定する\"\n    1:Object\n",[54,9536,9534],{"__ignoreMap":92},[13,9538,9539],{},"私のブログの左サイドにある目次も、上記のようなオブジェクトを利用して作っています。",[70,9541,9542],{"id":9542},"その他の項目や要素について",[13,9544,9545],{},"以上がブログに必要であろう要素をNuxt Contentで記述しました。私の一般記事のtemplateは以下ようになっていますので、是非参考にしてください。",[86,9547,9550],{"className":2443,"code":9548,"filename":9549,"language":2446,"meta":92,"style":92},"\u003Ctemplate>\n  \u003Cdiv class=\"p-main-container\">\n    \u003Cdiv class=\"p-main-wrapper\">\n        \u002F\u002F目次のコンポーネント\n        \u003Ctoc :toc=\"content.toc\"\u002F>\n\n        \u002F\u002Fメインのエリア\n        \u003Cdiv id=\"l-center-area\">\n\n            \u002F\u002Fサムネイル\n            \u003Cimg v-if=\"thumbnail\" class=\"c-article-thumbnail\" :src=\"thumbnail\" :alt=\"content.title\">\n\n            \u002F\u002Fタグとカテゴリーのバッチ\n            \u003Cdiv class=\"p-article-badge p-badge-container\">\n                \u003Cnuxt-link class=\"c-tag-badge u-blue\" v-for=\"(c,index) in content.category\" :key=\"'category-'+index\" :to=\"'\u002Fcategory\u002F'+c\">\n                    \u003Cspan>{{$store.getters['getCategoryTextBySlug'](c)}}\u003C\u002Fspan>\n                \u003C\u002Fnuxt-link>\n                \u003Cnuxt-link class=\"c-tag-badge\" v-for=\"(t,index) in content.tag\" :key=\"'tag-'+index\" :to=\"'\u002Ftag\u002F'+t\">\n                    \u003Cspan>{{$store.getters['getTagTextBySlug'](t)}}\u003C\u002Fspan>\n                \u003C\u002Fnuxt-link>\n            \u003C\u002Fdiv>\n\n            \u003Ch1 class=\"c-article-header\">{{ content.title }}\u003C\u002Fh1>\n\n            \u002F\u002F 更新一時など\n            \u003Cdiv class=\"p-articler-date\">\n                \u003Cspan class=\"c-date\">\u003Cfa-icon :icon=\"['fa', 'history']\"\u002F>{{ updateAt }}\u003C\u002Fspan>\n                \u003Cspan class=\"c-date\">\u003Cfa-icon :icon=\"['far', 'clock']\"\u002F>{{ createdAt }}\u003C\u002Fspan>\n            \u003C\u002Fdiv>\n\n            \u002F\u002F マークダウンのレンダリング箇所\n            \u003Cnuxt-content :document=\"content\" \u002F>\n        \u003C\u002Fdiv>\n\n        \u002F\u002Fサイドメニュー\n        \u003Csidemenu\u002F>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n","pages\u002Farticles\u002F_slug.vue",[54,9551,9552,9560,9579,9598,9603,9623,9627,9632,9652,9656,9661,9711,9715,9720,9739,9791,9809,9817,9869,9886,9894,9902,9906,9934,9938,9943,9962,10009,10051,10059,10063,10068,10086,10094,10098,10103,10112,10120,10128],{"__ignoreMap":92},[96,9553,9554,9556,9558],{"class":98,"line":99},[96,9555,2453],{"class":106},[96,9557,2456],{"class":102},[96,9559,2459],{"class":106},[96,9561,9562,9564,9566,9568,9570,9572,9575,9577],{"class":98,"line":120},[96,9563,8879],{"class":106},[96,9565,4138],{"class":102},[96,9567,6145],{"class":2285},[96,9569,2292],{"class":106},[96,9571,2477],{"class":106},[96,9573,9574],{"class":113},"p-main-container",[96,9576,2477],{"class":106},[96,9578,2459],{"class":106},[96,9580,9581,9583,9585,9587,9589,9591,9594,9596],{"class":98,"line":132},[96,9582,2464],{"class":106},[96,9584,4138],{"class":102},[96,9586,6145],{"class":2285},[96,9588,2292],{"class":106},[96,9590,2477],{"class":106},[96,9592,9593],{"class":113},"p-main-wrapper",[96,9595,2477],{"class":106},[96,9597,2459],{"class":106},[96,9599,9600],{"class":98,"line":141},[96,9601,9602],{"class":128},"        \u002F\u002F目次のコンポーネント\n",[96,9604,9605,9607,9610,9613,9615,9617,9619,9621],{"class":98,"line":152},[96,9606,2602],{"class":106},[96,9608,9609],{"class":102},"toc",[96,9611,9612],{"class":2285}," :toc",[96,9614,2292],{"class":106},[96,9616,2477],{"class":106},[96,9618,9529],{"class":113},[96,9620,2477],{"class":106},[96,9622,9180],{"class":106},[96,9624,9625],{"class":98,"line":162},[96,9626,622],{"emptyLinePlaceholder":621},[96,9628,9629],{"class":98,"line":171},[96,9630,9631],{"class":128},"        \u002F\u002Fメインのエリア\n",[96,9633,9634,9636,9638,9641,9643,9645,9648,9650],{"class":98,"line":181},[96,9635,2602],{"class":106},[96,9637,4138],{"class":102},[96,9639,9640],{"class":2285}," id",[96,9642,2292],{"class":106},[96,9644,2477],{"class":106},[96,9646,9647],{"class":113},"l-center-area",[96,9649,2477],{"class":106},[96,9651,2459],{"class":106},[96,9653,9654],{"class":98,"line":4},[96,9655,622],{"emptyLinePlaceholder":621},[96,9657,9658],{"class":98,"line":196},[96,9659,9660],{"class":128},"            \u002F\u002Fサムネイル\n",[96,9662,9663,9665,9667,9669,9671,9673,9675,9677,9679,9681,9683,9686,9688,9690,9692,9694,9696,9698,9700,9702,9704,9707,9709],{"class":98,"line":204},[96,9664,6863],{"class":106},[96,9666,9153],{"class":102},[96,9668,2608],{"class":2285},[96,9670,2292],{"class":106},[96,9672,2477],{"class":106},[96,9674,5045],{"class":113},[96,9676,2477],{"class":106},[96,9678,6145],{"class":2285},[96,9680,2292],{"class":106},[96,9682,2477],{"class":106},[96,9684,9685],{"class":113},"c-article-thumbnail",[96,9687,2477],{"class":106},[96,9689,9156],{"class":2285},[96,9691,2292],{"class":106},[96,9693,2477],{"class":106},[96,9695,5045],{"class":113},[96,9697,2477],{"class":106},[96,9699,9168],{"class":2285},[96,9701,2292],{"class":106},[96,9703,2477],{"class":106},[96,9705,9706],{"class":113},"content.title",[96,9708,2477],{"class":106},[96,9710,2459],{"class":106},[96,9712,9713],{"class":98,"line":214},[96,9714,622],{"emptyLinePlaceholder":621},[96,9716,9717],{"class":98,"line":228},[96,9718,9719],{"class":128},"            \u002F\u002Fタグとカテゴリーのバッチ\n",[96,9721,9722,9724,9726,9728,9730,9732,9735,9737],{"class":98,"line":240},[96,9723,6863],{"class":106},[96,9725,4138],{"class":102},[96,9727,6145],{"class":2285},[96,9729,2292],{"class":106},[96,9731,2477],{"class":106},[96,9733,9734],{"class":113},"p-article-badge p-badge-container",[96,9736,2477],{"class":106},[96,9738,2459],{"class":106},[96,9740,9741,9743,9745,9747,9749,9751,9754,9756,9758,9760,9762,9765,9767,9769,9771,9773,9776,9778,9780,9782,9784,9787,9789],{"class":98,"line":252},[96,9742,7287],{"class":106},[96,9744,6638],{"class":102},[96,9746,6145],{"class":2285},[96,9748,2292],{"class":106},[96,9750,2477],{"class":106},[96,9752,9753],{"class":113},"c-tag-badge u-blue",[96,9755,2477],{"class":106},[96,9757,6836],{"class":2285},[96,9759,2292],{"class":106},[96,9761,2477],{"class":106},[96,9763,9764],{"class":113},"(c,index) in content.category",[96,9766,2477],{"class":106},[96,9768,6848],{"class":2285},[96,9770,2292],{"class":106},[96,9772,2477],{"class":106},[96,9774,9775],{"class":113},"'category-'+index",[96,9777,2477],{"class":106},[96,9779,6868],{"class":2285},[96,9781,2292],{"class":106},[96,9783,2477],{"class":106},[96,9785,9786],{"class":113},"'\u002Fcategory\u002F'+c",[96,9788,2477],{"class":106},[96,9790,2459],{"class":106},[96,9792,9793,9796,9798,9800,9803,9805,9807],{"class":98,"line":264},[96,9794,9795],{"class":106},"                    \u003C",[96,9797,96],{"class":102},[96,9799,2635],{"class":106},[96,9801,9802],{"class":128},"{{$store.getters['getCategoryTextBySlug'](c)}}",[96,9804,2641],{"class":106},[96,9806,96],{"class":102},[96,9808,2459],{"class":106},[96,9810,9811,9813,9815],{"class":98,"line":275},[96,9812,7438],{"class":106},[96,9814,6638],{"class":102},[96,9816,2459],{"class":106},[96,9818,9819,9821,9823,9825,9827,9829,9832,9834,9836,9838,9840,9843,9845,9847,9849,9851,9854,9856,9858,9860,9862,9865,9867],{"class":98,"line":283},[96,9820,7287],{"class":106},[96,9822,6638],{"class":102},[96,9824,6145],{"class":2285},[96,9826,2292],{"class":106},[96,9828,2477],{"class":106},[96,9830,9831],{"class":113},"c-tag-badge",[96,9833,2477],{"class":106},[96,9835,6836],{"class":2285},[96,9837,2292],{"class":106},[96,9839,2477],{"class":106},[96,9841,9842],{"class":113},"(t,index) in content.tag",[96,9844,2477],{"class":106},[96,9846,6848],{"class":2285},[96,9848,2292],{"class":106},[96,9850,2477],{"class":106},[96,9852,9853],{"class":113},"'tag-'+index",[96,9855,2477],{"class":106},[96,9857,6868],{"class":2285},[96,9859,2292],{"class":106},[96,9861,2477],{"class":106},[96,9863,9864],{"class":113},"'\u002Ftag\u002F'+t",[96,9866,2477],{"class":106},[96,9868,2459],{"class":106},[96,9870,9871,9873,9875,9877,9880,9882,9884],{"class":98,"line":293},[96,9872,9795],{"class":106},[96,9874,96],{"class":102},[96,9876,2635],{"class":106},[96,9878,9879],{"class":128},"{{$store.getters['getTagTextBySlug'](t)}}",[96,9881,2641],{"class":106},[96,9883,96],{"class":102},[96,9885,2459],{"class":106},[96,9887,9888,9890,9892],{"class":98,"line":301},[96,9889,7438],{"class":106},[96,9891,6638],{"class":102},[96,9893,2459],{"class":106},[96,9895,9896,9898,9900],{"class":98,"line":312},[96,9897,7314],{"class":106},[96,9899,4138],{"class":102},[96,9901,2459],{"class":106},[96,9903,9904],{"class":98,"line":323},[96,9905,622],{"emptyLinePlaceholder":621},[96,9907,9908,9910,9912,9914,9916,9918,9921,9923,9925,9928,9930,9932],{"class":98,"line":334},[96,9909,6863],{"class":106},[96,9911,6808],{"class":102},[96,9913,6145],{"class":2285},[96,9915,2292],{"class":106},[96,9917,2477],{"class":106},[96,9919,9920],{"class":113},"c-article-header",[96,9922,2477],{"class":106},[96,9924,2635],{"class":106},[96,9926,9927],{"class":128},"{{ content.title }}",[96,9929,2641],{"class":106},[96,9931,6808],{"class":102},[96,9933,2459],{"class":106},[96,9935,9936],{"class":98,"line":345},[96,9937,622],{"emptyLinePlaceholder":621},[96,9939,9940],{"class":98,"line":354},[96,9941,9942],{"class":128},"            \u002F\u002F 更新一時など\n",[96,9944,9945,9947,9949,9951,9953,9955,9958,9960],{"class":98,"line":366},[96,9946,6863],{"class":106},[96,9948,4138],{"class":102},[96,9950,6145],{"class":2285},[96,9952,2292],{"class":106},[96,9954,2477],{"class":106},[96,9956,9957],{"class":113},"p-articler-date",[96,9959,2477],{"class":106},[96,9961,2459],{"class":106},[96,9963,9964,9966,9968,9970,9972,9974,9977,9979,9982,9985,9988,9990,9992,9995,9997,10000,10003,10005,10007],{"class":98,"line":375},[96,9965,7287],{"class":106},[96,9967,96],{"class":102},[96,9969,6145],{"class":2285},[96,9971,2292],{"class":106},[96,9973,2477],{"class":106},[96,9975,9976],{"class":113},"c-date",[96,9978,2477],{"class":106},[96,9980,9981],{"class":106},">\u003C",[96,9983,9984],{"class":102},"fa-icon",[96,9986,9987],{"class":2285}," :icon",[96,9989,2292],{"class":106},[96,9991,2477],{"class":106},[96,9993,9994],{"class":113},"['fa', 'history']",[96,9996,2477],{"class":106},[96,9998,9999],{"class":106},"\u002F>",[96,10001,10002],{"class":128},"{{ updateAt }}",[96,10004,2641],{"class":106},[96,10006,96],{"class":102},[96,10008,2459],{"class":106},[96,10010,10011,10013,10015,10017,10019,10021,10023,10025,10027,10029,10031,10033,10035,10038,10040,10042,10045,10047,10049],{"class":98,"line":383},[96,10012,7287],{"class":106},[96,10014,96],{"class":102},[96,10016,6145],{"class":2285},[96,10018,2292],{"class":106},[96,10020,2477],{"class":106},[96,10022,9976],{"class":113},[96,10024,2477],{"class":106},[96,10026,9981],{"class":106},[96,10028,9984],{"class":102},[96,10030,9987],{"class":2285},[96,10032,2292],{"class":106},[96,10034,2477],{"class":106},[96,10036,10037],{"class":113},"['far', 'clock']",[96,10039,2477],{"class":106},[96,10041,9999],{"class":106},[96,10043,10044],{"class":128},"{{ createdAt }}",[96,10046,2641],{"class":106},[96,10048,96],{"class":102},[96,10050,2459],{"class":106},[96,10052,10053,10055,10057],{"class":98,"line":393},[96,10054,7314],{"class":106},[96,10056,4138],{"class":102},[96,10058,2459],{"class":106},[96,10060,10061],{"class":98,"line":875},[96,10062,622],{"emptyLinePlaceholder":621},[96,10064,10065],{"class":98,"line":880},[96,10066,10067],{"class":128},"            \u002F\u002F マークダウンのレンダリング箇所\n",[96,10069,10070,10072,10074,10076,10078,10080,10082,10084],{"class":98,"line":885},[96,10071,6863],{"class":106},[96,10073,8891],{"class":102},[96,10075,8894],{"class":2285},[96,10077,2292],{"class":106},[96,10079,2477],{"class":106},[96,10081,8901],{"class":113},[96,10083,2477],{"class":106},[96,10085,8906],{"class":106},[96,10087,10088,10090,10092],{"class":98,"line":890},[96,10089,2790],{"class":106},[96,10091,4138],{"class":102},[96,10093,2459],{"class":106},[96,10095,10096],{"class":98,"line":896},[96,10097,622],{"emptyLinePlaceholder":621},[96,10099,10100],{"class":98,"line":901},[96,10101,10102],{"class":128},"        \u002F\u002Fサイドメニュー\n",[96,10104,10105,10107,10110],{"class":98,"line":906},[96,10106,2602],{"class":106},[96,10108,10109],{"class":102},"sidemenu",[96,10111,9180],{"class":106},[96,10113,10114,10116,10118],{"class":98,"line":911},[96,10115,2976],{"class":106},[96,10117,4138],{"class":102},[96,10119,2459],{"class":106},[96,10121,10122,10124,10126],{"class":98,"line":917},[96,10123,8911],{"class":106},[96,10125,4138],{"class":102},[96,10127,2459],{"class":106},[96,10129,10130,10132,10134],{"class":98,"line":923},[96,10131,2641],{"class":106},[96,10133,2456],{"class":102},[96,10135,2459],{"class":106},[34,10137,10138],{"id":10138},"静的書き出しをしてみる",[13,10140,10141,10142,10147,10148,7111,10150,10153],{},"それではpagesファイル、マークダウンも作成したのでとりあえずある分だけ静的書き出ししてみましょう。",[76,10143,10146],{"href":10144,"rel":10145},"https:\u002F\u002Fcontent.nuxtjs.org\u002Fja\u002Fadvanced#%E9%9D%99%E7%9A%84%E3%82%B5%E3%82%A4%E3%83%88%E7%94%9F%E6%88%90",[584],"公式の説明","がありますが",[54,10149,1891],{},[54,10151,10152],{},"generate","オプションを設定必要があります。では以下のように設定します。",[86,10155,10157],{"className":1910,"code":10156,"filename":1891,"language":1912,"meta":92,"style":92},"generate: {\n    async routes () {\n      const { $content } = require('@nuxt\u002Fcontent')\n      const files = await $content({ deep: true }).only(['path']).fetch()\n      return files.map(file => file.path === '\u002Findex' ? '\u002F' : file.path);\n    }\n},\n",[54,10158,10159,10167,10179,10205,10253,10310,10314],{"__ignoreMap":92},[96,10160,10161,10163,10165],{"class":98,"line":99},[96,10162,10152],{"class":1923},[96,10164,107],{"class":106},[96,10166,2146],{"class":106},[96,10168,10169,10171,10174,10177],{"class":98,"line":120},[96,10170,6246],{"class":128},[96,10172,10173],{"class":2295}," routes",[96,10175,10176],{"class":102}," () ",[96,10178,788],{"class":106},[96,10180,10181,10184,10186,10188,10190,10192,10194,10196,10198,10201,10203],{"class":98,"line":132},[96,10182,10183],{"class":2285},"      const",[96,10185,3010],{"class":106},[96,10187,6283],{"class":128},[96,10189,3027],{"class":106},[96,10191,3199],{"class":106},[96,10193,2296],{"class":2295},[96,10195,2299],{"class":102},[96,10197,1964],{"class":106},[96,10199,10200],{"class":113},"@nuxt\u002Fcontent",[96,10202,1964],{"class":106},[96,10204,3477],{"class":102},[96,10206,10207,10209,10212,10214,10216,10218,10220,10222,10224,10226,10228,10230,10232,10234,10236,10238,10240,10242,10244,10247,10249,10251],{"class":98,"line":141},[96,10208,10183],{"class":2285},[96,10210,10211],{"class":128}," files",[96,10213,3199],{"class":106},[96,10215,6280],{"class":2331},[96,10217,6283],{"class":2295},[96,10219,2299],{"class":102},[96,10221,6288],{"class":106},[96,10223,6291],{"class":102},[96,10225,107],{"class":106},[96,10227,6296],{"class":260},[96,10229,3027],{"class":106},[96,10231,2309],{"class":102},[96,10233,2312],{"class":106},[96,10235,6305],{"class":2295},[96,10237,6396],{"class":102},[96,10239,1964],{"class":106},[96,10241,6425],{"class":113},[96,10243,1964],{"class":106},[96,10245,10246],{"class":102},"])",[96,10248,2312],{"class":106},[96,10250,6351],{"class":2295},[96,10252,3347],{"class":102},[96,10254,10255,10258,10260,10262,10265,10267,10270,10272,10275,10277,10279,10281,10283,10286,10288,10291,10293,10295,10297,10300,10302,10304,10306,10308],{"class":98,"line":152},[96,10256,10257],{"class":2331},"      return",[96,10259,10211],{"class":128},[96,10261,2312],{"class":106},[96,10263,10264],{"class":2295},"map",[96,10266,2299],{"class":102},[96,10268,10269],{"class":3329},"file",[96,10271,3936],{"class":2285},[96,10273,10274],{"class":128}," file",[96,10276,2312],{"class":106},[96,10278,6425],{"class":128},[96,10280,5942],{"class":106},[96,10282,110],{"class":106},[96,10284,10285],{"class":113},"\u002Findex",[96,10287,1964],{"class":106},[96,10289,10290],{"class":106}," ?",[96,10292,110],{"class":106},[96,10294,2174],{"class":113},[96,10296,1964],{"class":106},[96,10298,10299],{"class":106}," :",[96,10301,10274],{"class":128},[96,10303,2312],{"class":106},[96,10305,6425],{"class":128},[96,10307,2309],{"class":102},[96,10309,2326],{"class":106},[96,10311,10312],{"class":98,"line":162},[96,10313,954],{"class":106},[96,10315,10316],{"class":98,"line":171},[96,10317,4258],{"class":106},[13,10319,10320,10321,10324,10325,10327,10328,10330,10331,10333],{},"これは何をやっているかというと、",[54,10322,10323],{},"$content({ deep: true })","を使用してcontent配下にあるマークダウン一式とそのパスを全て取得して、Nuxtに生成すべきルートを伝えています。なぜこれを行う必要があるのかという理由ですが、Nuxt.jsは",[54,10326,6202],{},"配下の構成を元にして必要なページを生成します。しかしどんなルート名になるかわからない",[54,10329,8859],{},"というファイル（動的ルート）がある場合は、とりうるルートを",[54,10332,10152],{},"オプション内で明示的に指定する必要があります。",[13,10335,10336],{},"Nuxt.js自身はcontents配下の構成とパスがどうなっているのかわからないので、Nuxt Contentから取得します。",[13,10338,10339,10341],{},[54,10340,1891],{},"でSSGができる設定にしたら",[86,10343,10346],{"className":10344,"code":10345,"language":460},[457],"npm run generate\n",[54,10347,10345],{"__ignoreMap":92},[13,10349,10350,10351,10354],{},"を叩くことで静的書き出しが行われます。書き出し後には",[54,10352,10353],{},"dist\u002F","というビルドファイルが作成されます。",[86,10356,10359],{"className":10357,"code":10358,"language":460},[457],"npm run start\n",[54,10360,10358],{"__ignoreMap":92},[13,10362,10363,10364,10366],{},"でひとまずローカル環境で",[54,10365,10353],{},"をドキュメントルートとしてみることができます。以下のような構成で作っていた場合、",[86,10368,10371],{"className":10369,"code":10370,"language":460},[457],"├── content\n│   ├── articles\n│   │   ├── sample.md\n│\n├── pages\n│   ├── articles\n│   　   ├── _slug.vue\n",[54,10372,10370],{"__ignoreMap":92},[13,10374,10375,10378,10379,10381],{},[54,10376,10377],{},"http:\u002F\u002Flocalhost:3000\u002Farticles\u002Fsample","にアクセスすると内容が見れると思います。curlで",[54,10380,10377],{},"でアクセスしてきちんと静的HTMLが書き出されているかを確認してみましょう。",[34,10383,10384],{"id":10384},"以上で基本的な構成の作成が完了",[13,10386,10387,10388,10391,10392,10394,10395,10397],{},"スタイルとかの問題はあるかもしれませんが、ひとまず",[54,10389,10390],{},"pages","・",[54,10393,8901],{},"ディレクトリ、",[54,10396,1891],{},"の設定を行えばブログ的な構成とCMSとしての機能が実装できました。次回は記事の一覧ページとページング処理について解説していきます。",[3795,10399,10400],{},"html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}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 pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}",{"title":92,"searchDepth":132,"depth":132,"links":10402},[10403,10408,10415,10416],{"id":8765,"depth":120,"text":8765,"children":10404},[10405,10406,10407],{"id":8768,"depth":132,"text":8768},{"id":8817,"depth":132,"text":8818},{"id":8853,"depth":132,"text":8854},{"id":9032,"depth":120,"text":9032,"children":10409},[10410,10411,10412,10413,10414],{"id":9044,"depth":132,"text":9044},{"id":9410,"depth":132,"text":9411},{"id":9441,"depth":132,"text":9441},{"id":9519,"depth":132,"text":9519},{"id":9542,"depth":132,"text":9542},{"id":10138,"depth":120,"text":10138},{"id":10384,"depth":120,"text":10384},[3836],"2021-05-05","Nuxt Content × SSG で作る静的ブログ。目次、記事内画像、どんな項目を記事にレンダーできるかと静的書き出しについて説明。",{},"\u002Fseries\u002Fnuxt-content-blog-2",{"title":8741,"description":10419},"series\u002Fnuxt-content-blog-2",[4902,3845],"G0AV4H-Vu8cOAUAeRcjFkqt5-eZKAcs9pHDQjdPhG4E",{"id":10427,"title":10428,"body":10429,"category":11213,"createdAt":11214,"description":11215,"extension":3838,"index":99,"meta":11216,"navigation":621,"path":11217,"publish":621,"seo":11218,"series":6688,"seriesTitle":6689,"stem":11219,"tag":11220,"thumbnail":6692,"updatedAt":3839,"__hash__":11221},"series\u002Fseries\u002Fnuxt-content-blog-1.md","Nuxt Content × SSG で作る静的ブログ。１：概念とセットアップ",{"type":10,"value":10430,"toc":11198},[10431,10434,10441,10457,10460,10464,10470,10475,10478,10481,10484,10501,10504,10506,10517,10524,10541,10544,10547,10550,10643,10670,10672,10713,10716,10718,10721,10745,10748,10751,10754,10757,10763,10780,10790,10841,10846,10852,10858,10880,10883,10886,10891,10942,10949,10952,10958,11124,11127,11131,11141,11144,11157,11180,11183,11189,11195],[13,10432,10433],{},"こんにちはjunです。私のブログは2021年4月にwordpressを卒業し、Nuxt contentというものを使用してリニューアルしました。wordpressは簡単にサイトは作れますし使い勝手はいいですが、公開サーバーにCMSがあるためにセキュリティ的に問題だったり、大量に記事があるとパフォーマンスが落ちるということがあります。webデベロッパーならばNuxt.jsと静的書き出しぐらいやろうぜ！と思ったのも理由です。",[13,10435,10436,10437,10440],{},"このブログリニューアルにはデータ移行含めて２週間ほどかかりましたが、その途中で少し詰まったり工夫したりした箇所が結構あったのでシリーズ記事として、 ",[29,10438,10439],{},"「Nuxt Contentによる静的書き出しブログの作成方法」"," として書きたいと思います。シリーズを通して以下のことを解説しようと思います。（まだ予定です。もしかすると執筆途中で変わります。）",[3895,10442,10443,10446,10449,10452,10454],{},[3742,10444,10445],{},"Nuxt Content の仕組みと基本的な使い方",[3742,10447,10448],{},"記事のレンダリングと静的書き出しルーティング",[3742,10450,10451],{},"記事一覧ページとページング実装",[3742,10453,4920],{},[3742,10455,10456],{},"記事の管理とデプロイ。SEO対策。アドセンスとアナリティクス設定",[13,10458,10459],{},"それでは一回目は早速、インストールから概念・基本的な使い方について説明していきます。Nuxt.jsにおける構成などは共有しますが、このサイトにおけるデザインの実装方法などは省略しますのでご了承ください。",[34,10461,10463],{"id":10462},"nuxt-contentとは","Nuxt Contentとは？",[13,10465,10466,10467,10469],{},"nuxt contentはNuxt.jsのモジュールです。",[54,10468,8790],{},"ディレクトリ配下にmarkdownを用いて記事原稿を作成し、Nuxt.jsがmarkdownを解析してオブジェクトとして利用できるようにしてくれます。",[13,10471,10472,10474],{},[54,10473,6258],{},"というインスタンスがグローバルに使用できますので、それらを利用してコンポーネントにレンダーするという仕組みです。",[13,10476,10477],{},"ブログを構成するソースが全てファイルで構成されるので、GitベースのCMSとして利用することができます。（記事の保存にDBを必要としない。）",[34,10479,10480],{"id":10480},"自分のブログの構成について",[13,10482,10483],{},"今回のシリーズ記事では私の記事と同じ構成を作成できるように解説していこうと思います。2021年5月現在では以下のような構成・機能を持っています。",[3739,10485,10486,10489,10492,10495,10498],{},[3742,10487,10488],{},"記事詳細ページ",[3742,10490,10491],{},"タグ・カテゴリー",[3742,10493,10494],{},"各々の一覧ページとページング",[3742,10496,10497],{},"シリーズ記事",[3742,10499,10500],{},"静的書き出しとデプロイ",[13,10502,10503],{},"詳細に解説します。",[70,10505,10488],{"id":10488},[13,10507,7180,10508,435,10510,10513,10514,10516],{},[54,10509,6736],{},[54,10511,10512],{},"\u002Fseries\u002F{sulg}\u002F{index}","というルートにて各記事本体を見ることができ、そのようなルールで構成されています。スラグ（sulg）は原稿マークダウンのファイル名と一致します。ソースでは",[54,10515,8790],{},"ディレクトリ配下にマークダウンファイルを以下のように格納しています。",[86,10518,10522],{"className":10519,"code":10520,"filename":10521,"language":460,"meta":92},[457],"├── content\n│   ├── articles\n│   │   ├── aaaaaa.md\n│   │   ├── bbbbbb.md\n│   │\n│   └── series\n│       ├── ~~~~~.md\n│       ├── ~~~~~.md\n","Nuxt",[54,10523,10520],{"__ignoreMap":92},[13,10525,10526,10527,10530,10531,10534,10535,10537,10538,10540],{},"上記のように格納することでコンポーネントで、例えば",[54,10528,10529],{},"this.$content('articles')","として呼び出すことでarticles配下のデータを取得できます。ファイル名＝スラグ名としているので、",[54,10532,10533],{},"\u002Farticles\u002Faaaaaaa","とすることで、",[54,10536,8802],{},"の内容がレンダーされるようにしています。このへんのルーティング設定は",[54,10539,9021],{},"ディレクトリの構成で制御しています。第二回目で解説します。",[70,10542,10491],{"id":10543},"タグカテゴリー",[13,10545,10546],{},"各記事ではタグとカテゴリーを指定できるようになっています。カテゴリー、タグをクリックするとその一覧に飛ぶのでユーザーはサイトのコンテンツを探しやすくなります。",[13,10548,10549],{},"タグとカテゴリーは実はマークダウンファイルに記述されています。例えばこの記事のマークダウンには一番最初に以下のようなyml形式の記述があります。",[86,10551,10553],{"className":88,"code":10552,"language":91,"meta":92,"style":92},"---\ntitle: Nuxt Content × SSG で作る静的ブログ。１：概念とセットアップ\ndescription: Nuxt Content × SSG で作る静的ブログ。概念とセットアップについてまずは解説\ncategory: [devstack]\ntag: [js,nuxt]\nseries: nuxt-content-blog\nseriesTitle: Nuxt Content × SSG で作る静的ブログ。\nindex: 1\npublish: false\n---\n",[54,10554,10555,10559,10568,10577,10589,10605,10613,10621,10630,10639],{"__ignoreMap":92},[96,10556,10557],{"class":98,"line":99},[96,10558,4950],{"class":1923},[96,10560,10561,10563,10565],{"class":98,"line":120},[96,10562,4955],{"class":102},[96,10564,107],{"class":106},[96,10566,10567],{"class":113}," Nuxt Content × SSG で作る静的ブログ。１：概念とセットアップ\n",[96,10569,10570,10572,10574],{"class":98,"line":132},[96,10571,4965],{"class":102},[96,10573,107],{"class":106},[96,10575,10576],{"class":113}," Nuxt Content × SSG で作る静的ブログ。概念とセットアップについてまずは解説\n",[96,10578,10579,10581,10583,10585,10587],{"class":98,"line":141},[96,10580,4975],{"class":102},[96,10582,107],{"class":106},[96,10584,4980],{"class":106},[96,10586,3836],{"class":113},[96,10588,683],{"class":106},[96,10590,10591,10593,10595,10597,10599,10601,10603],{"class":98,"line":152},[96,10592,4989],{"class":102},[96,10594,107],{"class":106},[96,10596,4980],{"class":106},[96,10598,4902],{"class":113},[96,10600,3016],{"class":106},[96,10602,3845],{"class":113},[96,10604,683],{"class":106},[96,10606,10607,10609,10611],{"class":98,"line":162},[96,10608,5006],{"class":102},[96,10610,107],{"class":106},[96,10612,5011],{"class":113},[96,10614,10615,10617,10619],{"class":98,"line":171},[96,10616,5016],{"class":102},[96,10618,107],{"class":106},[96,10620,5021],{"class":113},[96,10622,10623,10625,10627],{"class":98,"line":181},[96,10624,5026],{"class":102},[96,10626,107],{"class":106},[96,10628,10629],{"class":4452}," 1\n",[96,10631,10632,10634,10636],{"class":98,"line":4},[96,10633,5036],{"class":102},[96,10635,107],{"class":106},[96,10637,10638],{"class":260}," false\n",[96,10640,10641],{"class":98,"line":196},[96,10642,4950],{"class":1923},[13,10644,10645,10646,435,10649,435,10651,10653,10654,435,10656,435,10659,10661,10662,1940,10664,10666,10667,10669],{},"上記のカラムは自由につけることができます。例えば",[54,10647,10648],{},"serise",[54,10650,5026],{},[54,10652,5036],{},"は私が独自につけています。一方で",[54,10655,4955],{},[54,10657,10658],{},"updateAt",[54,10660,6485],{},"など自動的に付与される属性もあります。",[54,10663,4989],{},[54,10665,4975],{},"は配列形式の記述をすることでjs側でも配列で扱うことができます。",[54,10668,5080],{},"というファイルでキーとカテゴリー名、タグを管理しています。（storeでもOKですが、ちょっと困ることがありました。こちらも後で解説します。）",[70,10671,10494],{"id":10494},[3739,10673,10674,10678,10683,10688,10694,10698,10703,10708],{},[3742,10675,10676],{},[54,10677,6740],{},[3742,10679,10680],{},[54,10681,10682],{},"\u002Fseries\u002F{sulg}\u002F",[3742,10684,10685],{},[54,10686,10687],{},"\u002Fcategory\u002F{category_key}\u002F",[3742,10689,10690,10693],{},[54,10691,10692],{},"\u002Ftag\u002F{tag_key}\u002F","\nというパスではその記事種別、カテゴリーの一覧が表示されます。１ページあたり15記事表示されるので記事が多くなるとページングが発生します。ページングの際は",[3742,10695,10696],{},[54,10697,7201],{},[3742,10699,10700],{},[54,10701,10702],{},"\u002Fseries\u002F{sulg}\u002Fpage\u002F{n}",[3742,10704,10705],{},[54,10706,10707],{},"\u002Fcategory\u002F{category_key}\u002Fpage\u002F{n}",[3742,10709,10710],{},[54,10711,10712],{},"\u002Ftag\u002F{tag_key}\u002Fpage\u002F{n}",[13,10714,10715],{},"というルーティングで現在ページを判別しています。ちなみに静的書き出しするときはページごとにディレクトリが作成されます。",[70,10717,10500],{"id":10500},[13,10719,10720],{},"私の場合、まずNuxt.jsには静的書き出し（SSG:static site generate）を使用して作成してページ分のHTMLを生成します。そして生成されたHTMLと画像をrsyncで公開サーバーに送っています。作成全体の流れを簡単に説明しますと、",[3895,10722,10723,10726,10729,10732,10738],{},[3742,10724,10725],{},"markdownで原稿を記述",[3742,10727,10728],{},"pageコンポーネントに原稿内容をレンダーする。",[3742,10730,10731],{},"公開してよい原稿のみをクエリで取得してルーティングを設定する。",[3742,10733,10734,10737],{},[54,10735,10736],{},"npm run generate"," を使用して作成した原稿分のHTMLを生成する",[3742,10739,10740,10741,10744],{},"rsyncを使用して",[54,10742,10743],{},"\u002Fdist","配下を公開サーバーを同期する",[13,10746,10747],{},"上記のような感じです。結構簡単です。",[13,10749,10750],{},"以上が簡単な概念と構成の説明です。それでは以降からは具体的なインストールと使い方を説明していきます。",[34,10752,10753],{"id":10753},"インストール方法とセットアップ",[13,10755,10756],{},"Nuxt contentはNuxt.jsのモジュールですのでまずはNuxtプロジェクトを作成します。",[86,10758,10761],{"className":10759,"code":10760,"language":460},[457],"npx create-nuxt-app \u003Cproject-name>\n\n... #普通にNuxtのインストールをする\n\nnpm install @nuxt\u002Fcontent\n\n",[54,10762,10760],{"__ignoreMap":92},[4138,10764,10767,10768],{"className":10765},[7147,10766],"alert-success","\n各種の使用バージョンは以下の通りです。\n",[3739,10769,10770,10771,10770,10774,10770,10777],{},"\n    ",[3742,10772,10773],{},"Node.js：v12.19.0",[3742,10775,10776],{},"Nuxt.js：2.14.12",[3742,10778,10779],{},"Nuxt Content：1.14.0",[13,10781,10782,10783,10785,10786,10789],{},"インストールが終わったので、",[54,10784,1891],{},"も",[54,10787,10788],{},"modules","に以下のように追記します。",[86,10791,10794],{"className":10792,"code":10793,"filename":1891,"language":4902,"meta":92,"style":92},"language-js shiki shiki-themes material-theme-ocean","  ...\n\n  modules: [\n    '@nuxt\u002Fcontent',\n  ],\n\n  ...\n",[54,10795,10796,10801,10805,10815,10826,10833,10837],{"__ignoreMap":92},[96,10797,10798],{"class":98,"line":99},[96,10799,10800],{"class":106},"  ...\n",[96,10802,10803],{"class":98,"line":120},[96,10804,622],{"emptyLinePlaceholder":621},[96,10806,10807,10810,10812],{"class":98,"line":132},[96,10808,10809],{"class":1923},"  modules",[96,10811,107],{"class":106},[96,10813,10814],{"class":128}," [\n",[96,10816,10817,10820,10822,10824],{"class":98,"line":141},[96,10818,10819],{"class":106},"    '",[96,10821,10200],{"class":113},[96,10823,1964],{"class":106},[96,10825,1940],{"class":106},[96,10827,10828,10831],{"class":98,"line":152},[96,10829,10830],{"class":128},"  ]",[96,10832,1940],{"class":106},[96,10834,10835],{"class":98,"line":162},[96,10836,622],{"emptyLinePlaceholder":621},[96,10838,10839],{"class":98,"line":171},[96,10840,10800],{"class":106},[13,10842,9453,10843,10845],{},[54,10844,8790],{},"というディレクトリをプロジェクトルートに作成します。",[86,10847,10850],{"className":10848,"code":10849,"language":460},[457],".\n├── README.md\n├── assets\n├── components\n├── content # これをつくる\n├── layouts\n├── middleware\n├── node_modules\n├── nuxt.config.js\n├── package-lock.json\n├── package.json\n├── pages\n├── plugins\n├── static\n├── store\n",[54,10851,10849],{"__ignoreMap":92},[13,10853,10854,10855,10857],{},"マークダウンファイルは",[54,10856,8790],{},"配下に作成していくと、nuxt contentは自動的にそれらのファイルを解析してくれます。",[13,10859,9453,10860,10862,10863,10866,10867,10869,10870,10872,10873,10875,10876,10879],{},[54,10861,8790],{},"ディレクトリを作成したらさらに",[54,10864,10865],{},"articles\u002F","といったディレクトリを作っておくと良いです。別に",[54,10868,6771],{},"としなくてもいいですが、サブディレクトリ を作ることで",[54,10871,7110],{},"のように区別してコンテンツを取得できます。私の場合は",[54,10874,10865],{},"と",[54,10877,10878],{},"series\u002F","というサブディレクトリでコンテンツを区切っています。",[34,10881,10882],{"id":10882},"記事を試しに作成してみる",[70,10884,10885],{"id":10885},"マークダウン原稿を作成する",[13,10887,6223,10888,10890],{},[54,10889,10865],{},"を作ったら何かマークダウンを作成してみましょう。",[86,10892,10895],{"className":9050,"code":10893,"filename":10894,"language":9052,"meta":92,"style":92},"---\ntitle: テスト\ndescription:テスト\n---\nここに記事内容をマークダウン で記述します。\n\n## 見出し\n- リスト\n- リスト\n- リスト\n","articles\u002Ftest.md",[54,10896,10897,10901,10906,10911,10915,10920,10924,10929,10934,10938],{"__ignoreMap":92},[96,10898,10899],{"class":98,"line":99},[96,10900,4950],{},[96,10902,10903],{"class":98,"line":120},[96,10904,10905],{},"title: テスト\n",[96,10907,10908],{"class":98,"line":132},[96,10909,10910],{},"description:テスト\n",[96,10912,10913],{"class":98,"line":141},[96,10914,4950],{},[96,10916,10917],{"class":98,"line":152},[96,10918,10919],{},"ここに記事内容をマークダウン で記述します。\n",[96,10921,10922],{"class":98,"line":162},[96,10923,622],{"emptyLinePlaceholder":621},[96,10925,10926],{"class":98,"line":171},[96,10927,10928],{},"## 見出し\n",[96,10930,10931],{"class":98,"line":181},[96,10932,10933],{},"- リスト\n",[96,10935,10936],{"class":98,"line":4},[96,10937,10933],{},[96,10939,10940],{"class":98,"line":196},[96,10941,10933],{},[13,10943,10944,10945,10948],{},"まずnuxt contentでマークダウン原稿を作る際には、１・２行目に示されたように",[54,10946,10947],{},"---","で囲ったymlにて書かれたメタデータを記述します。メタデータをかけたら、マークダウン 形式で内容を記述していきます。",[70,10950,10951],{"id":10951},"ページコンポーネントで読み込む",[13,10953,10954,10955,10957],{},"サンプルを作成したら",[54,10956,9021],{},"ディレクトリでページコンポーネントを作成します。今回は簡単に以下のような構成にしてみます。",[86,10959,10962],{"className":2443,"code":10960,"filename":10961,"language":2446,"meta":92,"style":92},"\u003Ctemplate>\n    \u003Cdiv>\n        {{content}}\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\u003Cscript>\nexport default{\n    name:'test_page',\n    async asyncData({ store,$content, params }) {\n        const content = await $content('articles').fetch();\n\n        return {\n            content,\n        }\n    },\n}\n\u003C\u002Fscript>\n","pages\u002Ftest.vue",[54,10963,10964,10972,10980,10985,10993,11001,11009,11017,11032,11058,11088,11092,11098,11104,11108,11112,11116],{"__ignoreMap":92},[96,10965,10966,10968,10970],{"class":98,"line":99},[96,10967,2453],{"class":106},[96,10969,2456],{"class":102},[96,10971,2459],{"class":106},[96,10973,10974,10976,10978],{"class":98,"line":120},[96,10975,2464],{"class":106},[96,10977,4138],{"class":102},[96,10979,2459],{"class":106},[96,10981,10982],{"class":98,"line":132},[96,10983,10984],{"class":128},"        {{content}}\n",[96,10986,10987,10989,10991],{"class":98,"line":141},[96,10988,2976],{"class":106},[96,10990,4138],{"class":102},[96,10992,2459],{"class":106},[96,10994,10995,10997,10999],{"class":98,"line":152},[96,10996,2641],{"class":106},[96,10998,2456],{"class":102},[96,11000,2459],{"class":106},[96,11002,11003,11005,11007],{"class":98,"line":162},[96,11004,2453],{"class":106},[96,11006,3000],{"class":102},[96,11008,2459],{"class":106},[96,11010,11011,11013,11015],{"class":98,"line":171},[96,11012,2332],{"class":2285},[96,11014,2335],{"class":2331},[96,11016,788],{"class":106},[96,11018,11019,11021,11023,11025,11028,11030],{"class":98,"line":181},[96,11020,3050],{"class":1923},[96,11022,107],{"class":106},[96,11024,1964],{"class":106},[96,11026,11027],{"class":113},"test_page",[96,11029,1964],{"class":106},[96,11031,1940],{"class":106},[96,11033,11034,11036,11038,11040,11042,11044,11046,11048,11050,11052,11054,11056],{"class":98,"line":4},[96,11035,6246],{"class":128},[96,11037,6249],{"class":2295},[96,11039,2299],{"class":102},[96,11041,6288],{"class":106},[96,11043,4050],{"class":128},[96,11045,3016],{"class":106},[96,11047,6258],{"class":128},[96,11049,3016],{"class":106},[96,11051,6263],{"class":128},[96,11053,3027],{"class":106},[96,11055,4084],{"class":102},[96,11057,788],{"class":106},[96,11059,11060,11062,11064,11066,11068,11070,11072,11074,11076,11078,11080,11082,11084,11086],{"class":98,"line":196},[96,11061,6272],{"class":2285},[96,11063,6366],{"class":128},[96,11065,3199],{"class":106},[96,11067,6280],{"class":2331},[96,11069,6283],{"class":2295},[96,11071,2299],{"class":102},[96,11073,1964],{"class":106},[96,11075,6771],{"class":113},[96,11077,1964],{"class":106},[96,11079,2309],{"class":102},[96,11081,2312],{"class":106},[96,11083,6351],{"class":2295},[96,11085,2318],{"class":102},[96,11087,2326],{"class":106},[96,11089,11090],{"class":98,"line":204},[96,11091,622],{"emptyLinePlaceholder":621},[96,11093,11094,11096],{"class":98,"line":214},[96,11095,3074],{"class":2331},[96,11097,2146],{"class":106},[96,11099,11100,11102],{"class":98,"line":228},[96,11101,7929],{"class":128},[96,11103,1940],{"class":106},[96,11105,11106],{"class":98,"line":240},[96,11107,1376],{"class":106},[96,11109,11110],{"class":98,"line":252},[96,11111,3130],{"class":106},[96,11113,11114],{"class":98,"line":264},[96,11115,987],{"class":106},[96,11117,11118,11120,11122],{"class":98,"line":275},[96,11119,2641],{"class":106},[96,11121,3000],{"class":102},[96,11123,2459],{"class":106},[13,11125,11126],{},"画面は以下のように映ると思います。（私の場合はたくさん記事があるので、たくさんあります。）",[42,11128],{":src":11129,":width":11130,":center":46},"'_mix\u002Fsch-2021-05-05 8.44.11.png'","'400px'",[13,11132,11133,11134,11136,11137,11140],{},"変数",[54,11135,8901],{},"には",[54,11138,11139],{},"$content('articles').fetch()","によって取得されたページのデータがオブジェクト形式で入っています。",[42,11142],{":src":11143,":width":11130,":center":46},"'_mix\u002Fsch-2021-05-05 8.48.11.png'",[13,11145,11146,11148,11149,11152,11153,11156],{},[54,11147,11139],{},"では",[54,11150,11151],{},"aticles","配下のデータが配列でくるので、",[54,11154,11155],{},"$content('articles\u002Ftest').fetch()","としてみると単体の該当するファイルが提供されます。",[13,11158,11159,11162,11163,11168,11169,11172,11173,11176,11177,11179],{},[54,11160,11161],{},"{{content}}","ではただのオブジェクトしか表示されません。",[76,11164,11167],{"href":11165,"rel":11166},"https:\u002F\u002Fcontent.nuxtjs.org\u002Fja\u002Fdisplaying",[584],"公式サイトのように"," ",[54,11170,11171],{},"\u003Cnuxt-content :document=\"content\" \u002F>","というコンポーネントの",[54,11174,11175],{},"document","プロップスに",[54,11178,7151],{},"で取得したものを渡すことで、HTMLでレンダーされます。",[34,11181,11182],{"id":11182},"以上でセットアップ完了",[13,11184,11185,11186,11188],{},"以上がNuxt Contentのセットアップと基本的な使い方です。ひとまずモジュールをインストールして",[54,11187,7151],{},"を用いて対応するコンテンツを取得することで、ブログを作成できます。",[13,11190,11191,11192,11194],{},"次回は個別記事のレンダリングと",[54,11193,7151],{},"の詳細と静的書き出しを行っていこうと思います。",[3795,11196,11197],{},"html pre.shiki code .s5Dmg, html code.shiki .s5Dmg{--shiki-default:#FFCB6B}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .sbqyR, html code.shiki .sbqyR{--shiki-default:#FF9CAC}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 pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}",{"title":92,"searchDepth":132,"depth":132,"links":11199},[11200,11201,11207,11208,11212],{"id":10462,"depth":120,"text":10463},{"id":10480,"depth":120,"text":10480,"children":11202},[11203,11204,11205,11206],{"id":10488,"depth":132,"text":10488},{"id":10543,"depth":132,"text":10491},{"id":10494,"depth":132,"text":10494},{"id":10500,"depth":132,"text":10500},{"id":10753,"depth":120,"text":10753},{"id":10882,"depth":120,"text":10882,"children":11209},[11210,11211],{"id":10885,"depth":132,"text":10885},{"id":10951,"depth":132,"text":10951},{"id":11182,"depth":120,"text":11182},[3836],"2021-05-04","Nuxt Content × SSG で作る静的ブログ。概念とセットアップについてまずは解説",{},"\u002Fseries\u002Fnuxt-content-blog-1",{"title":10428,"description":11215},"series\u002Fnuxt-content-blog-1",[4902,3845],"NqL_rCruXq92Ga0wFzu-aNSycgZTrVDwKB90Uio1hxw",{"id":11223,"title":11224,"body":11225,"category":11306,"createdAt":11307,"description":11224,"extension":3838,"index":3839,"meta":11308,"navigation":621,"path":11309,"publish":621,"seo":11310,"series":3839,"seriesTitle":3839,"stem":11311,"tag":11312,"thumbnail":3839,"updatedAt":3839,"__hash__":11313},"articles\u002Farticles\u002Fdjango-nuxt-routing.md","djangoでNuxt SPAを使うときのルーティング 設定",{"type":10,"value":11226,"toc":11304},[11227,11230,11233,11238,11245,11280,11287,11301],[13,11228,11229],{},"大体のwebフレームワークではルーティングの設定ができます。最近のフロントエンド はNuxt、Nextとかjsを用いて構築して、バックエンドへの通信はAPIを用いていると思います。Djangoにもurls.pyというルーティング を定義するファイルがあります。",[13,11231,11232],{},"しかし一部のurlではNuxt.jsのjsとhtmlを返すことができず、404になってしまいます。api、adminのルートを生かしながらそのほかのURLに対してはNuxtのファイルを返すにはどうすればいいのか？それを調べていたら、以下のstack overflow の記事が答えてくれました",[13,11234,11235],{},[29,11236,11237],{},"検索ワード「Django SPA url」",[4138,11239,11241],{"className":11240},[7147,10766],[76,11242,11244],{"href":11243,"target":7156},"https:\u002F\u002Fstackoverflow.com\u002Fquestions\u002F42864641\u002Fhandling-single-page-application-url-and-django-url","Handling single page application url and django url",[86,11246,11249],{"className":11247,"code":11248,"language":5500,"meta":92,"style":92},"language-python shiki shiki-themes material-theme-ocean","urlpatterns = [\n    url(r'^admin\u002F', admin.site.urls),\n    url(r'^api-auth\u002F', include('rest_framework.urls', namespace='rest_framework')),\n    url(r'^api\u002F', include(router.urls)),\n    url(r'^.*$', TemplateView.as_view(template_name=\"index.html\")),\n]\n",[54,11250,11251,11256,11261,11266,11271,11276],{"__ignoreMap":92},[96,11252,11253],{"class":98,"line":99},[96,11254,11255],{},"urlpatterns = [\n",[96,11257,11258],{"class":98,"line":120},[96,11259,11260],{},"    url(r'^admin\u002F', admin.site.urls),\n",[96,11262,11263],{"class":98,"line":132},[96,11264,11265],{},"    url(r'^api-auth\u002F', include('rest_framework.urls', namespace='rest_framework')),\n",[96,11267,11268],{"class":98,"line":141},[96,11269,11270],{},"    url(r'^api\u002F', include(router.urls)),\n",[96,11272,11273],{"class":98,"line":152},[96,11274,11275],{},"    url(r'^.*$', TemplateView.as_view(template_name=\"index.html\")),\n",[96,11277,11278],{"class":98,"line":162},[96,11279,683],{},[13,11281,11282,11283,11286],{},"admin、api-auth、apiなど必要なルーティングはurlpatternsの最初の方に定義して、残りの全てのurlパターンを",[54,11284,11285],{},"r'^.*$"," という正規表現でカバーしています。ちなみにこの正規表現は「いかなる文字の全て」という意味です。そしてSPAのファイルをtemplateから引っ張ってきています。",[13,11288,11289,11290,435,11293,11296,11297,11300],{},"こうすることで、例えば ",[54,11291,11292],{},"https:\u002F\u002F~~~\u002Faaa",[54,11294,11295],{},"https:\u002F\u002F~~~\u002Fbbb\u002Faa","、などはSPAのファイルが返され、あとはSPAがそのURLに対しての処理を行ってくれます。一方で",[54,11298,11299],{},"https:\u002F\u002F~~~\u002Fadmin"," とすればDjangoの場合は管理画面にアクセスできます。",[3795,11302,11303],{},"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);}",{"title":92,"searchDepth":132,"depth":132,"links":11305},[],[5192],"2020-12-04",{},"\u002Farticles\u002Fdjango-nuxt-routing",{"title":11224,"description":11224},"articles\u002Fdjango-nuxt-routing",[5530,3845],"u-7H0bMOrS2JKrDbSIBUgp_ZgGLGpmCAjFGtTaDoVqc",{"id":11315,"title":11316,"body":11317,"category":11413,"createdAt":11414,"description":11316,"extension":3838,"index":3839,"meta":11415,"navigation":621,"path":11416,"publish":621,"seo":11417,"series":3839,"seriesTitle":3839,"stem":11418,"tag":11419,"thumbnail":3839,"updatedAt":3839,"__hash__":11420},"articles\u002Farticles\u002Fwhat-ssg-ssr-spa.md","Nuxt.jsのSPA、SSR、SSGって何？",{"type":10,"value":11318,"toc":11406},[11319,11322,11326,11340,11353,11367,11371,11374,11377,11380,11384,11387,11390,11393,11396,11400,11403],[13,11320,11321],{},"こんにちはjunです。Nuxt.jsを学びはじめにこれら３つの用語が？？？となったのを思い出し、復習がてら解説したいともいます。",[34,11323,11325],{"id":11324},"ssrssgspaって何","SSR・SSG・SPAって何？",[13,11327,11328,11329,11332,11333,11335,11336,11339],{},"SSRは ",[29,11330,11331],{},"S","erver ",[29,11334,11331],{},"ide ",[29,11337,11338],{},"R","enderingのことでVueなどによるjsレンダリングをブラウザではなくサーバーで行うことです。",[13,11341,11342,11343,11345,11346,11348,11349,11352],{},"SSGは ",[29,11344,11331],{},"tatic ",[29,11347,11331],{},"ite ",[29,11350,11351],{},"G","enerateのことで静的書き出しとも言われています。HTML、CSS、JSだけで構成されるファイルを生成することをいいます。",[13,11354,11355,11356,11358,11359,11362,11363,11366],{},"SPAは ",[29,11357,11331],{},"ingle ",[29,11360,11361],{},"P","age ",[29,11364,11365],{},"A","pplication と言われレンダーから何もかもブラウザ側で実行します。初期表示は遅いのでローディングは必須ですが、ページ遷移を感じさせず軽快なUIは非常に魅力的です。SEOを気にせず、操作するブラウザ環境が比較的新しいものに限定するならば選択しても良いと思います。内部的なwebアプリの操作画面などに使用されることが多いです。",[70,11368,11370],{"id":11369},"ssrの利点","SSRの利点",[13,11372,11373],{},"javaScriptは基本的にブラウザ側、つまり閲覧者のマシンで行われます。VueやReactはjsを用いてHTMLをレンダー（構築）するので、ブラウザ側でHTMLが完成します。PHPなどで書かれたwebサービスはサーバー側で完成したHTMLを閲覧者に配信します。",[13,11375,11376],{},"ブラウザでのレンダリングを前提に構築すると、サーバー側のHTMLは最低限の記述しかないためSEOが弱かったり、ブラウザ側の表示に時間がかかったり、古いブラウザや低スペックのスマホだと表示・動作が遅かったりするデメリットがあります。それを解決するのがSSRです。",[13,11378,11379],{},"本来ブラウザで実行されるjsをサーバー側で実行して、完成したHTMLを配信します。この場合、サーバーにはnode.jsが必要になりますが、クローラーは完成したHTMLを読み込み、またユーザー側も高速で表示することができます。",[70,11381,11383],{"id":11382},"ssgの利点","SSGの利点",[13,11385,11386],{},"昨今のwebアプリ・サイトはサーバーからの情報を当て込んだり、Ajaxなどで常に通信していることが多いです。しかしそれはwebアプリ（ブラウザ側）からサーバーへアクセスできる穴が必要で、そこを狙った脆弱性を通じてサーバーが攻撃されることがあります。（webアプリのソースとサーバで動く言語、DBが同居しているので危ない）",[13,11388,11389],{},"他にもサーバー側の実行が遅いと結果的にブラウザの表示が遅くなったり、もっと高速に表示したい場合はSSG、静的書き出しを行います。静的に書き出す際にjsを実行しますし、設計によってはあらかじめサーバーからの取得した情報も読み込んでおきます。",[13,11391,11392],{},"静的に書き出したものはHTML、CSS、JSだけであり基本的にはサーバーと通信して、DBからデータを取ってきたりなどもしません。そのためPHPやDBがインストールされていないサーバーに置いて、配信することが可能です。",[13,11394,11395],{},"配信側にサーバーへ対する攻撃手段が残らないのでセキュリティも格段に上がりますし、表示速度もある程度上がります。これがSSGの利点です。",[70,11397,11399],{"id":11398},"spaの利点","SPAの利点",[13,11401,11402],{},"SPAの利点は実装のしやすさと軽快なUIにあります。レンダーはブラウザで行われるのでサーバーには最低限のHTMLとJSが配信される様にしておけばOKです。SSG・SSRはサーバーにnode.jsを入れたり、webサーバーとあれこりしたりと配信する前に手間がかかります。",[13,11404,11405],{},"SPAはそんなめんどくさいことは必要なく、クラアントに適切なHTMLとJSを渡すだけで解決します。SEOに弱く、また表示と操作がブラウザの性能に依存するので会員制のサイトで、その操作部分などSEOを気にしないサイトやwebアプリであればガンガン使ってもいいと思います。",{"title":92,"searchDepth":132,"depth":132,"links":11407},[11408],{"id":11324,"depth":120,"text":11325,"children":11409},[11410,11411,11412],{"id":11369,"depth":132,"text":11370},{"id":11382,"depth":132,"text":11383},{"id":11398,"depth":132,"text":11399},[5192],"2020-11-23",{},"\u002Farticles\u002Fwhat-ssg-ssr-spa",{"title":11316,"description":11316},"articles\u002Fwhat-ssg-ssr-spa",[4902,3845],"usMpTQM9YqI1I4JGQYjVe7BELZbLtw1Et1nerwUPpek",{"id":11422,"title":11423,"body":11424,"category":12893,"createdAt":12894,"description":12895,"extension":3838,"index":99,"meta":12896,"navigation":621,"path":12897,"publish":621,"seo":12898,"series":12899,"seriesTitle":12895,"stem":12900,"tag":12901,"thumbnail":12902,"updatedAt":3839,"__hash__":12903},"series\u002Fseries\u002Fheadlesscms-strapi-1.md","headlessCMSのstrapi をnuxt.jsで静的書き出しを行う。その１：strapiとnuxtの連携",{"type":10,"value":11425,"toc":12865},[11426,11429,11432,11452,11455,11458,11469,11473,11479,11482,11488,11492,11495,11501,11504,11511,11522,11529,11532,11535,11541,11547,11550,11557,11560,11563,11566,11569,11572,11578,11588,11594,11597,11605,11608,11611,11614,11617,11621,11624,11630,11633,11639,11642,11645,11648,11651,11662,11665,11668,11671,11674,11677,11680,11683,11686,11692,11695,11698,11704,11707,11710,11713,11716,11733,11736,11739,11742,11745,11748,11751,11754,11757,11760,11763,11766,11769,11772,11776,11779,11782,11785,11789,11792,11795,11798,11801,11804,11815,11822,11826,11833,11836,11839,11843,11846,12143,12146,12267,12281,12284,12290,12295,12301,12315,12319,12322,12739,12742,12746,12749,12752,12759,12763,12766,12810,12817,12823,12830,12833,12836,12839,12842,12856,12859,12862],[13,11427,11428],{},"こんにちはjunです。新しいCMSを採用してみようという動きが社内であり、その過程で私がstrapiというheadlessCMSとnuxt.jsを用いて静的サイトを作成するとこまでやりました。",[13,11430,11431],{},"JAMstack構成でCMSを用いたwebサイトを作成できるということもあり、今までのCMSに変わっていく予感があったので調べてみました。シリーズ記事にして以下",[3739,11433,11434,11437,11440,11443,11446,11449],{},[3742,11435,11436],{},"headlessCMSとは何か、なぜ使うのか",[3742,11438,11439],{},"strapiの使い方",[3742,11441,11442],{},"strapiとnuxtの連携方法",[3742,11444,11445],{},"strapiでブログライクなデータ構築",[3742,11447,11448],{},"nuxtでの構築",[3742,11450,11451],{},"静的書き出し",[13,11453,11454],{},"の６つを中心に説明していきます。また完成したフロントとバックのソースはgithubに上げる予定です。今回の記事では上３つを行います。",[13,11456,11457],{},"なお解説で登場するアプリケーションのバージョンは以下の通りです。",[3739,11459,11460,11463,11466],{},[3742,11461,11462],{},"node.js 13.12.0",[3742,11464,11465],{},"strapi 3.2.5",[3742,11467,11468],{},"nuxt 2.14.6",[34,11470,11472],{"id":11471},"headlesscmsって何","headlessCMSって何？",[13,11474,11475,11476],{},"headlessCMSのheadというのはビューのことを言います。ビューは今あなたが見ているこのサイトの見た目そのもの、HTMLのことです。そしてその ",[29,11477,11478],{},"ビューの構築がCMSから切り離されているのが、ビューの生成処理が存在しないのがheadlessCMSです。",[13,11480,11481],{},"wordpressなどの一般的なCMSはビューの生成をwordpressという１つのシステムで行っています。リクエストに応じて対応するデータを引っ張ってきて、HTMLを生成してレスポンスとして返します。",[13,11483,11484,11485],{},"しかしheadlessCMSのビューは ",[29,11486,11487],{},"独立したフロントエンド プロジェクトを立てて、そのプロジェクトからAPIを呼び出してデータを取得して、主にjsを用いてHTMLをレンダーします。",[70,11489,11491],{"id":11490},"どうしてヘッドをレスするの","どうしてヘッドをレスするの？",[13,11493,11494],{},"例えばwordpressの場合はHTML構造とデータの出力をwordpressというPHPシステム1つで行います。つまりフロントエンド の構築をPHPで行います。しかしその場合VueやReactを用いたフロントの構築は難しいですし、フロントはwordpressのシステムに従った構築を行う必要があります。最近のUIが優れたサイトや、生産的にフロントを作る場合は大変です。",[13,11496,11497,11498],{},"できるだけフロントはJSで構築したい！でもデータもバックから呼び出したい！そんな時にheadlessCMSを用います。 ",[29,11499,11500],{},"フロントがCMSから分離することでバックエンドの都合や制限を受けにくくなります。（いわゆる疎結合な状態）",[13,11502,11503],{},"例えばwordpressのテーマ構築にはindex.php、page.php、header\u002Ffooter.php、single.phpと言った決まりきったビューファイルがあります。フロント構築はこの制限されたファイルを上手く使用する必要があります。デザインの都合や仕様によっては開発が困難になったりします。",[13,11505,11506,11507,11510],{},"さらに ",[29,11508,11509],{},"Nuxt.jsやNext.jsを用いることで静的書き出しを行うことができます。"," 静的に書き出すことでCMSの課題であった不正ログインや攻撃のリスク、そしてメモリ消費による処理能力ダウンを防ぐことができます。（ただし構成と運用によります）",[3739,11512,11513,11516,11519],{},[3742,11514,11515],{},"jsフレームワークを用いてリッチで生産性高くフロントエンド を開発できる。",[3742,11517,11518],{},"静的書き出しを行うことでセキュリティやパフォーマンスをあげることが可能",[3742,11520,11521],{},"バックエンドへAPIを送ることでJSONでデータを受け取れる。JSONならば様々な媒体へデータを配信できるので、再利用性が高い（ブログの内容をスマホアプリに使用するなど）",[13,11523,11524,11525,11528],{},"上記の様な構成を ",[29,11526,11527],{},"JAMStack","、javaScript API Markup Stackといいます。",[34,11530,11531],{"id":11531},"strapiとは",[13,11533,11534],{},"headlessCMSはまだ登場してからまだ日が浅く、また種類も多いです。その中でも特に有名なのはContentful、strapiそして日本ではmicroCMSが人気です。",[13,11536,11537,11540],{},[29,11538,11539],{},"strapiはheadlessCMSの中でもオープンソースでありバックエンドを自由にカスタマイズ可能です。"," ContentfulとmicroCMSはそれらの会社がバックエンドをホスティングしており、指定のAPIキーとエンドポイントへAIPを送ります。その分お金はかかりますが、アーキテクチャによっては完全なサーバレス構成が可能になります。",[13,11542,11543,11544],{},"今回の記事では ",[29,11545,11546],{},"カスタマイズ性に優れたstrapiを用いて説明を行います。",[34,11548,11549],{"id":11549},"アプリの構成",[13,11551,11552,11553,11556],{},"今回の説明では以下の図の様に ",[29,11554,11555],{},"バックエンドにstrapi、フロントエンド にnuxt.jsを用いて構築した上で静的書き出しを行います。"," ローカルのプロジェクトでバックとフロントはポートを分けて実質別のサーバの様にします。そしてデータベースは用意が面倒だったのでMAMPのmysqlを使用しています。",[42,11558],{":src":11559,":width":45,":center":46},"'_mix\u002FheadlessFlow.png'",[13,11561,11562],{},"そしてお知らせの様な定型の項目に沿って入力するコンテンツだけでなく、今のCMSの様に自由に項目を入力できる様なページも作ろうと思います。",[34,11564,11565],{"id":11565},"strapiとnuxtのインストール",[70,11567,11568],{"id":11568},"バックエンドのstrapiをいれる",[13,11570,11571],{},"まずは適当にフロントとバックをいれるheadlessCMSディレクトリを作成",[86,11573,11576],{"className":11574,"code":11575,"language":460},[457],"~ % mkdir headless && cd headless\n",[54,11577,11575],{"__ignoreMap":92},[13,11579,11580,11581,11587],{},"(公式サイト)",[96,11582,11583],{},[76,11584,11585],{"href":11585,"rel":11586},"https:\u002F\u002Fstrapi.io\u002Fdocumentation\u002Fdeveloper-docs\u002Flatest\u002Fsetup-deployment-guides\u002Finstallation\u002Fcli.html#step-1-make-sure-requirements-are-met",[584],"にもある様にnpxを用いてstrapiをインストール。カスタムモードで行います。",[86,11589,11592],{"className":11590,"code":11591,"language":460},[457],"strapinpx create-strapi-app backend\n? Choose your installation type Custom (manual settings)\n? Choose your default database client mysql\n? Database name: strapi\n? Host: 127.0.0.1 (DBのホスト先）\n? Port: 8889 (DBのポート）\n? Username: (DBのユーザー）\n? Password: (DBのパスワード）\n? Enable SSL connection: No(開発環境だから）\n\nCreating a project with custom database options.\nCreating files.\nDependencies installed successfully.\n\nheadless % cd backend && npm run develop\n",[54,11593,11591],{"__ignoreMap":92},[70,11595,11596],{"id":11596},"strapiユーザーを作成する",[13,11598,2299,11599,11604],{},[76,11600,11603],{"href":11601,"rel":11602},"http:\u002F\u002Flocalhost:1337\u002Fadmin)%5Bhttp:\u002F\u002Flocalhost:1337\u002Fadmin",[584],"http:\u002F\u002Flocalhost:1337\u002Fadmin)[http:\u002F\u002Flocalhost:1337\u002Fadmin"," ]ビルドをすると1337のポートが開くので指示されたURLにアクセスします。すると以下の様なユーザー作成画面が表示されます。最初に作成されるユーザーはスーパーユーザーになります。",[42,11606],{":src":11607,":width":45,":center":46},"'_mix\u002Fstrapi_create_user-735x1024.png'",[13,11609,11610],{},"ここで任意の名前、アドレス、パスワードを設定します。「READY TO START」を押してログインします。",[42,11612],{":src":11613,":width":1656},"'_mix\u002Fstapi_fitst.png'",[13,11615,11616],{},"最近になって日本語が当てられる様になったらしいですが、ほとんどが英語のままです。日本語表示はあまり期待しない方がいいです。とりあえずバックエンドはこれでインストール完了です。",[70,11618,11620],{"id":11619},"nuxtjs-のインストール","nuxt.js のインストール",[13,11622,11623],{},"ではフロントを構築するnuxt.jsを入れましょう。",[86,11625,11628],{"className":11626,"code":11627,"language":460},[457],"headless % npx create-nuxt-app frontend\n",[54,11629,11627],{"__ignoreMap":92},[13,11631,11632],{},"静的に書き出すのでUniversalモードでデプロイターゲットは Static を選びましょう。UIは作るのが面倒なのでbootstrap使います。あしからず。",[86,11634,11637],{"className":11635,"code":11636,"language":460},[457],"? Project name: frontend\n? Programming language: JavaScript\n? Package manager: Npm\n? UI framework: Bootstrap Vue\n? Testing framework: None\n? Rendering mode: Universal (SSR \u002F SSG)\n? Deployment target: Static (Static\u002FJAMStack hosting)\n? Development tools: (Press \u003Cspace> to select, \u003Ca> to toggle all, \u003Ci> to invert selection)\n? What is your GitHub username? jun\n? Version control system: Git\n\nSuccessfully created project frontend\n\nheadless % cd frontend && npm run dev\n",[54,11638,11636],{"__ignoreMap":92},[13,11640,11641],{},"npm run dev で開発サーバーを立ち上げます。localhost:3000 にアクセスするとお馴染みのnuxt.jsの画面がみれます。",[34,11643,11644],{"id":11644},"コンテンツタイプを作成する",[13,11646,11647],{},"フロントにデータを表示しようにも、バックにデータがなければ何も始まりません。まずはstrapiで表示するデータを作成します。そこでstrapiで「コンテンツタイプ」というものでコンテンツを定義してデータの型を作成します。",[13,11649,11650],{},"とりあえず以下の様なカラム を持つコンテンツ「お知らせ」を作ります。",[3739,11652,11653,11656,11659],{},[3742,11654,11655],{},"タイトル",[3742,11657,11658],{},"内容（リッチテキスト）",[3742,11660,11661],{},"サムネイル",[13,11663,11664],{},"headlessCMSではまず最初に「どんなデータ構成を持つコンテンツを作るか？」ということを考えます。コンテンツを構成する要素を洗い出し、ユーザーが登録する項目を決定します。strapiでの操作はまず画面左の「Contents-Type Builder」をクリックします。画面が切り替わり、「コンテンツタイプ」と書かれているとこの「Create collection type」をクリックして追加します。",[42,11666],{":src":11667,":width":11130,":center":46},"'_mix\u002Fsch-2020-11-03-0.00.31-300x197.png'",[13,11669,11670],{},"するとコンテンツタイプの名前をいれる様に言われますので、入力します。「お知らせ」なので「newsItem」としておきます。（なぜかタイトル名を「news」にすると400エラーになってしまいます。予約されている可能性あり。）",[42,11672],{":src":11673,":width":1656},"'_mix\u002Fcreate_conten-768x283.png'",[13,11675,11676],{},"「続ける」をクリックすると次は項目とそのデータ型を登録します。",[42,11678],{":src":11679,":width":1656},"'_mix\u002Fcreate_news_items-768x480.png'",[13,11681,11682],{},"タイトル→文字（text）、内容→リッチテキスト（Rich Text : Long Text)、サムネイル→画像（Media）とします。例えば「タイトル」は以下の通りです。",[42,11684],{":src":11685,":width":1656},"'_mix\u002Fcreate_news_title-768x399.png'",[13,11687,11688,11691],{},[54,11689,11690],{},"Name","に入力された値はカラム名となるので日本語入力できません。引き続き入力する場合は「+Add another filed」で続け、項目の設定を終了する場合は「終了」を押します。ひとまず以下の様に設定します。",[42,11693],{":src":11694,":width":1656},"'_mix\u002Fnews_ct-768x338.png'",[13,11696,11697],{},"そして「保存」を押すとこのコンテンツタイプが登録されます。ターミナルをみてみると",[86,11699,11702],{"className":11700,"code":11701,"language":460},[457],"[2020-11-02T16:43:29.201Z] info File created: \u002FUsers\u002Fjun\u002Fheadless\u002Fbackend\u002Fapi\u002Fnews-items\u002Fconfig\u002Froutes.json\n[2020-11-02T16:43:29.201Z] info File created: \u002FUsers\u002Fjun\u002Fheadless\u002Fbackend\u002Fapi\u002Fnews-items\u002Fcontrollers\u002Fnews-items.js\n[2020-11-02T16:43:29.201Z] info File created: \u002FUsers\u002Fjun\u002Fheadless\u002Fbackend\u002Fapi\u002Fnews-items\u002Fmodels\u002Fnews-items.js\n[2020-11-02T16:43:29.201Z] info File created: \u002FUsers\u002Fjun\u002Fheadless\u002Fbackend\u002Fapi\u002Fnews-items\u002Fmodels\u002Fnews-items.settings.json\n[2020-11-02T16:43:29.201Z] info File created: \u002FUsers\u002Fjun\u002Fheadless\u002Fbackend\u002Fapi\u002Fnews-items\u002Fservices\u002Fnews-items.js\n[2020-11-02T16:43:29.588Z] debug POST \u002Fcontent-type-builder\u002Fcontent-types (650 ms) 201\n[2020-11-02T16:43:29.614Z] info The server is restarting\n",[54,11703,11701],{"__ignoreMap":92},[13,11705,11706],{},"content-typeを作成するためのAPIが POSTされていることがわかります。実はstrapi内でもこの様にREST APIで呼び合ってコンテンツの編集を行っています。",[70,11708,11709],{"id":11709},"感覚的にはテーブルを作成",[13,11711,11712],{},"コンテンツタイプ作成の手順を一通りみてみるとDBでテーブルを作成したり、MVCフレームワークの「モデル」部分の実装と操作が似ています。というか同じです。コンテンツタイプでデータ項目と型を決定します。さらにstrapiではこのコンテンツタイプに応じたエンドポイントも作成してくれます。",[13,11714,11715],{},"headlessCMSはこの様に",[3739,11717,11718,11721,11724,11727,11730],{},[3742,11719,11720],{},"データテーブルとそのカラム・データ型の作成",[3742,11722,11723],{},"エンドポイントの自動作成",[3742,11725,11726],{},"エンドポイントの権限管理",[3742,11728,11729],{},"自動マイグレーション",[3742,11731,11732],{},"コンテンツタイプ（モデル）に応じたレコードの挿入、削除",[13,11734,11735],{},"これらを提供してくれます。",[70,11737,11738],{"id":11738},"お知らせを１件登録する",[13,11740,11741],{},"では早速レコードを１つ登録してみましょう。コレクションタイプ（画面左上）に「newsItem」が登録されているので、それをクリックします。すると以下の様な一覧画面が表示されます。",[42,11743],{":src":11744,":width":1656},"'_mix\u002Fcreate_news_record-768x246.png'",[13,11746,11747],{},"レコードを登録するために右上の「newsItemを追加」をクリックします。すると先程コンテンツタイプ作成で定義した項目の入力欄が出現します。そこに値を入力します。",[42,11749],{":src":11750,":width":1656},"'_mix\u002Fnews_input-768x263.png'",[13,11752,11753],{},"テキストエリア はプレーンテキスト、リッチテキストはマークダウンで入力します。画像を定義した箇所はファイルアップローダーが起動するので、ファイルをアップロードできますし、アップロードしたものを選択することもできます。",[13,11755,11756],{},"内容が入力し終わったら画面右上の「保存」をクリックします。そして保存終了後に隣の「Publish」をクリックしてこの登録したレコードが外部に公開できる様になります。",[13,11758,11759],{},"このPublishをクリックしないと、このデータをAPIで呼び出しても404が帰ってきますので注意。",[70,11761,11762],{"id":11762},"公開するためにもう一歩",[13,11764,11765],{},"現段階ではまだ先程登録したnewsItemは外部から呼び出せません。「設定」から「権限とロール」をクリックします。権限とロールでは公開するコンテンツタイプやPOST、 PUT、DELETEの権限設定を行えます。",[42,11767],{":src":11768,":width":1656},"'_mix\u002Fpermission-768x223'",[13,11770,11771],{},"初期では「Authenticated（認証ユーザー）」「Public（匿名・全てのリクエスト）」のロールがあります。Publicをまずクリックすると、それぞれの権限設定が表示されます。",[42,11773],{":src":11774,":width":11775,":center":46},"'_mix\u002Froles-768x634.png'","'600px'",[13,11777,11778],{},"登録したnewsItemの設定があります。ここで findとcount、findoneにチェックを入れます。そして「保存」を押します。 チェックした３つは読み取り専用です。ここでcreateやdeleteにチェックをいれると、APIを通じて誰もがデータを操作できてしまうので気をつけてください。",[13,11780,11781],{},"逆にstrapiで登録したユーザーが外部からデータを操作する場合は Authenticatedでチェックを入れます。私は面倒なのでAuthenticatedは Select all にして、Publicは読み取りのみにしています。",[13,11783,11784],{},"ちなみにロールは３つまで無料です。３ロール以上はなぜか課金が必要です。",[70,11786,11788],{"id":11787},"apiドキュメントプラグインをインストール","APIドキュメントプラグインをインストール",[13,11790,11791],{},"フロントの構築に移る前にAPIドキュメントプラグインを入れておきます。このプラグインは最初から入っておらず、「マーケットプレイス」から無料インストール可能です。このプラグインをいれるとコンテンツタイプに応じたAPIのエンドポイント一覧ドキュメントを自動で作成してくれます。",[42,11793],{":src":11794,":width":11130,":center":46},"'_mix\u002Fdocument-300x206.png'",[13,11796,11797],{},"メニューにDocumentationが現れ、その画面から「Open the Documentation」でドキュメントが開きます。登録したnewsItemのAPIもあります。",[34,11799,11800],{"id":11800},"フロントから呼び出してみる",[13,11802,11803],{},"ドキュメントをみてみると",[3739,11805,11806,11809,11812],{},[3742,11807,11808],{},"\u002Fnews-items で一覧",[3742,11810,11811],{},"\u002Fnews-items\u002Fcount で総数",[3742,11813,11814],{},"\u002Fnews-items\u002F{id}　でidで紐づいたデータ",[13,11816,11817,11818,11821],{},"を取得することができます。idはstrapiのnewsItemの一覧画面で見れます。数字で表されます。さっき例で作ったのは最初なので ",[54,11819,11820],{},"\u002Fnews-items\u002F1"," で取得できます。",[70,11823,11825],{"id":11824},"talend-api-tester-でテスト","Talend API Tester でテスト",[13,11827,11828,11829,11832],{},"とりあえずAPIがきちんと呼び出せるか、内容が取れるかが確かめたい場合はTalend API Testerなどを使用してAPIをテストすることができます。以下の様に ",[54,11830,11831],{},"http:\u002F\u002Flocalhost:1337\u002Fnews-items\u002F1"," に対してAPIを投げてみると先程の登録した内容がレスポンスにJSONで戻ってきました。",[42,11834],{":src":11835,":width":1656},"'_mix\u002Fapi_test-768x461.png'",[13,11837,11838],{},"Nuxtでの構築もこのJSONを元に作成します。",[70,11840,11842],{"id":11841},"strapi-nuxt-moduleをインストール","strapi nuxt moduleをインストール",[13,11844,11845],{},"nuxtでAPIベースの呼び出しを行う場合、呼び出し用のプラグインを自作することがあります。例えば以下の様な感じです。",[86,11847,11850],{"className":1910,"code":11848,"filename":11849,"language":1912,"meta":92,"style":92},"export default function(context,inject){\n    \u002F\u002F axios　インスタンス\n    const api = context.$axios.create({\n        timeout:5000,\n        headers:{\n            'Content-Type': 'application\u002Fjson',\n            'X-Requested-With': 'XMLHttpRequest',\n        }\n    })\n\n    \u002F\u002F APIの呼び出し先を設定\n    api.setBaseURL(context.env.apiBaseURL);\n\n \n    api.onRequest(config=>{\n        \u002F\u002F リクエスト時の処理\n    })\n\n\n    api.onResponseError(err=>{\n        switch(err.response.status){\n            \u002F\u002F ステータスごとに異なる処理\n        }\n    })\n\n    \u002F\u002F こうすると $API で上記の設定をしたaxiosインスタンスを呼び出せる。つまり通信処理を共通化できる。\n    inject('API',api);\n}\n","plugins\u002Faxios.js",[54,11851,11852,11872,11877,11901,11913,11920,11941,11961,11965,11971,11975,11980,12008,12012,12016,12033,12038,12044,12048,12052,12069,12092,12097,12101,12107,12111,12116,12139],{"__ignoreMap":92},[96,11853,11854,11856,11858,11860,11862,11865,11867,11870],{"class":98,"line":99},[96,11855,2332],{"class":2331},[96,11857,2335],{"class":2331},[96,11859,4044],{"class":2285},[96,11861,2299],{"class":106},[96,11863,11864],{"class":3329},"context",[96,11866,3016],{"class":106},[96,11868,11869],{"class":3329},"inject",[96,11871,3333],{"class":106},[96,11873,11874],{"class":98,"line":120},[96,11875,11876],{"class":4284},"    \u002F\u002F axios　インスタンス\n",[96,11878,11879,11881,11884,11886,11888,11890,11892,11894,11897,11899],{"class":98,"line":132},[96,11880,6967],{"class":2285},[96,11882,11883],{"class":128}," api",[96,11885,3199],{"class":106},[96,11887,4547],{"class":128},[96,11889,2312],{"class":106},[96,11891,4561],{"class":128},[96,11893,2312],{"class":106},[96,11895,11896],{"class":2295},"create",[96,11898,2299],{"class":102},[96,11900,788],{"class":106},[96,11902,11903,11906,11908,11911],{"class":98,"line":141},[96,11904,11905],{"class":102},"        timeout",[96,11907,107],{"class":106},[96,11909,11910],{"class":4452},"5000",[96,11912,1940],{"class":106},[96,11914,11915,11918],{"class":98,"line":152},[96,11916,11917],{"class":102},"        headers",[96,11919,1927],{"class":106},[96,11921,11922,11925,11928,11930,11932,11934,11937,11939],{"class":98,"line":162},[96,11923,11924],{"class":106},"            '",[96,11926,11927],{"class":102},"Content-Type",[96,11929,1964],{"class":106},[96,11931,107],{"class":106},[96,11933,110],{"class":106},[96,11935,11936],{"class":113},"application\u002Fjson",[96,11938,1964],{"class":106},[96,11940,1940],{"class":106},[96,11942,11943,11945,11948,11950,11952,11954,11957,11959],{"class":98,"line":171},[96,11944,11924],{"class":106},[96,11946,11947],{"class":102},"X-Requested-With",[96,11949,1964],{"class":106},[96,11951,107],{"class":106},[96,11953,110],{"class":106},[96,11955,11956],{"class":113},"XMLHttpRequest",[96,11958,1964],{"class":106},[96,11960,1940],{"class":106},[96,11962,11963],{"class":98,"line":181},[96,11964,1376],{"class":106},[96,11966,11967,11969],{"class":98,"line":4},[96,11968,4637],{"class":106},[96,11970,3477],{"class":102},[96,11972,11973],{"class":98,"line":196},[96,11974,622],{"emptyLinePlaceholder":621},[96,11976,11977],{"class":98,"line":204},[96,11978,11979],{"class":4284},"    \u002F\u002F APIの呼び出し先を設定\n",[96,11981,11982,11985,11987,11990,11992,11994,11996,11999,12001,12004,12006],{"class":98,"line":214},[96,11983,11984],{"class":128},"    api",[96,11986,2312],{"class":106},[96,11988,11989],{"class":2295},"setBaseURL",[96,11991,2299],{"class":102},[96,11993,11864],{"class":128},[96,11995,2312],{"class":106},[96,11997,11998],{"class":128},"env",[96,12000,2312],{"class":106},[96,12002,12003],{"class":128},"apiBaseURL",[96,12005,2309],{"class":102},[96,12007,2326],{"class":106},[96,12009,12010],{"class":98,"line":228},[96,12011,622],{"emptyLinePlaceholder":621},[96,12013,12014],{"class":98,"line":240},[96,12015,129],{"class":102},[96,12017,12018,12020,12022,12025,12027,12029,12031],{"class":98,"line":252},[96,12019,11984],{"class":128},[96,12021,2312],{"class":106},[96,12023,12024],{"class":2295},"onRequest",[96,12026,2299],{"class":102},[96,12028,2315],{"class":3329},[96,12030,4594],{"class":2285},[96,12032,788],{"class":106},[96,12034,12035],{"class":98,"line":264},[96,12036,12037],{"class":4284},"        \u002F\u002F リクエスト時の処理\n",[96,12039,12040,12042],{"class":98,"line":275},[96,12041,4637],{"class":106},[96,12043,3477],{"class":102},[96,12045,12046],{"class":98,"line":283},[96,12047,622],{"emptyLinePlaceholder":621},[96,12049,12050],{"class":98,"line":293},[96,12051,622],{"emptyLinePlaceholder":621},[96,12053,12054,12056,12058,12061,12063,12065,12067],{"class":98,"line":301},[96,12055,11984],{"class":128},[96,12057,2312],{"class":106},[96,12059,12060],{"class":2295},"onResponseError",[96,12062,2299],{"class":102},[96,12064,4650],{"class":3329},[96,12066,4594],{"class":2285},[96,12068,788],{"class":106},[96,12070,12071,12074,12076,12078,12080,12083,12085,12088,12090],{"class":98,"line":312},[96,12072,12073],{"class":2331},"        switch",[96,12075,2299],{"class":102},[96,12077,4650],{"class":128},[96,12079,2312],{"class":106},[96,12081,12082],{"class":128},"response",[96,12084,2312],{"class":106},[96,12086,12087],{"class":128},"status",[96,12089,2309],{"class":102},[96,12091,788],{"class":106},[96,12093,12094],{"class":98,"line":323},[96,12095,12096],{"class":4284},"            \u002F\u002F ステータスごとに異なる処理\n",[96,12098,12099],{"class":98,"line":334},[96,12100,1376],{"class":106},[96,12102,12103,12105],{"class":98,"line":345},[96,12104,4637],{"class":106},[96,12106,3477],{"class":102},[96,12108,12109],{"class":98,"line":354},[96,12110,622],{"emptyLinePlaceholder":621},[96,12112,12113],{"class":98,"line":366},[96,12114,12115],{"class":4284},"    \u002F\u002F こうすると $API で上記の設定をしたaxiosインスタンスを呼び出せる。つまり通信処理を共通化できる。\n",[96,12117,12118,12121,12123,12125,12128,12130,12132,12135,12137],{"class":98,"line":375},[96,12119,12120],{"class":2295},"    inject",[96,12122,2299],{"class":102},[96,12124,1964],{"class":106},[96,12126,12127],{"class":113},"API",[96,12129,1964],{"class":106},[96,12131,3016],{"class":106},[96,12133,12134],{"class":128},"api",[96,12136,2309],{"class":102},[96,12138,2326],{"class":106},[96,12140,12141],{"class":98,"line":383},[96,12142,987],{"class":106},[13,12144,12145],{},"こうすることでAPIの呼び出し処理を共通化できます。しかしstrapiには上記の様な strapi nuxt module というものがあります。そのモジュールを用いると以下の様に呼び出しが可能です。",[86,12147,12149],{"className":1910,"code":12148,"language":1912,"meta":92,"style":92},"async asyncData(){\n    await this.$strapi.findOne('newsItem',1)\n        .then(async res=>{\n            \u002F\u002F成功時の処理 res にstrapi からのデータが入っている\n        })\n        .catch(err=>{\n　　　　　　　\u002F\u002Fエラー時の処理\n            console.log(error);\n        })\n}\n",[54,12150,12151,12163,12192,12208,12213,12220,12234,12239,12257,12263],{"__ignoreMap":92},[96,12152,12153,12156,12159,12161],{"class":98,"line":99},[96,12154,12155],{"class":128},"async ",[96,12157,12158],{"class":2295},"asyncData",[96,12160,2318],{"class":128},[96,12162,788],{"class":106},[96,12164,12165,12167,12169,12172,12174,12177,12179,12181,12184,12186,12188,12190],{"class":98,"line":120},[96,12166,4556],{"class":2331},[96,12168,3246],{"class":106},[96,12170,12171],{"class":128},"$strapi",[96,12173,2312],{"class":106},[96,12175,12176],{"class":2295},"findOne",[96,12178,2299],{"class":102},[96,12180,1964],{"class":106},[96,12182,12183],{"class":113},"newsItem",[96,12185,1964],{"class":106},[96,12187,3016],{"class":106},[96,12189,4453],{"class":4452},[96,12191,3477],{"class":102},[96,12193,12194,12196,12198,12200,12202,12204,12206],{"class":98,"line":132},[96,12195,6391],{"class":106},[96,12197,4583],{"class":2295},[96,12199,2299],{"class":102},[96,12201,4588],{"class":2285},[96,12203,4591],{"class":3329},[96,12205,4594],{"class":2285},[96,12207,788],{"class":106},[96,12209,12210],{"class":98,"line":141},[96,12211,12212],{"class":4284},"            \u002F\u002F成功時の処理 res にstrapi からのデータが入っている\n",[96,12214,12215,12218],{"class":98,"line":152},[96,12216,12217],{"class":106},"        }",[96,12219,3477],{"class":102},[96,12221,12222,12224,12226,12228,12230,12232],{"class":98,"line":162},[96,12223,6391],{"class":106},[96,12225,3586],{"class":2295},[96,12227,2299],{"class":102},[96,12229,4650],{"class":3329},[96,12231,4594],{"class":2285},[96,12233,788],{"class":106},[96,12235,12236],{"class":98,"line":171},[96,12237,12238],{"class":4284},"　　　　　　　\u002F\u002Fエラー時の処理\n",[96,12240,12241,12244,12246,12249,12251,12253,12255],{"class":98,"line":181},[96,12242,12243],{"class":128},"            console",[96,12245,2312],{"class":106},[96,12247,12248],{"class":2295},"log",[96,12250,2299],{"class":102},[96,12252,3591],{"class":128},[96,12254,2309],{"class":102},[96,12256,2326],{"class":106},[96,12258,12259,12261],{"class":98,"line":4},[96,12260,12217],{"class":106},[96,12262,3477],{"class":102},[96,12264,12265],{"class":98,"line":196},[96,12266,987],{"class":106},[13,12268,12269,12271,12272,12275,12276],{},[54,12270,12171],{}," というコンテキストが自動で追加され ",[54,12273,12274],{},"findOne('contens-type-name',id)","で呼び出すことができます。loginや認証ユーザーのAPIもあるのでnuxt strapiモジュールを入れておくと構築がしやすくなります。",[76,12277,12280],{"href":12278,"rel":12279},"https:\u002F\u002Fstrapi.nuxtjs.org\u002F",[584],"公式サイト",[13,12282,12283],{},"npm でインストールしておきます。",[86,12285,12288],{"className":12286,"code":12287,"language":460},[457],"npm install @nuxtjs\u002Fstrapi\n",[54,12289,12287],{"__ignoreMap":92},[13,12291,12292,12294],{},[54,12293,1891],{},"で以下の様に追記します。",[86,12296,12299],{"className":12297,"code":12298,"language":460},[457],"modules: [\n  '@nuxtjs\u002Fstrapi',\n],\nstrapi: {\n  url:'http:\u002F\u002Flocalhost:1337'\n},\n",[54,12300,12298],{"__ignoreMap":92},[13,12302,12303,12304,12306,12307,12310,12311,12314],{},"こうするとモジュールが有効になり、",[54,12305,12171],{},"のコンテキストが使用可能になります。",[54,12308,12309],{},"strapi:{}","でオプションが指定できます。strapiがホストされているurlを書いておきます。開発版なので",[54,12312,12313],{},"http:\u002F\u002Flocalhost:1337","にしていますが実際の運用ではenvファイルに書いておくなりしておきましょう。",[70,12316,12318],{"id":12317},"とりあえず-初期画面に表示してみる","とりあえず 初期画面に表示してみる",[13,12320,12321],{},"nuxt.jsをインストールした時に既に存在している pages\u002Findex.vue に登録したニュースをboostrap のcardを用いて表示させてみましょう。以下の様に記述",[86,12323,12326],{"className":2443,"code":12324,"filename":12325,"language":2446,"meta":92,"style":92},"\u003Ctemplate>\n  \u003Cdiv class=\"container\">\n    \u003Cdiv>\n      \u003Cb-card\n        :title=\"title\"\n        :img-src=\"img\"\n        img-top\n        tag=\"article\"\n        style=\"max-width: 20rem;\"\n        class=\"my-5\"\n      >\n        \u003Cb-card-text>\n          {{content}}\n        \u003C\u002Fb-card-text>\n      \u003C\u002Fb-card>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n\n\u003Cscript>\nexport default {\n  data(){\n    return{\n\n    }\n  },\n  async asyncData(context){\n    return await context.$strapi.findOne('news-items',1)\n      .then(res=>{\n        return {\n          title:res.title,\n          img:context.env.envSet.IMG_BASE_URL+res.thumbnail.url,\n          content:res.conctent\n        }\n      })\n      .catch(err=>{\n        console.error(err)\n      })\n  }\n}\n\u003C\u002Fscript>\n","pages\u002Findex.vue",[54,12327,12328,12336,12355,12363,12370,12383,12396,12401,12414,12428,12442,12447,12456,12461,12469,12478,12486,12494,12502,12506,12514,12522,12529,12535,12539,12543,12547,12559,12590,12605,12611,12626,12665,12679,12683,12689,12703,12717,12723,12727,12731],{"__ignoreMap":92},[96,12329,12330,12332,12334],{"class":98,"line":99},[96,12331,2453],{"class":106},[96,12333,2456],{"class":102},[96,12335,2459],{"class":106},[96,12337,12338,12340,12342,12344,12346,12348,12351,12353],{"class":98,"line":120},[96,12339,8879],{"class":106},[96,12341,4138],{"class":102},[96,12343,6145],{"class":2285},[96,12345,2292],{"class":106},[96,12347,2477],{"class":106},[96,12349,12350],{"class":113},"container",[96,12352,2477],{"class":106},[96,12354,2459],{"class":106},[96,12356,12357,12359,12361],{"class":98,"line":132},[96,12358,2464],{"class":106},[96,12360,4138],{"class":102},[96,12362,2459],{"class":106},[96,12364,12365,12367],{"class":98,"line":141},[96,12366,2580],{"class":106},[96,12368,12369],{"class":102},"b-card\n",[96,12371,12372,12375,12377,12379,12381],{"class":98,"line":152},[96,12373,12374],{"class":2285},"        :title",[96,12376,2292],{"class":106},[96,12378,2477],{"class":106},[96,12380,4955],{"class":113},[96,12382,225],{"class":106},[96,12384,12385,12388,12390,12392,12394],{"class":98,"line":162},[96,12386,12387],{"class":2285},"        :img-src",[96,12389,2292],{"class":106},[96,12391,2477],{"class":106},[96,12393,9153],{"class":113},[96,12395,225],{"class":106},[96,12397,12398],{"class":98,"line":171},[96,12399,12400],{"class":2285},"        img-top\n",[96,12402,12403,12406,12408,12410,12412],{"class":98,"line":181},[96,12404,12405],{"class":2285},"        tag",[96,12407,2292],{"class":106},[96,12409,2477],{"class":106},[96,12411,8882],{"class":113},[96,12413,225],{"class":106},[96,12415,12416,12419,12421,12423,12426],{"class":98,"line":4},[96,12417,12418],{"class":2285},"        style",[96,12420,2292],{"class":106},[96,12422,2477],{"class":106},[96,12424,12425],{"class":113},"max-width: 20rem;",[96,12427,225],{"class":106},[96,12429,12430,12433,12435,12437,12440],{"class":98,"line":196},[96,12431,12432],{"class":2285},"        class",[96,12434,2292],{"class":106},[96,12436,2477],{"class":106},[96,12438,12439],{"class":113},"my-5",[96,12441,225],{"class":106},[96,12443,12444],{"class":98,"line":204},[96,12445,12446],{"class":106},"      >\n",[96,12448,12449,12451,12454],{"class":98,"line":214},[96,12450,2602],{"class":106},[96,12452,12453],{"class":102},"b-card-text",[96,12455,2459],{"class":106},[96,12457,12458],{"class":98,"line":228},[96,12459,12460],{"class":128},"          {{content}}\n",[96,12462,12463,12465,12467],{"class":98,"line":240},[96,12464,2790],{"class":106},[96,12466,12453],{"class":102},[96,12468,2459],{"class":106},[96,12470,12471,12473,12476],{"class":98,"line":252},[96,12472,2967],{"class":106},[96,12474,12475],{"class":102},"b-card",[96,12477,2459],{"class":106},[96,12479,12480,12482,12484],{"class":98,"line":264},[96,12481,2976],{"class":106},[96,12483,4138],{"class":102},[96,12485,2459],{"class":106},[96,12487,12488,12490,12492],{"class":98,"line":275},[96,12489,8911],{"class":106},[96,12491,4138],{"class":102},[96,12493,2459],{"class":106},[96,12495,12496,12498,12500],{"class":98,"line":283},[96,12497,2641],{"class":106},[96,12499,2456],{"class":102},[96,12501,2459],{"class":106},[96,12503,12504],{"class":98,"line":293},[96,12505,622],{"emptyLinePlaceholder":621},[96,12507,12508,12510,12512],{"class":98,"line":301},[96,12509,2453],{"class":106},[96,12511,3000],{"class":102},[96,12513,2459],{"class":106},[96,12515,12516,12518,12520],{"class":98,"line":312},[96,12517,2332],{"class":2331},[96,12519,2335],{"class":2331},[96,12521,2146],{"class":106},[96,12523,12524,12527],{"class":98,"line":323},[96,12525,12526],{"class":102},"  data",[96,12528,3069],{"class":106},[96,12530,12531,12533],{"class":98,"line":334},[96,12532,7075],{"class":2331},[96,12534,788],{"class":106},[96,12536,12537],{"class":98,"line":345},[96,12538,622],{"emptyLinePlaceholder":621},[96,12540,12541],{"class":98,"line":354},[96,12542,954],{"class":106},[96,12544,12545],{"class":98,"line":366},[96,12546,2382],{"class":106},[96,12548,12549,12551,12553,12555,12557],{"class":98,"line":375},[96,12550,4532],{"class":2285},[96,12552,6249],{"class":102},[96,12554,2299],{"class":106},[96,12556,11864],{"class":3329},[96,12558,3333],{"class":106},[96,12560,12561,12563,12565,12567,12569,12571,12573,12575,12577,12579,12582,12584,12586,12588],{"class":98,"line":383},[96,12562,7075],{"class":2331},[96,12564,6280],{"class":2331},[96,12566,4547],{"class":128},[96,12568,2312],{"class":106},[96,12570,12171],{"class":128},[96,12572,2312],{"class":106},[96,12574,12176],{"class":2295},[96,12576,2299],{"class":102},[96,12578,1964],{"class":106},[96,12580,12581],{"class":113},"news-items",[96,12583,1964],{"class":106},[96,12585,3016],{"class":106},[96,12587,4453],{"class":4452},[96,12589,3477],{"class":102},[96,12591,12592,12595,12597,12599,12601,12603],{"class":98,"line":393},[96,12593,12594],{"class":106},"      .",[96,12596,4583],{"class":2295},[96,12598,2299],{"class":102},[96,12600,4621],{"class":3329},[96,12602,4594],{"class":2285},[96,12604,788],{"class":106},[96,12606,12607,12609],{"class":98,"line":875},[96,12608,3074],{"class":2331},[96,12610,2146],{"class":106},[96,12612,12613,12616,12618,12620,12622,12624],{"class":98,"line":880},[96,12614,12615],{"class":102},"          title",[96,12617,107],{"class":106},[96,12619,4621],{"class":128},[96,12621,2312],{"class":106},[96,12623,4955],{"class":128},[96,12625,1940],{"class":106},[96,12627,12628,12631,12633,12635,12637,12639,12641,12644,12646,12649,12652,12654,12656,12658,12660,12663],{"class":98,"line":885},[96,12629,12630],{"class":102},"          img",[96,12632,107],{"class":106},[96,12634,11864],{"class":128},[96,12636,2312],{"class":106},[96,12638,11998],{"class":128},[96,12640,2312],{"class":106},[96,12642,12643],{"class":128},"envSet",[96,12645,2312],{"class":106},[96,12647,12648],{"class":128},"IMG_BASE_URL",[96,12650,12651],{"class":106},"+",[96,12653,4621],{"class":128},[96,12655,2312],{"class":106},[96,12657,5045],{"class":128},[96,12659,2312],{"class":106},[96,12661,12662],{"class":128},"url",[96,12664,1940],{"class":106},[96,12666,12667,12670,12672,12674,12676],{"class":98,"line":890},[96,12668,12669],{"class":102},"          content",[96,12671,107],{"class":106},[96,12673,4621],{"class":128},[96,12675,2312],{"class":106},[96,12677,12678],{"class":128},"conctent\n",[96,12680,12681],{"class":98,"line":896},[96,12682,1376],{"class":106},[96,12684,12685,12687],{"class":98,"line":901},[96,12686,9334],{"class":106},[96,12688,3477],{"class":102},[96,12690,12691,12693,12695,12697,12699,12701],{"class":98,"line":906},[96,12692,12594],{"class":106},[96,12694,3586],{"class":2295},[96,12696,2299],{"class":102},[96,12698,4650],{"class":3329},[96,12700,4594],{"class":2285},[96,12702,788],{"class":106},[96,12704,12705,12707,12709,12711,12713,12715],{"class":98,"line":911},[96,12706,4659],{"class":128},[96,12708,2312],{"class":106},[96,12710,3591],{"class":2295},[96,12712,2299],{"class":102},[96,12714,4650],{"class":128},[96,12716,3477],{"class":102},[96,12718,12719,12721],{"class":98,"line":917},[96,12720,9334],{"class":106},[96,12722,3477],{"class":102},[96,12724,12725],{"class":98,"line":923},[96,12726,4680],{"class":106},[96,12728,12729],{"class":98,"line":928},[96,12730,987],{"class":106},[96,12732,12733,12735,12737],{"class":98,"line":933},[96,12734,2641],{"class":106},[96,12736,3000],{"class":102},[96,12738,2459],{"class":106},[13,12740,12741],{},"いらないものはとりあえず省きましました。コードの解説をします。",[1691,12743,12745],{"id":12744},"asyncdataでapiを呼ぶ","asyncDataでAPIを呼ぶ",[13,12747,12748],{},"asyncDataというのはssr・ssgモードのpages配下で利用できます。これはcreatedよりも前、コンポーネントインスタンスが完成する際に呼び出せます。このasycData内の処理が終わってからコンポーネントが作成されます。",[13,12750,12751],{},"静的出力・ssrの場合はasycDataに処理を書くことでサーバーサイドでAPIの呼び出し、そしてコンポーネントのdataへのマージを行ってくれます。createdでやると、静的出力した際になんと静的htmlからAPIを呼び出してしまいます。",[13,12753,12754,12755],{},"詳しくは",[76,12756,79],{"href":12757,"rel":12758},"https:\u002F\u002Fnuxtjs.org\u002Fdocs\u002F2.x\u002Ffeatures\u002Fdata-fetching#async-data",[584],[1691,12760,12762],{"id":12761},"画像のurlはちょっと注意","画像のURLはちょっと注意",[13,12764,12765],{},"APIで呼び出した際に、サムネイルのURLは以下の様になっていました。",[86,12767,12771],{"className":12768,"code":12769,"language":12770,"meta":92,"style":92},"language-json shiki shiki-themes material-theme-ocean","thumbnail:{\n...\nurl\": \"\u002Fuploads\u002Fnuxt_strapi_8a86d72c14.png\",\n...\n}\n","json",[54,12772,12773,12780,12784,12802,12806],{"__ignoreMap":92},[96,12774,12775,12778],{"class":98,"line":99},[96,12776,12777],{"class":128},"thumbnail:",[96,12779,788],{"class":106},[96,12781,12782],{"class":98,"line":120},[96,12783,1086],{"class":128},[96,12785,12786,12788,12790,12793,12795,12798,12800],{"class":98,"line":132},[96,12787,12662],{"class":128},[96,12789,2477],{"class":106},[96,12791,12792],{"class":2285},": ",[96,12794,2477],{"class":106},[96,12796,12797],{"class":128},"\u002Fuploads\u002Fnuxt_strapi_8a86d72c14.png",[96,12799,2477],{"class":106},[96,12801,1940],{"class":2285},[96,12803,12804],{"class":98,"line":141},[96,12805,1086],{"class":2285},[96,12807,12808],{"class":98,"line":152},[96,12809,987],{"class":2285},[13,12811,12812,12813,12816],{},"ローカルの環境では画像にアクセスできません。（実物が",[54,12814,12815],{},"http:\u002F\u002Flocalhost:1337\u002F","配下にいるため）そのためenvを用いて",[86,12818,12821],{"className":12819,"code":12820,"language":460},[457],"img:context.env.envSet.IMG_BASE_URL+res.thumbnail.url,\n",[54,12822,12820],{"__ignoreMap":92},[13,12824,12825,12826,12829],{},"としてURLの前に",[54,12827,12828],{},"env.envSet.IMG_BASE_URL = http:\u002F\u002Flocalhost:1337","が出力される様にしています。そして表示されたページが以下の感じです。",[42,12831],{":src":12832,":width":11130,":center":46},"'_mix\u002Ffinal-768x414.png'",[13,12834,12835],{},"きちんとデータ・画像がとってこれていますし、bootstrapにはめ込まれていい感じです。",[1691,12837,12838],{"id":12838},"データ取得できない場合",[13,12840,12841],{},"データが取得できない場合以下のことを確認",[3739,12843,12844,12847,12850,12853],{},[3742,12845,12846],{},"レコードは「Publish」になっているか",[3742,12848,12849],{},"権限とロールでPublicの読み取り権限にチェックを入れているか",[3742,12851,12852],{},"エンドポイントを間違っていないか",[3742,12854,12855],{},"strapiのサーバを落としていないか",[34,12857,12858],{"id":8710},"次回は…",[13,12860,12861],{},"今回の記事ではstrapiとNuxtのインストール・連携まで行いました。次回の記事からはブログ構成を構築するためのstrapiで「ページ」データの構築、そしてnuxtでの出力方法を書いていきます。",[3795,12863,12864],{},"html pre.shiki code .s6cf3, html code.shiki .s6cf3{--shiki-default:#89DDFF;--shiki-default-font-style:italic}html pre.shiki code .sJ14y, html code.shiki .sJ14y{--shiki-default:#C792EA}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .s7ZW3, html code.shiki .s7ZW3{--shiki-default:#BABED8;--shiki-default-font-style:italic}html pre.shiki code .sC9rS, html code.shiki .sC9rS{--shiki-default:#464B5D;--shiki-default-font-style:italic}html pre.shiki code .s0W1g, html code.shiki .s0W1g{--shiki-default:#BABED8}html pre.shiki code .sdLwU, html code.shiki .sdLwU{--shiki-default:#82AAFF}html pre.shiki code .s-wAU, html code.shiki .s-wAU{--shiki-default:#F07178}html pre.shiki code .sx098, html code.shiki .sx098{--shiki-default:#F78C6C}html pre.shiki code .sfyAc, html code.shiki .sfyAc{--shiki-default:#C3E88D}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);}",{"title":92,"searchDepth":132,"depth":132,"links":12866},[12867,12870,12871,12872,12877,12883,12892],{"id":11471,"depth":120,"text":11472,"children":12868},[12869],{"id":11490,"depth":132,"text":11491},{"id":11531,"depth":120,"text":11531},{"id":11549,"depth":120,"text":11549},{"id":11565,"depth":120,"text":11565,"children":12873},[12874,12875,12876],{"id":11568,"depth":132,"text":11568},{"id":11596,"depth":132,"text":11596},{"id":11619,"depth":132,"text":11620},{"id":11644,"depth":120,"text":11644,"children":12878},[12879,12880,12881,12882],{"id":11709,"depth":132,"text":11709},{"id":11738,"depth":132,"text":11738},{"id":11762,"depth":132,"text":11762},{"id":11787,"depth":132,"text":11788},{"id":11800,"depth":120,"text":11800,"children":12884},[12885,12886,12887],{"id":11824,"depth":132,"text":11825},{"id":11841,"depth":132,"text":11842},{"id":12317,"depth":132,"text":12318,"children":12888},[12889,12890,12891],{"id":12744,"depth":141,"text":12745},{"id":12761,"depth":141,"text":12762},{"id":12838,"depth":141,"text":12838},{"id":8710,"depth":120,"text":12858},[3836],"2020-11-03","headlessCMSのstrapi をnuxt.jsで静的書き出しを行う。",{},"\u002Fseries\u002Fheadlesscms-strapi-1",{"title":11423,"description":12895},"headlesscms-strapi","series\u002Fheadlesscms-strapi-1",[4902,3845,5560],"_mix\u002Fnuxt_strapi-768x768.png","AUAh3SzPOmWReCngs8NQezSo25YbFgc0ddNoPwO0euE",1780987144845]