Nextjs+React+KOA-1

next.js koa 笔记

nextjs 是自身带有服务器,但它这个服务器只是用于处理 ssr 服务器渲染。像 http 请求数据,数据库连接、session 状态这些操作 nextjs 的这个服务都不处理,所以需要使用 koa 等类似的服务,手动来处理。

运行

1
node server.js

next 默认不支持 css 文件

从服务端原理讲解课程中了解到,style-loader无法把css挂载到页面上,因为服务端渲染不生成html文件,所以即使生成了css文件,也没地方挂,服务端返回的直接是 html 字符串。所以需要使用isomorphic-style-loader,使用这个loader,在组件中可以以对象的方式获取到引用的css,然后把css内容放到context的全局对象中,在返回html内容时把全局中的 css 字符串都拼接好,然后拼到html字符串中。

添加 nextjs 的配置文件,在根目录创建next.config.js,使用官网推荐的@zeit/next-css
安装:

1
npm i @zeit/next-css

在配置文件中添加

1
2
3
4
5
6
7
const withCss = require("@zeit/next-css");

if (typeof require !== "undefined") {
require.extensions[".css"] = file => {};
}

module.exports = withCss({});

这样就可以在组件中import css文件了,并且生成的是 css 的 chunk 文件并在文件中引用,而不像 style-loader 那样是把 css 内容直接插进 html 中。

使用 antd

安装

1
npm i and --save

###配置 antd 的按需加载
官网推荐了两种方式,一种是使用babel-import-plugin

1
2
3
4
5
6
7
8
9
10
// .babelrc or babel-loader option
{
"plugins": [
["import", {
"libraryName": "antd",
"libraryDirectory": "es",
"style": "css" // `style: true` 会加载 less 文件 -------注意:ssr这个不能配置
}]
]
}

一种是使用绝对路径来引入每个组件

1
2
3
import DatePicker from 'antd/es/date-picker'; // 加载 JS
import 'antd/es/date-picker/style/css'; // 加载 CSS
// import 'antd/es/date-picker/style'; // 加载 LESS

第一种使用方便,使用第一种方式,打包的时候,babel 会转换成第二种,所以推荐第一种。

解决 antd 样式问题

上面的标注写了,无法配置样式的引入,所以需要在手动来处理。
###_app.js
_app.js是覆盖 nextjs 默认 app 组件的地方。
在 pages 文件夹中新建_app.js文件

1
2
3
4
5
6
import App from "next/app";

// 引入antd的样式,但这样会把所有的样式都引入进来,可以通过配置babel来解决,但是现在支持不好
import "antd/dist/antd.css";

export default App;

这样 andt 的组件就有样式了。

###前端路由跳转

1
2
3
4
5
import Link from 'next/link'

<Link href="/a" title="desc">
<Button>click </Button>
</Link>

Link 本身是没有标签的(可以 F12 查看),所以需要指定渲染的内容,必须给它传递一个有 onclick 的组件,Link 就是给他的子组件绑定 click 事件,click 事件跳转到指定的路径。并且 Link 只能有一个直接子节点。

####手动跳转

1
2
3
import Router from 'next/router'

Router.push('a/b')

###动态路由

  • query 参数
    接收的时候需要用到next/Router
1
2
3
4
5
import {withRouter} from 'next/router

const A=({router})=><span>A{router.query.Id}</span>

export default withRouter(A)
  • 路由映射
    使用 as 属性定义显示的方式
1
<LInk href="/a?id=1" as ="/a/1" >

这样/a?id=1会被显示成/a/1; 但这有个问题,当刷新的时候就会出现 404,因为这种映射是在前端的,服务端并没有/a/1的资源。
因此服务端需要对/a/1的请求做下处理,转换成原来的/a?id=1;使用 koa 的 router

1
2
3
4
5
6
7
8
9
10
11
12
const router = new Router()
router.get('/a/:id',(ctx)=>{
const id=ctx.params.id;
handle=(ctx.req,ctx.res,{
pathname:'/a',
query:{
id
}
})
ctx.respond=false;
})
server.use(router.routes())

getInitialProps

只有放在 pages 文件下的 getInitialProps 才会被调用;具体内容查看官网介绍;

  • 在页面中获取数据
  • 在 App 文件中获取数据
    需要注意的是:

    如果在 getinitialProps 中异步获取数据,当点击 Link 进行跳转时,等到异步数据获取到才会跳转过去,这就会有延迟的问题。点击跳转可能半天才过去。

