時間真快,如今已是這個系列教程的下半部 Vue 第 12 篇了,昨天我也簡單思考了下,可能明天再來一篇,Vue 就基本告一段落了,由於什麼呢,這裏給你們說個題外話,當時寫博文的時候,只是想給你們增長點兒學習的動力,天天提醒下,徹底沒有提綱或者安排說明什麼的,就是按照我本身學的方向走,正好發現了一個規律就是:每個系列正好是 1 個引子 + 12 篇正文,不知道你們對這個有沒有感受,你們可能看到個人頭像就知道了,哈哈,其實我是一個紅迷,正好這裏機緣巧合,兩個系列都造成了這樣的,我自私的給本身畫了一個規劃,正好是一組判詞——十二釵正冊,副冊,又副冊等等(說明:這是我本身的一廂情願哈,你們若是有紅迷愛好者,請不要噴我 [苦笑] ),按照計劃,應該會寫 9 部,除了引子,正好是 108 篇,哈哈之後的再說吧。這裏本身昨天瞎想了一通,若是有紅迷愛好者也能夠找我喲,加羣(867095512)或者我的QQ(3143422472)都行。css
由於 Vue 這個系列仍是不少的要說的,不過基本的我們都說了,你們能夠再經過傳送門看看《Vue 學習12篇》,今天呢,我們就說說一個老生常談的問題,就是如何實現 Vue 的 SSR 服務端渲染,你們若是是第一次接觸到,可能還比較陌生,不要慌,本文就給你們經過 Nuxt.js 框架,來說解這個問題,而後爲了之後實現我們的第二個項目,大體會是這個樣子(注意頁面的源代碼已經有內容了):html
注意:今天僅僅給你們說明 SSR 服務端渲染,會簡單說一個小 DEMO ,上圖中的框架具體的代碼,在我們的第二個博客項目中會說到,由於這個框架一兩篇是說不完的。前端
來自官方的解釋:vue
Vue.js 是構建客戶端應用程序的框架。默認狀況下,能夠在瀏覽器中輸出 Vue 組件,進行生成 DOM 和操做 DOM。然而,也能夠將同一個組件渲染爲服務器端的 HTML 字符串,將它們直接發送到瀏覽器,最後將這些靜態標記"激活"爲客戶端上徹底可交互的應用程序。node
服務端渲染的 Vue.js 應用程序也能夠被認爲是"同構"或"通用",由於應用程序的大部分代碼均可以在服務器和客戶端上運行。webpack
羞澀難懂ios
個人我的理解就是:git
一、目前Vue的模式是:github
在生成頁面的工做中,咱們如今是把組件放在瀏覽器裏,而後把 Data 填充到組件中生成 DOM,這也是通常的異步操做的動做,我們平時必定是這麼操做的,先在頁面寫上 DIV,而後用 Jquery 獲取數據,把數據填充到 DIV 裏web
二、SSR的模式呢,轉變成了先在服務器中 Data 先把組件先渲染成 Html 字符串,當成靜態資源,就像 css 字符串那樣,再拋到前臺頁面。
這第二種就是 SSR 服務端渲染,你們應該發現了這個和普通的區別——就是渲染html片斷的控制權轉向了服務端,那爲何要這麼作呢?請往下看。
注意:這裏的服務端,並非在咱們的.net core api 中生成的,而是在vue中,咱們經過webpack 打包後 node server來處理的。
我們打開任何搜索引擎,不管是谷歌,仍是百度,亦或者搜狗等等,都能看到各類各樣的信息,文字,圖片,視頻,不知道你們是如何看待這個過程的,之前天真的我覺得是各類各樣的人,把本身的內容或者文檔提交給百度的服務器,而後咱們從百度的服務器去讀取,搜索,嗯,這個源自於我上高中的時候,搜索各類百度文庫的臆想,這個屬於我認識的搜索 1.0 。
後來我工做了,第一次開始寫 Web ,那個時候經理讓寫 TDK(Title + Description + Key),當時很好奇爲何要這麼說,經理說,是爲了 SEO ,額好吧,雖然不是很明白,大概懂了——設置好頁面的 TDK 之後,那個搜索引擎就能找到咱們的關鍵字,而後咱們就能夠在搜索引擎中搜到咱們的網站了,嗯~聽起來不錯,這個時候,我凌亂了,不是說咱們必須存進百度的數據庫,咱們才能搜索到麼,太神奇了吧,這就是我認識的搜索 2.0 。
轉眼過去一段時間,接觸的也愈來愈多,這個時候我負責了一個旅遊遊記的項目,老闆說,咱們的數據才千級別,比較少,用戶搜索我們的不是很方便,讓我搞些網上的數據(固然是合法的哈,只是用來展現,劃重點),這個時候我才知道了居然有 爬蟲 這個東西!原來網上的資源能夠爲所欲爲的獲取(合法的 × 3 !),這個時候回頭看引擎,哦!原來他們的都是爬取的網上的信息,舉個栗子:你們能夠在百度上搜索 「老張的哲學 .net core」,會看到我們的文章,能夠看到不只有題目,還有文章正文,這也就是說,百度爬取的不只僅是我們的 TDK ,還有我們的文章內容,大概這就是我認識的搜索 3.0 了。
因此說,我們若是須要想讓我們的文章,網站等被搜索引擎爬取,而後被你們所搜索到,必需要設置 TDK 和有頁面內容,可是這個時候,咱們滿心開新的打開咱們的 Vue 博客初版的項目時候,卻發現了這個樣子。。
我們辛辛苦苦寫的文章並無被瀏覽器所生成,天然之後不會被爬取到,若是要是咱們的商城也是這樣,那得少了多少流量,都是錢喲,因此說,若是是帶有文章類,內容類,商品類的 Web 項目,必定要解決這個問題,這個就是爲何要 SSR 的第一個緣由。
你們能夠看看文章最頂部的第二個項目的gif圖,就是經過 Nuxt 實現的服務端渲染,已經在源代碼中有了內容。
在上面兩個流程中,我們能夠看到,普通的客戶端渲染,和SSR 的服務端渲染的最大區別就在於,Html 片斷到底被誰控制,是 Vue 仍是服務端,若是是前端,h5請求 API 的時候,由於涉及到跨域,自己除了服務器的限制,還有用戶網絡,寬帶等諸多限制,我們須要等待入口頁面和 JS 下載成功,才能根據邏輯去獲取數據,中間又會出現不少問題。
而且若是當前頁面邏輯過多,數據過於繁瑣的狀況下,咱們的 vue 在客戶端渲染也會成爲性能瓶頸,最明顯的就是一些電商公司的首頁(好比某寶,那首頁打開真是複雜的要命)首次加載的時候,白屏的出現,loading圖的渲染,着實讓人難堪,雖然如今都採用柵欄預熱,但仍是不舒服。這是瓶頸問題,若是單單從 Vue 端來解決這個瓶頸,花費比較大,因此這個時候就用到了 SSR 服務端渲染,並且咱們可使用 Redis 去緩存這些頁面的 Html 片斷,咱們加載後會更快。
注意:今天我們主要是說明下 SSR 的內容、原理和基本使用,這裏簡單說一個小Demo栗子,我們以後的項目會用到一個框架 Nuxt.js
新建文件夾 VueSSRDemo3 ,進入以後,執行 npm i ,初始化項目
npm i
會看到一個 package-lock.json 文件,你們還記得這個麼,這個簡單來講,是保證項目的一致性,保證團結開發的時候都依賴相同的包,這裏再補充下:
一、npm 5.0.x 版本,無論package.json怎麼變,npm i 時都會根據lock文件下載
package-lock.json file not updated after package.json file is changed · Issue #16866 · npm/npm
這個 issue 控訴了這個問題,明明手動改了package.json,爲啥不給我升級包!而後就致使了5.1.0的問題...
二、npm 5.1.0 版本後 npm install 會無視lock文件 去下載最新的npm
而後有人提了這個issue why is package-lock being ignored? · Issue #17979 · npm/npm
控訴這個問題,最後演變成5.4.2版本後的規則。
三、npm 5.4.2 版本後 why is package-lock being ignored? · Issue #17979 · npm/npm
大體意思是,若是改了package.json,且package.json和lock文件不一樣,那麼執行`npm i`時npm會根據package中的版本號以及語義含義去下載最新的包,並更新至lock。
{ "name": "ssr", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "server": "webpack --config ./webpack/webpack.server.js", "client": "webpack --config ./webpack/webpack.client.js" }, "author": "laozhang", "license": "ISC", "dependencies": { "axios": "^0.16.0", "babel": "^6.23.0", "babel-plugin-transform-runtime": "^6.23.0", "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.7.0", "body-parser": "^1.18.3", "compression": "^1.7.2", "express": "^4.15.4", "express-http-proxy": "^1.2.0", "gulp": "^3.9.1", "gulp-shell": "^0.6.5", "http-proxy-middleware": "^0.18.0", "less": "^3.0.4", "less-loader": "^4.1.0", "shell": "^0.5.0", "superagent": "^3.8.3", "vue": "^2.2.2", "vue-meta": "^1.5.0", "vue-router": "^2.2.0", "vue-server-renderer": "^2.2.2", "vue-ssr-webpack-plugin": "^3.0.0", "vuex": "^2.2.1", "vuex-router-sync": "^4.2.0" }, "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^6.4.1", "babel-preset-es2015": "^6.24.1", "css-loader": "^0.28.4", "style-loader": "^0.18.2", "vue-loader": "^11.1.4", "vue-template-compiler": "^2.2.4", "webpack": "^2.7.0" } }
npm install
而後就會增長 node_modules 文件夾,這個你們就很熟悉了。
結構以下:
├── dist // 保存咱們的打包後的文件 ├── node_modules // 依賴包文件夾 ├── entry // │ └── entry-server.js // 服務端文件 ├── src // 咱們的項目的源碼編寫文件 │ ├── views // view存放目錄 │ │ ├── about.vue //about 頁面 │ │ ├── like.vue //like 頁面 │ │ └── Home.vue //Home 頁面 │ └── App.vue // App入口文件 │ └── main.js // 主配置文件 │ └── router.js // 路由配置文件 └── .babelrc // babel 配置文件 └── package.json // 項目依賴包配置文件 └── package-lock.json // npm5 新增文件,優化性能 └── server.js // server 文件 └── README.md // 說明文檔
總體結構就是這樣,而後就是開始寫入代碼了,一共七個文件,你們能夠本身試一試,或者直接下載 git 代碼
更新:
下邊沒有具體的更新,只有代碼和部分註釋,你們能夠看看個人下一篇文章,更好懂些:
《從壹開始先後端分離 [ Vue2.0+.NetCore2.1] 二十六║Client渲染、Server渲染知多少{補充}》
//一、/* entry-server.js */ import { createApp } from '../src/main' export default context => { return new Promise((resolve, reject) => { const app = createApp() // 更改路由 app.$router.push(context.url) // 獲取相應路由下的組件 const matchedComponents = app.$router.getMatchedComponents() // 若是沒有組件,說明該路由不存在,報錯404 if (!matchedComponents.length) { return reject({ code: 404 }) } resolve(app) }) } //二、三個 vue 頁面,和app vue頁面,就是簡單的寫法,你們能夠直接隨意 //三、//main.js import Vue from 'vue' import createRouter from './router' import App from './App.vue' // 導出一個工廠函數,用於建立新的vue實例 export function createApp() { const router = createRouter() const app = new Vue({ router, render: h => h(App) }) return app } /*四、 route.js */ import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) export default function createRouter() { let router = new VueRouter({ // 要記得增長mode屬性,由於#後面的內容不會發送至服務器,服務器不知道請求的是哪個路由 mode: 'history', routes: [ { alias: '/', path: '/home', component: require('./views/home.vue') }, { path: '/like', component: require('./views/like.vue') }, { path: '/about', component: require('./views/about.vue') } ] }) return router } /* 五、webpack.server.js */ const path = require('path'); const projectRoot = path.resolve(__dirname, '..'); module.exports = { // 此處告知 server bundle 使用 Node 風格導出模塊(Node-style exports) target: 'node', entry: ['babel-polyfill', path.join(projectRoot, 'entry/entry-server.js')], output: { libraryTarget: 'commonjs2', path: path.join(projectRoot, 'dist'), filename: 'bundle.server.js', }, module: { rules: [{ test: /\.vue$/, loader: 'vue-loader', }, { test: /\.js$/, loader: 'babel-loader', include: projectRoot, exclude: /node_modules/, options: { presets: ['es2015'] } }, { test: /\.less$/, loader: "style-loader!css-loader!less-loader" } ] }, plugins: [], resolve: { alias: { 'vue$': 'vue/dist/vue.runtime.esm.js' } } } //六、babelre文件 { "presets": [ "babel-preset-env" ], "plugins": [ "transform-runtime" ] } /*七、 server.js */ const express = require('express')() const renderer = require('vue-server-renderer').createRenderer() const createApp = require('./dist/bundle.server.js')['default'] // 響應路由請求 express.get('*', (req, res) => { const context = { url: req.url } // 建立vue實例,傳入請求路由信息 createApp(context).then(app => { renderer.renderToString(app, (err, html) => { if (err) { return res.state(500).end('運行時錯誤') } res.send(` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue2.0 SSR渲染頁面</title> </head> <body> ${html} </body> </html> `) }) }, err => { if(err.code === 404) { res.status(404).end('所請求的頁面不存在') } }) }) // 服務器監聽地址 express.listen(8089, () => { console.log('服務器已啓動!') })
npm run server
這個時候,你會發現,咱們的dist 文件夾內,多了一個 bundle.server.js 文件
node server
這個時候咱們就能夠看到效果了
這樣咱們已經達到了 SSR 的做用
由於今天時間的問題,我們我先把核心文件搭建好了,你們知道了什麼是 SSR,以及存在的問題和解決辦法,可是具體的文件 entry-server.js 、 webpack-server.js、和server.js 都是什麼意思,明天我們再統一說明,而且順帶給你們引入新的框架 Nuxg.js,一個成熟的 SSR 框架。
https://github.com/anjoy8/Blog.Vue/tree/master/Demo/Vue_SSR
下載後,先 npm install 安裝依賴
而後執行打包和啓動服務命令
npm run server
node server
最後訪問 localhost:8089