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

先說說先要搭建這個工具的原由吧!最近這段時間分別了學習Typescript和react服務端渲染,可是苦於沒有沒有實際使用端場景,我就忽然想起了將Typescript與Next結合起來搭建一個服務端渲染端工具,一是這樣便可以起到練手的做用,二是若是之後有相應業務需求能夠直接拿來用。話很少說,咱們開始吧!javascript

Next特色

next.js做爲一款輕量級的應用框架,主要用於構建靜態網站和後端渲染網站,爲何選Next呢?,只因Next有以下幾個優勢:前端

  1. 使用後端渲染
  2. 自動進行代碼分割(code splitting),以得到更快的網頁加載速度
  3. 簡潔的前端路由實現
  4. 使用webpack進行構建,支持模塊熱更新(Hot Module Replacement)
  5. 可與主流Node服務器進行對接(如express)
  6. 可自定義babel和webpack的配置

建立並初始化項目

這裏就很少說了,相信你們都很熟練了java

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便可查看初始頁面。node

配置Typescript

  1. 安裝Typescript
npm install typescript --save
複製代碼

@zeit/next-typescript 再也不須要,由於Next.js已內置支持Typescriptreact

  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文件。webpack

  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,並作以下修改git

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,但沒有安裝所需的包。咱們只需運行下列命令便可github

npm install --save-dev @types/node
複製代碼

再次運行命令打開http://localhost:3000便可看到咱們的頁面。web

引入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中引入,typescript

@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。但願你們能夠有所收穫,想要了解更多不一樣技術實現的服務端渲染框架的同窗能夠看這裏github.com/zeit/next.j…

最後

原文在這裏:gitHub 若有遺漏,還請指正!!

若是以爲對您有幫助!請別忘記點個贊或者關注哦您的關注將是我前進的動力!!衝鴨!!!

banner.png

「無畏前端」不定時更新社區優秀技術文章!

相關文章
相關標籤/搜索