個人用メモをそのまま公開します。
前提
jQuery時代のJavaScriptは分かるけど、それ以降の知識が曖昧なため、
VueとかNuxtとか書こうとすると、すぐにつまずく。
export default...は何?
JSでモジュール分割するための記法というのは分かる。
const { data } = ...は何?
複数の変数に代入するための記法と思うけど、1個のときもこの書き方を見る。
中括弧を使うのは、どういう意味?
オブジェクトを書くときの中括弧と同じか、文法的にはまったく別の意味か。
コード例
いま見てるチュートリアルのコードを例に疑問をまとめる。
JS、TS、Vueの疑問が混在してるけど構わない。
https://www.newt.so/docs/tutorials/get-contents-in-nuxt
<script lang="ts" setup>
// {}を削除しても動いたけど警告が出る: Default export is not declared in imported module
// Q1 {}は複数のときだけ必須というわけじゃなく、importの解釈に影響する?
// A1 {}の有無はデフォルトインポートかどうか。importの挙動に影響する。JSの仕様。
import type { Article } from '~/types/article'
// async/awaitが入れ子で複数出てきて混乱する
// awaitだから非同期処理を待ち、dataに代入してから次の行に進む、というのは分かる
// useAsyncDataはNuxtの機能
// Q2 {}を消してconst data = にすると取得結果が表示されなくなる。JS解釈が変わってVueに影響する?
// A2 {}を付けると戻り値のなかの特定のプロパティを取得する。ここではref型のdataプロパティが欲しいので、{}が必要。
const { data } = await useAsyncData('articles', async () => {
// Q3 識別子を渡してないのにNewt用機能が返されるのが分からない。分かりやすい変数名にしてるだけ?
// A3 {}でNuxtAppのnewtClientプロパティを取得している。変数名に$が付くのはよく分からない。Nuxtの仕様?
const { $newtClient } = useNuxtApp()
// asyncのなかでawaitしてる。
// これはuseAsyncDataのハンドラのreturnで、戻り値がdataに入るのは分かる
return await $newtClient.getContents<Article>({
appUid: 'blog',
modelUid: 'article',
query: {
// Q4 要素を削除してもエラーにならない。型定義の不一致も、SDKがうまいこと処理してくれる?
// A4 よく分からない。ここまでチェックできないという感じかもしれない。
select: ['_id', 'title', 'slug', 'body']
}
})
})
// Q5 .valueがあるということは、const {data}はrefになる?
// A5 だいたい合ってる。refになるというより、ref型であるdataプロパティを取得している。
const articles = data.value?.items
</script>
Q1 import
import type
はTypeScriptの仕様。型のみをインポートするためのTS記法。
https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html
import type Foo, { Bar, Baz } from "some-module";
// ~~~~~~~~~~~~~~~~~~~~~~
// error! A type-only import can specify a default import or named bindings, but not both.
{}なしはデフォルトインポート、{}つきは名前付きバインドと解釈されるみたい。
{}の解釈は、JSのimportの仕様がある
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/import
{}つきは、エクスポートされたものから特定のオブジェクト等をインポートする。
{}なしは、デフォルトとしてエクスポートされたものをインポートする。
exportの仕様を見る
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/export
一番最初に説明がある。
- 名前付きエクスポート (モジュールごとに 0 以上のエクスポート)
- デフォルトエクスポート (モジュールごとに 1 つのエクスポート)
それぞれの意味は簡単に理解できるけど、使い分けがよく分からない。
慣例とか書きやすさで適当にやればいい気がする。
値をひとつエクスポートしたい、あるいはモジュールでフォールバック先の値を持ちたい場合は、デフォルトエクスポートを使用するとよいでしょう。
Q2 const { data } = ...
分割代入というJS仕様。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
({ a, b } = { a: 10, b: 20 });
console.log(a); // 10
console.log(b); // 20
const {data}
は、戻り値のなかで、dataという名前の値を取得する記述。
{}なしにすると、戻り値そのものを受け取ることになるので、結果が変わって当然。
useAsyncData
https://nuxt.com/docs/api/composables/use-async-data/
useAsyncDataの戻り値はAsyncData型で、data以外もいくつかのプロパティがある。
dataはdata: Ref<DataT | null>
と定義されてて、予想通りrefだった。
Q3 useNuxtApp
Nuxtの仕様
https://nuxt.com/docs/api/composables/use-nuxt-app
プラグインを定義して呼び出す方法
https://nuxt.com/docs/guide/directory-structure/plugins#automatically-providing-helpers
呼び出す時に$をつける理由が分からない。
Q4 型定義の不一致
NewtのAPIドキュメント
https://developers.newt.so/apis/cdn#tag/contents_methods/operation/getContents
JS用のSDK
https://github.com/Newt-Inc/newt-client-js
TypeScriptの型
TypeScriptで定義されたインターフェースは、コンパイルチェックに活用された後、JavaScriptコードを生成する過程で消されるため、インターフェースがJavaScript実行時に影響することはありません。
export interface Article
のなかを削除変更しても、エラーにならない。問題なく動く。
SDKの内部の作りによって、コンパイルチェックが及ばないのかもしれない。
感想
JSの分割代入が分かれば、ほとんどの疑問が解消した。
分割代入は、イメージ的にはこんなコードと同じ感じ。
// 分割代入
const { data } = get()
// 普通に書くと、こんな感じ
const data = get().data
// 指定のデータだけ取得するという意味では、こんな感じもする
const data = get('data')
左辺に識別子を書くという記法が、たぶん初めて経験することで、説明を見るまで全然分からなかった。
https://ja.javascript.info/destructuring-assignment#ref-1858
「入れ子構造の非構造化」以下の書き方が非常に強力。
ただ短いだけの記法じゃなく、広範囲に便利に使えそう。
翌日の感想
分割代入に感じた違和感
const { data } = get()
このコードの場合、getメソッドの中に書かれてる識別子を、左辺で使ってることに違和感があった。呼び出し元から、呼び出し先を覗き込んでいるような感じで、それまでのプログラミング観と相容れなかった。
でも、よく考えると、全然そういう話ではない。
getメソッドから受け取った戻り値を、呼び出し元で処理してるだけなので、getメソッドの中を覗き込んでいるわけではない。
// こう書けば、戻り値を処理してるだけなのは明白
const ret = get()
const { data } = ret
慣れないNuxtを前にして、必要以上に「分からない」と思い込んでたかもしれない。
そのせいで、一目で分からない記述に不思議な解釈をしようとしてた。
phpでも分割代入できるみたい
古いlistのイメージしかなかった。
この記事を見ると、JSの分割代入と同じようなことができるみたい。JS独自の仕様ではなかった。
https://qiita.com/okumurakengo/items/9c781a17c15ea2916566