next.js入门之路-1
从创建一个next.js项目开始
关于node环境,笔者在最初开发时,next.js处于v13版本,还可以使用node16来进行开发,在开发途中,官网发布了v14版本,发现只是一些性能上的优化,所以毫不犹豫便直接升级到了v14,注意v14版本要求node18及以上,所以目前开发环境为nodev18
使用create-next-app
来创建项目
pnpm create next-app
通过pnpm run dev
来启动我们的项目
通过pnpm run build
来打包我们的项目
next.js专门提供可以让我们在本地来查看生产环境的运行情况,在打包完成之后通过
pnpm run start
来启动生产环境下的next.js应用程序
关于静态资源
./public/
被映射到/
关于app与~~pages~~
本系列暂不记录
pages
模式的相关文件系统
app模式下,以文件的目录作为route的规则
关于Layout与Template
layout与page
layout
会在page跳转的时候保持本身的状态,但是这个也分情况
如果跳转之后存在根布局root layout
,从一个page跳转到另一个page,会导致整个页面加载
如果page的页面只存在“普通”的layout,那么这个layout会是根布局的children Node
关于根布局:根布局是根目录中最顶层的布局
1.存在着这两个HTML标签
<html><body>
2.app目录下,必须存在着一个根布局:
app/layout.*sx
测试一下layout与page的层级以及root layout 与 normal layout
的区别
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
console.log('我是根路由,我被执行了');
return (
<html lang='en'>
<body>
我是根布局{children}//注意这里,后面要说到
</body>
</html>
);
}
// app/page.tsx
import MainLayout from './components/MainLayout';
export default function Home() {
console.log('我是app下的page,我被执行了');
return (
<div style={{ backgroundColor: '#ff00ff' }}>
我是app下的page
<MainLayout />
</div>
);
}
// app/components/MainLayout
export default function HomeLayout() {
console.log('我是app下page的子组件,我被执行了');
return <div style={{ height: '200px', width: '200px', backgroundColor: '#ff0f0f' }}>我是homeLayout</div>;
}
为了再测试一下根布局与”普通“布局的区别,我们增加一个test route
// app/test/layout.tsx
export default async function RootLayout({ children }: { children: React.ReactNode }) {
console.log('我是test的(根?)layout,我被执行了');
return (
<html lang='en'>
<body>我是test的根layout{children}</body>
</html>
);
// return (
// <>
// <>我是test的layout{children}</>
// </>
// );
}
// app/test/page.tsx
export default function Home() {
console.log('我是test的page,我被执行了');
return <header>我是test的page</header>;
}
结果
我们来看一下日志的打印顺序
渲染顺序是当前page的page->layout->其他
再来看一下渲染出来的布局
可以看到大致上是这样的一个组成
const render =()=>{
return (<rootLayout>
<page>
<HomeLayout></HomeLayout>
</page>
</rootLayout>)
}
在我们跳转test
的时候,我们来看一下日志的打印顺序,也是当前page的page->layout->其他
这里注意到根路由(
app/layout.tsx
)的日志也被打印了,我理解为 由于app模式下是以文件目录为route规则,那么app/layout
对应的文件目录是/
,应该是作为”顶级“布局,在加载其他布局的时候,这个布局都应该会被渲染,然后在多根组件都存在的情况下会进行“合并”,下面会单独说一下这个“合并”,最后渲染出来的就是合并之后的布局谈到这里,突发奇想,如果在纯
.html
文件中存在这样的层级<html><body>1<html><body>2</body><html></body><html>
最后会被解析器解析成什么样子呢
而让我们看下test的渲染结果,会有一个错,这个错误的原因是由于服务端渲染和客户端渲染出来的结果不一致,这个错,在上面“合并”的基础上很好理解,其原因是在app/layout.tsx
中我们在body里面增加了除{children}
以外的其他东西,并不是说body
里面只能有{chidlren}
,只是说渲染的结果不同,/
对应的渲染内容为我是根布局{children}
,而test
页面的渲染内容为我是test的根layout{children}
合并的时候渲染的内容出现冲突,所以导致错误的出现
那如果我们给
app/layout.tsx
中只给出{children}
,而其他根路由增加一些额外的内容,这样还会报错吗,答案不是肯定的,分两种情况当前page(例如当前路由是/test,则对应的layout就是test目录下的的layout)所对应的layout可以增加额外的内容
当前page的父级以及父级往上的根布局不能增加与当前page对应layout增加的内容不同的东西
简单点来说就只能在最底层(叶子节点)的layout的
body
中增加额外的内容这里的父级可以用文件目录来对应
最后所渲染的全部根布局会合并在一起,然后展示
关于多个根组件渲染总结
多个根组件渲染会进行合并,当前page的layout的属性会被后执行的layout中相同的属性覆盖,执行顺序可以自行打印日志,例如
//当前page对应的layout
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html
lang='en'
suppressHydrationWarning={true}
data-c='333'
data-a='1'
>
<body>{children}</body>
</html>
);
}
//当前page对应的layout的父layout
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html
lang='en'
suppressHydrationWarning={true}
data-b='1'
data-c='222'>
<body>{children}</body>
</html>
);
}
最后的结果是
好了,其实说了这么多,官网推荐的是单根布局,也就是有一个根布局就行了,其余的整普通布局就行(甚至可以只有page.tsx
都行),前面的内容留个印象就行,记住多根布局会有坑就行
普通布局(没有html、body)
经过前面的内容,这个就好理解了,按照目录的层级来理解就行
template
template
类似于layout
,但它与layout
有一点不同的是他在跳转路由的时候会为每个子级创建新的实例
目前我还没用到,这个是非必须的,看开发需求了
由于篇幅,app模式的剩下部分会在下一篇继续
参考链接
File Conventions: layout.js | Next.js (nextjs.org)