three.jsのGLTFLoader.jsのimportに失敗したときの解決備忘録

# 概要

2022年2月22日時点の現象

https://threejs.org/

こちらの[Download](https://github.com/mrdoob/three.js/archive/master.zip)からZipダウンロードしたファイル群を下のように展開

ディレクトリ構造

```
root
└ index.html
└ index.js
└ three.js-master/three.js-master
  └ build/three.module.js
  └ examples/jsm/loaders/GLTFLoader.js
```

GLTFLoader.jsをimport

```js:index.js
import * as THREE from './three.js-master/three.js-master/build/three.module.js'
import {GLTFLoader} from './three.js-master/three.js-master/examples/jsm/loaders/GLTFLoader.js'
```

サーバー立ち上げ
```
npm start —force
```

`localhost:8080`を確認するもエラー 


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96970/42eca0de-9855-d1a4-25ea-b4dde418a7cd.png)

```
Uncaught TypeError: Failed to resolve module specifier "three". Relative references must start with either "/", "./", or "../"
```

# 解決法

GLTFLoader.jsの64行目付近の from文

元々はこちら

```js:GLTFLoader.js
//中略
	VectorKeyframeTrack,
	sRGBEncoding
} from 'three';
//中略
```


`from 'three';` の部分を`from '../../../build/three.module.js';`に変更

```js:GLTFLoader.js
//中略
	VectorKeyframeTrack,
	sRGBEncoding
} from '../../../build/three.module.js';
//中略
```

これで解決しました。

---
# 追記 (おそらくこちらの方が正着？)

[@o_ob](https://twitter.com/o_ob)さんからTwitterでコメントをいただきました。(ありがとうございます！)

https://twitter.com/o_ob/status/1496164433881665543?s=20&t=n25PafDSvgLh0AuQuvWQhg

https://twitter.com/o_ob/status/1496172198746378240?s=20&t=n25PafDSvgLh0AuQuvWQhg

参考にした結果以下の書き方でも解決しました。

## GLTFLoader.jsは元のまま

```js:GLTFLoader.js
//中略
	VectorKeyframeTrack,
	sRGBEncoding
} from 'three';
//中略
```

## エラー時のhtml

```html:index.html

<html lang="ja">
<head>
  <meta charset="utf-8"/>
  <meta http-equiv="X-UA-Compatible">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>3D Model Loader</title>
</head>
<body>
  <canvas class="webgl" id="myCanvas"></canvas>
  <script src="index.js" type="module"></script>
</body>
</html>
```
## 修正後のhtml

```html:index.html

<html lang="ja">
<head>
  <meta charset="utf-8"/>
  <meta http-equiv="X-UA-Compatible">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>3D Model Loader</title>
  <!-- 追加 -->
	<script type="importmap">
    {
      "imports": {
        "three": "./three.js-master/three.js-master/build/three.module.js"
      }
    }
  </script>
<!-- ここまで -->
</head>
<body>
  <canvas class="webgl" id="myCanvas"></canvas>
  <script src="index.js" type="module"></script>
</body>
</html>
```

# importMapとは

気になったので調べました。

https://laboradian.com/import-maps/

> 「JavaScript の実際のファイル名」と「そのファイルを import するときに指定する名前」を切り離すことができます（2つの対応情報はJSON形式で用意しておきます）。
これにより、ファイル名が後から変わっても（ファイル名にバージョン番号が含まれている場合など）、一度記述した import 文を変更する必要はなくなります。（JSON形式による対応情報側のみを変えます）
npm のパッケージ名のようなシンプルな名前でインポートできます。
静的インポート、動的インポートのどちらでも使えます。

とのこと。
これにより `three`という名まえを使えるようになるということですね。

ちなみに、three.jsの[サンプル実装](https://github.com/mrdoob/three.js/blob/master/examples/webgl_loader_gltf.html#L18-L28)では

```html:webgl_loader_gltf.html
		<!-- Import maps polyfill 古いブラウザーでも動かすためのコード -->
		<!-- Remove this when import maps will be widely supported この1行はimport mapが普及したら削除してよい -->
		<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>

		<script type="importmap">
			{
				"imports": {
					"three": "../build/three.module.js"
				}
			}
		</script>
```

とありました。こちら確認しておけば...!

# 補足議論

おそらく、three.js開発者はnode_modulesによるinstall想定だったため、今回のようなエラーが発生したのでは...?など予想しています。(詳しい人教えてください。)