- 技術書典6でNetlifyとGatsby本を頒布します
技術書典6「か67」でNetlifyとGatsbyの本を頒布します。
Netlifyは92ページ、Gatsbyは100ページ超(執筆中)でどちらとも¥1,000で頒布を予定しています。
興味のある方はサークルページからチェックをしてもらえると嬉しいです。
本について
Netlify本「Netlify Recipes 30」は新刊、Gatsby本「Gatsby Guidebook」は既刊の改訂版になります。
今回はデザインのアドバイスをタカユリさんにしてもらい、前回の技術書典で頒布したものよりよいものになったと思います。お忙しい中、ありがとうございました!
Netlify Recipes 30
前回の技術書典では「Netlifyで始めるサーバーレス開発」という本を出しましたが、今回はNetlifyのHosting, Forms, Functionsを扱ったNetlify本です。
30のレシピ集という体裁で書いており、機能の使い方からマニアックな使い方まで取り扱っています。
本文のページをいくつかピックアップしたので、購入の検討にお使いください。
今回はRe:VIEWのカスタマイズを頑張ったり、改ページ位置の調整で読みやすい本になるような工夫をしています。ぜひぜひ。
Gatsby Guidebook
こちらはBOOTHで販売している「GatsbyJSで作るモダンウェブサイト」の改訂版になります。(Netlifyの本にテイストを揃えたいので名前を変えました。)
前回の技術書典で頒布したものですが、Gatsbyの入門箇所を中心に書き直して、16ページほど書き足しました。前回は要素ごとに説明していたものをポートフォリオの枠組みだけ作る流れに変更しました。また、Re:VIEWのカスタマイズが進んだおかげで読みやすい本になっています。
第1章ではReactのおさらいをしつつJSXでポートフォリオを構築します。最後にReact単体でウェブサイトを作る際のデメリットを確認し第2章でGatsbyに入門します。
第2章ではポートフォリオの要件を達成するGatsbyのサイトを作成します。Gatsbyのルーティング、データの取扱、Netlifyのデプロイまで一通りサイトが作れるレベルまでやります。
第3章では記事データを外部のContentfulというHeadless CMS(Viewを持たないCMS)に保存し、そのデータをもとにサイトを構築する方法を学びます。
今回はおやかたさんの「ワンストップ勉強会」(あ01)、エンジニアの登壇を応援する会の「エンジニアの成長を応援する本」(あ01)にも寄稿しています。そちらもよろしくおねがいします。
2019/03/28 - サポーターズCoLabでNetlifyハンズオンを行いました
2019/03/13にサポーターズで行った『Netlifyでポートフォリオサイトを作ろう!』というイベントでのハンズオン講師をしてきました。
今回のハンズオンはNetlifyの手軽さを実感してもらうことが目標で、小難しいことや静的サイトジェネレーターなどには触れませんでした。
ハンズオンの内容
自分が普段激推ししているNetlifyの入り口的な内容を話してきました。
「Netlify便利だよ」って言っても触ってくれないのは目に見えているので、便利だと実感してもらうことを重点に置きました。
「Netlifyで始めるサーバーレス開発」でいう一章の内容とフォームの設置を、その場でアシストしながら進める空気感です。
具体的には次のような内容を取り扱いました。とにかく「欲張らない」を目標にしたので分量としては少なめです。
GitHubにリポジトリを用意して、Netlifyと連携するだけでサイト公開。
GitHubに新しいコミットを追加して自動でサイトが更新されるのを確認。
Netlify Formsを設置して、サイト内にお問い合わせフォームを設置する。
適宜質問を受け付けて答える。
また、参加者の一人である遠藤ヒズミさんが早速参加ブログを公開し、Netlifyでサイトを公開してくれたみたいで非常に嬉しいです。
振り返り
自分の反省なのですが、ハンズオンはLTと比べて難しいと感じます。
ハンズオンは2回目なのですが、次のような反省点がありました。
接続トラブル
接触が非常に悪く準備に手間取ってしまいました。
予備端子を持ってきておらず、参加者の方にお借りしました。ありがとうございます!
参加者が持っている開発環境の整備状況の差。
開発環境が整っているものと思いましたが、そうとは限らないことがわかりました。
ここは参加要項に書いておくべきことでした。
今回は内容も簡単だったのでGitHubのWeb UIで編集する方法で回避しました。
参加者間の進捗状況の差
説明部分と手を動かす部分の区切りが明確でない。
全体的にメリハリのない空気のまま進めてしまった。反省。
手を動かす部分ははっきり分離しようと思いました。
逆に、良かった点としては
時間内で説明したい内容をやりきれた。
前回は分量が多すぎて途中までしかできなかったので、進歩を感じました。
スライドでなくプリントを用意した判断は良さそう。
前回はスライドを用意したものの、ほとんど使わなかったので今回はテキストをプリントアウトしたものを参加者に配りました。
CSS組版でMarkdownをプリントしました。
プリントを見ながら先に進んでくれる人もいたので、各々のペースで進められてよかったです。
やってみて
人にプログラミングを伝える手段としてブログや書籍、ハンズオンとやってきましたが、ハンズオンは難しいです。ただ、ブログや書籍と違って、自分の操作を見せられるという意味で非常に伝えやすい方法だと思っています。
そういった意味では、自分の操作を見せつつ各々の進捗に合わせられる「動画」という手段にはポテンシャルを感じています。
YouTuber、ありです。
ただし、「より良いハンズオンとは?」という問いは繰り返していこうと思っています。
技術書典6でNetlifyの本出します。
前回の技術書典5ではNetlifyのFunctionsに特化した本を出しましたが、今回の技術書展6では「Netlify Recipes」という本を出す予定です。
初心者に機能を説明するだけでなく、Netlifyの機能を活用したアイディアも紹介しています。
サークル詳細でチェックリストに加えてもらうと部数の想定ができるます。なので、「欲しい」と思った方はチェックお願いします。
2019/03/21 - engineers-lt合宿レポート
3月9〜10日にかけてengineers-lt(エンジニアの登壇を応援する会)の運営メンバーで日光合宿に行ってきました。今回はこのコミュニティでどんなエンジニアにどういったサポートをしていきたいのか?というテーマでのディスカッションをしてきたので、その様子と自分の感じた内容を振り返ります。
adminsLT大会
まずは「エンジニアの登壇を応援する会」らしく、運営メンバー全員がどういう人なのか、どういう支援を行っていきたいのかという観点でLTを行いました。
自分は「ロゴ(仮)に込めた目指したいコミュニティのあり方」というテーマでLTを行いました。
自分以外のLTではどういった理由で関わっているのか、どういった方向に進みたいのかと個人にフォーカスした内容を話していて、
将来どうしたいのか、今どういう方向で進んでいるのか、自分はどうありたいのかなどの話を聞いているうちに、自分は他の人と比べると主体性が弱く感じました。
ワークショップ
今回、合宿の目的の1つに「自分たちが見つけたペルソナの成長支援する施策をアウトプットする」というものがあります。
その施策をアウトプットするため、9人の参加者が各3人ごとにチームにわかれワークショップを行いました。自分のチームはariakiさんとかめねこさんでした。
アイスブレイク
1つのコミュニティを運営する同士ではありますが、基本的にオンラインでのコミィニケーションをしているために相手を深く理解しているとは言えません。
そこで、アイスブレイクでは「実は…」といえるような自己紹介をするといいというヒントをもらい意外性のある自己紹介を行いました。
ただ、チームのお二人とも結構絡んだこともあったので、基本的なプロフィールはお互い把握しており、「実は…」ネタに振り切ったアイスブレイクになりました。
ペルソナ設定
アイスブレイクを終えたあとは、チームでサポートしたい人物像を考えました。
ただ、どういった人がいるかわからないので、事前にTwitterで募集した「成長支援アンケート」の結果を参考にしました。どういった人がアンケートに答えたのか?そういった人たちは今どういった問題意識を抱えているのか?などの回答をもとに現状を認識するところからはじめました。
【拡散希望】エンジニアの成長を支援するためのアンケート調査今後の活動方針を検討するため、エンジニアの皆様より現状や問題点をお教えいただくためのアンケート調査を実施します実施概要https://t.co/lXt0JSiJr2※アンケートは実施概要内のリンクからお進みください— エンジニアの登壇を応援する会 (@engineers_lt) January 17, 2019
アンケートを見るだけで興味深い、数値や傾向を知ることができました。(結果は後日公開予定です)
結果から自分たちのチームでは次のようなペルソナを設定しました。
- 名前:なかもとさん
- 性別:男性
- 年齢:26歳(7月7日生まれ)
- 彼女がいてそろそろ結婚を考えたい
- 職業
- バックエンドソフトウェアエンジニア(Java6)
- 大卒・新卒入社4年目
- 残業時間:月30時間程度
- 年収:380万円
大卒で入社4年目の26歳のJava6エンジニア。多少の学習意欲はあるため、Javaの最新版についてのキャッチアップは独自で行い職場の環境とのギャップに悩むエンジニアです。基本的なプロフィールとは別に次のような情報も用意しました。
- 新卒教育を4月から任された
- 新しいテクノロジーを評価/採用する文化が無い会社に所属
- 概ねやりたい仕事をやれているが、レガシーな技術や評価が低いことに不満
- 社外に接点はないがブログ執筆や登壇を積極的に行う大学同期をTwitterで観測
- 客観的な自己評価ができない
- 転職に踏み切れない
- 新しい技術に興味はあって自己学習はしているが、精度は高くない
自分たちのチームはこのペルソナに対してどうすれば成長を支援できるかを考えていくことにしました。
課題抽出と解決策の検討
設定したペルソナに対して、課題抽出と解決案の検討を進めていきます。課題抽出の前にまずは理想像を話したところ次のような理想像にたどり着きました。
- 必要な最新技術を社内で利用できる
- 自身の学習内容について評価されている
- 正しい技術的な判断ができる仲間と仕事をできる
- 社外(外部コミュニテイや企業)との接点をもち交流している
- 収入の問題がなくなり結婚を決断
- 大学時代の同期に対する劣等感から開放。
- 技術について話す仲間から、客観的な評価を得られる。
- 相談できる人が身近にいる
- 自己を客観的に評価できる
パット見、ペルソナの人から見ると理想のように思えますが、自分たちは「6つの帽子」という思考フレームワークを利用し、よりよい理想像を探ることにしました。
6つの帽子は強制的に異なる視点で議論をする並行思考です。6つの異なる視点(客観的・直感的・肯定的・否定的・革新的・俯瞰的)と帽子を対応させ思考することで幅広く考えを巡らす方法です。
緑の帽子(革新的なアイディア)
緑の帽子は新しい視点やアイディアに対応する帽子です。
そういった念頭のままディスカッションをすると次のようなアイディアが出てきました。
- 小さいチームで勉強会を開催することで、複数メンバーで問題を認識できる
- 最新技術を学習できる
- 収入の問題で結婚ができないのは問題では?
- 人生の問題なので、最優先に解決したい
- イケイケ企業に対して劣等感ってあったっけ?
- 最新技術を学べないことで取り残される焦り
- 自己を客観的に評価するのは難しい
- 何かしらの指数でその人を評価するサービスが夜の中にあるべきでは?
- 技術的な評価ができる
- イケてる開発をしたいといったけど
- 具体的な目標を設定できる技術を身につけるべき
白の帽子(事実に基づいたアイディア)
白の帽子ではデータや事実を述べます。自分の視点などは入れないというのがポイントです。
ここでは中本さんと同い年での平均年収は中本さんより多少高く、中本さんの年収では結婚が厳しそうなどということがわかりましたが、有意義なデータは得られませんでした。
赤い帽子(直感的)
赤い帽子では直感的な意見を出します。結婚してほしい、転職活動したほうがいい、上層部の頭の硬さ、現職をこのまま続けた時の未来、まずは行動して欲しいなど各自が思っているような意見を出しました。
黄色い帽子(希望的)
黄色の帽子では希望的な意見を出します。ここでは、自分が会社でやっていることがレガシーであることを認識して、上司に伝えていることはよい、上司からの評価や自動テストの認識をしているのはよいなど、中本さんのいいところについての視点を得ました。
青い帽子(分析的)
青い帽子では議論の整理を行います。今までの議論で足りないことやよかったことを改めて話しました。次のような視点を得られました。
行動にどうやったら移せるか
自分がやってるのをLegacyと認識しているのはすごく重要
問題の認識までは掘り下げられてきた
黒い帽子(批判的)
黒い帽子では批判的な意見を出します。今までの意見を通してから話したため次のような視点を得られました。そもそも問題は何だったのか?環境ではなく中本さんにあるのではないか?などより本質に近い問題に対して意見を出し合えました。
緑の帽子(2回目)
6つの帽子を一周させたところで、もう一回革新的なアイディアの緑の帽子を使って意見を出し合いました。黒い帽子の際に得られた「変化を嫌う」といった視点があったため、自己認識と他社からの認識がずれている、ちゃんと行動に移せていない、未来を見れていないから短期的な思考になっているなど手厳しい意見が出てきました。
青の帽子(2回目)
緑の帽子で、かなり突っ込んだ意見が出たので、2回目の青い帽子で議論の整理を行いました。最初に出ていた結婚の話はなくなっことや、成長を望んでいるわけではない、行動していないので変わらないなどの話が出て、そろそろまとめに入ることにしました。
現状の問題点と理想像
6つの帽子で意見を出し合ったので、「今どういったところが問題なのか?」という視点で次のようにまとまりました。
- 自分が不満と思っているものを認識できていない
- 妥協できるもの、許容できないものの軸がない
- 客観的になっていないので、言語化できない
- 自分の周囲をきちんと変えようとしていない
- 周囲にやって欲しいという要望だけ投げっぱなしにしている
- 具体的な行動をなにもおこしていない
- キャリアプランが描けていない
- 自分の未来が定まらないから、隣の芝が青く映る
- 努力すべきところがわからず、力を入れられない
- 「俺も同じ会社にいたら同じだけ活躍できている」という現実逃避をしている
- 新しい技術を学んでいるのに、キャッチアップする方法を模索していない
- 他者との交流を持たず、自分の中で完結している
- 新しい技術に触れているだけで満足している
- 優先度が決まってないので何から手を付けていいのかわからない
- 自分に甘えている
- 常に楽をするための言い訳を考えている
- 自分では十分やっていると思っている
- 自分の軸と世の中の軸がずれているのを認識していない
- 第三者評価をされるのが怖いから転職活動に身をさらせない
- 「自分なんか」って言葉でごまかしている
- 都合が悪い部分は、自己を過小評価しているように見せかけている
- 責任を求められても、やるやる詐欺で行動を起こさない
- 楽をしたい
- 成長したいわけではない
- 「楽」の意味をはき違えている
手厳しい意見ですが、これのなかのどれかに当てはまる人もいるのではないでしょうか?
こういった現状から、どういった理想像が描けるかを考えたところ次のようになりました。
- 自分がなりたい姿が描けている
- 自己が客観的に評価できてる
- 強みと弱みを分析し、目標が定まっている
- 周囲に対して目標を宣言し、共感されている
- 変えるための行動をする
- 転職も1つの手段だが、それがすべての解決策ではない
- 自身及び周囲を変えるきっかけを作り出し、それに向かってきちんと行動する必要がある
- 会社以外の評価軸を持つ
- アウトプットによって周囲からの評価を受け、それによって自信を得ている
- 近しい環境を持つ他者と寄り添い、相対的に自身を評価できるようになっている
- 楽しい環境に身を置く
- 「楽な状況」ではなくて、モチベーションを源泉として毎日の成長を楽しむことができる状況が作られている
- 楽しいを言語化し、それを周囲に伝えられている
解決策
この理想像にたどり着くために、どういったことができるのでしょうか?
中本さんは非常に腰が重い人だと思います。しかし、何もしないまま年を重ねていくうちに必ず現状への違和感や不満を感じていくはずです。
そういった際に、コミュニティの先人が成功・成功している姿を見せて、自分も前に進む楽しさといったものを与えられる。そういったことが出来るコミュニティ・先人になっていきます。
- 成長に対する具体的な目標設定方法や**成功体験**を提示する
- 頑張っている人と時間を共有することで、良い影響を与える
- 成長体験ができる場所を提供する
- 成長の楽しさを理解させる
- 自身及び周囲の変化と達成感を与える
- 行動で変えられることを実感する
- 1つずつ変えていく必要性を再認識する
- 周囲を巻き込み変化するための方法を学ぶ
- 次のことを自身に問いかけ、結論を導く支援をする
- 自分は、どうなりたいか
- 自分は、何を大切にしているか
- 未来の可能性を提示する場所を提供する
合宿を通して
この合宿の最後にariakiさんから「この合宿を通して一番大事なワードはなんだったのか?」という問いがありました。
その問いに対し、自分の考えを述べるとするならば「可能性」という言葉がふさわしいと思っています。
今回自分たちが考えたペルソナのように、自分がやっていることは正しいと思い込んで可能性を自ら小さくしてしまうエンジニアはたくさんいると思っています。(恥ずかしながら過去の自分もそのような傾向にありました。)
エンジニアのサポートをするというのは、そのエンジニアの可能性を最大化するということに他ならないと考えています。
Admins LT大会でariakiさんが話していた「可能性とは現在+未来でどう成長するか」という言葉があるように、可能性を一番大きくするには今動き出すことだと思います。
応援する人だけでなく、自分の可能性も大きくして、大きなインパクトを生みたい。そう思った合宿でした。
感想
会社組織に入っていない自分としては、フリーランスで営業をしたり、アドバイザー・1エンジニアという立場で組織と関わっているが、engineers-ltはそれとは違う距離感で関わっているとと思う。
あまり人付き合いは得意ではないのですが、運営に参加するような人たちなので(いい意味で)暖かく迎い入れてくれています。
まだまだ拙いものが多いコミュニティだけれども、多くの人にとっての道標となるチームになりたいと感じました。
コミュニティとは別に個人としてどうするか?という気持ちにもさせられました。
自分はフリーランスを選んだ立場ですが、このままずっとフリーランスを続けられるとは思っておらず、将来に漠然とした不安を抱えるペルソナに合致する特徴をもつITエンジニアです。
そもそもITという産業自体が比較的新しくキャリアパスなんてものはなかったのですが、さらなる多様化が起きている現状、少なくない人たちが不安を抱えていると思います。
ただそういった不安の一方、Engineering Manager界隈ではEngineering Manager MeetupやEM .FM、OSS開発ではPatoreonのようなサービスで個人に対しての支援が出来るようになったり、OSSコミッターを雇う会社が出てきたりしており、いろんなエンジニアの生き方が少しずつ外に出てきました。会社員から独立してフリーランスになるって話もよく聞きますね。
そういった取り組みがもっと加速し、人と違っても不安を感じないような世界が実現できたら…という気持ちもあります。
漠然とした不安に押しつぶされず、人に背中を見せられる強さを持てるようになりたいと思った合宿でした。
最後に、ワークショップを終えたあとに食べた湯葉御膳の写真です。とても美味しかったです。
2019/03/13 - iOSアプリ開発を始めて1.5ヶ月たった
自分はもっぱらWebアプリケーション開発を専門としていますが、友人の会社から「iOSアプリを開発してほしい」という話があったのでiOSアプリ開発を始めた。1
実務を始めて1ヶ月半ほど立ったので、自分の能力習得プロセスのメモ的な日記。勤務記録を見る感じ今は90時間ぐらいやったところ。
始める前
Webアプリケーション開発歴 7年目
Android 4.x時代に半年ほどJavaでアプリ開発の実務経験
4年ほどReact, VueのSPA開発歴あり
ReactNativeでマルチプラットフォームアプリの個人開発経験(ストア申請で躓いた)
iOSネイティブをちゃんとやるのは始めて
Androidユーザー。タブレットはiOS。
始めた時
最初は簡単なViewの修正IssueをAssignしてもらった
Swiftはエンジニア始めた当初から触っているRubyと似た文法なので書くだけなら問題なかった
Optional周りだけちゃんとやった。
Storyboard, xibに慣れることが大変だった
アプリ開発初心者本を見ながら覚えた。
Xcodeがマジで鬼門。
普段使い慣れたVSCodeとキーバインディングが違くてだいぶ辛い
使用頻度の高いショートカットキーは早めに覚えた。
ストレスがある開発だとWebに逃げたくなる。嫌いにならないための試み。
最近の話
楽しい。
アプリデザインを片手間でやってい時期もあり、UIパーツの名前がわかったりするとググり力になる
Issueの粒が大きくなってきて、触る範囲も増えてきた結果、設計の意図がうっすらとわかってきた。
VIPERアーキテクチャってのを使っている。ビッパーではなくバイパーと読むらしい。
Viewならモックさえあれば、それなりに再現できるようになった。
View以外はほとんどSPAの感覚で書ける。
言語が違うだけなイメージ。VueよりはReactのイメージ。
「iOSはReactNativeでいいじゃん」みたいな気持ちだったけど、今はSwiftで書くのもありな気がした。
ReactNativeでは難しい表現も書ける。UI寄りのエンジニアとしては楽しい。
UI系のライブラリは、導入する前に自分でかけそうかどうか検討している。基本自分で書いている。
細かい動きのハンドリングが出来ないのでWebの開発でもこの方針で書いている。
分からないこともある
参考にすべきOSSってなんなのか?
そもそもSwiftのメジャーバージョンが違う例が多くてうーん…という感じ。どうしているのか。
メンターがいると嬉しい。自分はWebフロントを教えられる。
これから
RxSwiftに興味を持っている。触るだけ触っておきたい。
公式ドキュメントをちゃんと読んでいきたい。英語だけど頑張る。
MENTAとかで、気軽に聞けるメンターを探すのもありかと思ってる。
購入したiPhone XRのRedがかっこいいので、Androidからの乗り換えもありかも。
注釈
基本的に未経験分野の仕事は受けないのですが、成功して欲しい人がいれば手段にはこだわらないことにしています。フロントエンドエンジニアとしてiOSのフロントエンドをやっておこうと思ってやっています。 ↩
2019/03/08 - GatsbyJSで複数のサイトの情報をまとめたRSS Feedをつくる
自分はWrite Blog Every Weekという週に一回ブログを書くコミュニティに所属しています。
コミュニティ内ではRSS Feedに基づいてブログを書いたかを判断するBotが整備されており、ブログ管理者がそのBotに対してRSSのURLを登録するフローを取っています。
しかし現状、1人が登録できるRSSは一つまでという制約があり、ブログとQiita、noteを併用して認識させることが難しくなっています。
正攻法で言えばプルリクエストを出そうという話になります。しかし、AWS LambdaやDynamoDBが使われておりデバッグも面倒なので(自分の登録する)RSSに複数のサイトの情報をまとめるという変則的な方法を取って解決することにしました。
その際に、Gatsbyのgatsby-plugin-feedを使って複数のサイトをまとめたのでその方法を紹介したいと思います。
該当のPull Request: https://github.com/mottox2/website/pull/41
gatsby-plugin-feedとは?
gatsby-plugin-feedGraphQLで取得したデータをもとにRSS Feedを作成、metaタグに情報を埋め込む責務をもつプラグインです。gatsbyjs/gatsbyで管理されている公式のプラグインとなっており、gatsby-starter-blogにも使用されていることから利用者の多いプラグインです。
通常の利用法では次のようになります。(以下の例はREADME.mdの例から抜粋です。)
options.feeds以下のquery内でgraphqlを実行した結果がserializeにqueryとして渡されます。
serializeではqueryをもとにRSSを組み立てるObjectを配列にして返します。
この際のObjectはrssモジュールに準じたものにします。
gatsby buildを実行した際にhtmlに<link rel="alternate" type="application/rss+xml" href="/rss.xml"/>が挿入されます。
gatsby-config.js
plugins: [
{
resolve: `gatsby-plugin-feed`,
options: {
query: `
{
site {
siteMetadata {
title
description
siteUrl
site_url: siteUrl
}
}
}
`,
feeds: [
{
serialize: ({ query: { site, allMarkdownRemark } }) => {
return allMarkdownRemark.edges.map(edge => {
return Object.assign({}, edge.node.frontmatter, {
description: edge.node.excerpt,
date: edge.node.frontmatter.date,
url: site.siteMetadata.siteUrl + edge.node.fields.slug,
guid: site.siteMetadata.siteUrl + edge.node.fields.slug,
custom_elements: [{ "content:encoded": edge.node.html }],
})
})
},
query: `
{
allMarkdownRemark(
limit: 1000,
sort: { order: DESC, fields: [frontmatter___date] },
filter: {frontmatter: { draft: { ne: true } }}
) {
edges {
node {
excerpt
html
fields { slug }
frontmatter {
title
date
}
}
}
}
}
`,
},
],
},
},
]
補足: optionsの初期値にはgatsby-source-filesystem, gatsby-transformer-remarkを使った構成でのものが設定されており、単純な構成の場合optionsが必要ありません。(人のソースを参考にする場合注意してください。)
複数のサイトの情報を混ぜる
上記の例からわかるのは、「GraphQLで得られるデータをもとにRSSを組み立てる」ということです。なので、混ぜ込みたい情報をGatsbyのGraphQLで扱うためにsource-pluginを導入したり、gatsby-node.jsでcreateNode関数を実行します。
今回は自分のnote.mum, qiita.com1のRSSを混ぜ込むことにしました。
データの用意
RSSを混ぜるには gatsby-source-rss-feedを使用します。今回は以下の記述をgatsby-config.jsに追加しました。
module.exports = {
...
plugins: [
...,
{
resolve: `gatsby-source-rss-feed`,
options: {
url: `https://note.mu/mottox2/rss`,
name: `NotePost`
}
},
{
resolve: `gatsby-source-rss-feed`,
options: {
url: `https://qiita.com/mottox2/feed`,
name: `QiitaPost`
}
},
]
}
これでNote, Qiitaの記事をそれぞれallFeedNotePost, allFeedQiitaPostというクエリで情報が取得できるようになります。
RSSをつくる
gatsby-config.js内のgatsby-plugin-feedのoptionsにロジックを以下のコードを書きます。
query内でいろんなデータソースからデータを取得
queryの結果を纏めた配列をsort関数で日付順に並び替え
internal.typeにデータのtypeが入っているので、それをもとにrssのitemObjectを作成、serializeの結果としてreturnする。
ちょっと雑な感じで、拡張する際に壊れそうな匂いのするコードですが、この状態でgatsby buildを行い生成されたRSSを見たところ期待する結果になっていました。
feeds: [
{
serialize: ({ query: { site, allEsaPost, allFeedQiitaPost, allFeedNotePost } }) => {
return [...allEsaPost.edges, ...allFeedNotePost.edges, ...allFeedQiitaPost.edges].sort((a, b) => {
const bDate = b.node.pubDate ? new Date(b.node.pubDate) : new Date(b.node.childPublishedDate.published_on)
const aDate = a.node.pubDate ? new Date(a.node.pubDate) : new Date(a.node.childPublishedDate.published_on)
return bDate - aDate
}).map(edge => {
const node = edge.node
switch (node.internal.type) {
case 'EsaPost':
const day = dayjs(node.childPublishedDate.published_on)
return {
date: day.toISOString(),
pubDate: day.toISOString(),
url: `${site.siteMetadata.siteUrl}/posts/${node.number}`,
guid: node.number,
title: node.fields.title,
description: node.fields.excerpt
}
break;
case 'FeedQiitaPost':
case 'FeedNotePost':
return {
date: dayjs(node.pubDate).toISOString(),
pubDate: dayjs(node.pubDate).toISOString(),
url: node.link,
guid: node.link,
title: node.title,
description: node.contentSnippet.substring(0, 512)
}
break;
default:
throw `${node.internal.type} is unknown type`
}
})
},
query: `
{
allEsaPost {
edges {
node {
number
fields {
title
excerpt
}
childPublishedDate {
published_on
}
internal {
type
}
}
}
}
allFeedQiitaPost {
edges {
node {
title
pubDate
contentSnippet
link
internal {
type
}
}
}
}
allFeedNotePost {
edges {
node {
title
pubDate
contentSnippet
link
internal {
type
}
}
}
}
}
`,
この方法の問題点
以上の方法でRSS Feedを一つにまとめることが出来ました。
しかし、静的サイトという特性上、ビルドのタイミング次第でRSSが更新されないという現象が起こりえます。ブログ記事の更新はWebhookを利用することで更新していますが、RSSの更新をhookするにはIFTTTやZapierなどの方法を別に用意する必要があります。
ただ、自分は厳密なRSSを必要としていないので、無視することにしました。どうしても気になるようであれば、スケジューラーでサイトの更新を行うと思います。
何度もいうと、複数のRSSを受け付ける実装をするのが一番いいです。
おわりに
これで、週一ブログに追われて自分のブログだけを更新する。ということはなくなると思います。
ちょっと薄めの記事はQiita、デザイナー層にリーチしたい記事はnote.mute。といった使い分けをしていけたら…という気持ちです。
また、今回は複数のデータソースをRSSにまとめるという形を取りましたが、これはRSSじゃなくてHTML上にまとめるということも可能です。GatsbyJSの強みには複数のデータソースに対してGraphQLという統一されたインターフェースでアクセスできるというものがあります。個人的には、「会社のエンジニアが運営するブログを一つのサイトにまとめて採用に使う」などといった方法で使われたりすると面白いなと思っています。
注釈
QiitaのRSSを取得するには、URLの最後に/feedを付けると見れるみたいです。タグページでも大丈夫みたいです。 ↩
2019/03/01 - ReactとIonicでアプリ開発ができる@ionic/reactを触ってみた
どうも、クロスプラットフォーム開発の夢を捨てきれない@mottox2です。
クロスプラットフォーム開発の選択肢はReactNative, Flutter, Xamarinなどがありますが、つい最近にメジャーバージョンアップされたIonicをReactで書けるという話を聞いたので触ってみました。
@ionic/react 0.0.4(alpha)での動作を元にしているので、すぐに役立たない記事になると思うのでその当たりはご承知いただけると嬉しいです。Ionicは初見です。
検証につかったコードはこちら: mottox2-sandbox/ionic-react-app
@ionic/reactって?
IonicでReactを使うには @ionic/reactを使って進めていくようです。
@ionic/react 0.0.4のREADME.mdを見ながらやっていきます。
https://www.npmjs.com/package/@ionic/react/v/0.0.4
README.mdを見ると、次のようなことが書いてありました。(意訳)
@ionic/react, @ionic/coreを入れて開発を進める。create-react-appをベースにしてTypeScriptを使うのがおすすめだよ。
ドキュメンテーションは頑張ってるけど、俺らの作ったReact Conference Appのコードを見た方が早いよ。
なので、サンプルコードを見ながらcreate-react-appを使って簡単なアプリケーションを作ってみようと思います。
プロジェクトの設定
create-react-appでTypeScriptということで毎度おなじみのcreate-react-appのコマンドを叩いてみます。
$ npx create-react-app ionic-react-app --typescript
$ cd ionic-react-app
$ npm install @ionic/core @ionic/react
$ npm install react-router-dom
インストールした時点でpeer dependencyとしてreact-routerとreact-router-domが必要らしいのでreact-router-domもインストールしておきます。1
src/index.jsにregisterIonicの記述を追加します。(0.0.5でメソッドが削除されました)
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { registerIonic } from '@ionic/react';
registerIonic()
ReactDOM.render(<App />, document.getElementById('root'));
準備は整ったので適当にUIを作っていきます。Ionicの知識はないのでドキュメントを頼りにします。AngularとReactの差分ですが、ドキュメントのion-appはIonAppに対応します。
src/App.jsを次のように変更しました。
UIコンポーネントは@ionic/react、全体のスタイルは@iconic/coreの方から呼び出します。
https://ionicframework.com/docs/api/
https://ionicframework.com/docs/layout/structure
import React, { Component } from 'react';
import { IonApp, IonHeader, IonToolbar, IonTitle, IonContent, IonButton } from '@ionic/react';
import logo from './logo.svg';
import './App.css';
import '@ionic/core/css/core.css';
import '@ionic/core/css/ionic.bundle.css';
class App extends Component {
render() {
return (
<div id='app'>
<IonApp>
<IonContent>
<IonHeader>
<IonToolbar>
<IonTitle>
IonicReactApp
</IonTitle>
</IonToolbar>
</IonHeader>
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<IonButton href="https://reactjs.org">Learn React</IonButton>
</IonContent>
</IonApp>
</div>
);
}
}
export default App;
この状態でnpm run startを実行すると、次のように表示されました。無事IonicのコンポーネントをReactで呼び出すことができました。
capacitorの追加
それでは今作ったものをiOSで動かしてみましょう。cordovaの後継として開発されているcapacitorを利用します。
capacitorの関連ライブラリをインストールして、npx経由でcap initを実行します。
$ npm install @capacitor/core @capacitor/cli
$ npx cap init
するとAppNameとAppIDの入力を求められるので適当に入力します。
入力が終わるとcapacitor.config.jsonというファイルが生成されるので、その中のwebDirをwwwからbuildに書き換えます。
{
"appId": "com.example.app",
"appName": "App",
"bundledWebRuntime": false,
"webDir": "build"
}
その後npm run buildを行いReactのアプリケーションをビルドしましょう。ビルドされたものはbuild/に保存されます。
保存されていることが確認したら次のコマンドを実行してios用のプロジェクトを生成します。
$ npx cap add ios
$ npx cap sync ios
設定が正しければiosディレクトリが生成されます。
npx cap open iosを実行するとXcodeが開くので、アプリを実行するとシミュレーターが起動しアプリとして動作
※ StatusBarとかぶってしまったので、divタグにmarginTopをつける感じで暫定対応しています。(求む、対応策)
@rdlaboさんより https://qiita.com/rdlabo/items/e05ae0b8986c36af9665 を読むといいかもというアドバイスをいただきました。
見ていった結果iPhoneのsafe-area-inset(iPhoneのノッチに対応する部分の数値を検出するもの)のコードにたどり着きそれが有効になっていないことがわかりました。
調べた結果safe-area-insetを有効にするには、viewportを適切に設定する必要があるとわかりviewport-fit=coverを設定することで上記の問題は解決しました。
さいごに
そもそも日本語情報には期待していないのですが、英語情報ですら今はライブラリのREADME.mdとサンプルアプリが1つみたいな状況です。つらい。
ネイティブな機能に関してはまだ触ってないので、暇な時に触れればいいかなと思っています。
JSでアプリを書くという意味ではReactにはすでにReactNativeが存在しているので、流行るのかはわかりません。ただ、ReactNativeはNativeからWeb、IonicはDOMからNativeと思想が違うのでそういった面では面白いと思いました。Ionic + Vueはなかなか相性が良さそうです。(WeexやVueNativeよりは筋がよさそう)
ただし、私はIonic初心者なので経験者でなにか言いたいことがあればTwitterやDMで連絡もらえると嬉しいです。
注釈
react-routerはreact-router-domの依存ライブラリなのでreact-router-domを入れておけば問題ありません。 ↩
2019/02/20 - ブログのReactを16.8にあげてReact Hooksで書き換えてみた
2019/02/06にReact16.8がリリースされ、16.7のalphaから入っていたReact Hooksが安定版にやってきました。
そこで今回このブログで使われているReactを16.8に上げて、ステートフルなコンポーネントをReact Hooksを使いFunction Compoenentに書き換えてみました。
React Hooksの解説をした記事はたくさんあると思うので、今回は書き換えてどう変わったかを見ていきましょう。
該当する部分はHeaderの検索フォームです。
プルリクエストだけ見たいかたはこちら。
https://github.com/mottox2/website/pull/39
書き換えのポイント
stateの書き換え
これはいろんな記事で言及されているuseStateを用いて書き換えます。
useStateに初期値を引数として与えると、値と更新用の関数が返ってきます。
const NewComponent = props => {
const [query, updateQuery] = useState('')
const handleInput = (e) => {
updateQuery(e.target.value)
}
return <input onChange={handleInput}/>
}
componentDidMountの書き換え
書き換え前のClassComponentです。主にデータの取得とキーボードイベントのつけ外しです。
コンポーネントの各所に処理が散らばってて可読性が低い状態です。
class OldComponent from Component {
async componentDidMount() {
const res = await axios.get('/search.json')
this.setState({ data: res.data })
this.focusShortcutHandler = this.focusShortcut.bind(this);
window.addEventListener('keydown', focusShortcutHandler)
}
componentWillUnmount() {
window.removeEventListener('keydown', focusShortcutHandler)
}
focusShortcut(e) {
if (e.keyCode === keyCodes.SLASH && !this.state.isActive) {
this.focusInput()
e.preventDefault()
}
}
}
useEffectで書き換えると以下のようになります。
useEffectはcomponentDidMount, componentDidUpdateのタイミングで呼ばれて、returnで返した関数がcomponentWillUnmountのタイミングで呼ばれるHookと考えてください。
つまりイベントの付け外しが同じHooksの中で操作出来るので、以前は散らばっていた同じような処理がまとめられ読みやすくなっていると思います。
また、axiosでデータ取得を行っている部分はコンポーネントがマウントされたタイミングでのみ実行したいので第2引数に[]を渡しています。(第2引数は監視したい値を配列で渡す。この場合変更を監視しないという意味でから配列を渡している)
useEffectに関してはReactの公式ドキュメントに詳しく書いてあるので読むといいでしょう。
https://reactjs.org/docs/hooks-effect.html
const NewComponent = (props) => {
useEffect(() => {
axios.get('/search.json').then(res => {
updatePosts(res.data)
})
return undefined
}, [])
useEffect(() => {
const focusShortcut = (e: KeyboardEvent) => {
if (e.keyCode === keyCodes.SLASH && !isActive) {
inputEl.current.focus()
e.preventDefault()
}
}
window.addEventListener('keydown', focusShortcut)
return () => {
window.removeEventListener('keydown', focusShortcut)
}
})
return <div/>
}
prevStateを利用するcomponentDidUpdateの書き換え
Hooksの公式Q&Aにもある内容ですが、prevStateとstateを比較して処理を行う部分があってその書き換えにusePreviousというCustom Hooksを利用しました。
もともとのコードがあまり綺麗でないのはご愛嬌…
const usePrevious = (value) => {
const ref = useRef(null)
useEffect(() => {
ref.current = value
})
return ref.current
}
const NewComponent = () => {
const inputEl = useRef(null)
const prevMobileShow = usePrevious(props.isMobileShow)
if (prevMobileShow !== props.isMobileShow) {
window.setTimeout(() => {
inputEl.current.focus()
}, 10)
}
return <input ref={inputEl} />
}
useMemoを利用した値のメモ化
今回、Hooksに書き換えたコンポーネントは検索フォームです。言ってしまえば、入力されたクエリに応じて表示する記事をフィルタリングするためのコンポーネントです。
こういったコンポーネントで更新するたびにフィルリングの処理を実行すれば動作は重くなっていきます。
そういったときに利用したいのがuseMemoというフックです。useMemoはいわゆるメモ化を行ってくれるフックで関数の実行結果を保持してくれます。
この場合、posts(記事の配列)とquery(入力されたクエリ)に変化がなければ再計算は必要ないはずなので次のような実装になりました。
const filterPosts = (posts, query) => {
return posts.filter(item => {
const itemString = `${item.title} ${item.tags.join('')}`.toLowerCase()
if (!(itemString.indexOf(query) > -1)) {
return false
}
return true
})
}
const NewComponent = (props) => {
const [query, updateQuery] = useState('')
const [posts, updatePosts] = useState([])
const filteredPosts = useMemo(() => filterPosts(posts, query), [posts, query])
return <div />
}
感想
パラダイムシフトを感じた。Viewのすべてが関数で表現出来る世界が近づきつつあるのかもしれない。
これは自分がjQuery文化からReact文化に入った4年前ぐらいの時と同じような衝撃を受けた。
今までVueとReactは大きく変わらないという印象を持っていたが、今回のアップデートで別物という印象に変わった。
React公式のスタンスとしては、React Hooksへの利用を強制するようなものではない…1としているので今まで通りの書き方を続けてもいいが、ぜひReactユーザーにはHooksを触って欲しいと思いました。
注釈
You don’t have to learn Hooks right now. Hooks have no breaking changes, and we have no plans to remove classes from React. https://reactjs.org/blog/2019/02/06/react-v16.8.0.html ↩
2019/02/07 - Netlifyのデプロイ通知をDiscordで受け取る方法
Netlifyでサイトをホスティングしていると、デプロイ開始から完了までのタイムラグがあることでデプロイの完了を把握するにはWeb UIを確認する必要があります。
しかし、デプロイごとにWeb UIを確認するのは面倒です。そんな人のためにNetlifyでは外部のアプリケーションに通知する方法が用意されています。
今回はゲーマー向きチャットとして誕生し、最近はオープンソースのプロジェクトで利用されることが多くなってきたDiscordでの設定方法を説明します。
設定方法
Discordには、特定のチャンネルにメッセージを投稿するWebhook URLを生やす機能があります。DiscordのWebhookはSlack-Compatible Webhookという機能が用意されており、Slackと同じ形式のリクエストでメッセージの投稿ができます。
この機能とNetlifyのSlackインテグレーションを利用することでDiscordにNetlifyのデプロイ通知を投稿することができます。
それでは設定をはじめましょう。
Discord側での設定
Discordで投稿したいチャンネルの設定画面を開いてください。左サイドバーにある「Webhooks」をクリックするとWebhooksの一覧画面が表示されるので「Create Webhook」ボタンをクリックしましょう。
ボタンをクリックすると次のようなモーダルが出現するので、「WEBHOOK URL」の欄に表示されているURLをコピーしておきましょう。Netlify側の設定で使用します。
Netlifyでの設定
設定はサイト詳細ページの「Settings > Build & Deploy > Deploy notification」から行います。
Outgoing notificationをクリックすると通知方法の一覧が表示されるのでSlack integrationを選択しましょう。
選択したタイミングでモーダルが表示されるので「Event to listen for」には通知を受けたいタイミング、「Slack incoming webhook URL」には次のURLを入力しましょう。「Save」を押せば設定は完了です。
[先程取得したURL]/slack
例) https://discordapp.com/api/webhooks/xxxxxxxxxxxxxxxxx/slack
通知の確認
最後に、デプロイを行い通知が正しく送信されているかを確認しましょう。
正しく設定されていれば次のように通知が行われます。
おわりに
このように非常に簡単に通知を設定することができます。Netlifyではビルドで失敗しても、以前のサイトが表示されているためエラーに気づきにくいと思います。Deploy Faildのタイミングでなんらかの通知を設定しておき、ストレスレスなNetlify運用を目指しましょう。
2019/02/02 - netlify-cliを使ってCLIでデプロイを行う
Netlifyには優れたUIを持つWeb画面が用意されていますが、CLI(コマンドラインインターフェース)も用意されており、CLIのみでのデプロイも可能になっています。
通常の開発ではWeb UIで十分ですが、検証中などGitの操作なしでサイトに反映できるので便利です。
netlify-cli@2.6.3で確認した動作を前提に進めます。
インストール方法
npmがインストールされていることが前提で次のコメントを実行します。
$ npm install -g netlify-cli
実行後、次のコマンドを実行してバージョン情報が出力されれば、インストール完了です。
$ netlify -v
netlify-cli/2.6.3 darwin-x64 node-v10.13.0
使用方法
ログイン
まずはCLIとNetlifyのアカウント情報を紐付ける必要があるので次のコマンドを実行します。
実行するとブラウザで認証画面が表示されるので、「Authorize」をクリックします。この操作で、CLIによってNetlifyの情報を操作することが可能になります。
$ netlify login
なお、ログアウトをするにはnetlify logoutを実行してください。
もっと理解する
netlify-cliはOauth Applicationとして動作しています。そのTokenはユーザー情報とともに`~/.netlify/config.json`に保存されています。
このCLI実装は netlify/netlify-cli で公開されているので、NetlifyのようなプラットフォームのCLIを実装したいとなったときの実装の際、大いに参考になるでしょう。
プロジェクトとの紐付け(既存プロジェクト)
ログインとは別にプロジェクトと紐付ける操作も必要です。この操作はWeb UIとは別に紐付ける操作が必要なので注意してください。
紐付けはディレクトリ単位で行います。ディレクトリとNetlifyのsiteが対応するイメージです。
該当のディレクトリで次のコマンドを実行しましょう。
$ netlify link
netlify link will connect a site in app.netlify.com to this folder
? How do you want to link this folder to a site?
❯ Use current git remote url https://github.com/mottox2/website
Site Name
Site ID
コマンドを実行するとgit remote url、site name, site IDのどれで紐づけるかを選択します。
Site Nameは xxxxx.netlify.com 中のxxxxxxに当たる部分、Site IDは WebUIの「Settings > Site Details」に記載されているAPI IDに当たります。
すでにGitHubで連携しているようなサイトであればgit remote urlで連携すると良いでしょう。
紐づけた後は netlify status で紐づけ状況の確認や、netlify open:siteでサイトの確認、netlify deployでCLIからデプロイができるようになります。
もっと理解する
netlify-cliの情報は`.netlify/state.json`に格納されます。個人的には共有しなくていい情報だと思っているので、globalでgitgnoreの対象に入れています。
プロジェクトの作成(新規プロジェクト)
まだNetlifyに登録していないサイトに関してはnetlify initで設定を行います。
netlify initを実行するとContinuous deploymentの設定を行います。
Site nameとAccountの設定を行いましょう。
なお、Web UIとは違いサイトを作っただけではデプロイは行われません。手動でnetlify deployを実行する必要があります。
CLIによるデプロイ
CLIでのデプロイはnetlify deployコマンドで行います。
気をつけないといけないポイントが2点あります。
1点目は、CLIでのデプロイではWeb UIやnetlify.tomlで設定しているビルドコマンドが適用されない点です。
ローカルマシンでビルドコマンドを実行して、公開ディレクトリにファイルが生成された状態で実行する必要があります。
2点目は、デフォルトのdeployではDraft URLが生成され、本番環境には直接反映されません。
次のログはnetlify deployのものですが、Live Draft URLが生成されていることがわかると思います。
Live Draft URLを確認し、問題なければnetlify deploy --prodを実行し本番環境に反映するのが基本的な利用フローになります。
$ netlify deploy
Please provide a deploy path relative to:
/Users/yuki/.ghq/github.com/mottox2/netlify-cli-sample
? deploy path /Users/yuki/.ghq/github.com/mottox2/netlify-cli-sample
Deploy path: /Users/yuki/.ghq/github.com/mottox2/netlify-cli-sample
Deploying to draft URL...
✔ Finished hashing 1 files
✔ CDN requesting 0 files
✔ Finished uploading 0 assets
✔ Draft deploy is live!
Logs: https://app.netlify.com/sites/distracted-stallman-ad88c0/deploys/5c49a1d50e310fd3e09e44d3
Live Draft URL: https://5c49a1d50e310fd3e09e44d3--distracted-stallman-ad88c0.netlify.com
If everything looks good on your draft URL, take it live with the --prod flag.
netlify deploy --prod
$ netlify deploy --prod
Please provide a deploy path relative to:
/Users/yuki/.ghq/github.com/mottox2/netlify-cli-sample
? deploy path /Users/yuki/.ghq/github.com/mottox2/netlify-cli-sample
Deploy path: /Users/yuki/.ghq/github.com/mottox2/netlify-cli-sample
Deploying to live site URL...
✔ Finished hashing 1 files
✔ CDN requesting 0 files
✔ Finished uploading 0 assets
✔ Deploy is live!
Logs: https://app.netlify.com/sites/distracted-stallman-ad88c0/deploys/5c49a6170e310f113c9e44d2
Unique Deploy URL: https://5c49a6170e310f113c9e44d2--distracted-stallman-ad88c0.netlify.com
Live URL: https://distracted-stallman-ad88c0.netlify.com
また、deployコマンドにはオプションが用意されており、--dir=dirで公開ディレクトリを変更したり、--functions=functionsでFunctionsのディレクトリを変更することが出来ます。
CLIでオプションを与えた場合、Web UIやnetlify.tomlでの設定よりも優先されることは念頭に置いておく必要があるでしょう。
もっと理解する
netlify-cliでのデプロイは一瞬で終了しURLが表示されます。Web UIでのデプロイではデプロイする環境を立ち上げてビルドコマンドを実行するなどの操作が含まれるからです。
なので、Netlifyの環境より良い環境を用意できれば、デプロイ時間の短縮やキャッシュの利用も含んだデプロイが可能になります。
まとめ
様々なnetlify-cliの利用法を紹介しました。
Netlifyには優れたUIを持つWeb UIもありますが、CLIも地味に便利なのでぜひ使ってください。
僕はnetlify deployを覚えたおかげでForce Push(git push origin -f)の頻度が減って、誤操作の心配もなくなりました。
2019/01/25 - Netlifyで環境変数を設定する
Netlifyではビルドコマンドを設定して、デプロイ時にビルド処理を実行することが多いです。その際に利用するデータベースのユーザーネーム・パスワードや外部サービスのTokenなどは一般的にはソースコードに含めず環境変数に設定します。
こういった値をソースコードから取り除くことで、開発環境と本番環境の差異を小さくしたり、ソースコードを公開しても問題ない状態に保つことができます。
もちろんNetlifyでも環境変数を設定する方法が用意されています。
Web画面で設定する
プロジェクト画面の「Settings > Build & Deploy」をクリックすると「Build environment variables」という設定項目があります。
この設定項目の「Edit variables」をクリックすると、KeyとValueを入力するテキストフィールドが表示されます。
テキストフィールドに設定したい環境変数を入力しSaveボタンをクリックしてください。
設定した後のデプロイから環境変数が有効になります。
netlify.tomlで設定する
Web画面での設定が推奨されていますが、netlify.tomlでも環境変数を設定できます。
ただし、netlify.tomlで環境変数を設定する場合、gitの管理下に環境変数が露出するため管理方法に気をつける必要があります。
例えば SOME_VARIABLE に some_value という値を設定する場合、次のようなnetlify.tomlを用意します。
netlify.toml
[build.environment]
SOME_VARIABLE = "some_value"
Privateリポジトリであれば、こちらの設定でも問題ありませんがPublicにする可能性がある場合はWeb画面で設定を推奨します。
2019/01/19 - GatsbyJSハンズオン資料
2019/01/08にサポーターズで行われる『Gatsbyで今風ウェブサイトの開発ハンズオン』のハンズオン資料です。
スライド
各種リンク
CodeSandbox
Netlify
Reactのおさらい
src/index.js
import React from "react";
import ReactDOM from "react-dom";
import Hello from "./Hello";
import "./styles.css";
const App = () => {
return (
<div className="App">
<Hello name="an" />
<Hello name="en" />
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
src/Hello.js
import React from "react";
const Hello = props => {
return <p>Hello {props.name}</p>;
};
export default Hello;
Gatsby入門
https://codesandbox.io/s/vvx7w5q2oy
gatsby-config.js
module.exports = {
siteMetadata: {
title: `Gatsby Default Starter`,
description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
author: `@gatsbyjs`,
},
plugins: [
{
resolve: 'gatsby-source-rss-feed',
options: {
name: 'BlogPost',
url: 'https://mottox2.com/rss.xml',
},
},
`gatsby-plugin-react-helmet`,
{
resolve: `gatsby-source-filesystem`,
options: {
name: `images`,
path: `${__dirname}/src/images`,
},
},
`gatsby-transformer-sharp`,
`gatsby-plugin-sharp`,
{
resolve: `gatsby-plugin-manifest`,
options: {
name: `gatsby-starter-default`,
short_name: `starter`,
start_url: `/`,
background_color: `#663399`,
theme_color: `#663399`,
display: `minimal-ui`,
icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
},
},
// this (optional) plugin enables Progressive Web App + Offline functionality
// To learn more, visit: https://gatsby.app/offline
// 'gatsby-plugin-offline',
],
}
gatsby-node.js
const path = require('path')
exports.createPages = ({ graphql, actions }) => {
const { createPage } = actions
const post = path.resolve('./src/templates/post.js')
return graphql(`
{
allFeedBlogPost {
edges {
node {
guid
}
}
}
}
`).then(result => {
console.log(result.data.allFeedBlogPost.edges)
result.data.allFeedBlogPost.edges.forEach(post => {
createPage({
path: `/stories/${post.guid}`,
component: post,
context: {
guid: post.guid
}
})
})
})
}
src/pages/index.js
import React from 'react'
import { Link, graphql } from 'gatsby'
import Layout from '../components/layout'
export default props => (
<Layout>
{props.data.allFeedBlogPost.edges.map(edge => {
const post = edge.node
return (
<p key={post.guid}>
<Link to={`/posts/${post.guid}`}>
{post.title}
</Link>
</p>
)
})}
</Layout>
)
export const query = graphql`
{
allFeedBlogPost {
edges {
node {
guid
title
}
}
}
}
`
src/templates/post.js
import React from 'react'
import { graphql } from 'gatsby'
export default props => {
const post = props.data.feedBlogPost
return (
<div>
<p>{post.title}</p>
<div
dangerouslySetInnerHTML={{
__html: post.content.replace('\n', '<br/>'),
}}
/>
</div>
)
}
export const query = graphql`
query($guid: String!) {
feedBlogPost(guid: { eq: $guid }) {
title
content
}
}
`
2019/01/08 - npmに公開したパッケージを非推奨(deprecated)にする
npmにパッケージを公開したけど、今はもうメンテナンスしていないパッケージはないだろうか?自分にはある。
とあるコードを書いている最中にnpmに非推奨(deprecated)の概念があることがわかったので手順を記録しておく。忘備録的な記事です。
手順
調べたところ、npmのドキュメントがあった。
https://docs.npmjs.com/cli/deprecate.html
npm deprecate <pkg>[@<version>] <message>という形式で実行できる。今回は以下のコマンドを実行した。
$ npm deprecate gatsby-plugin-workbox "Gatsby now supports workbox. Please use gatsby-plugin-offline."
deprecatedになるとどうなるか?
npmのパッケージページ上部にThis package has been deprecatedという表示が出る。
https://www.npmjs.com/package/gatsby-plugin-workbox
また、npm / yarn CLIでインストールする際にdeprecatedに設定したメッセージが表示されるようになる。
この表示からするにdeprecatedなメッセージ以外に、代わりに使ってほしいパッケージを表示するのが親切だろう。
2019/01/05