$ cat "dev63.hatenablog.com/entry/2024/07/18/234554.md"
// ブログエントリー — 2024/7/18

Next.js(React)とNuxt.js(Vue)のAPI早見ナレッジ

React と Vue の技術者がスムーズにコンバートするためのナレッジを以下にまとめます。両方のフレームワークにおける主要なコンセプトや一般的な実装例を比較して紹介します。

1. コンポーネントの定義とレンダリング

React

  • Functional Component:
import React from 'react'

// 関数コンポーネントの定義
function MyComponent() {
  return <div>Hello, React!</div> // JSXを返す
}

export default MyComponent // コンポーネントをエクスポート
  • Class Component:

    import React, { Component } from 'react'
    
    // クラスコンポーネントの定義
    class MyComponent extends Component {
      render() {
        return <div>Hello, React!</div> // JSXを返す
      }
    }
    
    export default MyComponent // コンポーネントをエクスポート
    

Vue

  • Single File Component:
<!-- シングルファイルコンポーネントの定義 -->
<template>
  <div>Hello, Vue!</div>
  <!-- テンプレート部分 -->
</template>

<script>
export default {
  name: 'MyComponent', // コンポーネント名の定義
}
</script>

<style scoped>
/* CSSスタイルの定義 */
</style>

2. ステート管理

React (useState)

import React, { useState } from 'react'

function Counter() {
  // useStateフックを使用して状態管理
  const [count, setCount] = useState(0)

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button> {/* ボタンをクリックすると状態を更新 */}
    </div>
  )
}

export default Counter // コンポーネントをエクスポート

Vue (data)

<template>
  <div>
    <p>{{ count }}</p>
    <!-- 状態を表示 -->
    <button @click="increment">Increment</button>
    <!-- ボタンをクリックするとメソッドを呼び出す -->
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0, // 初期状態を定義
    }
  },
  methods: {
    increment() {
      this.count++ // メソッドで状態を更新
    },
  },
}
</script>

3. ライフサイクルメソッド

React

import React, { useEffect } from 'react'

function MyComponent() {
  useEffect(() => {
    // ComponentDidMountおよびComponentDidUpdate
    console.log('Component mounted or updated')
    return () => {
      // ComponentWillUnmount
      console.log('Component will unmount')
    }
  }, []) // 第二引数の空配列によりComponentDidMountと同じ動作

  return <div>Hello, React!</div> // JSXを返す
}

export default MyComponent // コンポーネントをエクスポート

Vue

<template>
  <div>Hello, Vue!</div>
  <!-- テンプレート部分 -->
</template>

<script>
export default {
  mounted() {
    // ComponentDidMount
    console.log('Component mounted')
  },
  beforeDestroy() {
    // ComponentWillUnmount
    console.log('Component will unmount')
  },
}
</script>

4. コンポーネント間のデータ伝達

React (props)

function ChildComponent({ message }) {
  return <div>{message}</div> // 親コンポーネントから受け取ったpropsを表示
}

function ParentComponent() {
  return <ChildComponent message="Hello from parent!" /> // 子コンポーネントにpropsを渡す
}

export default ParentComponent // コンポーネントをエクスポート

Vue (props)

<!-- ChildComponent.vue -->
<template>
  <div>{{ message }}</div>
  <!-- 親コンポーネントから受け取ったpropsを表示 -->
</template>

<script>
export default {
  props: ['message'], // 親コンポーネントから受け取るpropsを定義
}
</script>

<!-- ParentComponent.vue -->
<template>
  <ChildComponent message="Hello from parent!" />
  <!-- 子コンポーネントにpropsを渡す -->
</template>

<script>
import ChildComponent from './ChildComponent.vue'

export default {
  components: {
    ChildComponent, // 子コンポーネントを登録
  },
}
</script>

5. コンポーネント間のイベント伝達

React (callback functions)

function ChildComponent({ onClick }) {
  return <button onClick={onClick}>Click me</button> // ボタンがクリックされたときに親コンポーネントから渡されたコールバックを呼び出す
}

function ParentComponent() {
  const handleClick = () => {
    alert('Button clicked!') // 子コンポーネントでイベントが発生したときに実行される関数
  }

  return <ChildComponent onClick={handleClick} /> // 子コンポーネントにコールバック関数を渡す
}

export default ParentComponent // コンポーネントをエクスポート

Vue (event emitters)

<!-- ChildComponent.vue -->
<template>
  <button @click="$emit('click')">Click me</button>
  <!-- ボタンがクリックされたときにカスタムイベントを発火 -->
</template>

<script>
export default {}
</script>

<!-- ParentComponent.vue -->
<template>
  <ChildComponent @click="handleClick" />
  <!-- 子コンポーネントのカスタムイベントを監視 -->
</template>

<script>
import ChildComponent from './ChildComponent.vue'

export default {
  components: {
    ChildComponent, // 子コンポーネントを登録
  },
  methods: {
    handleClick() {
      alert('Button clicked!') // 子コンポーネントでイベントが発生したときに実行される関数
    },
  },
}
</script>

6. フォームと双方向データバインディング

React

import React, { useState } from 'react';

function MyForm() {
  const [inputValue, setInputValue] = useState(''); // 状態を管理するためのフックを使用

  return (
    <div>
      <input
        type="text"
        value={inputValue} // 入力フィールドの値を状態にバインド
        onChange={(e) => setInputValue(e.target.value)} // 入力値が変更されたときに状態を更新
      />
      <p>{inputValue}</p> <!-- 状態を表示 -->
    </div>
  );
}

export default MyForm; // コンポーネントをエクスポート

Vue

<template>
  <div>
    <input type="text" v-model="inputValue" />
    <!-- 入力フィールドの値を状態にバインド -->
    <p>{{ inputValue }}</p>
    <!-- 状態を表示 -->
  </div>
</template>

<script>
export default {
  data() {
    return {
      inputValue: '', // 初期状態を定義
    }
  },
}
</script>

7. ルーティング

React (React Router)

import React from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'

function Home() {
  return <h2>Home</h2> // Homeコンポーネント
}

function About() {
  return <h2>About</h2> // Aboutコンポーネント
}

function App() {
  return (
    <Router>
      <Switch>
        <Route path="/about">
          <About /> //
          /aboutにマッチするルートに対応するコンポーネントをレンダリング
        </Route>
        <Route path="/">
          <Home /> // /にマッチするルートに対応するコンポーネントをレンダリング
        </Route>
      </Switch>
    </Router>
  )
}

export default App // コンポーネントをエクスポート

Vue (Vue Router)

// main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'
import Home from './components/Home.vue' // Homeコンポーネントのインポート
import About from './components/About.vue' // Aboutコンポーネントのインポート

Vue.use(VueRouter) // Vue Routerの使用を宣言

const routes = [
  { path: '/', component: Home }, // /にマッチするルートに対応するコンポーネントを定義
  { path: '/about', component: About } // /aboutにマッチするルートに対応するコンポーネントを定義
]

const router = new VueRouter({
  routes // ルート定義を渡す
})

new Vue({
  router, // ルーターインスタンスをVueインスタンスに渡す
  render: h => h(App) // Appコンポーネントをレンダリング
}).$mount('#app') // #app要素にマウント

8. 状態管理

React (Context API)

import React, { createContext, useContext, useState } from 'react'

const MyContext = createContext() // コンテキストを作成

function MyProvider({ children }) {
  const [value, setValue] = useState('Hello, World!') // 状態を管理

  return (
    <MyContext.Provider value={{ value, setValue }}>
      {' '}
      {/* コンテキストプロバイダーで状態を提供 */}
      {children}
    </MyContext.Provider>
  )
}

function MyComponent() {
  const { value, setValue } = useContext(MyContext) // コンテキストから状態を取得

  return (
    <div>
      <p>{value}</p>
      <button onClick={() => setValue('Hello, React!')}>Change</button> {/* ボタンをクリックすると状態を更新 */}
    </div>
  )
}

function App() {
  return (
    <MyProvider>
      <MyComponent /> {/* コンポーネントをラップしてコンテキストを利用 */}
    </MyProvider>
  )
}

export default App // コンポーネントをエクスポート

Vue (Vuex)

// store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex) // Vuexの使用を宣言

export default new Vuex.Store({
  state: {
    message: 'Hello, World!' // 初期状態を定義
  },
  mutations: {
    setMessage(state, payload) {
      state.message = payload // 状態を更新するためのミューテーション
    }
  },
  actions: {
    updateMessage({ commit }, payload) {
      commit('setMessage', payload) // 状態を更新するためのアクション
    }
  }
})

