搜尋
首頁web前端Vue.js什麼是SSR? vue中怎麼實作SSR服務端渲染?

什麼是SSR?以下這篇文章跟大家介紹一下在vue中實作SSR服務端渲染的方法,希望對大家有幫助!

什麼是SSR? vue中怎麼實作SSR服務端渲染?

一、SSR是什麼

#Server-Side Rendering 我們稱之為SSR ,意為服務端渲染

指由服務側完成頁面的 HTML 結構拼接的頁面處理技術,傳送到瀏覽器,然後為其綁定狀態與事件,成為完全可互動頁面的過程。 【相關推薦:vuejs影片教學

先來看看Web3個階段的發展史:

  • 傳統服務端渲染SSR
  • 單頁應用SPA
  • 服務端渲染SSR

#傳統web開發

##網頁內容在服務端渲染完成,⼀次性傳輸到瀏覽器

什麼是SSR? vue中怎麼實作SSR服務端渲染?

開啟頁面查看原始碼,瀏覽器拿到的是全部的

dom#結構

單頁應用SPA

單頁應用優秀的使用者體驗,使其逐漸成為主流,頁面內容由

JS渲染出來,這種方式稱為客戶端渲染

什麼是SSR? vue中怎麼實作SSR服務端渲染?

開啟頁面查看源碼,瀏覽器拿到的僅有宿主元素

#app,並沒有內容

服務端渲染SSR

SSR解決方案,後端渲染出完整的首屏的dom結構返回,前端拿到的內容包含首屏及完整spa結構,應用激活後依然按照spa方式運行

什麼是SSR? vue中怎麼實作SSR服務端渲染?##看完前端發展,我們再看看

Vue

官方對SSR的解釋:

Vue.js 是建立客戶端應用程式的框架。預設情況下,可以在瀏覽器中輸出 Vue 元件,進行產生 DOM 和操作 DOM。然而,也可以將同一個元件渲染為伺服器端的HTML 字串,將它們直接傳送到瀏覽器,最後將這些靜態標記"激活"為客戶端上完全可交互的應用程式

伺服器渲染的Vue.js 應用程式也可以被認為是"同構"或"通用",因為應用程式的大部分程式碼都可以在伺服器和客戶端上運行

##我們從上門解釋得到以下結論:

Vue SSR
    是一個在
  • SPA上改良的服務端渲染透過Vue SSR
  • 渲染的頁面,需要在客戶端啟動才能實現互動
  • Vue SSR
  • 將包含兩部分:服務端渲染的首屏,包含互動的
  • SPA
  • 二、解決了什麼

SSR主要解決了以下兩種問題:

seo:搜尋引擎優先爬取頁面

HTML
    結構,使用
  • ssr時,服務端已經產生了和業務想關聯的HTML,有利於seo首屏呈現渲染:使用者不需要等待頁面所有js
  • 載入完成就可以看到頁面視圖(壓力來到了伺服器,所以需要權衡哪些用服務端渲染,哪些交給客戶端)
  • 但使用
  • SSR
同樣存在以下的缺點:

複雜度:整個專案的複雜度
  • #函式庫的支持性,程式碼相容
  • 效能問題
  • #每個請求都是

    n
      個實例的創建,不然會污染,消耗會變得很大
    • 快取 node serve 
    • 、 
    • nginx判斷目前使用者有沒有過期,如果沒過期的話就緩存,用剛剛的結果。 降級:監控cpu
    • 、記憶體佔用過多,就
    • spa,回傳單一的殼
    伺服器負載變大,相對於前後端分離務器只需要提供靜態資源來說,伺服器負載更大,所以要慎重使用
  • 所以在我們選擇是否使用
  • SSR
前,我們需要慎重問問自己這些問題:

需要
    SEO
  • 的頁面是否只是少數幾個,這些是否可以使用預渲染( Prerender SPA Plugin)實作

    首屏的請求回應邏輯是否複雜,資料回傳是否大量且緩慢
  • ##三、如何實作

對於同構開發,我們依然使用webpack打包,我們要解決兩個問題:服務端首屏渲染和客戶端啟動

這裡需要產生一個伺服器bundle檔案用於服務端首屏渲染和一個客戶端bundle檔案用於客戶端啟動

什麼是SSR? vue中怎麼實作SSR服務端渲染?

程式碼結構除了兩個不同入口之外,其他結構和之前vue應用完全相同

src 
├── router 
├────── index.js # 路由声明 
├── store 
├────── index.js # 全局状态 
├── main.js # ⽤于创建vue实例 
├── entry-client.js # 客户端⼊⼝,⽤于静态内容“激活” 
└── entry-server.js # 服务端⼊⼝,⽤于⾸屏内容渲染

