Corredor

ウェブ、プログラミングの勉強メモ。

VSCode + Gradle で構築したプロジェクトで JavaFX を実装する

エディタに VSCode、ビルドツールに Gradle を使用した Java プロジェクトで、JavaFX を使ってみる。

JavaFX とは、AWT や Swing の後発ライブラリで、Java 言語でクロスプラットフォームな GUI アプリを作るためのライブラリだ。

実装するにあたって、いくつか手を入れないとうまく動かなかったので、その点を紹介する。

前提条件

前提条件は以下のとおりとする。

  • JDK は JavaFX が同梱されている JDK8 を使用する
    • 現在の JDK には同梱されていないので、最新版の JDK で構成する場合は、OpenJDK と OpenJFX を入れる必要があるらしい (試していない)
  • $ gradle init --type java-application で雛形を作成しておく
  • VSCode に拡張機能「Java Extension Pack」をインストールしておく

VSCode に拡張機能を入れたあと Gradle プロジェクトを開くと、

  • .project
  • .classpath
  • bin/

などのファイルが自動的に生成される。これらによって VSCode 上の自動補完などが効くようになるようだ。

.classpath を編集して JavaFX を参照できるようにする

現状、VSCode 上でコードを入力しても、JavaFX 関連のクラス名だとうまく自動補完が効かない。そこで、.classpath ファイルを開いて次のように編集すると、自動補完などが効くようになる。

<!-- ↓元はこのような行がある -->
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/" />

<!-- ↓次のように編集する -->
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/">
  <accessrules>
    <accessrule kind="accessible" pattern="javafx/**" />
    <accessrule kind="accessible" pattern="com/sun/javafx/**" />
  </accessrules>
</classpathentry>

このように accessrules 要素でパッケージを参照できるようにしておく。これは外部 JAR ライブラリを導入する場合も必要になることがあるので、覚えておくと良いかと。

<!-- 例として OpenCV の JAR ファイルを読み込む場合 -->
<classpathentry kind="lib" exported="true" path="lib/opencv.jar">
  <accessrules>
    <accessible kind="accessible" pattern="**" />
  </accessrules>
</classpathentry>

コレを自動でなんとかしてくれる方法がないか調べたが、よく分からず。良い管理方法があったら教えてほしい。

build.gradle を編集して JavaFX 込みのビルドができるようにする

.classpath の記述は VSCode 上の自動補完やコンパイルにのみ影響するらしい。gradle build コマンドなどを使う際の依存関係は、build.gradle ファイルを編集しないといけない。

JavaFX を使う上で必要な変更を以下に記す。

plugins {
  // 既存の 'java' と 'application' はそのままに以下を追加
  
  // classpath を出力させて自動補完を効かせる
  id 'eclipse'
}

dependencies {
  // 既存の依存関係はそのままに以下を追加
  
  // JavaFX を追加する
  compile files("${System.properties['java.home']}/lib/ext/jfxrt.jar")
}

// Java ファイルと同じ階層に FXML ファイルを置けるようにする
sourceSets {
  main {
    java {
      srcDir 'src'
    }
    resources {
      srcDir 'src/main/java'
    }
    java.outputDir = file('bin')
    output.resourcesDir = file('bin')
  }
}

ユニットテストが鬱陶しい場合は以下を消してしまえば、./test/ 配下のファイルによる影響をなくせる。

dependencies {
  // Use JUnit test framework
  testImplementation 'junit:junit:4.12'
}

JavaFX を実装してみる

試しに JavaFX を用いたウィンドウを開いてみる。とっても簡素なモノだ。

  • src/main/java/my_app/App.java
    • エントリポイント
package my_app;

import java.io.IOException;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

public final class App extends Application {
  public static void main(final String ...args) {
    launch(args);
  }
  
  @Override
  public void start(final Stage primaryStage) throws IOException {
    final FXMLLoader loader = new FXMLLoader(getClass().getResource("./root.fxml"));
    final Pane root = loader.load();
    final Scene scene = new Scene(root, 600, 400);
    primaryStage.setScene(scene);
    primaryStage.show();
  }
}

