官网给出的解释:css
Vue.js 是构建客户端应用程序的框架。默认状况下,能够在浏览器中输出 Vue 组件,进行生成 DOM 和操做 DOM。然而,也能够将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器,最后将静态标记"混合"为客户端上彻底交互的应用程序。html
服务器渲染的 Vue.js 应用程序也能够被认为是"同构"或"通用",由于应用程序的大部分代码均可以在服务器和客户端上运行。前端
ssr的服务端渲染大体的意思就是vue在客户端将标签渲染成的整个html片断的工做在服务端完成,服务端造成的html片断直接返回给客户端这个过程就叫作服务端渲染。vue
正常状况下咱们使用vue或react框架浏览器获取全部资源后作的事情node
1.浏览器加载全部资源(html,css,js,img...)-->2.cdn-->3.返回资源-->4.vue请求server获取业务数据-->5.返回数据渲染成html片断-->6.css渲染片断成一个网页-->用户react
没错这里面最耗时的时间是4,5这两步骤,h5请求serverapi的过程自己除了服务器的限制,还有用户网络,宽带等等诸多限制,而且当页面逻辑过多,数据过于繁琐的状况下,咱们的vue在client端渲染也会成为性能瓶颈,最明显的就是一些电商公司的首页,商品详情页等等。测试这个过程在优化前大概须要500ms左右,即便通过优化也须要200ms左右,这个时间几乎是难以接受的,而且咱们在用户网络不是很好的状况下,若是咱们serverfetch的过程须要500ms,再加上其余的各类请求资源,手机性能等等,用户就要看到将近一秒的白屏时间,这个明显是不好的用户体验。jquery
ssr渲染webpack
1.浏览器加载全部资源(html,css,js,img...)-->2.cdn-->3.返回资源-->4.css渲染片断成一个网页-->用户ios
这里咱们不仅是用ssr,咱们也须要把全部的html片断缓存在node内存中,这个html片断必定只能放在内存中,不要想着要一小片redis内存和其余server端共用,由于并发亮极大的状况下出得流量有可能直接让redis挂掉。而这个性能放在node的内存中几乎能够忽略不计。咱们若是须要存的时间很短的话,那么咱们放在内存中并无问题,由于实时数据刷新五秒可能就换一分内存数据,可是若是咱们长时间去存这个备份可能就会出现数据不一致的问题,咱们都知道通常线上部署node服务最少须要三台服务,而每一台的数据咱们很难保证一只,用户a可能两个请求一个打到nodeA服务器上,另外一个打到nodeB服务器上,这样就会出问题。这种内存只适合存那种时间很短的缓存,若是咱们须要存几个小时那种咱们还要考虑redis,由于咱们须要数据实时同步,可是咱们只能存储serverfetch的数据,而不能存整个html。一个ssr的时间大概是5ms左右,一台服务器的1s承受量就是1000/5*60% = 120个请求,也就是说咱们三台服务器的请求并发量大概能承受360-400左右,超出就要红色预警了!!这对那些并发量极大的项目并不合适,全部咱们中和考虑,这个无非就是时间换空间,空间换时间的游戏。咱们能够选择增长缓存,也能够添加服务器!es6
首先你须要熟悉webpack2,vue,vuex,vue-router(vue的全家桶),node,express。个别逻辑还须要redis等等后端资源,若是你想作到极致(并发状况下不穿透),咱们还须要了解锁的概念,同时咱们也须要知道如何处理避免死锁,事务等等机制!
首先最开始考虑的就是模版渲染,咱们知道咱们在本地打开本地html文件的时候几乎是瞬间就能看到页面的全部内容,那么咱们有可能让用户直接看到一个用户页面么?
首先我想到的就是node的各类渲染模版,ejs?jade?咱们能够经过node server端去fetch咱们后台的全部数据,以后把数据拼成一个html直接给用户,这样确实能实现咱们想要的东西,但不是最好的,首先咱们目前市面上的三大框架vue,react,angular咱们须要摒弃,咱们还要把全部的业务逻辑拆分,由于有了框架的限制,这些都是不现实的,而且咱们直接用server端的模版对于咱们前端开发来讲效率也是极低。
不论是react仍是vue都有基于本身框架的服务端渲染。
今天咱们来讲一下基于vue的ssr
https://ssr.vuejs.org/zh/
ssr的好处官网已经给出,最吸引个人只有两点
1.更好的 SEO
2.更快的内容到达时间(time-to-content)
基本上按照官网step by step均可以写一个很小的vuessr的demo一些基础细节咱们不去介绍了。官网给出的ssr大概的流程
vue-router在ready以前fetch全部vue的业务数据调用asyncData钩子,以后获取的数据去更新vuex以后咱们渲染vue组件的时候组件获取全部的vuex的store数据,拼接成一个html字符串。
首先咱们的须要两份webpack打包入口,一份去压缩client,一份去压缩server。
client的一端是new一个vue的实例而后经过app.$mount('#app')将其挂载到 DOM
server的一端咱们须要返回一个promise,咱们能够在这里fetchpro的数据放在这个promise里面return,这里咱们能够new一个promise,也可使用fetch,或vue的axios。(注意咱们全部须要在服务端渲染的数据都要在这里获取到,而后再client端也要获取到,咱们全部的数据不能放在vue中的mounted中获取,由于这样和客户端渲染没什么区别,vue暴漏的这个环境支持window也就是说这个位置实际上是client端作的,也就是在ssr全部功能实现以后在执行,这样咱们和以前就没有任何区别了)
client,和server须要import你的vue全部组件,以后就会吧全部的vue组件渲染成你须要的html,这里官网给的例子须要大家去使用vue的全家桶,而我刚刚说的serverfetch就不须要使用vue-router和vuex,咱们已经把全部须要的数据在ssr以前就直接放进vue中,经过props的形式传给组件
app.js
export function createApp (obj) { const app = new Vue({ render: h => h(App.default, obj) }) return { app } }
咱们在client和server压缩入口就把全部内容传入组件,这样咱们就能够实现把内容数据传到组件里面,实现vue的ssr
个人webpack:client
var webpack = require('webpack'); var ExtractTextPlugin = require("extract-text-webpack-plugin"); const VueSSRServerPlugin = require('vue-server-renderer/client-plugin') const isProd = process.env.NODE_ENV === 'production' console.log('NODE_ENV--->', process.env.NODE_ENV) module.exports = { //页面入口文件配置 entry: { index : './build/index/entry-client.js' }, target: 'web', devtool: isProd?false:'#source-map', //入口文件输出配置 output: { path: 'dist/index', filename: 'client_index_[hash].js', }, module: { noParse: /es6-promise\.js$/, // avoid webpack shimming process rules: [ { test: /\.vue$/, loader: 'vue-loader' }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.(png|jpg|gif|svg)$/, loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]?[hash]' } }, { test: /\.css$/, use: ['vue-style-loader', 'css-loader'] }, { test: /\.es6$/, loader: "babel-loader", exclude: /node_modules/ }, ] }, resolve: { alias: { 'vue$': 'vue/dist/vue.common.js', } }, externals: { "jquery": "$", 'Vue': true, 'Swiper': true, 'VueLazyload': true, '$': true }, plugins: [ // new webpack.optimize.UglifyJsPlugin({ // compress: { warnings: isProd?false:true } // }), // new ExtractTextPlugin({ // filename: 'common.[chunkhash].css' // }), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 'process.env.VUE_ENV': '"server"' }), new VueSSRServerPlugin() ] };
server:
var webpack = require('webpack'); var ExtractTextPlugin = require("extract-text-webpack-plugin"); const nodeExternals = require('webpack-node-externals') const VueSSRServerPlugin = require('vue-server-renderer/server-plugin') const isProd = process.env.NODE_ENV === 'production' console.log('NODE_ENV--->', process.env.NODE_ENV) module.exports = { //页面入口文件配置 entry: { index : './build/index/entry-server.js' }, target: 'node', devtool: isProd?false:'#source-map', //入口文件输出配置 output: { path: 'dist/index', filename: 'server-bundle.js', libraryTarget: 'commonjs2' }, module: { noParse: /es6-promise\.js$/, // avoid webpack shimming process rules: [ { test: /\.vue$/, loader: 'vue-loader' }, { test: /\.js$/, loader: 'babel-loader', exclude: /node_modules/ }, { test: /\.(png|jpg|gif|svg)$/, loader: 'url-loader', options: { limit: 10000, name: '[name].[ext]?[hash]' } }, { test: /\.css$/, use: ['vue-style-loader', 'css-loader'] }, { test: /\.es6$/, loader: "babel-loader", exclude: /node_modules/ }, ] }, resolve: { // alias: { // 'vue$': 'vue/dist/vue.js' // 'vue/dist/vue.common.js' for webpack 1 // } }, externals: nodeExternals({ // do not externalize CSS files in case we need to import it from a dep whitelist: /\.css$/, "jquery": "$", 'Vue': true, 'Swiper': true, 'VueLazyload': true, '$': true, }), plugins: [ // new webpack.optimize.UglifyJsPlugin({ // compress: { warnings: isProd?false:true } // }), // new ExtractTextPlugin({ // filename: 'common.[chunkhash].css' // }), new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 'process.env.VUE_ENV': '"server"' }), new VueSSRServerPlugin() ] };
可是上面的形式咱们须要每次访问页面都须要请求后台server的接口,这样的接口彻底是不必的,试想一下若是首页咱们每秒都有500个请求,那么咱们server端就先挡雨请求了1000次api,这样的消耗毫无疑问是过大的,那么咱们须要怎么去作到接口的缓存呢?
咱们使用的node是express框架,而后在进入/index的时候咱们去fetch后台server的数据,而后咱们能够把数据传到client和server的config中,而不是每次在client,server中请求,而后咱们每次内存缓存失效咱们再去重新fetch后台server,这样咱们假设每秒500个请求量,咱们在node 端缓存5s,一共是2500个请求数量,咱们在node其实只是请求了一次后台的server以后每次拿的node内存去返回用户html,这种效果很定是极好的,也极大的缓解了咱们后台server的压力!
我作的公司首页迁移ssr效果:
43ms就获取了全部的数据,mobile端流量大概是电脑*10的时间,(其实4g状态下和电脑wifi也是不相上下的,几乎上下波动都在1m~2m左右),假设咱们手机网速很通常,时间*10就是0.4s的时间,也就是说在用户首次访问过咱们页面的状况下,只要手机中有缓存咱们能够一最快的数据打开页面,即便用户在首次访问,咱们的时间也能够控制在1s就能让用户看到大致的网页框架,而不是看了一秒的白屏!由于用户获取的其实就是node缓存的html,这个就跟在网上看一个html的专题页面没什么区别!咱们节省的时间也就说咱们client去请求接口的时间和框架渲染的时间,这个白屏的时间咱们至关于缓存在了node中,既不占用内存,也能让用户有一个更高的用户体验。