メインコンテンツへ移動

Next.js から学んだ 6 つのデザインテクニック

無料2021-01-02#Front-End#Mind#Nextjs#create serverless react app#下一代前端框架#前端API设计#前端一体化应用框架

本稿は Next.js シリーズの第 3 篇(そして最終篇)として、そこから発見したデザインテクニック(API デザイン、ドキュメントデザイン、フレームワークデザインなど)を記録し、あなたとも共有します

はじめに

最近 SSR を研究する過程で、Next.js についてより多くの認識を得ました:

  • 全面的紹介:Next.js

  • 核心特性:[Next.js 混合レンダリング](/articles/next-js 混合レンダリング/)

  • デザインテクニック:本稿

本稿は Next.js シリーズの第 3 篇(そして最終篇)として、そこから発見したデザインテクニック(API デザイン、ドキュメントデザイン、フレームワークデザインなど)を記録し、あなたとも共有します

基底クラスを定義するより、モジュールを定義する方が良いかもしれない

まず、クラス(Class)とモジュール(Module)はどちらもコードを組織化する選択肢であり、API デザインのシナリオに置けば、どちらも書き方を制約し、フレームワーク能力を公開するために使用できます。モジュール概念が正統になる之前、フロントエンドフレームワークの多くはこのニーズを満たすために基底クラスを提供していました。選択肢がなかったからです

典型的には、React は React.Component 基底クラスを通じて様々なライフサイクル Hook を公開し、同時にコンポーネントの書き方を定義しています:

// Components
class Clock extends React.Component {
  // Props
  constructor(props) {
    super(props);
    // State
    this.state = {date: new Date()};
  }

  // Lifecycle
  componentDidMount() { }
  componentWillUnmount() { }

