Vue服务端渲染和Vue浏览器端渲染的性能对比(实例PK )

Vue 2.0 开始支持服务端渲染的功能,所以本文章也是基于vue 2.0以上版本。网上对于服务端渲染的资料还是比较少,最经典的莫过于Vue作者尤雨溪大神的 vue-hacker-news。本人在公司做Vue项目的时候,一直苦于产品、客户对首屏加载要求,SEO的诉求,也想过很多解决方案,本次也是针对浏览器渲染不足之处,采用了服务端渲染,并且做了两个一样的Demo作为比较,更能直观的对比Vue前后端的渲染。

talk is cheap,show us the code!话不多说,我们分别来看两个Demo:(欢迎star 欢迎pull request)

1.浏览器端渲染Demo: https://github.com/monkeyWangs/doubanMovie

2.服务端渲染Demo:https://github.com/monkeyWangs/doubanMovie-SSR

两套代码运行结果都是为了展示豆瓣电影的,运行效果也都是差不多,下面我们来分别简单的阐述一下项目的机理:

一、浏览器端渲染豆瓣电影

首先我们用官网的脚手架搭建起来一个vue项目

npm install -g vue-cli
vue init webpack doubanMovie
cd doubanMovie
npm install
npm run dev

这样便可以简单地打起来一个cli框架,下面我们要做的事情就是分别配置 vue-router, vuex,然后配置我们的webpack proxyTable 让他支持代理访问豆瓣API。

1.配置Vue-router

我们需要三个导航页:正在上映、即将上映、Top250;一个详情页,一个搜索页。这里我给他们分别配置了各自的路由。在 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)
/**
 * 路由信息配置
 */
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
 }
 ]
})

这样我们的路由信息配置好了,然后每次切换路由的时候,尽量避免不要重复请求数据,所以我们还需要配置一下组件的keep-alive:在app.vue组件里面。

<keep-alive exclude="moviesDetail">
 <router-view></router-view>
</keep-alive>

这样一个基本的vue-router就配置好了。

2.引入vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。

简而言之:Vuex 相当于某种意义上设置了读写权限的全局变量,将数据保存保存到该“全局变量”下,并通过一定的方法去读写数据。

Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

应用层级的状态应该集中到单个 store 对象中。

提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。

异步逻辑都应该封装到 action 里面。

对于大型应用我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:

├── index.html
├── main.js
├── api
│ └── ... # 抽取出API请求
├── components
│ ├── App.vue
│ └── ...
└── store
 ├── index.js  # 我们组装模块并导出 store 的地方
 └── moving  # 电影模块
 ├── index.js # 模块内组装,并导出模块的地方
 ├── actions.js # 模块基本 action
 ├── getters.js # 模块级别 getters
 ├── mutations.js # 模块级别 mutations
 └── types.js # 模块级别 types

所以我们开始在我们的src目录下新建一个名为store 的文件夹 为了后期考虑 我们新建了moving 文件夹,用来组织电影,考虑到所有的action,getters,mutations,都写在一起,文件太混乱,所以我又给他们分别提取出来。

stroe文件夹建好,我们要开始在main.js里面引用vuex实例:

import store from './store'
new Vue({
 el: '#app',
 router,
 store,
 template: '<App/>',
 components: { App }
})

这样,我们便可以在所有的子组件里通过 this.$store 来使用vuex了。

3.webpack proxyTable 代理跨域

webpack 开发环境可以使用proxyTable 来代理跨域,生产环境的话可以根据各自的服务器进行配置代理跨域就行了。在我们的项目config/index.js 文件下可以看到有一个proxyTable的属性,我们对其简单的改写

proxyTable: {
 '/api': {
 target: 'http://api.douban.com/v2',
 changeOrigin: true,
 pathRewrite: {
  '^/api': ''
 }
 }
 }

这样当我们访问

localhost:8080/api/movie

的时候 其实我们访问的是

http://api.douban.com/v2/movie

这样便达到了一种跨域请求的方案。

至此,浏览器端的主要配置已经介绍完了,下面我们来看看运行的结果:

为了介绍浏览器渲染是怎么回事,我们运行一下npm run build 看看我们的发布版本的文件,到底是什么鬼东西....

run build 后会都出一个dist目录 ,我们可以看到里面有个index.html,这个便是我们最终页面将要展示的html,我们打开,可以看到下面:

观察好的小伙伴可以发现,我们并没有多余的dom元素,就只有一个div,那么页面要怎么呈现呢?答案是js append,对,下面的那些js会负责innerHTML。而js是由浏览器解释执行的,所以呢,我们称之为浏览器渲染,这有几个致命的缺点:

