Reactで
同型アプリケーションを作成するには、通常、Node.jsをサーバー側として使用します。 ただし、サーバーがJavaで作成されている場合は、同形アプリケーションを放棄しないでください。Javaには、Reactを使用したHTMLのサーバー側レンダリングに対応できる組み込みJavaScriptエンジン(Nashorn)が含まれています。
Javaサーバーを使用したサーバー側のReactレンダリングを示すアプリケーションのコードは
GitHubにあります 。 記事で私は考慮します:
Javaサーバー
マイクロサービス(サーブレットコンテナを使用する必要のない自己実行jar)のスタイルで、Javaでサーバーを作成することを検討してください。 依存関係管理のライブラリとして、CDI標準(Contexts and Dependency Injection)を使用します。これはJava EEの世界から来ましたが、Java SEアプリケーションで使用できます。 CDI実装-Weld SEは、依存関係管理のための強力で十分に文書化されたライブラリです。 CDIには、他のライブラリへの多くのバインダーがあります。たとえば、アプリケーションはJAX-RSおよびNetty用のCDIバインダーを使用します。 src / main / resources / META-INFディレクトリ(このモジュールがCDIをサポートするという宣言)にbeans.xmlファイルを作成し、クラスを標準属性でマークアップし、コンテナを初期化するだけで十分です。依存関係を注入できます。 特別な注釈が付けられたクラスは自動的に登録されます(手動登録も可能です)。
CDI依存関係を持つクラスをテストするには、JUnitの
Arquillian拡張機能を使用します。
単体テスト @RunWith(Arquillian.class) public class IncrementResourceTest { @Inject private IncrementResource incrementResource; @Deployment public static JavaArchive createDeployment() { return ShrinkWrap.create(JavaArchive.class) .addClass(IncrementResource.class) .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml"); } @Test public void getATest() { final Map<String, Integer> response = incrementResource.getA(); assertNotNull(response.get("value")); assertEquals(Integer.valueOf(1), response.get("value")); } .............. @Produces @RequestScoped public IncrementService getIncrementService() { final IncrementService service = mock(IncrementService.class); when(service.getA()).thenReturn(1); when(service.incrementA()).thenReturn(2); when(service.getB()).thenReturn(2); when(service.incrementB()).thenReturn(3); return service; } }
組み込みのウェブサーバーNettyを介してウェブリクエストの処理を設定します。 関数-ハンドラーを作成するために、Java EE、JAX-RSに由来する別の標準を使用します。 JAX-RS標準の実装として、Resteasyライブラリを選択します。 Netty、CDI、およびResteasyを接続するには、
resteasy-netty4-cdiモジュールが使用されます。 JAX-RSは、javax.ws.rs.core.Application下位クラスを使用して構成されます。 通常、要求ハンドラーおよびその他のJAX-RSコンポーネントはそこに登録されます。 CDIとResteasyを使用する場合、JIX-RSに登録されたリクエストハンドラー(JAX-RS:Pathアノテーションでマーク)およびプロバイダーと呼ばれる他のJAX-RSコンポーネント(JAX-RS:プロバイダーアノテーションでマーク)がJAX-RSコンポーネントとして使用されることを示すだけで十分です。 。
ドキュメントからResteasyの詳細を学ぶことができ
ます 。
NettyおよびJAX-RSアプリケーション public static void main(String[] args) { ...............
すべての静的ファイル(javascriptバンドル、css、写真)はクラスパス(src / main / resources / webapp)に配置され、結果のjarファイルに収まります。 このようなファイルにアクセスするには、{fileName :. *}。{Ext}という形式のURLハンドラーが使用され、クラスパスからファイルがロードされ、クライアントに渡されます。
静的リクエストハンドラ @Path("/") @RequestScoped public class StaticFilesResource { private final static Date START_DATE = DateUtils.setMilliseconds(new Date(), 0); @Inject private Configuration configuration; @GET @Path("{fileName:.*}.{ext}") public Response getAsset( @PathParam("fileName") String fileName, @PathParam("ext") String ext, @Context UriInfo uriInfo, @Context Request request) throws Exception { if(StringUtils.contains(fileName, "nomin") || StringUtils.contains(fileName, "server")) {
React HTMLサーバーレンダリング
Javaアプリケーションを構築するときにバンドルを構築するには、maven
frontend-maven-pluginプラグインを使用でき
ます 。 希望するバージョンのNodeJを個別にダウンロードしてローカルに保存し、webpackを使用してバンドルを構築します。 mvnコマンド(またはmavenとの統合をサポートするIDE)でJavaプロジェクトの通常の構築を開始するだけで十分です。 クライアントのjavascript、styles、package.json、webpack構成ファイルをsrc / main / frontendディレクトリに配置し、結果のバンドルをsrc / main / resources / webapp / static / assetに配置します。
fronend-maven-pluginのセットアップ <plugin> <groupId>com.github.eirslett</groupId> <artifactId>frontend-maven-plugin</artifactId> <configuration> <nodeVersion>v${node.version}</nodeVersion> <npmVersion>${npm.version}</npmVersion> <installDirectory>${basedir}/src/main/frontend</installDirectory> <workingDirectory>${basedir}/src/main/frontend</workingDirectory> </configuration> <executions> <execution> <id>nodeInstall</id> <goals> <goal>install-node-and-npm</goal> </goals> </execution> <execution> <id>npmInstall</id> <goals> <goal>npm</goal> </goals> </execution> <execution> <id>webpackBuild</id> <goals> <goal>webpack</goal> </goals> <configuration> <skip>${webpack.skip}</skip> <arguments>${webpack.arguments}</arguments> <srcdir>${basedir}/src/main/frontend/app</srcdir> <outputdir>${basedir}/src/main/resources/webapp/static/assets</outputdir> <triggerfiles> <triggerfile>${basedir}/src/main/frontend/webpack.config.js</triggerfile> <triggerfile>${basedir}/src/main/frontend/package.json</triggerfile> </triggerfiles> </configuration> </execution> </executions> </plugin>
JAX-RSで独自のHTMLページジェネレーターを設定するには、クラスを作成し、javax.ws.rs.ext.MessageBodyWriterインターフェースを実装するプロバイダーアノテーションでハンドラーを作成し、Web要求ハンドラーから応答として返す必要があります。
サーバーのレンダリングは、組み込みのJava JavaScriptエンジンであるNashornを使用して実行されます。 これはシングルスレッドスクリプトエンジンです。複数の同時リクエストを処理するには、エンジンの複数のキャッシュインスタンスを使用する必要があります。リクエストごとに無料のインスタンスが取得され、HTMLがレンダリングされ、プール(
Apache Commons Pool 2 )に返されます。
public class ViewResult { private final String template; private final Map<String, Object> viewData = new HashMap<>(); private final Map<String, Object> reduxInitialState = new HashMap<>(); .............. } @Provider @ApplicationScoped public class ViewResultBodyWriter implements MessageBodyWriter<ViewResult> { .............. private ObjectPool<AbstractScriptEngine> enginePool = null; @PostConstruct public void initialize() {
エンジンはJavascriptバージョンECMAScript 5.1を実行し、モジュールのロードをサポートしていません。そのため、クライアントスクリプトと同様に、サーバースクリプトはwebpackを使用してバンドルされます。 サーバーバンドルとクライアントバンドルは共通のコードベースに基づいていますが、エントリポイントが異なります。 何らかの理由で、Nashornは最小化されたバンドル(--optimize-minimizeキーでアセンブルされたwebpack)を実行できません-エラーでクラッシュするため、サーバー側では非最小化バンドルを実行する必要があります。 両方のタイプのバンドルを構築するには、Webpackプラグイン
unminified-webpack-pluginを同時に使用でき
ます 。
ページの最初のリクエストで、またはエンジンの空きインスタンスがない場合、新しいインスタンスを初期化します。 初期化プロセスでは、Nashornのインスタンスを作成し、その中のクラスパスからロードされたサーバースクリプトを実行します。 Nashornは、setInterval、setTimeoutなどのいくつかの通常のJavaScript関数を実装しないため、最も単純なポリフィルスクリプトを接続する必要があります。 次に、コードが直接ロードされ、HTMLページ(およびクライアント)が形成されます。 このプロセスは非常に高速ではなく、かなり強力なコンピューターでは数秒かかるため、エンジンインスタンスのキャッシュが必要です。
すでに初期化されたエンジンでのHTMLレンダリングははるかに高速です。 Reactによって生成されたHTMLを取得するには、renderHtml関数を記述します。この関数は、サーバーエントリポイント(src \ server.jsx)に配置します。 現在のURLは、react-routerを使用して処理するためにこの関数に渡され、要求されたページのreduxの初期状態(JSONの形式)が渡されます。 JSONの形式のreduxの同じ状態が、変数window.INITIAL_STATEのページに配置されます。 これは、クライアント上でReactによって構築された要素のツリーがサーバー上で生成されたHTMLと一致するために必要です。
サーバーエントリポイントjsバンドル:
renderHtml = function renderHtml(url, initialStateJson) {
クライアントjsバンドルエントリポイント:
ホットリロードHTML /スタイルのサポート
クライアント側の開発の便宜のために、変更されたページまたはスタイルの「ホット」リロードをサポートするwebpack devサーバーを設定できます。 開発者はアプリケーションを起動し、別のポートでwebpack devサーバーを起動し(package.jsonでnpm run debugコマンドを設定するなど)、ほとんどの場合、変更されたページを更新しないようにします-変更はその場で適用され、これはHTMLコードとスタイルコードの両方に適用されます。 これを行うには、ブラウザーで、以前に構成したwebpack devサーバーのアドレスに移動する必要があります。 サーバーはその場でバンドルを構築し、アプリケーションへの他のリクエストをプロキシします。
package.json:
{ "name": "java-react-redux-isomorphic-example", "version": "1.0.0", "private": true, "scripts": { "debug": "cross-env DEBUG=true APP_PORT=8080 PROXY_PORT=8081 webpack-dev-server --hot --colors --inline", "build": "webpack", "build:debug": "webpack -p" } }
「ホット」リブートを設定するには、以下で説明する手順を実行する必要があります。
webpack設定ファイルで:
- devtoolsでmodule-source-mapまたはmodule-eval-source-mapを指定します。 module-source-mapをオンにすると、デバッグ情報がモジュールの本文に含まれます-この場合、ページが完全にリロードされるとブレークポイントが機能しますが、Chromeデバッグツールでページを変更すると、それぞれ独自のバージョンの重複したモジュールが表示されます。 module-eval-source-mapを有効にした場合、重複はありませんが、一般的なページのリロード中のブレークポイントは機能しません。
devtool: isHot
- devServerで、webpackデバッグサーバーを構成します。「ホット」リブートフラグを設定し、サーバーポートを指定し、アプリケーションへのリクエストのプロキシ設定を指定します。
- クライアントスクリプトエントリポイントのエントリで、mediatorモジュールを接続します:react-hot-loader / patch。
entry: {
- publicPath設定の出力で、webpack devサーバーの完全なURLを指定します。
output: {
- babelブートローダー設定で、プラグインを接続して「ホット」リブートをサポートします:syntax-dynamic-importとreact-hot-loader / babel。
{
- スタイルローダーの設定で、スタイルローダーローダーの使用を指定します。 この場合、スタイルはjavascriptコードでインラインになります。 スタイルの「ホット」リロードが無効になっている場合(実稼働環境など)、extract-text-webpack-pluginを使用したスタイルバンドルの形成が使用されます。
{
- Webpack.NamedModulesPluginプラグインを接続して、名前付きモジュールを作成します。
アプリケーションへのクライアントエントリポイントで、モジュール更新ハンドラーを挿入します。 ハンドラーは更新されたモジュールをダウンロードし、Reactを使用してHTMLレンダリングプロセスを開始します。
reduxリポジトリが作成されるモジュールに、モジュール更新ハンドラーを挿入します。 このハンドラーは、更新されたreduxコンバーターをロードし、古いコンバーターをそれらに置き換えます。
const store = createStore(reducers, initialState, applyMiddleware(...middleware)) if (module.hot) {
Javaアプリケーション自体では、frontend-maven-pluginとReactのサーバーサイドレンダリングを使用してバンドルの構築を無効にする必要があります:webpack devサーバーはスクリプトとスタイルバンドルの構築を担当します。バンドル。 frontend-maven-pluginとサーバー側のReactレンダリングを使用して再構築を無効にするには、mavenプロファイル:frontendDevelopment(mavenとの統合をサポートするIDEに含めることができます)を提供できます。 必要に応じて、バンドルはwebpackを使用していつでも手動で再構築されます。