原文:Next.js 13 项目中的多布局使用方法,你应该要会 - 掘金
一、需求讲解
在项目编写过程中,有可能会遇到这样的需求,在/app/demo01
中需要有侧边栏,在同级目录下/app/demo02
中不需要侧边栏,或者不需要侧边栏需要头部,这样的需求,我们就不能单单的靠一个Layout文件来使用。
1.1、/app/demo01
(有侧边栏,没有header)
1.2 /app/demo02
(没有侧边栏,有header)
1.3、/app/demo03
(有侧边栏,有header)
二、实现讲解
注意📢:本文主要以NextJs为主,所以讲解的是NextJs的实现
2.1、NextJs12的实现方式
本文主要以NextJs13
为主,所以NextJs12
只是略微带过。
首先在NextJs12以前,我们可以实现类似的需求,但是相对来说比较麻烦,因为是嵌套的,相当于你需要侧边栏,那么被侧边栏的Layout包裹的所有页面都会有有这个侧边栏,如果某个页面不需要这个侧边栏只能通过js去控制侧边栏的显示。
大概得实现方式:
在Layout组件中,同时写<Asider/>
和<Header/>
,通过监听路由的变化去指定显示,例如指定/app/demo01
只显示<Asider/>
NextJs12简单实现如下
typescript
复制代码
import { useRouter } from 'next/router' import React, { memo, useEffect, useMemo, useState } from 'react' const AsiderAndHeaderLayout = ({ children }: { children: React.ReactNode }) => { const router = useRouter() const [isShowAside, setIsShowAside] = useState<boolean>(true) const [isShowHeader, setIsShowHeader] = useState<boolean>(true) const asidePaths = ["/app/demo01"] const headerPaths = ["/app/demo02"] const asiderAndHeaderPaths = ["/app/demo03"] useEffect(() => { const path = router.pathname; if (asidePaths.includes(path)) { setIsShowAside(true) } else if (headerPaths.includes(path)) { setIsShowHeader(true) } else if (asiderAndHeaderPaths.includes(path)) { setIsShowAside(true) setIsShowHeader(true) } }, [router]) const ContainerHeight = useMemo(() => { return isShowHeader ? "h-[calc(100vh-60px)]" : "h-screen" }, [isShowHeader]) return ( <div className='w-full h-screen flex flex-col'> {isShowHeader && <div className='w-full h-[60px] bg-green-500'>Header</div>} <div className={`w-full flex ${ContainerHeight}`}> {isShowAside && <div className='flex-[320px_0_0] bg-blue-300'>Aside</div>} <div className='flex-1'>{children}</div> </div> </div> ) } export default memo(AsiderAndHeaderLayout)
2.2、NextJs13的实现方式
在使用NextJs13
的方法之前,我们需要先了解NextJs13
的两个新特性
2.2.1、在NextJs13
的App目录下,每个页面都有一个Layout.tsx
的布局文件,但是不需要每个页面都设置,如果页面文件夹下有Layout.tsx
文件才会走页面的Layout
布局
但是如图所示,如果页面的上一级设置了Layout
布局,则会在上一次布局的基础上再嵌套一个Layout
布局
2.2.2、在NextJs13
中支持在App目录下创建(name)
以括号的形式命名的文件目录,该文件不会作为路由
如图所示
当前目录下共有三个页面/
、/demo03
、/demo04
,所以在以括号括起来的文件夹将不会渲染为路由,相当于下图的路径
回到正题,我们熟悉了上面两个新特性后,这样我们就会明白其实需要不同的Layout
只需要有不同的带()
的文件夹而已,带括号的文件夹虽然不会被渲染成路由,但是同样也是拥有和页面一样的目录组成,可以给()
文件夹单独设置layout.tsx
和loading.tsx
以及error.tsx
文件夹,这样在()
号目录文件夹下的全部具有就会有layout
布局。
即我们需要实现开头说的需求,只需要设置不同的带()
的文件夹即可实现
代码实现如下
目录结构
scss
复制代码
丨-app 丨--(asider) ---只显示侧边栏布局 丨----layout.tsx 丨----demo01 丨——————page.tsx 丨--(header)---只显示Header布局 丨----layout.tsx 丨----demo02 丨------page.tsx 丨--(asiderAndHeader)---显示侧边栏布局和Header布局 丨----layout.tsx 丨----demo03 丨------page.tsx
typescript
复制代码
// (asider)的Layout.tsx import React from 'react' const layout = ({ children }: { children: React.ReactNode }) => { return ( <div className='w-full h-screen flex'> <div className='flex-[327px_0_0] bg-gray-500 text-white'>aside</div> <div className='flex-1'> {children} </div> </div> ) } export default layout
typescript
复制代码
// (header)的Layout.tsx import React from 'react' const HeaderLayout = ({ children }: { children: React.ReactNode }) => { return ( <div className='w-full h-screen flex flex-col'> <div className='flex-[70px_0_0] bg-gray-500 text-white'>Header</div> <div className='flex-1'>{children}</div> </div> ) } export default HeaderLayout
typescript
复制代码
// (asiderAndHeader)的Layout.tsx import React, { memo } from 'react' const AsiderAndHeaderLayout = ({children}:{children:React.ReactNode}) => { return ( <div className='w-full h-screen flex flex-col'> <div className='w-full h-[60px] bg-green-500'>Header</div> <div className='w-full h-[calc(100vh-60px)] flex'> <div className='flex-[320px_0_0] bg-orange-300'>Aside</div> <div className='flex-1'>{children}</div> </div> </div> ) } export default memo(AsiderAndHeaderLayout)
总结
通过上面的代码,我们就可以很容易的实现需要Aside
或者需要Header
或者需要Asider
和Header
的布局效果。当让这也只是我的一己之见,我也是相当于初接触NextJs13
的App全新布局,有什么地方有问题或者有其他的方案的,欢迎大家评论区讨论。
本文使用的代码已上传到github,地址github.com/izz520/Next…
我是YaSol,一个区块链行业的前端开发小菜鸡。