Next.js踩坑入門系列
- (一) Hello Next.js
- (二) 新增Antd && CSS
- (三) 目錄重構&&再談路由
- (四) Next.js中期填坑
- (五) 引入狀態管理Redux
- (六) 再次重構目錄
- (七) 其他相關知識
目錄重構
來說一說為什麼要目錄重構吧,在踩坑系列(一)的最後我提到了,Next.js很強大,為我們封裝了路由,只需要在pages下面新建js檔案,裡面寫上我們熟悉的頁面也就是react元件,就會渲染出來!
其實對於開發來說沒區別,但是專案龐大以後,一個路由對應一個js檔案,但是如果頁面很複雜其實不是這個React元件也會很複雜,不是很符合元件化理念,後期也不好維護啊。而且,肯定要加redux的,這樣的話就更加混亂了。所以現在趁著還清醒,趕快重新構建一下~
抽離Layout
我其實是想邊學Next.js邊用它搭建一個系統,具體是什麼慢慢開發最後大家就清楚了。系統的樣式就打算仿照掘金來寫,感覺很簡潔~掘金的樣式很簡單,就是一個Header,然後就是下方的內容區域了,最後這篇文章完事的樣子會變成下面這樣~
首先,我們在跟目錄下新建一個components資料夾,專門用來放我們的元件,新寫一個Header.js:// /components/Header.js
import React, { Component } from 'react';
import Link from 'next/link';
import { color_youdao, color_youdao_border } from '../constants/CustomTheme';
class Header extends Component {
constructor(props) {
super(props);
const { title } = props;
this.state = { title };
}
render() {
const { title } = this.state;
return (
<div className='header-container'>
<Link href='/'>
<div className='logo-container'>
<img className='logo' alt='logo' src='/static/logo.png' />
<span className='sys-name'>XXX系統</span>
</div>
</Link>
<h2>{title}</h2>
<style jsx>{`
.header-container {
height: 60px;
background-color: ${color_youdao};
border: 1px solid ${color_youdao_border};
margin-bottom: 10px;
}
h2 {
text-align: center;
line-height: 60px;
font-size: 1.6rem;
font-weight: 500;
color: #fff;
}
.logo-container {
position: absolute;
display: flex;
justify-content: center;
align-items: center;
top: 15px;
left: 20px;
cursor: pointer;
}
.sys-name {
display: inline-block;
margin-left: 10px;
font-size: 20px;
color: #fff;
font-weight: 600;
}
.logo {
width: 30px;
height: 30px;
}
`}</style>
</div>
)
}
}
export default Header;
複製程式碼
然後,把Layout.js裡面加上Header,然後我們每個元件都使用Layout巢狀,就完成了整個骨架的搭建~真的很簡單!
// /components/Layout.js
import { Fragment } from 'react';
import Head from 'next/head';
import Header from './Header';
export default ({title, children }) => (
<Fragment>
<Head>
<meta name='viewport' content='width=device-width, initial-scale=1' />
<meta charSet='utf-8' />
<title>Next-Antd-Scafflod</title>
<link rel='stylesheet' href='/_next/static/style.css' />
</Head>
<style jsx global>{`
* {
margin: 0;
padding: 0;
}
body {
font-family: Helvetica, 'Hiragino Sans GB', 'Microsoft Yahei', '微軟雅黑', Arial, sans-serif;
}
.content-container {
display: flex;
flex-direction: column;
align-items: center;
}
`}</style>
<Header title={title} />
<div className='content-container'>
{children}
</div>
</Fragment>
);
複製程式碼
OK,現在Layout元件就暫時算完成了。
抽離頁面元件
上面提到過,pages作為next的路由索引目錄,那麼我就想讓它專心的做路由,就不要做元件的複雜邏輯了,因此,我想把元件的內部實現全部放在components資料夾下。然後路由頁面只需要直接引用元件就可以啦~
// /components/Home/Home.js 頁面元件
import React, { Fragment } from 'react';
import { Button } from 'antd';
import Link from 'next/link';
import Layout from '../Layout';
const Home = () => (
<Layout title='首頁'>
<Fragment>
<h1>Hello Next.js</h1>
<Link href='/userList'>
<Button type='primary'>使用者列表頁</Button>
</Link>
</Fragment>
</Layout>
);
export default Home;
複製程式碼
// /pages/index.js 路由元件
import Home from '../components/Home/Home';
export default Home;
複製程式碼
其實很簡單,但是這麼看起來就很清晰嘛,O(∩_∩)O哈哈~
靜態資源引用
靜態資源的引用比如圖片,你可以使用CDN然後src直接填寫url,也可以通過工程內部的靜態檔案引用。Next同樣為我們提供了非常簡便的方式,與引用css一樣,css是通過Head元件來引入頁面的,靜態檔案next官網推薦我們在根目錄新建一個static資料夾,然後靜態檔案放在static資料夾下,引用的時候使用絕對路徑的形式,next就會找到它們~就像下面這樣:
<img className='logo' alt='logo' src='/static/logo.png' />
抽離靜態常量
然後就是抽離靜態常量,這個就很簡單了,新建一個constants資料夾,把我們經常用到的常量在裡面定義好,然後就可以使用了,比如CSS的配色(從我選擇的系統配色不知道小夥伴是不是能猜出來我是哪個公司的),^_^比如將來引入Redux的Action type。
// /constants/ConstTypes.js
export const RoleType = {
1: '管理員',
10: '普通使用者'
}
// 使用
import { RoleType } from '../../constants/ConstTypes';
複製程式碼
完成目錄重構
現在基本暫時完成了目錄重構(將來引入redux肯定還得重構一次)。目錄結構如下:
-- root
| -- components // 元件目錄
| -- constants // 常量目錄
| -- pages // 路由目錄
| -- static // 靜態資源目錄
| -- .babelrc
| -- .eslintrc
| -- .gitignore
| -- package.json
| -- ...其他配置檔案
複製程式碼
再談路由
Next.js的路由剛開始給我的感覺就是,我靠,很NB啊。但是慢慢的用起來發現,坑還真不少。前面幾篇也提到了,它是以pages下面的js檔案作為路由路徑驚醒匹配的,所以也就是說你想用到的頁面全要以js檔案的形式放進pages,那麼層級巢狀關係怎麼辦?ok,嘗試了一下,很容易解決了。
路由層級
[需求]: 與使用者相關的包括使用者列表,使用者詳細資訊等等...這兩個功能應該是同屬於使用者子模組,所以應該與首頁不是同級關係。
[解決辦法]:pages下面新建子目錄user裡面包括userList.js和userDetail.js。
-- pages
| -- user
| -- userList.js
| -- userDetail.js
| -- index.js
複製程式碼
可以看到,這樣算是解決了一個問題。
路由引數
緊接著,問題又出現了,我們需要檢視使用者詳情,以往來說,很容易想到 /user/userDetail/:username
,這種嘛,引數通過url的params獲取,但是,悲劇了。查了一下Next.js路由API,人家沒給你提供params,只提供了query。
query形式路由
也就是說,暫時我們需要/user/userDetail?username=XXX
的形式來實現工程,雖然說沒什麼問題,但是可能每個人習慣不一樣吧。當然,對於我這種好說話的人,我可以接受O(∩_∩)O哈哈~
// 其實Next的Link元件的href屬性可以傳入一個物件
<Link href={{ pathname: '/user/userDetail', query: { username: text } }}>
<a>{text}</a>
</Link>
複製程式碼
ok,實現效果就是這樣,反正符合預期,只是使用query代替params了。
P.S.真實是我不想費事搞這個東西,應該是可以解決的,稍後說我的想法
params形式路由
下面我來說說我的理解吧:
首先,是為什麼它不支援params形式的路由,前面提到過了,他是根據pages下的js檔案來匹配路由的,那麼你用params的路由勢必/user/userDetail/:username
,那麼解析器會以為我應該尋找的是pages目錄下面user目錄下面UserDetail目錄下面的${username}檔案,不用想肯定找不到啊,這時候就是404頁面了。所以這是我的理解,他為什麼只使用query。
其次,我認為兩者只是形式上的區別,並沒有本質上的區別,也就是實現效果是一樣的,都能跳轉到指定頁面嘛,何必糾結呢?^_^
最後,就是我看完路有部分的文件,我認為是可以做到params形式的跳轉的,官方文件裡可以自定義server:
// 官方文件自定義server
const { createServer } = require('http')
const { parse } = require('url')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
createServer((req, res) => {
// Be sure to pass `true` as the second argument to `url.parse`.
// This tells it to parse the query portion of the URL.
const parsedUrl = parse(req.url, true)
const { pathname, query } = parsedUrl
if (pathname === '/a') {
app.render(req, res, '/b', query)
} else if (pathname === '/b') {
app.render(req, res, '/a', query)
} else {
handle(req, res, parsedUrl)
}
}).listen(3000, err => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})
複製程式碼
從上面可以看出來,我們可以將a路由匹配到b頁面。也就是我們可以把/user/userDetail/:username
的路由匹配到/user/userDetail?username=${username}
上面嘛。不就解決問題了~O(∩_∩)O哈哈~機智如我,不過我沒試驗過,只是猜測,目前優先想開發一個系統,這裡留坑,以後有機會再填~
總結&&預告
這篇文章講的還是有點多了,哈哈,不過覺得越寫越有靈感,而且說一句,Next.js的官方文件是我讀過最喜歡的英文文件了Vue的是最好的中文文件^_^。
接下來準備往專案里加入redux了~越來越有一個系統樣子了