loading...

89

react 服务端(ssr) 框架next.js开发个人网站分享

react读完大概需要29分钟

  • 发布时间:2018-07-22 16:52 星期日
  • 刘伟波
  • 4502
  • 更新于2018-09-14 15:12 星期五

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则调不到接口,需要自己去写接口。

  1. 运行
    cnpm i
    npm run dev
  2. 部署
    cnpm i
    npm run build
    npm start

使用说明

  • 关于演示不能上传图片,不能发表文章或者修改属于正常情况,因为只是为了展示。
  • 关于路看不到发布文章路由和后台管理也属于正常情况,可以修改代码展示路由效果。

网站截图

  1. 详情页
    http://images.liuweibo.cn/image/common/detail_1536836727000_459470_1536836749510.png
  2. 列表页
    http://images.liuweibo.cn/image/common/list_1536836639000_822188_1536836780676.png
  3. 编辑页面和发布文章,上传图片到七牛云
    http://images.liuweibo.cn/image/common/edit_1536836607000_802376_1536836825962.png

网站技术介绍

完全借助于 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}&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://images.liuweibo.cn/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

来源:刘伟波博客

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

你可能感兴趣的文章

    发表评论

    评论支持markdown,评论内容不能超过500字符,如果内容过多或者要及时回复,建议去 平台,一般一天之内就会回复。
    关于技术问题或者有啥不懂的都可以留言,我会定期回复答 疑,推荐最新仓库 前端知识体系, 感謝支持!
    狗尾草说道:

    您好,最近在研究有关ssr的东西,你的分享对我帮助很大。谢谢你。
    还有个问题我想咨询依稀你关于next.js项目部署的问题。执行build ,start后,他应该会改期一个服务,我们部署的话,是需要将那些文件上传。是通过查找文件路径的方式映射,还是启动服务。希望能帮忙看看这个问题。非常感谢!本人的服务器和您都是linux的。

    • 你可以试一试只上传 build 之后的文件,不过我这边为了方便文件是全部需要上传的,部署用的 pm2 来部署,可以执行 npm run prd。 但是不理解 '查找文件路径的方式映射' .

      刘伟波作者2019-07-18 16:55 星期四

    linux服务端部署后,next build && next start,detail详情页(localhost:3000/p/206)刷新 404,如何解决?

    • 好的,多谢波波解答

      taotao2019-06-25 15:54 星期二
    • 我这个是部署在阿里linux服务器的,你这种问题得靠自己细细看下

      刘伟波作者2019-06-25 15:51 星期二
    刘伟波作者说道:

    最新仓库 前端知识体系 喜欢的可以收藏一下!

    这明明引用的是express呀,哪有koa2 :)

    • 我可能没表述清楚,koa2是后端代码,在这里没有开发出来

      刘伟波作者2019-02-25 18:54 星期一
    大螃蟹说道:

    请问next在跳转路由的时候怎么做loading状态

    • 页面放着不操作,时间稍微一长点击跳转路由一直处于加载状态,但是实际上那个js文件已经在head中引用了

      大螃蟹2018-12-21 17:05 星期五
    • 还有引入antd样式后,页面所有样式都被antd的样式覆盖了,我有我自己的默认样式=。=!

      大螃蟹2018-12-21 15:47 星期五
    • 我使用antd后 next的路由跳转不起作用。。。 问题是只要在下个路由页面中引入antd组件,页面就跳不了

      大螃蟹2018-12-21 15:28 星期五
      1. loading可以查看根文件_app.jsNProgress的使用
      2. 开发模式这么大的文件不影响,部署到线上就ok了
      刘伟波作者2018-12-21 10:08 星期五
    • 还有我发现开发模式下的时候,访问首页会加载3个300多kb的js文件和两个800左右kb的js文件。。。build后部署服务器加载不会慢吗 我新手前端=。=

      大螃蟹2018-12-18 22:12 星期二

    请问一下next.js做得网站如何保存用户的登录信息呢

    • 这个就和普通网站一样,用cookie

      刘伟波作者2018-11-28 09:20 星期三

    请问自己用next开发个人博客,最后是怎么样部署和上线的呢?next build之后有个.next文件夹,然后怎么利用这个文件夹呢,需要放到哪里才能通过个性域名来让别人能够访问到?谢谢!

    • 部署推荐用pm2一键部署

      刘伟波作者2018-11-26 08:15 星期一
    chen111111说道:

    linux 系统打包报错的是怎么回事

    Location: "/usr/local/src/node/mall-next/.babelrc"
    npm ERR! code ELIFECYCLE
    npm ERR! errno 137
    npm ERR! next-demo@1.0.0 build: next build
    npm ERR! Exit status 137
    npm ERR!
    npm ERR! Failed at the next-demo@1.0.0 build script.
    npm ERR! This is probably not a problem with npm. There is likely additional logging output above.

    npm ERR! A complete log of this run can be found in:
    npm ERR! /root/.npm/_logs/2018-10-19T04_57_22_622Z-debug.log

    • 1.可以尝试删掉build之后的文件任何执行,npm run build
      2.可能内存不够,需要重启服务器

      刘伟波作者2018-10-19 18:06 星期五

    怎么把 less 文件打包到style.css 的呢

    • 能具体说遇到了什么问题吗?less文件是自动编译的

      刘伟波作者2018-09-26 22:23 星期三

    cssModules 应该房子外面那一层,我已经实现了但是遇到一个问题,antd 也被cssModules 转换了

    • 已回复!

      刘伟波作者2018-09-17 14:13 星期一
    • 你在你的GitHub上提了 issuse

      chen22018-09-17 11:55 星期一
    • jsx 对lexx 的支持是 2.7.3 依赖 ,最新的是3.8.0 这个和less-loader 可能会有一些冲突
      我期望
      https://github.com/zeit/next.js/issues/5180
      这是我来的例子
      https://github.com/VanquisherMe/with-nextjs-antd-app

      希望能帮助我解决

      用户名咋登陆

      chen12018-09-17 11:53 星期一
    • 你的具体代码实现呢?贴出来看看

       <style jsx global>{`
      
      //这里css就相当于cssModules
      
              `}</style>
      刘伟波作者2018-09-17 10:37 星期一

    发现只有首页才会服务端渲染,点击进入其他页面后还是客户端渲染的,那是不是可以理解成其他页面就没有必要使用getInitialProps了

    • 我写了个demo 首页的请求写在getInitalProp里,发现确实是在服务端请求好之后再返回的,但是,我在其他页面里的getInitialProps里写请求(在首页点击链接后跳转进去的页面),发现并不是服务端渲染,而是在页面点击进入后,进行请求

      Scc12018-09-17 14:02 星期一
    • 不是的。用了next.js框架。getInitialProps方法去请求就相当于服务端渲染,如果不在这里面请求的数据,那就不是服务端渲染。

      刘伟波作者2018-09-16 16:16 星期日

    我在使用next.js+axios时,遇到一个问题,就是在getInitialProps中且在服务端请求数据的时候报错,但是把,但是在其他地方请求数据一点问题都没有

    • 请问报错提示是什么?你是怎么请求的?贴上代码 看看

      刘伟波作者2018-09-14 14:43 星期五

    不错不错

    有大佬知道怎么设置菜单高亮吗?pathname获取不到啊。。

    • 这里您可以引入,在DIdMount的时候可以打印一下Router就会有pathname,如果有其他的问题,可以把我的项目克隆下来,全局查找就可以找到解决办法。
      import Router from next/router

      刘伟波作者2018-09-03 21:35 星期一

    在getInitialProps里的操作感觉在生命周期函数里也可以,为什么要在getInitialProps里执行?

    • 在getInitialProps这里执行,请求数据,才会映射到props,才能起到服务端渲染的作用,虽然在生命周期函数里也可以,但是和单页面一样了

      刘伟波作者2018-08-21 17:36 星期二
    刘伟波作者说道:

    关于技术问题或者有啥不懂的都可以留言,我会定期回复!

    没看到心得,就是把 https://github.com/zeit/next.js/tree/canary/examples 下的几个功能整合到一起而已...

    • 说的挺好,功能整合,分享一下经验吧,也算是一种分享

      刘伟波作者2018-08-04 13:42 星期六

    向大佬学习

    • 总结的很好,收藏一下

      love2018-09-03 22:44 星期一
    • 666

      你好2018-09-03 22:43 星期一
    • 好好学习,天天向上 哈哈

      刘伟波作者2018-08-04 13:42 星期六