検索
ホームページウェブフロントエンドjsチュートリアルNext.js を使用した動的なブログ ダッシュボードの構築

導入

こんにちは、お元気ですか?こちらは Vítor です。プログラミング スキルの向上に役立つ新しいプロジェクトを携えて戻ってきました。最後にチュートリアルを公開してからしばらく時間が経ちました。過去数か月間、私は休息を取り、他の活動に集中する時間を取りました。この期間中に、私は小さな Web プロジェクト、つまりこのチュートリアルの焦点となるブログを開発しました。

このガイドでは、Markdown をレンダリングできるブログ ページのフロントエンドを作成します。このアプリケーションには、パブリックおよびプライベート ルート、ユーザー認証、Markdown テキストの作成、写真の追加、記事の表示などの機能が含まれます。

お好みに合わせてアプリケーションを自由にカスタマイズしてください。私はそれをお勧めします。

このアプリケーションのリポジトリにはここからアクセスできます:

Building a Dynamic Blog Dashboard with Next.js ゴンドラク08 / ブログプラットフォーム

Next.js/typescript で作られたブログ プラットフォーム。

Platforma パラブログ

  • テキストによるチュートリアル

材料

  • next-auth - Next.js
  • の認証文書
  • github.com/markdown-it/markdown-it - マークダウン図書館。
  • github.com/sindresorhus/github-markdown-css- 最も重要なエディターのマークダウン。
  • github.com/remarkjs/react-markdown - レンダリング マークダウンとそのコンポーネントの反応に関する参考文献。
  • github.com/remarkjs/remark-react/tree/4722bdf - React の Markdown を変換するプラグイン
  • codemirror.net - ウェブ用エディターコンポーネント
  • 反応アイコン - 反応するアイコンのライブラリ

コモ・ユーザー

npm i
npm run start

サーバー

サーバーアプリケーションのサーバーを起動します


GitHub で表示


このチュートリアルには、このガイドで使用される Node.js サーバーの作成も含まれています。

楽しんでいただければ幸いです。

コーディングを楽しんでください!

図書館

このプロジェクトで使用されるライブラリの概要は次のとおりです:

  • next-auth - Next.js
  • の認証ライブラリ
  • github.com/markdown-it/markdown-it - マークダウン ライブラリ。
  • github.com/sindresorhus/github-markdown-css - Markdown エディターのスタイル設定用。
  • github.com/remarkjs/react-markdown - React コンポーネントで Markdown をレンダリングするためのライブラリ。
  • github.com/remarkjs/remark-react/tree/4722bdf - Markdown を React に変換するプラグイン
  • codemirror.net - Web コンポーネント エディター。
  • act-icons - React 用のアイコン ライブラリ

React プロジェクトの作成

Next.js フレームワークの最新バージョンを使用します。このチュートリアルの作成時点ではバージョン 13.4 です。

次のコマンドを実行してプロジェクトを作成します:

npm i
npm run start

インストール中に、テンプレート設定を選択します。このチュートリアルでは、プログラミング言語として TypeScript を使用し、アプリケーションのスタイル設定に Tailwind CSS フレームワークを使用します。

構成

次に、使用するすべてのライブラリをインストールしましょう。

マークダウン
npx create-next-app myblog
リアクションコメント
npm i  markdown-it @types/markdown-it markdown-it-style github-markdown-css react-markdown
コードミラー
remark remark-gfm remark-react
アイコン
npm @codemirror/commands @codemirror/highlight @codemirror/lang-javascript @codemirror/lang-markdown @codemirror/language @codemirror/language-data @codemirror/state @codemirror/theme-one-dark @codemirror/view

次に、使用しないものをすべて削除して、インストールの初期構造をクリーンアップします。

建築

これがアプリケーションの最終的な構造です。

npm i react-icons @types/react-icons

最初のステップ

next.config の構成

プロジェクトのルートのファイル next.config.js で、記事の画像にアクセスするドメイン アドレスを設定しましょう。このチュートリアルでは、またはローカル サーバーを使用している場合は、localhost を使用します。

アプリケーションに画像が正しく読み込まれるように、必ずこの構成を含めてください。

