Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to transform Vue SSR server rendering

2025-02-25 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 transform Vue SSR server rendering". 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!

SSR rendering of Vue can be used as a brand new project, which requires the installation of dependent modules (node_modules). You can copy the package.json of the project originally created with vue cli 3 to ensure that there is no lack of related modules, and then add the modules needed by SSR.

Mainly vue-server-renderer:

Npm install vue vue-server-renderer-save

Vue-server-renderer is the core of SSR rendering. Bundle renderer is provided to call the renderToString () method to render Vue components into HTML strings. It is important to note that vue-server-renderer and vue must match the version. For example, @ 2.6.11 version of vue must correspond to @ 2.6.11 version of vue-server-renderer.

Route pattern history

For SSR rendering of Vue with vue-router, you must use history as the route mode, because the route in hash mode cannot be submitted to the server. If you used hash mode before, you need to modify it:

Const router = new Router ({

Mode: 'history'

...

})

Two entrances.

The SSR rendering of Vue is generally isomorphic, that is, the business code is a set. Client-side client and server-side server are built respectively through different construction configurations. For webpack construction, two entrances are required to modify vue.config.js to support it. The code is as follows:

Const TARGET_NODE = process.env.WEBPACK_TARGET = = 'node'

Const target = TARGET_NODE? 'server': 'client'

...

ConfigureWebpack: {

/ / point entry to the application's server / client file

Entry: `. / src/entry-$ {target} .js`

/ / this allows webpack to handle dynamic import (dynamic import) in Node applicable way (Node-appropriate fashion)

/ / and when compiling the Vue component

/ / tell `server loader` to send server-oriented code (server-oriented code).

Target: TARGET_NODE? 'node': 'web'

/ / node: TARGET_NODE? Undefined: false

/ / server bundle is told here to use Node style export module (Node-style exports)

Output: {

LibraryTarget: TARGET_NODE? 'commonjs2': undefined

}

/ / devtool: 'source-map'

/ / https://webpack.js.org/configuration/externals/#function

/ / https://github.com/liady/webpack-node-externals

/ / externalize the application dependency module. Can make the server build faster

/ / and generate smaller bundle files.

Externals: TARGET_NODE? NodeExternals ({

/ / do not externalize the dependent modules that webpack needs to handle.

/ / you can add more file types here. For example, the * .vue raw file is not processed

/ / you should also whitelist dependent modules that modify `global` (such as polyfill).

Whitelist: /\ .css $/

}): undefined

Optimization: {

SplitChunks: false

}

}

ChainWebpack: config = > {

Config.module

.rule ('vue')

.use ('vue-loader')

.tap (options = > {

Return merge (options, {

OptimizeSSR: false / / https://vue-loader-v14.vuejs.org/zh-cn/options.html#optimizeSSR

})

})

}

...

You can use entry-client.js and entry-server.js as the entry entry, respectively. If you need to execute two builds at the same time, you can modify the package.json as follows:

"scripts": {

"build": "vue-cli-service build"

"build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build & & vue-cli-service build"

}

For SSR builds in development mode, since there is not much use, only SSR builds in production mode are provided here, and execute the following command:

Npm run build:server

Bundle file

When Vue's SSR rendering server starts, bundle renderer mainly parses two bundle files, namely, vue-ssr-server-bundle.json and vue-ssr-client-manifest.json files, which are generated by webpack construction, and can modify the previous vue.config.js configuration file:

ConfigureWebpack: {

...

Plugins: [

TARGET_NODE? New VueSSRServerPlugin (): new VueSSRClientPlugin ()

]

...

}

Vue-ssr-server-bundle.json mainly stores the mapping information of resources, which is necessary for bundle renderer, while vue-ssr-client-manifest.json is an object clientManifest configuration item, which contains the information of the entire construction process of webpack, so that bundle renderer can automatically derive the content that needs to be injected into the HTML template, thus achieving the best preloading (preload) and prefetching (prefetch) resources, as shown in the following figure:

Simulate window object

