React.js+WebAssembly+webpackでbuildする(地獄の環境構築から)

## はじめに
web関係初めて1ヶ月未満の超初心者です　しかも高校生です

ここら辺難しすぎて発狂しそう

### 要求
React環境下でWebAssemblyを使いたい！！！！！！！！！！！！！！！！！！！

### 要件
Reactを入れる
Webpackを導入して各種ファイルを読み込めるようにする
Emscriptenを使いC++をビルドしてjs側が読み込める形にする
jsからC++コードをビルドしたものを読み込む

### Reactとは
UIを作るためのフロントエンドライブラリ
Facebook社が開発したらしい
12月ぐらいからノリで触っている
まだまだ初心者

### Webpackとは
複数のモジュールをひとつにまとめるツールらしい
難しい　使ってる最中吐きそうになってた　えーん

### WebAssemblyとは
高水準の言語をバイナリー形式に変換してウェブ上から動かすことができるもの
インタプリンタのJavaScriptよりも高速に動作させるためにあるもの

# 第一章 ReactとWebpackの導入

## Reactを入れる

```
npx create-react-app webpack_test
cd webpack_test/
```

以下のファイルが生成されている

```:生成ファイル一覧
README.md		package-lock.json	public
node_modules	package.json		src
```

必要なものがあれば追加する

```
yarn add react-router-dom
yarn add react-select
```

## Webpackを入れる
```
yarn add --dev webpack webpack-cli webpack-dev-server
```

ちゃんと増えました

```package.json
  "devDependencies": {
    "webpack": "^5.66.0",
    "webpack-cli": "^4.9.1"
  }
```
yarn.lockが増えた

```:生成ファイル一覧 
README.md		node_modules		package.json		src
build			package-lock.json	public			yarn.lock
```

## webpack.config.jsを生成する

```
yarn add --dev @webpack-cli/generators 
npx webpack init
```

`yarn add --dev @webpack-cli/generators`を忘れていても`npx webpack init`実行時にインストールするかどうかを聞かれます

対話内容と日本語訳

```
Which of the following JS solutions do you want to use?
次のJSソリューションのうち、どれを使用したいですか？

Do you want to use webpack-dev-server?
webpack-dev-serverを使用しますか？

Do you want to simplify the creation of HTML files for your bundle?
バンドル用のHTMLファイルの作成を簡素化したいですか？

Do you want to add PWA support?
PWA対応にするか？
(PWAはモバイル向けWebサイトをスマートフォンアプリのように使えるようにするための仕組みのこと、、、らしい)

Which of the following CSS solutions do you want to use?
以下のCSSソリューションのうち、どれを使用したいですか？

Will you be using PostCSS in your project?
あなたのプロジェクトではPostCSSを使うのですか？

Do you want to extract CSS for every file?
ファイルごとにCSSを抽出しますか？

Do you like to install prettier to format generated configuration?
フォーマットするためにprettierを使いますか？(生成されたコンフィギュレーションをフォーマットするためにprettierをインストールするのが好きですか？)

Pick a package manager
パッケージマネージャを選ぶ

以降ファイルを上書きするかどうか聞かれます
```


### 補足
devDependencies(`yarn add --div` したときの行先)
パッケージとして公開したとき、利用側でインストールされない　
サイトの開発に関わるもの

dependencies 
パッケージとして公開したときに利用側でインストールされる
サイトの動作に関わるもの

詳しくはこの記事が参考になります

https://qiita.com/chihiro/items/ca1529f9b3d016af53ec

## babelを入れる

babel関連を入れる

```
yarn add --dev @babel/core @babel/runtime @babel/plugin-transform-runtime @babel/preset-env babel-loader
```

ちゃんと増えました

```package.json
  "devDependencies": {
    "@babel/core": "^7.16.10",
    "@babel/plugin-transform-runtime": "^7.16.10",
    "@babel/preset-env": "^7.16.11",
    "@babel/runtime": "^7.16.7",
    "babel-loader": "^8.2.3",
    "webpack": "^5.66.0",
    "webpack-cli": "^4.9.1"
  }
```

増えました

```
README.md		node_modules		public			yarn.lock
build			package-lock.json	src
package.json		webpack.config.js
```
作業ディレクトリ直下にindex.htmlが生成されても使わないので消してもらって大丈夫です

.babelrcを作ります

```
touch .babelrc
```

書きます

```json:.babelrc
{
    "presets": ["@babel/preset-env", "@babel/preset-react"]
}
```

babelについてはこの記事が参考になります

https://qiita.com/koedamon/items/92c986456e4b9e845acd

## 各種ローダーを設定する
最低限 js,css,各種画像ファイルの読み込みの設定はしておきましょう
`npx webpack init`時の対話時の設定項目によっては既に生成されてるかと思います

```js:webpack.config.js(configの中)
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/i,
        loader: "babel-loader",
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
        type: "asset",
      },
    ],
  },
```

## ビルド時にhtmlファイルを生成できるようにする
```
yarn add --dev html-webpack-plugin
```

追加記入

```js:webpack.config.js(configの外)
const HtmlWebpackPlugin = require('html-webpack-plugin');
```

追加記入

```js:webpack.config.js(config-pluginsの中)
    new HtmlWebpackPlugin({
      template: './public/index.html',
      filename: 'index.html',
    }),
```
templateの指定先のファイルは、Reactのindex.htmlファイルにしてください

