lama dev beginners
1.安装
bash
npx create-next-app@latest
jsconfig.json
json
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
2.基本页面
error.jsx必须是“use client"
2.1 获取当前页面的路径
jsx
import { usePathname } from "next/navigation";
const pathName = usePathname();
2.2 使用第三方的图片
next.config.js
js
export const images = {
domains: ["www.example.com"],
};
js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "images.pexels.com",
},
{
protocol: "https",
hostname: "lh3.googleusercontent.com",
},
],
},
};
module.exports = nextConfig;
2.3 获取当前页面的参数
jsx
import { useRouter } from "next/router";
const router = useRouter();
const { id } = router.query;
2.4 获取当前页面的参数
js
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "images.pexels.com",
},
],
},
};
module.exports = nextConfig;
2.5 使用@/路径
jsconfig.json或tsconfig.json
json
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
2.6 使用Image
css中object-fit的使用
- fill: 默认值,图片被拉伸填充整个容器,不保持原始比例。
- contain: 图片保持原始比例,同时缩放以适应容器,可能留有空白区域。
- cover: 图片保持原始比例,同时缩放以覆盖整个容器,可能部分图片会被裁剪。
- none: 图片保持原始比例,不缩放,可能超出容器。
- scale-down: 图片保持原始比例,如果原始比例小于容器比例,则缩放以适应容器,否则保持原始比例。
jsx
<div className={styles.imgContainer}>
<Image src="/about.png" alt="About Image" fill className={styles.img} />
</div>
css
.imgContainer {
flex: 1;
position: relative;
}
.img {
object-fit: contain;
}
2.7 把彩色图片变灰色
css
.brands {
filter: grayscale(1);
}
2.8 使用服务器渲染时
- 不能用use hooks
- 不能用onClick等事件
2.9 处理hydration问题
jsx
import dynamic from "next/dynamic";
const HydrationTestNoSSR = dynamic(() => import("@/components/HydrationTest"), { ssr: false });
<HydrationTestNoSSR />
jsx
<div suppressHydrationWarning>{a}</div>
2.10 当我用客户端组件包装服务器端组件时仍然是服务器端组件
3. Navigation
/navigationTest/page.jsx
jsx
import { usePathname, useRouter, useSearchParams } from "next/navigation";
const router = useRouter(); // 获取router,可以用来跳转页面back,forward,push,refresh
const pathname = usePathname(); // '/navigationTest'
const query = useSearchParams(); // 获取url中的参数
router.push("/"); // 会留下历史记录
router.replace("/"); // 不会留下历史记录
router.refresh("/"); // 会重新请求页面
router.back("/"); // 会返回上一个页面
/app/blog/[slug]/page.jsx
jsx
const SinglePostPage = ({ params, searchParams }) => {
console.log(params); // { slug: 'my-post' }
console.log(searchParams); // ?page=2 // { page: '2' }
}
4. fake api
jsx
const getData = async (slug) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${slug}`);
if (!res.ok) {
throw new Error("Something went wrong");
}
console.log(res);
return res.json();
};
5. fake data
js
// TEMPORARY DATA
const users = [
{ id: 1, name: "John" },
{ id: 2, name: "Jane" },
];
const posts = [
{ id: 1, userId: 1, title: "Post 1", body: "This is post 1" },
{ id: 2, userId: 1, title: "Post 2", body: "This is post 2" },
{ id: 3, userId: 2, title: "Post 3", body: "This is post 3" },
{ id: 4, userId: 2, title: "Post 4", body: "This is post 4" },
];
export const getPosts = async () => {
return posts;
};
export const getPost = async (id) => {
return posts.find((post) => post.id === +id);
};
export const getUser = async (id) => {
return users.find((user) => user.id === +id);
};
6. mongodb
.env
bash
MONGO = mongodb+srv://bolepan21:admin8888@neopan21dev.npgletw.mongodb.net/next14tutorial?retryWrites=true&w=majority&appName=neopan21dev
/src/lib/utils.js
js
const { default: mongoose } = require("mongoose");
const connection = {};
export const connectToDb = async () => {
try {
if (!connection.isConnected) {
console.log("Using existing connection");
return;
}
const db = await mongoose.connect(process.env.MONGO);
connection.isConnected = db.connections[0].readyState;
} catch (error) {
throw new Error(error);
}
};
使用在线数据库https://cloud.mongodb.com/v2/660ebc1e1ae3c10ec4726537#/overview
- Database Access-> Add New Database User
- Network Access -> Add Ip Address -> 0.0.0.0/0 任何人都可以访问
- Database -> Create -> next14tutorial
7. meta title description
tsx
export const metadata = {
title: "Next App Title",
description: "Next.js starter app Description",
};
/layout.js
tsx
export const metadata = {
title: {
default: "Next.js 14 Homepage",
template: "%s | Next.js 14",
},
description: "Next.js starter app Description",
};
/blog/first
tsx
export const generateMetadata = async ({ params }) => {
const { slug } = params;
const post = await getPost(slug);
return {
title: post.title,
description: post.desc,
};
};
8. Server Actions
jsx
const ServerActionTestPage = () => {
return (
<div>
<form action={addPost}>
<input type="text" placeholder="title" value="title" name="title" />
<input type="text" placeholder="desc" value="desc" name="desc" />
<input type="text" placeholder="slug" value="slug" name="slug" />
<input type="text" placeholder="userId" value="userId" name="userId" />
<button>Create</button>
</form>
<form action={deletePost}>
<input type="text" placeholder="postId" name="id" />
<button>Delete</button>
</form>
</div>
);
};
jsx
"use server";
import { revalidatePath } from "next/cache";
import { Post } from "./models";
import { connectToDb } from "./utils";
export const addPost = async (formData) => {
const { title, desc, slug, userId } = Object.fromEntries(formData);
try {
connectToDb();
const newPost = new Post({ title, desc, slug, userId });
await newPost.save();
console.log("saved to db");
revalidatePath("/blog");
} catch (err) {
console.log(err);
return { error: "Something went wrong" };
}
};
export const deletePost = async (formData) => {
const { id } = Object.fromEntries(formData);
try {
connectToDb();
await Post.findByIdAndDelete(id);
console.log("deleted from db");
revalidatePath("/blog");
} catch (err) {
console.log(err);
return { error: "Something went wrong" };
}
};
9. Api router
创建
9.1 /app/api/blog/route.js
js
import { Post } from "@/lib/models";
import { connectToDb } from "@/lib/utils";
import { NextResponse } from "next/server";
export const GET = async (request) => {
try {
connectToDb();
const posts = await Post.find();
return NextResponse.json(posts);
} catch (err) {
console.log(err);
throw new Error("Failed to fetch posts!");
}
};
jsx
// FETCH DATA WITH AN API
const getData = async (slug) => {
const res = await fetch(`http://localhost:3000/api/blog/${slug}`);
if (!res.ok) {
throw new Error("Something went wrong");
}
console.log(res);
return res.json();
};
9.2 /app/api/blog/[slug]/route.js
js
import { Post } from "@/lib/models";
import { connectToDb } from "@/lib/utils";
import { NextResponse } from "next/server";
export const GET = async (request, { params }) => {
const { slug } = params;
try {
connectToDb();
const post = await Post.findOne({ slug });
return NextResponse.json(post);
} catch (err) {
console.log(err);
throw new Error("Failed to fetch post!");
}
};
export const DELETE = async (request, { params }) => {
const { slug } = params;
try {
connectToDb();
await Post.deleteOne({ slug });
return NextResponse.json("Post deleted");
} catch (err) {
console.log(err);
throw new Error("Failed to delete post!");
}
};
10 Authentication
- https://authjs.dev/getting-started/installation?framework=Next.js
- url:https://authjs.dev/getting-started/authentication/oauth
- client,server
client
jsx
"use client";
import { signIn } from "next-auth/react";
import React from "react";
const LoginPage = () => {
return (
<div>
<button onClick={() => signIn("google", { callbackUrl: "/my-account" })}>Login with Google</button>
<button onClick={() => signIn("github", { callbackUrl: "/my-account" })}>Login with Github</button>
</div>
);
};
export default LoginPage;
server
jsx
import { auth, signIn } from "@/auth/authSetup";
const LoginPage = async () => {
const session = await auth();
console.log("session", session);
const handleGithubLogin = async () => {
"use server";
console.log("signIn", await signIn());
await signIn("github");
};
return (
<div>
<form action={handleGithubLogin}>
<button>Login with Github</button>
</form>
{/* <form
action={async () => {
"use server";
await signIn("google");
}}
>
<button type="submit">Signin with Google</button>
</form> */}
</div>
);
};
export default LoginPage;
auth/authSetup.js
js
import NextAuth from "next-auth";
import Google from "next-auth/providers/google";
import Github from "next-auth/providers/github";
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [Google, Github],
callbacks: {
authorized: async ({ auth }) => {
// Logged in users are authenticated, otherwise redirect to login page
return !!auth;
},
},
pages: {
signIn: "/login",
},
});
.env.local
bash
MONGO = mongodb+srv://lamadev:lamadev@lamadev.p3umv.mongodb.net/next14tutorial?retryWrites=true&w=majority&appName=lamadev
AUTH_SECRET="02h****" # Added by `npx auth`. Read more: https://cli.authjs.dev
AUTH_URL = http://localhost:3000/api/auth
AUTH_GITHUB_ID="*****"
AUTH_GITHUB_SECRET="****"
AUTH_GOOGLE_ID="****"
AUTH_GOOGLE_SECRET="****"