【Nextjs】搭建react+nextjs+tailwindcss+typescript项目
我这里安装的是15版本,根据提示选择需要的库(y)... my-app... No /» ESLint... No /... No /... No /... @/*安装成功后,基于求稳心态,我将react版本降到了18版本运行需要注意的是,我选择的是App Router 模式,因此页面都是在app目录下创建的,而访问路由就是app目录下的文件夹名,比如创建了app/home/page.tsx,那么
命令行搭建
npx create-next-app@latest
我这里安装的是15版本,根据提示选择需要的库
Ok to proceed? (y) y
√ What is your project named? ... my-app
√ Would you like to use TypeScript? ... No / Yes
√ Which linter would you like to use? » ESLint
√ Would you like to use Tailwind CSS? ... No / Yes
√ Would you like your code inside a `src/` directory? ... No / Yes
√ Would you like to use App Router? (recommended) ... No / Yes
√ Would you like to use Turbopack? (recommended) ... No / Yes
√ Would you like to customize the import alias (`@/*` by default)? ... No / Yes
√ What import alias would you like configured? ... @/*安装成功后,基于求稳心态,我将react版本降到了18版本
npm install -D @types/react@18 @types/react-dom@18
npm install -S react@18 react-dom@18
运行
npm run dev
需要注意的是,我选择的是App Router 模式,因此页面都是在app目录下创建的,而访问路由就是app目录下的文件夹名,比如创建了app/home/page.tsx,那么访问路由就是 "/home"
并且创建的路由文件夹需要小写开头,遵循统一规范原则
配置国际化
新建il8n目录:
// src\i18n\navigation.ts
// 路由配置
import { createNavigation } from "next-intl/navigation";
import { routing } from "./routing";
// Next.js导航的轻量级封装
// 考虑路由配置的API
export const { Link, redirect, usePathname, useRouter, getPathname } =
createNavigation(routing);
// src\i18n\request.ts
import { getRequestConfig } from 'next-intl/server';
import { hasLocale } from "next-intl";
import { routing } from './routing';
// 服务端获取文案配置
export default getRequestConfig(async ({ requestLocale }) => {
const requested = await requestLocale;
const locale = hasLocale(routing.locales, requested)
? requested
: routing.defaultLocale;
// 动态导入对应语言的文案(Next.js 会自动按需加载)
const messages = (await import(`./messages/${requested}.json`)).default;
return {
locale,
messages,
};
});
// src\i18n\routing.ts
import { defineRouting } from "next-intl/routing";
export const routing = defineRouting({
locales: ["en", "zh"],
defaultLocale: "zh",
});
新建il8n/messages目录:src\i18n\messages\en.json | src\i18n\messages\zh.json
{
"Home": {
"title": "Home",
"subtitle": "Welcome to Next.js App Router i18n",
"button": "Switch Language"
}
}
{
"Home": {
"title": "首页",
"subtitle": "欢迎使用 Next.js App Router 国际化",
"button": "切换语言"
}
}
// next.config.ts
import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";
// 指向配置文件
const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
const nextConfig: NextConfig = {
// // 输出独立构建
// output: 'standalone',
// // 启用压缩
// compress: true,
// experimental: {
// // 启用服务端操作
// serverActions: {
// // 服务端操作请求体大小限制
// bodySizeLimit: "50mb",
// },
// // 优化包大小导入
// optimizePackageImports: [
// "@next/font",
// "@next/bundle-analyzer",
// "next-intl",
// "next-intl/server",
// "next-intl/routing",
// "next-intl/navigation",
// ],
// },
// typescript: {
// // 在构建时忽略类型错误
// ignoreBuildErrors: true,
// },
// eslint: {
// // 忽略 ESLint 错误
// ignoreDuringBuilds: true,
// },
// images: {
// // 允许 Next.js 访问本地图片
// remotePatterns: [
// {
// protocol: "http",
// hostname: "localhost",
// },
// ],
// },
};
export default withNextIntl(nextConfig);
中间件文件为访问根目录public下的文件,调整正则:
// src\middleware.ts
import createIntlMiddleware from 'next-intl/middleware';
import { NextRequest } from 'next/server';
import { routing } from './i18n/routing';
// 创建国际化中间件
const intlMiddleware = createIntlMiddleware(routing);
// 匹配需要拦截的路由(App Router 规则)
export const config = {
matcher: [
// 规则解读:
// 1. 匹配所有路径,但排除:
// - /api/*:接口请求
// - /_next/*:Next.js 内置静态资源
// - /favicon.ico:网站图标
// - 以 . 结尾的路径(如 /logo.png、/style.css):public 下的静态资源
// - /images/*、/assets/* 等自定义静态资源目录(按需添加)
'/((?!api|_next/static|_next/image|favicon.ico|.*\\..*|images|assets).*)'
]
};
// 自定义中间件逻辑
export default function middleware(request: NextRequest) {
// 执行 next-intl 核心逻辑
return intlMiddleware(request);
}
新建变量目录
// src\app\layout.tsx
import { ReactNode } from 'react';
import './globals.css'
export default function RootLayout({ children }: { children: ReactNode }) {
return children;
}
// src/app/[locale]/layout.tsx
import { ReactNode } from 'react';
import { NextIntlClientProvider, useLocale, useMessages } from 'next-intl';
import { getTranslations } from 'next-intl/server';
import { Metadata } from 'next';
import { routing } from '@/i18n/routing';
type Props = {
children: ReactNode;
params: { locale: string };
};
// 生成动态元数据
export async function generateMetadata({ params: paramsPromise }: { params: { locale: string } }): Promise<Metadata> {
const params = await paramsPromise;
// 服务端获取翻译函数
const t = await getTranslations({
locale: params.locale,
namespace: 'Home',
});
return {
title: t('title'),
description: t('subtitle')
};
}
// 生成所有语言的静态路由
export async function generateStaticParams() {
return routing.locales.map((locale) => ({ locale }));
}
export default function LocaleLayout({ children }: Props) {
const locale = useLocale();
const messages = useMessages();
return (
<html lang={locale}>
<body>
{/* 为客户端组件提供国际化上下文 */}
<NextIntlClientProvider locale={locale} messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
// src/app/[locale]/page.tsx(首页服务器组件)
import { useTranslations } from 'next-intl';
export default function HomePage({ params }: { params: { locale: string } }) {
// 初始化翻译函数(指定命名空间)
const t = useTranslations('Home');
return (
<div>
<h1>{t('title')}</h1>
<p>{t('subtitle')}</p>
{/* 语言切换按钮(后续实现) */}
<button>{t('button')}</button>
</div>
);
}
删除文件
src\app\page.tsx
按需安装shadcn组件
npx shadcn@latest add button
问题集锦
问题1:
Event handlers cannot be passed to Client Component props.<div className=... onClick={function onClick} children=...> ^^^^^^^^^^^^^^^^^^ If you need interactivity, consider converting part of this to a Client Component.
问题2:window is not defined
解决办法:
这个错误表明你正在尝试将事件处理器传递给一个客户端组件,但是当前的组件没有被标记为客户端组件。在 Next.js App Router 中,你需要显式声明哪些组件是客户端组件。
以下是解决这个问题的方法:
在文件顶部添加
"use client"指令,将报错的这个页面组件转换为客户端组件
项目中遇到的其他问题:
1、'use client' 页面引入了 'server-only' 函数
解决办法:去掉'use client',并将页面导出改成异步函数导出
2、在报错页面提示:useSearchParams() should be wrapped in a suspense boundary at page
解决办法:使用 useSearchParams() 的组件必须被 Suspense 包裹。将使用 useSearchParams() 的逻辑提取到内部组件,并用 Suspense 包裹。
3、本地访问慢,缓存未生效。
命令行加入指定值 next dev --turbopack
替换正则
将 onClick="printSpec()" onClick="exportSpec(3)" 改成 onClick={() => { // printSpec() }} onClick={() => { // exportSpec(3) }}
// 搜索框(精准匹配函数调用)
\bonClick="([^"]+)"
// 替换框(生成带注释的 JSX 函数)
onClick={() => {\n // $1\n}}
正则匹配 rows="3", rows="4", 将其替换成 rows={3} rows={4}
// 搜索
rows(\s*)=(\s*)"(\d+)"
// 替换
rows$1=$2{$3}
vscode 搜索正则匹配input ,就为了在结尾加个 />
// 搜索
<input(?![^>]*/>)([^>]*)>
// 替换
<input$1 />
更多推荐




所有评论(0)