ReactとNashornを使用した同形アプリケーション

  • link:/category/programming/ [プログラミング]

1. 概要

このチュートリアルでは、同型アプリとは何かを正確に理解します。 また、JavaにバンドルされているJavaScriptエンジンであるhttps://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html[Nashorn]についても説明します。
さらに、Nashornとhttps://reactjs.org/[React]のようなフロントエンドライブラリを使用して同形アプリを作成する方法を検討します。

2. 少し歴史

従来、クライアントおよびサーバーアプリケーションは、サーバー側で非常に重い方法で記述されていました。 PHPは、主に静的なHTMLおよびWebブラウザーで生成されるスクリプトエンジンと考えてください。
Netscapeには、90年代半ばにブラウザの方法でJavaScriptがサポートされていました*。 それにより、処理の一部がサーバー側からクライアント側のブラウザーに移行し始めました。 長い間、開発者はWebブラウザーでのJavaScriptサポートに関するさまざまな問題に苦労していました。
より高速でインタラクティブなユーザーエクスペリエンスに対する需要の高まりに伴い、境界線は既により厳しくなりつつありました。 *ゲームを変えた最も初期のフレームワークの1つはhttps://jquery.com/[jQuery]でした。*それはいくつかのユーザーフレンドリーな機能とAJAXの非常に強化されたサポートをもたらしました。
すぐに、フロントエンド開発用の多くのフレームワークが登場し始め、開発者のエクスペリエンスが大幅に向上しました。 Googleのhttps://angular.io/[AngularJS]、FacebookのReact、および以降のhttps://vuejs.org/[Vue]から、開発者の注目を集め始めました。
最新のブラウザサポート、優れたフレームワーク、および必要なツールにより、*潮流は主にクライアント側*に移行しています。
より高速なハンドヘルドデバイスでの没入型エクスペリエンスには、より多くのクライアント側の処理が必要です。

3. 同形アプリとは何ですか?

そのため、フロントエンドフレームワークが、ユーザーインターフェースがクライアント側で完全にレンダリングされるWebアプリケーションの開発にどのように役立つかを見ました。
ただし、*サーバー側で同じフレームワークを使用して*、同じユーザーインターフェイスを生成することも可能です。
これで、必ずしもクライアント側のみまたはサーバー側のみのソリューションに固執する必要がなくなりました。 より良い方法は、クライアントとサーバーが同じフロントエンドコードを処理し、同じユーザーインターフェイスを生成できるソリューションを用意することです。
このアプローチには利点がありますが、これについては後で説明します。
link:/uploads/Isomorphic-Apps-100x32.jpg%20100w []
 
*このようなWebアプリケーションはIsomorphicまたはUniversal *と呼ばれます。 現在、クライアント側の言語はJavaScriptのみです。 したがって、同型アプリが機能するには、サーバー側でもJavaScriptを使用する必要があります。
Node.jsは、サーバー側のレンダリングされたアプリケーションを構築する最も一般的な選択肢です。

4. Nashornとは何ですか?

それで、Nashornはどこに収まり、なぜそれを使用する必要があるのでしょうか? Nashornは*デフォルトでJavaにパッケージ化されたJavaScriptエンジンです*。 したがって、すでにJavaのWebアプリケーションバックエンドがあり、同形アプリを作成する場合は、https://www.baeldung.com/java-nashorn [Nashornは非常に便利です]!
NashornはJava 8の一部としてリリースされました。 これは主に、Javaに埋め込まれたJavaScriptアプリケーションを許可することに焦点を当てています。
Nashornは、メモリ内のJavaScriptをJavaバイトコードにコンパイルし、実行のためにJVMに渡します。 これにより、以前のエンジンであるRhinoと比較してパフォーマンスが向上します。

[[let’s_create_an_isomorphic_app]]
=== 5. 同形アプリの作成

十分なコンテキストを確認しました。 ここのアプリケーションは、フィボナッチ数列を表示し、数列を生成して表示するためのボタンを提供します。 バックエンドとフロントエンドを使用して、単純な同形アプリを作成しましょう。
  • フロントエンド:React.jsベースのシンプルなフロントエンド

  • バックエンド:処理するNashornを備えたシンプルなSpring Bootバックエンド
    JavaScript

