ホームページ >ウェブフロントエンド >Vue.js >SSRとは何ですか? Vue で SSR サーバー側レンダリングを実装するにはどうすればよいですか?

SSRとは何ですか? Vue で SSR サーバー側レンダリングを実装するにはどうすればよいですか?

青灯夜游
青灯夜游転載
2022-02-25 11:23:535104ブラウズ

SSRとは何ですか?次の記事では、vue で SSR サーバーサイド レンダリングを実装する方法を紹介します。

SSRとは何ですか? Vue で SSR サーバー側レンダリングを実装するにはどうすればよいですか?

1. SSR とは何ですか

サーバーサイド レンダリングこれを SSR # と呼びます## (サーバー側レンダリングを意味します)

は、サーバー側でページの

HTML 構造のスプライシングを完了し、それをブラウザーに送信して、ステータスをバインドするページ処理テクノロジを指します。完全にインタラクティブなページになるプロセス。 [関連する推奨事項: vuejs ビデオ チュートリアル ]

まず

Web の開発履歴を 3 つの段階で見てみましょう:

    従来のサーバー レンダリング SSR
  • シングル ページ アプリケーション SPA
  • サーバーサイド レンダリング SSR

従来の Web 開発

Webページ コンテンツが提供される エンドサイドのレンダリングが完了し、一度にブラウザに転送される

SSRとは何ですか? Vue で SSR サーバー側レンダリングを実装するにはどうすればよいですか?

#ページを開いてソース コードを表示します。ブラウザが取得するのは # 全体です。 ##dom

構造

シングル ページ アプリケーション SPA

シングル ページ アプリケーションの優れたユーザー エクスペリエンスにより、それが徐々に主流になってきました。ページ コンテンツは次によってレンダリングされます。

JS

。このメソッドはクライアント用のレンダリングと呼ばれます

SSRとは何ですか? Vue で SSR サーバー側レンダリングを実装するにはどうすればよいですか?#ページを開いてソース コードを表示します。ブラウザはホスト要素のみを取得します

#app

、コンテンツなし

サーバー側レンダリング SSR

SSR

解決策として、バックエンドは完全な最初の画面をレンダリングします dom 構造体が返され、フロントエンドがそれを取得します コンテンツには、最初の画面と完全な spa 構造体が含まれています。アプリケーションはアクティブ化された後も、spa## で実行されます。 # way

フロントエンド開発を読んだ後、SSRとは何ですか? Vue で SSR サーバー側レンダリングを実装するにはどうすればよいですか?Vue

による

SSR の公式説明を見てみましょう。 : Vue.js は、クライアント アプリケーションを構築するためのフレームワークです。デフォルトでは、Vue コンポーネントをブラウザに出力して DOM を生成し、DOM を操作できます。ただし、同じコンポーネントをサーバー側の HTML 文字列としてレンダリングし、ブラウザーに直接送信し、最終的にこれらの静的タグをクライアント上の完全にインタラクティブなアプリケーションに「アクティブ化」することもできます。

Server Rendering Vue .js アプリケーションは、アプリケーションのコードのほとんどがサーバーとクライアントの両方で実行できるため、「同型」または「ユニバーサル」であると見なすこともできます。

ホームの説明から取得します。以下の結論:

Vue SSR
    は、
  • SPAVue SSR# でのサーバー側のレンダリングを改善したものです ##レンダリングされたページを実現するには、クライアント上でアクティブ化する必要がありますインタラクション
  • Vue SSR
  • には 2 つの部分が含まれます。サーバーによってレンダリングされる最初の画面と、インタラクティブな SPA
  • 2. 解決内容
  • SSR は主に次の 2 つの問題を解決します。

seo: 検索エンジンの優先クローリング ページ HTML

構造を取得して使用する

ssr

、サーバーはビジネスに関連付けられた
    HTML
  • をすでに生成しています。これは seo##最初の画面のレンダリング: ユーザーは次のことを確認できます。ページのすべての js がロードされるのを待たずにページを表示します (サーバーにプレッシャーがかかるため、どれをサーバー側でレンダリングするか、どれをクライアントに任せるかを検討する必要があります) )
  • しかし、
  • SSR の使用には次の欠点もあります:

複雑さ: プロジェクト全体の複雑さ

    ライブラリ サポート、コードの互換性
  • パフォーマンスの問題
  • 各リクエストは
  • n
  • インスタンスの作成であり、それ以外の場合はインスタンスの作成です。汚染され、消費量が非常に大きくなります。

    cache
      nodeserve
    • nginx 現在のユーザーの有効期限が切れているかどうかを判断し、期限切れになっていない場合はキャッシュします。 、今の結果を使用します。
    • ダウングレード: cpu を監視し、メモリ使用量が多すぎる場合は spa、単一のシェルを返します
    • ## サーバーの負荷が増加する 静的リソースのみを提供するフロントエンドとバックエンドの分離サーバーに比べてサーバーの負荷が大きくなるため、慎重に使用する必要があります
    • #したがって、
    Before SSR
  • を使用するかどうかを選択するときは、次の質問を慎重に自問する必要があります:
SEO が必要なページはわずか数ページだけですか?

、これらはプリレンダリングを使用できますか (実装方法

同型開発の場合、パッケージ化には引き続き
    webpack
  • を使用します。次の 2 つの問題を解決する必要があります: サーバー側最初の画面のレンダリングとクライアント側のアクティベーション

    ここでは、サーバーの最初の画面レンダリング用のサーバーbundle ファイルと、クライアントのアクティベーション用のクライアントbundle ファイルを生成する必要があります

    SSRとは何ですか? Vue で SSR サーバー側レンダリングを実装するにはどうすればよいですか?

    ##2 つの異なる入り口を除いて、コード構造は前の

    vueapplication

    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"); });

    Configure

    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 クロス環境 - 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##にマウント #<pre class="brush:js;toolbar:false;">import { createStore } from &amp;#39;./store&amp;#39; export function createApp (context) { // 创建实例 const store = createStore() const app = new Vue({ store, // 挂载 render: h =&gt; h(App) }) return { app, router, store } }</pre> サーバー側レンダリングはアプリケーションの「スナップショット」です。アプリケーションが非同期データに依存している場合は、レンダリングを開始する前にデータをプリフェッチして解析する必要がありますIn

    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);
                    });
                },
            },
        });
    }

    コンポーネントのデータ プリフェッチ ロジック<pre class="brush:js;toolbar:false;">export default { asyncData({ store, route }) { // 约定预取逻辑编写在预取钩⼦asyncData中 // 触发 action 后,返回 Promise 以便确定请求结果 return store.dispatch(&quot;getCount&quot;); } };</pre>サーバー側のデータ プリフェッチ、

    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

    <pre class="brush:js;toolbar:false;">// 导出store const { app, router, store } = createApp(); // 当使⽤ template 时,context.state 将作为 window.__INITIAL_STATE__ 状态⾃动嵌⼊到最终的 HTML // 在客户端挂载到应⽤程序之前,store 就应该获取到状态: if (window.__INITIAL_STATE__) { store.replaceState(window.__INITIAL_STATE__); }</pre>クライアント データ プリフェッチ処理を取得する必要があります。 main.js

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

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

    概要

    使用

    ssr
      シングルトン モードはありません。ユーザー リクエストごとに新しい
    • vue

      インスタンスが作成されます実装

      ssr
    • 必須 Realize サーバー-サイドのファースト スクリーンのレンダリングとクライアントのアクティブ化
    • サーバー側の非同期データ取得

      asyncData
    • は、ファースト スクリーンの非同期取得とスイッチング コンポーネントの取得に分けることができます
    • 最初の画面はデータを非同期で取得します。これはサーバー側のプリレンダリング中に完了するはずです。

      スイッチング コンポーネントは
        mixin
      • を通じてミックスされ、データ取得は
      • beforeMount
      • フック
      • (学習ビデオ共有:
      vuejs チュートリアル
    Web フロントエンド

    )

以上がSSRとは何ですか? Vue で SSR サーバー側レンダリングを実装するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。