react 服務端(ssr) 框架next.js開發個人網站分享

Weibozzz發表於2019-02-16

next-blog

專案介紹

利用react服務端框架next.js寫的部落格,喜歡就給個Star支援一下。
https://github.com/Weibozzz/next-blog
線上地址: http://www.liuweibo.cn
本專案使用next.js經驗分享:http://www.liuweibo.cn/p/206

軟體架構

軟體架構說明
react.js next.js antd mysql node koa2 fetch

網站使用技術

  • 前端:React(16.x) Next.js antd-design fetch Less
  • 後端:node框架koa和mysql (目前前後端分離,這裡只負責寫介面,和平常的ajax獲取介面一樣,這裡就不開放原始碼了)
  • 網站目的:業餘學習,記錄技術文章,學以致用
  • 網站功能

    • markdown釋出文章
    • 修改文章(增刪改查)
    • 使用者評論
    • 上傳圖片到七牛雲端儲存

安裝教程

1.快速開始
雖然是服務端渲染,但是也要呼叫介面,所以需要呼叫後端的介面

進入config資料夾下的env.js的isShow設定為true,這裡只是呼叫了我自己線上的介面,當然你
只能看不能修改介面哦。如果為false則調不到介面,需要自己去寫介面。

2.執行

cnpm i
npm run dev

3.部署

cnpm i
npm run build
npm start

使用說明

  • 關於演示不能上傳圖片,不能發表文章或者修改屬於正常情況,因為只是為了展示。
  • 關於路看不到釋出文章路由和後臺管理也屬於正常情況,可以修改程式碼展示路由效果。

網站截圖

1.詳情頁
http://pd96wjt4m.bkt.clouddn.com/image/common/detail_1536836727000_459470_1536836749510.png
2.列表頁
http://pd96wjt4m.bkt.clouddn.com/image/common/list_1536836639000_822188_1536836780676.png
3.編輯頁面和釋出文章,上傳圖片到七牛雲
http://pd96wjt4m.bkt.clouddn.com/image/common/edit_1536836607000_802376_1536836825962.png

網站技術介紹

完全藉助於 next.js 開發的個人網站,線上地址 http://www.liuweibo.cn 總結一下開發完成後的心得和使用體會。gtihub原始碼https://github.com/Weibozzz/next-blog。喜歡就給個Star支援一下。

為什麼使用伺服器端渲染(SSR)?

  • 網站是要推廣的,所以需要更好的 SEO,搜尋引擎可以抓取完整頁面
  • 訪問速度,更快的載入靜態頁面

網站使用技術

  • 前端:React(16.x) Next.js antd-design fetch Less
  • 後端:node框架koa和mysql (目前前後端分離,這裡只負責寫介面,和平常的ajax獲取介面一樣,這裡就不開放原始碼了)
  • 網站目的:業餘學習,記錄技術文章,學以致用
  • 網站功能

    • 釋出文章
    • 修改文章(增刪改查)
    • 使用者評論

原始碼剖析

這裡就只講重點了

入口檔案server.js

這裡用的官方提供的express,同時開啟gzip壓縮

const express = require(`express`)
const next = require(`next`)

const compression = require(`compression`)
const dev = process.env.NODE_ENV !== `production`
const app = next({ dev })
const handle = app.getRequestHandler()
let port= dev?4322:80

app.prepare()
  .then(() => {
    const server = express()

    if (!dev) {
      server.use(compression()) //gzip
    }
    //文章二級頁面
    server.get(`/p/:id`, (req, res) => {
      const actualPage = `/detail`
      const queryParams = { id: req.params.id }
      app.render(req, res, actualPage, queryParams)
    })

    server.get(`*`, (req, res) => {
      return handle(req, res)
    })

    server.listen(port, (err) => {
      if (err) throw err
      console.log(`> Ready on http://localhost ` port)
    })
  })
  .catch((ex) => {
    process.exit(1)
  })

page根元件_app.js

用於傳遞redux資料,store就和普通react用法一樣了,還有header和footer可以放在這裡,同理還有_err.js用於處理404頁面


import App, {Container} from `next/app`
import React from `react`
import {withRouter} from `next/router` // 接入next的router
import withReduxStore from `../lib/with-redux-store` // 接入next的redux
import {Provider} from `react-redux`


class MyApp extends App {
  render() {

    const {Component, pageProps, reduxStore, router: {pathname}} = this.props;
    return (
      <Container>
        <Provider store={reduxStore}>
         <Component {...myPageProps}  />
        </Provider>

      </Container>
    )
  }
}

export default withReduxStore(withRouter(MyApp))

網站的服務端渲染頁面Blog頁面

  • link用於跳轉頁面,利用as把原本的http://*.com?id=1變為漂亮的 /id/1
  • head可以巢狀meta標籤進行seo
  • 配置不需要seo的元件
import dynamic from `next/dynamic`;

//不需要seo
const DynasicTopTipsNoSsr = dynamic(import(`../../components/TopTips`),{
  ssr:false
})

import React, {Component} from `react`
import {connect} from `react-redux`
import Router from `next/router`
import `whatwg-fetch` // 用於fetch請求資料
import Link from `next/link`; // next的跳轉link
import Head from `next/head`  // next的跳轉head可用於seo

