来讲一说为何要目录重构吧,在踩坑系列(一)的最后我提到了,Next.js很强大,为咱们封装了路由,只须要在pages下面新建js文件,里面写上咱们熟悉的页面也就是react组件,就会渲染出来!
其实对于开发来讲没区别,可是项目庞大之后,一个路由对应一个js文件,可是若是页面很复杂其实不是这个React组件也会很复杂,不是很符合组件化理念,后期也很差维护啊。并且,确定要加redux的,这样的话就更加混乱了。因此如今趁着还清醒,赶快从新构建一下~css
我实际上是想边学Next.js边用它搭建一个系统,具体是什么慢慢开发最后你们就清楚了。系统的样式就打算仿照掘金来写,感受很简洁掘金的样式很简单,就是一个Header,而后就是下方的内容区域了,最后这篇文章完事的样子会变成下面这样react
首先,咱们在跟目录下新建一个components文件夹,专门用来放咱们的组件,新写一个Header.js:git
// /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嵌套,就完成了整个骨架的搭建~真的很简单!github
// /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组件就暂时算完成了。json
上面提到过,pages做为next的路由索引目录,那么我就想让它专心的作路由,就不要作组件的复杂逻辑了,所以,我想把组件的内部实现所有放在components文件夹下。而后路由页面只须要直接引用组件就能够啦~redux
// /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哈哈~babel
静态资源的引用好比图片,你可使用CDN而后src直接填写url,也能够经过工程内部的静态文件引用。Next一样为咱们提供了很是简便的方式,与引用css同样,css是经过Head组件来引入页面的,静态文件next官网推荐咱们在根目录新建一个static文件夹,而后静态文件放在static文件夹下,引用的时候使用绝对路径的形式,next就会找到它们~就像下面这样:markdown
<img className='logo' alt='logo' src='/static/logo.png' />
antd
而后就是抽离静态常量,这个就很简单了,新建一个constants文件夹,把咱们常常用到的常量在里面定义好,而后就可使用了,好比CSS的配色(从我选择的系统配色不知道小伙伴是否是能猜出来我是哪一个公司的),^_^好比未来引入Redux的Action type。app
// /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。
也就是说,暂时咱们须要/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形式的路由,前面提到过了,他是根据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了~愈来愈有一个系统样子了