JS實(shí)現(xiàn)面包屑導(dǎo)航功能從零開始示例
面包屑導(dǎo)航
url:domain/path/[[…path]]
目前可以通過(guò) url 的的結(jié)構(gòu)信息定位當(dāng)前目錄的情況,但是無(wú)法快速的前進(jìn)與后退。所以使用“面包屑”來(lái)完成這一功能
功能實(shí)現(xiàn)
安裝依賴
cd /explorer pnpm i axios ahooks
文件樹
explorer/src/app/path/[[...path]]/layout.tsx
explorer/src/app/path/api/readdir/route.ts
explorer/src/components/breadcrumb-dropdown-readdir/index.tsx
explorer/src/components/explorer-breadcrumb/index.tsx
explorer/src/components/use-replace-pathname.ts
文件路徑:explorer/src/app/path/api/readdir/route.ts
新增一個(gè)獲取指定目錄的接口
import { NextRequest, NextResponse } from 'next/server' import { readdir } from '@/explorer-manager/src/main.mjs' export type SearchParamsType = { path: string only_dir: '0' | '1' only_file: '0' | '1' has_file_stat: '0' | '1' } export const GET = async (req: NextRequest) => { const { path, only_dir, only_file, has_file_stat } = Object.fromEntries(req.nextUrl.searchParams) as SearchParamsType return NextResponse.json({ readdir: readdir(path, { only_dir, only_file, has_file_stat }) }) }
文件路徑:explorer/src/app/path/[[...path]]/layout.tsx
將面包屑組件添加至 layout 的 Card 組件 title 部分。
import React from 'react' import { readdir } from '@/explorer-manager/src/main.mjs' import { PathContextProvider } from '@/app/path/context' import { Card } from 'antd' import ExplorerBreadcrumb from '@/components/explorer-breadcrumb' const Layout: React.FC<React.PropsWithChildren & { params: { path: string[] } }> = ({ children, params: { path = [] }, }) => { const readdirList = readdir(path.join('/'), { only_dir: '0', only_file: '0', has_file_stat: '1' }) return ( <PathContextProvider value={readdirList}> <Card title={<ExplorerBreadcrumb />}>{children}</Card> </PathContextProvider> ) } export default Layout
文件路徑:explorer/src/components/explorer-breadcrumb/index.tsx
面包屑組件,使用 Antd 的 Breadcrumb 組件。跟目錄"/"使用 home icon 替代
'use client' import React from 'react' import Link from 'next/link' import { HomeOutlined } from '@ant-design/icons' import { usePathname } from 'next/navigation' import { Breadcrumb, Space } from 'antd' import BreadcrumbDropdownReaddir from '@/components/breadcrumb-dropdown-readdir' type ItemType = { title: string; href: string } const ExplorerBreadcrumb: React.FC = () => { const pathname = usePathname() || '' return ( <Space> <Breadcrumb items={decodeURIComponent(pathname) .split('/') .filter(Boolean) .reduce<ItemType[]>((p, c) => { const last = p[p.length - 1] return p.concat({ title: c, href: [last?.href, c].join('/') }) }, []) .map(({ title, href }, k, { length }) => { return { title: length === k + 1 && length !== 1 ? ( title ) : ( <BreadcrumbDropdownReaddir title={title} href={href}> <Link href={href}>{k === 0 ? <HomeOutlined /> : title}</Link> </BreadcrumbDropdownReaddir> ), } })} /> </Space> ) } export default ExplorerBreadcrumb
文件路徑:explorer/src/components/breadcrumb-dropdown-readdir/index.tsx
鼠標(biāo) hover 某個(gè)路徑時(shí),下拉出現(xiàn)當(dāng)前路徑下文件夾菜單,方便快速跳轉(zhuǎn)。
當(dāng)菜單超出最大高度時(shí)滾動(dòng)顯示。會(huì)高亮當(dāng)前路徑上的文件。使用 dom 的 scrollIntoView
方法,確保高亮的菜單在可視區(qū)域內(nèi)。
顯示時(shí)對(duì)菜單項(xiàng)進(jìn)行 decodeURIComponent
處理,保證顯示的文本正確。
import React, { useRef } from 'react' import { Dropdown, Menu } from 'antd' import { useMount, useRequest } from 'ahooks' import axios from 'axios' import { ReaddirListType } from '@/explorer-manager/src/type' import { encodePathItem, pathExp } from '@/components/use-replace-pathname' import Link from 'next/link' import { FolderOpenOutlined, FolderOutlined } from '@ant-design/icons' import { usePathname } from 'next/navigation' const ScrollIntoView: React.FC<{ is_open: boolean; className?: string }> = ({ is_open, className }) => { const ref = useRef<HTMLDivElement>(null) useMount(() => { is_open && ref.current?.scrollIntoView({ block: 'center', behavior: 'instant' }) }) return is_open ? <FolderOpenOutlined ref={ref} className={className} /> : <FolderOutlined className={className} /> } const BreadcrumbDropdownReaddir: React.FC<React.PropsWithChildren & { title: string; href: string }> = ({ children, href, title, }) => { const pathname = usePathname() || '' const { data = [], runAsync } = useRequest( () => axios .get<{ readdir: ReaddirListType }>('/path/api/readdir', { params: { path: href.replace(pathExp, ''), only_dir: 1 }, }) .then(({ data: { readdir } }) => { return readdir }), { manual: true, cacheKey: `readdir-${href}`, staleTime: 60 * 1000 }, ) const open_dit_text = decodeURIComponent(pathname).split([title, '/'].join('')).pop()?.split('/').filter(Boolean).shift() || '' return ( <Dropdown destroyPopupOnHide={true} arrow={true} trigger={['hover']} dropdownRender={() => { return ( <Menu selectedKeys={[open_dit_text]} style={{ maxHeight: '50vw', overflow: 'scroll' }} items={data?.map((item) => { const is_open = open_dit_text === item.name return { label: ( <Link href={encodePathItem(`${href}/${item.name}`)} prefetch={false}> {item.name} </Link> ), key: item.name, icon: <ScrollIntoView is_open={is_open} />, } })} /> ) }} onOpenChange={(open) => { open && runAsync().then() }} > {children} </Dropdown> ) } export default BreadcrumbDropdownReaddir
文件路徑:explorer/src/components/use-replace-pathname.ts
對(duì)路徑進(jìn)行操作 hooks 文件,對(duì)路徑字符串項(xiàng) encodeURIComponent
處理
export const pathExp = /(^\/)?path/ export const encodePathItem = (path: string) => { return path .split('/') .map((text) => encodeURIComponent(text)) .join('/') }
效果
git-repo
以上就是JS實(shí)現(xiàn)面包屑導(dǎo)航功能從零開始示例的詳細(xì)內(nèi)容,更多關(guān)于JS面包屑導(dǎo)航的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
微信公眾號(hào)平臺(tái)接口開發(fā) 獲取微信服務(wù)器IP地址方法解析
這篇文章主要介紹了微信公眾號(hào)平臺(tái)接口開發(fā) 獲取微信服務(wù)器IP地址方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08js面向?qū)ο笾o態(tài)方法和靜態(tài)屬性實(shí)例分析
這篇文章主要介紹了js面向?qū)ο笾o態(tài)方法和靜態(tài)屬性,實(shí)例分析了靜態(tài)方法和靜態(tài)屬性的原理及應(yīng)用,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-01-01JavaScript實(shí)現(xiàn)沿五角星形線擺動(dòng)的小圓實(shí)例詳解
這篇文章主要介紹了JavaScript實(shí)現(xiàn)沿五角星形線擺動(dòng)的小圓實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07深入理解JavaScript系列(2) 揭秘命名函數(shù)表達(dá)式
網(wǎng)上還沒(méi)用發(fā)現(xiàn)有人對(duì)命名函數(shù)表達(dá)式進(jìn)去重復(fù)深入的討論,正因?yàn)槿绱?,網(wǎng)上出現(xiàn)了各種各樣的誤解,本文將從原理和實(shí)踐兩個(gè)方面來(lái)探討JavaScript關(guān)于命名函數(shù)表達(dá)式的優(yōu)缺點(diǎn)2012-01-01淺述節(jié)點(diǎn)的創(chuàng)建及常見功能的實(shí)現(xiàn)
本文主要對(duì)節(jié)點(diǎn)的創(chuàng)建及常見功能的實(shí)現(xiàn)方法進(jìn)行介紹,希望會(huì)對(duì)大家學(xué)習(xí)javascript有所幫助,下面就跟小編一起來(lái)看下吧2016-12-12JS實(shí)現(xiàn)的駝峰式和連字符式轉(zhuǎn)換功能分析
這篇文章主要介紹了JS實(shí)現(xiàn)的駝峰式和連字符式轉(zhuǎn)換功能,結(jié)合實(shí)例形式分析了JS實(shí)現(xiàn)字符串的駝峰式與連接符式轉(zhuǎn)換的實(shí)現(xiàn)技巧,涉及js字符串遍歷、轉(zhuǎn)換及正則表達(dá)式相關(guān)操作方法,需要的朋友可以參考下2016-12-12基于dropdown.js實(shí)現(xiàn)的兩款美觀大氣的二級(jí)導(dǎo)航菜單
這篇文章主要介紹了基于dropdown.js實(shí)現(xiàn)的兩款美觀大氣的二級(jí)導(dǎo)航菜單,通過(guò)調(diào)用js插件實(shí)現(xiàn)導(dǎo)航效果,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下2015-09-09