class Blog extends Component {

  render() {
    return (
      <div className="Blog">
        <Head>
          <title>{BLOG_TXT}&raquo;{COMMON_TITLE}</title>
        </Head>
        <MyLayout>
          <Link   as={`/Blog/${current}`} href={`/Blog?id=${current}`}>
            <a onClick={this.onClickPageChange.bind(this)}>{current}</a>
          </Link>
        </MyLayout>
      </div>
    )
  }
}
//這裡才是重點,getInitialProps方法來請求資料進行渲染,達到服務端渲染的目的
Blog.getInitialProps = async function (context) {
  const {id = 1} = context.query
  let queryStringObj = {
    type: ALL,
    num: id,
    pageNum
  }
  let queryTotalString = {type: ALL};
  const pageBlog = await fetch(getBlogUrl(queryStringObj))
  const pageBlogData = await pageBlog.json()


  return {pageBlogData}
}
// 這裡根據需要傳入redux
const mapStateToProps = state => {
  const {res, searchData, searchTotalData} = state
  return {res, searchData, searchTotalData};
}
export default connect(mapStateToProps)(Blog)

靜態資源

根目錄建立static資料夾,這裡是強制要求,否則載入不到靜態資源

配置antd和主題並且按需載入

主題配置

antd-custom.less

@primary-color: #722ED0;

@layout-header-height: 40px;
@border-radius-base: 0px;

styles.less

@import "~antd/dist/antd.less";
@import "./antd-custom.less";

最後統一配置在公共head

<Head>
    <meta charSet="utf-8"/>
    <meta httpEquiv="X-UA-Compatible" content="IE=edge, chrome=1"/>
    <meta name="viewport"
          content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no"/>
    <meta name="renderer" content="webkit"/>
    <meta httpEquiv="description" content="劉偉波-天天向上"/>
    <meta name="author" content="劉偉波,liuweibo"/>
    <link rel=`stylesheet` href=`/_next/static/style.css`/>
    <link rel=`stylesheet` type=`text/css` href=`/static/nprogress.css` />
    <link rel=`shortcut icon` type=`image/x-icon` href=`/static/favicon.ico` />
  </Head>

按需載入配置

.babelrc檔案

{
  "presets": ["next/babel"],
  "plugins": [
    "transform-decorators-legacy",
    [
      "import",
      {
        "libraryName": "antd",
        "style": "less"
      }
    ]
  ]
}

next.config.js檔案配置

const withLess = require(`@zeit/next-less`)

module.exports =   withLess(
  {
    lessLoaderOptions: {
      javascriptEnabled: true,
      cssModules: true,

    }
  }
)

頁面css

感覺和vuescope一樣,stylejsx,加了global為全域性,否則只在這裡生效

render() {

    return (
      <Container>
        <Provider store={reduxStore}>
          <Component {...myPageProps}  />
        </Provider>

        <style jsx global>{`

.fl{
    float: left;
}
.fr{
    float: right;
}
        `}</style>
      </Container>
    )

頁面頂部載入進度條

import Router from `next/router`
import NProgress from `nprogress`

Router.onRouteChangeStart = (url) => {
  NProgress.start()
}
Router.onRouteChangeComplete = () => NProgress.done()
Router.onRouteChangeError = () => NProgress.done()

markdown發表文章和程式碼高亮

使用只需要marked(`放入markdown字串`);

import marked from `marked`
import hljs from `highlight.js`;

hljs.configure({
  tabReplace: `  `,
  classPrefix: `hljs-`,
  languages: [`CSS`, `HTML, XML`, `JavaScript`, `PHP`, `Python`, `Stylus`, `TypeScript`, `Markdown`]
})
marked.setOptions({
  highlight: (code) => hljs.highlightAuto(code).value,
  gfm: true,
  tables: true,
  breaks: false,
  pedantic: false,
  sanitize: true,
  smartLists: true,
  smartypants: false
});

學累了,來個圖放鬆下

http://pd96wjt4m.bkt.clouddn.com/image/common/2a35e89324d3ad64d52683ad1343732e_1535531349000_84470_1535531469641.jpg

參與貢獻

  1. Fork 本專案
  2. 新建 Feat_xxx 分支
  3. 提交程式碼
  4. 新建 Pull Request

遺留問題

  1. 訪問量大的時候要做資料快取
  2. cdn node檢視圖片日期
  3. 配置圖片描述和更改
  4. 上傳圖片高質量暫未支援上傳,上傳程式碼改進
  5. 上傳為剛好1M bug
  6. 登陸後支援收藏文章和修改評論
  7. 頂部載入滾動條首次沒loading
  8. 增加koa子模組
  9. 評論支援markdown,評論內容過多建議去sf平臺

待學習修改

  1. 開發環境 warning.js:33 Warning: A component is contentEditable
  2. eslint

關於作者 / About

版權宣告

  • 所有原創文章的著作權屬於 Weibozzz。

作者:劉偉波

連結:http://www.liuweibo.cn/p/206

來源:劉偉波部落格

本文原創版權屬於劉偉波 ,轉載請註明出處,謝謝合作

相關文章