js放在dom结尾,如果js文件过大,那么必然造成页面阻塞。用户体验明显不好(这也是我我在公司反复被产品逼问的事情)

不利于SEO

客户端运行在老的JavaScript引擎上

对于世界上的一些地区人,可能只能用1998年产的电脑访问互联网的方式使用计算机。而Vue只能运行在IE9以上的浏览器,你可能也想为那些老式浏览器提供基础内容 - 或者是在命令行中使用 Lynx的时髦的黑客

基于以上的一些问题,服务端渲染呼之欲出....

二、服务器端渲染豆瓣电影

先看一张Vue官网的服务端渲染示意图

从图上可以看出,ssr 有两个入口文件,client.js 和 server.js, 都包含了应用代码,webpack 通过两个入口文件分别打包成给服务端用的 server bundle 和给客户端用的 client bundle. 当服务器接收到了来自客户端的请求之后,会创建一个渲染器 bundleRenderer,这个 bundleRenderer 会读取上面生成的 server bundle 文件,并且执行它的代码, 然后发送一个生成好的 html 到浏览器,等到客户端加载了 client bundle 之后,会和服务端生成的DOM 进行 Hydration(判断这个DOM 和自己即将生成的DOM 是否相同,如果相同就将客户端的vue实例挂载到这个DOM上, 否则会提示警告)。

具体实现:

我们需要vuex,需要router,需要服务器,需要服务缓存,需要代理跨域....不急我们慢慢来。

1.建立nodejs服务

首先我们需要一个服务器,那么对于nodejs,express是很好地选择。我们来建立一个server.js

const port = process.env.PORT || 8080
app.listen(port, () => {
 console.log(`server started at localhost:${port}`)
})

这里用来启动服务监听 8080 端口。

然后我们开始处理所有的get请求,当请求页面的时候,我们需要渲染页面

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(`whole request: ${Date.now() - s}ms`))
 .pipe(res)
})

然后我们需要代理请求,这样才能进行跨域,我们引入http-proxy-middleware模块:

const proxy = require('http-proxy-middleware');//引入代理中间件
/**
 * proxy middleware options
 * 代理跨域配置
 * @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);

这样我们的服务端server.js便配置完成。接下来 我们需要配置服务端入口文件,还有客户端入口文件,首先来配置一下客户端文件,新建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__)
}
/**
 * 异步组件
 */
router.onReady(() => {
 // 开始挂载到dom上
 app.$mount('#app')
})
// service worker
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
 navigator.serviceWorker.register('/service-worker.js')
}

客户端入口文件很简单,同步服务端发送过来的数据,然后把 vue 实例挂载到服务端渲染的 DOM 上。

再配置一下服务端入口文件: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 返回一个函数,该函数接受一个从服务端传递过来的 context 的参数,将 vue 实例通过 promise 返回。context 一般包含 当前页面的url,首先我们调用 vue-router 的 router.push(url) 切换到到对应的路由, 然后调用 getMatchedComponents 方法返回对应要渲染的组件, 这里会检查组件是否有 fetchServerData 方法,如果有就会执行它。

下面这行代码将服务端获取到的数据挂载到 context 对象上,后面会把这些数据直接发送到浏览器端与客户端的vue 实例进行数据(状态)同步。

context.state = store.state

然后我们分别配置客户端和服务端webpack,这里可以在我的github上fork下来参考配置,里面每一步都有注释,这里不再赘述。

接着我们需要创建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)
/**
 * 创建vue实例
 * 在这里注入 router store 到所有的子组件
 * 这样就可以在任何地方使用 `this.$router` and `this.$store`
 * @type {Vue$2}
 */
const app = new Vue({
 router,
 store,
 render: h => h(App)
})
/**
 * 导出 router and store.
 * 在这里不需要挂载到app上。这里和浏览器渲染不一样
 */
export { app, router, store }

这样 服务端入口文件和客户端入口文件便有了一个公共实例Vue, 和我们以前写的vue实例差别不大,但是我们不会在这里将app mount到DOM上,因为这个实例也会在服务端去运行,这里直接将 app 暴露出去。

接下来创建路由router,创建vuex跟客户端都差不多。详细的可以参考我的项目...

到此,服务端渲染配置 就简单介绍完了,下面我们启动项目简单的看下:

这里跟服务端界面一样,不一样的是url已经不是之前的 #/而变成了请求形式 /

