In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-13 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article introduces the relevant knowledge of "how to achieve server-side rendering in the Vue project". In the operation of actual cases, many people will encounter such a dilemma, so let the editor lead you to learn how to deal with these situations. I hope you can read it carefully and be able to achieve something!
The practice of vue-ssr in the project is written before the article.
Due to the diversity of front-end scaffolding, packaging tools, Node and other versions, this paper can not take into account at the same time, what is described in this paper is based on the following technology stack.
Scaffolding: vue-cli3
Packaging tool: webpack4, integrated in vue-cli3 and configured by modifying vue.config.js
Node framework: koa2
Brief introduction
Server-side rendering, that is, the strategy of "isomorphism" is adopted to render part of the front-end code on the server side to reduce the amount of page rendering by the browser.
Generally speaking, the advantages and uses of server-side rendering are as follows:
1. Better SEO
two。 Faster page loading speed
3. Complete the data loading on the server side
However, it should be noted that while server-side rendering improves client performance, it also brings the problem of higher server load. The advantages and disadvantages need to be weighed in the development of the project.
How to implement server-side rendering in Vue project? Some thoughts before doing Vue-ssr
1.Vue uses Vue instances as the basic unit when rendering pages. When rendering on the server side, should Vue instances also be rendered?
two。 The relationship between the user and the client is one-to-one, while the relationship with the server is many-to-one. How to avoid the problem of data sharing between multiple users on the server side?
3. How to realize the isomorphism strategy? That is, to enable the server side to run the front-end code?
4. How should server-side rendered Vue projects, development environment and production environment be deployed respectively? What's the difference?
5. How to ensure that the modified code of server-side rendering can still be accessed directly by accessing static resources?
These thoughts will be reviewed at the end of the article.
Concrete realization scheme
Vue officially provides [vue-server-renderer] package to realize server rendering of Vue project. The installation method is as follows:
Npm install vue-server-renderer-save
There are some issues to be aware of when using vue-server-renderer:
The 1.vue-server-renderer version must be consistent with vue
2.vue-server-renderer can only be run on node. Node.js6+ version is recommended.
First, the simplest implementation
Vue-server-renderer provides us with a [createRenderer] method that supports rendering a single Vue instance and outputting rendered html strings or node-readable stream streams.
/ / 1. Create Vue instance const Vue = require ('vue'); const app = new Vue ({template:'',}); / / 2. Introduce renderer method const renderer = require ('vue-server-renderer'). CreateRenderer (); / 3-1. Render the Vue instance as the html string renderer.renderToString (app, (err, html) = > {}); / / orrenderer.renderToString (app) .then ((html) = > {}, (err) = > {}); / / 3-2. Render the Vue instance as stream stream const renderStream = renderer.renderToStream (app); / / operate / / event available values of 'data',' beforeStart', 'start',' beforeEnd', 'end',' error' and other renderStream.on (event, (res) = > {}) in the callback by subscribing events
But in general, there is no need to create Vue instances on the server side and render them. Instead, we need to render the Vue instances of each SPA in the front-end Vue project. Based on this, vue-server-renderer provides us with the following server-side rendering scheme.
Second, the complete realization
The complete implementation process is divided into three modules: [template Page] (HTML), [client side] (Client Bundle) and [Server side] (Server Bundle) as shown in the figure below. The functions of the three modules are as follows:
Template page: a html framework for client-side and server-side rendering that enables client-side and server-side rendering of pages in this framework
Client: execute only on the browser side, injecting static resources such as js, css and so on into the template page
Server side: execute on the server side only, render the Vue instance as a html string and inject it into the corresponding location of the template page
The entire service construction process is divided into the following steps:
1. Package Vue applications as client Bundle executable on the browser side through webpack
two。 Package the Vue application as a server-side Bundle executable on the Node side through webpack
The 3.Node side calls the server-side Bundle rendering Vue application, and sends the rendered html string and client-side Bundle to the browser.
4. When received by the browser, the client-side Bundle is called to inject static resources into the page and match the rendered page on the server side.
It should be noted that the client-side and server-side rendered content needs to be matched for normal page loading, and some page loading anomalies are described in detail below.
Third, specific code realization 1. Vue application program transformation
In SPA mode, there is an one-to-one relationship between users and Vue applications, while in SSR mode, because Vue instances are rendered on the server side, and the server is shared by all users, the relationship between users and Vue applications becomes many-to-one. This results in multiple users sharing the same Vue instance, resulting in mutual contamination of data in the instance.
To solve this problem, we need to modify the entrance to the Vue application, change the creation of Vue instances to "factory mode", and create new Vue instances each time you render, so as to avoid users sharing the same Vue instance. The specific transformation code is as follows:
/ / router.jsimport Vue from 'vue';import Router from' vue-router';Vue.use (Router); export function createRouter () {return new Router ({mode: 'history', routes: [],});} / / store.jsimport Vue from' vue';import Vuex from 'vuex';Vue.use (Vuex) Export function createStore () {return new Vuex.Store ({state, actions: {}, mutations: {}, modules: {},});} / / main.jsimport Vue from 'vue';import App from'. / App.vue';import {createRouter} from'. / router';import {createStore} from'. / store';export function createApp () {const router = createRouter (); const store = createStore () Const app = new Vue ({router, store, render: (h) = > h (App),}); return {app, router, store};}
It should be noted that we need to configure the modules used inside Vue instances such as vue-router and vuex to "factory mode" to avoid routing, status, and so on being shared among multiple Vue instances.
At the same time, because we need to use both client-side and server-side modules in the SSR process, we need to configure client-side and server-side portals.
The client portal is configured as follows:
/ / entry-client.jsimport {createApp} from'. / main';const {app, router, store} = createApp (); if (window.__INITIAL_STATE__) {store.replaceState (window.__INITIAL_STATE__);} router.onReady (() = > {app.$mount ('# app');})
As mentioned above, the function of the client Bundle is to inject static resources and secondary rendering work into the page after the browser receives the html string rendered by the server, so we only need to mount the Vue instance to the specified html tag in the client entry of the Vue application as before.
At the same time, if there is a data prefetch operation on the server side during rendering, the data in store will be first injected into [window.__INITIALSTATE\ _]. In the client, we need to re-assign the value in window.__INITIALSTATE\ _ to store.
The server-side portal is configured as follows:
/ / entry-server.jsimport {createApp} from'. / main';export default (context) = > {return new Promise ((resolve, reject) = > {const {app, router, store} = createApp (); / / set the server-side router location router.push (context.url) / wait until router parses the possible asynchronous components and hook functions router.onReady (() = > {const matchedComponents = router.getMatchedComponents (); / / matches the mismatched route, executes the reject function and returns 404 if (! matchedComponents.length) {return reject ({code: 404}) } Promise.all (matchedComponents.map ((Component) = > {if (Component.extendOptions.asyncData) {const result = Component.extendOptions.asyncData ({store, route: router.currentRoute, options: {},}) Return result;}}) .then (() = > {/ / the state is automatically serialized to window.__INITIAL_STATE__, and injected into HTML. Context.state = store.state; resolve (app);}) .catch (reject);}, reject);});}
The server needs to dynamically match the Vue components that need to be rendered according to the user's request, and set up modules such as router and store.
For router, you only need to call the push method of vue-router to switch routes
For store, you need to detect and call the [asyncData] method in the Vue component to initialize the store, and assign the initialized state to the context. When rendering, the server will serialize the state in the context to window.__INITIALSTATE\ _ and inject it into the html. The operation and processing of data prefetching will be described in detail in the following section "Server-side data prefetching".
2. Webpack packaging logic configuration
Because the server-side rendering service needs two packages: client-side Bundle and server-side Bundle, it needs to be packaged twice with webpack to package client-side and server-side respectively. Here we can write the packaging logic through the shell script:
#! / bin/bashset-eecho "Delete old dist file" rm-rf distecho package SSR server side "export WEBPACK_TARGET=node & & vue-cli-service buildecho" move server side Json file out of dist "mv dist/vue-ssr-server-bundle.json bundleecho" package SSR client "export WEBPACK_TARGET=web & & vue-cli-service buildecho" move server side Json file back to dist "mv bundle dist/vue-ssr-server-bundle.json"
In the shell command, we configured the environment variable [WEBPACK_TARGET] to provide webpack with an identity that can identify the client / server side packaging process.
At the same time, vue-server-renderer provides us with [server-plugin] and [client-plugin] two webpack plug-ins for packaging server-side and client-side Bundle, respectively. The following is the specific configuration for packaging with these two plug-ins in the webpack configuration file:
/ / vue.config.jsconst path = require ('path'); const nodeExternals = require (' webpack-node-externals'); const VueSSRServerPlugin = require ('vue-server-renderer/server-plugin'); const VueSSRClientPlugin = require (' vue-server-renderer/client-plugin'); const merge = require ('lodash.merge'); const TARGET_NODE = process.env.WEBPACK_TARGET = =' node';const entry = TARGET_NODE? 'server':' client';const isPro = process.env.NODE_ENV! = 'development' Module.exports = {/ * static resources when requested, if the request path is relative, it will be accessed based on the current domain name * in order to ensure the normal loading of static resources during local development Start a static resource server on port 8080 * this process will be described in detail in the fourth section "Node Development Environment configuration" * / publicPath: isPro?'/': 'http://127.0.0.1:8080/', outputDir:' dist', pages: {index: {entry: `src/pages/index/entry-$ {entry} .js` Template: 'public/index.html'}}, css: {extract: isPro? True: false,}, chainWebpack: (config) = > {/ / disable the default server-side rendering function config.module .rule ('vue') .use (' vue-loader') .tap ((options) = > {merge (options, {optimizeSSR: false,}) in vue-loader) }) }, configureWebpack: {/ / source-map file mapping needs to be enabled, because the server side will query the file through the map file mapping relationship in Bundle when rendering. Devtool: 'source-map', / / the server side runs in the Node environment Need to be packaged as a Node.js-like environment available package (use Node.js require to load chunk) / / the client runs in the browser, need to be packaged into a target: TARGET_NODE available package in the similar browser environment? 'node':' web', / / turns off polyfill node: TARGET_NODE for node variables and modules? Undefined: false, output: {/ / configuration module exposure mode, server side uses module.exports mode, client side uses default var variable mode libraryTarget: TARGET_NODE? 'commonjs2': undefined,}, / / externalize the application dependency module. Can make the server build faster externals: TARGET_NODE? NodeExternals ({whitelist: [/\ .css $/],}): undefined, plugins: [/ / package as client / server Bundle TARGET_NODE based on previously configured environment variables? New VueSSRServerPlugin (): new VueSSRClientPlugin (),],},}
Combined with the code and comments of the webpack configuration file, let's go back to the packaged shell script to comb through the packaging process.
1) package server-side Bundle
Setting the [WEBPACK_TARGET] environment variable to node,webpack first sets the portal entry to the server-side portal [entry-server.js], which is packaged through the plug-in [server-plugin].
After packaging, a [vue-ssr-server-bundle.json] file is generated under the dist folder (the name is the default name, which can be set in the plug-in), which has three properties: entry, files, and maps. The entry attribute is the packaged entry file path string, and the files attribute is a set of packaged [file path-file content key-value pairs]. The contents of the compiled file are stored in the files attribute of the json file, and maps is a set of file resource configuration information compiled through [source-map].
/ vue-ssr-server-bundle.json {"entry": "js/ index.[ hash] .js", "files": {"js/ index.[ hash] .js": ",}," maps ": {" js/ index.[ hash] .js ": {}
2) temporarily remove the packaged files from the dist folder on the server side
Because it needs to be packaged twice, the previous dist folder will be deleted when the client is packaged. In order to avoid server-side Bundle loss, it needs to be temporarily moved out of the dist folder.
3) package client Bundle
When packaging the client, changing the [WEBPACK_TARGET] environment variable to web,webpack sets the portal entry to the client portal [entry-client.js], which is packaged through the plug-in [client-plugin].
After packaging, the packaged static resource file of the front-end project and the [vue-ssr-client-manifest.json] file are generated under the dist folder, where the static resource file can be deployed to the server to provide traditional SPA services. The vue-ssr-client-manifest.json file contains publicPath, all, initial, async, and modules attributes, which are used as follows:
PublicPath: the root-relative path to access static resources, which is the same as publicPath in webpack configuration
All: all static resource file paths after packaging
Initial: files that need to be loaded during page initialization will be configured into preload when the page is loaded
Async: the file that needs to be loaded when the page is redirected will be configured in prefetch when the page is loaded
Modules: the sequence number of the files contained in each module of the project, corresponding to the order of the files in the all
/ / vue-ssr-client-manifest.json {"publicPath": "/", "all": [], "initial": [], "async": [], "modules": {"moduleId": [fileIndex]}}
4) move the server-side Bundle temporarily removed from dist back to the dist folder
3. Configuration of production environment on Node side
After the above packaging process, we have packaged the project into three parts: [vue-ssr-server-bundle.json], [vue-ssr-client-manifest.json] and [front-end static resources]. Then we need to use the packaged contents of these three modules for server-side rendering on the Node side.
1) the difference between Renderer and BundleRenderer
There are two main classes [Renderer] and [BundleRenderer] for server-side rendering in vue-server-renderer.
In the "simplest implementation" section, we mentioned the [createRenderer] method, which is actually creating a Renderer object for rendering, which contains renderToString and renderToStream methods for rendering Vue instances into html strings or generating node readable streams.
In the "complete implementation" section, we use the method of packaging the project as a client-side and server-side Bundle. In this case, we need to use another method of vue-server-renderer [createBundleRenderer] to create BundleRenderer objects for rendering.
/ / vue-server-renderer/build.dev.js createBundleRenderer method function createBundleRenderer (bundle, rendererOptions) {if (rendererOptions = void 0) rendererOptions = {} in source code; var files, entry, maps; var basedir = rendererOptions.basedir; / / load bundle if given filepath if (typeof bundle = 'string' & & /\ .js (on)? $/ .test (bundle) & & path$2.isAbsolute (bundle)) {/ / parse bundle file} entry = bundle.entry Files = bundle.files; basedir = basedir | | bundle.basedir; maps = createSourceMapConsumers (bundle.maps); var renderer = createRenderer (rendererOptions); var run = createBundleRunner (entry, files, basedir, rendererOptions.runInNewContext) Return {renderToString: function (context, cb) {run (context). Catch ((err) = > {}). Then ((app) = > {renderer.renderToString (app, context, (err, res) = > {cb (err, res);}) }, renderToStream: function (context) {run (context). Catch ((err) = > {}). Then ((app) = > {renderer.renderToStream (app, context);});}
As you can see in the above createBundleRenderer method code, the BundleRenderer object also contains [renderToString] and [renderToStream] methods, but unlike the createRenderer method, it receives a server-side Bundle file or file path. During execution, it will first determine whether it is receiving an object or a string, and if it is a string, use it as a file path to read the file. After reading the Bundle file, the relevant attributes of the server-side Bundle described in the [Webpack Packaging Logic configuration] section are parsed. At the same time, build the Renderer object and call the renderToString and renderToStream methods of the Renderer object.
As you can see, the only difference between BundleRenderer and Renderer is that there is one more step in the process of Bundle parsing, and then Renderer is still used for rendering.
2) Code implementation
After learning the difference, we will use the BundleRenderer object for server-side rendering. The code is as follows:
/ / prod.ssr.jsconst fs = require ('fs'); const path = require (' path'); const router = require ('koa-router') (); const resolve = file = > path.resolve (_ _ dirname, file); const {createBundleRenderer} = require (' vue-server-renderer'); const bundle = require ('vue-ssr-server-bundle.json'); const clientManifest = require (' vue-ssr-client-manifest.json') Const renderer = createBundleRenderer (bundle, {runInNewContext: false, template: fs.readFileSync (resolve ('index.html'),' utf-8'), clientManifest,}); const renderToString = (context) = > {return new Promise ((resolve, reject) = > {renderer.renderToString (context, (err, html) = > {err? Reject (err): resolve (html);}; router.get ('*', async (ctx) = > {let html ='; try {html = await renderToString (ctx); ctx.body = html;} catch (e) {}}); module.exports = router
You can see in the code that the entire rendering process is divided into three steps:
1. Get server-side, client-side, template files, and build BundleRenderer objects by createBundleRenderer method
two。 When a user request is received, the renderToString method is called and the request context is passed. In this case, the server-side rendering service calls the server-side entry file entry-server.js to render the page.
3. Configure the rendered html string into the body of response and return to the browser side.
4. Node development environment configuration
Vue officially only provides a server-side rendering solution for Vue instances and packaged Bundle packages, but we will face the following problems in the development environment:
1) webpack stores the packaged resource file in memory, how to get the json file of the packaged Bundle?
2) how to package and run both the client and the server in the development environment?
Here, our strategy is to use webpack to start the front-end project of the development environment and get the client static resources in memory through http requests [vue-ssr-client-manifest.json] At the same time, in Node, use [@ vue/cli-service/webpack.config] to get the server-side webpack configuration, use the webpack package to package the server-side Bundle directly, listen and get the latest [vue-ssr-server-bundle.json] file. In this way, we get the client-side and server-side files, and then the process is the same as in the production environment.
First, let's look at the configuration of the npm command:
/ / package.json {"scripts": {"serve": "vue-cli-service serve", "server:dev": "export NODE_ENV=development WEBPACK_TARGET=node SSR_ENV=dev & & node-- inspect server/bin/www", "dev": "concurrently\" npm run serve\ "\" npm run server:dev\ "}}
The serve command starts the front-end service in client mode, and webpack packages the client Bundle in the development environment and stores it in memory.
The server:dev command gets the webpack configuration packaged by server-side Bundle in the development environment by setting the environment variables [NODE_ENV] and [WEBPACK_TARGET], and makes the node application recognize the current environment as the development environment by setting the environment variable [SSR_ENV].
Dev command is the running command of the development environment, through the concurrently command two processes execute serve command and server:dev command.
Next, let's take a look at the server-side rendering service code for the development environment:
Const webpack = require ('webpack'); const axios = require (' axios'); const MemoryFS = require ('memory-fs'); const fs = require (' fs'); const path = require ('path'); const Router = require (' koa-router'); const router = new Router (); / / webpack configuration file const webpackConf = require ('@ vue/cli-service/webpack.config'); const {createBundleRenderer} = require ("vue-server-renderer"); const serverCompiler = webpack (webpackConf); const mfs = new MemoryFS () ServerCompiler.outputFileSystem = mfs;// listens for file modifications and compiles the latest vue-ssr-server-bundle.jsonlet bundle;serverCompiler.watch in real time ({}, (err, stats) = > {if (err) {throw err;} stats = stats.toJson (); stats.errors.forEach (error = > console.error (error)); stats.warnings.forEach (warn = > console.warn (warn) Const bundlePath = path.join (webpackConf.output.path, 'vue-ssr-server-bundle.json',); bundle = JSON.parse (mfs.readFileSync (bundlePath,' utf-8')); console.log ('New bundle generated.');}) const handleRequest = async ctx = > {if (! bundle) {ctx.body =' wait for webpack to be packaged before visiting'; return } / / get the latest vue-ssr-client-manifest.json const clientManifestResp = await axios.get (`http://localhost:8080/vue-ssr-client-manifest.json`); const clientManifest = clientManifestResp.data; const renderer = createBundleRenderer (bundle, {runInNewContext: false, template: fs.readFileSync (path.resolve (_ _ dirname, 'index.html'),' utf-8'), clientManifest,}); return renderer } const renderToString = (context, renderer) = > {return new Promise ((resolve, reject) = > {renderer.renderToString (context, (err, html) = > {err? Reject (err): resolve (html);}; router.get ('*, async (ctx) = > {const renderer = await handleRequest (ctx); try {const html = await renderToString (ctx, renderer); console.log (html); ctx.body = html;} catch (e) {}}); module.exports = router
As can be seen from the code, the node server-side rendering service flow of the development environment is basically the same as that of the production environment, and the difference lies in the different ways of obtaining Bundle on the client side and the server side.
In a production environment, node directly reads locally packaged static resources
In the development environment, we first use axios to send http requests to get the client Bundle of the front-end project packaged in memory. At the same time, use the [@ vue/cli-service/webpack.config] package to obtain the webpack configuration under the current environment (NODE_ENV=development WEBPACK_TARGET=node SSR_ENV=dev), use the webpack package and the webpack configuration to run the server-side directly in the current node program, and get the server-side Bundle from it.
The subsequent process is the same as the production environment.
5. Node application configuration
So far, we have configured the basic files for server-side rendering, and of course we need a node application to start the service.
/ / app.jsconst Koa = require ('koa'); const app = new Koa (); const path = require (' path'); const koaStatic = require ('koa-static'); const koaMount = require (' koa-mount'); const favicon = require ('koa-favicon'); const isDev = process.env.SSR_ENV = =' dev';// routesconst ssr = isDev? Require ('. / dev.ssr'): require ('. / prod.ssr'); / / Static File Serverconst resolve = file = > path.resolve (_ _ dirname, file); app.use (favicon (resolve ('. / favicon.ico')); app.use (koaMount ('/', koaStatic (resolve ('.. / public'); app.use (ssr.routes (), ssr.allowedMethods ()); module.exports = app
In the node entry file, determine whether the current environment is a development environment or a production environment according to the environment variable [SSR_ENV], and call the corresponding server-side rendering file.
It should be noted that if the publicPath configured in webpack is a relative path, after the client injects static resources with the relative path into the page, the browser will access the static resources based on the current domain name / IP. If the server has not done other proxies (other than the node service), the requests for these static resources will be sent directly to our node application. The most convenient way is to build a static resource server in the node application to proxy the packaged static resources (js, css, png, jpg, etc.), using [koa-mount] and [koa-static] middleware. At the same time, the favicon.ico icon can be mounted using [koa-favicon] middleware.
Server-side data prefetching
Server-side data prefetching is a feature that injects data into Vue instances when rendering Vue applications on the server. It is commonly used in the following two cases:
1. The amount of data during page initialization is large, which affects the loading speed of the first screen.
two。 Some of the data is not available on the browser.
For data prefetching, the solution provided by the official vue-server-renderer package consists of two steps:
1. Server-side data prefetching
Server-side data prefetching is mainly aimed at the problem of stutter loading on the first screen caused by slow data reading on the client side. After the Vue instance on the server is rendered, the data is injected into the store of the Vue instance. The code can be reviewed in the section "Vue Application Transformation". The specific process is as follows:
1) change store to factory mode, which has been mentioned above, so I won't repeat it.
2) register the static method asyncData in the vue instance and provide it to the server for calling. The function of this method is to call the action method in store and call the API to get the data.
/ / vue component file export default Vue.extend ({asyncData ({store, route, options}) {return store.dispatch ('fetchData', {options,});},})
3) call the asyncData method in the server entry [entry-server.js] to obtain the data, and store the data in [window.__INITIALSTATE\ _]. This configuration is visible in the [entry-server.js] file configuration above.
4) remount the data in [window.__INITIALSTATE\ _] to the store in the client entry [entry-client.js].
/ / entry-client.jsconst {app, router, store} = createApp (); if (window.__INITIAL_STATE__) {store.replaceState (window.__INITIAL_STATE__);} router.onReady (() = > {app.$mount ('# app');}); 2. Client data prefetching
Client-side data prefetching is actually a supplement to server-side data prefetching. In view of the scenario, after the rendered page is delivered to the browser by the server, the vue virtual route on the browser will take over the routing and other tasks, instead of sending the page request to the server. As a result, the problem of data prefetching on the server will not be triggered after switching to the new page.
To solve this problem, the strategy of client-side data prefetching is to operate in the client entry [entry-client.js]. When a route switch is detected, data prefetching is given priority (in fact, the operation flow of server-side data prefetching is replicated in the client), and then the vue application is mounted after the data is loaded.
Specifically, we need to transform [entry-client.js]:
/ / entry-client.jsconst {app, router, store} = createApp (); if (window.__INITIAL_STATE__) {store.replaceState (window.__INITIAL_STATE__);} router.onReady () = > {router.beforeResolve ((to, from, next) = > {const matched = router.getMatchedComponents (to); const prevMatched = router.getMatchedComponents (from) / / find out the different components of the two matching lists, and do not do repeated data reading let diffed = false const activated = matched.filter ((c, I) = > {return diffed | | (diffed = (prevMatched [I]! = = c));}); if (! activated.length) {return next () } Promise.all (activated.map (c = > {if (c.extendOptions.asyncData) {return c.extendOptions.asyncData ({store, route: to, options: {},}) }}) .then (() = > {next ();}) .catch (next);}) app.$mount ('# app');}); Note 1, page loading exception
After the server-side rendered html string is sent to the browser, the client needs to match its template, and if the match is not successful, the page cannot be rendered normally, so in some cases, abnormal page loading occurs, mainly in the following categories.
1. Lack of client-side or server-side recognizable rendering identity in the template page
This problem can affect the static resource injection on the client side or the rendering of Vue instances on the server side. For the client side, a recognizable h6 tag element is generally required to mount. In this article, a div tag with id as app is used; for the server side, a comment ID that can be recognized by the official vue-server-renderer package is required. The complete template page code is as follows:
/ / index.html template page
two。 Client side and server side routing are different
The problem of route mismatch occurs when the user sends a / a routing page request to the server, and the server renders the component corresponding to the / b route into a html string and returns it to the browser. When the client detects that the rendered Vue route is inconsistent with the route in the current browser, the client will re-switch the page to the page under / a route, resulting in a second refresh of the page.
3. Page static resource loading failed
Because when the page static resource uses the relative path, the browser will make the request for the static resource based on the current domain name / IP, so it will request the static resource to our Node service. If we only do the server-side rendering service, and do not build a static resource server to proxy static resources, there will be the problem of static resource loading failure.
The 4.H5 label is incompletely written.
The server will automatically complete the H5 tag when rendering the page, such as
The tag automatically completes the unwritten or
Second, multi-page project packaging
The relationship between client-plugin and server-plugin plug-ins in the vue-server-renderer package and SPA pages is one-to-one, that is, a SPA page corresponds to a set of client-side Bundle and server-side Bundle, that is, a client-side json file and a server-side json file correspond to a SPA application. If we create multiple SPA pages in the project, the client-plugin and server-plugin plug-ins will report an error indicating that there is more than one entry entry when packaging, which does not match properly.
But there are many cases where we need to have multiple SPA pages in a project. For this problem, we can use the shell script to call the npm command and use webpack to package multiple times, while in webpack, we can do dynamic SPA page entry entry matching according to the command parameters. In fact, we can think of this as breaking down a multi-SPA project into multiple single SPA projects.
3. AsyncData needs to return Promise object
Because the data prefetching and store initialization in the asyncData function is an asynchronous operation, the server-side rendering needs to return the rendered page to the browser after the data prefetching is completed. So you need to set the return value of asyncData to the Promise object, just as the action object in vuex also needs to return a Promise object.
Fourth, the server calls to the Vue hook.
When the Vue instance component is rendered on the server side, only beforeCreate and created hooks will be triggered. Therefore, we need to pay attention to the following issues:
1. The contents of page initialization should be placed in beforeCreate and created hooks as far as possible.
two。 Logic that takes up global memory, such as timers, global variables, closures, etc., should not be placed in beforeCreate or created hooks as far as possible, otherwise you will not be able to log out in the beforeDestory method, resulting in memory leakage.
Fifth, the server rendering template page and the SPA application template use the same html page.
Sometimes, for convenience, ease of management, and simplicity of the project, we want to directly use the template page of the SPA application as the template page for server-side rendering. It is important to note that the template page rendered on the server side has one more comment mark than the SPA application template page. When webpack is packaged, the comments in the template applied by SPA will be deleted.
For this problem, you can set the SPA application template page not to be packaged in the webpack configuration as follows:
/ / vue.config.jsmodule.exports = {chainWebpack: (config) = > {config .plugin ('html-index') .tap (options = > {options [0] .minify = false; return options;});},}
Vue-cli3 registers a html plug-in for each SPA page to manage the webpack configuration. It should be noted that when the project is a single entry, the name of the plug-in is' html';, and the project is multi-entry (that is, if the pages attribute is configured, even if there is only one entry in the pages, it will be recognized as a "multi-entry project"), the plug-in name is `html-$ {entryName} `, where entryName is the entry entry name.
6. Client-side and server-side common js packages need to support both browser-side and node-side
When js packages are shared between client and server, mainly in the scenario of data prefetching, packages with "isomorphic" strategy must be used, such as using axios instead of vue-resource.
Review
At the beginning, we have thought about some problems of server-side rendering and made answers in the article. Let's review them one by one here.
1.Vue uses Vue instances as the basic unit when rendering pages. When rendering on the server side, should Vue instances also be rendered?
The official [vue-server-renderer] package provides the method to render Vue instances, and provides two objects, Renderer and BundleRenderer, to render "single Vue instance" and "Vue instance in Vue project" respectively. The common way is the latter, which dynamically matches the Vue instance to be rendered on the server side according to the route requested by the user.
two。 The relationship between the user and the client is one-to-one, while the relationship with the server is many-to-one. How to avoid the problem of data sharing between multiple users on the server side?
Vue server-side rendering adopts client-side and server-side cooperative rendering scheme.
The client is responsible for loading static resources and adopts singleton mode.
The server is responsible for rendering Vue instances, using factory mode, that is, all places that may produce "closures" or "global variables" need to be transformed into factory mode, including but not limited to creating Vue instances, Vuex instances (store), module modules in store, vue-router instances, other public js configuration files, and so on.
3. How to realize the isomorphism strategy? That is, to enable the server side to run the front-end code?
First of all, the project is packaged through webpack. According to the different environment variables on the client side and the server side, the project is packaged into a browser-side recognizable pattern and a Node-side recognizable commonjs2 pattern, respectively.
Secondly, some common js packages are developed with compatible browsers and Node packages, such as interface requests can be processed by axios.js.
4. How should server-side rendered Vue projects, development environment and production environment be deployed respectively? What's the difference?
What they have in common:
No matter which environment, the server-side rendering scheme needs to use two Bundle on the client side and the server side to render together, so the project needs to be packaged twice. Among them, the client-side Bundle includes the browser-recognizable static files originally packaged by the front-end project, and the client-side Bundle entry files; the server-side Bundle packages the project into commonjs2 mode and injects it into the json file using source-map.
Differences:
First of all, the deployment of the production environment is relatively simple and rough, that is, the packaged client-side and server-side Bundle are placed on the server and run using a node service.
However, the deployment of the development environment is relatively complex because the client after the webpack package is stored in memory. The scenario used in this article is to read the client-side Bundle through http requests, and to package, read, and listen to the server Bundle directly using the webpack package in Node.
5. How to ensure that the modified code of server-side rendering can still be accessed directly by accessing static resources?
To solve this problem, one solution is to proxy all static resource requests in the Node service and forward the static resources back to the browser by http forwarding; the other is the relatively simple and fast way used in this article to build a static resource server in the Node service and mount all static resources to a specific route.
This is the end of the content of "how to achieve server-side rendering in Vue project". Thank you for reading. If you want to know more about the industry, you can follow the website, the editor will output more high-quality practical articles for you!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.