從壹開始先後端分離 [ Vue2.0+.NET Core2.1] 二十五║初探服務端渲染(我的博客二)

 緣起

時間真快,如今已是這個系列教程的下半部 Vue 第 12 篇了,昨天我也簡單思考了下,可能明天再來一篇,Vue 就基本告一段落了,由於什麼呢,這裏給你們說個題外話,當時寫博文的時候,只是想給你們增長點兒學習的動力,天天提醒下,徹底沒有提綱或者安排說明什麼的,就是按照我本身學的方向走,正好發現了一個規律就是:每個系列正好是 1 個引子 + 12 篇正文,不知道你們對這個有沒有感受,你們可能看到個人頭像就知道了,哈哈,其實我是一個紅迷,正好這裏機緣巧合,兩個系列都造成了這樣的,我自私的給本身畫了一個規劃,正好是一組判詞——十二釵正冊,副冊,又副冊等等(說明:這是我本身的一廂情願哈,你們若是有紅迷愛好者,請不要噴我 [苦笑] ),按照計劃,應該會寫 9 部,除了引子,正好是 108 篇,哈哈之後的再說吧。這裏本身昨天瞎想了一通,若是有紅迷愛好者也能夠找我喲,加羣(867095512)或者我的QQ(3143422472)都行。css

由於 Vue 這個系列仍是不少的要說的,不過基本的我們都說了,你們能夠再經過傳送門看看《Vue 學習12篇》,今天呢,我們就說說一個老生常談的問題,就是如何實現 Vue 的 SSR 服務端渲染,你們若是是第一次接觸到,可能還比較陌生,不要慌,本文就給你們經過 Nuxt.js 框架,來說解這個問題,而後爲了之後實現我們的第二個項目,大體會是這個樣子(注意頁面的源代碼已經有內容了):html

 

 

注意:今天僅僅給你們說明 SSR 服務端渲染,會簡單說一個小 DEMO ,上圖中的框架具體的代碼,在我們的第二個博客項目中會說到,由於這個框架一兩篇是說不完的。前端

 

零、今天要完成藍色區塊部分

 

 

1、Vue 的 SSR 究竟是個什麼?

 

來自官方的解釋: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來處理的。

2、爲何要 SSR 服務端渲染?

一、首先我們須要說說搜索引擎 —— SEO

我們打開任何搜索引擎,不管是谷歌,仍是百度,亦或者搜狗等等,都能看到各類各樣的信息,文字,圖片,視頻,不知道你們是如何看待這個過程的,之前天真的我覺得是各類各樣的人,把本身的內容或者文檔提交給百度的服務器,而後咱們從百度的服務器去讀取,搜索,嗯,這個源自於我上高中的時候,搜索各類百度文庫的臆想,這個屬於我認識的搜索 1.0 。

後來我工做了,第一次開始寫 Web ,那個時候經理讓寫 TDK(Title + Description + Key),當時很好奇爲何要這麼說,經理說,是爲了 SEO ,額好吧,雖然不是很明白,大概懂了——設置好頁面的 TDK 之後,那個搜索引擎就能找到咱們的關鍵字,而後咱們就能夠在搜索引擎中搜到咱們的網站了,嗯~聽起來不錯,這個時候,我凌亂了,不是說咱們必須存進百度的數據庫,咱們才能搜索到麼,太神奇了吧,這就是我認識的搜索 2.0 。

轉眼過去一段時間,接觸的也愈來愈多,這個時候我負責了一個旅遊遊記的項目,老闆說,咱們的數據才千級別,比較少,用戶搜索我們的不是很方便,讓我搞些網上的數據(固然是合法的哈,只是用來展現,劃重點),這個時候我才知道了居然有 爬蟲 這個東西!原來網上的資源能夠爲所欲爲的獲取(合法的 × 3 !),這個時候回頭看引擎,哦!原來他們的都是爬取的網上的信息,舉個栗子:你們能夠在百度上搜索  「老張的哲學 .net core」,會看到我們的文章,能夠看到不只有題目,還有文章正文,這也就是說,百度爬取的不只僅是我們的 TDK ,還有我們的文章內容,大概這就是我認識的搜索 3.0 了。

 

因此說,我們若是須要想讓我們的文章,網站等被搜索引擎爬取,而後被你們所搜索到,必需要設置 TDK 和有頁面內容,可是這個時候,咱們滿心開新的打開咱們的 Vue 博客初版的項目時候,卻發現了這個樣子。。

 

我們辛辛苦苦寫的文章並無被瀏覽器所生成,天然之後不會被爬取到,若是要是咱們的商城也是這樣,那得少了多少流量,都是錢喲,因此說,若是是帶有文章類,內容類,商品類的 Web 項目,必定要解決這個問題,這個就是爲何要 SSR 的第一個緣由。

你們能夠看看文章最頂部的第二個項目的gif圖,就是經過 Nuxt 實現的服務端渲染,已經在源代碼中有了內容。 

 

二、爲 Vue 渲染瓶頸提供幫助

在上面兩個流程中,我們能夠看到,普通的客戶端渲染,和SSR 的服務端渲染的最大區別就在於,Html 片斷到底被誰控制,是 Vue 仍是服務端,若是是前端,h5請求 API 的時候,由於涉及到跨域,自己除了服務器的限制,還有用戶網絡,寬帶等諸多限制,我們須要等待入口頁面和 JS 下載成功,才能根據邏輯去獲取數據,中間又會出現不少問題。

而且若是當前頁面邏輯過多,數據過於繁瑣的狀況下,咱們的 vue 在客戶端渲染也會成爲性能瓶頸,最明顯的就是一些電商公司的首頁(好比某寶,那首頁打開真是複雜的要命)首次加載的時候,白屏的出現,loading圖的渲染,着實讓人難堪,雖然如今都採用柵欄預熱,但仍是不舒服。這是瓶頸問題,若是單單從 Vue 端來解決這個瓶頸,花費比較大,因此這個時候就用到了 SSR 服務端渲染,並且咱們可使用 Redis 去緩存這些頁面的 Html 片斷,咱們加載後會更快。

 

 3、如何實現 SSR 服務端渲染 —— 基於 webpack 的簡單程序

注意:今天我們主要是說明下 SSR 的內容、原理和基本使用,這裏簡單說一個小Demo栗子,我們以後的項目會用到一個框架 Nuxt.js

一、基於 webpack 初始化 npm 項目

新建文件夾 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。

 

二、新建 package.json 依賴包文件

{
  "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 安裝依賴包

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 的做用

 

 

4、結語

由於今天時間的問題,我們我先把核心文件搭建好了,你們知道了什麼是 SSR,以及存在的問題和解決辦法,可是具體的文件 entry-server.js 、 webpack-server.js、和server.js 都是什麼意思,明天我們再統一說明,而且順帶給你們引入新的框架 Nuxg.js,一個成熟的 SSR 框架。

 

5、Github

https://github.com/anjoy8/Blog.Vue/tree/master/Demo/Vue_SSR

下載後,先 npm install 安裝依賴

而後執行打包和啓動服務命令

npm run server
node server

 最後訪問 localhost:8089 

相關文章
相關標籤/搜索