这样每当浏览器发送一个页面的请求,会有服务器渲染出一个dom字符串返回,直接在浏览器段显示,这样就避免了浏览器端渲染的很多问题。

说起SSR,其实早在SPA (Single Page Application) 出现之前,网页就是在服务端渲染的。服务器接收到客户端请求后,将数据和模板拼接成完整的页面响应到客户端。 客户端直接渲染, 此时用户希望浏览新的页面,就必须重复这个过程, 刷新页面. 这种体验在Web技术发展的当下是几乎不能被接受的,于是越来越多的技术方案涌现,力求 实现无页面刷新或者局部刷新来达到优秀的交互体验。但是SEO却是致命的,所以一切看应用场景,这里只为大家提供技术思路,为vue开发提供多一种可能的方案。

为了更清晰的对比两次渲染的结果,我做了一次实验,把两个想的项目build后模拟生产环境,在浏览器netWork模拟网速3g环境,先来看看服务端渲染的结果:

可以看到整体加载dom一共花了832ms;用户可能在网络比较慢的情况下从远处访问网站 - 或者通过比较差的带宽。 这些情况下,尽量减少页面请求数量,来保证用户尽快看到基本的内容。

然后我们可以看到其中有一个vendor.js 达到了563KB,整体的加载时间达到了了8.19s,这是因为单页面文件的原因,会把所有的逻辑代码打包到一个js里面。可以用分webpack拆分代码避免强制用户下载整个单页面应用,但是,这样也远没有下载个单独的预先渲染过的HTML文件性能高。

