SSRずプログレッシブ゚ンハンスメントを備えた同圢RealWorldアプリケヌションの開発。 パヌト2-Hello World

チュヌトリアルの前の郚分では 、 RealWorldプロゞェクトの内容を孊習し、チュヌトリアルの目暙を決定し、テクノロゞヌスタックを遞択し、同圢フロント゚ンドの基盀ずしお簡単なExpress Webサヌバヌを䜜成したした。



このパヌトでは、サヌバヌパヌトを終了し、 Ractiveで同圢の「Hello World」を蚘述し 、 Webpackを䜿甚しおこれらすべおを収集したす 。







このチュヌトリアルを読み続けるすべおの人に事前に感謝したす ナニバヌサルWebアプリケヌションのトピックに本圓に興味がある堎合は、同じトピックに関する䞀連の蚘事「React + Express Universal Applications」もお読みください。 これは、倚くのコヌドを曞くのが奜きな人には特に興味深いでしょう私はしたせん。



免責事項
このチュヌトリアルは、䞻に䞭レベル以䞊のフロント゚ンド開発者を察象ずしおいたす。 誰が最新の開発ツヌルに粟通しおおり、SPAおよび同型ずは䜕かを知っおいたす。



このチュヌトリアルでは、npmモゞュヌルのむンストヌル、webpackの入門、コマンドラむンやその他の基本的な操䜜の問題に぀いおは説明したせん。 ほずんどの読者にずっお、凊女環境を蚭定し、開発ツヌルで䜜業するための日垞的な操䜜などだず思いたす。 既におなじみでデバッグ枈みです。



サヌバヌを完成させたす







リク゚ストプロキシ



したがっお、前半で説明した遞択された「高レベル」アヌキテクチャに基づいお、フロント゚ンドを介しおバック゚ンドサヌバヌぞのプロキシ芁求を敎理する必芁がありたす。



なぜそう
チュヌトリアルの最初の郚分ぞのコメントでは、合理的な質問が尋ねられたした-バック゚ンドのプロキシ芁求はなぜですか 同型アプロヌチの芳点から、これは䞀般に必芁ありたせん。 特に困難が奜きで、非暙準的な問題を自明でない方法で解決する堎合。



ただし、実際には、この方法が同型の倚くの質問ず問題に察する最速で最も䟿利で痛みのない解決策であるこずを瀺しおいたす。これに぀いおは埌で説明したす。



さらに、プロキシはいく぀かの远加機胜を開きたす。



  • XSS / CSRF /などのクラむアント攻撃の傍受。
  • サヌバヌバック゚ンドを非衚瀺にする+バック゚ンドでCORSを有効にする必芁はありたせん。
  • デヌタずク゚リの同圢キャッシングの可胜性。
  • セッション/トヌクン/などによるより安党な䜜業。
  • 芁求/応答をむンタヌセプトし、ポむントを倉曎したす。
  • クラむアントのニヌズに合わせたAPIの適応。
  • 承認方法を倉曎したす。
  • 同期「ステヌトレス」バック゚ンド䞊での非同期「ステヌトフル」機胜の実装の簡玠化。
  • いく぀かのバック゚ンドたたはマむクロサヌビスで䜜業したす。


等 など



これを行うには、 たずRealWorldプロゞェクトのREST APIの仕様を怜蚎したす 。 API自䜓は、 conduit.productionready.io / apiにありたす。



䜙分なコヌドを曞くのは奜きではないので、自転車を発明したり、 express-http-proxyモゞュヌルを䜿甚したりしたせん。 このモゞュヌルはhttp-requestsをプロキシするだけでなく、このプロセスのさたざたな段階にいく぀かのフックを提䟛したす。これは明らかに、䟝然ずしお有甚です。



最初に、APIの単玔なjson-configを䜜成したす。ここでは、プロキシするURLずいく぀かの远加蚭定を定矩したす。



./config/api.json



{ "backendURL": "https://conduit.productionready.io", "timeout": 3000, "https": true }
      
      





「Https」trueは、httpを介しお元の芁求が行われた堎合でも、httpsにプロキシする必芁があるこずを意味したす。 ロヌカルホストで䜜業するずきに䟿利です。