src-
  |- app/
  |    |-(pages)/
  |    |      |- (private)/
  |    |      |       |- (home)
  |    |      |       |- editArticle/[id]
  |    |      |       |
  |    |      |       |- newArticle
  |    |      | - (public)/
  |    |              | - article/[id]
  |    |              | - login
  |    |
  |   api/
  |    |- auth/[...nextAuth]/route.ts
  |    |- global.css
  |    |- layout.tsx
  |
  | - components/
  | - context/
  | - interfaces/
  | - lib/
  | - services/
middleware.ts

ミドルウェアの構成

アプリケーション src/ のルート フォルダーに、プライベート ルートへのアクセスを確認するための middleware.ts を作成します。

const nextConfig = {
   images: {
    domains: ["localhost"],
  },
};

ミドルウェアとそれを使用して実行できるすべてのことについて詳しくは、ドキュメントを確認してください。

認証ルートの構成

/app フォルダー内の api/auth/[...nextauth] に、route.ts という名前のファイルを作成します。これには、CredentialsProvider を使用して認証 API に接続するルートの構成が含まれます。

CredentialsProvider を使用すると、ユーザー名とパスワード、ドメイン、2 要素認証、ハードウェア デバイスなどの任意の資格情報を使用してログインを処理できます。

まず、プロジェクトのルートに .env.local ファイルを作成し、シークレットとして使用されるトークンを追加します。

npm i
npm run start

次に、認証システムを作成しましょう。この NEXTAUTH_SECRET が src/app/auth/[...nextauth]/routes.ts ファイル内のシークレットに追加されます。

npx create-next-app myblog

認証プロバイダー

プライベート ルートのページ間でユーザーのデータを共有する認証プロバイダー、つまりコンテキストを作成しましょう。後でこれを使用して、layout.tsx ファイルの 1 つをラップします。

次の内容を含むファイルを src/context/auth-provider.tsx に作成します。

npm i  markdown-it @types/markdown-it markdown-it-style github-markdown-css react-markdown

グローバルスタイル

全体として、私たちのアプリケーションでは、Tailwind CSS を使用してスタイルを作成します。ただし、一部の場所では、ページとコンポーネント間でカスタム CSS クラスを共有します。

remark remark-gfm remark-react

レイアウト

次に、プライベートとパブリックの両方のレイアウトを書きましょう。

app/layout.tsx

npm @codemirror/commands @codemirror/highlight @codemirror/lang-javascript @codemirror/lang-markdown @codemirror/language @codemirror/language-data @codemirror/state @codemirror/theme-one-dark @codemirror/view

ページ/レイアウト.tsx

npm i react-icons @types/react-icons

API呼び出し

私たちのアプリケーションは API を複数回呼び出します。このアプリケーションを任意の外部 API を使用するように適応させることができます。この例では、ローカル アプリケーションを使用しています。バックエンドのチュートリアルとサーバーの作成をまだ見ていない場合は、チェックしてください。

src/services/ に次の関数を書きましょう:

  1. authService.ts: サーバー上でユーザーを認証する機能。
src-
  |- app/
  |    |-(pages)/
  |    |      |- (private)/
  |    |      |       |- (home)
  |    |      |       |- editArticle/[id]
  |    |      |       |
  |    |      |       |- newArticle
  |    |      | - (public)/
  |    |              | - article/[id]
  |    |              | - login
  |    |
  |   api/
  |    |- auth/[...nextAuth]/route.ts
  |    |- global.css
  |    |- layout.tsx
  |
  | - components/
  | - context/
  | - interfaces/
  | - lib/
  | - services/
middleware.ts

2.refreshAccessToken.tsx:

const nextConfig = {
   images: {
    domains: ["localhost"],
  },
};
  1. getArticles.tsx: データベースに保存されているすべての記事を取得する関数:
export { default } from "next-auth/middleware";
export const config = {
  matcher: ["/", "/newArticle/", "/article/", "/article/:path*"],
};
  1. postArticle.tsx: 記事データをサーバーに送信する機能。
.env.local
NEXTAUTH_SECRET = SubsTituaPorToken
  1. editArticle.tsx: データベース内の特定の記事を変更する関数。
