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.詳情頁
2.列表頁
3.編輯頁面和釋出文章,上傳圖片到七牛雲
網站技術介紹
完全藉助於 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}»{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
感覺和vue
的scope
一樣,style
的jsx
,加了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
});
學累了,來個圖放鬆下
參與貢獻
- Fork 本專案
- 新建 Feat_xxx 分支
- 提交程式碼
- 新建 Pull Request
遺留問題
- 訪問量大的時候要做資料快取
- cdn node檢視圖片日期
- 配置圖片描述和更改
- 上傳圖片高質量暫未支援上傳,上傳程式碼改進
- 上傳為剛好1M bug
- 登陸後支援收藏文章和修改評論
- 頂部載入滾動條首次沒loading
- 增加koa子模組
- 評論支援markdown,評論內容過多建議去sf平臺
待學習修改
- 開發環境 warning.js:33 Warning: A component is
contentEditable
- eslint
關於作者 / About
- github:https://github.com/Weibozzz
- 個人部落格:http://www.liuweibo.cn
- segmentfault:https://segmentfault.com/u/weibozzz
版權宣告
- 所有原創文章的著作權屬於 Weibozzz。
作者:劉偉波
連結:http://www.liuweibo.cn/p/206
來源:劉偉波部落格
本文原創版權屬於劉偉波 ,轉載請註明出處,謝謝合作