Nuxt3ベータ版で見るNuxt2との違うところ

Nuxt3がやっとパブリックベータ版になった!!!
いや〜待ってたよずっと待ってた。で早速使ってみた。

必要環境

  • Node.js
  • VisualStudio Code + Volar Extension

これを書いている時点だと、Node.jsのバージョンは(^14.16.0 || ^16.11.0 || ^17.0.0)だけどしょっちゅう変わるので都度確認が必要。
VSCodeの圧が強い。が、まあ仕方ないねこれは。
Atomあんまり使わなくなってしまった。

Nuxt2からの移行

できるかできないかでいうと、一応できる。NuxtBridgeを使用する。
NuxtBridgeを使うと2と3の間みたいな状態になるので、Nuxt2、Nuxt2+Bridge、Nuxt3と3パターンバージョンがある感じだ。

去年作ったNuxt2のやつにBridge入れてみようかなあ。

今から作るプロジェクトはどれ使うのがええんや?

これについてはdicussionsの質問についてたコメントを引用しておく。

Hi @dijaca. I would recommend using Nuxt 3 with Vue 3 compatible libraries if you are starting a new project. During the beta period, we try to break as little as possible and bug fixes are immediately released.

https://github.com/nuxt/framework/discussions/1572#discussioncomment-1558284

V2使うくらいならReact使う方がいいと思っていたのは前にも書いた。が、V2は色々な意味で安定しているので、将来的に陳腐化するのを考慮して使うならありだと思う。
V3は更新が激しいので使うなら半年くらいはバグとバージョンアップに振り回される覚悟が必要だと思う。

Nuxt3の主な新機能

起動からサーバーが立ち上がるまではちょいかかるが、それ以降が爆速。
ビルドは規模に比例して長引くからガチなプロジェクトでどうなのかはわからないが、後述のお試しアプリではDoneまで12.49sだった。
Devtoolsはまだなくて、Vue.js Devtoolsも未対応。だからちょっと不便。

Vue3がIE11のサポートを切ったことにより、IE11のサポートをキメたい場合はNuxt2(Vue2)を利用しなければならない。

Vue2は2.7が最終バージョンで、2.7リリース後18ヶ月がサポート期間となり、以降はセキュリティメンテナンスが行われる予定。

TypeScriptサポート

プラグインや設定が必要ないので気楽に使える。
V2で使おうとして酷い目にあったのが嘘のようだ。

Vueファイルならscriptタグにlang属性でtsを指定するだけでそこはTypeScriptの領域になる。
(lang属性による言語指定は必須になった)

<script lang="ts">
// TypeScript
</script>

同じコンポーネントファイルの中でTSとJSを混在させると怒られる。

ルートディレクトリ内がTypeScript環境になってるので、拡張子TSのファイルを作ればいい。
後述する server/api とかの戻り値は内部で型定義してあれば自動で型生成してくれるが、外部APIからデータとってきててanyになる場合なんかは指定が要る。

const { data } = await useFetch<string, User[]>(
  'https://example.com/api/users',
);

まだ型定義不足してるらしくてめっちゃエラー出る。

Auto Import

コンポーネントから関連APIまで自動でimportする。
V2(2.13〜)ではconfigで components: true が必要だったが、この設定は不要になった。
対象になっているAPIとかはこの定義ファイル見るのが早い。

プラグインも自動でインポートされるので、nuxt.config.jsの設定は不要である。
この自動インポート環境楽すぎてやばい🙄

useState

V2ではアプリ内で共有される状態を保存しておくためにVuexを使っていたが、V3ではそれに変わるものとしてuseStateが使えるようになった。なんというかすごくReactっぽいな?

V2の例

// plugins/sample.js
export default ({ store }, inject) => {
   store.commit('foo', 'bar');
}
// pages/index.vue
<template>
   <div>
     {{ foo }}
   </div>
</template>
<script>
export default {
  asyncData({ store }) {
    return {
       foo: store.state.foo
    }
  }
}
</script>

V3の例

// plugins/sample.ts
import { defineNuxtPlugin, useState } from '#app'

export default defineNuxtPlugin((nuxt) => {
  const foo = useState(
    'foo',
    () => 'bar'
  )
})
// app.vue
<template>
  <div>
    {{ foo }}
  </div>
</template>

<script setup>
const foo = useState('foo')
</script>

あまりにもお手軽に使えてしまうが故に、アプリ全体で共通する状態を保存するものであるというのを忘れると闇鍋になるからV2以上に注意して使わないと怖いなあと思った。

Nuxt内部でのAPI作成

server ディレクトリにバックエンドが作れるようになった。

