Node8.0以上なのにWHATWG URLが使えない理由
2019/06/23

Nodeに標準で備わっているWHATWG URLを使おうとすると、TypeError: url__WEBPACK_IMPORTED_MODULE_0__.URL is not a constructorとエラーを出て使えないことがあったので、原因を調査して対策しました。

WHATWG URL

WHATWG URLは、Node.js8.0から正式対応したURL解析モジュールであり、IE・Edgeを除くブラウザにも対応しています。

import { URL } from 'url';

const url = new URL('https://test.jp/hoge');
console.log(url);
URL {
  href: 'https://test.jp/hoge',
  origin: 'https://test.jp',
  protocol: 'https:',
  username: '',
  password: '',
  host: 'test.jp',
  hostname: 'test.jp',
  port: '',
  pathname: '/hoge',
  search: '',
  searchParams: URLSearchParams {},
  hash: ''
}

このようにURLをprotocolだったり、hostだったり、URLを解析した結果をオブジェクト型で返します。

これを利用すると簡単にパラメータの操作ができます。
例えば、パラメータでページャーの制御をしているときには、以下のように使えます。

url.searchParams.set('page', 2);
console.log(url.href);
// => https://test.jp/hoge?page=2

url.searchParams.set('page', 3);
console.log(url.href);
// => https://test.jp/hoge?page=3

エラーになる理由

便利で普段から使っていたのですが、あるとき以下のようなエラーが出て使えませんでした。

Uncaught TypeError: url__WEBPACK_IMPORTED_MODULE_0_.URL is not a constructor

URLが見つからないようです。

Nodeのバージョン古かったかな?と思い確認したのですが、

$ node -v
v12.4.0

Node8.0以上はクリア。もちろん、公式リファレンスも存在しています。

コンソール画面でNodeを実行して確認してみても、URLというメソッドが存在しています。

$ node
> var url = require('url');
> url
{
  Url: [Function: Url],
  parse: [Function: urlParse],
  resolve: [Function: urlResolve],
  resolveObject: [Function: urlResolveObject],
  format: [Function: urlFormat],
  URL: [Function: URL],
  URLSearchParams: [Function: URLSearchParams],
  domainToASCII: [Function: domainToASCII],
  domainToUnicode: [Function: domainToUnicode],
  pathToFileURL: [Function: pathToFileURL],
  fileURLToPath: [Function: fileURLToPath]
}
>

webpackを通しているのが原因かなと思い、console.log(url);をしてみると、

ブラウザでのurlモジュール

URLが存在しません!
あれ?なんで?webpackのバージョンが古い?configミスった?とか考え、いろいろと試行錯誤した結果原因がわかりました。

サーバーサイドでは動くけど、クライアントサイドでは動かない!

そうなんです。同じ実行環境で、サーバーサイドとクライアントサイドでURLを確認すると、なぜかクライアントサイドでは、URLというメソッドがなくなっていました。

クライアントサイドでURLを使うためには

ブラウザ上ではURLが使えないのが分かったのですが、どうしても使いたい!

そんなときは、方法として2パターン考えました。

1. ブラウザに標準実装されているURLを使う

もし、実行するファイルがクライアントサイドだけで使うものであれば、import { URL}せずに使うとこができます。その1行をコメントアウトして実行してみてください。

ただし、URLはIE・Edgeには対応していませんので、url-polyfill を読み込ませることで、全ブラウザ上でも実行可能になります。

インストール

$ yarn add url-polyfill

使い方

import 'url-polyfill';

読み込ませる位置は、URLを使うファイル上でも良いですし、app.jsといったメインファイルに読み込ませておいても問題ありません。

2. Node上でもブラウザ上でも使えるuniversal-urlを使う

universal-url

importしたurlモジュールをuniversal-urlに置き換えるだけで、サーバーサイド・クライアントサイド両方で使えるコードとなります。

インストール

$ yarn add universal-url

使い方

import { URL } from 'url'と置き換える

import { URL } from 'universal-url';

私は、UniversalJSの方がラクなので、後者のuniversal-urlを導入して解決しました。