20代後半エンジニアである私がこれから学ぶべきこと

f:id:silverbirder180:20201027191727j:plain
PexelsのAkil Mazumderによる写真

私は、現在26歳のWebエンジニアです。これまでの技術に対する学び方と、これからの技術に対する学び方について、少し考えたいと思っています。

これまでの20代前半

新卒入社した当時の私は、業務上、PHP + MySQL on AWS の組み合わせでWebアプリケーション開発を学んでいました。 それとは別に、プライベートでは、Node.js + MongoDB on SAKURA レンタルサーバでWebアプリケーション開発もしていました。

当時、Webアプリケーション開発のフロントエンドとバックエンドをなんとなく動かせる程度で満足していたのですが、 先輩や同僚のエンジニアや、お勧めされた書籍から影響を受け、あらゆるものに興味を示しました。

Webアプリケーションに関する幅広い領域をあらゆる角度で学んでいったのかなと思います。

これからの20代後半

これからも、興味を持った分野を見つけて、手あたり次第学んでいくのも1つのキャリアだと思います。 ただ、自信過剰という訳じゃないですが、多分やればある程度できるようになれると自負しています。

しかし、私としては、そろそろ広く浅くから、狭く深く学ぶ領域(専門性)を見つけたいと思っています。 どういった領域を突き詰めたいか、考える時がきたのかもしれません。

何を突き詰めたいか

単純にソフトウェア技術を突き詰め、テックリードを目指すか、 プロジェクトマネージャーやエンジニアリングマネージャーといった管理職を目指すか。 それでいうと、前者を目指したい。具体的には、Webに関わる専門性を突き詰めたいなと。 また、アーキテクトのような職業に憧れています。

VueやReactなど言語は違えど、Webアプリケーションの成果物はそう変わりません。 だとすると、あまり似たようなベクトルの言語を学んだところで、面白みが薄いのではと思っています。

それよりも、どのレイヤーにどういう役割を持たせるか、クラスをどのように設計するか、レスポンス速度をより早くするためにはどのようなシステム構成にすべきなのか、そういった視点を考える方が楽しいと最近感じています。

また、アーキテクト的な視点以外に、WebのNative(標準)部分の話やブラウザの話、W3Cの動向など、これからのWebについてキャッチアップし続けることにも、力を注ぎたいと思っています。

それらを踏まえると、『Webの専門性が高いアーキテクト』のような立ち位置を目指したいと思います。

最後に

ポエムみたいな話になってしまいました。以上、最近の悩み事でした。

技術におけるアンテナの張り方 (巨人の肩に乗れ!)

Photo by JESHOOTS.COM on Unsplash

エンジニアは、普段様々なところから技術をキャッチアップすると思います。それは、SNSであったり、ブログであったり、動画であったりです。 そこで、私の技術をキャッチアップするためのアンテナの張り方について、紹介しようと思います。

RSS

RSSは、良いです。不定期にお気に入りの企業やサービスのテクニカルな情報を知ることができます。 特に、大手の企業のブログや、自分自身の関心を持っているサービスのRSSがオススメです。

Tech Blog

Web

CDN

BBS

Book

News

Twitter

Twitterの公式アカウントをフォローするのも良いです。 また、特定分野に長けているエンジニアの方であったり、コミュリティアカウントもフォローすると良いでしょう。

例えば、私の場合は、次のアカウントをフォローしていたりします。

UseCotlin

私の独自ツールです。TwitterAPIを使って、SpeakerdeckやGoogleSlidesなどの技術スライドをSpreadsheetに収集しています。 勉強会やカンファレンスが開催されると、Twitterで宣伝されることが多いため、こちらのツールで自動的にキャッチアップできます。 また、日本だけではなく、世界各国のプレゼンテーションを知ることができます。

Medium

Mediumの有料会員です。Mediumは、Tech系の質の高い投稿が多く充実できます。 無料会員だと、一日の閲覧回数に制限があるため、ちょっとストレスに感じ、有料会員になりました。