路由配置

import Vue from "vue"; import Router from "vue-router"; Vue.use(Router);
//导出⼯⼚函数
export function createRouter(){
    return new Router({
    mode: 'history',
    routes: [ 
       // 客户端没有编译器,这⾥要写成渲染函数 
        { path: "/", component: { render: h => h('div', 'index page') } }, 
        { path: "/detail", component: { render: h => h('div', 'detail page') }} 
    ] 
  });
 }

主檔案main.js

跟之前不同,主檔案是負責建立vue實例的工廠,每次請求都會有獨立的vue實例建立

import Vue from "vue";
import App from "./App.vue";
import { createRouter } from "./router"; 
// 导出Vue实例⼯⼚函数,为每次请求创建独⽴实例 
// 上下⽂⽤于给vue实例传递参数
export function createApp(context) {
    const router = createRouter();
    const app = new Vue({ router, context, render: h => h(App) });
    return { app, router };
  }

編寫服務端入口src/entry-server.js

它的任務是建立Vue實例並根據傳入url指定首屏

import { createApp } from "./main";
// 返回⼀个函数,接收请求上下⽂,返回创建的vue实例
export default context => {
// 这⾥返回⼀个Promise,确保路由或组件准备就绪
    return new Promise((resolve, reject) => {
        const { app, router } = createApp(context);
        // 跳转到⾸屏的地址
        router.push(context.url);
        // 路由就绪,返回结果
        router.onReady(() => {
        resolve(app);
        }, reject);
    }); 
};

寫客戶端入口entry-client.js

#客戶端入口只需建立vue實例並執行掛載,這⼀步稱為啟動

import { createApp } from "./main"; 
// 创建vue、router实例
const { app, router } = createApp();
// 路由就绪,执⾏挂载
router.onReady(() => { app.$mount("#app"); });

webpack進行設定

安裝依賴

npm install webpack-node-externals lodash.merge -D

vue.config.js進行配置

// 两个插件分别负责打包客户端和服务端
const VueSSRServerPlugin = require("vue-server-renderer/server-plugin");
const VueSSRClientPlugin = require("vue-server-renderer/client-plugin");
const nodeExternals = require("webpack-node-externals");
const merge = require("lodash.merge");
// 根据传⼊环境变量决定⼊⼝⽂件和相应配置项
const TARGET_NODE = process.env.WEBPACK_TARGET === "node";
const target = TARGET_NODE ? "server" : "client";
module.exports = {
    css: {
        extract: false
    },
    outputDir: './dist/'+target,
    configureWebpack: () => ({
        // 将 entry 指向应⽤程序的 server / client ⽂件
        entry: `./src/entry-${target}.js`,
        // 对 bundle renderer 提供 source map ⽀持
        devtool: 'source-map',
        // target设置为node使webpack以Node适⽤的⽅式处理动态导⼊,
        // 并且还会在编译Vue组件时告知`vue-loader`输出⾯向服务器代码。
        target: TARGET_NODE ? "node" : "web",
        // 是否模拟node全局变量
        node: TARGET_NODE ? undefined : false,
        output: {
            // 此处使⽤Node⻛格导出模块
            libraryTarget: TARGET_NODE ? "commonjs2" : undefined
        },
        // https://webpack.js.org/configuration/externals/#function
        // https://github.com/liady/webpack-node-externals
        // 外置化应⽤程序依赖模块。可以使服务器构建速度更快,并⽣成较⼩的打包⽂件。
        externals: TARGET_NODE
        ? nodeExternals({
            // 不要外置化webpack需要处理的依赖模块。
            // 可以在这⾥添加更多的⽂件类型。例如,未处理 *.vue 原始⽂件,
            // 还应该将修改`global`(例如polyfill)的依赖模块列⼊⽩名单
            whitelist: [/\.css$/]
        })
        : undefined,
        optimization: {
            splitChunks: undefined
        },
        // 这是将服务器的整个输出构建为单个 JSON ⽂件的插件。
        // 服务端默认⽂件名为 `vue-ssr-server-bundle.json`
        // 客户端默认⽂件名为 `vue-ssr-client-manifest.json`。
        plugins: [TARGET_NODE ? new VueSSRServerPlugin() : new
                  VueSSRClientPlugin()]
    }),
    chainWebpack: config => {
        // cli4项⽬添加
        if (TARGET_NODE) {
            config.optimization.delete('splitChunks')
        }

        config.module
            .rule("vue")
            .use("vue-loader")
            .tap(options => {
            merge(options, {
                optimizeSSR: false
            });
        });
    }
};

