CSR & SSR
Client Side Rendering
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
- Project Address:https://github.com/yjdjiayou/react-ssr-demo
- There are already plenty of comments in the project source code, so I won’t go into them here
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
- Next. Js has its own specification for retrieving data, and data requests need to be placed in
getInitialProps
Internal, rather than within the lifecycle of a component, needs to follow its specifications. getInitialProps
The attributes of the input object are as follows:pathname
– Path part of the URLquery
– The query part of the URL, which is parsed into objectsasPath
– The actual path (including the query part) displayed in the browser isString
typereq
– HTTP request object (server-side only)res
– HTTP return object (server only)jsonPageRes
– Gets the data response object(Available only on the client)err
– Any errors in the rendering process
- When the page is initially loaded,
getInitialProps
It will only be called on the server. Only when a route redirect (Link
When a component jump or API method jump) is executed by the clientgetInitialProps
.The online demo - Only components placed in the Pages directory, its
getInitialProps
Is called and used by the child componentgetInitialProps
Is invalid- Because components in the Pages directory are by default a routing component, only routing components are processed. Next. Js will first call the
getInitialProps
Method to get the returned data asprops
Passes into the routing component, and finally renders the routing component.The online demo - The most direct way for a child component to retrieve data is as follows:
- Because components in the Pages directory are by default a routing component, only routing components are processed. Next. Js will first call the
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 components
getInitialProps
Method to obtain data
routing
- Reductive route
- The default in
pages
In the directory.js
Files are all level 1 routing - If you want to use a secondary route, it’s in
pages
Directory Create a folder
- The default in
- Next, in the js
Link
Component that does not render anything by default (e.ga
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 specifyclick
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 defined
params
,Dynamic routes can only passquery
implementation
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/id
Rather 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 startsrouteChangeComplete(url)
– Triggered when route switchover is completerouteChangeError(err, url)
– Triggered when an error is reported during route switchoverbeforeHistoryChange(url)
– the browserhistory
Triggered when mode starts to switchhashChangeStart(url)
– Start switchinghash
Value but not triggered when switching page routinghashChangeComplete(url)
– Complete switchhash
Value but not triggered when switching page routing- Here,
url
It is displayed in the browserurl
. If you use itRoute map“In the browserurl
Will be displayedas
The 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.js
File, 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
- use
componentDidCatch
Custom 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
Rendering process
Server execution order
The online demo
- _app getInitialProps()
- page getInitialProps()
- _document getInitialProps()
- _app constructor()
- _app render()
- page constructor()
- page render()
- _document constructor()
- _document render()
Page indicates the routing component
Client execution order (first opening of the page)
- _app constructor()
- _app render()
- page constructor()
- 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
- _app getInitialProps()
- page getInitialProps()
- _app render()
- page constructor()
- 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.render
The method will be based ondata-react-checksum
Tag, reuseReactDOMServer
Render the result without repeating the render. According to thedata-reactid
Property to find the event element to bind and handle the event binding. - In React V16,
ReactDOMServer
Rendered content no longer carriesdata-react
Properties,ReactDOM.render
Can be used but with a warning. - In React V17,
ReactDOM.render
Will 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 on
ReactDOM.hydrate
。
SSR needs to be usedStaticRouter
(static route container) instead ofBrowserRouter
和 HashRouter
Both the client and server need to configure the Store repository, but the two repositories will be different
componentDidMount
Is not executed on the server side, whilecomponentWillMount
Both on the client side and the server side, so that’s why it’s not recommended incomponentWillMount
The reason for sending the request
Registration events must be placed incomponentDidMount
In, cannot be placedcomponentWillMount
Because the server will not executecomponentWillUnmount
If 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-al
l & nodemon
To 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
,response
How does it ensure that Next. Js is exportedhandle
Is the method compatible with these frameworks?
- ensure
handle
Method receives NodeJS nativerequset
Objects, andresponse
Object that the framework is not based on native encapsulationrequest
,response
Object. So that’s why when you use KOA,handle
Receive isctx.req
、ctx.res
Rather thanctx.request
,ctx.response
The 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
- @zeit/next-css
- @zeit/next-sass
- @zeit/next-less
- @zeit/next-stylus
In next.js: Antd styles cannot be loaded on demand at packaging time
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 !!!!!!
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