Hatena Blog

今日のホットエントリを見て、何が流行りなのか、どういう分野に関心があるのかざっとななめ読みしています。

また、テクノロジだけではなく、総合分野を見ていたりします。

最後に

このネタを思いついたので、勢いで投稿してみました。 もしどなたかの参考になれば、幸いです。

Micro Frontends を調べたすべて

Photo by Dil on Unsplash

Micro Frontendsに関わる記事を100件以上読みました(参考記事に記載しています)。そこから得たMicro Frontendsについてこの投稿に記録します。 また、調査メモについて、次のリポジトリに残しています。 github.com

発端

www.thoughtworks.com

実績企業

Pros/Cons

Pros

観点 内容
独立性 ・任意のテクノロジーと任意のチームで開発可能
展開 ・特定の機能をエンドツーエンド(バック、フロント、デプロイ)で確実に実行可能
俊敏性 ・特定のドメインについて最高の知識を持つチーム間で作業を分散すると、リリースプロセスが確実にスピードアップして簡素化される。
・フロントエンドとリリースが小さいということは、リグレッションテストの表面がはるかに小さいことを意味する。リリースごとの変更は少なく、理論的にはテストに費やす時間を短縮できる。
・フロントエンドのアップグレード/変更にはコストが小さくなる

Cons

観点 内容
独立性 ・独立できず、相互接続しているチームが存在しがち
・多くの機能で複数のマイクロフロントエンドにまたがる変更が必要になり、独立性や自律性が低下
・ライブラリを共有すること自体は問題ないが、不適切な分割によって作成された任意の境界を回避するための包括的な場所として使用すると、問題が発生する。
コンポーネント間の通信の構築は、実装と維持が困難であるだけでなく、コンポーネントの独立性が取り除かれる
・横断的関心事への変更ですべてのマイクロフロントエンドを変更することは、独立性が低下する
展開 ・より大きな機能の部分的な実装が含まれているため、個別にリリースできない
・サイト全体の CI / CD プロセス
俊敏性 ・重複作業が発生する
・検出可能性が低下した結果、一部の標準コンポーネントを共有できず、個別のフロントエンド間で実装が重複してしまう。
・共有キャッシュがないと、各コンポーネントは独自のデータセットをプルダウンする必要があり、大量の重複呼び出しが発生する。
パフォーマンス ・マイクロフロントエンドの実装が不適切な場合、パフォーマンスが低下する可能性がある。

統合パターン

bluesoft.com

統合 選択基準 技術
サーバーサイド統合 良好な読み込みパフォーマンスと検索エンジンのランキングがプロジェクトの優先事項であること ・Podium
・Ara-Framework
・Tailor
・Micromono
・PuzzleJS
・namecheap/ilc
エッジサイド統合 サーバーサイド統合と同じ ・Varnish EDI
・Edge Worker

CDN
Akamai
・ Cloudfront
・ Fastly
・CloudFlare
・ Fly.io
クライアント統合 さまざまなチームのユーザーインターフェイスを 1 つの画面に統合する必要があるインタラクティブなアプリケーションを構築すること Ajax
・Iframe
・Web Components
・Luigi
・Single-Spa
・FrintJS
・Hinclude
・Mashroom
ビルド時統合 他の統合が非常に複雑に思われる場合に、
小さなプロジェクト(3 チーム以下)にのみ使用すること
・ Bit.dev
・ Open Components
・ Piral

機能

コミュニケーション

developer.mozilla.org github.com

データ共有

  • ストレージ
    • URL
    • Cookie
    • Local Storage/Session Storage

モジュール共有

  • webpack

webpack.js.org webpack.js.org webpack.js.org

ルーティング

Vaddin router vaadin.com

キャッシュ

developer.mozilla.org developer.mozilla.org

認証

  • JWT

jwt.io

計測

Real User Monitoring

  • SpeedCurve
  • Catchpoint
  • New Relic
  • Boomerang.js
  • Parfume.js
  • sitespeed.io

