react服务端渲染ssr

Next.js

一个轻量级的 React 服务端渲染框架

1 概念

SPA

single page application : 单页面应用程序
缺点:首屏加载慢,不利于SEO

SSR

Server-side rendering : 服务端渲染

SEO

Search Engine Optimization : 搜索引擎优化

Next.js优点

1 搭建轻松
2 自带数据同步 SSR 服务端-->客户端
3 丰富的生态, 插件
4 灵活的配置

2 搭建项目

2.1 手动搭建

https://nextjs.org/docs/getting-started#manual-setup

# scripts
dev - Runs next dev which starts Next.js in development mode
build - Runs next build which builds the application for production usage
start - Runs next start which starts a Next.js production server

2.2 create-next-app脚手架搭建

  • 安装
npx create-next-app <project name>

// 启动
yarn dev

3 页面和组件

  • 页面
# 不需要引入react依赖
# 页面写在pages文件夹内
# 自动根据其文件名与路由关联,不需要配置路由

pages/index.js --> /
pages/about.js --> /about

# 嵌套路径使用文件夹
pages/blog/nextBlog.js --> /blog/nextBlog.js
  • 组件
# 组件写在components文件夹内
// compones/button.js

export default ({ children }) => {
  return <button>{children}</button>
}

// 使用
import Button from '../components/button'

<Button>按钮组件</Button>

4 路由

https://www.nextjs.cn/docs/api-reference/next/router

4.1 声明式导航和编程式导航

  • 声明式导航
import Link from '/next/link'

<Link href='/router-b?id=123'>
    <a>去页面b</a>
</Link>
  • 编程式导航
import Router from 'next/router'

// 传递string
Router.push('/router-b?id=123')
// 传递对象
Router.push({
    pathname:'/router-b',
    query:{id:123}
})
  • 获取路由相关参数
// hook的方式
import { useRouter } from 'next/router'
const router = useRouter()
const { id } = router.query

// 高阶组件的方式
import { withRouter } from 'next/router'
withRouter(组件)
this.props.router.query

4.2 动态路由

// pages/post/[id].js -- 通过文件名的方式实现动态路由

import { useRouter } from 'next/router'

const Post = () => {
  const router = useRouter()
  console.log('router: ', router)
  return <div>post---{router.query.id}</div>
}

export default Post


// 访问页面
http://localhost:3000/post/123

4.3 路由守卫

router.beforePopState

4.4 路由事件 (router.events)

  • routeChangeStart(url) -- 路由将要改变时触发
  • routeChangeComplete(url) -- 路由改变完成时触发
  • beforeHistoryChange(url) -- 浏览器history改变之前触发
  • hashChangeStart(url) -- hash将要改变时触发
// pages/_app.js

import '../styles/globals.css'
import { useRouter } from 'next/router'
import { useEffect } from 'react'

function MyApp({ Component, pageProps }) {
  const router = useRouter()

  const handleRouteChangeStart = url => console.log('routeChangeStart---', url)
  const handleRouteChangeComplete = url => console.log('routeChangeComplete---', url)
  const handleBeforeHistoryChange = url => console.log('beforeHistoryChange---', url)

  useEffect(() => {
    // 注册事件
    router.events.on('routeChangeStart', handleRouteChangeStart)
    router.events.on('routeChangeComplete', handleRouteChangeComplete)
    router.events.on('beforeHistoryChange', handleBeforeHistoryChange)

    return () => {
      // 注销事件
      router.events.off('routeChangeStart', handleRouteChangeStart)
      router.events.off('routeChangeComplete', handleRouteChangeComplete)
      router.events.off('beforeHistoryChange', handleBeforeHistoryChange)
    }
  }, [])

  return <Component {...pageProps} />
}

export default MyApp

5 获取远程数据 getInitialProps

  • 函数组件
function Page({ stars }) {
  return <div>Next stars: {stars}</div>
}

