CSR & SSR

Client Side Rendering

  • CSR rendering process:


Server Side Rendering

  • It refers to the process of rendering a single page application (SPA) into AN HTML fragment on the server and sending it to the browser, which then binds its states and events to become a fully interactive page. (PS: SSR content in this paper is all about isomorphism application)
  • SSR rendering process:

  • The server is only responsible for “rendering” the first time (in the real sense, only the browser can render the page, the server actually generates HTML content), and then returns it to the client, the client takes over the page interaction (logic such as event binding), after the client route switch, directly through the JS code to display the corresponding content. Server rendering is no longer required (only required when the page is refreshed)

Why SSR

Advantages:

  • Faster first screen loading: No need to wait for JavaScript to complete download and execute before displaying content, faster access to fully rendered pages, better user experience.
  • More friendly SEO:
    • The crawler can directly grab the rendered page. In the HTML document returned by the CSR for the first time, there is an empty node (root), which does not contain content. The crawler cannot analyze the content of your website, so it cannot give you a good ranking. SSR returns the rendered HTML fragment with complete content, so it can be better analyzed and indexed by crawler.


  • Based on older versions of search engines: We did simple SEO optimization by adding title and description to HTML, both of which did not increase search rankings per se, but increased site conversion. Give your site more description, make users want to click on it, and improve your ranking.
<title>Home page title</title>
<meta name="description" content="Home Page Description"></meta>
Copy the code
  • Based on the new version of the search engine (full text search) : want to rely on the above two to give the website a good ranking is not enough, so SSR is needed to provide more website content.

Disadvantages:

  • High server performance consumption
  • As project complexity increases, problems need to be found between front-end, Node, and back-end
  • It is necessary to consider the operation and maintenance, application and expansion of SSR machines, which increases the operation and maintenance costs (Serverless can be solved).

What are isomorphic applications

  • A set of code that can run on both the server and the client is called a homogeneous application.
  • Render content is generated on the server so that users can see the page with information as early as possible. A complete application includes, in addition to purely static content, various event responses, user interactions, and so on. This means that JavaScript scripts must be executed on the browser side to bind events, handle asynchronous interactions, and so on.
  • From the perspective of performance and user experience, server rendering should express the most important, core, and basic information of the page; On the browser side, further page rendering, event binding, and other enhancements are required for interaction. The so-called isomorphism, is refers to use a set of code or logic, front and back side in the code or logic, ideally in the process of the browser to render further, determine the existing DOM structure and to apply colours to a drawing gives structure are the same, if the same, is not to render the DOM structure, you just need to binding for events.
  • In this dimension, isomorphism is different from server-side rendering. Isomorphism is more like the intersection of server-side rendering and browser-side rendering. It makes up for the differences between server-side and browser-side rendering, so that the same set of code or logic can be run uniformly. The core of isomorphism is “the same code”, which is another dimension separated from the Angle of both ends.

Manually build an SSR framework

useNext.js(Mature SSR framework)

  • Here are just some of the points worth noting and my own experience, please see the official Chinese document for more details

The installation

npx create-next-app project-name
Copy the code

Check the package. The json

