Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Typescript+Ant-Design + Redux+Next.js搭建服务端渲染框架 #56

Open
Rashomon511 opened this issue Oct 9, 2019 · 0 comments
Open

Comments

@Rashomon511
Copy link
Owner

先说说先要搭建这个工具的起因吧!最近这段时间分别了学习Typescript和react服务端渲染,但是苦于没有没有实际使用端场景,我就突然想起了将Typescript与Next结合起来搭建一个服务端渲染端工具,一是这样即可以起到练手的作用,二是如果以后有相应业务需求可以直接拿来用。话不多说,我们开始吧!

Next特点

next.js作为一款轻量级的应用框架,主要用于构建静态网站和后端渲染网站,为什么选Next呢?,只因Next有如下几个优点:

  1. 使用后端渲染
  2. 自动进行代码分割(code splitting),以获得更快的网页加载速度
  3. 简洁的前端路由实现
  4. 使用webpack进行构建,支持模块热更新(Hot Module Replacement)
  5. 可与主流Node服务器进行对接(如express)
  6. 可自定义babel和webpack的配置

创建并初始化项目

这里就不多说了,相信大家都很熟练了

mkdir TAN
cd TAN
npm init -y

安装React

  1. 安装react、react-dom等依赖
npm install react react-dom express next --save
npm install @types/{react,react-dom} --save-dev
  1. 在根目录下新建pages文件夹(一定要命名为pages,这是next的强制约定,不然会导致找不到页面),并新建index.js
export default () => <div>Welcome to next.js!</div>
  1. 将如下脚本添加到package.json,用于启动项目
{
  ...
  "scripts": {
    "dev": "next"
  }
  ...
}

运行npm run dev命令打开 http://localhost:3000即可查看初始页面。

配置Typescript

  1. 安装Typescript
npm install typescript --save

@zeit/next-typescript 不再需要,因为Next.js已内置支持Typescript

  1. 在根目录下新建.babelrc,输入如下代码
{
  "presets": [
    "next/babel"
  ]
}
  1. 在根目录下新建tsconfig.json,输入如下代码
{
  "compileOnSave": false,
  "compilerOptions": {
    "strict": true,
    "target": "esnext",
    "module": "esnext",
    "jsx": "preserve",
    "allowJs": true,
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "experimentalDecorators": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "removeComments": false,
    "preserveConstEnums": true,
    "sourceMap": true,
    "skipLibCheck": true,
    "baseUrl": ".",
    "typeRoots": [
      "./node_modules/@types/",
    ],
    "lib": [
      "dom",
      "es2015",
      "es2016"
    ]
  },
  "exclude": ["node_modules"]
}

在上面的配置中,请注意:
使用“jsx”:“preserve”而不是react-native,因为Next.js在5.0.0之后支持.jsx文件扩展名,之前它只识别.js文件。

  1. 引入tslint.json,帮助我们对ts的写法进行一些约束规范统一风格。
{
    "extends": ["tslint:latest"],
    "rules": {
      "arrow-parens": false,
      "interface-name": [true, "never-prefix"],
      "no-object-literal-type-assertion": false,
      "no-submodule-imports": false,
      "semicolon": [true, "never"],
      "trailing-comma": [true, {"multiline": "nerver", "singleline": "never"}]
    }
  }

将page/index.js修改为index.tsx,并做如下修改

interface Person {
    name: String;
}
const Rashomon: Person = {
    name: 'rashomon',
}
export default () => <div>{Rashomon.name}!</div>
  1. 根目录下新建server.js,server.js作为入口文件可以启动自定义服务,我们可以在自定义服务中进行路由解析。
const next = require('next');
const { createServer } = require('http');
const { parse } = require('url')
const dev = process.env.NODE_ENV !== 'production';
const port = parseInt(process.env.PORT, 10) || 3000;
const app = next({dev})
const handle = app.getRequestHandler()

app.prepare().then(() => {
    createServer((req, res) => {
      const parsedUrl = parse(req.url, true)
      handle(req, res, parsedUrl)
    }).listen(port, err => {
        console.log(err, port)
      if (err) throw err
      console.log(`> Ready on http://localhost:${port}`)
    })
  })

更改package.json启动命令    "dev": "node server.js", 运行npm run dev,你会发现提示一行错误:It looks like you're trying to use TypeScript but do not have the required package(s) installed.意思是试图使用TypeScript,但没有安装所需的包。我们只需运行下列命令即可

npm install --save-dev @types/node

再次运行命令打开http://localhost:3000即可看到我们的页面。

引入antd

  1. 安装antd以及babel-plugin-import实现按需加载
npm install antd --save
npm install babel-plugin-import --save-dev
  1. 修改.babelrc文件
{
    "presets": [
      "next/babel"
    ],
    "plugins": [
      ["import", { "libraryName": "antd", "style": false }]
    ]
}