V2の serverMiddleware は server/middleware に変更。これはディレクトリが変わっただけで機能は同じ。
新しく server/api が増えてエンドポイントがお手軽に作れるようになった。
pages のようにファイル追加するだけでそのファイル名と同じエンドポイントが生成される。

// server/api/hello.ts

export default () => 'Hello World'
// app.vue

<template>
<p>{{data}}</p>
</template>

<script setup>
const { data } = await useFetch('/api/hello');
</script>

app.vue

Nuxt3でメインコンポーネントとなるファイル。
V2では app.html をルートに置くことでhtml, head, bodyタグの操作ができたけれども、それが app.vue に置き換わった感じ。
それらメタタグは新たに用意されたメタコンポーネントを使うもよし、useMeta 使うもよしって感じで変わらず操作できる。

Nuxt3は pages がオプションなので、
ページ分けの必要がないいわゆるペライチサイトの場合、このapp.vueファイルだけあればOKである。

レイアウト

layoutsディレクトリに配置したファイルがページレイアウトとして利用されるのはV2と同じ。

layouts/default.vueapp.vue で代用できるようになったので、2つ以上レイアウトがあれば使うかなって感じ。

composables ディレクトリ

ここに作成したファイルも自動インポート対象。
composables/useFoo.ts  は useFoo() てな感じで利用できる。

Vue Composable の説明はこれがわかりやすいと思う。

同じことの繰り返しだなあこれ…ってロジックはここに切り出しておけばいいのだ。

$fetch

ohmyfetchを利用した内臓ヘルパー。

ブラウザで実行した場合はサーバーへのAPI呼び出し、
サーバーで実行した場合は関連する関数を呼び出す。という挙動をする。

const todo = await $fetch('/api/todos/add', {
      params: {
        body: this.newTodo
      }
})

外部のAPIも叩けるからaxioshttpモジュールを追加する必要は無くなったと思う。

useAsyncDataとuseFetch

setupで使えるNuxt3のデータ取得用関数。
2つもあるとどっち使うのか迷うけど、ソースみた感じuseFetchはuseAsyncDataのラッパーでキーの設定とかを勝手にやってくれるので、大抵の場合はuseFetch使っておけばよさそう🤔

基本的にsetupで1回だけ呼び出すものだが、更新が必要な場合はrefreshを利用する。

<script setup lang="ts">
  const id = ref(1)

  const {
    data,
    refresh
  } = await useAsyncData('characters', () => {
    return $fetch('https://rickandmortyapi.com/api/character/' + id.value)
  })

  const getNextChar = () => {
    id.value += 1
    refresh()
  }
</script>

<template>
  <div>{{ data }}</div>
  <button @click="getNextChar">Get next character</button>
  <button @click="refresh">Refetch</button>
</template>

以下はheaderが必要な場合の例。

<script setup>
const useFetchWithHeaders = (req, opts) => useFetch(req, { ...opts, headers: { test: '123' } });

const { data, refresh } = await useFetchWithHeaders('/api/test');
</script>

プラグイン

pluginsディレクトリにファイル作ったらオートインポートされる。
そしてプラグインの作り方が変わった。

import { defineNuxtPlugin } from '#app'

export default defineNuxtPlugin(nuxtApp => {
  nuxtApp.provide('hello', msg => `Hello ${msg}!`);
})

declare module '#app' {
  interface NuxtApp {
    $hello (msg: string): string
  }
}

サーバーあるいはクライアントどちらに対応したプラグインかの指定は、ファイル名の拡張子前にclientもしくはserverを追加する。(例:sample.client.js

Tailwind(PostCSS)を利用する例

assets/css/tailwind.css を用意する

@tailwind base;
@tailwind components;
@tailwind utilities;

nuxt.config.js に設定を追加する。

import { defineNuxtConfig } from 'nuxt3'

export default defineNuxtConfig({

  css: ['~/assets/css/tailwind.css'],

  build: {
    postcss: {
      plugins: {
        tailwindcss: {},
        autoprefixer: {},
      }
    },
  },
})

postcss.config.js をrequireしても良い。

Todoアプリのサンプル

いつものcodesandboxで簡単なTodoアプリを作ってみた。
前に作ったページネーションのコンポーネントを再利用してる。
お試しにはログインしてforkが必要。

CompositionAPIちょっとわかってきた気がするぞ。

リポジトリ

以下はtailwindcssも使ってみたやつ。
codesandboxやstackblitzではエラーでて動かなかったから、Todoだけ切り出した。

https://github.com/Tenderfeel/nuxt3-app

難点

現状SSRしかないのでSSGができない。データもSSRからアクセスできるものでないとレンダリング時に反映されないので、LocalStorageを保存領域にしたい場合はちょっと面倒なことになる。

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください