【JS】named exportとdefault exportの違いやReactでの使い分け
こんにちは!外が大雪だ!Mizutani(@sirycity)です。名古屋でこんなに降るのめずらし。
今日はJavaScriptのexportの書き方2通り、named export と default export についてです。それぞれの長所短所を見ていきます。Reactでの使い方も見るよ!
書き方(定数)
まずそれぞれの書き方見ていこう。こんな感じ。どっちも簡単!
default export
// tom.js
export default 'Tom'
// index.js
import tom from './tom'
console.log(`Hello! I'm ${tom} .`)
// Hello! I'm Tom.
named export
// tom.js
export const tom = 'Tom'
// index.js
import { tom } from './tom'
console.log(`Hello! I'm ${tom} .`)
// Hello! I'm Tom.
書き方(コンポーネント)
次にReactコンポーネント、JSXでの書き方。
default export
// tom.jsx
export default () => <>Tom</>
// index.jsx
import Tom from './tom'
const Hello = () => (
<div>
Hello! I'm
<Tom />.
</div>
)
// <div>Hello! I'm Tom.</div>
named export
export const Tom = () => <>Tom</>
// index.jsx
import { Tom } from './tom'
const Hello = () => (
<div>
Hello! I'm
<Tom />.
</div>
)
// <div>Hello! I'm Tom.</div>
それぞれの特徴
default export
- 短く書ける
defaultの後に値をいきなり持ってくることができます。定数はもちろん、上のReactみたいに無名関数を持ってくることもできます。
// 1を足す関数(default)
export default n => n + 1
// 1を足す関数(named)
export const plus1 = n => n + 1
ただ、無名関数を直接exportするのは実はあまり良くない。TypeScriptだとReact.FC型が付けれないし、ESLintだとエラーが追えなくなるって怒られる。一応default exportでもこんな風に別定義することはできるよ。
// 1を足す関数(default、別で定義)
const plus1 = n => n + 1
export default plus1
// 1を足す関数(named、別で定義)
const plus1 = n => n + 1
export { plus1 }
あと、読み込む側も短い。ちょっとだけね。
// 読み込み(default)
import tom from './tom'
// 読み込み(named)
import { tom } from './tom'
- 遅延読み込みが簡単
遅延読み込みってのは最初は読み込まないけど後から読み込むよってやつのこと。Reactだとあんま重要じゃないコンポーネントは後から読み込んだりしたいよね。
Reactには遅延読み込み機能とかそれを簡単にできるライブラリとかあるけど、それらの機能はdefault exportの方が書きやすいです。例えば有名な遅延読み込みライブラリのloadable-componentsの書き方を比較すると…
import loadable from 'loadable-components'
// Tom遅延読み込み(default)
const Tom = loadable(() => import('./tom'))
// Tom遅延読み込み(named)
const Tom = loadable(async () => await (await import('./tom')).Tom)
named exportの方が究極にめんどくさい。まさかのawaitネスト。
- 1ファイルで1つしかexportできない
これはまあ当たり前ね。デフォルトだから当然1個しかexportできません。namedはいくつでもできる。 ただこれはcomponentを管理する場面ではあんまり関係ないかもしれません。Reactの作法的にも1ファイルから2つのコンポーネント出力よりも2ファイルからそれぞれ1つのコンポーネントを出力の方が良い気がしますね。
- コードのジャンプが簡単
default exportされたコンポーネントは定義にジャンプする時にcmd + click でジャンプできます(VSCodeの場合、たぶんほとんどのエディタはそう)。namedだとcmd+clickのあとに出てきたコードを2回クリックしなきゃジャンプできない。ちょっと面倒。
named export
- 変更に強い
namedの唯一無二の長所がこれ。読み込む側を変更すると読み込まれる側も変更する必要があります。例えば最初のtomの読み込まれる側をjohnに変更した場合…
// john.js
export default 'John'
// john.js
export const john = 'John'
読み込む側はdefaultの場合tomのままでも動いてしまいますが、namedの場合johnに変えないとエラーになります。
// index.js(defaultはtomのままでも動いちゃう)
import tom from './john'
console.log(`Hello! I'm ${tom} .`)
// Hello! I'm Jorn.
// index.js(namedはtomのままだと動かない)
import { tom } from './john'
console.log(`Hello! I'm ${tom} .`)
// ERROR!
この変更忘れに気づく点がnamed exportの長所であるとされています。もっとも、ファイル名(今回の場合はtom.js→john.js)を変更したらdefault exportでも変更に気付くので、個人的にはこの強みはどうかなーと思っています。コンポーネントの役割が大きく変わる時は普通はファイル名も変えるだろうし…
さいごに
近年のフロントエンドを見るに基本namedでどうしてもって場面だけdefaultって流れが大きいように感じるので、そうするのが良いかと思います。やっぱnamedが変更に強いってのが大きいのかな?みんなもimport、してみよう!以上。
おまけ
export defaultは特別な文法ではなくdefaultって名前でnamed exportしていて、でもdefaultは予約語だからちょっと変則的な形になるって考えると理解が進むと想います。そう、defaultは予約語なんです。殆ど使わないswitch文の…