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を利用しますが、cssModulestrueにすると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",
  ...
}
"scripts": {
  "deploy": "now --target production"
}

少し前まではコマンド内でエイリアスの追加と削除を行なっていたようですが、現在は上記のコマンドで一発解決するようになっています。

おわりに

Nextは直感的です。今回シンプルなことしかしていないというのもありますが、お作法に従えばロジック少なめでデザインやコンポーネントの設計に集中できます。

余談ですが今回1番時間がかかったのはNextと関係ない部分…データそのものでした。母体がエクセルにあるため、それを集約してJSONで書き出すというマクロ作成がすこぶる時間泥棒でした。現状はNextでJSONを読みだしているだけですが、無駄にサーバーレスにしているので、恩恵を受けられる程度に何か試してみたいところであります。