FXMLLoader を使い、root.fxml というファイルを読み込んでいる。

  • src/main/java/my_app/root.fxml
    • コンポーネントの UI (View) を定義する XML 風のファイル
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>

<HBox xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1"
      fx:controller="my_app.RootController">
  <Label text="TEST" />
</HBox>

先程 build.gradlesourceSets ブロックを記述したので、.java ファイルと同じ階層に .fxml ファイルが置けるようになった。

fx:controller 属性で対応するクラスを指定している。

  • src/main/java/my_app/RootController.java
    • root.fxml に紐付くコントローラクラス
package my_app;

public final class RootController { }

今回は特に処理を入れていないので、コントローラクラスには実装なし。

  • Initializable を実装すれば、FXMLLoaderload() したタイミングで初期処理が実行できる
  • 画面上の要素に fx:id 属性を付与すれば、コントローラ側でその要素を特定して参照できる
    • <Label fx:id="myLabel" />
    • @FXML private Label myLabel; とアノテーション付きでフィールドを宣言する
    • this.myLabel.setText("HOGE FUGA"); というように要素を操作できる
  • onAction 属性を使えば、ボタンからコントローラのメソッドを呼び出したりできる
    • <Button onAction="#onExec" text="実行する" />
    • @FXML public onExec() {} とアノテーション付きでメソッドを宣言する

どんなレイアウトがあって、どんなコントロール部品があるのか、どうやってプロパティを設定したらいいか、とかいうことは、チマチマ検索していくしかない…。「こういうことをやりたいんだけどどうやるんだろう?」という逆引きは StackOverflow が参考になると思う。

アプリを起動してみる

とりあえずココまで実装すると、

$ ./gradlew run

で Java アプリが起動する。実行割合が 75% 程度で止まったまま、GUI ウィンドウが開くことが確認できると思う。ウィンドウを閉じたり、Ctrl + C でターミナルを終了したりすれば良い。

今日はココまで。

VSCode で Gradle ベースの Java プロジェクトを開発し始める

久々に Java プロジェクトを作ることになったが、モッサリした Eclipse に戻る気になれず、VSCode で開発したいと考えている。

また、古くは Ant でビルドステップを整えたり、それが Maven になって依存関係まで解決してくれたりしていた時代を経て、最近の Java プロジェクトでは Gradle というモノを使って環境を整えるみたい。Node.js の界隈でいうと、npm っぽいパッケージ管理の概念もあり、webpack などのようなビルドツールも内包していてユニットテストツールとの連携もバッチリ、という意味で SPA の CLI ツールっぽくもある、そんな感じ。

というワケで今回は、VSCode + Gradle な Java プロジェクトを立ち上げてみる。対象 OS は MacOS と Windows で、どちらでも同じ結果になるように解説した。

JDK をインストールする

まずは JDK のインストール。以下の Oracle のサイトより、Java 8 系の JDK をダウンロードし、インストールする。

なんで今さら Java 8 系かというと、JavaFX という GUI アプリを構築するためのライブラリが JDK 本体に内包されているのが Java 8 までで、導入を楽にしたいからw。そうした横着したい理由などがなければ、JDK は Homebrew や Chocolatey で最新版を導入しても問題ない。

Oracle のサイトから JDK をダウンロードするには Oracle アカウントが必要になるのがちょっと面倒。

MacOS のみ : 環境変数を ~/.bash_profile で設定する

Windows の場合はインストールさえすれば環境変数も自動設定されると思うが、MacOS だとうまくいかないかと思われるので紹介。次のような記述を ~/.bash_profile に入れておこう。

# Java
export JAVA_HOME="/Library/Java/JavaVirtualMachines/jdk1.8.0_65.jdk/Contents/Home"
export _JAVA_OPTIONS='-Dfile.encoding=UTF-8'
export PATH="${JAVA_HOME}/bin:${PATH}"

インストール後の確認