自定义 App

nextjsapp(也就是_app.js)相当于母版、基类的作用。我的理解就是一个高阶组件。pages 文件夹下的每个页面都会经过 App 进行“包装”。
app 的作用:

  • 固定 Laout
  • 保持一些公共的状态
  • 给页面传入一些自定义数据
  • 自定义错误处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import App, { Container } from "next/app";
import "antd/dist/antd.css";

class MyApp extends App {
//每次页面跳转都会执行这个方法;
//如果组件中也定义了这个方法,则需要在这手动执行,并把返回数据传给组件
static async getInitialProps({ Component,ctx }) {
let pageProps;
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return pageProps;
}
render() {
// Component就是pages文件夹下的组件
const { Component, pageProps } = this.props;
//需要使用Container进行包裹
<Container>
<div>公共部分</div>
<Component {...pageProps} />
</Container>;
}
}

export default App;

###自定义 document
document 是只有在服务端渲染的时候才会被调用。
用来修改服务端渲染的文档内容。
一般用来配合第三方 css-in-js 方案使用。
创建和_app.js 一样,在 pages 文件夹下创建下划线开头的文件_document.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import Document, { Html, Head, Main, NextScript } from "next/document";

class MyDocument extends Document {
render() {
return (
<Html>
<Head>
<title>my doc</title>
<style>{`.myClass{ color:red;}`}</style>
</Head>
<body className="myClass">
<Main></Main>
<NextScript />
</body>
</Html>
);
}
}

export default MyDocument;

style-css

是组件级的,每个组件中的样式不会影响其他组件,组件和组件之间是隔离的,即使定义的是 global 的,也是组件级的。每个组件的元素上会添加一个类似jsx-12342131231的样式,这就是组件隔离的原因所在。并且当组件别卸载时,样式也会被删除,所以不会影响其他组件的样式。

1
2
3
4
5
6
7
8
<style jsx>
{`
.fontSize {
font-size: 20px;
color: green;
}
`}
</style>

上面的样式在元素上就会是jsx-32342342 fontSize;
但如果同一个页面中的两个子组件都定义了相同的全局样式,还是会影响的(废话)。

###LazyLoading

  • 异步加载模块
1
2
3
const moment = await import("moment");
//注意:这是要先调用default
const time=moment.default(Date.now() - 60 * 1000 * 2).fromNow(),
  • 异步加载组件
1
2
3
import dynamic from "next/dynamic";

const DynamicDemo = dynamic(import("./DynamicDemo"));

###next.config.js 的配置
这个文件就是让我们来重置 nextjs 的配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
const config = {
// 编译文件的输出目录
distDir: "build",
// 是否给每个路由生成Etag,这是用来进行缓存验证用的。如果ngix有配置,这个可以关闭,提高性能
generateEtags: true,
// 页面内容缓存策略
onDemandEntries: {
// 内容在内存中缓存的时长(ms)
maxInactiveAge: 25 * 1000,
// 同时缓存多少个页面
pagesBufferLength: 2,
},
// 在pages目录下哪种后缀的文件会被认为是页面
pageExtensions: ["jsx", "js"],
// 配置buildId
generateBuildId: async () => {
if (process.env.YOUR_BUILD_ID) {
return process.env.YOUR_BUILD_ID;
}
// 返回null使用默认的unique id
return null;
},
// 修改webpack的配置
webpack(config, options) {
return config;
},
// 修改webpackDevMiddleware配置
webpackDevMiddleware: config => {
return config;
},
// 可以在页面上通过 process.env.customKey 获取value
env: {
yourKey: "your value",
},
// 下面两个要通过 'next/config' 来读取
// 只有在服务器渲染时才会获取的配置
serverRuntimeConfig: {
mySecret: "secret",
secondSecret: process.env.SECOND_SECRET,
},
// 在服务端渲染和客户端渲染都可以获取的配置
publicRuntimeConfig: {
staticFolder: "/static",
},
};

###redux-devtools-extension 的使用
安装后,把 applyMiddleware 传给它的方法即可。

文章作者: wenmu
文章链接: http://blog.wangpengpeng.site/2020/01/09/Nextjs+React+KOA-1/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 温木的博客
微信打赏