以上所述是小编给大家介绍的Vue服务端渲染和Vue浏览器端渲染的性能对比(实例PK ),希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • 详解vue服务端渲染(SSR)初探

    前言 首先来讲一下服务端渲染,直白的说就是在服务端拿数据进行解析渲染,直接生成html片段返回给前端.具体用法也有很多种比如: 传统的服务端模板引擎渲染整个页面 服务渲染生成htmll代码块, 前端 AJAX 获取然后js动态添加 服务端渲染的优劣 首先是seo问题,前端动态渲染的内容是不能被抓取到的,而使用服务端渲染就可以解决这个问题.还有就是首屏加载过慢这种问题,比如在SPA中,打开首页需要初始加载很多资源,这时考虑在首屏使用服务端渲染,也是一种折中的优化方案.但是使用SSR时,势必会增加服

  • Vue 2.0 服务端渲染入门介绍

    1 什么是服务端渲染 SSR server side render 就是通过后端吐模板,而不是通过前端ajax获取数据,拼接字符串. 2 为什么需要SSR 需要SEO,因为爬虫不会等待ajax结果. 客户端网络慢,加载速度慢,影响用户体验. 3 另一种解决办法 预渲染 不是一次性下载整个单页应用,预渲染只是在构建时为了特定的路由生成特定的几个静态页面 你用webpack可以很简单地通过prerender-spa-plugin来添加预渲染  4 NodeJS编写Vue的SSR 首先npm inst

  • 详解如何使用Vue2做服务端渲染

    花费了一个月时间,终于在新养车之家项目中成功部署了vue2服务端渲染(SSR),并且使用上了Vuex 负责状态管理,首屏加载时间从之前4G网络下的1000ms,提升到了现在500-700ms之间,SSR的优势有很多,现在让我来跟你细细道来. 技术栈 服务端:Nodejs(v6.3) 前端框架 Vue2.1.10 前端构建工具:webpack2.2 && gulp 代码检查:eslint 源码:es6 前端路由:vue-router2.1.0 状态管理:vuex2.1.0 服务端通信:axi

  • Vue服务端渲染和Vue浏览器端渲染的性能对比(实例PK )

    Vue 2.0 开始支持服务端渲染的功能,所以本文章也是基于vue 2.0以上版本.网上对于服务端渲染的资料还是比较少,最经典的莫过于Vue作者尤雨溪大神的 vue-hacker-news.本人在公司做Vue项目的时候,一直苦于产品.客户对首屏加载要求,SEO的诉求,也想过很多解决方案,本次也是针对浏览器渲染不足之处,采用了服务端渲染,并且做了两个一样的Demo作为比较,更能直观的对比Vue前后端的渲染. talk is cheap,show us the code!话不多说,我们分别来看两个D

  • 详解vue服务端渲染浏览器端缓存(keep-alive)

    在使用服务器端渲染时,除了服务端的接口缓存.页面缓存.组建缓存等,浏览器端也避免不了要使用缓存,减少页面的重绘. 这时候我们就会想到vue的keep-alive,接下来我们说一下keep-alive的使用 假如现在我们有两个页面,home.vue 和 about.vue home.vue <template> <div> home </div> </template> <script> export default { name: Home, c

  • vue服务端渲染的实例代码

    一.什么是服务端渲染 客户端请求服务器,服务器根据请求地址获得匹配的组件,在调用匹配到的组件返回Promise (官方是asyncData方法)来将需要的数据拿到.最后再通过window.__initial_state=data将其写入网页,最后将服务端渲染好的网页返回回去.接下来客户端将用新的store状态把原来的store状态替换掉,保证客户端和服务端的数据同步.遇到没被服务端渲染的组件,再去发异步请求拿数据. 服务端渲染的环境搭建 这是vue官网的服务端渲染的示意图,ssr有两个入口文件,

  • 详解Nuxt.js Vue服务端渲染摸索

    本文采用nuxt进行服务端渲染https://zh.nuxtjs.org/ Nuxt.js 十分简单易用.一个简单的项目只需将 nuxt 添加为依赖组件即可. Vue因其简单易懂的API.高效的数据绑定和灵活的组件系统,受到很多前端开发人员的青睐.国内很多公司都在使用vue进行项目开发,我们正在使用的简书,便是基于Vue来构建的. 我们知道,SPA前端渲染存在两大痛点:(1)SEO.搜索引擎爬虫难以抓取客户端渲染的页面meta信息和其他SEO相关信息,使网站无法在搜索引擎中被用户搜索到.(2)用

  • vue服务端渲染缓存应用详解

    服务端渲染简介 服务端渲染不是一个新的技术:在 Web 最初的时候,页面就是通过服务端渲染来返回的,用 PHP 来说,通常是使用 Smarty 等模板写模板文件,然后 PHP 服务端框架将数据和模板渲染为页面返回,这样的服务端渲染有个缺点就是一旦要查看新的页面,就需要请求服务端,刷新页面. 但如今的前端,为了追求一些体验上的优化,通常整个渲染在浏览器端使用 JS 来完成,配合 history.pushState 等方式来做单页应用(SPA: Single-Page Application),也收

  • Vue服务端渲染实践之Web应用首屏耗时最优化方案

    随着各大前端框架的诞生和演变,SPA开始流行,单页面应用的优势在于可以不重新加载整个页面的情况下,通过ajax和服务器通信,实现整个Web应用拒不更新,带来了极致的用户体验.然而,对于需要SEO.追求极致的首屏性能的应用,前端渲染的SPA是糟糕的.好在Vue 2.0后是支持服务端渲染的,零零散散花费了两三周事件,通过改造现有项目,基本完成了在现有项目中实践了Vue服务端渲染. 关于Vue服务端渲染的原理.搭建,官方文档已经讲的比较详细了,因此,本文不是抄袭文档,而是文档的补充.特别是对于如何与现

  • vue服务端渲染操作简单入门实例分析

    本文实例讲述了vue服务端渲染操作.分享给大家供大家参考,具体如下: 想到要学习vue-ssr的同学,自不必多说,一定是熟悉了vue,并且多多少少做过几个项目.然后学习vue服务端渲染无非解决首屏渲染的白屏问题以及SEO友好. 话不多说,笔者也是研究多日才搞明白这个服务端渲染到底是杂么回事!!! 一,首先实现下官网的基本案例 随便建一个目录,然后执行npm init初始化项目,生成工程文件package.json:创建server.js:然后按照vue-ssr官方链接:https://ssr.v

  • vue服务端渲染添加缓存的方法

    什么是服务器端渲染(SSR)? Vue.js 是构建客户端应用程序的框架.默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM.然而,也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将这些静态标记"激活"为客户端上完全可交互的应用程序. 服务器渲染的 Vue.js 应用程序也可以被认为是"同构"或"通用",因为应用程序的大部分代码都可以在服务器和客户端上运行. 缓存 虽然 Vue 的服务

  • Vue 服务端渲染SSR示例详解

    目录 手写Vue服务端渲染 一.开始vue-ssr之旅 二.采用模板渲染 三.ssr目录创建 四.通过webpack实现编译vue项目 app.js client-entry.js server-entry.js 五.配置客户端打包和服务端打包 六.配置运行脚本 七.服务端配置 七.通过json配置createBundleRenderer方法 八.集成VueRouter 配置入口文件 配置组件信息 防止刷新页面不存在 保证异步路由加载完成 十.集成vuex配置 在后端更新vuex 在浏览器运行时

随机推荐