Java がインストールできたかどうか確認する。

  • MacOS
$ java -version
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)
  • Windows (PowerShell にて確認)
PS> java -version
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)

Gradle をインストールする

続いて Gradle をインストールする。後述するが、実をいうと Gradle は必ずしもインストールが必須ではない。というのも、Gradle で構築したプロジェクトに含まれている gradlew (Gradle-Wrapper) を使えば、Gradle をインストールせずとも動作するからだ。

しかし今回はホントに初回というテイなので、Gradle 本体を純粋にインストールする手順も紹介する。

MacOS でのインストール手順

MacOS で Gradle をインストールするには、Homebrew を使うのが手っ取り早い。

$ brew install gradle

$ gradle -v
Picked up _JAVA_OPTIONS: -Dfile.encoding=UTF-8

------------------------------------------------------------
Gradle 6.3
------------------------------------------------------------

Build time:   2020-03-24 19:52:07 UTC
Revision:     bacd40b727b0130eeac8855ae3f9fd9a0b207c60

Kotlin:       1.3.70
Groovy:       2.5.10
Ant:          Apache Ant(TM) version 1.10.7 compiled on September 1 2019
JVM:          1.8.0_65 (Oracle Corporation 25.65-b01)
OS:           Mac OS X 10.15.4 x86_64

こんな感じ。

Windows でのインストール手順

Windows の場合は Chocolatey でインストールできる。

PS> choco install -y gradle
# 環境変数 PATH の再読み込み
PS> rerefreshenv

PS> gradle -v

------------------------------------------------------------
Gradle 6.3
------------------------------------------------------------

Build time:   2020-03-24 19:52:07 UTC
Revision:     bacd40b727b0130eeac8855ae3f9fd9a0b207c60

Kotlin:       1.3.70
Groovy:       2.5.10
Ant:          Apache Ant(TM) version 1.10.7 compiled on September 1 2019
JVM:          1.8.0_144 (Oracle Corporation 25.144-b01)
OS:           Windows 10 10.0 amd64

こんな感じ。

Gradle プロジェクトを立ち上げる

Gradle をインストールしたら、gradle init コマンドで新規プロジェクトを作成する。--type java-application の指定を忘れないようにする。ビルドスクリプトは Groovy、テストツールは JUnit (それぞれデフォルトのまま) で良いかと。

# 作業ディレクトリを作成し移動する
$ mkdir practice-java-gradle && cd $_

# Gradle プロジェクトを作成する
$ gradle init --type java-application

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit 4) [1..4]

Project name (default: practice-java-gradle):
Source package (default: practice.java.gradle):

> Task :init
Get more help with your project: https://docs.gradle.org/6.3/userguide/tutorial_java_projects.html

BUILD SUCCESSFUL in 15s
2 actionable tasks: 2 executed

# 作成されたファイルを確認する
$ tree
.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── practice
    │   │       └── java
    │   │           └── gradle
    │   │               └── App.java
    │   └── resources
    └── test
        ├── java
        │   └── practice
        │       └── java
        │           └── gradle
        │               └── AppTest.java
        └── resources

15 directories, 8 files

こんな感じ。とても簡単だ。

gradlew (Gradle-Wrapper) の使い方

以降の操作で、

$ gradle run
$ gradle build

gradle コマンドを直接叩いても良いのだが、Gradle がインストールされていない環境向けに gradlew というラッパースクリプトが用意されている。コレを使えば Gradle をインストールすることなく、このプロジェクトを git clone などしてくるだけですぐに動作させられる。

MacOS や Linux 環境では、プロジェクトルートにある gradlew を、Windows コマンドプロンプト環境では gradlew.bat を使えば良い。

# Gradle-Wrapper を使う例
$ ./gradlew run
$ ./gradlew build
> .\gradlew.bat run
> .\gradlew.bat build

このようにすると、Gradle-Wrapper が必要な資材を自動的に用意してくれて、プロジェクトを動作させてくれる。とっても便利だ。正直 Gradle は最初に gradle init を叩く時さえ存在すれば良いので、イチイチインストールしなくても良いかな、とすら思う。w

