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ā)布文章
- 修改文章(增刪改查)
- 用戶評論
- 上傳圖片到七牛云存儲
安裝教程
- 快速開始
雖然是服務(wù)端渲染,但是也要調(diào)用接口笙僚,所以需要調(diào)用后端的接口
進(jìn)入config文件夾下的env.js的isShow設(shè)置為true,這里只是調(diào)用了我自己線上的接口芳肌,當(dāng)然你
只能看不能修改接口哦。如果為false則調(diào)不到接口肋层,需要自己去寫接口庇勃。
- 運(yùn)行
cnpm i
npm run dev
- 部署
cnpm i
npm run build
npm start
使用說明
- 關(guān)于演示不能上傳圖片,不能發(fā)表文章或者修改屬于正常情況槽驶,因為只是為了展示责嚷。
- 關(guān)于路看不到發(fā)布文章路由和后臺管理也屬于正常情況,可以修改代碼展示路由效果掂铐。
網(wǎng)站截圖
-
詳情頁
http://pd96wjt4m.bkt.clouddn.com/image/common/detail_1536836727000_459470_1536836749510.png -
列表頁
http://pd96wjt4m.bkt.clouddn.com/image/common/list_1536836639000_822188_1536836780676.png -
編輯頁面和發(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}»{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
感覺和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>
)
頁面頂部加載進(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)
- Fork 本項目
- 新建 Feat_xxx 分支
- 提交代碼
- 新建 Pull Request
遺留問題
- 訪問量大的時候要做數(shù)據(jù)緩存
- cdn node查看圖片日期
- 配置圖片描述和更改
- 上傳圖片高質(zhì)量暫未支持上傳,上傳代碼改進(jìn)
- 上傳為剛好1M bug
- 登陸后支持收藏文章和修改評論
- 頂部加載滾動條首次沒loading
- 增加koa子模塊
- 評論支持markdown陡蝇,評論內(nèi)容過多建議去sf平臺
待學(xué)習(xí)修改
- 開發(fā)環(huán)境 warning.js:33 Warning: A component is
contentEditable
- eslint
關(guān)于作者 / About
- github:https://github.com/Weibozzz
- 個人博客:http://www.liuweibo.cn
- segmentfault:https://segmentfault.com/u/weibozzz
版權(quán)聲明
- 所有原創(chuàng)文章的著作權(quán)屬于 Weibozzz痊臭。
作者:劉偉波
鏈接:http://www.liuweibo.cn/p/206
來源:劉偉波博客
本文原創(chuàng)版權(quán)屬于劉偉波 ,轉(zhuǎn)載請注明出處登夫,謝謝合作