// main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store' // ストアをインポート

new Vue({
  store, // ストアをVueインスタンスに渡す
  render: h => h(App) // Appコンポーネントをレンダリング
}).$mount('#app') // #app要素にマウント

// MyComponent.vue
<template>
  <div>
    <p>{{ message }}</p>
    <button @click="updateMessage('Hello, Vue!')">Change</button>
    <!-- ボタンをクリックするとアクションをディスパッチ -->
  </div>
</template>

<script>
export default {
  computed: {
    message() {
      return this.$store.state.message // ストアから状態を取得
    }
  },
  methods: {
    updateMessage(newMessage) {
      this.$store.dispatch('updateMessage', newMessage) // アクションをディスパッチ
    }
  }
}
</script>

9. サーバーサイドレンダリング

Next.js (React)

  • pages/index.js

    // Next.jsのサーバーサイドレンダリングページ
    export default function Home() {
      return <h1>Hello, Next.js!</h1>
    }
    

Nuxt.js (Vue)

  • pages/index.vue
  <!-- Nuxt.jsのサーバーサイドレンダリングページ -->
  <template>
    <h1>Hello, Nuxt.js!</h1>
  </template>

10. スタイルの適用

React

import React from 'react'
import './App.css' // 外部CSSをインポート

function App() {
  return <div className="App">Hello, React!</div> // CSSクラスを適用
}

export default App // コンポーネントをエクスポート

Vue

<template>
  <div class="app">Hello, Vue!</div>
  <!-- CSSクラスを適用 -->
</template>

<script>
export default {
  name: 'App',
}
</script>

<style>
.app {
  color: red; // CSSスタイルの定義
}
</style>

これらの比較を通じて、React と Vue の技術者が互いのフレームワークの知識を理解し、スムーズにコンバートするための手助けとなるでしょう。


React(Next.js)と Vue(Nuxt.js)の技術者がスムーズにコンバートするためのナレッジを以下にまとめました。各フレームワークにおける主要なコンセプトや一般的な実装例を比較し、サンプルソースに細かい解説コメントを入れています。

1. プロジェクトのセットアップ

Next.js

# Next.jsプロジェクトの作成
npx create-next-app@latest
cd my-next-app
npm run dev

Nuxt.js

# Nuxt.jsプロジェクトの作成
npx create-nuxt-app my-nuxt-app
cd my-nuxt-app
npm run dev

2. ページの作成とルーティング

Next.js

// pages/index.js
// Next.jsのホームページ
export default function Home() {
  return <h1>Hello, Next.js!</h1>;
}

// pages/about.js
// Next.jsのアバウトページ
export default function About() {
  return <h1>About Next.js</h1>;
}

Nuxt.js

<!-- pages/index.vue -->
<template>
  <h1>Hello, Nuxt.js!</h1>
</template>

<script>
export default {
  name: 'HomePage',
}
</script>

<!-- pages/about.vue -->
<template>
  <h1>About Nuxt.js</h1>
</template>

<script>
export default {
  name: 'AboutPage',
}
</script>

3. レイアウト

Next.js

// pages/_app.js
// Next.jsのカスタムAppコンポーネント。全ページ共通のレイアウトやスタイルを適用。
import '../styles/globals.css'

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp

Nuxt.js

<!-- layouts/default.vue -->
<template>
  <!-- Nuxt.jsのデフォルトレイアウト。全ページ共通のレイアウトを定義 -->
  <div>
    <Nuxt />
  </div>
</template>

<script>
export default {
  name: 'DefaultLayout',
}
</script>

4. グローバルスタイルの適用

Next.js

/* styles/globals.css */
/* Next.jsのグローバルスタイル */
body {
  font-family: Arial, sans-serif;
}

Nuxt.js

/* assets/css/main.css */
/* Nuxt.jsのグローバルスタイル */
body {
  font-family: Arial, sans-serif;
}
// nuxt.config.js
export default {
  // グローバルCSSファイルの設定
  css: ['~/assets/css/main.css'],
}

5. API ルート

Next.js