  render() {
    // Template
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

Props、State、Lifecycle、Template などのフレームワーク能力を 1 つの Class に統合し、コンポーネントと呼びます。そして、長い期間にわたり、React でコンポーネントと呼べるのは Class のみでした

この長い期間とはどのくらいか?

React 誕生之初から React Hooks が推出され完全形態に進化するまでです。現在(2021/1/2)React Hooks はまだ完全形態ではなく、componentDidCatchgetSnapshotBeforeUpdategetDerivedStateFromError などの特性はまだ健全ではありません。詳細は Do Hooks cover all use cases for classes? を参照

つまり、今日に至るまで、React Components は依然として Class Components と同等であり、早期の関数型コンポーネントは Stateless Components としか呼べず、Hooks のサポートを得た後の関数型コンポーネントは Stateless から脱却しましたが、完全形態の Class Components とはまだ少し差距があります

Components 概念を Class と強く結びつけるのは本当に最悪の選択でした。期待を寄せられた Hooks がこのことを十分に説明しています。しかし Props、State、Lifecycle、Template などのフレームワーク能力は何らかのもので担う必要があります。では、より良い選択は何でしょうか?

おそらく Module です。おそらくと強調するのは、コードを組織化するという点だけで、Module は Class よりも純粋だからです。Module はコードを組織化するのみで、変数、関数などの構文要素を一緒に囲むだけで、Class のようにインスタンス状態、メンバーメソッドなどの追加概念を強加しません

例えば、Next.js の Page 定義は単なるファイルモジュールに過ぎません:

// pages/about.js
function About() {
  return <div>About</div>
}

export default About

最もシンプルな Page は、デフォルトで React コンポーネントを 1 つ公開するだけでよいです。より多くの機能が必要なら、さらに按需でより多くの既定 API を公開します:

// pages/blog.js
function Blog({ posts }) {
  // Render posts...
}

// API 1
export async function getStaticProps() { }
// API 2
export async function getStaticPaths() { }
// API 3
export async function getServerSideProps() { }
// API n
export async function xxx() { }

export default Blog

Class 形式の API デザインと比較して、このModule 式 API デザインはより純粋で、追加の構文要素(特に Class のような根基庞大な構文要素、一連の super()bind(this)static をもたらす)を強加せず、あるシナリオではより良い選択と言えます

ファイル約定ルート

Next.js には Router.registernew Route()app.use() も、考えられるすべてのルート定義 API が存在しません

API が全くないため、ルートはファイルパス約定を採用しています:

// 静的ルート
pages/index.js → /
pages/blog/index.js → /blog
pages/blog/first-post.js → /blog/first-post
pages/dashboard/settings/username.js → /dashboard/settings/username

// 動的ルート
pages/blog/[slug].js → /blog/:slug (/blog/hello-world)
pages/[username]/settings.js → /:username/settings (/foo/settings)
pages/post/[...all].js → /post/* (/post/2020/id/title)

つまり、ソースコードが所在するファイルパスを通じてルートを識別し、さらにはワイルドカードもサポートできます。こんなに神奇なのだから、もちろんソースコードディレクトリを自分の目で見なければ視覚的衝撃を感じられません:

pages
├── _app.js
├── _document.tsx
├── api
│?? ├── collection
│?? │?? ├── [id].tsx
│?? │?? └── index.tsx
│?? ├── photo
│?? │?? ├── [id].tsx
│?? │?? ├── download
│?? │?? │?? └── [id].tsx
│?? │?? └── index.tsx
│?? ├── stats
│?? │?? └── index.tsx
│?? └── user
│??     └── index.tsx
├── collection
│?? └── [slug].tsx
└── index.tsx

API 間のシームレスな連動

前の 2 篇の文章を通じて、Next.js が解決しようとしている問題はプリレンダリングであり、プリレンダリングを巡って SSG、SSR の 2 つのレンダリングモードを探索し、これに基づいて CSR を含む異なるレンダリングモードの混用をサポートしていることがわかります:

  • ISR(Incremental Static Regeneration):増分静的再生成、ランタイムで定期的に静的 HTML を再生成
  • SSG 降级 SSR:事前に生成された静的 HTML にミスした場合、直ちに SSR を実行
  • SSR 帯静的キャッシュ:SSR 完了後、結果をキャッシュし、次回の静的キャッシュ命中時に直接返却(SSG に相当)
  • SSG 結合 CSR:コンパイル時に静的部分(ページ外枠)を生成、CSR で動的部分(ページ内容)を填充
  • SSR 連動 CSR:URL 直接アクセスはより速い SSR、SPA ジャンプはより体験が優れる CSR

API デザインの角度から一見すると、各組み合わせに別致的な名前を付け、専門の API を公開する必要があるように思えます。SSGwithFallback、SSRwithStaticCache、PartialSSG、SPAMode のように...

しかし、Next.js はこれらすべての混用特性をサポートしているだけでなく、トップレベル API を一切追加していません。その做法はいくつかのオプションを追加することです。例えば:

// SSG 基礎款
export async function getStaticProps(context) {
  return {
    props: {}, // will be passed to the page component as props
  }
}

// SSG 変身 ISR、戻り値に revalidate 属性を追加
export async function getStaticProps(context) {
  return {
    props: {}, // will be passed to the page component as props

    // Next.js will attempt to re-generate the page:
    // - When a request comes in
    // - At most once every second
    revalidate: 1, // In seconds
  }
}

// SSG 感知ルートの高級款、getStaticPaths を実装
export async function getStaticPaths() {
  return {
    paths: [
      { params: { ... } } // See the "paths" section below
    ],
    fallback: false
  };
}

// SSG 変身 SSR 帯静的キャッシュ、fallback オプションを true に変更
export async function getStaticPaths() {
  return {
    paths: [
      { params: { ... } } // See the "paths" section below
    ],
    fallback: true
  };
}

// SSG 変身 SSG 降级 SSR、fallback オプションを'blocking'に変更
export async function getStaticPaths() {
  return {
    paths: [
      { params: { ... } } // See the "paths" section below
    ],
    fallback: 'blocking'
  };
}

この細分オプションに基づく API 連動はより軽量で、常にユーザーに漸進的な体感をもたらし、最初にすべての API、関連デザイン概念を理解する必要はなく、どのシナリオに属するかをトップレベルから区分し、どの API を使用すべきかではなく、シナリオの深入りに伴い、最も適切な API/オプションがそこに存在することに気づきます

ドキュメントから明らかにこの差異を感じ取れます。例えば、Next.js は ISR を紹介する場所で、関連する SSR 帯静的キャッシュモードにユーザーを誘導しています:

Incremental Static Regeneration

With getStaticProps you don't have to stop relying on dynamic content, as static content can also be dynamic. Incremental Static Regeneration allows you to update existing pages by re-rendering them in the background as traffic comes in.

This works perfectly with fallback: true. Because now you can have a list of posts that's always up to date with the latest posts, and have a blog post page that generates blog posts on-demand, no matter how many posts you add or update.

ポイント、インタラクティブな初心者チュートリアル

これはドキュメントデザインテクニックとみなせます(ドキュメントももちろんデザインが必要です)。多くの公式ドキュメント/チュートリアルを見ましたが、深い印象を残したのはわずか 3 つです:

  • Redux ドキュメント物語性ドキュメント、手把手で少しずつ redux を設計し、読んでいて全く止められません

  • Electron Demo Appインタラクティブドキュメント、正確には完全ドキュメント付きの Demo で、Demo App を体験しながら関連特性の用法を理解します。React 在做中学 よりも怠け者な方法です

  • Next.js チュートリアルポイント、インタラクティブな初心者チュートリアル、数十ページのチュートリアルを一気に読み終えます

P.S.Redux ドキュメントは 2017 年のバージョン を指し、現在は多くのバージョンに変更され、読むのが非常に悪くなりました(これだけの概念をどうやってそんなに多くのドキュメントにできるのか)

ポイント、インタラクティブな初心者チュートリアルの威力はどの程度か?

眠くて迷糊した状態でもチュートリアルの全内容を読み終え、すべてのテスト問題に正答し、500 ポイントを満タンにできました(もちろん、幻想しないでください。全答でも何の報酬もありません)。事後回想しても不可思議に思えます。そのテクニックは以下の通り:

  • チュートリアルとドキュメントの分離:ナビゲーションバー一級メニューで Docs と Learn を明確に区分。チュートリアル中の部分概念はドキュメントへのリンクがありますが、完全に見なくても完全に付いていけます

  • ポイント:チュートリアルの目立つ位置に獲得ポイントを表示し、1 篇クリックごとに加点

  • インタラクティブ:重要章節にテスト問題があり、正答でも加点。総ポイントはソーシャルプラットフォーム(Twitter)で共有可能

このように見ると、ドキュメントに少量のオンライン教育の成熟モードを融合させるのは、効果が極めて良いかもしれません

デフォルトでベストプラクティスを提供

体験科技と良い製品 を読み、その中で玉伯が提出したデフォルトで好用に深い印象を受けました。Next.js はデフォルトで好用がフレームワークデザイン上の真実のケースと言えます

例えば:

生産活動の角度から見ると、ベストプラクティスは本来デフォルトで提供されるべきです。新たに出現したベストプラクティスを不断に環境層に下沉させます。npm package、ES Module、Babel などのように、現在のフロントエンド開発者はこれらのかつてのベストプラクティスをほとんど気にする必要がなくなりました

フレームワークデザインの角度からのみ言えば、デフォルトで好用はベストプラクティスを提供する基礎上でさらに一歩進み、ベストプラクティスを作り消し、使用者が怠けてすべて本来如此だと思えるようにする必要があります。したがって、ベストプラクティスはただの臨時態であり、ベストプラクティスが形成されていない部分こそ開発者が关心し、差異化競争力を体現する場所です。一度広く认同されたベストプラクティスが形成されれば、デフォルトの基礎施設として沉淀すべきであり、開発者は关心せずともこれらのベストプラクティスがもたらす様々な利点を獲得できます

ベストプラクティスが形成されていない段階から、ベストプラクティスを提供する段階、デフォルトでベストプラクティスを提供する段階までの 3 つの段階は、画像遅延読み込みの例を通じて理解できます:

// 第一段階:ベストプラクティスが形成されていない
scroll
IntersectionObserver
// 業務各自実装、用法示例は存在しない

// 第二段階:ベストプラクティスを提供
React Lazy Load Component
// 用法示例
<LazyLoad height={683} offsetTop={200}>
  <img src='http://apod.nasa.gov/apod/image/1502/2015_02_20_conj_bourque1024.jpg' />
</LazyLoad>

// 第三段階:デフォルトでベストプラクティスを提供
next/image
// 用法示例
<Image
  src="/me.png"
  alt="Picture of the author"
  layout="fill"
/>

第三段階と第二段階の違いは、開発者がどのコンポーネントが遅延読み込み機能を提供できるか(ベストプラクティスを選択)を关心する必要がなく、コンポーネントライブラリ中で最も普通の Image コンポーネントを直接使用するだけで、该有的機能は自然にあり、遅延読み込みはその中の 1 項に過ぎないことです

Serverless へ延伸

Serverless 浪潮 の下、フロントエンド生態もいくつかの変化を起こしており、様々な一体化アプリケーションが湧現しています:

  • フロントエンドプロジェクト/バックエンドプロジェクトを主体とする一体化アプリケーション:Midway Serverless のように、React、Vue などのフロントエンドプロジェクトの統合をサポート

  • SSR を主体とする一体化アプリケーション:Next.js のように、SSR とデータインターフェース(API endpoints)を Serverless Functions としてデプロイすることをサポート

Next.js は SSR サポートを提供し、元々サーバーサイド環境を必要としていました。Serverless の興起は SSR レンダリングサービスの運用保守問題を很好地に解決しました。したがって、その Vercel プラットフォームはデフォルトで SSR サービスと API を Serverless Functions 形式でデプロイすることをサポートしています:

Pages that use Server-Side Rendering and API routes will automatically become isolated Serverless Functions. This allows page rendering and API requests to scale infinitely.

このような一体化アプリケーションはまだベストプラクティスを形成していませんが、伝統的なフロントエンドフレームワークは変革を経験しています。おそらく、未来のある日には、Serverless 技術と十分に融合した一体化アプリケーションフレームワークに取って代わられ、Universal 体系が大行其道するかもしれません

コメント

コメントはまだありません

コメントを書く