Page.getInitialProps = async (ctx) => {
  const res = await fetch('https://api.github.com/repos/vercel/next.js')
  const json = await res.json()
  return { stars: json.stargazers_count }
}

export default Page
  • 类组件
import React from 'react'

class Page extends React.Component {
  static async getInitialProps(ctx) {
    const res = await fetch('https://api.github.com/repos/vercel/next.js')
    const json = await res.json()
    return { stars: json.stargazers_count }
  }

  render() {
    return <div>Next stars: {this.props.stars}</div>
  }
}

export default Page

这种方法获取数据是在服务端完成,通过打开network查看元素发现,整个页面时包括数据生成的页面结构都是一次性返回的。这样的好处是有利于seo优化,百度爬虫能抓取到页面的内容

6 css的使用

6.1 添加全局样式

要将样式表添加到您的应用程序中,请在 pages/_app.js 文件中导入(import)CSS 文件

// pages/_app.js

import '../styles/globals.css'

6.2 css模块化和使用sass

// yarn add sass

// 使用方式和cra项目一致
import styles from './index.module.scss'

const Style = () => {
  return (
    <div className={styles.Style}>
      123
      <div className={styles.title}>title</div>
    </div>
  )
}

export default Style

.Style {
  color: red;

  .title{
    font-weight: 700;
    font-size: 40px;
  }
}

7 懒加载

7.1 第三方库懒加载

import { useState } from 'react'

export default () => {
  const timeStamp = +new Date()

  const [time, setTime] = useState(timeStamp)

  const handleClick = async () => {
    // 当点击按钮的时候才会去加载moment
    const moment = await import('moment')
    console.log(setTime(moment.default(timeStamp).format('YYYY-MM-DD HH:mm:ss')))
  }

  return (
    <div>
      <div>将时间戳格式化</div>
      <div>{time}</div>
      <button onClick={handleClick}>格式化</button>
    </div>
  )
}

7.2 组件懒加载

// components/button.js

export default ({ children, ...restProps }) => {
  return <button {...restProps}>{children}</button>
}

import dynamic from 'next/dynamic'

// 基础用法
// const Button = dynamic(import('../components/button'))

// 高级用法
const Button = dynamic(() => import('../components/button'), {
  // 添加加载中状态
  loading: () => <div>loading...</div>,
})

export default () => {
  return (
    <div>
      dynamicComponent
      <Button onClick={() => console.log('click')}>按钮组件</Button>
    </div>
  )
}

8 head

设置head有利于实现更好的seo

给每个页面单独设置head

// Head组件的使用

import Head from 'next/head'

const Header = () => {
  return (
    <>
      <Head>
        <title>Head的使用</title>
        <meta name='description' content='123,123123,3213' />
        <link rel='shortcut icon' href='favicon.ico' type='image/x-icon' />
      </Head>
      <div>Header</div>
    </>
  )
}

export default Header

9 使next支持引入使用css

全局的css只能在pages/_app.js 内引入, 或者使用css module 进行引入使用css

安装

yarn add @zeit/next-css

const withCss = require('@zeit/next-css')

if(typeof require !== 'undefined'){
    require.extensions['.css']=file=>{}
}

module.exports = withCss({})

要实现 antd 按需加载需要配置这个

想要同时可以使用css module的配置

10 antd按需加载

安装

yarn add babel-plugin-import

配置文件 .babelrc

{
  "presets":["next/babel"],  //Next.js的总配置文件,相当于继承了它本身的所有配置
  "plugins":[     //增加新的插件,这个插件就是让antd可以按需引入,包括CSS
      [
          "import",
          {
              "libraryName":"antd",
              "style":"css"
          }
      ]
  ]
}

参考资料

服务端渲染背景:https://segmentfault.com/a/1190000016704384
next框架:https://nextjs.frontendx.cn/
https://www.ctolib.com/docs-nextjs-c-Nextjs3.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容