使用
- react必须是16.x以上
- 安装
npm install next react react-dom --save
- 文件夹static,固定,所有的静态文件,图片都放在这里面,不会被打包进
.next
- css样式打包
npm install @zeit/next-css --save-dev
- next.config.js
// 使用了这个之后,项目里使用到的css都会被打包到.next/static/css文件夹下
const withCSS = require('@zeit/next-css')
// fix: prevents error when .css files are required by node
if (typeof require !== 'undefined') {
require.extensions['.css'] = (file) => {}
}
module.exports = withCss();
- page/_app.js
// 这是入口文件,也就是第一加载的js
// 在这里引入css,router,redux
// 还有判断token等
import React from 'react'
import App from 'next/app'
import { Provider } from 'react-redux'
import withRedux from 'next-redux-wrapper'
import { Router } from 'next/router'
import API from '../api/api'
import Layout from '../components/Layout'
import PageLoading from '../components/PageLoading'
import makeStore from '../store/store'
import {getJwt} from '../lib/auth'
class MyApp extends App {
static async getInitialProps({Component, ctx,router}) {
let {reduxStore,req} = ctx;
let {dispatch} = reduxStore;
let pageProps = {}
let token = getJwt({req});
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx)
}
if(token&&!!req){
let res = await API.getUserInfo({token})
if (res.data.code === 1) {
let {username,avatar,_id} = res.data.data;
dispatch(updateInfo({
avatar,
username,
userId:_id
}))
}
}
return { pageProps }
}
state = {
loading: false
}
startLoading = () => {
this.setState({
loading: true
})
}
stopLoading = () => {
this.setState({
loading: false
})
}
componentDidMount () {
Router.events.on('routeChangeStart', this.startLoading)
Router.events.on('routeChangeComplete',this.stopLoading)
Router.events.on('routeChangeError', this.stopLoading)
}
componentWillUnmount() {
Router.events.off('routerChangeStart', this.startLoading)
Router.events.off('routerChangeComplete',this.stopLoading)
Router.events.off('routerChangeError', this.stopLoading)
}
render() {
const {Component, pageProps, store} = this.props
return (
<div>
<Provider store={store}>
<Layout>
{ this.state.loading ? <PageLoading/> : null}
<Component {...pageProps} />
</Layout>
</Provider>
</div>
)
}
}
export default withRedux(makeStore)(MyApp);
- 打包在.next文件夹下的文件会被自动生成一个html文件,渲染到浏览器端
- 也可以自定义html
// _document.js
import Document, { Head, Main, NextScript } from 'next/document'
export default class MyDocument extends Document {
render() {
return (
<html>
<Head>
// 这里不要设置title标签,原因往下看
<style>{
'body{color:red}'
}</style>
</Head>
<body className="custom_class">
123123
<Main />
<NextScript />
</body>
</html>
)
}
}
- 错误页面
// _error.js
// next自带_error.js这个可以在.next里找到这个页面,如果觉得不好看
// 重写就行
import React from 'react'
export default class Error extends React.Component {
static async getInitialProps({ res, err }) {
const statusCode = res ? res.statusCode : err ? err.statusCode : null;
return { statusCode }
}
render() {
return (
<p>
{this.props.statusCode
? `An error ${this.props.statusCode} occurred on server`
: 'An error occurred on client'}
</p>
)
}
}
- 路由,跟nuxt一样,根据pages文件夹的文件摆放生成的,但是还是需要添加进路由里
pages/
--| _slug/
-----| index.vue
--| users/
-----| _id.vue // 注意这里是下划线
--| index.vue
// 生成
<Switch>
<Route path="/" exact component={pages/index.jsx} />
<Route path="/:slug" component={pages/_slug/index.jsx} />
<Route path="/users/:id" component={pages/users/_id.jsx} />
</Switch>
- 组件添加进路由里,添加redux
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Button,Icon, Tabs } from 'antd'
import { withRouter } from 'next/router'
import API from '../../api/api'
import { setArticles } from "../store/articles/action"
class LoginComponent extends Component {
// 这是componentDidMount前的服务器生命周期
static async getInitialProps({Component, ctx,router}) {
let {reduxStore,req} = ctx;
// 取值
let article = reduxStore.getState().article
let {dispatch} = reduxStore;
let articleList = await API.getArticleList();
// 更新
dispatch(setArticles(articleList))
// 这些数据都在this.props里
return { articleList, article }
}
state = { }
render() {
const { articleList,article} = this.props
return (
<div>
// 如果想给页面添加title,就在return添加title标签
<title>一个路由一个title</title>
{resBlog}
</div>
)
}
export default connect(state => ({
article: state.article,
}), {
setArticles
})(withRouter(Text));
- redux,跟redux一样,查看【JS/redux】笔记
// store/article/action.js
import * as article from './action-type';
export const setArticles = (articleList) => {
return {
type: article.SAVEFORMDATA,
articleList: articleList,
}
}
// store/article/action-type.js
export const SAVEFORMDATA = 'SAVEFORMDATA';
// store/article/reducer.js
import * as article from './action-type';
let defaultState = {
article:"测试"
}
// 首页表单数据
export const article = (state = defaultState , action = {}) => {
switch(action.type){
case article.SAVEFORMDATA:
// 这个state是defaultState,articleList是action.js传过来的
// 这是个合并并更新defaultState的操作
return {...state, ...{ articleList }};
default:
return state;
}
}
- server/index.js
// 起个服务器,不会的查看【Koa】笔记
const Koa = require('koa')
const next = require('next')
const Router = require('koa-router')
const Redis = require('koa-redis')
const session = require('koa-session')
const json = require('koa-json')
const bodyParser = require('koa-bodyparser')
//开发状态
const port = 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
//http响应
const handle = app.getRequestHandler()
//页面编译完成后响应请求
app.prepare().then(()=>{
const server = new Koa()
const router = new Router()
server.proxy = true
server.use(bodyParser({
entableTypes: ['json','form','text']
}))
server.use(json())
server.keys= ['_ayuan#']
server.use(session(SESSION_CONFIG,server))
// 接口路由
router.get('/login', async (ctx) => {
...
})
server.use(router.routes())
//监听
server.use(async (ctx,next) => {
ctx.req.session = ctx.session
await handle(ctx.req, ctx.res)
//不处理返回响应
ctx.respond = false
})
server.listen(port,() => {
console.log(`server is running on ${port}`)
})
})
- 启动服务
"scripts": {
// 执行这个就会自动打包出.next文件夹
"dev": "node ./server/index.js",
"build": "next build",
"start": "next start"
},
我还是不会用怎么办
把上面的github下载下来,没用的代码删了,改成自己的就行