Synthetics Monitoring

  • Lighthouse
  • WebpageTest

Proxy

コンポジションプロキシ。テンプレートを組み合わせる。 github.com

アクセス履歴

developer.mozilla.org

分割ポリシー

フロントエンドを分割する方針について

  • 水平分割
    • 画面内にある要素で分割
    • ビジネス上の機能
  • 垂直分割
    • 画面毎に分割

Webサイト⇔Webアプリ

document-application

Microfrontends: An approach to building Scalable Web Apps

マイクロフロントエンドは、かなりのオーバーラップがあるバンドの中央部分の大部分に最も適しています。バンドの両極端に該当するプロジェクトにマイクロフロントエンドアーキテクチャを実装しようとすると、生産性に反するそうです。

リポジトリ

パターン Pros Cons 技術
モノリポ コードベース全体に簡単にアクセスできる。
(検出可能性が高い)
モノリポジトリは、特に大規模なチームで作業しているときに、
動作が遅くなる傾向があり、バージョン管理下のコミットとファイルの数が増加する。
・nx.dev
・lerna
マルチリポ ・マルチリポジトリは、非常に大規模なプロジェクトと
それに取り組む非常に大規模なチームがある場合に最適。
マルチリポジトリ環境では、各マイクロアプリを
個別にビルドする必要がある。

アーキテクチャ

アーキテクチャ 関係リンク
Modular Monolith Deconstructing the Monolith – Shopify Engineering
kgrzybek/modular-monolith-with-ddd
Enterprise Architecture (Clean Architecture) Building an Enterprise Application with Vue
soloschenko-grigoriy/vue-vuex-ts
Jam Stack Jam Stack
App Shell App Shell モデル

書籍

www.manning.com

参考記事

Zalando tailor で Micro Frontends with ( LitElement & etcetera)

Photo by Kenny Luo on Unsplash

Zalando社が開発したTailorを使って、サンプルWebアプリをMicro Frontendsで構築してみました。Tailorはサーバーサイドで統合するアーキテクチャです。クライアントサイドは、Web Componentsで作られているLit Elementを使って統合しました。どういった内容か、ここに投稿しようと思います。

作ったリポジトリは、下記に残しています。 github.com

全体構成

アプリケーション構成

ざっくり説明すると、HTMLからTailorに対してフラグメント(コンポーネント)を取得・返却するようにします。各フラグメントは、LitElementでWebComponentsを定義させたJavascriptを指します。フラグメントを読み込むだけで、カスタムエレメントを使えるようになります。

Tailor

image

github.com

A streaming layout service for front-end microservices

tailorは、ストリーミングレイアウトサービスというだけあって、fragmentのloadをストリーミングするそうです。(こちらのライブラリは、FacebookBigPipe に影響されたそう)

まず、tailor.jsのHTMLテンプレートは次のとおりです。

templates/index.html

<body>
  <div id="outlet"></div>
  <fragment src="http://localhost:7000" defer></fragment>
  <fragment src="http://localhost:8000" defer></fragment>
  <fragment src="http://localhost:9000" defer></fragment>
</body>

これらのfragmentの取得は、tailor.jsを経由します。

tailor.js

const http = require('http')
const Tailor = require('node-tailor')
const tailor = new Tailor({
    templatesPath: __dirname + '/templates'
})

http
    .createServer((req, res) => {
        req.headers['x-request-uri'] = req.url
        req.url = '/index'
        tailor.requestHandler(req, res)
    })
    .listen(8080)

x-request-uriは、後ろのフラグメントにURLを引き継ぐためのようです。 そして、フラグメントサーバーは、次のとおりです。

fragments.js

const http = require('http')
const url = require('url')
const fs = require('fs')

const server = http.createServer((req, res) => {
  const pathname = url.parse(req.url).pathname
  const jsHeader = { 'Content-Type': 'application/javascript' }
  switch(pathname) {
    case '/public/bundle.js':
      res.writeHead(200, jsHeader)
      return fs.createReadStream('./public/bundle.js').pipe(res)
    default:
      res.writeHead(200, {
        'Content-Type': 'text/html',
        'Link': '<http://localhost:8000/public/bundle.js>; rel="fragment-script"'
      })
      return res.end('')
  }
})

