This is the 128th article without water original, want to get more original good article, please search the public account to follow us ~ this article first in the political cloud front-end blog: server rendering SSR and implementation principle
preface
In everyday front-end development, you’ve more or less heard of server-side rendering (SSR) in scenarios that require first-screen rendering speed optimization. This paper will combine Vue to interpret the SSR implementation logic. By reading this article you will learn:
- Usage scenarios for server-side rendering
- Vue SSR realization principle
- SSR scaffolding available out of the box
Server side rendering
Server-side Rendering SSR (Server Side Rendering) refers to completing HTML stitching processing on the Server and then sending it to the browser, binding events and states of THE HTML structure that does not have the ability of interaction, and presenting the application program with complete ability of interaction on the client.
Applicable scenario
SSR can provide good scenario support in the following two situations
- Need to better support SEO
The advantage is synchronization. Search engine crawlers do not wait for asynchronous request data to end before retrieving information. If SEO is critical to your application, but your page is asynchronously requesting data, SSR can help you solve this problem.
- Faster arrival time is required
The advantages are slow networks and slow device scenarios. Traditional SPA can be executed only after complete JS download is completed, while SSR server rendering mark can be displayed after RENDERING HTML on the server, so users can see the first screen rendering page more quickly. If first screen rendering time conversion is critical to your application, then SSR can be used to optimize.
Not applicable Scenario
Use SSR carefully in the following three scenarios
- Treatment of isomorphic resources
The disadvantage is that the program needs to be universal. Combined with Vue hooks, the only life cycles that can be called in SSR are beforeCreate and Created, which makes it necessary to run without errors when using the tripartite API. References to tripartite libraries need special handling to make them both server – and client-enabled.
- Deployment build configures resource support
The disadvantage is that the operating environment is single. The program must be running in the Node. js server environment.
- More cache preparation for the server
The disadvantage is that the cache policy is required in high-traffic scenarios. Application code needs to be parsed on both sides, with higher CPU performance costs and more preparation for load balancing and multi-scenario caching than SPA.
Let’s combine Vue. Js to see how Vue implements SSR.
Vue SSR realization principle
A prerequisite for
Components are rendered based on VNodes
VNode itself is a JS object, highly compatible, independent of the current execution environment, which can be rendered in the server and native rendering. The virtual DOM is frequently modified, and the real DOM needs to be modified, which can achieve the purpose of local rendering and reduce performance loss.
vue-server-renderer
Is a package with independent rendering application capability, is the core code of Vue server rendering.
The source code below this article is also developed in conjunction with this package, so there is not much redundancy here.
SSR rendering architecture
We combineWebsite figureandThe project architectureTwo dimensions to understand the overall PICTURE of SSR
The project architecture
SRC ├── Components ├─ app.vue ├─ app.js ---- Generic Entry ├── entry-custom.js ---- run only in browser ├─ entry-server.js ---- run only on serverCopy the code
App.js exports the createApp function factory, which can be executed repeatedly, injected from the root Vue instance and used to create router, Store, and application instances.
import Vue from 'vue'
import App from './App.vue'
// Exports a factory function that creates new application, Router, and Store instances
export function createApp () {
const app = new Vue({
render: h= > h(App)
})
return { app }
}
Copy the code
Entry-client.js is responsible for creating the application, mounting the instance DOM, and running only in the browser.
import { createApp } from './app'
const { app } = createApp()
// #app is the root element with a replaceable name
app.$mount('#app')
Copy the code
Entry -server.js creates and returns application instances. It also performs route matching and data preprocessing, and only runs on servers.
import { createApp } from './app'
export default context => {
const { app } = createApp()
return app
}
Copy the code
Server and client code writing principles
As a homogeneous framework, the application code compilation process Vue SSR provides two compilation portals as a way to smooth out code differences due to different environments. There are two rules that distinguish the logic of writing code in Client entry from Server Entry
- Generic code
Generic code that requires different module environment applications to be configured in Webpack resolve.alias due to different authentication logic and gateway configuration.
- Non-generic code
Client Entry is responsible for mounting DOM node code, introducing tripartite packages and loading compatible libraries. Server Entry generates only Vue objects.
Two compiled products
After webpack, there are two bundle products
The server bundle is used to generate vue-SSR-server-bundle. json. The familiar sourceMap and the list of code that needs to be run on the server side are all in this product.
vue-SSR-server-bundle.json
{
"entry":,"files": {A: contains A list of all code to run on the server B: entry file}}Copy the code
The client Bundle is used to generate vue-SSR-client-manifest.json, which contains all static resources, script tags that need to be loaded for the first rendering, and code that needs to be run on the client.
vue-SSR-client-manifest.json
{
"publicPath": public resource path file address,"all": Resource list"initial": Outputs an HTML string"async": Asynchronously loads a collection of components"modules": Mapping between moduleIdentifier and files in the all array}Copy the code
In the prerequisites we mentioned an important package called vue-server-renderer, so let’s focus on what we can learn from this package.
vue-server-renderer
Vue SSR is the core code, worth our attention is the application initialization and application output. The two phases provide complete application-layer code compilation and assembly logic.
Application initialization
During application initialization, the instantiation process and cross-contamination prevention are introduced.
First let’s take a look at how a Vue SSR application is initialized.
Instantiation process
- Generate Vue objects
const Vue = require('vue')
const app = new Vue()
Copy the code
- The renderer is generated, and the two objects of interest are Render and templateRenderer
const renderer = require('vue-server-renderer').createRenderer()
// The createRenderer function has two important objects: render and templateRenderer
function createRenderer (ref) {
// render: Render the HTML component
var render = createRenderFunction(modules, directives, isUnaryTag, cache);
// templateRenderer: template rendering, clientManifest file
var templateRenderer = new TemplateRenderer({
template: template,
inject: inject,
shouldPreload: shouldPreload,
shouldPrefetch: shouldPrefetch,
clientManifest: clientManifest,
serializer: serializer
});
Copy the code
The render and templateRenderer functions that go through this process are not called; they are actually called when the project instantiates createBundleRenderer, the function created in step 3.
- Create sandbox VM and instantiate Vue entry file
var vm = require('vm');
// Call createBundleRunner. RendererOptions is configurable
varThe run = createBundleRunner (entry, entry document files collection, packaging file basedir collection, rendererOptions. RunInNewContext.) ; }Copy the code
In the createBundleRunner method the source code instantiates a method called compileModule with two functions: getCompiledScript and evaluateModule
function createBundleRunner (entry, files, basedir, runInNewContext) {
// Trigger the compileModule method to find the code compiled by Webpack
var evaluate = compileModule(files, basedir, runInNewContext);
}
Copy the code
GetCompiledScript: To compile the wrapper, find the files file name of the entry file and the script script compilation execution
function getCompiledScript (filename) {
if (compiledScripts[filename]) {
return compiledScripts[filename]
}
// Find the corresponding file name in the entry file files
var code = files[filename];
var wrapper = NativeModule.wrap(code);
// Execute the build script script in the sandbox context
var script = new vm.Script(wrapper, {
filename: filename,
displayErrors: true
});
compiledScripts[filename] = script;
return script
}
Copy the code
EvaluateModule: Determines whether to execute in the current or separate context based on configuration items in runInThisContext.
function evaluateModule (filename, sandbox, evaluatedFiles) {
if ( evaluatedFiles === void 0 ) evaluatedFiles = {};
if (evaluatedFiles[filename]) {
return evaluatedFiles[filename]
}
var script = getCompiledScript(filename);
// It is used to determine whether the sandbox context is executed under the current mode, where two functions are called to each other
var compiledWrapper = runInNewContext === false
? script.runInThisContext()
: script.runInNewContext(sandbox);
// m: exports data of functions
var m = { exports: {}};
// r: replaces the native require used to resolve modules referenced by the require function in the bundle
var r = function (file) {...return require(file)
};
}
Copy the code
Call to compiledWrapper.call, exports, require, module and so on.
- Error throw fault tolerance and global error listening
RenderToString: returns a promise when no CB function is available, so we can use a try catch for global error tolerance.
renderToString: function (context, cb) {
var assign;
if (typeof context === 'function') {
cb = context;
context = {};
}
var promise;
if (!cb) {
((assign = createPromiseCallback(), promise = assign.promise, cb = assign.cb));
}
...
return promise
},
}
Copy the code
RenderToStream: This method is used to listen for cast errors. The hook function for throwing errors will be fired in this method.
renderToStream: function (context) {
var res = new PassThrough();
run(context).catch(function (err) {
rewriteErrorTrace(err, maps);
// the listener is fault-tolerant
process.nextTick(function () {
res.emit('error', err);
});
}).then(function (app) {
if (app) {
varrenderStream = renderer.renderToStream(app, context); . }}}Copy the code
Prevention of cross contamination
The Node.js server is a long-running process, and the context of variables is preserved when code written by the client enters the process, resulting in cross-request state contamination. Therefore, an instance cannot be shared, so createApp is a function that can be repeated. In fact, within the package, there is also the ability to prevent cross-contamination between variables.
Is the ability to prevent cross contamination by rendererOptions. RunInNewContext this configuration items to provide, this configuration support true, false, and once three types of configuration items into.
/ / rendererOptions runInNewContext configuration items as follows
true: New Context mode: Create a new context and reevaluate the bundle on each render. Ensuring that the entire application state of each application is a new render incurs additional evaluation costs.falseDirect mode: It only calls the exported function each time it renders. Rather than reevaluate the entire bundle module on the cost of evaluation, but requires structured source codeonceThe initial context mode is only used to collect possible styles injected by non-component VUE style loaders.Copy the code
Specifically for the false and once scenarios, the rendering process is strictly scoped to prevent cross-contamination, to ensure that different objects do not contaminate each other.
if(! runner) {var sandbox = runInNewContext === 'once'
? createSandbox()
: global;
initialContext = sandbox.__VUE_SSR_CONTEXT__ = {};
runner = evaluate(entry, sandbox);
_VUE_SSR_CONTEXT_uu will not be available in subsequent renderings
// Prevent cross-contamination
delete sandbox.__VUE_SSR_CONTEXT__;
if (typeofrunner ! = ='function') {
throw new Error(
'bundle export should be a function when using ' +
'{ runInNewContext: false }.')}}Copy the code
Application of output
In the stage of application output, SSR will focus more on loading script content and template rendering. During template rendering, whether the template engine source code has been defined in the code will provide different HTML splicing structures.
Loading script content
This process implements data binding between the Reader and templateRender methods constructed in the previous phase.
TemplateRenderer: is responsible for HTML encapsulation. Its prototype will have several methods. The functions are shown below. The bindRenderFns function binds four render functions to the user’s context, allowing the user to do custom assembly and rendering of the content.
Render: The function is called recursively, in order from parent to child, to render all components into HTML.
function createRenderFunction (modules, directives, isUnaryTag, cache) {
return function render (component, write, userContext, done) {
warned = Object.create(null);
var context = new RenderContext({
activeInstance: component,
userContext: userContext,
write: write, done: done, renderNode: renderNode,
isUnaryTag: isUnaryTag, modules: modules, directives: directives,
cache: cache
});
installSSRHelpers(component);
normalizeRender(component);
// Render the node node and bind the user context
var resolve = function () {
renderNode(component._render(), true, context);
};
After the serverPrefetch component completes, _render generates the vNode of the child node for renderingwaitForServerPrefetch(component, resolve, done); }}Copy the code
After the above compilation process, we have got the HTML string, but if we want to display the page in the browser, we still need JS, CSS and other tags to assemble the HTML into a complete message and output it to the browser, so we need the template rendering stage to assemble these elements.
Template rendering
After application initialization phase, the code is compiled for the HTML string, the need to rely on the context rendering templateRenderer. The prototype. BindRenderFns binding in the state, the script, styles and other resources.
TemplateRenderer.prototype.bindRenderFns = function bindRenderFns (context) {
var renderer = this; ['ResourceHints'.'State'.'Scripts'.'Styles'].forEach(function (type) {
context[("render" + type)] = renderer[("render"+ type)].bind(renderer, context); }); Context.getpreloadfiles = r**erer.ge**** : ** reloadfiles. bind(renderer, context); };Copy the code
When rendering a template in detail, there are two situations:
- No template engine is defined
Render results are returned directly to renderToString’s callback function, and the script we need for the page depends on renderStyles from our user context, RenderResourceHints, renderState, and renderScripts are obtained separately.
- Defines the template engine
TemplateRender will help us with the HTML assembly
TemplateRenderer.prototype.render = function render (content, context) {
// parsedTemplate is used to parse the function's three-part compile object,
// Concatenate string templates in sequence
var template = this.parsedTemplate;
if(! template) {throw new Error('render cannot be called without a template.')
}
context = context || {};
if (typeof template === 'function') {
return template(content, context)
}
if (this.inject) {
return (
template.head(context) +
(context.head || ' ') +
this.renderResourceHints(context) +
this.renderStyles(context) +
template.neck(context) +
content +
this.renderState(context) +
this.renderScripts(context) +
template.tail(context)
)
} else{... }};Copy the code
So far we have understood the overall architecture logic of Vue SSR and the ** core code of VUE-server-renderer, ** of course SSR also has a lot of scaffolding for us to choose from out of the box.
Out of the box SSR scaffolding
React, Vue and Angula, three popular technology stacks at the front end, have incubated corresponding server rendering frameworks, which are out-of-the-box and can be used by students who are interested.
- React: Next.js
- Vue: Nuxt.js
- Angula: Nest.js
conclusion
Server-side rendering (SSR) is a homogeneous program, and the use of SSR depends on how important the content arrival time is to the application. If a few hundred milliseconds of initial loading were acceptable, SSR use would be overkill.
Study of the source code can help to better draw lessons from excellent program writing and inspire thinking of the daily code programming architecture, if you prefer a box of the box solution, it can use existing SSR scaffold in construction project, the scaffolding template abstraction and additional function extension can provide a smooth out of the experience.
reference
-
Vue SSR’s official website
-
Vue Usage Guide
-
Vue SSR source code analysis
okokokokok
You vote, I vote, zheng CAI yun will debut tomorrow, the Nuggets 2021 author list hot list!
Please move your little finger and vote for us. Your support is the biggest motivation for us to move forward,
- Web entry: Click me vote
- App entrance: Please scroll to the top of the article and press ↓ as shown below
Recommended reading
- Sketch Plug-in Development Guide
- Why is index not recommended as key in Vue
- Brief analysis of Web screen recording technology scheme and implementation
Open source works
- Political cloud front-end tabloid
Open source address www.zoo.team/openweekly/ (wechat communication group on the official website of tabloid)
- Item selection SKU plug-in
Open source addressGithub.com/zcy-inc/sku…
, recruiting
ZooTeam, a young passionate and creative front-end team, belongs to the PRODUCT R&D department of ZooTeam, based in picturesque Hangzhou. The team now has more than 60 front-end partners, with an average age of 27, and nearly 40% of them are full-stack engineers, no problem in the youth storm group. The members consist of “old” soldiers from Alibaba and NetEase, as well as fresh graduates from Zhejiang University, University of Science and Technology of China, Hangzhou Electric And other universities. In addition to daily business docking, the team also carried out technical exploration and practice in material system, engineering platform, building platform, performance experience, cloud application, data analysis and visualization, promoted and implemented a series of internal technical products, and continued to explore the new boundary of front-end technology system.
If you want to change what’s been bothering you, you want to start bothering you. If you want to change, you’ve been told you need more ideas, but you don’t have a solution. If you want change, you have the power to make it happen, but you don’t need it. If you want to change what you want to accomplish, you need a team to support you, but you don’t have the position to lead people. If you want to change the pace, it will be “5 years and 3 years of experience”; If you want to change the original savvy is good, but there is always a layer of fuzzy window… If you believe in the power of believing, believing that ordinary people can achieve extraordinary things, believing that you can meet a better version of yourself. If you want to be a part of the process of growing a front end team with deep business understanding, sound technology systems, technology value creation, and impact spillover as your business takes off, I think we should talk. Any time, waiting for you to write something and send it to [email protected]