When Vue's SSR renders the Vue component (including the root component App.vue and several of its sub-components), because there is no dynamic update, of all the component lifecycle hook functions, only beforeCreate and created are called during SSR rendering. This means that any code in any other lifecycle hook function, such as beforeMount or mounted, will only be executed on the client side. It is also important to note that code that has global side effects during the beforeCreate and created lifecycles should be avoided, such as using setInterval to set up timer. In the pure client (client-side only) code, we can set up a timer and then destroy it during the beforeDestroy or destroyed life cycle. However, since the destroy hook function is not called during SSR, timer will remain forever. To avoid this, move the side effect code to the beforeMount or mounted lifecycle. For SSR rendering, due to the Node.js environment, window objects need to be compatible. It is recommended to use jsdom:

Npm install jsdom-save

Then, in the entry file service.js of SSR (non-entry-server.js), add the following code:

Const jsdom = require ('jsdom')

Const {JSDOM} = jsdom

/ * simulate window object logic * /

Const resourceLoader = new jsdom.ResourceLoader ({

UserAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 13.2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1"

}); / / set UA

Const dom = new JSDOM ('', {

Url:' https://app.nihaoshijie.com.cn/index.html',

Resources: resourceLoader

});

Global.window = dom.window

Global.document = window.document

Global.navigator = window.navigator

Window.nodeis = true / / identify the flag bits of the node environment to the window

/ * simulate window object logic * /

NormalizeFile get undefined file error

When operating SSR rendering, the following error is encountered:

This error usually results in abnormal mapping information in the vue-ssr-client-manifest.json file, resulting in the failure to find the corresponding resource file correctly, which may belong to the bug of VueSSRClientPlugin (), but can be circumvented by webpack configuration to modify the vue.config.js and add the code as follows:

Css: {

SourceMap: true

}

The relevant issue address.

Request SSR first screen data on the server side

For some scenarios with non-static pages on the first screen, the rendering of these pages depends on the back-end data, so the data is pulled on the SSR side, and after the SSR rendering is completed, the data is directly brought to the client for secondary rendering to reduce the number of requests. Therefore, for the data sharing solution, the most appropriate way is to use the Store of Vuex, so it is recommended that the project use Vuex. Modify the entry-server.js code as follows:

/ / call `routing () `to all matching routing components

Promise.all (matchedComponents.map (Component = > {)

If (Component.asyncData) {

Return Component.asyncData ({

Store

Route: router.currentRoute

})

}

}) .then (() = > {

/ / after all prefetch preFetch hook resolve

/ / our store is now populated with the state required by the rendering application.

/ / when we attach the state to the context

/ / and the `template` option is used for renderer

/ / the status is automatically serialized to `window.__INITIAL_STATE__ `and injected into HTML.

Context.state = store.state

Resolve (app)

}) .catch ((err) = > {

Console.error (err)

Reject (err)

})

At the same time, add the asyncData method to the first routing component of the home screen to request data. Note that it is the static method of the component, not the method defined in methods. The code is as follows:

Export default {

Name: 'wecircle'

...

AsyncData ({store}) {

/ / when action is triggered, Promise is returned.

Return store.dispatch ('setWecircleDataListSSR')

}

...

}

The following action and mutation can be written according to normal logic. Finally, when the SSR data is rendered, a window.__INITIAL_STATE__ object is added to the generated HTML. Modify the entry-client.js to assign the data directly to the client for rendering. The code is as follows:

Const {app, router, store} = createApp ()

If (window.__INITIAL_STATE__) {

Store.replaceState (window.__INITIAL_STATE__)

}

The last point to note is that the client code and the server code are isomorphic, but the address of the server requesting data is completely different from that of the client. Therefore, for this scenario, you need to use the previously set window.nodejs flag bit to determine the interface address in different scenarios, so as to treat it differently.

Cookie transparent transmission

When requesting data on the SSR side, you may need to bring the browser's cookie, and the client carries cookie data in the request from the client to the SSR server. However, when the SSR server requests the backend interface, there is no corresponding cookie data. Therefore, when the SSR server makes the interface request, we need to manually get the client's cookie to the back-end server. If axios is used here, you can manually set the headers field of the axios request as follows: get the browser cookie in server.js and use the window object storage:

App.use ('*', (req, res) = > {

...

Window.ssr_cookie = req.cookie

...

})

In axios, add header to plug the cookie in:

Axios.create ({

...

Headers: window.ssr_cookie | | {}

...

})

This allows you to bring the browser's cookie to the SSR server.

No stacktrace on NavigationDuplicated error error

The error itself is due to repeated clicks on the same navigation component, resulting in an error NavigationDuplicated. Because SSR rendering uses hostory mode, when you enter the route for the first time, you will go through multiple navigation (you can see it by adding a beforeEach hook to router), so you will also report a NavigationDuplicated error. This error does not affect your use, but if you need to avoid it, you can use the following code to add it to router.js:

Const originalPush = Router.prototype.push

Router.prototype.push = function push (location, onResolve, onReject) {

If (onResolve | | onReject) return originalPush.call (this, location, onResolve, onReject)

Return originalPush.call (this, location) .catch (err = > err)

}

The relevant issue address.

Support both client-side rendering and server-side rendering

Since Vue's SSR rendering is used, the first thing to consider is the stability of the SSR service. In order to maximize the service availability, when the server rendering hangs up, you need to have fault-tolerant logic to ensure that the page is available. Therefore, the original build related to client rendering should be retained, that is, the page can be used normally by directly accessing index.html. Here, it is directly forwarded through the nginx configuration path. The code is as follows:

Location / index.html {

Return 301 https://$server_name/;

}

The original address accessed through http://www.abc.com/index.html is forwarded to http://www.abc.com/, so that the path= "/" route of vue-router in history mode can be triggered. Different forwarding is configured for client access and service access, as follows:

# client rendering service

Location / {

# add cache to static files

Location ~. *\. (js | css | png | jpeg) (. *) {

Valid_referers * .nihaoshijie.com.cn

If ($invalid_referer) {

Return 404

}

Proxy_pass http://localhost:8080;

Expires 3D cross # 3 days

}

Proxy_pass http://localhost:8080; # static resources go through port 8080

}

# ssr Service

Location = / index_ssr {

Proxy_pass http://localhost:8888; # ssr service uses port 8888

}

Leave only / index_ssr as the entry point for SSR rendering, and then in server.js, process / index_ssr as the path to the home page, and add fault-tolerant logic for SSR rendering, as follows:

If (req.originalUrl = ='/ index_ssr' | | req.originalUrl = ='/ index_ssr/') {

Context.url ='/'

}

...

Renderer (bundle, manifest) .renderToString (context, (err, html) = > {

...

If (err) {

/ / if you find an error, go directly to the client to render

Res.redirect ('/')

/ / recording error messages can be uploaded to the log platform for statistics

Console.error (`error during render: ${req.url} `)

Console.error (err)

}

...

})

The fault-tolerant mechanism for server rendering is not limited to the solution described above, but can also be solved according to the actual scenario.

Integration of PWA and SSR

Since the PWA technology is used in this project, it should be noted that the PWA-related codes and plug-ins only need to be added in the logic of the entry-client.js entry. The SSR server does not need to configure PWA-related logic, such as the previous OfflinePlugin plug-in, which is configured in vue.config.js as follows:

If (TARGET_NODE) {

Plugins.push (new VueSSRServerPlugin ())

} else {

Plugins.push (new VueSSRClientPlugin ())

Plugins.push (new OfflinePlugin ({

/ / request to trigger ServiceWorker event callback

ServiceWorker: {

Events: true

/ / push event logic is written in another file

Entry:'. / public/sw-push.js'

}

/ / Update Policy Select Update all

UpdateStrategy: 'all'

/ / remove some files that do not need caching

Excludes: ['* * / * .map','* * / * .svg','* * / * .png','* * / * .jpg','* / sw-push.js','* / sw-my.js','**/*.json']

/ / add updates to index.html

Rewrites (asset) {

If (asset.indexOf ('index.html') >-1) {

Return'. / index.html'

}

Return asset

}

}))

}

This is the end of "how to transform Vue SSR server rendering". 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.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report