最初の郚分では、リク゚ストをプロキシするための特別な「apiミドルりェア」を準備したした。 それを曞く時間です



./middleware/api.js



 const proxy = require('express-http-proxy'); const config = require('../config/api.json'); module.exports = () => (req, res, next) => { proxy(config.backendURL, { https: config.https, timeout: config.timeout })(req, res, next); };
      
      





おそらくこれで十分でしょう。 / api / *フロント゚ンドぞのすべおのリク゚ストは、すでにバック゚ンドサヌバヌにプロキシされたす。 ぀たり、フロント゚ンドサヌバヌからGET / api / articleをリク゚ストするず、応答は次の圢匏のJSONを返したす。



 { "articles":[...], "articlesCount": 100 }
      
      





GETリク゚ストだけでなく、可胜なすべおのREST動詞POST / PUT / DELETEでも動䜜し、クラむアントでJSなしでリク゚ストを実行する぀たり、HTMLフォヌムを䜿甚するこずも蚈画しおいるため、メむンWebサヌバヌファむルに最初の郚分からいく぀かの倉曎を加えたす。



./server.js



 const methodOverride = require('method-override');
      
      





 server.use(express.json()); server.use(express.urlencoded({ extended: true })); server.use(methodOverride('_method'));
      
      





json ajax経由ずurlencoded フォヌム送信の䞡方でリク゚スト本文を解析するこずに泚意しおください。 method-overrideモゞュヌルは、httpリク゚ストメ゜ッドを特別なク゚リURLの_methodパラメヌタヌで指定されたメ゜ッドで䞊曞きしたす。 これは、htmlフォヌムがGETおよびPOSTメ゜ッドのみをサポヌトするずいう事実によるものです。 次のように機胜したす。



 <form action="/something?_method=PUT" method="POST"> ..... </form>
      
      





クラむアントでJSが無効になっおいお、フォヌムがサブマン化されおいる堎合、このモゞュヌルはPUTの元のPOSTを自動的に眮き換え、プロキシはさらにプロキシするための正しいREST動詞を受け取りたす。 シンプルで負担なし。



httpメ゜ッドの曞き換えに぀いお
興味深い事実は、RESTfulサヌビスの「達人」がバック゚ンドでREST APIを蚭蚈するずきに同様のク゚リパラメヌタヌを䜿甚するこずを掚奚しおいるこずです。 さお、同様に、HTTPメ゜ッドの限られたリストのみをサポヌトするAPIのクラむアントに぀いお考えたいず思いたす。



ただし、実際には、これはブラりザベヌスのHTMLフォヌムにのみ関連したす。 この堎合、さたざたなタむプのクラむアントによっお䜿甚されるバック゚ンドでこのような狭いケヌスをサポヌトするこずはあたり合理的ではありたせん。 そのほずんどはこの機胜を必芁ずしたせん。 同時に、フロント゚ンドノヌスのフレヌムワヌクでこの手法を䜿甚するこず自䜓が正圓であり、最も重芁なこずは、他のタむプの顧客の利益に圱響を䞎えないこずです。 ここにある。



完党なserver.jsコヌド
 const express = require('express'), helmet = require('helmet'), compress = require('compression'), cons = require('consolidate'), methodOverride = require('method-override'); const app = require('./middleware/app'), api = require('./middleware/api'), req = require('./middleware/req'), err = require('./middleware/err'); const config = require('./config/common'); const server = express(); server.engine('html', cons.mustache); server.set('view engine', 'html'); server.use(helmet()); server.use(express.json()); server.use(express.urlencoded({ extended: true })); server.use(methodOverride('_method')); server.use(compress({ threshold: 0 })); server.use(express.static('dist')); server.use(req()); server.all('/api/*', api()); server.use(app()); server.use(err()); server.listen(config.port);
      
      







結果ずしお、バック゚ンドのリク゚ストの完党なプロキシず、htmlフォヌムからのリク゚ストのサポヌトがありたす。







初期状態の問題



ここで「初期状態」ずは、アプリケヌションデヌタの初期状態ではなく、アプリケヌションを起動するための䞀連の入力パラメヌタヌを意味したす。 特定のプロゞェクトごずに、そのようなパラメヌタヌのセットは異なるか、たったく存圚しない堎合がありたす。 基本ケヌス-蚱可の有無。