// pages/api/hello.js
// Next.jsのAPIルート。エンドポイントを定義してAPIを提供
export default function handler(req, res) {
  res.status(200).json({ message: 'Hello from Next.js!' })
}

Nuxt.js

// server/api/hello.js
// Nuxt.jsのAPIルート。エンドポイントを定義してAPIを提供
export default function (req, res) {
  res.status(200).json({ message: 'Hello from Nuxt.js!' })
}

6. データフェッチング

Next.js

// pages/index.js
// Next.jsのデータフェッチング。getStaticPropsでビルド時にデータを取得
export async function getStaticProps() {
  // APIからデータをフェッチ
  const res = await fetch('https://api.example.com/data')
  const data = await res.json()

  return {
    props: {
      data,
    },
  }
}

export default function Home({ data }) {
  return <div>{JSON.stringify(data)}</div>
}

Nuxt.js

<!-- pages/index.vue -->
<template>
  <div>{{ data }}</div>
</template>

<script>
export default {
  // Nuxt.jsのデータフェッチング。asyncDataでページ表示前にデータを取得
  async asyncData() {
    const res = await fetch('https://api.example.com/data')
    const data = await res.json()
    return { data }
  },
}
</script>

7. 動的ルーティング

Next.js

// pages/posts/[id].js
// Next.jsの動的ルーティング。getStaticPathsで動的パスを生成
export async function getStaticPaths() {
  const res = await fetch('https://api.example.com/posts')
  const posts = await res.json()

  const paths = posts.map((post) => ({
    params: { id: post.id.toString() },
  }))

  return { paths, fallback: false }
}

export async function getStaticProps({ params }) {
  const res = await fetch(`https://api.example.com/posts/${params.id}`)
  const post = await res.json()

  return { props: { post } }
}

export default function Post({ post }) {
  return <div>{post.title}</div>
}

Nuxt.js

<!-- pages/posts/_id.vue -->
<template>
  <div>{{ post.title }}</div>
</template>

<script>
export default {
  // Nuxt.jsの動的ルーティング。asyncDataでページ表示前にデータを取得
  async asyncData({ params }) {
    const res = await fetch(`https://api.example.com/posts/${params.id}`)
    const post = await res.json()
    return { post }
  },
}
</script>

8. 静的サイト生成

Next.js

// next.config.js
module.exports = {
  exportPathMap: async function (
    defaultPathMap,
    { dev, dir, outDir, distDir, buildId }
  ) {
    return {
      '/': { page: '/' },
      '/about': { page: '/about' },
      // 追加のパス
    }
  },
}
# Next.jsのビルドとエクスポート
npm run build
npm run export

Nuxt.js

// nuxt.config.js
export default {
  target: 'static', // 静的サイト生成のターゲットを設定
}
# Nuxt.jsの静的サイト生成
npm run generate

9. プラグイン

Next.js

// Next.jsはプラグインシステムがなく、必要なライブラリを直接使用
import SomeLibrary from 'some-library'

function MyComponent() {
  return <SomeLibrary />
}

export default MyComponent

Nuxt.js

// plugins/some-library.js
import Vue from 'vue'
import SomeLibrary from 'some-library'

Vue.use(SomeLibrary)
// nuxt.config.js
export default {
  plugins: ['~/plugins/some-library.js'], // プラグインの設定
}

10. ミドルウェア

Next.js

// Next.jsでミドルウェアを使用するためにはカスタムサーバーを作成
const express = require('express')
const next = require('next')

const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express()

  server.use((req, res, next) => {
    console.log('Middleware log')
    next()
  })

  server.all('*', (req, res) => {
    return handle(req, res)
  })

  server.listen(3000, (err) => {
    if (err) throw err
    console.log('> Ready on http://localhost:3000')
  })
})

Nuxt.js

// middleware/logger.js
export default function (req, res, next) {
  console.log('Middleware log')
  next()
}
// nuxt.config.js
export default {
  serverMiddleware: ['~/middleware/logger'], // ミドルウェアの設定
}

この比較を通じて、Next.js と Nuxt.js の技術者が互いのフレームワークの知識を理解し、スムーズにコンバートするための手助けとなるでしょう。

ENTRY: dev63.hatenablog.com/entry/2024/07/18/234554.md
DATE: 2024/7/18
WORDS: 1710