引入Less

  1. 安装less、@zeit/next-less 依赖
npm install less @zeit/next-less null-loader --save 
  1. 根目录下新建next.config.js, 输入如下代码
const withLess = require('@zeit/next-less');

module.exports = withLess({
  lessLoaderOptions: {
    javascriptEnabled: true,
  },
  webpack: (config, { isServer }) => {
    if (isServer) {
      const antStyles = /antd\/.*?\/style.*?/
      const origExternals = [...config.externals]
      config.externals = [
        (context, request, callback) => {
          if (request.match(antStyles)) return callback()
          if (typeof origExternals[0] === 'function') {
            origExternals[0](context, request, callback)
          } else {
            callback()
          }
        },
        ...(typeof origExternals[0] === 'function' ? [] : origExternals),
      ]

      config.module.rules.unshift({
        test: antStyles,
        use: 'null-loader',
      })
    }
    return config
  },
})

3.引入antd样式
在page文件夹下新建index.less文件,引入antd样式,并在index.tsx中引入,

@import '~antd/dist/antd.less';

并在index.tsx中做如下修改

import {Button} from 'antd';
import './index.less'
interface Person {
    name: String;
}
const Rashomon: Person = {
    name: 'rashomon',
}
export default () => <Button type='primary'>{Rashomon.name}!</Button>
  1. 如要使用自定义的antd主题,举个例子,我们改变一下antd的主题色,安装less-vars-to-js
npm install less-vars-to-js --save

根目录下新建assets文件并新建antd-custom.less

@primary-color: #08979c;

在next.config.js中读取该样式文件,经过less-vars-to-js将less文件的内容作为字符串接收,并返回一个包含所有变量的对象。
next.config.js添加如下内容:

... // 添加内容
const lessToJS = require('less-vars-to-js');
const fs = require('fs');
const path = require('path');

// Where your antd-custom.less file lives
const themeVariables = lessToJS(
  fs.readFileSync(path.resolve(__dirname, './assets/antd-custom.less'), 'utf8')
)
...
  lessLoaderOptions: {
    javascriptEnabled: true,
    modifyVars: themeVariables, 
  },
 ···

运行启动命令,页面如下图:
image.png

引入redux

  1. 安装redux与redux-saga,引入redux-saga是为了解决异步请求操作
npm install redux react-redux next-redux-wrapper @types/react-redux --save
npm install redux-saga next-redux-saga @types/next-redux-saga --save

由于篇幅关系创建store、reducers、saga、action的过程我们就不再这里缀述,感兴趣的同学可以查看github上的代码。我们在这里着重讲一下如何引入store,为了在页面初始化时引入store,需要自定义,在pages文件夹下新建_app.tsx.
_app.tsx帮我们做如下几件事:

  • 控制页面初始化
  • 当页面变化时保持页面布局
  • 当路由变化时保持页面状态
  • 使用componentDidCatch自定义处理错误
  • 注入额外数据到页面里 (如 GraphQL 查询)

因此,在pages文件下新建_app.tsx,引入store,代码如下

import React from 'react'
import App from 'next/app';
import { Provider } from 'react-redux';
import Head from 'next/head'
import withRedux from 'next-redux-wrapper'
import withReduxSaga from 'next-redux-saga'
import Layout from '../components/Layout' // 页面基础布局
import initStore from '../redux/store'  // store
import '../static/style/index.less' // antd样式

class MyApp extends App {
    // next.js提供了一个标准的获取远程数据的接口:getInitialProps,通过getInitialProps我们可以获取到远程数据并赋值给页面的props。
    // getInitialProps即可以用在服务端也可以用在前端
    static async getInitialProps({ Component, ctx }: any) {
        let pageProps = {};
        if (Component.getInitialProps) {
            pageProps = await Component.getInitialProps({ ctx });
        }
        return { pageProps };
    }
    render() {
        const { Component, pageProps, store }: any = this.props
        return (
            <Provider store={store}>
                <Head>
                    <title>Tarn</title>
                </Head>
                <Layout>
                    <Component {...pageProps} />
                </Layout>
            </Provider>
        )
    }
}
export default withRedux(initStore)(withReduxSaga(MyApp))

最终实现一个计数器与列表,如下图:
image.png
image.png

自定义错误处理

404和500错误客户端和服务端都会通过error.js组件处理。如果你想改写它,则在pages文件下新建_error.js,当用户访问错误路由时会访问_error页面

import React from 'react'
export default class Error extends React.Component {
  static 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>
    )
  }
}

总结

到此为止,我们已经基本实现了一个基于Typescript+Ant-Design + Redux+Next.js的服务端渲染框架,也熟悉了一部分next的用法,想要了解更多可以前往官网地址为next.js。希望大家可以有所收获,想要了解更多不同技术实现的服务端渲染框架的同学可以看这里https://github.com/zeit/next.js/tree/7.0.0-canary.8/examples

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant