サーバーサイドレンダリング (SSR) を使用する理由
サーバー側レンダリング (SSR) を使用する場合、いくつかのトレードオフもあります。
ディレクトリ構造 1. パッケージコマンドと開発コマンドを定義する開発コマンドはクライアント開発に使用されます パッケージングコマンドはサーバー側開発のデプロイに使用されます –watchはファイルを変更して自動的にパッケージ化するのに便利です "client:build": "webpack --config scripts/webpack.client.js --watch", "server:build": "webpack --config scripts/webpack.server.js --watch", "run:all": "同時に \"npm run client:build\" \"npm run server:build\"" client:buildとserver:buildを同時に実行するには 1.1 パッケージ.json { "名前": "11.vue-ssr", "バージョン": "1.0.0", "説明": ""、 "メイン": "index.js", 「スクリプト」: { "client:dev": "webpack サーブ --config スクリプト/webpack.client.js", "client:build": "webpack --config scripts/webpack.client.js --watch", "server:build": "webpack --config scripts/webpack.server.js --watch", "run:all": "同時に \"npm run client:build\" \"npm run server:build\"" }, 「キーワード」: [], "著者": ""、 「ライセンス」: 「ISC」、 「依存関係」: { 「同時に」: 「^5.3.0」、 "コア": "^2.13.1", "koa-ルーター": "^10.0.0", "koa-static": "^5.0.0", "vue": "^2.6.12", "vue-router": "^3.4.9", "vue-server-renderer": "^2.6.12", "vuex": "^3.6.0", "webpack-merge": "^5.7.3" }, 「devDependencies」: { "@babel/core": "^7.12.10", "@babel/プリセット環境": "^7.12.11", "バベルローダー": "^8.2.2", "css-loader": "^5.0.1", "html-webpack-プラグイン": "^4.5.1", "vue-loader": "^15.9.6", "vue-style-loader": "^4.1.2", "vue-テンプレートコンパイラ": "^2.6.12", "ウェブパック": "^5.13.0", "webpack-cli": "^4.3.1", "webpack-dev-server": "^3.11.2" } } 1.2 webpack.base.js の基本設定 // webpack によってパッケージ化されたエントリファイルは設定をエクスポートする必要があります // webpack webpack-cli // @babel/core babel のコア モジュール // babel-loader は webpack と babel のブリッジです // @babel/preset-env は es6+ を低レベルの構文に変換します // vue-loader vue-template-compiler は .vue ファイルを解析してテンプレートをコンパイルします // vue-style-loader css-loader は CSS スタイルを解析してスタイル タグに挿入します。vue-style-loader はサーバー側のレンダリングをサポートします const path = require('path'); HtmlWebpackPlugin は 'html-webpack-plugin' を必要とします。 const VueLoaderPlugin = require('vue-loader/lib/plugin') モジュール.エクスポート = { モード: '開発'、 出力: { ファイル名: '[name].bundle.js' , // デフォルトは main、デフォルトは dist ディレクトリ パス: path.resolve(__dirname,'../dist') }, モジュール: { ルール: [{ テスト: /\.vue$/, 使用: 'vue-loader' }, { テスト: /\.js$/, 使用: { ローダー: 'babel-loader', // @babel/core -> preset-env オプション: presets: ['@babel/preset-env'], // プラグインのコレクション} }, exclude: /node_modules/ // node_modules の下のファイルを検索する必要がないことを示します}, { テスト: /\.css$/, 使用方法: ['vue-style-loader', { ローダー: 'css-loader', オプション: esModule: false, // vue-style-loaderは、 } }] // 右から左へ実行します }] }, プラグイン: [ 新しい VueLoaderPlugin() // 修正済み] } 1.3 webpack.client.js構成はクライアント開発構成であり、通常のvue spa開発モード構成です。 const {merge} = require('webpack-merge'); 定数 base = require('./webpack.base'); 定数パス = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = merge(base,{ エントリー: { クライアント:path.resolve(__dirname, '../src/client-entry.js') }, プラグイン:[ 新しいHtmlWebpackプラグイン({ テンプレート: path.resolve(__dirname, '../public/index.html'), ファイル名:'client.html' // デフォルト名はindex.htmlです })、 ] }) 1.4 webpack.server.js 構成はパッケージ化後のサーバー展開に使用されます 定数 base = require('./webpack.base') const {merge} = require('webpack-merge'); const HtmlWebpackPlugin = require('html-webpack-plugin') 定数パス = require('path') module.exports = merge(base,{ ターゲット: 'ノード', エントリー: { server:path.resolve(__dirname, '../src/server-entry.js') }, 出力:{ libraryTarget:"commonjs2" // module.exports export}, プラグイン:[ 新しいHtmlWebpackプラグイン({ テンプレート: path.resolve(__dirname, '../public/index.ssr.html'), ファイル名:'server.html', 除外チャンク:['server'], 縮小:false、 クライアント:'/client.bundle.js' // デフォルト名はindex.htmlです })、 ] }) excludeChunks:['server']はserver.bundle.jsパッケージをインポートしません クライアントは変数です ファイル名はパッケージ化後に生成されるHTMLファイルの名前です。 テンプレート: テンプレートファイル 2. HTMLファイルを書く2人分: 2.1 パブリック/index.html <!DOCTYPE html> <html lang="ja"> <ヘッド> <メタ文字セット="UTF-8"> <meta name="viewport" content="width=デバイス幅、初期スケール=1.0"> <title>ドキュメント</title> </head> <本文> <div id="アプリ"></div> </本文> </html> 2.2 パブリック/index.ssr.html <!DOCTYPE html> <html lang="ja"> <ヘッド> <メタ文字セット="UTF-8"> <meta name="viewport" content="width=デバイス幅、初期スケール=1.0"> <title>ドキュメント</title> </head> <本文> <!--vue-ssr-outlet--> <!-- ejs テンプレート --> <script src="<%=htmlWebpackPlugin.options.client%>"></script> </本文> </html> <!--vue-ssr-outlet--> は、サーバーがDOMをレンダリングするために使用する固定スロット位置です。<%=htmlWebpackPlugin.options.client%> は、htmlwebpackplugin の変数を埋めます。 3. 通常のVue開発に従って、対応するファイルを記述します。app.jsファイルを定義する src/app.js エントリを関数に変換する目的は、サーバーがレンダリングされるたびにこのファクトリー関数を通じて新しいインスタンスを返し、訪問者全員が自分のインスタンスを取得できるようにすることです。 'vue' から Vue をインポートします。 './App.vue' からアプリをインポートします。 './router.js' から createRouter をインポートします。 './store.js' から createStore をインポートします。 // エントリを関数に変換する目的は、サーバーがレンダリングされるたびにこのファクトリ関数を通じて新しいインスタンスを返すことです。これにより、訪問者全員が自分のインスタンスを取得できるようになります。 export default () => { 定数ルーター = createRouter(); 定数ストア = createStore() constアプリ = 新しいVue({ ルーター、 店、 レンダリング: h => h(App) }); {アプリ、ルーター、ストア} を返します } src/app.vue <テンプレート> <div id="アプリ"> <router-link to="/">foo</router-link> <router-link to="/bar">バー</router-link> <ルータービュー></ルータービュー> </div> </テンプレート> <スクリプト> デフォルトをエクスポートします {}; </スクリプト> src/コンポーネント/Bar.vue <テンプレート> <div> {{$store.state.name}} </div> </テンプレート> <スタイル スコープ="true"> div { 背景: 赤; } </スタイル> <スクリプト> エクスポートデフォルト{ asyncData(store){ // メソッドはサーバー上で実行されますが、このメソッドはバックエンドで実行されます console.log('server call') // axios.get('/サーバーパス') Promise.resolve('success') を返します。 }, マウントされた(){ // ブラウザは実行し、バックエンドは無視します} } </スクリプト> src/コンポーネント/Foo.vue <テンプレート> <div @click="show">フード</div> </テンプレート> <スクリプト> エクスポートデフォルト{ 方法:{ 見せる(){ 警告(1) } } } </スクリプト> src/ルーター.js 'vue' から Vue をインポートします。 'vue-router' から VueRouter をインポートします。 './components/Foo.vue' から Foo をインポートします。 './components/Bar.vue' から Bar をインポートします。 Vue.use(VueRouter); // 2つのグローバルコンポーネントが内部的に提供されます Vue.component() //サーバーにアクセスするすべての人がルーティングシステムを生成する必要があります export default ()=>{ ルーター = new VueRouter({ モード: '履歴', ルート:[ {パス:'/',コンポーネント:Foo}, {path:'/bar',component:Bar}, // 遅延読み込み。パス {path:'*',component:{ に従って対応するコンポーネントを動的に読み込みます。 レンダリング:(h)=>h('div',{},'404') }} ] }); リターンルーター; } //フロントエンドルーティングハッシュ履歴の2つの方法 // ハッシュ # // ルーティングとは、異なるパスに応じて異なるコンポーネントをレンダリングすることです。ハッシュ値の特徴は、ハッシュ値の変更によってページが再レンダリングされないことです。ハッシュ値の変更を監視して、対応するコンポーネントを表示できます(履歴を生成できます)。hashApi の特徴は、醜いことです(サーバーがハッシュ値を取得できない) // historyApi H5 の API は美しいです。問題は、更新すると 404 が生成されることです。 src/store.js 'vue' から Vue をインポートします。 'vuex' から Vuex をインポートします。 Vue.js で Vuex をビルドします。 // サーバー上の vuex を使用してグローバル変数 window にデータを保存し、サーバーによってレンダリングされたデータをブラウザによってレンダリングされたデータに置き換えます export default ()=>{ ストア = 新しい Vuex.Store({ 州:{ 名前:'zhufeng' }, 突然変異: changeName(状態、ペイロード){ state.name = ペイロード } }, アクション:{ changeName({commit}){// store.dispatch('changeName') 新しい Promise を返します ((resolve,reject)=>{ タイムアウトを設定する(() => { コミット('changeName','jiangwen'); 解決する(); }, 5000); }) } } }); if(typeof window!='undefined' && window.__INITIAL_STATE__){ // ブラウザがレンダリングを開始します // バックエンドのレンダリング結果をフロントエンドのコアメソッドに同期します vuex store.replaceState(window.__INITIAL_STATE__); // サーバーによってロードされたデータに置き換えます} 返品ストア; } 4. エントリファイルを定義するクライアント パッケージのパッケージ エントリ ファイル: src/client-entry.jsはクライアントのjsエントリファイルです './app.js' から createApp をインポートします。 app を createApp() します。 app.$mount('#app'); // クライアント側のレンダリングでは client-entry.js を直接使用できます src/server-entry.js サーバーエントリファイル サーバーからの要求に応じてサーバーによって実行される機能です。 // サーバーエントリ import createApp from './app.js'; // サーバー側レンダリングは関数を返すことができます export default (context) => { // サーバーはメソッドを呼び出すときに url 属性を渡します // このメソッドはサーバー上で呼び出されます // ルーティングは非同期コンポーネントなので、ここでルートが読み込まれるのを待つ必要があります const { url } = context; return new Promise((resolve, reject) => { // renderToString() let { app, router, store } = createApp(); // vue-router router.push(url); // 永続的なジャンプ/パスを示します router.onReady(() => { // ルートのジャンプが完了し、コンポーネントがトリガーする準備ができるまで待機します const matchComponents = router.getMatchedComponents(); // /abc if (matchComponents.length == 0) { //フロントエンドルートに一致しません return deny({ code: 404 }); } それ以外 { // matchComponents はルートに一致するすべてのコンポーネント (ページレベルのコンポーネント) を参照します Promise.all(matchComponents.map(コンポーネント => { if (component.asyncData) { // サーバーはレンダリング時にデフォルトでページレベルコンポーネント内のasyncDataを見つけ、サーバー上にvuexを作成してasyncDataに渡します。 コンポーネント.asyncData(store) を返します。 } })).then(()=>{ // デフォルトではウィンドウの下に変数が生成されます。これはデフォルトで実行されます // "window.__INITIAL_STATE__={"name":"jiangwen"}" context.state = store.state; // サーバーが実行されると、最新の状態が store.state に保存されます。resolve(app); // app はデータを取得したインスタンスです。 }) } }) }) // アプリは newVue に対応しており、ルーターによって管理されていません。ルーターがジャンプするまで待ってからサーバー側のレンダリングを実行したい // ユーザーが存在しないページにアクセスしたときに、フロントエンドのルートを一致させる方法 // 毎回新しいアプリケーションを生成できます} // ユーザーが bar: にアクセスすると、サーバー側レンダリングがサーバー上で直接実行され、レンダリングされた結果がブラウザーに返されます。 ブラウザはjsスクリプトを読み込み、パスに従ってjsスクリプトを読み込み、バーを再レンダリングします。
5. ノードでデプロイされたサーバー側のファイルserver.jsを定義し、対応するテンプレートファイルを要求します。リクエスト処理にはkoaとkoa-routerを使用する vue-server-rendererはサーバーサイドレンダリングに必須のパッケージです Koa-staticはjsファイルなどの静的リソースのリクエストを処理します serverBundleはパッケージ化されたjsです テンプレートは、サーバエントリserver:buildの後にパッケージ化されたHTMLです。 const Koa = require('koa'); const app = new Koa(); const Router = require('koa-router'); 定数ルーター = 新しいルーター(); const VueServerRenderer = require('vue-server-renderer') const static = require('koa-static') 定数 fs = require('fs'); 定数パス = require('path') 定数 serverBundle = fs.readFileSync(path.resolve(__dirname, 'dist/server.bundle.js'), 'utf8') const テンプレート = fs.readFileSync(path.resolve(__dirname, 'dist/server.html'), 'utf8'); // インスタンスに基づいてレンダラーを作成し、パッケージ化された js とテンプレートファイルを渡します。const render = VueServerRenderer.createBundleRenderer(serverBundle, { テンプレート }) // localhost:3000/ へのリクエスト url パラメータ - 》 {url:ctx.url} に従って、それを serverBundle に渡すと、サーバー上のパッケージ化された .js ルーティング システムに従ってルートの完全な DOM 分解を含むページがレンダリングされます。router.get('/', async (ctx) => { console.log('ジャンプ') ctx.body = 新しい Promise((resolve, reject) => {を待ちます render.renderToString({url:ctx.url},(err, html) => { // CSS を有効にするには、コールバック メソッドのみを使用できます if (err) deny(err); 解決(html) }) }) // const html = await render.renderToString(); // 文字列を生成する // console.log(html) }) // ユーザーが存在しないサーバー パスにアクセスすると、ホームページに戻ります。フロントエンド js を介してレンダリングすると、コンポーネントはパスに従って再レンダリングされます。// ユーザーがリフレッシュする限り、サーバーにリクエストが送信されます。router.get('/(.*)', async (ctx)=>{ console.log('ジャンプ') ctx.body = 新しい Promise((resolve, reject) => {を待ちます render.renderToString({url:ctx.url},(err, html) => { // サーバー側レンダリングによるレンダリング後に戻る if (err && err.code == 404) resolve(`not found`); コンソール.log(html) 解決(html) }) }) }) // クライアントがリクエストを送信すると、最初に dist ディレクトリを検索します。app.use(static(path.resolve(__dirname,'dist'))); // シーケンスの問題 app.use(router.routes()); // 静的ファイルを検索する前に、定義したルートを使用するようにしてください app.listen(3000); 5.1 localhost:3000/ へのリクエスト リクエスト URL パラメータ - 》 {url:ctx.url} に従って、それを serverBundle に渡すと、サーバー上のパッケージ化された .js ルーティング システムに従ってルートの完全な DOM 分解を含むページがレンダリングされます。 /に対応するコンポーネントはFooなので、ページにはFooが表示されます。 ウェブページのソースコードはDOMとして解析され、SEOに使用できます。 5.2 リクエストがhttp://localhost:3000/barの場合 ルートは/(.*)になります。 renderToStringはURLを渡します 行きます server-entry.js ファイルのデフォルト関数も vue です。クライアントの本来のロジックをすべて含みますが、サーバー上で操作されます。 URLは/barです ルート/barに従ってBarコンポーネントを削除します ルーターは bar にジャンプし、ページは bar コンポーネントになります。 同時にasyncData関数を実行すると、ストアやその他のデータが書き換えられる可能性がある。 次に、context.state = store.state を割り当てて、ストアの状態オブジェクトをウィンドウに追加することを忘れないでください。 window.INITIAL_STATE = {"name":"jiangwen"} store.js(window.INITIAL_STATE )を再処理することを忘れないでください store.replaceState( window.INITIAL_STATE )はサーバーの状態をクライアントに置くことです dist/server.htmlがパッケージ化された後、/client.bundle.jsが導入されるので、静的リクエスト処理を行うにはkoa-staticが必要です。 <!DOCTYPE html> <html lang="ja"> <ヘッド> <メタ文字セット="UTF-8"> <meta name="viewport" content="width=デバイス幅、初期スケール=1.0"> <title>ドキュメント</title> </head> <本文> <!--vue-ssr-outlet--> <!-- ejs テンプレート --> <script src="/client.bundle.js"></script> </本文> </html> 6. 展開6.1 npm run run:allコマンドを実行する "run:all": "同時に \"npm run client:build\" \"npm run server:build\"" js html などを含むクライアントおよびサーバーのリソース パッケージをパッケージ化します。 次にserver.js全体をサーバー上に置きます node server.jsを実行してノードサーバーを起動します。 6.2 server.js で指定されている server.bundle.js と server.html を、対応するサーバー フォルダーに指定するだけです。 コマンドの説明
サーバー側で使用する場合、ブラウザでは client.bundle.js が使用され、サーバーでは server.bundle.js が使用されます。 7. まとめ1. SSR にはまずノード サーバーと vue-server-renderer パッケージが必要です。 2. beforeMount またはマウントされたライフサイクルはサーバー側では使用できないことを考慮して、通常の Vue 開発を使用します。 3. server.jsを作成し、koaまたはexpressを設定してリクエスト解析を行い、serverBundleとテンプレートを VueServerRenderer.createBundleRenderer 関数 レンダリングを取得する 4. render.renderToStringは、/barなどの要求されたルートを渡します。 5. この時、serverBundle のデフォルト関数 (server-entry.js パッケージから派生) に入り、vue インスタンス アプリを作成し、ルーティング vue インスタンスを分析してからルートをジャンプします。 この時、変更されたのはサーバー側の vue インスタンスのみで、ページにはまだ反映されていません。 6. 対応するコンポーネントの asyncData 関数を実行します。これにより、store.state が変更される可能性があります。次に、その値を context.state に割り当てます。 7.resolve(app) このとき、server.js 内の render は、現時点での vue インスタンス app のルーティング状態に応じて DOM を解析し、ページに返します。ctx.body = ...resolve(html); 8. この時点で、ページは通常のルーティングマッチング後のDOM構造を取得します。 9. html にウィンドウが表示されます。INITIAL_STATE ={"name":"zhufeng"} は、サーバーのストア ステータスを記録することと同じです。 10. クライアントがストアを実行すると、実際にはサーバー上の状態は変更されません。store.replaceState( window.INITIAL_STATE ); を実行して、サーバー上の状態を置き換えます。 11. 全体的な状況としては、サーバーとクライアントの両方に js パッケージがあります。事前にサーバー上で js パッケージを実行し、dom を解析して表示します。サーバーは終了し、残りのロジックはクライアントの js によって処理されます。 コンセプトマップ 公式サイト:
要約するこれで、vue の ssr サーバーサイドレンダリングに関するこの記事は終了です。vue ssr サーバーサイドレンダリングに関するその他の関連コンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
<<: win10 での mysql5.7.21 の詳細なインストール手順
この記事では、テーブルページング機能を実現するためのVueの具体的なコードを例として紹介します。具体...
Springboot プロジェクトをサーバーにデプロイする方法としては、war パッケージにパッケー...
//MySQL ステートメント SELECT * FROM `MyTable` WHERE `id...
MySQL 8.0.22のダウンロード、インストール、設定方法、参考までに具体的な内容は次のとおりで...
ステップ1: サードパーティの信頼できるSSL証明書に署名するAlibaba Cloud で直接、無...
Linux バージョンに関する情報を表示および解釈するのは、見た目よりも少し複雑です。単純なバージョ...
win docker-desktopを使ってコンテナ開発に接続し、ネットワーク上で色々試してみたいと...
目次分離効果コマンドラインの説明関与する機能分離効果-- 別居前1,2,3,4 -- 別居後1 2 ...
目次使いやすいプロジェクトを作成するvue-cli 作成ヴィートクリエイションvue-routerを...
開発に Vue を使用する場合、次のような状況に遭遇することがあります。Vue インスタンスを生成し...
最近、社内の業務調整により、以前の超長文のロジックが大幅に変更されたため、リファクタリングする予定で...
目次製品要件アイデア問題ライブラリ選択をドラッグコンポーネントを生成する方法コンポーネントを生成する...
PCIE には 4 つの異なる仕様があります。下の図でそのうちの 2 つを見てみましょう。マザーボー...
目次Vuex とは何ですか? Vuex 使用サイクル図私のストアディレクトリvuexの例の実装要約す...
矢印関数は ES6 の新機能です。独自の this はありません。その this ポイントは外部のコ...