我学习ssr的那些事

起因

学习 ssr 还是因为需要加深自己的技能,不然被淘汰了。它的好处相信大家都了解,我也不多说了。对我而言,其实用处也不是很大。你说加载快吧?现在 spa 项目代码分割后其实也可以很快,但是这种东西肯定更快。那么有利于 seo 吧?据说现在大厂的爬虫是有技术可以对 spa 进行爬取信息的,具体不详,但是这应该算是它最大的优势吧。但是存在即合理,学习了上不了当。

nuxtjs

先说一下 vue 的生态简直太棒了。vue-cli 也好, nuxtjs 也好,都有一套非常完整的脚手架,使用者可以通过脚手架搭建完全符合自己想法的开发体系。nuxtjs 的使用就不多说了。这次主要讲一下 nextjs。

初见 nextjs

打开 nextjs 官网简直是无语了。啥玩意儿?在长沙,vue 的使用率大于 react。我相信它们之间的文档也有部分原因。这么烂的文档我怎么学?于是我花了将近大半天的时间自己搭建了一个我个人习惯的 nextjs 的架子。即 nextjs+ts+antd+dva。

nextjs + ts

ts 现在应该很多前端都已经基本掌握,用起来很香。nextjs 也提供了 ts 的配置,新版本 nextjs 只需要装它的 ts 模块即可。还是挺方便了。

1
npm i --save next react react-dom @zeit/next-typescript typescript  @types/react @types/node

先不管,一把装了,然后在 package.json 中添加

1
2
3
4
5
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},

接着在最外层目录新建 pages 文件夹,新建一个自己定义的 ts 写 react 吧。关于nextjs的规则我们暂时不说,这次主要谈论这次搭建这个架子的过程。

antd

antd 还是很不错的。可是按照官方配置和网上搜索的配置都不能配成按需加载。只有重写一下根组件。

1
2
3
4
// ./pages/_app.ts
import "antd/dist/antd.css";
import React from "react";
export default ({ Component, pageProps }) => <Component {...pageProps} />;

将 antd 的所有样式引入才能正常使用。或许我还是没找到正确的方法,还请各位大佬指正。

dva

之所以是 dva 而不是 redux 是因为我只认识这东西,没接触过 redux。

1
npm install --save dva-no-router

然后自己创建一个 Store 文件,地址为https://github.com/dvajs/dva/blob/master/examples/with-nextjs/utils/store.js,注意要转为 ts。然后我发现要使用 dva,每个页面也都有一个这么个东西(store 文件存于 utils 文件夹中)

1
2
3
4
5
import WithDva from '../utils/store';
....
static async getInitialProps(props) {}
....
export default WithDva((state) => { return { index: state.index }; })(Page);

用过 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
import WithDva from './store'
export const WithComponent = (Components: {
(props: any): JSX.Element;
getInitialProps?: (props: any) => Promise<{
[key: string]: any;
pathname: string;
query: { [key: string]: any };
isServer: boolean;
dvaStore: { [key: string]: any };
}>
}, initData?: (props?: Initialprops) => Promise<{ [key: string]: any }> | { [key: string]: any }) => {
Components.getInitialProps = async (props: Initialprops) => {
const {
pathname, query, isServer, store,
} = props;
let result: { [key: string]: any } = {};
// 扩展初始化数据
if (initData) {
result = await initData(props);
}
return {
pathname, query, isServer, dvaStore: store, ...result
};
};
return WithDva((state) => state)(Components)
}

第一个参数是 react 组件,第二个是原本应该写在 getInitialProps 中的内容。 在组件中使用如下:

1
2
3
4
5
6
7
8
9
10
11
export default WithComponent(
Home,
(props) =>
new Promise((res) => {
setTimeout(async () => {
// 事先全局渲染
// await props.store.dispatch({ type: 'index/init' })
res({ a: 67 });
}, 0);
})
);

其余的 dva 配置我也不多说了,和平时使用一致即可。值得注意的是,这里 dva 依然不支持 await/sync。要使用 yield 和*。这一点确实挺尴尬无语的。

动态路由

最后跳转动态路由的时候发现了一个问题,当刷新当前页面的时候,node 端不会重定向,所以会显示 404。这时候就需要用一个 express 了。首先在根目录新建一个 server.js,然后安装 express。将 package.json 代码更改一下

1
2
3
4
5
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "NODE_ENV=production node server.js"
},

server.js 中关键代码如下:

1
2
3
4
5
6
server.get('/dashboard/:id', (req, res) => {
return app.render(req, res, '/dashboard', { id: req.params.id })
})
server.get('*', (req, res) => {
return handle(req, res)
})

同时这里可以做接口的反向代理等等…这样子就比 nextjs 启动项目可扩展性大多了。

最后

看上去好像挺简单的,也是花了我大半天各种翻资料和尝试而成的。这是我个人比较喜欢的一套组合。想详细点就去这里拉下代码看下吧。毕竟也是我学习的阶段写的,肯定问题不少,欢迎各位大佬提出改进。