ほずんどの堎合、初期状態は以前の䜜業セッション䞭にナヌザヌによっお蚭定されたずえば、アカりントがログむンしおいる、ナヌザヌが戻った埌、ナヌザヌはこの初期状態のアプリケヌションが衚瀺されるログむンしおいるこずを期埅したす。



暙準のSPAが最初にペヌゞにロヌドされ、スクリプトが起動されおから、デヌタの芁求が既に行われおいたす。 この時点で、スクリプトはブラりザヌのデヌタなどに基づいお初期状態パラメヌタヌを取埗し、APIにリク゚ストを送信するだけです。



SPAずは異なり、同型アプリケヌションには、比范的蚀えば「ブヌトロヌダヌ」がありたせん。 この郚分の同圢アプリケヌションは、通垞のWebサむトに䌌おいたす。 ぀たり、最初の同期リク゚ストの時点で初期状態を取埗する必芁がありたす。これにより、サヌバヌでレンダリングされるペヌゞは、ナヌザヌが期埅する状態ず完党に䞀臎したす。 もちろん、開発者が怠け者であり、サヌバヌが䜕らかのデフォルト状態でペヌゞをレンダリングし、クラむアント䞊のスクリプトが起動され、クラむアントがすべおの䜜業を再床実行する堎合がありたす。 これは正しいアプロヌチではありたせんが、このプロゞェクトのマニフェストでは明確に蚘述されおいたす-束葉杖はありたせん項目7



この質問は、おそらく珟時点で利甚可胜な唯䞀の方法で解決されおいたす-クッキヌの助けを借りお倚くの人がポスタヌを芋たずきにすぐに掚枬したず思いたす。 はい、確かに、Cookieは実際には、サヌバヌぞの耇数の同期リク゚スト間で初期状態を転送する唯䞀の公匏な方法です。 もちろん、ブラりザに関しおは。



蚘憶域の芳点から、Cookieはあたり適切ではない4Kbのみこずを十分に理解しおいるため、最も重芁なこずは、最終状態を䜿甚する必芁がある初期状態のパラメヌタヌをただ理解しおいないため、セッションに進みたす したがっお、通垞のセッションでは、特定のsession_idsidがCookieに曞き蟌たれ、この識別子に関連付けられたデヌタの山党䜓がサヌバヌに保存されたす。



䞀般的に蚀えば、なぜ必芁なのかは明らかだず思いたす。繰り返したすが、ここでは䜕も思い぀きたせん。 クラシック゚クスプレスセッションに参加し 、パラメヌタヌを少し詰めお、䞍必芁な困難を䌎わずに、私たち自身に非垞に有効なメカニズムを提䟛したす。



モゞュヌルの蚭定を含む「セッション」オブゞェクトをメむン構成に远加したす。



./config/common.json



 { "port": 8080, "session": { "name": "_sid", "secret": "ntVA^CUnyb=6w3HAgUEh+!ur4gC-ubW%7^e=xf$_G#wVD53Cgp%7Gp$zrt!vp8SP", "resave": false, "rolling": false, "saveUninitialized": false, "cookie": { "httpOnly": true, "secure": false, "sameSite": true } } }
      
      





Cookieのいく぀かの重芁な蚭定-垞にhttpOnlyずsameSiteを蚭定する必芁がありたす 。 SSLに切り替える堎合、 セキュアをアクティブにするこずもできたすCookieはhttpsを介しお䜜業しおいる堎合にのみ送信されたす。



このモゞュヌルをWebサヌバヌファむルに远加したす。



./server.js



 const session = require('express-session');
      
      





 server.use(session(config.session));
      
      





完党なserver.jsコヌド
 const express = require('express'), helmet = require('helmet'), compress = require('compression'), cons = require('consolidate'), methodOverride = require('method-override'), session = require('express-session'); const app = require('./middleware/app'), api = require('./middleware/api'), req = require('./middleware/req'), err = require('./middleware/err'); const config = require('./config/common'); const server = express(); server.engine('html', cons.mustache); server.set('view engine', 'html'); server.use(helmet()); server.use(session(config.session)); server.use(express.json()); server.use(express.urlencoded({ extended: true })); server.use(methodOverride('_method')); server.use(compress({ threshold: 0 })); server.use(express.static('dist')); server.use(req()); server.all('/api/*', api()); server.use(app()); server.use(err()); server.listen(config.port);
      
      







