Open20

Nuxt3の素振り

Yuki TerashimaYuki Terashima

Nuxt 3 - Roadmap によると、nuxt@3.0.0のリリースが2022年6月に控え、そろそろ安定版になりそうなので今のうちに軽く素振りをしておく。

上記は2022年6月2日現在の画像。

Yuki TerashimaYuki Terashima
 🎉  Another grand Nuxt project just made! Next steps:

     📁  `cd nuxt-app-20220602`

     💿  Install dependencies with `npm install` or `yarn install` or `pnpm install --shamefully-hoist`

     🚀  Start development server with `npm run dev` or `yarn dev` or `pnpm run dev

初期化後はnpmyarn、もしくはpnpmで依存のインストールを行う。

Yuki TerashimaYuki Terashima

書き忘れたがNode.jsのバージョンは v16.13.0 を使っている。

インストール直後の構造。

tree -L 3 -I node_modules
.
├── README.md
├── app.vue
├── nuxt.config.ts
├── package.json
├── tsconfig.json
└── yarn.lock

package.jsonの内容。

{
  "private": true,
  "scripts": {
    "build": "nuxt build",
    "dev": "nuxt dev",
    "generate": "nuxt generate",
    "preview": "nuxt preview"
  },
  "devDependencies": {
    "nuxt": "3.0.0-rc.3"
  }
}

ちょっと前はgenerateコマンドがなかったような気がする。

Yuki TerashimaYuki Terashima

次は機能を見ていく予定だったが、Next steps として

Learn about the framework concepts

とあるので読んでみる。

Yuki TerashimaYuki Terashima

Nuxt 3 - What is Nuxt?

なんか色々できてすごいという話。nuxiコマンドについて、以前はpackage.jsonにかかれていたコマンドがnuxtじゃなくてnuxiだったような?と思って、手元でyarn nuxt -vyarn nuxi -vとかして確認してみたけど、どうやらnuxtコマンドはnuxiコマンドのエイリアスっぽい?

出力が全く同じだった。

Nuxt CLI v3.0.0-rc.3                                                                            08:20:16
Usage: npx nuxi dev|build|preview|start|analyze|generate|prepare|typecheck|usage|info|init|create|upgrade|test|add|new [args]

Use npx nuxi [command] --help to see help for each command                                      08:20:16

✨  Done in 0.17s.
Yuki TerashimaYuki Terashima

Nuxt 3 - Vue.js Development

Components auto-imports がデフォで入っている。

Differences with Nuxt 2 / Vue 2 という項目があって面白かった。

  • Better performance
  • Composition API
  • TypeScript support

が違うらしい。

Yuki TerashimaYuki Terashima

Nuxt 3 - Auto Imports

useAsyncDatarefなど、よく使う機能が自動でインポートされていますよ、という話。

'#imports'を使うことによって、明示的にインポートすることもできるらしい。

<script setup>
  import { ref, computed } from '#imports'

  const count = ref(1)
  const double = computed(() => count.value * 2)
</script>
Yuki TerashimaYuki Terashima

Nuxt 3 - TypeScript

yarn nuxi typecheckコマンドで型チェックができて良い。

が、試しに実行したらエラーになってしまった。悲しい...🥲

yarn nuxi typecheck
yarn run v1.22.19
$ /Users/yuki/Src/frameworks/nuxt/nuxt-app-20220602/node_modules/.bin/nuxi typecheck
Nuxt CLI v3.0.0-rc.3                                                                            08:36:50
Need to install the following packages:
  typescript
  vue-tsc
Ok to proceed? (y) y
app.vue:3:6 - error TS2322: Type '{}' is not assignable to type 'IntrinsicAttributes & Partial<{}> & Omit<Readonly<ExtractPropTypes<{}>> & VNodeProps & AllowedComponentProps & ComponentCustomProps, never> & Readonly<...>'.
  Type '{}' is missing the following properties from type 'Readonly<ExtractPropTypes<{ appName: { type: StringConstructor; default: string; }; version: { type: StringConstructor; default: string; }; title: { type: StringConstructor; default: string; }; readDocs: { ...; }; followTwitter: { ...; }; starGitHub: { ...; }; }>>': version, title, appName, readDocs, and 2 more.

3     <NuxtWelcome />
       ~~~~~~~~~~~


Found 1 error in app.vue:3


 ERROR  Command failed with exit code 2: npx -p vue-tsc -p typescript vue-tsc --noEmit          08:37:01

  at makeError (node_modules/nuxi/dist/chunks/index5.mjs:1242:11)
  at handlePromise (node_modules/nuxi/dist/chunks/index5.mjs:2071:26)
  at processTicksAndRejections (node:internal/process/task_queues:96:5)
  at async Object.invoke (node_modules/nuxi/dist/chunks/typecheck.mjs:43:7)
  at async _main (node_modules/nuxi/dist/cli.mjs:46:20)

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Yuki TerashimaYuki Terashima

あと Stricter checks で型チェックを厳密に出来る、みたいなのもあったけど、これは別にtsconfig.jsonを直接編集する感じでいいかなぁ。

export default defineNuxtConfig({
  typescript: {
    strict: true
  }
})

また、デフォのtsconfig.jsonは以下のようになっており、./.nuxt/tsconfig.jsonを利用していることが分かる。

{
  // https://v3.nuxtjs.org/concepts/typescript
  "extends": "./.nuxt/tsconfig.json"
}

これを確認するとなるほど色々頑張っていることが分かる。自動生成されるファイルなので、strictとか勝手に将来変わったらちょっと苦労するかもな〜とか思ったりした。ここまで来て原因を追求できる人ならいいけど、初学者は少し詰まりそう、かも?

./.nuxt/tsconfig.json
// Generated by nuxi
{
  "compilerOptions": {
    "jsx": "preserve",
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "Node",
    "skipLibCheck": true,
    "strict": false,
    "allowJs": true,
    "noEmit": true,
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "types": [
      "node"
    ],
    "baseUrl": "..",
    "paths": {
      "~~": [
        "."
      ],
      "~~/*": [
        "./*"
      ],
      "@@": [
        "."
      ],
      "@@/*": [
        "./*"
      ],
      "~": [
        "."
      ],
      "~/*": [
        "./*"
      ],
      "@": [
        "."
      ],
      "@/*": [
        "./*"
      ],
      "assets": [
        "assets"
      ],
      "public": [
        "public"
      ],
      "#app": [
        "node_modules/nuxt/dist/app"
      ],
      "#app/*": [
        "node_modules/nuxt/dist/app/*"
      ],
      "vue-demi": [
        "node_modules/nuxt/dist/app/compat/vue-demi"
      ],
      "#head": [
        "node_modules/nuxt/dist/head/runtime"
      ],
      "#head/*": [
        "node_modules/nuxt/dist/head/runtime/*"
      ],
      "#components": [
        ".nuxt/components"
      ],
      "#imports": [
        ".nuxt/imports"
      ],
      "#build": [
        ".nuxt"
      ],
      "#build/*": [
        ".nuxt/*"
      ]
    }
  },
  "include": [
    "./nuxt.d.ts",
    "../**/*"
  ]
}
Yuki TerashimaYuki Terashima

Nuxt 3 - Components directory

試しにcomponents/TheHeader.vueを作成し、

<template>
  <header>header!!!</header>
</template>

app.vueから呼び出すと、呼び出せることが確認できた。便利。

<template>
  <div>
    <TheHeader />
  </div>
</template>
Yuki TerashimaYuki Terashima

Component Names にあるように、ディレクトリがネストしていてもいい感じに対応するっぽいが、コンポーネントの命名としては

For clarity, we recommend that the component's filename matches its name. (So, in the example above, you could rename Button.vue to be BaseFooButton.vue.)

としたほうが良いらしい。

実際にやってみて、動作することを確認できた。

Yuki TerashimaYuki Terashima

Dynamic components として、resolveComponent を使うという話や Dynamic Imports で Lazy という接頭辞をつけるという話、あとは <ClientOnly> Component の話があったりした。

Yuki TerashimaYuki Terashima

<ClientOnly> Component、<template #fallback>でサーバーサイドでのレンダリングを定義できるっぽい。

<template>
  <div>
    <Sidebar />
    <ClientOnly>
      <!-- this component will only be rendered on client side -->
      <Comments />
      <template #fallback>
        <!-- this will be rendered on server side -->
        <p>Loading comments...</p>
      </template>
    </ClientOnly>
  </div>
</template>

これ、動作を確認してみたけどクライアント側で少しLoading comments...という表示がチラつくようだった。

Yuki TerashimaYuki Terashima

Nuxt 3 - Pages directory

pages/index.vueを追加し、さらにapp.vueを使っている場合はNuxtPageを使うように書き換える必要がある。

<template>
  <h1>Index page</h1>
</template>

この書き換え時にはDevサーバーの再起動が必要だった。

<template>
  <div>
    <NuxtPage />
  </div>
</template>

Pages の成約として

Pages must have a single root element to allow route transitions between pages. (HTML comments are considered elements as well.)

というのはあるらしい。コメントもだめっぽいので、ちょっと気をつける必要がありそう。

pages/working.vue
<template>
  <div>
    <!-- This page correctly has only one single root element -->
    Page content
  </div>
</template>
pages/bad-1.vue
<template>
  <!-- This page will not render when route changes during client side navigation, because of this comment -->
  <div>Page content</div>
</template>
Yuki TerashimaYuki Terashima

Dynamic Routes は_id.vueから[id].vueに変わっている。このページの追加時もサーバーの再起動が必要だった。もともと、こんなにサーバーの再起動って必要だったっけ?

-| pages/
---| index.vue
---| users-[group]/
-----| [id].vue
<template>
  <p>{{ $route.params.group }} - {{ $route.params.id }}</p>
</template>

http://localhost:3000/users-foo/1 にアクセスしてfoo - 1と表示されることを確認した。