In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
An example analysis of the comparison of rendering performance between Vue server rendering and Vue browser rendering. To solve this problem, this article introduces the corresponding analysis and solution in detail, hoping to help more partners who want to solve this problem to find a more simple and feasible method.
When I am doing the Vue project, I have been suffering from the requirements of products and customers for the loading of the first screen and the demands of SEO, and I have also thought about a lot of solutions. This time, in view of the shortcomings of browser rendering, I used server rendering, and made two identical Demo as a comparison, which can more intuitively compare the rendering of the front and rear of Vue.
Talk is cheap,show us the code! Needless to say, let's take a look at two Demo:
1. Browser-side rendering Demo: https://github.com/monkeyWangs/doubanMovie
two。 Server rendering Demo: https://github.com/monkeyWangs/doubanMovie-SSR
The running results of the two sets of codes are to show the Douban movie, and the running effect is similar. Let's briefly explain the mechanism of the project respectively:
1. Rendering Douban movie on the browser side
First of all, we use the scaffolding of the official website to build a vue project.
Npm install-g vue-clivue init webpack doubanMoviecd doubanMovienpm installnpm run dev
In this way, we can simply build a cli framework, and the next thing we need to do is to configure vue-router and vuex respectively, and then configure our webpack proxyTable to support proxy access to Douban API.
1. Configure Vue-router
We need three navigation pages: a Top250; details page and a search page. Here I configure each of them with their own routes. Configure the following information under router/index.js:
Import Vue from 'vue'import Router from' vue-router'import Moving from'@ / components/moving'import Upcoming from'@ / components/upcoming'import Top250 from'@ / components/top250'import MoviesDetail from'@ / components/common/moviesDetail'import Search from'@ / components/searchList'Vue.use (Router) / * * routing information configuration * / export default new Router ({routes: [{path:'/', name: 'Moving', component: Moving}, {path:' / upcoming' Name: 'upcoming', component: Upcoming}, {path:' / top250', name: 'Top250', component: Top250}, {path:' / search', name: 'Search', component: Search}, {path:' / moviesDetail', name: 'moviesDetail', component: MoviesDetail}]})
In this way, our routing information is configured, and then every time we switch routes, try not to repeat the request data, so we also need to configure the keep-alive of the component: in the app.vue component.
Such a basic vue-router is configured.
two。 Introduction of vuex
Vuex is a state management model developed specifically for Vue.js applications. It uses centralized storage to manage the state of all components of the application, and uses corresponding rules to ensure that the state changes in a predictable way. Vuex is also integrated into Vue's official debugging tool devtools extension, providing advanced debugging features such as zero configuration time-travel debugging, status snapshot import and export, and so on.
In short: Vuex is equivalent to a global variable that sets read and write permissions in a sense, saves data to the "global variable", and reads and writes data in a certain way.
Vuex does not limit the structure of your code. However, it sets out some rules to follow:
The state of the application level should be centralized into a single store object.
Committing the mutation is the only way to change the state, and the process is synchronous.
Asynchronous logic should be encapsulated in action.
For large applications, we will want to split the Vuex-related code into modules. The following is an example of the project structure:
├── index.html ├── main.js ├── api │ └──. # extract API request ├── components │ ├── App.vue │ └──... └── store ├── index.js # We assemble the module and export store's local └── moving # movie module ├── index.js # module And export the local ├── actions.js # module basic action ├── getters.js # module level getters ├── mutations.js # module level mutations └── types.js # module level types
So we started to create a new folder called store under our src directory for later consideration, we created a new moving folder to organize movies, considering that all the action,getters,mutations are written together, and the files are too messy, so I extracted them separately.
When the stroe folder is built, we need to start referencing the vuex instance in main.js:
Import store from'. / store'new Vue ({el:'# app', router, store, template:', components: {App}})
In this way, we can use vuex through this.$store in all the subcomponents.
3.webpack proxyTable agent cross-domain
Webpack development environment can use proxyTable to proxy cross-domain, while production environment can configure proxy cross-domain according to their respective servers. We can see that there is a property of proxyTable under our project config/index.js file, which we simply rewrite
ProxyTable: {'/ api': {target: 'http://api.douban.com/v2', changeOrigin: true, pathRewrite: {' ^ / api':''}
So that when we visit
Localhost:8080/api/movie
In fact, we were interviewing
Http://api.douban.com/v2/movie
This achieves a cross-domain request scheme.
Now that the main configuration of the browser is introduced, let's take a look at the running results:
To explain what browser rendering is all about, let's run npm run build to see what the heck our released version of the file is.
After run build, there will be a dist directory. We can see that there is an index.html in it. This is the html that our final page will display. When we open it, we can see the following:
Good friends can find that we have no extra dom elements, only a div, so how to render the page? The answer is js append, yes, the following js will be responsible for innerHTML. Js is interpreted and executed by browsers, so we call it browser rendering, which has several fatal disadvantages:
Js is placed at the end of the dom, and if the js file is too large, it is bound to cause page blocking. The user experience is obviously not good (this is also what I was repeatedly asked by the product in the company)
Not good for SEO
The client runs on the old JavaScript engine
For people in some parts of the world, they may only be able to use computers made in 1998 to access the Internet. While Vue can only run on browsers above IE9, you may also want to provide basic content for older browsers-or trendy hackers who use Lynx on the command line
Based on some of the above problems, server rendering is about to come out.
Second, the server-side rendering Douban movie
First, take a look at a schematic diagram of the server rendering of Vue's official website.
As can be seen from the figure, ssr has two entry files, client.js and server.js, which contain the application code. Through the two entry files, webpack is packaged into server bundle for the server and client bundle for the client. When the server receives the request from the client, it creates a renderer bundleRenderer. The bundleRenderer will read the server bundle file generated above, execute its code, and then send a generated html to the browser. After the client loads the client bundle, it will Hydration with the DOM generated by the server (to determine whether this DOM is the same as the DOM that it is about to generate. If it is the same, mount the client's vue instance to this DOM, otherwise you will be prompted with a warning.
Specific implementation:
We need vuex, we need router, we need server, we need service cache, we need proxy cross-domain. There's no hurry. Let's take our time.
1. Establish a nodejs service
First of all, we need a server, so it's a good choice for nodejs,express. Let's build a server.js.
Const port = process.env.PORT | | 8080app.listen (port, () = > {console.log (`server started at localhost:$ {port} `)})
This is used to start the service listening on port 8080.
Then we start to process all the get requests, and when we request the page, we need to render the page
App.get ('*', (req, res) = > {if (! renderer) {return res.end ('waiting for compilation...) Refresh in a moment.')} const s = Date.now () res.setHeader ("Content-Type", "text/html") res.setHeader ("Server") ServerInfo) const errorHandler = err = > {if (err & & err.code = 404) {res.status (404). End ('404 | Page Not Found')} else {/ / Render Error Page or Redirect res.status (500). End (' 500 | Internal Server Error') console.error (`error during render: ${req.url} `) console.error (err)} renderer.renderToStream ({url: req.url}) .on ('error', errorHandler) .on (' end') () = > console.log (`pipe: ${Date.now ()-s} ms`)) .pipe (res)})
Then we need to proxy the request so that we can cross-domain, we introduce the http-proxy-middleware module:
Const proxy = require ('http-proxy-middleware'); / / introduce agent middleware / * proxy middleware options * Agent cross-domain configuration * @ type {{target: string, changeOrigin: boolean, pathRewrite: {^ / api: string} * / var options = {target:' http://api.douban.com/v2', / / target host changeOrigin: true, / / needed for virtual hosted sites pathRewrite: {'^ / api':'}} Var exampleProxy = proxy (options); app.use ('/ api', exampleProxy)
In this way, our server-side server.js is configured. Next, we need to configure the server entry file and the client entry file. First, configure the client file and create a new src/entry-client.js.
Import 'es6-promise/auto'import {app, store Router} from'. / app'// prime the store with server-initialized state.// the state is determined during SSR and inlined in the page markup.if (window.__INITIAL_STATE__) {store.replaceState (window.__INITIAL_STATE__)} / * * Asynchronous component * / router.onReady (() = > {/ / start mounting to dom app.$mount ('# app')}) / / service workerif (process.env.NODE_ENV =' Production' & & 'serviceWorker' in navigator) {navigator.serviceWorker.register (' / service-worker.js')}
The client entry file is very simple, synchronize the data sent by the server, and then mount the vue instance to the server-rendered DOM.
Configure the server entry file again: src/entry-server.js
Import {app, router, store} from'. / app'const isDev = process.env.NODE_ENV! = = 'production'// This exported function will be called by `bundleRenderer`./ / This is where we perform data-prefetching to determine the// state of our application before actually rendering it.// Since data fetching is async, this function is expected to// return a Promise that resolves to the app instance.export default context = > {const s = isDev & & Date.now () return new Promise ((resolve) Reject) = > {/ / set router's location router.push (context.url) / / wait until router has resolved possible async hooks router.onReady (() = > {const matchedComponents = router.getMatchedComponents () / / no matched routes if (! matchedComponents.length) {reject ({code: 404})} / / Call preFetch hooks on components matched by the route. / / A preFetch hook dispatches a store action and returns a Promise, / / which is resolved when the action is complete and store state has been / / updated. Promise.all (matchedComponents.map (component = > {return component.preFetch & & component.preFetch (store)}). Then () = > {isDev & & console.log (`data pre-fetch: ${Date.now ()-s} ms`) / / After all preFetch hooks are resolved, our store is now / / filled with the state needed to render the app. / / Expose the state on the render context, and let the request handler / / inline the state in the HTML response. This allows the client-side / / store to pick-up the server-side state without having to duplicate / / the initial data fetching on the client. Context.state = store.state resolve (app)}) .catch (reject)}
Server.js returns a function that takes an argument to context passed from the server and returns the vue instance through promise. Context generally contains the url of the current page. First, we call router.push (url) of vue-router to switch to the corresponding route, and then call the getMatchedComponents method to return the component to be rendered. Here, we will check whether the component has a fetchServerData method, and execute it if so.
The following line of code mounts the data obtained by the server to the context object, and then sends the data directly to the browser to synchronize the data (status) with the client's vue instance.
Context.state = store.state
Then we configure the client and server webpack respectively. Here, you can fork the configuration on my github for reference. There are comments for each step, so I won't repeat them here.
Then we need to create an app.js:
Import Vue from 'vue'import App from'. / App.vue'import store from'. / store'import router from'. / router'import {sync} from 'vuex-router-sync'import Element from' element-ui'Vue.use (Element) / / sync the router with the vuex store.// this registers `store.state.route`sync (store) Router) / * create vue instance * inject router store into all sub-components here * so that you can export router and store anywhere using `this.$ router`and `this.$ store` * @ type {Vue$2} * / const app = new Vue ({router, store, render: h = > h (App)}) / *. * there is no need to mount to app here. This is different from browser rendering * / export {app, router, store}
In this way, there is a public instance Vue for the server entry file and the client entry file, which is not much different from the vue instance we wrote before, but we will not app mount to DOM here, because this instance will also run on the server, and the app will be directly exposed here.
Next, create a routing router, and create a vuex that is similar to the client. You can refer to my project for details.
At this point, the server rendering configuration is briefly introduced. Let's start the project and take a look at it:
This is the same as the server interface, except that url is no longer in the form of request / instead of # /.
In this way, whenever the browser sends a request for a page, the server will render a dom string and display it directly in the browser segment, which avoids many problems of browser-side rendering.
Speaking of SSR, in fact, long before the emergence of SPA (Single Page Application), web pages were rendered on the server side. After receiving the request from the client, the server splices the data and template into a complete page to respond to the client. The client rendering directly, when the user wants to browse the new page, they must repeat this process and refresh the page. This kind of experience is almost unacceptable in the current development of Web technology, so more and more technical solutions emerge to achieve no page refresh or local refresh to achieve excellent interactive experience. But SEO is fatal, so it all looks at the application scenario, here only provides technical ideas for everyone, and provides one more possible solution for vue development.
In order to compare the results of the two renderings more clearly, I did an experiment to simulate the production environment after build of the two desired projects, and simulate the 3G network speed environment in the browser netWork. First, let's take a look at the server rendering results:
You can see that it took a total of 832ms to load the dom overall; users may visit the site from a distance when the network is slow-or through poor bandwidth. In these cases, minimize the number of page requests to ensure that users see the basic content as soon as possible.
Then we can see that one of the vendor.js reaches 563KB, and the overall load time is 8.19s, which is because all the logic code is packaged into a js because of a single page file. You can use split webpack split code to avoid forcing users to download an entire single-page application, but it's not nearly as good as downloading a separate pre-rendered HTML file.
This is the answer to the sample analysis question on the comparison of rendering performance between Vue server rendering and Vue browser rendering. I hope the above content can be of some help to you. If you still have a lot of doubts to be solved, you can follow the industry information channel for more related knowledge.
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.