6. アプリケーションのフロントエンド

フロントエンドの作成には* React.jsを使用します*。 Reactは、単一ページのアプリを構築するための一般的なJavaScriptライブラリです。 オプションの状態と一方向のデータバインディングを使用して、複雑なユーザーインターフェイスを階層コンポーネントに分解するのに役立ちます。
Reactはこの階層を解析し、仮想DOMと呼ばれるメモリ内データ構造を作成します。 これは、Reactが異なる状態間の変更を検出し、ブラウザーのDOMに最小限の変更を加えるのに役立ちます。

6.1. 反応コンポーネント

最初のReactコンポーネントを作成しましょう:
var App = React.createClass({displayName: "App",
    handleSubmit: function() {
        var last = this.state.data[this.state.data.length-1];
        var secondLast = this.state.data[this.state.data.length-2];
        $.ajax({
            url: '/next/'+last+'/'+secondLast,
            dataType: 'text',
            success: function(msg) {
                var series = this.state.data;
                series.push(msg);
                this.setState({data: series});
            }.bind(this),
            error: function(xhr, status, err) {
                console.error('/next', status, err.toString());
            }.bind(this)
        });
    },
    componentDidMount: function() {
        this.setState({data: this.props.data});
    },
    getInitialState: function() {
        return {data: []};
    },
    render: function() {
        return (
            React.createElement("div", {className: "app"},
                React.createElement("h2", null, "Fibonacci Generator"),
                React.createElement("h2", null, this.state.data.toString()),
                React.createElement("input", {type: "submit", value: "Next", onClick: this.handleSubmit})
            )
        );
    }
});
さて、上記のコードが何をしているのかを理解しましょう:
  • まず、「App」というReactのクラスコンポーネントを定義しました

  • このコンポーネント内で最も重要な機能は「レンダリング」*です。
    ユーザーインターフェイスの生成を担当します

  • コンポーネントが使用できる_className_スタイルを提供しました

  • ここでは、コンポーネントの状態を使用して、
    シリーズ

  • 状態は空のリストとして初期化されますが、渡されたデータをフェッチします
    コンポーネントがマウントされるときの支柱としてコンポーネントに

  • 最後に、ボタン「追加」をクリックすると、RESTへのjQuery呼び出しが行われます
    サービスが行われます

  • 呼び出しは、シーケンス内の次の番号を取得し、それを
    コンポーネントの状態

  • コンポーネントの状態を変更すると、コンポーネントが自動的に再レン​​ダリングされます

6.2. Reactコンポーネントの使用

Reactは、そのコンテンツを固定するために、HTMLページで*「div」という名前の要素を探します*。 行う必要があるのは、この「div」要素を持つHTMLページを提供し、JSファイルをロードすることだけです。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello React</title>
    <script type="text/javascript" src="js/react.js"></script>
    <script type="text/javascript" src="js/react-dom.js"></script>
    <script type="text/javascript" src="https://code.jquery.com/jquery-1.10.0.min.js"></script>
</head>
<body>
<div id="root"></div>
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript">
    ReactDOM.render(
        React.createElement(App, {data: [0,1,1]}),
        document.getElementById("root")
    );
</script>
</body>
</html>
それでは、ここで行ったことを見てみましょう。
  • 必要なJSライブラリ、react、react-dom、jQueryをインポートしました

  • その後、「root」という「div」要素を定義しました

  • ReactコンポーネントとともにJSファイルもインポートしました

  • 次に、Reactコンポーネントを「App」と呼び、いくつかのシードデータ、
    最初の3つのフィボナッチ数

7. アプリケーションのバックエンド

次に、アプリケーションに適したバックエンドを作成する方法を見てみましょう。 このアプリケーションを構築するために、Spring Web *と共にlink:/spring-boot[Spring Boot]を使用することに既に決めています。 さらに重要なことは、前のセクションで開発したJavaScriptベースのフロントエンドを処理するために* Nashornを使用することにしました。

7.1. Mavenの依存関係

単純なアプリケーションでは、Spring MVCとともにJSPを使用するため、POMにいくつかの依存関係を追加します。
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-jasper</artifactId>
    <scope>provided</scope>