VSCode 拡張機能を入れる

Gradle プロジェクトを立ち上げたので、あとは VSCode 上で開発できるようにする。

といってもやることは簡単で、以下の拡張機能をインストールすればほぼ終わり。

  • Java Extension Pack : Microsoft 謹製
    • 「Language Support for Java(TM) by Red Hat」が内包されている

あとは Gradle プロジェクトを開いて開発が進めていけるはずだ。インポートの自動補完もされるし、コンパイルエラーになるようなコードにはちゃんとエラーがつくはず。

動作確認やユニットテストは gradlew を使ってやろう。

# アプリを動作させる
$ ./gradlew run

# テストする
$ ./gradlew test

# ビルドする
$ ./gradlew build

適宜 build/bin/ ディレクトリを .gitignore で無視しておくと良いかと。

以上

まだ build.gradle など、Gradle 特有の部分にほとんど触れてはいないのだが、それでもかなり簡単に Java プロジェクトが構築できたことが分かるだろう。

VSCode による Java 開発も、動作が快適で大満足。gradle run によるコンパイルと起動がかなり速いので、Eclipse よりもイライラせずに済むなーと思っている。

MacOS と Windows で ScreenFetch と NeoFetch を試してみる

OS 情報をイイカンジに表示してくれる screenfetchneofetch という CLI ツールがある。自分の PC の情報なんて自分が一番知ってるから別に要らないんだけど、何となくテンションが上がるので入れてみる。

Linux 系の場合はサクッと入ると分かっているので、今回は MacOS と Windows とでインストール方法を確認してみる。

MacOS で screenfetch と neofetch を使う

…といっても MacOS の場合はシンプルで、Homebrew から2つともインストールできる。

$ brew install screenfetch neofetch

$ screenfetch
$ neofetch

f:id:neos21:20200429002413p:plain

簡単。

Windows で screenfetch を使う

Windows の場合はそれぞれのパッケージをインストールする手順が違う。screenfetch は Windows 版があり、ワンライナーでインストールできた。

PS> Install-Module -Name windows-screenfetch

PS> screenfetch

手元で試したら、なんかゼロ除算エラーが出たけど気にしない。

f:id:neos21:20200429002131p:plain

Windows で neofetch を使う

neofetch が面倒だった。

scoop という、Chocolatey に似たパッケージマネージャを使うとインストールできるのだが、scoop でも Git をインストールしてやらないと、依存関係が解決できなかった。

# scoop というパッケージマネージャをインストールする
PS> Set-ExecutionPolicy RemoteSigned -scope CurrentUser
PS> iex (new-object net.webclient).downloadstring('https://get.scoop.sh')

# 依存する git パッケージと一緒にグローバルオプションを付与してインストールする
PS> scoop install --global neofetch git

PS> neofetch

f:id:neos21:20200429002126p:plain

オマケ:winfetch も試してみる

Windows 向けには winfetch というモノもあるみたいなので試してみた。コレも Scoop を使うので、Scoop 自体はインストールしておくこと。

PS> scoop bucket add extras
PS> scoop install winfetch

PS> winfetch

f:id:neos21:20200429002859p:plain

neofetch に近いかなー。

以上

やはり後発の neofetch の方が見た目はリッチかな?

Nyan Cat

Nyan Cat

  • 発売日: 2019/09/04
  • メディア: MP3 ダウンロード

Nyan Cat

Nyan Cat

  • 発売日: 2015/11/24
  • メディア: MP3 ダウンロード

Create React App + TypeScript + SCSS 環境を構築してみる

2016年に create-react-app 単体で試したことがあった。

neos21.hatenablog.com

あれから3年半が経過。React がもてはやされていた中で「Angular はちょっと重たいよねー」「とはいえ React も難解になってきたよねー」とか言っていると Vue.js の手軽さで SPA 全体が完全に主流になったと思う。Next.js や Nuxt.js、Angular CLI などの発展で、統一感のある CLI に沿った開発環境というのも安定してきたと思う。

