官網給出的解釋: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中,既不佔用內存,也能讓用戶有一個更高的用戶體驗。