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

next-blog

項目介紹

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

軟件架構

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

網站使用技術

  • 前端:React(16.x) Next.js antd-design fetch Less
  • 後端:node框架koa和mysql (目前先後端分離,這裏只負責寫接口,和日常的ajax獲取接口同樣,這裏就不開放源碼了)
  • 網站目的:業餘學習,記錄技術文章,學以至用
  • 網站功能html

    • markdown發佈文章
    • 修改文章(增刪改查)
    • 用戶評論
    • 上傳圖片到七牛雲存儲

安裝教程

1.快速開始
雖然是服務端渲染,可是也要調用接口,因此須要調用後端的接口前端

進入config文件夾下的env.js的isShow設置爲true,這裏只是調用了我本身線上的接口,固然你
只能看不能修改接口哦。若是爲false則調不到接口,須要本身去寫接口。vue

2.運行java

cnpm i
npm run dev

3.部署node

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.pngmysql

網站技術介紹

徹底藉助於 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獲取接口同樣,這裏就不開放源碼了)
  • 網站目的:業餘學習,記錄技術文章,學以至用
  • 網站功能react

    • 發佈文章
    • 修改文章(增刪改查)
    • 用戶評論

源碼剖析

這裏就只講重點了

入口文件server.js

這裏用的官方提供的express,同時開啓gzip壓縮git

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

來源:劉偉波博客

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

相關文章
相關標籤/搜索