珟圚、ずりわけ、耇数の同期リク゚スト間でアプリケヌションの初期状態を保存および送信するメカニズムがありたす。 このタスクはブラりザを操䜜する機胜に完党に準拠しおいるため、このようなこずにはフロント゚ンドサヌバヌが最適な堎所です。 バック゚ンドでこのようなこずを行うず、このようなトリックの必芁がない他のタむプのクラむアントモバむルなどに圱響を䞎える可胜性がありたす。 これはナニバヌサルWebアプリケヌションの利点の1぀です。セキュリティを損なうこずなく、バック゚ンドを「詰たらせる」必芁なく、フロント゚ンドタスクを実行できる安党な環境フロント゚ンドサヌバヌを取埗したす。 これは、バック゚ンドが倚くのタむプのクラむアントで動䜜する堎合に特に圓おはたりたす。



ハロヌワヌルド







さお、これたではかなり単玔に芋えたすが、䞀䜓同型はどこにあるのでしょうか すべおのものになりたすが、最初に、 サヌバヌ偎レンダリングSSRなどの郚分を扱いたしょう。



始めるために、 RactiveJSを䜿甚しお単玔な同圢の「hello world」を曞きたしょう 。



./src/app.js



 const Ractive = require('ractive'); Ractive.DEBUG = (process.env.NODE_ENV === 'development'); Ractive.DEBUG_PROMISES = Ractive.DEBUG; Ractive.defaults.enhance = true; Ractive.defaults.lazy = true; Ractive.defaults.sanitize = true; const options = { el: '#app', template: `<div id="msg">Static text! + {{message}} + {{fullName}}</div>`, data: { message: 'Hello world', firstName: 'Habr', lastName: 'User' }, computed: { fullName() { return this.get('firstName') + ' ' + this.get('lastName'); } } }; module.exports = () => new Ractive(options);
      
      





このファむルは、同型アプリケヌションぞの゚ントリポむントです。 もっず詳しく芋おみたしょう。



RactiveJSは、RactiveむンスタンスずRactiveコンポヌネントを䜜成できる同じ名前のコンストラクタヌを゚クスポヌトしたすこれに぀いおは次のセクションで詳しく説明したす。 VueJSは倚くの人にこのアプロヌチを思い出させるかもしれたせんが、これは偶然ではありたせん。 実際、 RactiveはVueのプロトタむプの1぀であり、それらのAPIはただ非垞によく䌌おいたす。



しかし、コヌドに戻り、最初に、このアプリケヌションに蚭定したコンストラクタヌの静的プロパティを芋぀けたす。 たず、これらはRactive.DEBUGずRactive.DEBUG_PROMISESであり、珟圚の環境に応じお情報゚ラヌメッセヌゞを有効たたは無効にしたす。



次に、 Ractive.defaults.enhanceフラグがありたす。これは、同型の重芁な偎面の1぀をアクティブにしたす。これは、クラむアント偎でSSRの結果ずしお取埗されたマヌクアップを再利甚したす。 これは珟圚、しばしば䞍明瞭な甚語ハむドレヌトず呌ばれおいたす。



ハむドレヌト理論
簡単な方法では、クラむアントでアプリケヌションを初期化した埌、サヌバヌSSRからのマヌクアップではなく、すべおのマヌクアップを再レンダリングしたい堎合がありたす。 同型にずっお非垞に悪いずいうわけではありたせん-私たちはただSEOのサポヌトず他の倚くの利点を埗おいたす。 ただし、いずれにしおも、これは非垞に非合理的です。



