Next.js踩坑入門系列(三)— 目錄重構&&再談路由

Next.js踩坑入門系列

目錄重構

來講一說爲何要目錄重構吧,在踩坑系列(一)的最後我提到了,Next.js很強大,爲咱們封裝了路由,只須要在pages下面新建js文件,裏面寫上咱們熟悉的頁面也就是react組件,就會渲染出來!
其實對於開發來講沒區別,可是項目龐大之後,一個路由對應一個js文件,可是若是頁面很複雜其實不是這個React組件也會很複雜,不是很符合組件化理念,後期也很差維護啊。並且,確定要加redux的,這樣的話就更加混亂了。因此如今趁着還清醒,趕快從新構建一下~css

抽離Layout

我實際上是想邊學Next.js邊用它搭建一個系統,具體是什麼慢慢開發最後你們就清楚了。系統的樣式就打算仿照掘金來寫,感受很簡潔~掘金的樣式很簡單,就是一個Header,而後就是下方的內容區域了,最後這篇文章完事的樣子會變成下面這樣~react

首先,咱們在跟目錄下新建一個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嵌套,就完成了整個骨架的搭建~真的很簡單!git

// /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組件就暫時算完成了。github

抽離頁面組件

上面提到過,pages做爲next的路由索引目錄,那麼我就想讓它專心的作路由,就不要作組件的複雜邏輯了,所以,我想把組件的內部實現所有放在components文件夾下。而後路由頁面只須要直接引用組件就能夠啦~json

// /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哈哈~redux

靜態資源引用

靜態資源的引用好比圖片,你可使用CDN而後src直接填寫url,也能夠經過工程內部的靜態文件引用。Next一樣爲咱們提供了很是簡便的方式,與引用css同樣,css是經過Head組件來引入頁面的,靜態文件next官網推薦咱們在根目錄新建一個static文件夾,而後靜態文件放在static文件夾下,引用的時候使用絕對路徑的形式,next就會找到它們~就像下面這樣:bash

<img className='logo' alt='logo' src='/static/logo.png' />babel

抽離靜態常量

而後就是抽離靜態常量,這個就很簡單了,新建一個constants文件夾,把咱們常常用到的常量在裏面定義好,而後就可使用了,好比CSS的配色(從我選擇的系統配色不知道小夥伴是否是能猜出來我是哪一個公司的),^_^好比未來引入Redux的Action type。antd

// /constants/ConstTypes.js
export const RoleType = {
  1: '管理員',
  10: '普通用戶'
}

// 使用
import { RoleType } from '../../constants/ConstTypes';
複製代碼

完成目錄重構

如今基本暫時完成了目錄重構(未來引入redux確定還得重構一次)。目錄結構以下:app

-- 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了~愈來愈有一個系統樣子了

代碼地址

相關文章
相關標籤/搜索