server.listen(8000)

fragments.jsは、Response HeaderにLinkヘッダを追加するようにします。Tailorは、このヘッダのJavascriptを読み込むことになります。 さらに、fragments.jsは、Linkヘッダで指定されたリクエストを return fs.createReadStream('./public/bundle.js').pipe(res) でストリームのパイプを返すそうです。

Lerna

それぞれのフラグメントをLernaで管理するようにします。 私は、下記のようなpackages分けをしました。

  • common
    • 共通する変数・ライブラリ
  • fragment
    • LitElementのカスタムエレメント定義
  • function
    • フラグメントと連携する関数 (ヒストリーやイベントなど)

具体的に言うと、次のようなものを用意しました。

directoy name package name
packages/common-module @type/common-module
packages/common-variable @type/common-variable
packages/fragment-auth-components @auth/fragment-auth-components
packages/fragment-product-item @product/fragment-product-item
packages/fragment-search-box @search/fragment-search-box
packages/function-event-hub @controller/function-event-hub
packages/function-history-navigation @controller/function-history-navigation
packages/function-renderer-proxy @controller/function-renderer-proxy
packages/function-search-api @search/function-search-api
packages/function-service-worker @type/function-service-worker

どの名前も、その時の気分で雑に設定したので、気にしないでください。(笑) 伝えたいのは、@XXX が1チームで管理する領域みたいなことをしたかっただけです。

packageを使いたい場合は、次のような依存を設定します。

package.json

{
  "dependencies": {
    "@controller/function-event-hub": "^0.0.0",
    "@type/common-variable": "^0.0.0",
  }
}

LitElement

lit-element.polymer-project.org

LitElement A simple base class for creating fast, lightweight web components

純粋なWebComponentsだけを使えばよかったのですが、次のような理由で LitElementを使いました。

まあ、特にこだわりはないです。 書き方は、次のとおりです。

import {LitElement, html, customElement, css, property} from 'lit-element';

@customElement('product-item')
export class ProductItem extends LitElement {
    static styles = css`
    :host {
      display: block;
      border: solid 1px gray;
      padding: 16px;
      max-width: 800px;
    }
  `;
    @property({type: String})
    name = ``;

    render() {
        return html`<div>${this.name}</div>`;
    }
}

declare global {
    interface HTMLElementTagNameMap {
        'product-item': ProductItem;
    }
}

LitElement + Typescript では、open-testing を使ってテストすることができます。 github.com

また、jestでもテストができるようです。

www.ninkovic.dev

DynamicRendering

このサンプルでは、カスタムエレメントを使って、ブラウザ側でレンダリングする 所謂SPAの動きで構築しています。 『SEOガー!』とSSRしなきゃと思う訳ですが、正直SSRを考えたくないです。(ハイドレーションなんて無駄なロードをブラウザにさせたくない) 次の記事のように、ボットのアクセスのみに、ダイナミックレンダリングした結果(SPAのレンダリング結果HTML)を返すようにしたいです。

developers.google.com

技術的には、次のようなものを使えば良いです。

github.com

function-renderer-proxy/src/renderer.ts

...
const page = await this.browser.newPage(); // browser: Puppeteer.Browser
...
const result = await page.content() as string;  // Puppeteerのレンダリング結果コンテンツ(HTML)

要は、Puppeteerで実際にレンダリングさせた結果をBotに返却しているだけです。

EventHub

フラグメント同士は、CustomEventを通して連携します。

https://developer.mozilla.org/ja/docs/Web/Guide/Events/Creating_and_triggering_eventsdeveloper.mozilla.org

全て、このCustomEventとAddEventListenerを管理するEventHub(packages名)を経由するようにします。(理想)

History

ページ全体のヒストリーは、HistoryNavigation(packages名)で管理したいと考えています。(理想)