そして何より、TypeScript を使った開発というのが、もはや「モダン」でもなんでもない「スタンダード」にすらなったかなと思う。

以前の記事では TypeScript などを試さなかったので、今回は最新の Create React App をベースに、TypeScript + SCSS を使った環境を作ってみる。そこで遭遇したトラブルも記録するのでご参考までに。

Create React App でプロジェクトの雛形を作る

npm の npx コマンドを使えば、create-react-app のグローバルインストールは不要。もちろん、事前に $ npm i -g create-react-app としても問題はない。

# 以下のコマンドで雛形を作成する
$ npx create-react-app example-my-app --use-npm --typescript

Yarn が別に好きではないので、個人的な趣味で npm を使う --use-npm オプションを設定。

TypeScript ベースのプロジェクトにするため --typescript オプションを付けたが、ワーニングで「今後は --template typescript オプションを使え」と出ていた。今後は

$ npx create-react-app example-my-app --use-npm --template typescript

が雛形作成コマンドになるな。

$ cd ./example-my-app/
$ npm start

プロジェクトを作成したら、上のようにすることで http://localhost:3000/ に簡易サーバが起動する。

SCSS を書けるようにする

上述のように TypeScript ベースのプロジェクトを作っても、CSS 周りは標準的な CSS しかサポートされていない。

SCSS を書けるようにするには、node-sass をインストールしておくだけで良い。とてもお手軽!

$ npm install --save node-sass

node-sass をインストールするだけ。

あとは index.cssApp.css の拡張子を .scss にリネームし、import 部分も .css から .scss に直せば、自動的に SCSS のコンパイルを行って動作してくれるようになる。

Linux 環境で npm start 実行時に「System limit for number of file watchers reached」エラーが出た

Ubuntu 18.04 環境で React プロジェクトを立ち上げたところ、npm start 時に

Error: ENOSPC: System limit for number of file watchers reached,

このようなエラーメッセージが出た。

以下のとおり対応すれば良い。

# inotify が小さいと思われる
$ cat /proc/sys/fs/inotify/max_user_watches
8192

# 上限値を増やす
$ echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
$ sudo sysctl -p

コレで npm start してもエラーが出なくなる。

package.jsonhomepage プロパティを指定すると npm start が正常に動作しなくなる

npm initpackage.json を生成すると、homepage プロパティが記録される。コレに合わせて、Create React App が作った package.json にも homepage プロパティを書いた。すると npm start 時に URL の階層構造が狂ってしまった。

どうやら React は package.jsonhomepage プロパティを見てパス解決を調整しているようなので、通常の npm パッケージの感覚でこのプロパティを書かないようにしないといけない。最終的にアプリが動作する URL、例えば GitHub Pages の URL などを書いておくのが適切だろう。

ビルドしたモノを動かす

npm start による簡易サーバ・ホットリロードではなく、一度ちゃんとビルドして、それを動作させてみる場合。serve というパッケージを使うのが公式で紹介されていた。

$ npm run build

$ npx serve -s ./build/
# http://localhost:5000/

npm start 時と npm run build 時とで homepage を変えたい

前述のとおり、package.jsonhomepage プロパティは、npm run build でビルドした資材を公開する場所に合わせておくことが望ましい。

しかしそれだと、npm start で開発したい時にこの URL が適用されてしまい、パス解決に不具合が出る場合がある。

その場合は、次のように PUBLIC_URL 環境変数を指定して npm start を実行すれば良い。

{
  "scripts": {
    "start": "PUBLIC_URL='.' react-scripts start"
  }
}

Service Worker を使わない場合は完全削除して良い

標準だと、index.tsx より serviceWorker.tsx がインポートされているが、いきなり有効化されないように、unregister() としかしていない。

// src/index.tsx 抜粋

import * as serviceWorker from './serviceWorker';  
serviceWorker.unregister();

Service Worker を使わないことが確定している場合は、これらの記述をすべて削除し、serviceWorker.tsx ごと削除しても問題なかった。