對腳本進行配置,安裝依賴

npm i cross-env - D

"scripts": {
 "build:client": "vue-cli-service build",
 "build:server": "cross-env WEBPACK_TARGET=node vue-cli-service build",
 "build": "npm run build:server && npm run build:client"
}

執行打包:npm run build

#最後修改宿主檔案/public/index.html##

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width,initial-scale=1.0">
        <title>Document</title>
    </head>
    <body>
        <!--vue-ssr-outlet-->
    </body>
</html>

是服務端渲染入口位置,注意不能為了好看而在前後加空格

安裝

vuex

npm install -S vuex

建立

vuex工廠函數

import Vue from &#39;vue&#39;
import Vuex from &#39;vuex&#39;
Vue.use(Vuex)
export function createStore () {
    return new Vuex.Store({
        state: {
            count:108
        },
        mutations: {
            add(state){
                state.count += 1;
            }
        }
    })
}

#在

main.js檔案中掛載store

import { createStore } from &#39;./store&#39;
export function createApp (context) {
    // 创建实例
    const store = createStore()
    const app = new Vue({
        store, // 挂载
        render: h => h(App)
    })
    return { app, router, store }
}

伺服器端渲染的是應用程式的"快照",如果應用依賴於⼀些異步數據,那麼在開始渲染之前,需要先預取和解析好這些數據

store進行一步資料取得

export function createStore() {
    return new Vuex.Store({
        mutations: {
            // 加⼀个初始化
            init(state, count) {
                state.count = count;
            },
        },
        actions: {
            // 加⼀个异步请求count的action
            getCount({ commit }) {
                return new Promise(resolve => {
                    setTimeout(() => {
                        commit("init", Math.random() * 100);
                        resolve();
                    }, 1000);
                });
            },
        },
    });
}

元件中的資料預取邏輯

export default {
    asyncData({ store, route }) { // 约定预取逻辑编写在预取钩⼦asyncData中
        // 触发 action 后,返回 Promise 以便确定请求结果
        return store.dispatch("getCount");
    }
};

服務端資料預取,

entry-server.js

import { createApp } from "./app";
export default context => {
    return new Promise((resolve, reject) => {
        // 拿出store和router实例
        const { app, router, store } = createApp(context);
        router.push(context.url);
        router.onReady(() => {
            // 获取匹配的路由组件数组
            const matchedComponents = router.getMatchedComponents();

            // 若⽆匹配则抛出异常
            if (!matchedComponents.length) {
                return reject({ code: 404 });
            }

            // 对所有匹配的路由组件调⽤可能存在的`asyncData()`
            Promise.all(
                matchedComponents.map(Component => {
                    if (Component.asyncData) {
                        return Component.asyncData({
                            store,
                            route: router.currentRoute,
                        });
                    }
                }),
            )
                .then(() => {
                // 所有预取钩⼦ resolve 后,
                // store 已经填充⼊渲染应⽤所需状态
                // 将状态附加到上下⽂,且 `template` 选项⽤于 renderer 时,
                // 状态将⾃动序列化为 `window.__INITIAL_STATE__`,并注⼊ HTML
                context.state = store.state;

                resolve(app);
            })
                .catch(reject);
        }, reject);
    });
};

客戶端在掛載到應用程式之前,

store 就應該取得到狀態,entry-client.js

// 导出store
const { app, router, store } = createApp();
// 当使⽤ template 时,context.state 将作为 window.__INITIAL_STATE__ 状态⾃动嵌⼊到最终的 HTML 
// 在客户端挂载到应⽤程序之前,store 就应该获取到状态:
if (window.__INITIAL_STATE__) {
    store.replaceState(window.__INITIAL_STATE__);
}

客戶端資料預先取處理,

main.js

Vue.mixin({
    beforeMount() {
        const { asyncData } = this.$options;
        if (asyncData) {
            // 将获取数据操作分配给 promise
            // 以便在组件中,我们可以在数据准备就绪后
            // 通过运⾏ `this.dataPromise.then(...)` 来执⾏其他任务
            this.dataPromise = asyncData({
                store: this.$store,
                route: this.$route,
            });
        }
    },
});

修改伺服器啟動檔案