## ReactでRouterを使うときには
const config = {}の中に書きます

```js:webpack.config.js(configの中)
  devServer: {
    historyApiFallback: true, //これで複数ページ対応
  },
```

## webpack.config.js - 最終的にこんな感じになるはずです 

```js:webpack.config.js
// Generated using webpack-cli https://github.com/webpack/webpack-cli

const path_require = require("path");
const HtmlWebpackPlugin = require('html-webpack-plugin');
//~~~なんか色々~~~

const isProduction = process.env.NODE_ENV == "production";

const config = {
  //~~~なんか色々~~~
  devServer: {
    historyApiFallback: true, //これで複数ページ

    open: true,
    host: "localhost",//対話時の設定によればこの2つが追加されているかもしれません
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
      filename: 'main.html',
    }),
    //~~~なんか色々~~~
  ],

  rules: [
    {
      test: /\.(js|jsx)$/i,
      loader: "babel-loader",
    },
    {
      test: /\.css$/i,
      use: ["style-loader", "css-loader"],
    },
    {
      test: /\.(eot|svg|ttf|woff|woff2|png|jpg|gif)$/i,
      type: "asset",
    },
  ],
  //~~~なんか色々~~~
}
//~~~なんか色々~~~
```

## 動かしてみる

:::note warn
reactの標準ページでテストする場合は
src/App.js　の先頭に `import React from 'react';`を追加する必要があります
:::

```js:App.js
import logo from './logo.svg';
import './App.css';
import React from 'react';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;
```

起動してみましょう

```
yarn webpack-dev-server --open
```

src以下のファイルを先述のimport Reactの追加以外操作していなければ、よくみるこの画面が出てくるかと思います
![Reactぐるぐる](https://pbs.twimg.com/media/FJmZkDfakAE97ZB?format=jpg&name=medium)

ビルドもしてみましょう

```
yarn webpack build
```
/distディレクトリ以下にファイルが生成されるはずです

```
index.html
main.js (new HtmlWebpackPlugin - filenameで設定した名前で出力されます)
node_modules_web-vitals_dist_web-vitals_js.main.js
```

# 第二章 cppのビルドをその読み込み

## 準備
webpack等を一切使わないでのEmscriptenでのビルド方法はこの記事を参照してください

https://qiita.com/Shio_3001/items/56938511a0cdfccd8e80

また今回は記事にあるC++コードを利用します

ビルドします。設定項目が多いためmakefileに記入するのがおすすめです

```makefile
emcc --bind forjs.cpp --no-entry -s SINGLE_FILE=1 -s ENVIRONMENT="web" -s WASM=1 -s EXPORT_NAME='createModule' -o wasm_forweb.mjs -std=c++14 -s USE_ES6_IMPORT_META=0
```
:::note warn
出力拡張子はwasmではなくmjsです
:::

`WASM=1`
出力をwasmにする

`--bind`
EMSCRIPTEN_BINDINGS によるjsとC++の対応付けのため

`--no-entry`
main関数がないことを示します

`ENVIRONMENT="web"`
web環境で実行するためシェル環境を無効にする

`SINGLE_FILE=1`
wasmファイルをmjsファイルにする

`EXPORT_NAME='createModule'`
モジュールの名前がModuleだとややこしいから変更

`USE_ES6_IMPORT_META=0`
あんまり理解していないがpolyfillが関わってくるらしい

`-o wasm_forweb.mjs`
wasm_forweb.mjsという名前で出力します

:::note info
js拡張子を指定した場合は wasm+jsが出てきますが、mjsの場合はmjs単体で出力されます
:::

## 呼び出す
mjsファイルをimportします
import時の名前はなんでも大丈夫です 
(mjsファイル内で `export default createModule;`と記述されていることを確認しました)

```App.js
import createModule from "./cpp/wasm_forweb.mjs";
```

利用にはthen構文を使います
関数コンポーネントを利用するときは`useEffect`ですが、クラスコンポーネントを利用するときは`componentDidMount`になります

```App.js
useEffect(() => {
  // Update the document title using the browser API
  createModule().then((Module) => {
    var hoge= new Module.ClassName();
    hoge.FunctionName();
  });
});
```

またuseEffectを利用するときは別途importが必要です

```App.js
import React, { useEffect } from 'react';
```


起動してみましょう

```
yarn webpack-dev-server --open
```
![おうどん食べたい](https://pbs.twimg.com/media/FJnEbC9aUAIA94U?format=jpg&name=large)

ちゃんと「おうどん食べたい」が出力されました お疲れ様でした

## その他参考にしたもの


[Emscriptenを使ったC/C++の関数のエキスポート方法まとめ]
(https://qiita.com/chikoski/items/462b34db61daf13a7897)

https://ja.reactjs.org/

https://github.com/bobbiec/react-wasm-demo

https://qiita.com/rubytomato@github/items/1696530bb9fd59aa28d8

https://zenn.dev/ichigo_dev/articles/d061205249e6db364c2a

https://dev-yakuza.posstree.com/react/start/

https://qiita.com/t-yng/items/ab2eb58c0dc2f84a8938

https://qiita.com/shizen-shin/items/506e63deb3d21a41c557

https://qiita.com/minato-naka/items/a2645aeb158662a9f0e9