React is designed for large-scale applications. Electron and React-Native have given it the ability to build mobile cross-platform apps and desktop applications. Taro has given it the ability to write and generate multiple platform mini-programs and React-native applications at one time. The document is quite good, and its upgrade speed is relatively fast. I think there are issues that will be solved in time, and their maintenance personnel are very dedicated!
Tips
: This article some knowledge points if the introduction is not right or incomplete place welcome to point out, this article may be more content, reading time takes a long time, but I hope you can carefully look down, if you can best hand in hand to achieve somecode
, all codes in this article are handwritten.
This article will take you from native browser environments to cross-platform development
- Write React to optimize scaffolding belt projects
- The source code of the react – SSR
- Handwritten Node.js native static resource server
- Cross platform Electron demo
Native browser environment:
- Native browser environment is actually the most front-end engineer capacity programming environment, because most front end our browser oriented programming at the beginning, now a lot of a lot of work for 5-10 years of front end, performance panel API all don’t know, how can call a function analysis takes all don’t know, this is also a recent interview, feel someone said unemployment situation, 35 years old It’s universal, but a lot of it’s you, man.
Used in native browser environmentsReact
Frames are more common for making single pagesSPA
Application:
The nativeSPA
Applications are as follows:
-
Pure CSR Rendering (client-side rendering)
-
Pure SSR rendering (server-side rendering)
-
Hybrid rendering (pre-render, webpack plugin pre-render,Next. Js compact route SSR, or use Node.js for middleware, do partial SSR, speed up first screen rendering, or specify route SSR.)
Here’s a closer look at each of these types of refined rendering, as well as the pros and cons:
pureCSR
Apply colours to a drawing
- Client request
RestFul
Interface, interface spit back static resource file Node.js
The implementation code
const express = require('express')
const app = express()
app.use(express.static('pulic'App.get (app.get(app.get(app.get(app.get(app.get)))'/',(req,res)=>{
//do something
})
app.listen(3000,err=>{
if(! err)=>{ console.log('Listening on port 3000 succeeded')}})Copy the code
-
The client receives an HTML file, several CSS files, and several javaScript files
-
The user enters the URL bar and the client returns the static file, and the client begins parsing
-
The client parses the file and the JS code dynamically generates the page. (This is why SEO for a single page application is not friendly; it starts as an HTML file with an empty DIV tag.)
-
To determine whether a page is a CSR, you can, to a large extent, right click to view page elements. If there is only an empty DIV tag, it is highly likely to be a single page, CSR, and client-rendered web page.
##### Pure CSR application, how to fine render?
Single page takeCSR
Form, for the most part, depends on frame,Vue
andReact
And so on. Once this type of technical architecture is used, centralized management of state data, one-way data flow, immutable data, lazy loading of routes, loading of components on demand, appropriate caching mechanisms (PWA
Technology), finely split components, refreshing components from a single data source, these are all directions we can refine. Often the pureCSR
Single-page applications are generally not too complex, so I won’t introduce them herePWA
andweb work
Wait, I’m going to pile on those technologies later in the complex cross-platform applications.
- A single data source determining whether a component is refreshed or not is the most important direction for refinement.
class app extends React.PureComponent{
///////
}
export default connect(
(({xx,xxx,xxxx,xxxxx}))
////
)(app)
Copy the code
Once the business logic is very complex, assuming that we use DVA centralized state management and connect so many state tree modules at the same time, arbitrary data refresh in the state tree module may cause the component to be refreshed, but in fact the component does not need to be refreshed at this time.
-
Here, the required state can be passed in through the root component using props to accurately refresh the source. A single variable data source has strong traceability and is more convenient for debugging
-
One-way data flow immutable data is implemented through the immutable. Js library
import Immutable from require('immutable');
var map1: Immutable.Map<string, number>;
map1 = Immutable.Map({ a: 1, b: 2, c: 3 });
var map2 = map1.set('b', 50);
map1.get('b'); // 2
map2.get('b'); / / 50Copy the code
Immutable data, data sharing, persistent storage, through IS comparison, each map is unique, they compare codeHash values, and the performance is much better than through recursion or directly. When shallow PureComponent is not useful
-
For generic components, use PureComponent to reduce repeated rendering
-
PureComponent. The React Component inherits from Component. PureComponent is a purer Component that performs a shallow comparison of the data before and after the update. Components are rerender only if the data has actually changed. This can greatly improve the performance of components.
-
PureComponent is a shallow comparison that evaluates some special values:
function is(x: any, y: any) {
return( (x === y && (x ! == 0 || 1 / x === 1 / y)) || (x ! == x && y ! == y) ); }Copy the code
It’s important to note here, whyImmutable. Js and pureComponent
Because theReact
Once the root component is brushed
New, will gradually refresh the entire descendant component from top to bottom, so that the performance loss of repeated rendering will be much more, so we not only need to control the component refresh from a single data source, Occasionally you need to compare nextProps with this.props and this.state with nextState in shouldComponentUpdate.
-
Route lazy loading +code-spliting, which speeds up the first screen rendering, also takes the load off the server, because many people may visit your page and not see some of the route content
-
Use react-loadable to support SSR, very recommended, official lazy does not support SSR, this is a pity, we need to use wepback4 optimization configuration, code splitting
The Babel default package @babel/plugin-syntax-dynamic-import supports dynamic import, which supports dynamic import of components
Webpack configuration: Optimization: {runtimeChunk:true,
splitChunks: {
chunks: 'all'}}Copy the code
import React from 'react'
import Loading from './loading-window'// the placeholder component initially loads import Loadable from'react-loadable'
const LoadableComponent = Loadable({
loader: () => import('./sessionWindow'),// True component loading loading: loading,});export default LoadableComponent
Copy the code
-
Ok, now the routing lazy load component and code segmentation are ready, and it supports SSR. Very good
-
Since the web pages of pure CSR are generally not very complex, here is another aspect, that is, the state tree cannot be added without redux, DVA and other centralized state management states. Practice has proved that frequent updating of the state tree has a great impact on user experience. This asynchronous process is more time-consuming. It is better to use props for inter-component communication. In principle, only data shared by many components can be added to the state tree, otherwise, other methods are used for communication.
SSR
, server render:
Server-side rendering can be divided into:
Pure server-side rendering, such asjade,tempalte,ejs
Wait for the template engine to render, and then return to the corresponding front-endHTML
file
- It’s also used here
Node. Js + express framework
const express= require('express')
const app =express()
const jade = require('jade') const result = *** const url path = *** const html = jade.renderFile(url, { data: Result, urlPath})// Pass data to template engine app.get('/',(req,res)=>{res.send(HTML)// Directly send the rendered HTML file to the client as a string}) //RestFul interface app.listen(3000,err=>{//do something
})
Copy the code
Mix render, usewebpack4
Plugin, prerender the specified route, the specified route isSSR
Rendering, background 0 code implementation
const PrerenderSPAPlugin = require('prerender-spa-plugin')
new PrerenderSPAPlugin({
routes: ['/'.'/home'.'/shop'],
staticDir: resolve(__dirname, '.. /dist'),}),Copy the code
Mix render, useNode.js
As middleware,SSR
Specifying routes speeds up first screen rendering, of courseCSS
Can also be server-side rendering, dynamicTitle and meta tags
, betterSEO
Optimization, right hereNode.js
Data can also be processed simultaneously, reducing the front-end computing burden.
-
I think nuggets on the god three that article is written very well, behind I went to gradually realize a, feel more thorough understanding of SSR, plus was writing Node.js every day, but also a little Next,Nuxt, server-side rendering, feel similar.
-
Server rendering essence, in the server to run the code once, the data will be requested back in advance, return to run the HTML file, the client received the file, pull JS code, code water, and then display, dehydration, JS take over the page.
-
Isomorphic straight out code, can greatly reduce the first screen rendering time, through practice, according to different content and configuration can shorten 40%-65% of the time, but the server rendering will bring pressure to the server, so compromise according to the situation.
-
Here is a simple server rendering, where the server directly spit out the stitched HTML structure string:
var express = require('express')
var app = express()
app.get('/', (req, res) => {
res.send(
`
<html>
<head>
<title>hello</title>
</head>
<body>
<h1>hello world </h1>
</body>
</html>
`
)
})
app.listen(3000, () => {
if(! err)=>{ console.log('3000 listen') I}})Copy the code
As long as the client visits localhost:3000, the data page can be accessed
The server renders the core, ensuring that the code runs once on the server, willredux
thestore
The data in the state tree is returned to the client together, and the client dehydrates and renders. Success is achieved by ensuring that their state data is consistent with the route. Client and server code and data must be consistent, otherwiseSSR
Even if it fails.
//server.js
// server/index.js
import express from 'express';
import { render } from '.. /utils';
import { serverStore } from '.. /containers/redux-file/store';
const app = express();
app.use(express.static('public'));
app.get(The '*'.function(req, res) {
if (req.path === '/favicon.ico') {
res.send();
return;
}
const store = serverStore();
res.send(render(req, store));
});
const server = app.listen(3000, () => {
var host = server.address().address;
var port = server.address().port;
console.log(host, port);
console.log('Connection started'); }); //render function import Routes from'.. /Router';
import { renderToString } from 'react-dom/server';
import { StaticRouter, Link, Route } from 'react-router-dom';
import React from 'react';
import { Provider } from 'react-redux';
import { renderRoutes } from 'react-router-config';
import routers from '.. /Router';
import { matchRoutes } from 'react-router-config';
exportconst render = (req, store) => { const matchedRoutes = matchRoutes(routers, req.path); MatchedRoutes. ForEach (item => {// If the component corresponding to this route has a loadData methodif(item.route.loadData) { item.route.loadData(store); }}); console.log(store.getState(),Date.now()) const content = renderToString( <Provider store={store}> <StaticRouter location={req.path}>{renderRoutes(routers)}</StaticRouter> </Provider> );return `
<html>
<head>
<title>ssr123</title>
</head>
<body>
<div id="root">${content}</div>
<script>window.context={state:${JSON.stringify(store.getState())}}</script>
<script src="/index.js"></script>
</body>
</html>
`;
};
Copy the code
- Data water injection, dehydration, hold client and server
store
The consistency of.
The script tag returned above, which has been watered, adds the data obtained on the server to the context property under the global window, which we dehydrate when initializing the client store. Initialize render using data retrieved by the server
import thunk from 'redux-thunk';
import { createStore, applyMiddleware } from 'redux';
import reducers from './reducers';
export const getClientStore = () => {
const defaultState = window.context ? window.context.state : {};
return createStore(reducers, defaultState, applyMiddleware(thunk));
};
export const serverStore = () => {
return createStore(reducers, applyMiddleware(thunk));
};
Copy the code
-
Note that when sending ajax or other data in the component’s componentDidMount lifecycle, check whether there is data in the state tree. If there is data, don’t send requests repeatedly, resulting in a waste of resources.
-
Multi-level SSR routing
// Route configuration file: import Home from'./containers/Home';
import Login from './containers/Login';
import App from './containers/app';
export default [
{
component: App,
routes: [
{
path: '/',
component: Home,
exact: true,
loadData: Home.loadData
},
{
path: '/login',
component: Login,
exact: true}}]];Copy the code
- The route part of the entry file was changed to:
server.js
const content = renderToString(
<Provider store={store}>
<StaticRouter location={req.path}>{renderRoutes(routers)}</StaticRouter>
</Provider>
);
client.js
<Provider store={store}>
<BrowserRouter>{renderRoutes(routers)}</BrowserRouter>
</Provider>
Copy the code
- It may be used later
loader
forCSS
Server-side rendering as wellhelmet
The dynamics of themeta, title
Label forSEO
Optimization, etc., time is tight today, will not continue to writeSSR
.
buildElectron
Extremely complex, very large data applications.
It takes technology,Sqlite,PWA,web work, native node. js,react-window, react-lazyload, C++ plugins, etc
-
The first mentioned is SQLite, an embedded relational database, a lightweight, non-invasive, standard SQL statement that I won’t cover too much here.
-
PWA, progressive Web application, which uses webPack4 plug-in for quick use, can be used for some data content that does not need to store database, but want to pull once and reuse many times
Serverce Work also has its own life cycle
-
Generally, the following steps are used to use a Service Worker:
-
First we need to use serviceWorkerContainer in JavaScript the main thread of the page. The register () to register the Service Worker, in the process of registration, The browser initiates the installation steps of the Service Worker in the background.
-
If the registration is successful, the ServiceWorker runs in the ServiceWorkerGlobalScope environment. This is a special worker context that is independent of the main script’s running thread and has no DOM access.
-
The background starts the installation process, usually during the installation process to cache some static resources. If all resources are cached successfully, the installation succeeds; if any static resource cache fails, the installation fails. It does not matter if the installation fails, the installation will continue automatically until the installation succeeds. If the installation fails, the next step – activating the Service Worker cannot be proceeded.
-
To activate the Service Worker, you must install the Service Worker successfully before starting the activation step. When the installation is complete, an activation event will be received. In the activation event handler, the main action is to clean up the resources used in the old version of the Service Worker script.
-
After successfully activating the Service Worker can control pages, but only for pages opened after successfully registering the Service Worker. That is, whether a page is opened with or without a Service Worker determines whether the page is controlled by the Service Worker during its life cycle. Therefore, pages that were not previously controlled by the Service Worker can be controlled only when the page is refreshed.
Directly to the code, store all js files and images // actual storage according to their own needs, not the more the better.
const WorkboxPlugin = require('workbox-webpack-plugin')
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true,
importWorkboxFrom: 'local',
include: [/\.js$/, /\.css$/, /\.html$/, /\.jpg/, /\.jpeg/, /\.svg/, /\.webp/, /\.png/],
}),
Copy the code
-
PWA is not only these features, it is very powerful, you can go to Lavas to have a look,PWA technology for regular customers to visit the first screen rendering is very large, especially on the mobile end, can be added to the desktop save. 666 ah ~, on the PC side is more cache processing files ~
-
Use react-lazyLoad to lazily load components or images that are initially invisible to your viewport.
/ out of the box LazyLoad image import LazyLoad from'react-lazyload'<LazyLoad height={42} offset={100} once> <img src={this.state.src} onError={this.handleError.bind(this)} className={className ||'avatar'} /> </LazyLoad> Remember to call forceCheck on the mobile or PC side to dynamically calculate the element's position from the window and decide whether to display the real image ~ import {forceCheck} from'react-lazyload';
forceCheck()
Copy the code
- Lazily loaded component
import { lazyload } from 'react-lazyload'; // This is just a decorator, a higher order function. Forcecheck () @lazyload({height: 200, once:true,
offset: 100
})
class MyComponent extends React.Component {
render() {
return <div>this component is lazyloaded by default!</div>;
}
}
Copy the code
Big dataReact
Render, own let the application have 60FPS
– A very core bit of optimization
List
A long list of
- React-virtualized – Auto-Sizer and windowScroll were used together to achieve complex graphics and maintain 60FPS with big data. These components are described on the website above
High computational workload to handweb wrok
thread
var myWorker = new Worker('worker.js');
first.onchange = function() {
myWorker.postMessage([first.value,second.value]);
console.log('Message posted to worker');
}
second.onchange = function() {
myWorker.postMessage([first.value,second.value]);
console.log('Message posted to worker');
}
Copy the code
-
In this code, the variables first and second represent two input elements; When either of these values changes, myworker.postMessage ([first.value,second.value]) sends the two values as an array to the worker. You can send as much as you want in a message.
-
After receiving a message in the worker, we can write an event handler (worker.js) as a response:
onmessage = function(e) {
console.log('Message received from main script');
var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
console.log('Posting message back to main script');
postMessage(workerResult);
}
Copy the code
-
The onMessage handler allows us to execute code at any time once a message is received, using the message itself as the data property of the event. Here we simply multiply the two numbers and pass the result back to the main thread using the postMessage() method again.
-
Back to the main thread, we use onMessage again in response to the message returned by the worker:
myWorker.onmessage = function(e) {
result.textContent = e.data;
console.log('Message received from worker');
}
Copy the code
-
Here we get the data of the message event and set it to the textContent of Result, so the user can see the result of the operation directly.
-
Note: OnMessage and postMessage() must hang on worker objects when used in the main thread, but not when used in workers. The reason is that, within workers, workers are valid global scopes.
-
Note: When a message is passed between the main thread and the worker, it is copied or transferred, not shared.
In fact, node.js and javaScript are not suitable for doing a lot of computing work. This is obvious, especially when the JS engine and GUI rendering thread are mutually exclusive.
Make full use ofReact
theFeber
architecturediff
Algorithm optimization project
requestAnimationFrame
Call the high-priority task, interrupt the traversal of the scheduling phase, becauseReact
The new version of the scheduling stage is the interruptible list traversal with three Pointers, so this does not affect the following traversal, nor does it affect user interaction and other behaviors.
RequestAnimationFrame is also a better way to keep the browser animated at 60 frames
-
With requestAnimationFrame, while the page is inactive, the screen refresh task for that page is paused by the system, as is requestAnimationFrame, which keeps the screen refresh execution synchronized. When the page is activated, the animation picks up where it left off, saving CPU overhead.
-
It does not make sense if the function is executed more than once in a refresh interval, because the monitor is refreshed every 16.7ms and multiple draws do not show up on the screen
-
In high frequency events (resize, Scroll, etc.), using requestAnimationFrame prevents multiple function executions within a refresh interval, thus ensuring fluency. In some cases, you can simply use requestAnimationFrame instead of Throttle to limit the frequency of callback execution
-
RequestIdleCallback, this API is not very compatible at present, but in Electron development, it can be used, there are differences between the two, and these two apis can be used to solve many complex problems ~. Of course, you can also wrap this API with the above API, which is not very complicated.
-
RequestIdleCallback should be used when you are concerned about the user experience and don’t want users to feel stuck because of unimportant tasks such as statistics reporting. Because the requestIdleCallback callback is executed only if the current browser is idle.
- A frame contains user interaction, JS execution, requestAnimationFrame calls, layout calculations, and page redrawing. If a frame has a small number of tasks to perform, and the task is completed in less than 16ms (1000/60), then the frame has a certain amount of free time, which is exactly the time to execute the requestIdleCallback callback, as shown in the following figure:
usepreload
.prefetch
.dns-prefetch
The specified file is requested in advance, or, depending on the case, the browser decides whether to do sodns
Preparse or request certain resources on demand.
- I can do it here
webpack4
Plug-in implementation, jingdong is currently using this program ~
const PreloadWebpackPlugin = require('preload-webpack-plugin')
new PreloadWebpackPlugin({
rel: 'preload',
as(entry) {
if (/\.css$/.test(entry)) return 'style';
if (/\.woff$/.test(entry)) return 'font';
if (/\.png$/.test(entry)) return 'image';
return 'script';
},
include:'allChunks'
//include: ['app']}),Copy the code
To specifyjs
File lazy loading ~
- to
script
Label, add toasync
Tag, which is requested first but does not block parsinghtml
When the file comes back, the request will be loaded immediately
- to
script
Label, add todefer
Tag, lazily loaded, but not loaded until all scripts have loaded, but this tag doesbug
, not sure whether it can be loaded on time. Usually only one is given
It takes too long to write this article, react-native and some details will be added later
Here are some source codes and information addresses:
-
Write React to optimize scaffolding belt projects
-
The source code of the react – SSR
-
Handwritten Node.js native static resource server
-
Cross platform Electron demo