developer.mozilla.org

また、ルーティングを制御する Web Components向けライブラリ vaadin/router も便利そうだったので導入してみました。

vaadin.com

ShareModule

LitElementのようなどこでも使っているライブラリは、共通化してバンドルサイズを縮めたいです。 Webpackのようなバンドルツールには、ExternalやDLLPlugin、ModuleFederationなどの共通化機能があります。

webpack.js.org

今回は、externalを使っています。

common-module/common.js

exports['rxjs'] = require('rxjs')
exports['lit-element'] = require('lit-element')
exports['graphql-tag'] = require('graphql-tag')
exports['graphql'] = require('graphql')
exports['apollo-client'] = require('apollo-client')
exports['apollo-cache-inmemory'] = require('apollo-cache-inmemory')
exports['apollo-link-http'] = require('apollo-link-http')

common-module/webpack.config.js

module.exports = {
    entry: './common.js',
    output: {
        path: __dirname + '/public',
        publicPath: 'http://localhost:6006/public/',
        filename: 'bundle.js',
        libraryTarget: 'amd'
    }
}

通化したライブラリは、次のTailorのindex.htmlで読み込みます。

templates/index.html

    <script>
        (function (d) {
            require(d);
            var arr = [
                'lit-element',
                'rxjs',
                'graphql-tag',
                'apollo-client',
                'apollo-cache-inmemory',
                'apollo-link-http',
                'graphql'
            ];
            while (i = arr.pop()) (function (dep) {
                define(dep, d, function (b) {
                    return b[dep];
                })
            })(i);
        }(['http://localhost:6006/public/bundle.js']));
    </script>

そうすると、例えばsearchBoxのwebpackでは、次のようなことが使えます。

fragment-search-box/webpack.config.js

externals: {
    'lit-element': 'lit-element',
    'graphql-tag': 'graphql-tag',
    'apollo-client': 'apollo-client',
    'apollo-cache-inmemory': 'apollo-cache-inmemory',
    'apollo-link-http': 'apollo-link-http',
    'graphql': 'graphql'
}

その他

その時の気分で導入したものを紹介します。(or 導入しようと考えたもの)

GraphQL

APIは、雑にGraphQLを採用しました。特に理由はありません。

SkeltonUI

Skelton UIも使ってみたいなと思っていました。

material-ui.com

Reactを使わなくても、CSSの@keyframesを使えば良いでしょう。が、まあ使っていません。(笑)

developer.mozilla.org

Rxjs

typescriptの処理をリアクティブな雰囲気でコーディングしたかったので導入してみました。

(リアクティブに詳しい人には、怒られそうな理由ですね...笑)

rxjs.dev

所感

これまで、Podium、Ara-Framework, そして Tailor といったMicro Frontendsに関わるサーバーサイド統合ライブラリを使ってみました。

silverbirder180.hatenablog.com silverbirder180.hatenablog.com

これらは、どれも考え方が良いなと思っています。 Podiumのフラグメントのインターフェース設計、Ara-FrameworkのRenderとデータ取得の明確な分離、そしてTailorのストリーム統合です。 しかし、これらは良いライブラリではありますが、プロダクションとしてはあんまり採用したくない(依存したくない)と思っています。

むしろ、もっと昔から使われていた Edge Side Includeや Server Side Include などを使ったサーバーサイド統合の方が魅力的です。 例えば、Edge Worker とか良さそうです。(HTTP2やHTTP3も気になります)

まあ、まだ納得いくMicro Frontendsの設計が発見できていないので、これからも検証し続けようと思います。

Ara-Framework で Micro Frontends with SSR

みなさん、こんにちは。silverbirder です。 私の最近の興味として、Micro Frontends があります。 silverbirder180.hatenablog.com 今、Ara-Frameworkというフレームワークを使った Micro Frontends のアプローチ方法を学んでいます。

Ara-Framework とは

Build Micro-frontends easily using Airbnb Hypernova