Component クラスの関数はアロー関数で書くとよさげ

イマイチ分かっていないのだが、Component クラスに独自に定義する関数について。

export default class App extends React.Component {
  // State の型を宣言しておく
  state: Readonly<any>;
  
  // 画面から呼び出す予定の関数を定義
  onSubmit() {
    this.setState({ hoge: 'FUGA' });  // ← コレができない
  }
}

上のように、関数を onSubmit() {} という風に定義し、this.setState() を呼び出して値を変更しようとすると、なぜか undefined となってしまう。

this が解決できていないんだろうなーとは思ったが、何でコレがダメなんだっけ?というのが分かっていない。

とりあず回避するには、

  • onSubmit() {} のようにクラスのメソッドとして書くのではなく、
  • onSubmit = () => {} のようにアロー関数で書いてやると

エラーを回避できる。

export default class App extends React.Component {
  // State の型を宣言しておく
  state: Readonly<any>;
  
  // 画面から呼び出す予定の関数を定義
  onSubmit = () => {
    this.setState({ hoge: 'FUGA' });  // ← コレなら OK
  }
}

条件付きレンダ

Component の render() メソッドは、最終的に JSX を return すれば良いので、return キーワードを書くまでに if 文を組み合わせてみたりしても良い。状態に応じて大きく HTML 構造が変わる場合は、if 文で処理して複数の return を書いたりした方がスッキリするかもしれない。

export default class App extends React.Component {
  render() {
    // ローディング中の HTML
    if(this.state.isLoading) {
      return (
        <div>Loading...</div>
      );
    }
    
    // ロード完了後の HTML…
    return (
      <div></div>
    );
  }
}

一方、JSX の中で条件分岐を行いたい場合は、「条件付きレンダ」で書けば良い。

return (
  <div>
    { messages.length === 0 &&
      <p>表示できるメッセージはありません</p>
    }
  </div>
);

ブレース {} 内は JavaScript の構文を直接書けるので、JavaScript のイディオムである &&|| の他、三項演算子を組み合わせたりもできる。覚えておこう。

kuromoji を使う

nemui-gacha-js を作った時のメモ。kuromoji という形態素解析ツールを React 内で使うためにやったこと。

neos21.github.io

まずはインストール。@types での型定義もあるので一緒にインストールしておくと良い。

$ npm install -S kuromoji
$ npm install -D @types/kuromoji

同梱の dict/ ディレクトリを、./public/dict/ でアクセスできるように移動しておく。

$ cp -a ./node_modules/kuromoji/dict/ ./public/

そうすれば、次のように書くことで kuromoji が使えるようになる。

import * as kuromoji from 'kuromoji';

kuromoji.builder({
  dicPath: './dict'  // 予め kuromoji の dict/ ディレクトリを public/ 配下にコピーしておく
}).build((error: Error, tokenizer: kuromoji.Tokenizer<any>) => {
  // tokenizer を使って処理…
});

ts-node のコンパイラオプションを上書きする

React ベースのプロジェクト内で、ちょっとだけ ts-node を使って CLI 向けスクリプトを動かしたい場面があった。そんな時、React 向けの tsconfig.json だと ts-node が動作しなかった。

調べてみると、次のように環境変数 TS_NODE_COMPILER_OPTIONStsconfig.json の設定を上書きしてやれば動作した。

$ TS_NODE_COMPILER_OPTIONS='{"module":"commonjs"}' ts-node HOGE.ts

package.json に定義するならこんな感じ。

{
  "scripts": {
    "exec-cli": "TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' ts-node HOGE.ts"
  }
}

以上

こんな感じ~。TypeScript と SCSS が使えるようにできたので、Angular CLI や Vue CLI と同じく、React も Create-React-App で良い感じですね~。

React.js&Next.js超入門

React.js&Next.js超入門

速習 React 速習シリーズ

速習 React 速習シリーズ

  • 作者:山田祥寛
  • 発売日: 2018/08/27
  • メディア: Kindle版