我們昨天開發出了導覽列,但我們接下來的頁面都會需要導覽列,最直覺的做法就是在每個Component裡面放進去Header.js
const HomePage = props =>{
return(<div>
<Header></Header>
(...其他內容)
</div>)
}
export default HomePage;
const PostList = props =>{
return(<div>
<Header></Header>
(...其他內容)
</div>)
}
export default PostList;
但這樣每次要使用Header的頁面就要加一次,若有多個頁面就要多次處理;且Header算是一個與當前component無直接關聯的元件(彼此不會進行資料傳遞),這樣擺放Header的方式會稍微讓人混淆它的用途。
那我們該如何處理?
其實Header
和Footer
是框架(網頁)內的固定內容,我們每次切換頁面(除了一些不需要它們的頁面)只要切換裡面的main content就好,所以我們要製作一個框架,將Header包起來,然後框架內要有個區塊(main content)能依照當前的頁面去切換。
在pages
資料夾底下建立Root.js
//Root.js
import Header from "../layout/Header";
import { Outlet } from 'react-router-dom';
const RootLayout = props =>{
return <div className="overflow-x-hidden">
<Header />
<Outlet />
</div>
}
export default RootLayout;
上面的<Outlet/>
是透過父層路由元素來渲染其子路由元素,也就是我們用來渲染main content的地方。
它允許在渲染子路由時顯示嵌套的 UI(<Header/>
)。如果父路由完全匹配,則將渲染子路由;如果沒有子路由,則不渲染任何內容。
Outlet說明
先建立今天要處理的首頁檔案,在pages
資料夾底下建立HomePage.js
//HomePage.js
const HomePage = props =>{
return <div>HomePage</div>
}
export default HomePage;
接著回到App.js
修改router設定
import './App.css';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { AuthProvider } from './contexts/AuthContext';
import Login from './pages/Login';
import Register from './pages/Register';
import RootLayout from './pages/Root'; //引入剛剛建立的RootLayout
const router = createBrowserRouter([
{
path: '/',
element: <RootLayout/>,
children:[
{ path: '/', element: <HomePage />}
]
},
{
path: '/login',
element: <Login/>
},
{
path: '/register',
element: <Register/>
}
]);
const App = () => {
return (
<AuthProvider>
<RouterProvider router={router}/>
</AuthProvider>
);
}
export default App;
先設置RootLayout
,接著設定它的子路由,並引入HomePage
的component。
當我們切換到http://localhost:3000
時就能看到HomePage
的內容
從上方的wireframe可以看到,最新文章下面的卡片是重複的樣式,所以可以把它抽成共用的樣式元件
建立HomeItem.js
在components
底下的UI資料夾建立HomeItem.js
//HomeItem.js
import postCover from '../../assets/computer.jpg';
import avatar from '../../assets/avatar.jpg';
const HomeItem = props =>{
return <div className="mx-auto py-8 w-auto">
<img src={postCover} alt="cover"/>
<div className="flex items-center mt-2">
<div className="text-left">
<h3 className="text-2xl font-semibold">What is Micro Frontend?</h3>
<p className="text-m text-gray-600">You may have heard from your backend team that </p>
<div className="flex items-center my-4">
<div className="w-[32px] h-[32px] rounded-full border border-gray-200 overflow-hidden">
<img src={avatar} alt="avatar"/>
</div>
<p className="text-violet-600 ml-2 text-sm">Jonas Kakaroto</p>
<p className="text-sm text-gray-400 ml-3 text-sm">Jan.10.2023</p>
</div>
</div>
</div>
</div>
}
export default HomeItem;
回到我們剛剛已經建立好首頁的檔案HomePage.js
,引入HomeItem
,並處理其他的樣式。
//HomePage.js
import { Fragment } from 'react';
import HomeItem from '../components/Post/HomeItem';
import BlogCover from '../assets/blog-cover.jpg';
const HomePage = props =>{
return <Fragment>
<div className="w-screen pb-8 border-gray-200">
<div className="py-10 mx-auto flex justify-center items-center">
<h1 className="text-3xl font-bold">BLOG DEV</h1>
</div>
<div className="h-[50vh] overflow-hidden relative">
<img src={BlogCover} alt="cover" className="sm:w-full sm:h-auto sm:max-w-full max-w-none w-auto h-full"/>
<h1 className="absolute top-1/2 left-1/2 -translate-y-1/2 -translate-x-1/2 text-5xl text-white font-bold tracking-wide">
<em>Stay Wired,Stay Inspired.</em>
</h1>
</div>
</div>
<div className="lg:w-[1024px] md:w-full md:px-8 px-6 mx-auto mt-12">
<h2 className="text-violet-600 text-3xl font-semibold">Latest Posts</h2>
<div className="grid gap-8 grid-cols-1 grid-rows-6 lg:grid-cols-3 lg:grid-rows-2 sm:grid-cols-2 md:grid-rows-3">
<HomeItem />
<HomeItem />
<HomeItem />
<HomeItem />
<HomeItem />
<HomeItem />
</div>
</div>
</Fragment>
}
export default HomePage;
今天的程式碼在此
因為鐵人賽快來到結尾,所以會比較注重在功能處理,頁面樣式就會直接以程式碼帶過