https://ara-framework.github.io/website/

Ara-Frameworkは、Airbnbが開発したHypernovaというフレームワークを使って、Micro Frontendsを構築します。

Airbnb Hypernova とは

A service for server-side rendering your JavaScript views

https://github.com/airbnb/hypernova

簡単に説明すると、Hypernovaはデータを渡せばレンダリング結果(HTML)を返却してくれるライブラリです。 これにより、データ構築とレンダリングを明確に分離することができるメリットがあります。

Ara-Framework アーキテクチャ

Ara-Frameworkのアーキテクチャ図は、次のようなものです。

https://cdn-images-1.medium.com/max/2400/1*43CBDwIZ8P2q_ZfGg_ktUQ.png

https://ara-framework.github.io/website/docs/nova-architecture

構成要素は、次のとおりです。(↑の公式ページにも説明があります)

  • Nova Proxy
    • ブラウザのアクセスをLayoutへプロキシします。
    • Layoutから返却されたHTMLをパースし、Hypernovaのプレースホルダーがあれば、Nova Clusterへ問い合わせします。
    • Nova Clusterから返却されたHTMLを、Hypernovaのプレースホルダーに埋め込み、ブラウザへHTMLを返却します。
  • Nova Directive (Layout)
    • 全体のHTMLを構築します。Hypernovaのプレースホルダーを埋め込みます。
    • Node.js, Laravel, Jinja2 が対応しています。
  • Nova Cluster
    • Nova Bindingを管理するクラスタです。
    • Nova ProxyとNova Bindings の間に位置します。
  • Nova Bindings (Hypernova)
    • データを渡されて、HTMLをレンダリングした結果を返します。 (Hypernovaをここで使います)
    • React, Vue.js, Angular, Svelte, Preact が対応しています。

このように、LayoutとRendering (Nova Bindings) を明確に分けることで、独立性、スケーラビリティ性が良いのかなと感じます。 各レイアの間にキャッシュレイヤを設けることでパフォーマンス向上も期待できます。

詳しくは、公式ページをご確認下さい。

Ara-Framework サンプルコード

Ara-Frameworkを実際に使ってみました。サンプルコードは下記にあげています。 github.com

package.json はこんな感じです。

package.json

  "scripts": {
    "cluster": "cd cluster && PORT=5000 ara run:cluster --config ./views.json",
    "layout": "cd layout && PORT=8080 node ./bin/www",
    "proxy": "cd proxy && HYPERNOVA_BATCH=http://localhost:5000/batch PORT=8000 ara run:proxy --config ./nova-proxy.json",
    "search:dev": "cd search && PORT=3000 ./node_modules/webpack/bin/webpack.js --watch --mode development",
    "product:dev": "cd product && PORT=3001 ./node_modules/webpack/bin/webpack.js --watch --mode development",
    "dev": "concurrently -n cluster,layout,proxy,search,product \"npm run cluster\" \"npm run layout\" \"npm run proxy\" \"npm run search:dev\" \"npm run product:dev\"",
  }

作っていく手順は、次の流れです。

  1. Nova Proxy を作成
  2. Nova Directive (Layout) を作成
  3. Nova Cluster を作成
  4. Nova Bindings (Hypernova) を作成

Ara-Framework を使うためには、次の準備をしておく必要があります。

$ npm i -g ara-cli

Nova Proxy

Nova Proxyは、Nova DirectiveへProxyしますので、そのhostを書きます。

nova-proxy.json

{
  "locations": [
    {
      "path": "/",
      "host": "http://localhost:8080",
      "modifyResponse": true
    }
  ]
}

また、Nova Proxyは、Nova Clusterへ問い合わせするため、HYPERNOVA_BATCH という変数にURLを指定する必要があります。 Nova Proxyを動かすときは、次のコマンドを実行します。

$ HYPERNOVA_BATCH=http://localhost:5000/batch PORT=8000 ara run:proxy --config ./nova-proxy.json

Nova Directive (Layout)