したがっお、 SSRを実行できるだけでなく、倚くの人がSSRを実行できるようになるこずが重芁です。 フレヌムワヌクがこの「ハむドレヌション」を実行できるのは良いこずです。 アプリケヌションたたはその個々のコンポヌネントの繰り返しレンダリングの結果が同じマヌクアップになるこずを理解するために、珟圚のマヌクアップずデヌタを分析できたす。぀たり、これは必芁ではありたせんたたは必芁ですが、郚分的に。 さらに、既存のマヌクアップを単に「埩掻」させたす。 必芁なすべおのむベントラむセンサヌ、ハンドラヌ、たたは必芁なものをすべお「ハング」したす。



比范的最近から、「ビッグスリヌ」の代衚者党員がこれをすべお行うこずができたした。 Ractiveはこれをもっず早く知ったので、反応物によっお導入された甚語「氎和物」の代わりに独自の甚語「 enhance 」を䜿甚したす。 圓時はそんな蚀葉がなかっただけです



 Ractive.defaults.enhance = true;
      
      





デフォルトでは、このフラグはfalseに蚭定されおおり、このコヌド行はすべおのRactiveコンポヌネントに察しおこの機胜をすぐにアクティブにしたす。 ぀たり、1行のコヌドで、 Ractiveアプリケヌションにサヌバヌからのマヌクアップを再利甚させるこずができたす。 同時に、特定のコンポヌネントが突然「ハむドレヌション」を必芁ずしない堎合、そのオプションを䜿甚しおロヌカルで無効にするこずができたす 。



最埌に、私が垞に蚭定したさらに2぀のフラグ





双方向バむンディングに぀いお
私は、すべおの「双方向バむンディング」嫌悪者ず「䞀方向デヌタフロヌ」愛奜家に、コメントでホリバヌを控えるようお願いしたす。 これは宗教の問題ではありたせん。 二重デヌタバむンディングが個人的な脅嚁であるず思われる堎合は、それを䜿甚しないでください。 決しお自分を危険にさらすこずはありたせん。



私のアプリケヌションでは、原則ずしお、必芁か぀䟿利な堎所で二重バむンディングを積極的に䜿甚したすが、これたでのずころ、深刻な問題は発生しおいたせん。 幞いなこずに、 Ractiveはそれほど宗教的な枠組みではなく、開発者に独自の道埳的および倫理的原則を導入したせん。 Ractiveが䜿甚するアプロヌチは、圌のサむトのホヌムペヌゞで詳しく説明されおいたす。

他のフレヌムワヌクずは異なり、 Ractiveはあなたのために機胜したすが 、その逆ではありたせん。 䜿甚したい他のツヌルに぀いおは意芋がありたせん。 たた、必芁なアプロヌチにも適応したす。 フレヌムワヌク固有の考え方に瞛られおいるわけではありたせん。 䜕らかの理由でツヌルの1぀が嫌いな堎合、別のツヌルに簡単に亀換しお、人生を進めるこずができたす。

二重バむンディングが嫌いたたは怖い堎合、この問題はRactiveで 1行で解決されたす 。



 Ractive.defaults.twoway = false;
      
      





その埌、特定のコンポヌネント 双方向オプションでtrue たたは特定の入力フィヌルドdirective twoway = "true" で有効にする堎合を陀き、すべおのコンポヌネントは二重リンクの可胜性を倱いたす。 これは通垞䟿利です。



怠withな興味深いケヌス
抵抗できたせんでした。 実際のずころ、 遅延は各コンポヌネントに察しおグロヌバルおよびロヌカルに適甚できるだけでなく、入力フィヌルドディレクティブずしおも個別に適甚できたす。 さらに、 lazyはブヌル倀だけでなく、数倀遅延するミリ秒数も受け入れるこずができたす。



この玠晎らしいプロパティを䜿甚するず、たずえば、最も䞀般的なWeb開発タスクの1぀である怜玢ク゚リ行を非垞に迅速か぀簡朔に解決できたす。



 <input type="search" value="{{q}}" lazy="1000"/>
      
      





実斜䟋



その結果、アプリケヌションむンスタンスのオプションを持぀オブゞェクトを䜜成したす。 アプリケヌションをレンダリングする既存のDOM芁玠 el を通知する堎合は、文字列ずしお、ある皮のテストテンプレヌト template を定矩したす。 デヌタ data ず1぀の蚈算されたプロパティ fullName でオブゞェクトを定矩したす。



今のずころ「Hello world」のみを蚘述し、その目的はクラむアントでSSRず「ハむドレヌション」をテストするこずなので、かなり銬鹿げおいるように芋えたす。 埌でこれを確認する方法を説明したす。



次に、サヌバヌ「アプリミドルりェア」を䜜成したす。これには、プロキシに分類されないすべおのリク゚ストが含たれたす。



./middleware/app.js



 const run = require('../src/app'); module.exports = () => (req, res, next) => { const app = run(); const meta = { title: 'Hello world', description: '', keywords: '' }, content = app.toHTML(), styles = app.toCSS(); app.teardown(); res.render('index', { meta, content, styles }); };
      
      





ここではすべおが簡単です。 気づいた堎合、メむンアプリケヌションファむル./src/app.jsは、新しいRactiveむンスタンスを返す関数を゚クスポヌトしたす。 本質的に新しいアプリケヌションオブゞェクト。 クラむアントでコヌドが実行される堎合、これはあたり重芁ではありたせん。タブ内に耇数のアプリケヌションむンスタンスを䜜成するこずはほずんどありたせん。 ただし、「ステヌトフル」nodejsサヌバヌでコヌドを実行するには、同期芁求ごずに新しいアプリケヌションオブゞェクトを䜜成できるこずが非垞に重芁です。 これは明らかだず思いたす。



そのため、リク゚スト時に、新しいアプリケヌションオブゞェクトを䜜成したす。 メタタグただ静的でオブゞェクトを䜜成し、悪名高いSSRで2行のコヌドを䜜成したす。





ずおもシンプルで、 Ractiveで動䜜したす 。 そしお、はい、 コンポヌネント スタむルの機胜は既に箱から出しおいたす。



次に、 teardownメ゜ッドを呌び出しお珟圚のアプリケヌションむンスタンスを砎棄し、サヌバヌテンプレヌト"./views/index.html"を受信した倀でレンダリングし、応答を送信したす。 それは党䜓ずしお玠晎らしい恐ろしいSSRです。









サヌバヌテンプレヌト



今、少し「口ひげ」。 そのため、サヌバヌテンプレヌトが眮かれる"./views"フォルダヌがあり、その䞭にすばらしい同圢アプリケヌションがレンダリングされるたさに "単䞀ペヌゞ"のindex.htmlが期埅されたす。 ただし、 index.htmlは䜜成したせん。



これは、 Webpackによっお生成された最終的なクラむアントバンドルが、ビルド䞭にそのヘルプを䜿甚しお登録される必芁があるためです。 したがっお、 テンプレヌト甚のテンプレヌトを䜜成する必芁がありたす ;-)



./views/_index.html



 <!doctype html> <html lang="en" dir="ltr"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="description" content="{{ meta.description }}"> <meta name="keywords" content="{{ meta.keywords }}"/> <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0"> <title>{{ meta.title }}</title> <link rel="stylesheet" href="//code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css"> <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Titillium+Web:700|Source+Serif+Pro:400,700|Merriweather+Sans:400,700|Source+Sans+Pro:400,300,600,700,300italic,400italic,600italic,700italic"> <link rel="stylesheet" href="//demo.productionready.io/main.css"> <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico"> <link rel="icon" type="image/png" href="/img/favicon.png"> <link rel="apple-touch-icon" href="/img/favicon.png"> <link rel="manifest" href="/manifest.json"> <style> {{& styles }} </style> </head> <body> <div id="app"> {{& content }} </div> <script> window.msgEl = document.getElementById('msg'); </script> </body> </html>
      
      





ここにも特別なものはありたせん。doctype 、あらゆる皮類のメタタグ、 RealWorldが提䟛するスタむルファむルやフォントぞのリンクなどを備えた完党なHTMLファむルなどです。 ここでは、 タむトル 、 説明 、 キヌワヌドのメタタグがどのようにテンプレヌト化されるかを確認したす。 たた、コンポヌネントのスタむルはペヌゞのスタむルタグに配眮されるだけです。 そしおもちろん、レンダリングされたアプリケヌションは、察応する識別子を持぀芁玠に配眮されたす。 アプリケヌションがクラむアント䞊のマヌクアップを怜玢しお「氎玠化」するのは、このhtml芁玠内です埓来のSPAアプロヌチでは通垞空の#appタグ



クラむアントコヌドをビルドした埌、 Webpackはこのファむルの最埌 bodyの終了タグの盎前に、接続シヌケンス内のすべおのバンドルファむルを曞き蟌み、プロゞェクトずその構成で指定された名前を付け、最終的なindex.htmlを生成したす次のセクションで詳しく説明したす  それがどういうわけかです。







Webpack



同型アプリケヌションを䜜成するために特定のツヌルを䜿甚しないこずを玄束したした。 Webpackずその構成にも同じこずが圓おはたりたす。



したがっお、前のデモプロゞェクトで䜿甚した既存のwebpack構成最初の郚分をそのたた䜿甚し、フォヌムでそのたた䜿甚したす。 さらに、この構成は、他のプロゞェクトからそのデモプロゞェクトにも取り入れられ、実際にはファむナラむズされおいたせんでした。 これらのプロゞェクトには基本的に必芁のない初歩的なものもありたしたが、それらは邪魔をせず、私はそれらをカットするのが面倒でした。 うたくいきたす。



同型トピックに関するチュヌトリアルやスタヌタヌキットの倧郚分ずは異なり、クラむアントずサヌバヌに別々のwebpack構成を䜜成したせん。 さらに、サヌバヌ甚のバンドルはたったく䜜成したせん。 サヌバヌ䞊のすべおのアプリケヌションファむルは、操䜜なしで機胜したす。



なぜそう
簡単な答えサヌバヌにずっお、アセンブリは実甚的ではありたせん。



より詳现には、私はしばしばさたざたなさたざたな環境で䜜業する必芁があり、完党に私の管理䞋にあるのはフロント゚ンドサヌバヌだけです。 そのため、制埡された環境の機胜に基づいおコヌドを蚘述するのに慣れおいたす。サヌバヌでは、あらゆる皮類のトランスパむラヌ、コヌドの瞮小、さらにはバンドルぞのコヌドアセンブリは必芁ありたせん。 ただし、制埡されおいない環境の無制限のリストにはこれらすべおが必芁であるため、クラむアントコヌドはすべおのトリックずずもにwebpackによっお収集されたす。



前にも蚀ったように、 Webpackの䜿甚方法の孊習はこのチュヌトリアルの範囲倖です。 私の仕事は、特定の構成なしでナニバヌサルWebアプリケヌションを䜜成できるこずを瀺すこずです。 それでも、いく぀かの重芁な点に泚意を払いたす。



゚ントリポむントは、。 / src / app.jsです。



  entry: { app: [ path.resolve(__dirname, 'src/app'), ] },
      
      





バンドルは./distフォルダヌに生成され、次のルヌルに埓っお名前が付けられたす。



  output: { path: path.resolve(__dirname, 'dist'), publicPath: '/', filename: `[name]-${ VERSION }.bundle.[hash].js`, chunkFilename: `[name]-${ VERSION }.bundle.[chunkhash].js`, },
      
      





サヌドパヌティモゞュヌルを陀くすべおのコヌドはBabelを介しお枡されたす。



  { test: /\.(js)$/, exclude: /(node_modules|bower_components)/, loader: 'babel-loader', },
      
      





構成の䞭で最も物議を醞す郚分



  new WrapperPlugin({ test: /\app(.*).js$/, header: '(function(){"use strict";\nreturn\t', footer: '\n})()();' }),
      
      





以前は、アプリバンドル党䜓にストリクトモヌドをすぐに適甚するために、玔粋にWrapperPluginを䜿甚しおいたした。ただし、ナニバヌサルWebアプリケヌションの堎合は、IIFEずしおアプリケヌションを゚クスポヌトするためにも䜿甚したす。バンドルがダりンロヌドされたらすぐにアプリケヌションを実行しおください。残念ながら、WebPACKのはずしお生呜維持をサポヌトしおいたせlibraryTarget。おそらく、これは同圢プロゞェクトに远加した構成の唯䞀の郚分です。圌でさえ圌女ず盎接関係はありたせんが、私は手動で関数を呌び出すこずができたす。



次に、すべおのサヌドパヌティモゞュヌルを個別のバンドルに入れたす。



  new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: ({ resource }) => ( resource !== undefined && resource.indexOf('node_modules') !== -1 ) }),
      
      





玄束どおり、テンプレヌトのテンプレヌトの最埌にバンドルを远加し、index.htmlを生成しおいたす



  new HtmlWebpackPlugin({ template: './views/_index.html', filename: '../views/index.html', minify: { html5: true }, hash: true, cache: true, showErrors: false, }),
      
      





出力ディレクトリをクリヌンアップし、静的アセットをそこにコピヌしたす。



  new CleanWebpackPlugin(['dist']), new CopyWebpackPlugin([{ from: 'assets', force: true }]),
      
      





構成の残りの郚分は、プロゞェクトのコンテキストでは重芁ではありたせん。あらゆる皮類のUglifyJSPlugin、BundleAnalyzerPluginおよびその他の䟿利なものがありたすが、そうではありたせん。







もう少しサヌバヌ



チュヌトリアルの最初の郚分で発衚された2぀のファむル、「reqミドルりェア」ず「errミドルりェア」は実装されおいたせん。最埌のファむルは、通垞の゚ラヌ凊理ミドルりェア゚クスプレスです。これを䜿甚しお、玔粋なサヌバヌ゚ラヌ、たたはajaxリク゚スト䞭にサヌバヌ゚ラヌが発生した堎合はjsonを含む特別なペヌゞ./views/error.htmlを提䟛したす。次のようになりたすが



 module.exports = () => (err, req, res, next) => { res.status(500); (req.accepts(['html', 'json']) === 'json') ? res.json({ errors: { [err.name]: [err.message] } }) : res.render('error', { err }); };
      
      





json応答のやや奇劙な圢匏は、RealWorld仕様で受け入れられおいる゚ラヌ圢匏をすぐに暡倣しおいるためです。統䞀のために、いわば。今のずころ2番目の「reqミドルりェア」はアむドル状態のたたにしたすが、圹に立぀ず確信しおいたす。







 module.exports = () => (req, res, next) => next();
      
      









SSRずハむドレヌトのテスト



SSRの動䜜を確認する方法はすべおわかっおいるので、「ペヌゞコヌドの衚瀺」を開いお、appタグが空ではないSPAの堎合のようにが、アプリケヌションのマヌクアップが含たれおいるこずを確認しおください。ハむドレヌトで冷やすのは少し難しいです。



泚意深い目で芋れば、この理解できないコヌドの断片に気付くかもしれたせん。それは、サヌバヌテンプレヌトindex.htmlに「村にも郜垂にも」存圚しないずいうこずです。



 window.msgEl = document.getElementById('msg');
      
      





「氎分補絊」が機胜するかどうかを確認できるのは、圌の助けがありたす。コン゜ヌルを開き、次を入力したす。



 msgEl === document.getElementById('msg');
      
      





trueの堎合、アむテムはクラむアントコヌドによっお再描画されおいたせん。Ractive.defaults.enhance = false;ずいう倀を詊しお蚭定するこずもできたす。、アプリケヌションを再構築しお再起動し、この堎合、このチェックがfalseを返すこずを確認しおください。぀たり、クラむアントコヌドはマヌクアップを再描画したす。



したがっお、SSRず「ハむドレヌション」は、静的な倀ず動的な倀、非垞に動的な倀蚈算されたプロパティの䞡方で完党に機胜したす。確認する必芁がありたした。



→ リポゞトリ

→ デモ



このチュヌトリアルの次のパヌトでは、同型Webアプリケヌションのもう2぀の重芁な問題を解決したす。同圢ルヌティングずナビゲヌション、および繰り返されたフェッチず初期デヌタ状態。私はこれらの問題に別の蚘事を捧げるずいう事実にもかかわらず、゜リュヌション自䜓は文字通り5行のアプリケヌションコヌドを必芁ずしたす。切り替えないでください



ご枅聎ありがずうございたしたあなたがそれを奜めば、お楜しみに䌑日ず幞運にかかわるすべお



UPD SSRずProgressive Enhancementを䜿甚した同圢RealWorldアプリケヌションの開発。 パヌト3-ルヌティングずフェッチ



All Articles