</dependency>
1つ目は、Webアプリケーションのhttps://search.maven.org/search?q=g:org.springframework.boot%20AND%20a:spring-boot-starter-web [標準のスプリングブート依存関係]です。 2番目はhttps://search.maven.org/search?q=g:org.apache.tomcat.embed%20AND%20a:tomcat-embed-jasper[JSPのコンパイルに必要]です。

7.2. Webコントローラー

JavaScriptファイルを処理し、JSPを使用してHTMLを返すWebコントローラーを作成しましょう。
@Controller
public class MyWebController {
    @RequestMapping("/")
    public String index(Map<String, Object> model) throws Exception {
        ScriptEngine nashorn = new ScriptEngineManager().getEngineByName("nashorn");
        nashorn.eval(new FileReader("static/js/react.js"));
        nashorn.eval(new FileReader("static/js/react-dom-server.js"));
        nashorn.eval(new FileReader("static/app.js"));
        Object html = nashorn.eval(
          "ReactDOMServer.renderToString(" +
            "React.createElement(App, {data: [0,1,1]})" +
          ");");
        model.put("content", String.valueOf(html));
        return "index";
    }
}
それで、ここで正確に何が起こっているのか:
  • Nashorn型の_ScriptEngine_のインスタンスを取得します
    _ ScriptEngineManager _

  • 次に、関連するライブラリをReact、react.js、および
    react-dom-server.js *

  • また、reactコンポーネント「App」を含むJSファイルをロードします

  • 最後に、JSフラグメントを評価して、react要素を作成します。
    コンポーネント「App」といくつかのシードデータ

  • これにより、Reactの出力、_Object_としてのHTMLフラグメントが提供されます。

  • このHTMLフラグメントをデータとして関連ビュー(JSP)に渡します

7.3. JSP

さて、JSPでこのHTMLフラグメントをどのように処理しますか?
Reactは名前付きの「div」要素(この場合は「root」)に出力を自動的に追加することを思い出してください。 ただし、* JSPでサーバー側で生成されたHTMLフラグメントを同じ要素に手動で追加します*。
JSPがどのように見えるか見てみましょう。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello React!</title>
    <script type="text/javascript" src="js/react.js"></script>
    <script type="text/javascript" src="js/react-dom.js"></script>
    <script type="text/javascript" src="https://code.jquery.com/jquery-1.10.0.min.js"></script>
</head>
<body>
<div id="root">${content}</div>
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript">
    ReactDOM.render(
        React.createElement(App, {data: [0,1,1]}),
        document.getElementById("root")
    );
</script>
</body>
</html>
これは、以前に空だった「ルート」divにHTMLフラグメントを追加したという事実を除いて、先ほど作成したページと同じです。

7.4. RESTコントローラー

最後に、シーケンス内の次のフィボナッチ数を提供するサーバー側のRESTエンドポイントも必要です。
@RestController
public class MyRestController {
    @RequestMapping("/next/{last}/{secondLast}")
    public int index(
      @PathVariable("last") int last,
      @PathVariable("secondLast") int secondLast) throws Exception {
        return last + secondLast;
    }
}
ここには何も派手なものはなく、単純なSpring RESTコントローラーです。

8. アプリケーションを実行する

これで、フロントエンドとバックエンドが完成したので、アプリケーションを実行します。
ブートストラップクラスを使用して、Spring Bootアプリケーションを通常どおり起動する必要があります。
@SpringBootApplication
public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}
このクラスを実行すると、* Spring BootはJSPをコンパイルし、Webアプリケーションの他の部分とともに埋め込みTomcat *で使用できるようにします。
今、私たちのサイトにアクセスすると、以下が表示されます。
http://inprogress.baeldung.com/uploads/Index-Page.jpg []
イベントのシーケンスを理解しましょう:
  • ブラウザはこのページをリクエストします

  • このページのリクエストが到着すると、Spring Webコントローラープロセス
    JSファイル

  • NashornエンジンはHTMLフラグメントを生成し、これをJSPに渡します

  • JSPはこのHTMLフラグメントを「ルート」div要素に追加し、最後に
    上記のHTMLページを返す

  • ブラウザはHTMLをレンダリングし、その間JSファイルのダウンロードを開始します

  • 最後に、ページはクライアント側のアクションの準備ができています-さらに追加できます
    シリーズの数字

    ここで理解する重要なことは、Reactがターゲットの「div」要素でHTMLフラグメントを検出した場合に何が起こるかです。 このような場合、* Reactはこのフラグメントを持っているものと比較し、読みやすいフラグメントが見つかった場合は置き換えません*。 これは、サーバーサイドレンダリングおよび同形アプリを強化するものです。

