react ssr 框架next.js開發(fā)個人網(wǎng)站心得

next-blog

項目介紹

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

軟件架構(gòu)

軟件架構(gòu)說明
react.js next.js antd mysql node koa2 fetch

網(wǎng)站使用技術(shù)

  • 前端:React(16.x) Next.js antd-design fetch Less
  • 后端:node框架koa和mysql (目前前后端分離亏拉,這里只負(fù)責(zé)寫接口扣蜻,和平常的ajax獲取接口一樣,這里就不開放源碼了)
  • 網(wǎng)站目的:業(yè)余學(xué)習(xí)及塘,記錄技術(shù)文章莽使,學(xué)以致用
  • 網(wǎng)站功能
    • markdown發(fā)布文章
    • 修改文章(增刪改查)
    • 用戶評論
    • 上傳圖片到七牛云存儲

安裝教程

  1. 快速開始
    雖然是服務(wù)端渲染,但是也要調(diào)用接口笙僚,所以需要調(diào)用后端的接口

進(jìn)入config文件夾下的env.js的isShow設(shè)置為true,這里只是調(diào)用了我自己線上的接口芳肌,當(dāng)然你
只能看不能修改接口哦。如果為false則調(diào)不到接口肋层,需要自己去寫接口庇勃。

  1. 運(yùn)行
cnpm i
npm run dev
  1. 部署
cnpm i
npm run build
npm start

使用說明

  • 關(guān)于演示不能上傳圖片,不能發(fā)表文章或者修改屬于正常情況槽驶,因為只是為了展示责嚷。
  • 關(guān)于路看不到發(fā)布文章路由和后臺管理也屬于正常情況,可以修改代碼展示路由效果掂铐。

網(wǎng)站截圖

  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. 編輯頁面和發(fā)布文章罕拂,上傳圖片到七牛云


    http://pd96wjt4m.bkt.clouddn.com/image/common/edit_1536836607000_802376_1536836825962.png

網(wǎng)站技術(shù)介紹

完全借助于 next.js 開發(fā)的個人網(wǎng)站揍异,線上地址 http://www.liuweibo.cn 總結(jié)一下開發(fā)完成后的心得和使用體會。gtihub源碼https://github.com/Weibozzz/next-blog爆班。喜歡就給個Star支持一下衷掷。

為什么使用服務(wù)器端渲染(SSR)?

  • 網(wǎng)站是要推廣的柿菩,所以需要更好的 SEO戚嗅,搜索引擎可以抓取完整頁面
  • 訪問速度,更快的加載靜態(tài)頁面

網(wǎng)站使用技術(shù)

  • 前端:React(16.x) Next.js antd-design fetch Less
  • 后端:node框架koa和mysql (目前前后端分離枢舶,這里只負(fù)責(zé)寫接口懦胞,和平常的ajax獲取接口一樣,這里就不開放源碼了)
  • 網(wǎng)站目的:業(yè)余學(xué)習(xí)凉泄,記錄技術(shù)文章躏尉,學(xué)以致用
  • 網(wǎng)站功能
    • 發(fā)布文章
    • 修改文章(增刪改查)
    • 用戶評論

源碼剖析

這里就只講重點(diǎn)了

入口文件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數(shù)據(jù),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))

網(wǎng)站的服務(wù)端渲染頁面Blog頁面

  • link用于跳轉(zhuǎn)頁面胀糜,利用as把原本的http://***.com?id=1變?yōu)槠恋?/id/1
  • head可以嵌套meta標(biāo)簽進(jìn)行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請求數(shù)據(jù)
import Link from 'next/link'; // next的跳轉(zhuǎn)link
import Head from 'next/head'  // next的跳轉(zhuǎn)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>
    )
  }
}
//這里才是重點(diǎn),getInitialProps方法來請求數(shù)據(jù)進(jìn)行渲染蒂誉,達(dá)到服務(wù)端渲染的目的
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}
}
// 這里根據(jù)需要傳入redux
const mapStateToProps = state => {
  const {res, searchData, searchTotalData} = state
  return {res, searchData, searchTotalData};
}
export default connect(mapStateToProps)(Blog)

靜態(tài)資源

根目錄創(chuàng)建static文件夾教藻,這里是強(qiáng)制要求,否則加載不到靜態(tài)資源

配置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";

最后統(tǒng)一配置在公共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>
    )

頁面頂部加載進(jìn)度條

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

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

markdown發(fā)表文章和代碼高亮

使用只需要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
});

學(xué)累了,來個圖放松下

[圖片上傳失敗...(image-b0b5dd-1536913267870)]

參與貢獻(xiàn)

  1. Fork 本項目
  2. 新建 Feat_xxx 分支
  3. 提交代碼
  4. 新建 Pull Request

遺留問題

  1. 訪問量大的時候要做數(shù)據(jù)緩存
  2. cdn node查看圖片日期
  3. 配置圖片描述和更改
  4. 上傳圖片高質(zhì)量暫未支持上傳,上傳代碼改進(jìn)
  5. 上傳為剛好1M bug
  6. 登陸后支持收藏文章和修改評論
  7. 頂部加載滾動條首次沒loading
  8. 增加koa子模塊
  9. 評論支持markdown陡蝇,評論內(nèi)容過多建議去sf平臺

待學(xué)習(xí)修改

  1. 開發(fā)環(huán)境 warning.js:33 Warning: A component is contentEditable
  2. eslint

關(guān)于作者 / About

版權(quán)聲明

  • 所有原創(chuàng)文章的著作權(quán)屬于 Weibozzz痊臭。

作者:劉偉波

鏈接:http://www.liuweibo.cn/p/206

來源:劉偉波博客

本文原創(chuàng)版權(quán)屬于劉偉波 ,轉(zhuǎn)載請注明出處登夫,謝謝合作

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末广匙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子恼策,更是在濱河造成了極大的恐慌鸦致,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涣楷,死亡現(xiàn)場離奇詭異分唾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)狮斗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門绽乔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人碳褒,你說我怎么就攤上這事折砸】戳疲” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵睦授,是天一觀的道長两芳。 經(jīng)常有香客問我,道長去枷,這世上最難降的妖魔是什么怖辆? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮删顶,結(jié)果婚禮上竖螃,老公的妹妹穿的比我還像新娘。我一直安慰自己翼闹,他們只是感情好斑鼻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布蒋纬。 她就那樣靜靜地躺著猎荠,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蜀备。 梳的紋絲不亂的頭發(fā)上关摇,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機(jī)與錄音碾阁,去河邊找鬼输虱。 笑死,一個胖子當(dāng)著我的面吹牛脂凶,可吹牛的內(nèi)容都是我干的宪睹。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蚕钦,長吁一口氣:“原來是場噩夢啊……” “哼亭病!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嘶居,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤罪帖,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后邮屁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體整袁,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年佑吝,在試婚紗的時候發(fā)現(xiàn)自己被綠了坐昙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡芋忿,死狀恐怖炸客,靈堂內(nèi)的尸體忽然破棺而出襟士,到底是詐尸還是另有隱情,我是刑警寧澤嚷量,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布陋桂,位于F島的核電站,受9級特大地震影響蝶溶,放射性物質(zhì)發(fā)生泄漏嗜历。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一抖所、第九天 我趴在偏房一處隱蔽的房頂上張望梨州。 院中可真熱鬧,春花似錦田轧、人聲如沸暴匠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽每窖。三九已至,卻和暖如春弦悉,著一層夾襖步出監(jiān)牢的瞬間窒典,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工稽莉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瀑志,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓污秆,卻偏偏與公主長得像劈猪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子良拼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容