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

来源:刘伟波博客

本文原创版权属于刘伟波 ,转载请注明出处,谢谢合做

相关文章
相关标签/搜索