9. これ以上何が可能ですか?

簡単な例では、可能なことの表面をほんの少しスクラッチしました。 最新のJSベースのフレームワークを備えたフロントエンドアプリケーションは、ますます強力で複雑になっています。 この複雑さが増すと、次の点に注意する必要があります。
  • アプリケーションのReactコンポーネントを1つだけ作成しました
    現実には、これは、*小道具を介してデータを渡す*階層を形成する*いくつかのコンポーネントであり得る

  • *コンポーネントごとに個別のJSファイルを作成*して、
    「exports / require」または「export / import」を使用して、管理しやすく、依存関係を管理します

  • さらに、コンポーネント内の状態を管理することができない場合があります
    のみ; Redux*のような状態管理ライブラリを使用したい場合があります。

  • さらに、外部サービスと対話する必要がある場合があります
    アクションの副作用;これには、https://github.com/reduxjs/redux-thunk [redux-thunk]またはhttps://redux-saga.js.org/[Redux-Saga]*のようなパターンを使用する必要がある場合があります。

  • 最も重要なのは、構文拡張機能であるJSXを活用することです。
    ユーザーインターフェースを説明するJS *

    Nashornは純粋なJSと完全に互換性がありますが、上記のすべての機能をサポートしているわけではありません。 これらの多くは、JS互換性のためにトランスコンパイルとポリフィルを必要とします。
    このような場合の通常のプラクティスは、https://webpack.js.org/ [Webpack]やhttps://rollupjs.org/guide/en/[Rollup]*のようなモジュールバンドラーを活用することです。 彼らが主に行うことは、すべてのReactソースファイルを処理し、それらをすべての依存関係とともに単一のJSファイルにバンドルすることです。 これには、下位互換性のためにJavaScriptをコンパイルするために、常にhttps://babeljs.io/[Babel]のような最新のJavaScriptコンパイラが必要です。
    最終的なバンドルには、ブラウザが理解できる古き良きJSのみがあり、Nashornも同様です。

10. 同形アプリの利点

そのため、同形アプリについて多くのことを話し、今では簡単なアプリケーションを作成しました。 しかし、なぜこれを気にする必要があるのでしょうか? 同形アプリを使用する主な利点のいくつかを理解しましょう。

10.1。 最初のページのレンダリング

同形アプリの最も重要な利点の1つは、*最初のページの高速レンダリング*です。 一般的なクライアント側のレンダリングされたアプリケーションでは、ブラウザーはすべてのJSおよびCSSアーティファクトをダウンロードすることから始まります。
その後、ロードして最初のページのレンダリングを開始します。 サーバー側からレンダリングされた最初のページを送信すると、はるかに高速になり、ユーザーエクスペリエンスが向上します。

10.2。 SEOフレンドリー

サーバー側レンダリングでよく引用されるもう1つの利点は、SEO *に関連しています。 検索ボットはJavaScriptを処理できないため、クライアント側でReactなどのライブラリを介してレンダリングされたインデックスページが表示されないと考えられています。 したがって、サーバー側でレンダリングされたページはSEOにより適しています。 ただし、現代の検索エンジンボットがJavaScriptを処理すると主張していることは注目に値します。

11. 結論

このチュートリアルでは、同型アプリケーションとNashorn JavaScriptエンジンの基本概念を説明しました。 Spring Boot、React、Nashornを使用して同形アプリを作成する方法をさらに検討しました。
次に、フロントエンドアプリケーションを拡張する他の可能性と、同形アプリを使用する利点について説明しました。
いつものように、コードはhttps://github.com/eugenp/tutorials/tree/master/spring-boot-nashorn[GitHubで]にあります。