{"name": "next-demo-one", "version": "0.1.0", "private": true, "scripts": {// Default port 3000, to change the port use -p "dev": "Next dev 4000", "build": "next build", "start": "next start", "react": "16.12.0", "react-dom": "16.12.0"}Copy the code

Head

  • The next/head function is to set up each page<head>The contents of the react-Helmet tag
import Head from 'next/head'

export default() = ><div>
    <Head>
      <title>My page title</title>
      <meta name="viewport" content="Initial - scale = 1.0, width = device - width" />
    </Head>
    <p>Hello world!</p>
  </div>
Copy the code

getInitialProps

function PageA(props){
    const {childOneData,childTwoData} = props;
    return <div>
        <ChildOne childOneData/>
        <ChildTwo childTwoData/>
    </div>;
}
PageA.getInitialProps = async() = > {// In the getInitialProps method of the parent component, call the interface to get the data needed by the child component
    const childOneData = await getPageAChildOneData();
    const childTwoData = await getPageAChildTwoData();
    return {childOneData, childTwoData}
};
Copy the code
  • When a page structure is complex, multiple sub-components need to request data at the same time, or sub-components need to be loaded dynamically, the above solution may not be suitable. Don’t even think about requesting data during the life cycle of a child component; follow the nex.js specification. A better approach is to split these sub-components into sub-routes that can be called as routing componentsgetInitialPropsMethod to obtain data

routing

  • Reductive route
    • The default inpagesIn the directory.jsFiles are all level 1 routing
    • If you want to use a secondary route, it’s inpagesDirectory Create a folder

  • Next, in the jsLinkComponent that does not render anything by default (e.g a Tag), you need to specify what to render, and there must be a top-level element inside, not two sibling elements. It’s just listening for what we specify click Event, and jump to the specified path
import Link from 'next/link'
const Index = (a)= > {
  return (
    <>
        <Link href="/a? id=1">
            <div>
                <Button>AAA</Button>
                <Button>BBB</Button>
            </div>
        </Link>
    </>)};Copy the code
  • The route in next.js is generated by the convention file directory structure, so it cannot be definedparamsDynamic routes can only passqueryimplementation
import Router from 'next/router'
import Link from 'next/link'

const Index = (a)= > {
  // Jump through API
  function gotoTestB() {
    Router.push(
      {
        pathname: '/test/b'.query: {
          id: 2,},})}return (
    <>
        <Link href="/test/b? id=1" >
                <Button>BBB</Button>
        </Link>
    </>)};Copy the code
  • If you want the route in the browser to look better (e.g./test/idRather than/test? id=123456), yesRoute map
import Router from 'next/router'
import Link from 'next/link'

const Index = (a)= > {
  // Jump through API
  function gotoTestB() {
    Router.push(
      {
        pathname: '/test/b'.query: {
          id: 2,}},'/test/b/2')},return (
    <>
        <Link href="/test/b? id=1" as="/test/b/1" >
            <div>
                <Button>BBB</Button>
            </div>
        </Link>
    </>)};Copy the code
  • However, when the page is refreshed above, the page will be 404. Because it is a SPA application, the front end can not refresh the page by changing the browser route. However, when the page is refreshed and the server requests the file corresponding to the path again, the server cannot find the file corresponding to the path. So you need to use the Node framework (such as Koa2) instead of the server that comes with next.js by default
const Koa = require('koa');
const Router = require('koa-router');
const next = require('next');
const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare().then((a)= > {
  const server = new Koa();
  const router = new Router();
  
  router.get('/a/:id'.async ctx => {
    const id = ctx.params.id;
    await handle(ctx.req, ctx.res, {
      pathname: '/a'.query: { id },
    });
  });
  
  server.listen(3000, () = > {console.log('koa server listening on 3000')}); }Copy the code
  • Route interceptor
import Router from 'next/router'

Router.beforePopState(({ url, as, options }) = > {
  // I only want to allow these two routes!
  if (as! = ="/" || as! = ="/other") {
    // Have SSR render bad routes as a 404.
    window.location.href = as
    // If false is returned, the Router will not execute the popState event
    return false
  }

  return true
});

Copy the code
  • Routing event
    • routeChangeStart(url)– Triggered when route switchover starts
    • routeChangeComplete(url)– Triggered when route switchover is complete
    • routeChangeError(err, url)– Triggered when an error is reported during route switchover
    • beforeHistoryChange(url)– the browserhistoryTriggered when mode starts to switch
    • hashChangeStart(url)– Start switchinghashValue but not triggered when switching page routing
    • hashChangeComplete(url)– Complete switchhashValue but not triggered when switching page routing
    • Here,urlIt is displayed in the browserurl. If you use itRoute map“In the browserurlWill be displayedasThe value of the
import React from 'react';
import Router from 'next/router'

class User extends React.Component {

    handleRouteChange = url= > {
        console.log('url=> ', url);
    };

    componentDidMount() {
        Router.events.on('routeChangeStart', (res) => {
            console.log(res);
        });
        Router.events.on('routeChangeComplete', (res) => {
            console.log(res);
        });
        Router.events.on('routeChangeError', (res) => {
            console.log(res);
        });
    }

    componentWillUnmount() {
        Router.events.off('routeChangeStart', (res) => {
            console.log(res);
        });
        Router.events.off('routeChangeComplete', (res) => {
            console.log(res);
        });
        Router.events.off('routeChangeError', (res) => {
            console.log(res);
        });
    }

    render() {
        return <div>User </div>; }}Copy the code

style jsx

  • There are various CSS solutions in next.js, incorporating Styled – JSX by default
const A = ({ router, name}) = > {
  return (
    <>
      <Link href="#aaa">
        <a className="link">
          A {router.query.id} {name} 
        </a>
      </Link>
      <style jsx>{` a { color: blue; } .link { color: ${color}; } `}</style>
    </>)};Copy the code

Dynamically load resources & components

import { withRouter } from 'next/router'
import dynamic from 'next/dynamic'
import Link from 'next/link'

const LazyComp = dynamic(import('.. /components/lazy-comp'));

const A = ({time }) = > {

  return (
    <>
      <div>Time:{time}</div>
      <LazyComp />
    </>)}; A.goetinitialprops = async CTX => {// Dynamically loading moment, only when the current page is loaded, Instead of loading const moment = await import('moment') when the page is initialized; Const promise = new promise (resolve => {setTimeout(() => {resolve({name: 'jokcy'), // moment.default(Date.now() - 60 * 1000).fromNow(), }) }, 1000) }); return await promise }; export default A;Copy the code

_app.js

  • new./pages/_app.jsFile, custom App module
  • Customizing Next. Js can have the following benefits:
    • Realize the common Layout of each page – Layout
    • Keep some public state when routing changes (using redux)
    • Pass some custom data to the page
    • usecomponentDidCatchCustom processing error
// lib/my-context
import React from 'react'
export default React.createContext(' ')

// components/Layout
// Fix the layout
    xxx
    xxx
    xxx

// _app.js
import 'antd/dist/antd.css';
import App, { Container } from 'next/app';
import Layout from '.. /components/Layout'
import MyContext from '.. /lib/my-context'
import {Provider} from 'react-redux'

class MyApp extends App {
  state = {
    context: 'value'};/** * override the getInitialProps method */
  static async getInitialProps(ctx) {
    const {Component} = ctx;
    // This method is executed every time the page is switched!!
    console.log('app init');
    let pageProps = {};
    // Because if _app.js is not added, by default, Next. Js will execute app.getInitialprops
    // So when the getInitialProps method is overridden, the route component's getInitialProps must be executed
    if (Component.getInitialProps) {
      pageProps = await Component.getInitialProps(ctx)
    }
    return {
      pageProps
    }
  }

  render() {
    const { Component, pageProps, reduxStore } = this.props;

    return (
      // In the latest version of next.js, Container has been removed and no longer needs to wrap components around Container
      // https://github.com/zeit/next.js/blob/master/errors/app-container-deprecated.md
      <Container>
        <Layout>
            <MyContext.Provider value={this.state.context}>
              <Component {. pageProps} / >
            </MyContext.Provider>
        </Layout>
      </Container>
    )
  }
}
export default MyApp;

Copy the code

_document.js

  • It is called only when rendered on the server, not on the client
  • Used to modify the content of the document rendered by the server
  • It is used in conjunction with third-party CSS-IN-JS schemes, such as Styled – Components
import Document, { Html, Head, Main, NextScript } from 'next/document'

class MyDocument extends Document {
    // Override the getInitialProps method
    static async getInitialProps(ctx) {
        // Because if _document.js is not added, by default, Next. Js will execute document.getInitialprops
        // To customize, you must execute document.getInitialprops
        const props = await Document.getInitialProps(ctx);
        return {
            ...props
        }
    }

    // render the following contents must be added
    // render() {
    // return (
    // 
    // 
    // 
    // 
    // 
    // 
      
// // // / /) // } } export default MyDocument Copy the code

Internal integration with Webpack

  • Next. Js is internally integrated with Webpack, out of the box
  • Code and tree-shaking are split by default in the build environment

Integrated story

The online demo

Rendering process

Server execution order

The online demo

  1. _app getInitialProps()
  2. page getInitialProps()
  3. _document getInitialProps()
  4. _app constructor()
  5. _app render()
  6. page constructor()
  7. page render()
  8. _document constructor()
  9. _document render()

Page indicates the routing component

Client execution order (first opening of the page)

  1. _app constructor()
  2. _app render()
  3. page constructor()
  4. page render()

Note: When the page initializes loads, getInitialProps is only called on the server side. The client only executes getInitialProps when a route jump (Link component jump or API method jump) occurs.

Route redirect execution sequence

  1. _app getInitialProps()
  2. page getInitialProps()
  3. _app render()
  4. page constructor()
  5. page render()

Pros and cons of using next.js

Advantages:

  • Lightweight and easy to use, low learning cost, out of the box (such as: internal integration Webpack, reduced-form routing, etc.), no need to build their own projects. Personal opinion: a framework that trades freedom for ease of use.
  • The built-in data synchronization strategy solves the biggest difficulty in server rendering. Taking rendered data from the server side and re-using it on the client side can be very troublesome without a framework.
  • There are a wealth of plugins that allow us to use them as we need them.
  • Flexible configuration: Fast and flexible configuration based on project requirements.

Disadvantages: Must follow its specification (e.g., must get data from getInitialProps), is written in a fixed way, which is not very extensible.

Looking forward to Serverless

  • Serverless — No service architecture
  • Serverless does not mean that servers are no longer needed, but rather that developers no longer need to think about servers so much, and the concept of computing resources as services rather than servers emerges
  • Serverless will definitely be popular, front-end can not consider deployment, operation and maintenance, environment and other scenarios, directly write functions to achieve back-end logic, has a significant improvement in productivity
  • With Serverless, SSR can be called Serverless Side Rendering
  • Because I don’t know much about Serverless, I only know its concept and what its influence is, so I dare not say too much nonsense. Interested students can know by themselves

1. What is the Serverless architecture?

Q&A

Client needs to use reactdom. hydrate instead of reactdom. render to do things SSR didn’t do (e.g., event binding)

  • In React V15,ReactDOM.renderThe method will be based ondata-react-checksumTag, reuseReactDOMServerRender the result without repeating the render. According to thedata-reactidProperty to find the event element to bind and handle the event binding.
  • In React V16,ReactDOMServerRendered content no longer carriesdata-reactProperties,ReactDOM.renderCan be used but with a warning.
  • In React V17,ReactDOM.renderWill no longer have the function of reuse SSR content, unified usehydrate()For server-side rendering.
  • Because the HTML returned by the server is a string, there is content, but there are no events for each component, and there is no data in the client’s repository, which can be regarded as a dry string. The client uses these strings to initialize React, such as creating component instances, binding events, and initializing warehouse data. Hydrate plays an important role in this process, known as hydrate, which is supposed to give dry seeds water and make them more vibrant.
  • When working with next.js,Open the browser console => find network => find the request of the current route and check response => You can see that the HTML returned by the server contains the data required by the current page, so that the client will not re-initiate the request, which depends onReactDOM.hydrate

SSR needs to be usedStaticRouter(static route container) instead ofBrowserRouterHashRouter

Both the client and server need to configure the Store repository, but the two repositories will be different

componentDidMountIs not executed on the server side, whilecomponentWillMountBoth on the client side and the server side, so that’s why it’s not recommended incomponentWillMountThe reason for sending the request

Registration events must be placed incomponentDidMountIn, cannot be placedcomponentWillMountBecause the server will not executecomponentWillUnmountIf you put it incomponentWillMount, events are registered repeatedly and memory leaks occur

If you don’t want to use SSR, but want to optimize SEO, you can use itprerender orprerender-spa-pluginTo replace the SSR

When setting up the SSR framework manuallynpm-run-all & nodemonTo improve the efficiency of Node project development

  • Nodemon listens for changes in code files and restarts automatically when the code changes
  • Npm-run-all Cli tool used to run multiple NPM scripts in parallel or sequence
npm install npm-run-all nodemon --save-dev
Copy the code
 "scripts": {
    "dev": "npm-run-all --parallel dev:**",
    "dev:start": "nodemon build/server.js",
    "dev:build:client": "webpack --config webpack.client.js --watch",
    "dev:build:server": "webpack --config webpack.server.js --watch"
 }

Copy the code

In Next. Js: is introduced by defaultimport React from "react", but if you don’t, the editor will warn you when writing components, so it’s better to include them

In Next. Js: Each routing component in pages directory will be packaged separately, so when clicking the button to jump to the route, it will not jump to the corresponding routing page immediately, but must load the resource file of the target route first, and then jump to the past. This can be optimized with preloading.

In next.js: Webpack is integrated internally, and the build environment splits code and tree-shaking by default

Next. Js works with any Node framework, but those frameworks are notrequest,responseHow does it ensure that Next. Js is exportedhandleIs the method compatible with these frameworks?

  • ensurehandleMethod receives NodeJS nativerequsetObjects, andresponseObject that the framework is not based on native encapsulationrequest,responseObject. So that’s why when you use KOA,handleReceive isctx.reqctx.resRather thanctx.request,ctx.responseThe reason why.

In Next-js: How do I integrate Styled components

  • Needs to be integrated in _document.js
  • Using AOP aspect oriented programming idea
cnpm i styled-components babel-plugin-styled-components -D

Copy the code
// .babelrc
{
  "presets": ["next/babel"]."plugins": [["import",
      {
        "libraryName": "antd"}], ["styled-components", { "ssr": true }]
  ]
}

Copy the code
// _document.js
import Docuemnt, { Html, Head, Main, NextScript } from 'next/document'
import { ServerStyleSheet } from 'styled-components'

function withLog(Comp) {
  return props= > {
    console.log(props);
    return<Comp {... props} /> } } class MyDocument extends Docuemnt { static async getInitialProps(ctx) { const sheet = new ServerStyleSheet(); const originalRenderPage = ctx.renderPage; RenderPage = () => originalRenderPage({// enhanceApp: APP => props => sheet.collectStyles(< APP {... // props} />), // enhanceComponent: Component => withLog(Component)}); const props = await Docuemnt.getInitialProps(ctx); return { ... props, styles: ( <> {props.styles} {sheet.getStyleElement()} </> ), } } finally { sheet.seal() } } render() { return ( <Html> <Head /> <body> <Main /> <NextScript /> </body> </Html> ) } } export default MyDocumentCopy the code
// pages/a.js
import { withRouter } from 'next/router'
import Link from 'next/link'
import styled from 'styled-components'

const Title = styled.h1` color: yellow; font-size: 40px; `;

const color = '# 113366';

const A = ({ router, name}) = > {
  return (
    <>
      <Title>This is Title</Title>
      <Comp />
      <Link href="#aaa">
        <a className="link">
          A {router.query.id} {name} 
        </a>
      </Link>
      <style jsx>{` a { color: blue; } .link { color: ${color}; } `}</style>
    </>)}; export default withRouter(A)Copy the code

In next.js: How do I integrate CSS/Sass/Less/Stylus

CSS,.scss,.less, and.styl are supported. You need to configure the default file next. Config. js

In next.js: Antd styles cannot be loaded on demand at packaging time

www.cnblogs.com/1wen/p/1079…

www.jianshu.com/p/2f9f3e41c…

In next.js: Do not customize static folder names

Create a new folder in the root directory called static. Code can use /static/ to import related static resources. But you can only call it static, because only that name next.js will count as a static resource.

In Next. Js: Why is opening apps slow

  • You might put a module in getInitialProps that is only used by the server, and then Webpack that module as well. See import them properly

Next.js list of common errors

After the language

  • This article is just based on my understanding to write, if there is a wrong understanding please correct or better scheme please put forward
  • In order to write as detailed as possible, it took two months to sort out this article, see here, if you think this article is good, please give a thumbs up ~~

The project address

Manually build simple SSR framework

React16.8 + next.js + Koa2 Develop Github full stack project

reference

Taobao front and back end separation practice !!!!!!

UmiJS SSR

Why was the REACT project made into SSR isomorphic application

React isomorphism application revealed

React isomorphic solution with high reliability and high performance

Moocs Next. Js tutorials

Recommended reading

Do you really understand the React lifecycle

React Hooks 【 nearly 1W words 】+ project combat

Error in Webpack setting environment variables

Implement a simple Webpack from 0 to 1