Nova Directvieは、hypernova-handlebars-directive を使います。 これは、Node.jsのhandlebarsテンプレートエンジン(hbs)で使えます。

Expressの雛形を生成します。

$ npx express-generator -v hbs layout

詳細は割愛しますが、次のHTMLファイル(hbs)を作成します。

※ 詳しくはこちら https://ara-framework.github.io/website/docs/render-on-page

layout/index.hbs

<h1>{{title}}</h1>
<p>Welcome to {{title}}</p>

{{>nova name="Search" data-title=title }}

<script src="http://localhost:3000/public/client.js"></script>
<script src="http://localhost:3001/public/client.js"></script>

{{>nova}} がHypernovaのプレースホルダーである hypernova-handlebars-directive です。 nameは、Nova Bindingsの名前 (後ほど説明します)、data-*は、Nova Bindingsに渡すデータです。 また、scriptでclient.jsをloadしているのは、CSRを実現するためです。

動かすのは、Expressを動かすときと同じで、次になります。

$ PORT=8080 node ./bin/www

Nova Cluster

Nova Clusterは、Nova Bindingsを管理します。

views.json

{
  "Search": {
    "server": "http://localhost:3000/batch"
  },
  "Product": {
    "server": "http://localhost:3001/batch"
  }
}

SearchやProductは、後ほど作成するNova Bindingsの名前です。serverは、Nova Bindingsが動いているURLです。

Nova Clusterを動かすときは、次のコマンドを実行します。

$ PORT=5000 ara run:cluster --config ./views.json

Nova Bindings

Nova Bindings を作るために、次のコマンドを実行します。

$ ara new:nova search -t react
$ ara new:nova product -t vue

そこから、自動生成されたディレクトリから、少し修正したものが次のとおりです。

search/Search.jsx

import React, { Component } from 'react'
import { Nova } from 'nova-react-bridge'

class Search extends Component {
  render() {
      <div>
        <div>Search Components!</div>
        <table>
          <tr>
            {['🐙', '🐳', '🐊', '🐍', '🐷', '🐶', '🐯'].map((emoji, key) => {
              return <td key={key}>
                <Nova
                  name="Product"
                  data={{title: emoji}}/>
              </td>
            })}
          </tr>
        </table>
      </div>
  }
}

今までの説明ではなかったですが、Nova Bridge である nova-react-bridge を使っています。 これは、Nova Directiveに似ているのですが、使えるファイルが ReactやVue.jsなどのJSフレームワークに対応しています。 そのため、Nuxt.jsやNext.js,Gatsby.js にも使えるようになります。

※ わかりにくいですが、このサンプルのNova Bridgeは、CSRで動作します。SSRで動作させるためには、Nova Proxyを挟む必要が (たぶん) あります。

product/Product.vue

<template>
  <div>{{title}}</div>
</template>

<script>
export default {
  props: ['title']
}
</script>

Nova Bindingsのこれらを動作させるためには、次のコマンドを実行します。

# search
$ PORT=3000 ./node_modules/webpack/bin/webpack.js --watch --mode development
# product
$ PORT=3001 ./node_modules/webpack/bin/webpack.js --watch --mode development

動作確認

今まで紹介したものを同時に実行する必要があります。 そこで、concurrently を使います。

$ concurrently -n cluster,layout,proxy,search,product "npm run cluster" "npm run layout" "npm run proxy" "npm run search:dev" "npm run product:dev"

動作として、次のような画像になります。

最後に

繰り返しますが、Ara-Framework を使うとデータ構築(Nova Directive)とレンダリング(Nova Bindings)を明確に分離できます。 また、レンダリング部分は、それぞれ独立できます。今回紹介していないAPI部分は、誰がどのように管理するのか考える必要があります。

ただ、Nova Bindingsで使用するCSRjavascriptは、重複するコードが含まれてしまい、ブラウザロード時間が長くなってしまいます。 そこで、webpack 5から使えるようになったFederation機能を使って解決するとった手段があります。

Ara-Frameworkの紹介でした!