import NextAuth from "next-auth/next";
import type { AuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { authenticate } from "@/services/authService";
import refreshAccessToken from "@/services/refreshAccessToken";

export const authOptions: AuthOptions = {
  providers: [
    CredentialsProvider({
      name: "credentials",
      credentials: {
        email: {
          name: "email",
          label: "email",
          type: "email",
          placeholder: "Email",
        },
        password: {
          name: "password",
          label: "password",
          type: "password",
          placeholder: "Password",
        },
      },
      async authorize(credentials, req) {
        if (typeof credentials !== "undefined") {
          const res = await authenticate({
            email: credentials.email,
            password: credentials.password,
          });
          if (typeof res !== "undefined") {
            return { ...res };
          } else {
            return null;
          }
        } else {
          return null;
        }
      },
    }),
  ],

  session: { strategy: "jwt" },
  secret: process.env.NEXTAUTH_SECRET,
  callbacks: {
    async jwt({ token, user, account }: any) {
      if (user && account) {
        return {
          token: user?.token,
          accessTokenExpires: Date.now() + parseInt(user?.expiresIn, 10),
          refreshToken: user?.tokenRefresh,
        };
      }

      if (Date.now() 



<ol>
<li>
deleteArticle.tsx: データベースから特定の記事を削除する関数。
</li>
</ol>

<pre class="brush:php;toolbar:false">'use client';
import React from 'react';
import { SessionProvider } from "next-auth/react";
export default function Provider({
    children,
    session
}: {
    children: React.ReactNode,
    session: any
}): React.ReactNode {
    return (
        <sessionprovider session="{session}">
            {children}
        </sessionprovider>
    )
};

コンポーネント

次に、アプリケーション全体で使用される各コンポーネントを記述しましょう。

コンポーネント/Navbar.tsx

2 つのナビゲーション リンクを持つ単純なコンポーネント。

/*global.css*/
.container {
  max-width: 1100px;
  width: 100%;
  margin: 0px auto;
}

.image-container {
  position: relative;
  width: 100%;
  height: 5em;
  padding-top: 56.25%; /* Aspect ratio 16:9 (dividindo a altura pela largura) */
}

.image-container img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@keyframes spinner {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

.loading-spinner {
  width: 50px;
  height: 50px;
  border: 10px solid #f3f3f3;
  border-top: 10px solid #293d71;
  border-radius: 50%;
  animation: spinner 1.5s linear infinite;
}

コンポーネント/Loading.tsx

API 呼び出しが完了するのを待機している間に使用される、単純な読み込みコンポーネント。

import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Provider from "@/context/auth-provider";
import { getServerSession } from "next-auth";
import { authOptions } from "./api/auth/[...nextauth]/route";
const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Markdown Text Editor",
  description: "Created by ",
};

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const session = await getServerSession(authOptions);
  return (
    <provider session="{session}">
      
        {children}
      
    </provider>
  );
}

コンポーネント/ページネーション.tsx

プライベート ルートで、すべての記事を表示するページで使用されるページネーション コンポーネント。このコンポーネントの作成方法に関する詳細な記事は、こちらでご覧いただけます

npm i
npm run start

コンポーネント/ArticleCard.tsx

書かれた記事を表示するためのカードコンポーネント。

このコンポーネントには、記事表示ページと以前に書いた記事を編集するページの両方にアクセスするリンクも含まれています。

npx create-next-app myblog

コンポーネント/ArticleList.tsx

API 呼び出しを実行し、応答を表示するコンポーネント。

ここでは、作成した関数を通じて 2 つの API 呼び出しを使用します。

  1. getArticles.ts - コンポーネントに表示されるすべての記事を返します。
  2. RemoveArticle - リストとサーバーから特定の記事を削除します。

以前に作成した Pagination.tsx コンポーネントを使用して、記事の数をページ間で分割します。

npm i  markdown-it @types/markdown-it markdown-it-style github-markdown-css react-markdown

ページ

次に、それぞれのルートごとに分けて、各ページを見ていきます。

公開ページ

ログイン

これは私たちのアプリケーションのホームページです。シンプルなページなので、必要に応じて変更してください。このページでは、next-auth ナビゲーション ライブラリが提供するサインイン機能を使用します。

ファイル src/app/pages/public/login/page.tsx.

remark remark-gfm remark-react

記事ページ

記事閲覧ページを作成するために、動的ページを開発します。

あなたがアクセスしたことのあるすべてのブログ プラットフォームには、記事を読むための専用ページがあり、URL からアクセスできます。この理由は、動的なページ ルートです。幸いなことに、Next.js の新しい AppRouter メソッドでこれが簡単になり、私たちの生活がはるかにシンプルになりました。

最初に: [id] フォルダーを追加して、構造内にルートを作成する必要があります。これにより、pages/(public)/articles/[id]/pages.tsx.

という構造になります。
  • ID はナビゲーション ルートのスラッグに対応します。
  • params は、ナビゲーション スラッグを含むアプリケーションのツリーを介して渡されるプロパティです。
npm @codemirror/commands @codemirror/highlight @codemirror/lang-javascript @codemirror/lang-markdown @codemirror/language @codemirror/language-data @codemirror/state @codemirror/theme-one-dark @codemirror/view

2 番目: MarkdownIt ライブラリを使用して、ページに Markdown 形式でテキストを表示できるようにします。

npm i react-icons @types/react-icons

そして最後に、

ページの準備ができたら、たとえばブラウザで localhost:3000/articles/1 にアクセスすると、指定された ID で記事を表示できるようになります。

この場合、ArticleCards.tsx コンポーネントのいずれかをクリックすると、ID がナビゲーションを介して渡され、プライベート ルートのメイン ページにレンダリングされます。

src-
  |- app/
  |    |-(pages)/
  |    |      |- (private)/
  |    |      |       |- (home)
  |    |      |       |- editArticle/[id]
  |    |      |       |
  |    |      |       |- newArticle
  |    |      | - (public)/
  |    |              | - article/[id]
  |    |              | - login
  |    |
  |   api/
  |    |- auth/[...nextAuth]/route.ts
  |    |- global.css
  |    |- layout.tsx
  |
  | - components/
  | - context/
  | - interfaces/
  | - lib/
  | - services/
middleware.ts

プライベートページ

ここにプライベート ページがあります。このページには、ユーザーがアプリケーションで認証された場合にのみアクセスできます。

app/pages/ フォルダー内で、ファイルが () 内で宣言されている場合、ルートが /.

に対応することを意味します。

この場合、(ホーム) フォルダーはプライベート ルートのホームページを指します。これは、ユーザーがシステムに認証されたときに最初に表示されるページです。このページには、データベースの記事のリストが表示されます。

データは ArticlesList.tsx コンポーネントによって処理されます。このコードをまだ書いていない場合は、コンポーネントのセクションに戻って参照してください。

app/(pages)/(private)/(home)/page.tsx 内

npm i
npm run start

新しい記事

これは、記事を登録できるため、アプリケーションの最も重要なページの 1 つです。

このページにより、ユーザーは次のことが可能になります:

  1. Markdown 形式で記事を書きます。
  2. 記事に画像を割り当てます。
  3. サーバーに送信する前に、Markdown テキストをプレビューします。

このページでは複数の フック が使用されています:

  1. useCallback - 関数をメモ化するために使用されます。
  2. useState - コンポーネントに状態変数を追加できます。
  3. useSession - ユーザーが認証されているかどうかを確認し、認証トークンを取得します。

このために、2 つのコンポーネントを使用します:

  1. TextEditor.tsx: 以前に作成したテキスト エディター。
  2. Preview.tsx: Markdown 形式でファイルを表示するためのコンポーネント。

このページを構築する際、次の API を使用します:

  1. POST: 関数 postArticle を使用して、記事をサーバーに送信します。

また、next-auth ライブラリによって提供される useSession フックを使用して、サーバーに記事を登録するために使用されるユーザーの認証トークンを取得します。

これには 3 つの異なる API 呼び出しが含まれます。
app/pages/(private)/newArticle/page.tsx.

「クライアントを使用する」;
import React, { ChangeEvent, useCallback, useState } from "react";
import { useSession } から "next-auth/react";
import { リダイレクト } から "next/navigation";
"@/services/postArticle" から postArtical をインポートします。
import { AiOutlineFolderOpen } from "react-icons/ai";
import { RiImageEditLine } から "react-icons/ri";

「next/image」から画像をインポートします。
"@/components/textEditor" から TextEditor をインポートします。
"@/components/PreviewText" からプレビューをインポートします。
import { AiOutlineSend } から "react-icons/ai";
import { BsBodyText } から "react-icons/bs";

デフォルト関数をエクスポート NewArticle(params:any) {
  const { データ: セッション }: any = useSession({
    必須: true、
    onUnauthenticated() {
      リダイレクト("/ログイン");
    }、
  });
  const [imageUrl, setImageUrl] = useState<object>({});
  const [previewImage, setPreviewImage] = useState<string>("");
  const [previewText, setPreviewText] = useState<boolean>(false);
  const [title, setTitle] = useState<string>("");
  const [doc, setDoc] = useState<string>("# Escreva o seu texto... n");
  const handleDocChange = useCallback((newDoc: any) => {
    setDoc(newDoc);
  }、[]);

  (!session?.user) が null を返す場合。

  const handleArticleSubmit = async (e:any) => {
        e.preventDefault();
    const トークン: 文字列 = session.user.token;
    試す {
      const res = await postArtical({
        id: session.user.userId.toString()、
        トークン: トークン、
        画像 URL: 画像 URL、
        タイトル: "タイトル"、
        ドキュメント: ドキュメント、
      });
      console.log('re--->', res);
      リダイレクト('/成功');
    } キャッチ (エラー) {
      console.error('記事の送信エラー:', error);
      // 必要に応じてエラーを処理します
      エラーをスローします。
    }
  };

  const handleImageChange = (e: React.ChangeEvent<htmlinputelement>) => {
    if (e.target.files && e.target.files.length > 0) {
      const ファイル = e.target.files[0];
      const url = URL.createObjectURL(ファイル);
      setPreviewImage(url);
      setImageUrl(ファイル);
    }
  };

  const handleTextPreview = (e: any) => {
    e.preventDefault();
    setPreviewText(!previewText);
  };
  戻る (
    <section classname="w-full h-full min-h-screen相対 py-8">
      {プレビューテキスト && (
        <div classname="absolute right-16 top-5 p-5 border-2 border-slate-500 bg-slate-100rounded-xl w-full max-w-[33em] z-30">
           setPreviewText(!previewText)}
          />
        </div>
      )}

      <form classname="relative mx-auto max-w-[700px] h-full min-h-[90%] w-full p-2 border-2 border-slate-200rounded-md bg-slate-50ドロップシャドウ-xl フレックス フレックスコル ギャップ-2 ">
        {" "}
        <div classname="flex justify-between items-center">
          
            <bsbodytext></bsbodytext>
            プレビュー
          ボタン>{" "}
          
            エンビア テキスト
            <aioutlinesend classname="w-5 h-5 group-hover:text-red-500"></aioutlinesend>
          ボタン>
        </div>
        <div classname="header-wrapper flex flex-col gap-2 ">
          <div classname="画像ボックス">
            {previewImage.length === 0 && (
              <div classname="選択画像">
                
                  <aioutlinefolderopen classname="w-7 h-7"></aioutlinefolderopen>
                  ドラッグ アンド ドロップ画像
                ラベル>
                



<h4>
  
  
  記事を編集する
</h4>

<p><em>新しい記事</em> (newArticle) に似たページですが、いくつかの違いがあります。</p>

<p>まず、ナビゲーション パラメーターとして ID を受け取る動的ルートを定義します。これは、記事閲覧ページで行われたことと非常によく似ています。 <br>
app/(pages)/(private)/editArticle/[id]/page.tsx<br>
</p>
<pre class="brush:php;toolbar:false">「クライアントを使用する」;
import React, { useState, useEffect, useCallback, useRef, ChangeEvent } from "react";
import { useSession } から "next-auth/react";
import { リダイレクト } から "next/navigation";
'next/image' から画像をインポートします。

import { IArticle } から "@/interfaces/article.interface";
import { AiOutlineEdit } から "react-icons/ai";
import { BsBodyText } から "react-icons/bs";
import { AiOutlineFolderOpen } from "react-icons/ai";
import { RiImageEditLine } から "react-icons/ri";

"@/components/PreviewText" からプレビューをインポートします。
"@/components/textEditor" から TextEditor をインポートします。
import Loading from '@/components/Loading';
editArtical を "@/services/editArticle" からインポートします。

デフォルト関数をエクスポート EditArticle({ params }: { params: any }) {
 const { データ: セッション }: any = useSession({
    必須: true、
    onUnauthenticated() {
      リダイレクト("/ログイン");
    }、
  });
  定数 ID: 数値 = params.id;
  const [article, setArticle] = useState<iarticle null>(null);
  const [imageUrl, setImageUrl] = useState<object>({});
  const [previewImage, setPreviewImage] = useState<string>("");
  const [previewText, setPreviewText] = useState<boolean>(false)
  const [title, setTitle] = useState<string>("");
  const [doc, setDoc] = useState<string>('');
  const handleDocChange = useCallback((newDoc: any) => {
    setDoc(newDoc);
  }、[]);
  const inputRef= useRef<htmlinputelement>(null);

  const fetchArticle = async (id:number) => {
    試す {
      const 応答 = fetch を待ちます(
        `http://localhost:8080/articles/getById/${id}`、
      );
      const jsonData = 応答を待ちます.json();
      setArticle(jsonData);
    } キャッチ (エラー) {
      console.log("何か問題が発生しました:", err);
    }
  };
  useEffect(() => {
    if (記事 !== null || 記事 !== 未定義) {
      fetchArticle(id);
    }
  }, [id]);

  useEffect(()=>{
    if(記事 != null && 記事.コンテンツ){
        setDoc(article.content)
    }

    if(記事 !=null && 記事.画像){
      setPreviewImage(`http://localhost:8080/`article.image)
    }
  }、[記事])

  const handleArticleSubmit = async (e:any) => {
     e.preventDefault();
    const トークン: 文字列 = session.user.token;
    試す{
      const res = await editArtical({
      ID: ID、
      トークン: トークン、
      画像URL:画像URL、
      タイトル: タイトル、
      ドキュメント: ドキュメント、
      });
        console.log('re--->',res)
        応答を返します。
    キャッチ(エラー){
    console.log("エラー:", エラー)
    }
  };
  const handleImageClick = ()=>{
      console.log('hiii')
    if(inputRef.current){
      inputRef.current.click();
    }
  }const handleImageChange = (e: React.ChangeEvent<htmlinputelement>) => {
    if (e.target.files && e.target.files.length > 0) {
      const ファイル = e.target.files[0];
      const url = URL.createObjectURL(ファイル);
      setPreviewImage(url);
      setImageUrl(ファイル);
    }

  };
   const handleTextPreview = (e: any) => {
    e.preventDefault();
    setPreviewText(!previewText);
    console.log('プレビューからこんにちは!')
  };

  if(!article) return <loading></loading>
  if(記事?.コンテンツ)
  戻る (
    <section classname="w-full h-full min-h-screen相対 py-8">
      {プレビューテキスト && (
        <div classname="absolute right-16 top-5 p-5 border-2 border-slate-500 bg-slate-100rounded-xl w-full max-w-[33em] z-30">
           setPreviewText(!previewText)}
          />
        </div>
      )}

      <div classname="relative mx-auto max-w-[700px] h-full min-h-[90%] w-full p-2 border-2 border-slate-200rounded-md bg-whitedrop-シャドウ-MD フレックス フレックス-コル ギャップ-2">
        <form classname="relative mx-auto max-w-[700px] h-full min-h-[90%] w-full p-2 border-2 border-slate-200rounded-md bg-slate-50ドロップシャドウ-MD フレックス フレックスコル ギャップ-2 ">
          {" "}
          <div classname="flex justify-between items-center">
            
              <bsbodytext></bsbodytext>
              プレビュー
            ボタン>{" "}
            
                アーティゴを編集する 
              <aioutlineedit classname="w-5 h-5 group-hover:text-red-500"></aioutlineedit>
            ボタン>
          </div>
          <div classname="header-wrapper flex flex-col gap-2 ">
            <div classname="画像ボックス">
              {previewImage.length === 0 && (
                <div classname="select-image">
                  
                    <aioutlinefolderopen classname="w-7 h-7"></aioutlinefolderopen>;
                    ドラッグ アンド ドロップ画像
                  ラベル>
                  



<h2>
  
  
  結論
</h2>

<p>まず、このチュートリアルをお読みいただくために時間を割いていただきありがとうございます。また、このチュートリアルが完了したことをお祝いしたいと思います。ご参考になり、ステップごとの手順が分かりやすかったら幸いです。</p>

<p>次に、構築したものについていくつかの点を強調したいと思います。これはブログ システムの基礎であり、すべての記事を表示する公開ページ、ユーザー登録ページ、さらにはカスタム 404 エラー ページなど、追加すべき点はまだたくさんあります。チュートリアル中にこれらのページについて疑問に思って見逃した場合は、これが意図的なものであることを知ってください。このチュートリアルでは、これらの新しいページを自分で作成し、他のページを追加し、新機能を実装するための十分な経験を提供しました。</p>

<p>次回まで、本当にありがとうございました。 o/</p>


          </div>

            
        </div>
</div>
</form>
</div></section></htmlinputelement></htmlinputelement></string></string></boolean></string></object></iarticle>

以上がNext.js を使用した動的なブログ ダッシュボードの構築の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
JavaScriptのデータ型:ブラウザとNodejsに違いはありますか?JavaScriptのデータ型:ブラウザとNodejsに違いはありますか?May 14, 2025 am 12:15 AM

JavaScriptコアデータ型は、ブラウザとnode.jsで一貫していますが、余分なタイプとは異なる方法で処理されます。 1)グローバルオブジェクトはブラウザのウィンドウであり、node.jsのグローバルです2)バイナリデータの処理に使用されるNode.jsの一意のバッファオブジェクト。 3)パフォーマンスと時間の処理にも違いがあり、環境に従ってコードを調整する必要があります。

JavaScriptコメント://および / * *を使用するためのガイドJavaScriptコメント://および / * *を使用するためのガイドMay 13, 2025 pm 03:49 PM

javascriptusestwotypesofcomments:シングルライン(//)およびマルチライン(//)

Python vs. JavaScript:開発者の比較分析Python vs. JavaScript:開発者の比較分析May 09, 2025 am 12:22 AM

PythonとJavaScriptの主な違いは、タイプシステムとアプリケーションシナリオです。 1。Pythonは、科学的コンピューティングとデータ分析に適した動的タイプを使用します。 2。JavaScriptは弱いタイプを採用し、フロントエンドとフルスタックの開発で広く使用されています。この2つは、非同期プログラミングとパフォーマンスの最適化に独自の利点があり、選択する際にプロジェクトの要件に従って決定する必要があります。

Python vs. JavaScript:ジョブに適したツールを選択するPython vs. JavaScript:ジョブに適したツールを選択するMay 08, 2025 am 12:10 AM

PythonまたはJavaScriptを選択するかどうかは、プロジェクトの種類によって異なります。1)データサイエンスおよび自動化タスクのPythonを選択します。 2)フロントエンドとフルスタック開発のためにJavaScriptを選択します。 Pythonは、データ処理と自動化における強力なライブラリに好まれていますが、JavaScriptはWebインタラクションとフルスタック開発の利点に不可欠です。

PythonとJavaScript:それぞれの強みを理解するPythonとJavaScript:それぞれの強みを理解するMay 06, 2025 am 12:15 AM

PythonとJavaScriptにはそれぞれ独自の利点があり、選択はプロジェクトのニーズと個人的な好みに依存します。 1. Pythonは、データサイエンスやバックエンド開発に適した簡潔な構文を備えた学習が簡単ですが、実行速度が遅くなっています。 2。JavaScriptはフロントエンド開発のいたるところにあり、強力な非同期プログラミング機能を備えています。 node.jsはフルスタックの開発に適していますが、構文は複雑でエラーが発生しやすい場合があります。

JavaScriptのコア:CまたはCの上に構築されていますか?JavaScriptのコア:CまたはCの上に構築されていますか?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc;それは、解釈されていることを解釈しました。

JavaScriptアプリケーション:フロントエンドからバックエンドまでJavaScriptアプリケーション:フロントエンドからバックエンドまでMay 04, 2025 am 12:12 AM

JavaScriptは、フロントエンドおよびバックエンド開発に使用できます。フロントエンドは、DOM操作を介してユーザーエクスペリエンスを強化し、バックエンドはnode.jsを介してサーバータスクを処理することを処理します。 1.フロントエンドの例:Webページテキストのコンテンツを変更します。 2。バックエンドの例:node.jsサーバーを作成します。

Python vs. Javascript:どの言語を学ぶべきですか?Python vs. Javascript:どの言語を学ぶべきですか?May 03, 2025 am 12:10 AM

PythonまたはJavaScriptの選択は、キャリア開発、学習曲線、エコシステムに基づいている必要があります。1)キャリア開発:Pythonはデータサイエンスとバックエンド開発に適していますが、JavaScriptはフロントエンドおよびフルスタック開発に適しています。 2)学習曲線:Python構文は簡潔で初心者に適しています。 JavaScriptの構文は柔軟です。 3)エコシステム:Pythonには豊富な科学コンピューティングライブラリがあり、JavaScriptには強力なフロントエンドフレームワークがあります。

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

ホットツール

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。

DVWA

DVWA

Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

Dreamweaver Mac版

Dreamweaver Mac版

ビジュアル Web 開発ツール

EditPlus 中国語クラック版

EditPlus 中国語クラック版

サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません