Public account: wechat search front end tool person; Take more dry goods
Refer to official documentation for better results vue SSR
Why add SSR:
- Large screen data visualization requirements (
Maps, tables, pie charts, animations
And so on) - Plus background check data is often obtained
All data, all year, all month, all day
Data such as Too much data
Plus the rendering of the animation causes the wait time to fully render the interactive page to be a bit…There were a lot of holes in the construction process
, can refer to the source directory and version information, really notclone
Project code configuration all delete, their own- As a
The interview
Hot, important means to optimize projects; You have to Pay attention to
: Not recommended for mature projectsssr
, change too much, will certainly affect; It is recommended to have separate projects for needsssr
Page pull away in aggregation- Next article VUE-CLI3 SSR and Thermal overload
The source address
:https://github.com/laijinxian/vue2.x-ssr-template/tree/master/vue-cli2.x-ssr
To astar
呗
Existing VUE SSR schemes include:
Nuxt.js
: depends on theNuxt.js
Build the projectvue-server-renderer
(This article selected
); Easy to expand, but also conducive to their ownssr
Better understand- Alternatives:
prerender-spa-plugin
(Pre-render) is also a good option
I. Differences and principles between client side rendering and server side rendering
- Client rendering: VUE outputs vUE components through the virtual DOM in the browser to generate DOM and manipulate DOM
- Server-side rendering: Render a component as HTML strings (HTML only) on the server side and send them directly to the browser; In combination with CSS and JS, these static tags are styled and interactively “activated” into fully interactive applications on the client side
Two, SSR advantages:
Better SEO, thanks to search engine crawler crawler tools can view fully rendered pages directly
If your application initially displays a loading chrysanthemum graph and then fetps the content via Ajax, the fetching tool does not wait for the asynchronism to complete before fetching the page content. So the search engine is not grabbing important information about your site, just the initial index.html structure with no content.
Better user experience and retention
Reason: When a page has not seen the content they are interested in within 3s, most people simply quit the page; Faster content arrival times, especially for slow network conditions or slow-running devices. You don’t have to wait for all the JavaScript to download and execute before the server renders the markup, so your users will see the fully rendered page more quickly; Especially the initial rendering needs to load many external chain JS, CSS pages are particularly prominent advantages;
Three, SSR shortcomings:
Development conditions are limited
Reason: Browser-specific code (except for beforeCreate and Create life cycles) is not executed on the server, window and document do not exist, and some listener functions such as timer and addEventListener cannot be released in time) can only be used in some life cycle hook functions. Some external extension libraries may require special processing to run in a server rendering application; Because the SSR server simply spits out the HTML string, it does not render the DOM structure, so there is no beforeMount and Mounted, and it does not update it
Increased development difficulty, project construction, configuration, deployment
Reason: It is necessary to know node technology and configure and deploy different clients and servers. You also need to be aware of memory issues and server stress during development
More server-side load
Reason: Rendering a full application in Node.js is obviously more cpu-intensive than a server that just serves static files, so if you expect to use it in high-traffic environments, plan for the server load and use caching wisely
Four, SSR points to note:
Data acquisition problem asynchronously
- Prefetch all required data through the server to store
Vuex
In, browser-side render directly flushvuex
Value render page.
Child components depend on parent component interface data
- Component life cycle
BeforeCreate, Create
Will execute twice, both server and client; So page data assignment should be placed inmounted
Life cycle, so that the component can get the latest data passed by the parent component
streaming
renderToStream
renderToStream
applicationbigpipe
Technology can continually return a stream to the browser; File loading browsers can display things early and are friendly for those that don’t rely on background data and static pages in real time- Depending on context data populated by component lifecycle hook functions, streaming mode is not recommended
renderToString
Adapt to use some real-time according to the background interface data rendering page
Page-level caching always renders the same content official documents for all users
- Implementation of the Node. Js: Using the name
micro-caching
To dramatically improve the application’s ability to handle high traffic
Component level caching of official documents
- lru-cache:
serverCacheKey: props => props.item.id
; - A unique must be defined
name
Option, using a unique name, one component for each cache key: you don’t have to worry about both components returning the same key
Iv. Formal start of code transformation
Why should the code be modified in this way? Please refer to the official document for more detailed explanation
1. vue-router
Expose the constructor to the server call
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export function createRouter () {
return new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'HelloWorld',
component: () => import('@/pages/HelloWorld')
},
{
path: '/item',
name: 'Item',
component: () => import('@/pages/Item')
}
]
})
}
Copy the code
2. app.js
I put themain.js
Changed toapp.js
Corresponding,webpac
The configuration needs to be corrected.
import Vue from 'vue' import App from './App.vue' import { createRouter } from './router' import { createStore } from './store' import { sync } from 'vuex-router-sync' Vue.mixin({ beforeMount () { const { asyncData } = this.$options if (asyncData) {// Assign the fetch data operation to the Promise // so that in the component we can run 'this.dataPromise.then(...) when the data is ready. DataPromise = asyncData({store: this.$store, route: This.$route})}}}) Export function createApp () {const router = createRouter() const store = createStore() // Synchronize route state to store sync(store, router) Render router and store const app = new Vue({router, store, render: h => h(app)}) // Expose router, store and store return { app, router, store } }Copy the code
3. src
Under the newentry-client.js
及 entry-server.js
// entry-client.js import { createApp } from './app' const { app, router, store } = createApp() if (window.__INITIAL_STATE__) { store.replaceState(window.__INITIAL_STATE__) } router.onReady(() // Add a route hook function to handle asyncData. // Execute after the initial resolve route so that we do not double-fetch the existing data. // Use 'router.beforeresolve ()' to ensure that all asynchronous components are resolved. router.beforeResolve((to, from, next) => { const matched = router.getMatchedComponents(to) const prevMatched = router.getMatchedComponents(from) // We only care about non-pre-rendered components // so we compare them, Let diffed = false const activated = matched. Filter ((c, i) => { return diffed || (diffed = (prevMatched[i] ! == c)) }) if (! Value of activated. Length) {return next()} All (activated. Map (c => {if (c.syncdata) {return c.syncdata ({store, route: To})}})). Then (() = > {/ / stop loading indicator (loading indicator) next ()}). The catch (next)}) app. $mount (' # app, true)})Copy the code
// entry-server.js import { createApp } from './app' export default context => { return new Promise((resolve, reject) => { const { app, router, store } = createApp() router.push(context.url) router.onReady(() => { const matchedComponents = router.getMatchedComponents() if (! matchedComponents.length) { return reject(new Error({ code: 404}))} // Call 'asyncData()' promise.all (matchedComponents. Map (Component => {if (component.asyncData) { return Component.asyncData({ store, route: Router.currentroute})}})).then(() => {// After all preFetch hooks resolve, // our store is now populated with the state needed to render the application. // When we attach the state to the context, // and the 'template' option is used for renderer, // the state is automatically serialized to 'window.__INITIAL_STATE__' and injected into HTML. context.state = store.state resolve(app) }).catch(reject) }, reject) }) }Copy the code
4. build
Under the newwebpack.client.conf.js
及 webpack.server.conf.js
// webpack.client.conf.js const path = require('path') const merge = require('webpack-merge') const baseConfig = require('./webpack.base.conf') const HtmlWebpackPlugin = require('html-webpack-plugin'); const VueSSRClientPlugin = require('vue-server-renderer/client-plugin') module.exports = merge(baseConfig, { entry: { client: path.resolve(__dirname, '.. /src/entry-client.js') }, plugins: Json '. New VueSSRClientPlugin(), // This separates the Webpack runtime into a boot chunk, // so that asynchronous chunks can be properly injected later. // This also provides better caching for your application /vendor code. // new webpack.optimize.CommonsChunkPlugin({ // name: "manifest", // minChunks: Infinity // }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, '../src/template/index.html'), filename: 'index.html' }) ] })Copy the code
const webpack = require("webpack") const path = require('path') const merge = require('webpack-merge') const nodeExternals = require('webpack-node-externals') const baseConfig = require('./webpack.base.conf') const HtmlWebpackPlugin = require('html-webpack-plugin') const VueSSRServerPlugin = require('vue-server-renderer/server-plugin') module.exports = merge(baseConfig, {// Point entry to the application's Server Entry file entry: {server: path.resolve(__dirname, '.. / SRC /entry-server.js')}, // This allows webpack to handle dynamic imports in a Node-appropriate fashion, // It also tells the VUe-loader to transport server-oriented code when compiling the Vue component. Target: 'node', // provide source map support for bundle renderer 'source-map', // tell server bundle to use Node-style exports output: {libraryTarget: 'commonjs2' }, // https://webpack.js.org/configuration/externals/#function // https://github.com/liady/webpack-node-externals // Externalizing applications depends on modules. Make server builds faster, // and generate smaller bundles. Externals: nodeExternals({// Do not externalize dependencies that Webpack needs to process. // You can add more file types here. For example, unprocessed *. Vue raw files, // you should also whitelist allowList dependencies that modify 'global' (e.g. polyfill) : /\.css$/}), // this is a plug-in that builds the entire output of the server // into a single JSON file. // The default file name is' vue-ssr-server-bundle.json 'plugins: [ new VueSSRServerPlugin(), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 'process.env.VUE_ENV': '"server"' }), new HtmlWebpackPlugin({ template: path.resolve(__dirname, '../src/template/index.ssr.html'), filename: 'index.ssr.html', inject: true, files: { js: 'client.js' }, excludeChunks: ['server'] }) ] })Copy the code
5. Newindex.ssr.html
Comments are too important to delete
<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, <meta HTTP-equiv =" x-UA-compatible "content=" IE =edge"> <title> </title> </head> <body> <! --vue-ssr-outlet--> <script type="text/javascript" src="<%= htmlWebpackPlugin.options.files.js %>"></script> </body> </html>Copy the code
6. Add a serverserver/index.js
const { createBundleRenderer } = require('vue-server-renderer') const express = require('express') const { resolve } = require('path') const serverBundle = require(resolve(__dirname, '.. /dist/vue-ssr-server-bundle.json')) const clientManifest = require(resolve(__dirname, '.. /dist/vue-ssr-client-manifest.json')) const template = require('fs').readFileSync(resolve(__dirname, '.. /dist/index.ssr.html'), 'utf-8') const app = express() const renderer = createBundleRenderer(serverBundle, {runInNewContext: false, // template recommended, // (optional) page template clientManifest // (optional) client build manifest}) // In the server handler...... app.get('*', (req, res) => { if (req.url === '/favicon.ico') return const context = { url: Req.url} // There is no need to pass in an application because it is already created automatically when the bundle is executed. // Now our server is decoupled from the application! Renderer. RenderToString (Context, (err, HTML) => {// Handle exceptions...... res.end(html) }) }) const port = process.env.PORT || 8085 app.listen(port, () => { console.log(`server started at localhost:${port}`) })Copy the code
Increased 7.package.json
Packaging orders
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "node server/index.js",
"unit": "jest --config test/unit/jest.conf.js --coverage",
"e2e": "node test/e2e/runner.js",
"test": "npm run unit && npm run e2e",
"lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
"build": "rimraf dist && npm run build:client && npm run build:server",
"build:client": "webpack --config build/webpack.client.conf.js",
"build:server": "webpack --config build/webpack.server.conf.js"
},
Copy the code
Annotation 8.webpack.base.conf.js
Under theentry
configuration
. module.exports = { context: path.resolve(__dirname, '.. /'), // entry: { // app: './src/main.js' // }, output: { path: config.build.assetsRoot, filename: '[name].js', publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath }, ...... }...Copy the code
9. Build
yarn run build
ornpm run build
yarn run start
ornpm run start
- Browser input
http://localhost:8085/
You can see the effect
10. How do I check whether the server rendering is successful
Open the browser consolenetwork
chooseAll
You will see the following interface, corresponding to the routepreview
The return is nocss
The page structure returned by the browser rendering isindex.html
Content)
Five, advice,
- In the process of building their own pit encountered or a lot of; At present in figure
vue-cli3
的ssr
And thermal overload is also pit; So I forgot exactly which pits - If there is a pit friend, you can leave a message, or
github
提Issues
Timely reply - Personally, most of the problems are plug-in package version problems, mutual influence, please refer to my source code structure and version
- Next article
vue-cli3 ssr
And thermal overload