Next.js+Nowをかじってみた
目次
Next.jsの勉強ついでに、Hearthstoneのパック開封内容を表示するだけのログサイトを作成してみた。以下のURLでアクセスできます。前者はエイリアスです。
Nowの無料枠を使い公開はしていますが、とりあえず置いてあるだけです。もう少し追加したい要素があるのですが、時間ができたらって感じですかね。
以降は作成中のメモみたいものです。Next入門者なら参考になるかも…ぐらい。ビギナー故、間違ったことも書いているかもしれません。あしからず。
Next.js編
子コンポーネントでpages内のpropsにアクセス
初期状態を取得するgetInitialProps
は、親(pages内)しか利用できません。また、子へ状態を渡すにはReact同様propsを経由する必要があります。
一般的にコンポーネントを包括するLayout(高階コンポーネントのようなもの)を作成してページを構成していくと思いますが、これを各コンポーネントでimport
するのではなく、_app.jsでオーバーライドさせておくと利便が良くなります。
// pages/_app.js抜粋
imoprt Layout from '../components/Layout';
...
render() {
const { Component, pageProps } = this.props
return (
<Container>
<Layout {...pageProps}>
<Component {...pageProps} />
</Layout>
</Container>
);
}
}
Layoutにpages(親)のpropsを渡すため、Layout以下でもgetInitialProps
で取得した値を扱うことができます。全体で使う値ならReact Contextを用いると更に便利かもしれません。
尚、getInitialProps
のコンテキストで取得できるパス情報(pathname, query, asPath)を受け渡したいだけなら、withRouter
だけで取得できます。
markdownの利用
今回作成したサンプルサイトでは過去ログページだけmarkdownで記事を表示しています。jsxでmarkdownを扱うにはmdxが有名ですが、自分はshowdownを利用しました。
メタデータをmarkdown内に記述することができます。
---
title: Hello World
description: Getting Started
keywords: keyword1, keyword2
---
## Header2
paragraph
メタデータを取り出せば、metaタグに利用できます。
const { Converter } from 'showdown';
function Log(props) {
const { content, meta: { title, description, keywords } } = props;
return (
<>
<Head>
<title>{title}</title>
<meta name="description" content={description} />
<meta name="keywords" content={keywords} />
</Head>
<article>
<div dangerouslySetInnerHTML={{ __html: content }} /> }
</article>
</>
);
}
Log.getInitialProps = async (reqOrContext) => {
const data = import('test.md');
const converter = new Converter({
metadata: true,
tables: true
});
const content = converter.makeHtml(data.default);
const meta = converter.getMetadata();
return { content, meta }
}
dangerouslySetInnerHTML
の内部はスタイルが注入できないため、styled-jsxを使う場合はグローバル空間にスタイルを定義することになります。また、Converterのextensions機能を使うと、markdownにより書き出されるHTMLのタグを加工することができます。
グローバルスタイル
Nextのスタイル定義はstyled-jsxで、各コンポーネントでスコープが閉じられています。CSSの汚染がなくなり便利ですが、フォントなどはグローバルに定義したい場合もあります。
そのときは<styled jsx global>
で定義します。Layoutのような上位コンポーネントに定義することになるでしょう。
別ファイルに分けたい場合
スタイルを別ファイルに分けたい場合は、styled-jsx/css
を利用します。
// styles/global.js
import css from 'styled-jsx/css';
import './normalize.min.css';
export default css.global`
html {
font-size: 62.5%;
height: 100%;
}
`;
上記ではnormalizeもインポートしつつ、独自のスタイルを設定しています。
// components/Layout.js
import globalStyles from '../styles/global';
const Layout = props => (
<>
<div>
{props.children}
</div>
<style jsx global>{`
{globalStyles}
`}</style>
</>
);
CSS利用の注意点
NextでCSSを使いたい場合はnext-cssを利用しますが、cssModules
をtrue
にするとCSSを別途読み込むタイプの外部ライブラリは動かなくなるので注意です。
false
にすると、代わりスタイルのスコープ汚染について考える必要がでてきます。
Now編
アップロードさせたくないファイル
Nowを利用する場合、デフォルトではプロジェクト内の全ファイルがアップロードされます。すべてが公開になるわけではありませんが、デプロイに時間もかかるので避けましょう。
アップロード不要なファイルは.nowignore
というファイルに記述して、ルートに配置します。
node_modules
定義方法はgithubの.gitignore
と同じです。
ビルド時のクリーンアップ
変更分がビルドされるため、削除したファイルのビルドファイルが.nextディレクトリ内に残っているケースがあります。実行時にクリーンアップをした方がいいかもしれません。
"scripts": {
"build": "rm -rf .next && next build",
}
ダッシュボードから削除したエイリアスが残る場合
ZeitのWebダッシュボードからエイリアスを設定したプロジェクトを削除しても、URLは生きている場合があります。コマンドで一覧を表示すると、エイリアスが残っていてURLも有効でした。
now alias ls
> 3 aliases found under maeda [784ms]
source url age
pa9log-nirb5c5sm.now.sh pa9log.now.sh 11m
pa9log-nirb5c5sm.now.sh pa9log.maeda.now.sh 17m
hs-packrecord-fqloofbo1.now.sh hs-packrecord.maeda.now.sh 30m
完全に削除するには、コマンドから削除します。
now alias rm hs-packrecord.maeda.now.sh
> The following alias will be removed permanently
hs-packrecord-fqloofbo1.now.sh hs-packrecord.maeda.now.sh 31m ago
> Are you sure? [y/N] y
> Success! Alias hs-packrecord.maeda.now.sh removed [4s]
エイリアスはデプロイ時に
デプロイ時にエイリアスを設定するには、now.jsonにaliasを設けてnow --target production
で行うのがベストです。
{
"version": 2,
"name": "pa9log",
"alias": "pa9log.now.sh",
...
}
package.json
"scripts": {
"deploy": "now --target production"
}
少し前まではコマンド内でエイリアスの追加と削除を行なっていたようですが、現在は上記のコマンドで一発解決するようになっています。
おわりに
Nextは直感的です。今回シンプルなことしかしていないというのもありますが、お作法に従えばロジック少なめでデザインやコンポーネントの設計に集中できます。余談ですが今回1番時間がかかったのはNextと関係ない部分…データそのものでした。母体がエクセルにあるため、それを集約してJSONで書き出すというマクロ作成がすこぶる時間泥棒でした。