// 获取⽂件路径
const resolve = dir => require(&#39;path&#39;).resolve(__dirname, dir)
// 第 1 步:开放dist/client⽬录,关闭默认下载index⻚的选项,不然到不了后⾯路由
app.use(express.static(resolve(&#39;../dist/client&#39;), {index: false}))
// 第 2 步:获得⼀个createBundleRenderer
const { createBundleRenderer } = require("vue-server-renderer");
// 第 3 步:服务端打包⽂件地址
const bundle = resolve("../dist/server/vue-ssr-server-bundle.json");
// 第 4 步:创建渲染器
const renderer = createBundleRenderer(bundle, {
    runInNewContext: false, // https://ssr.vuejs.org/zh/api/#runinnewcontext
    template: require(&#39;fs&#39;).readFileSync(resolve("../public/index.html"), "utf8"), // 宿主⽂件
    clientManifest: require(resolve("../dist/client/vue-ssr-clientmanifest.json")) // 客户端清单
});
app.get(&#39;*&#39;, async (req,res)=>{
    // 设置url和title两个重要参数
    const context = {
        title:&#39;ssr test&#39;,
        url:req.url
    }
    const html = await renderer.renderToString(context);
    res.send(html)
})

小結

    ##使用
  • ssr

    不存在單例模式,每次使用者要求都會建立一個新的vue實例

  • 實作
  • ssr

    需要實作服務端首屏渲染與用戶端啟動

  • 服務端異步擷取資料
  • asyncData

    可以分為首屏非同步取得和切換元件取得

    #首屏異步獲取數據,在服務端預渲染的時候就應該已經完成
    • 切換組件通過
    • mixin
    • 混入,在beforeMount鉤子完成數據獲取
  • (學習影片分享:
vuejs教學

web前端

以上是什麼是SSR? vue中怎麼實作SSR服務端渲染?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:掘金社区。如有侵權,請聯絡admin@php.cn刪除
Vue常见面试题汇总(附答案解析)Vue常见面试题汇总(附答案解析)Apr 08, 2021 pm 07:54 PM

本篇文章给大家分享一些Vue面试题(附答案解析)。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

5 款适合国内使用的 Vue 移动端 UI 组件库5 款适合国内使用的 Vue 移动端 UI 组件库May 05, 2022 pm 09:11 PM

本篇文章给大家分享5 款适合国内使用的 Vue 移动端 UI 组件库,希望对大家有所帮助!

一文探究Angular中的服务端渲染(SSR)一文探究Angular中的服务端渲染(SSR)Dec 27, 2022 pm 07:24 PM

你知道 Angular Universal 吗?可以帮助网站提供更好的 SEO 支持哦!

聊聊vue指令中的修饰符,常用事件修饰符总结聊聊vue指令中的修饰符,常用事件修饰符总结May 09, 2022 am 11:07 AM

本篇文章带大家聊聊vue指令中的修饰符,对比一下vue中的指令修饰符和dom事件中的event对象,介绍一下常用的事件修饰符,希望对大家有所帮助!

如何覆盖组件库样式?React和Vue项目的解决方法浅析如何覆盖组件库样式?React和Vue项目的解决方法浅析May 16, 2022 am 11:15 AM

如何覆盖组件库样式?下面本篇文章给大家介绍一下React和Vue项目中优雅地覆盖组件库样式的方法,希望对大家有所帮助!

Vue 3中的SSR技术应用实践,提升应用的SEO效果Vue 3中的SSR技术应用实践,提升应用的SEO效果Sep 08, 2023 pm 12:15 PM

Vue3中的SSR技术应用实践,提升应用的SEO效果随着前端开发的快速发展,SPA(单页面应用)已经成为了主流。SPA的好处不言而喻,可以提供流畅的用户体验,但却在SEO(搜索引擎优化)方面有一些挑战。由于SPA在前端渲染阶段只返回一个HTML模板,大部分内容是通过JavaScript动态加载的,导致搜索引擎在抓取、索引和排名方面的困难。为了解决这个问题,

通过9个Vue3 组件库,看看聊前端的流行趋势!通过9个Vue3 组件库,看看聊前端的流行趋势!May 07, 2022 am 11:31 AM

本篇文章给大家分享9个开源的 Vue3 组件库,通过它们聊聊发现的前端的流行趋势,希望对大家有所帮助!

react与vue的虚拟dom有什么区别react与vue的虚拟dom有什么区别Apr 22, 2022 am 11:11 AM

react与vue的虚拟dom没有区别;react和vue的虚拟dom都是用js对象来模拟真实DOM,用虚拟DOM的diff来最小化更新真实DOM,可以减小不必要的性能损耗,按颗粒度分为不同的类型比较同层级dom节点,进行增、删、移的操作。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
2 週前By尊渡假赌尊渡假赌尊渡假赌
倉庫:如何復興隊友
1 個月前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒險:如何獲得巨型種子
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

mPDF

mPDF

mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),