webpack-dev-server コアコンセプトWebpack の ContentBase と publicPath と output.path の比較webpack-dev-serverは現在のパスを要求されたリソースパスとして使用します(いわゆる 現在のパス これは、webpack-dev-server コマンドを実行するためのパスです。webpack-dev-server が wcf などのようにパッケージ化されている場合、現在のパスは wcf コマンドを実行するためのパス (通常はプロジェクトのルート パス) を参照しますが、読者は content-base を指定してこのデフォルトの動作を変更できます。 webpack-dev-server --content-base ビルド/ このように、webpack-dev-server はビルド ディレクトリ内のリソースを使用して、css/pictures などの静的リソースの要求を処理します。通常、content-base は publicPath または output.path と混同しないでください。 content-base は、次の例のように、静的リソースのパスを示します。 <!DOCTYPE html> <html> <ヘッド> <タイトル></タイトル> <link rel="スタイルシート" type="text/css" href="index.css" rel="外部 nofollow" > </head> <本文> <div id="react-content">ここに js コンテンツを挿入します</div> </本文> </html> html-webpack-plugin のテンプレートとして使用した後、上記の index.css のパスは何ですか?誰に対してですか?上記で強調したように、content-base が指定されていない場合は、現在のパスを基準とします。いわゆる現在のパスは、webpack-dev-server が実行されているディレクトリです。したがって、このコマンドをプロジェクト ルート パスで実行する場合は、index.css リソースがプロジェクト ルート パスに存在することを確認する必要があります。そうでない場合、html-webpack-plugin で 404 エラーが発生します。もちろん、この問題を解決するには、content-base を html-webpack-plugin の html テンプレートと同じディレクトリに変更します。 前述のように、content-base は静的リソースのリクエストにのみ関連するため、publicPath と output.path を区別しましょう。 モジュール.エクスポート = { エントリー: { アプリ: ["./app/main.js"] }, 出力: { パス: path.resolve(__dirname, "build"), パブリックパス: "/assets/", //この時点で、/assets/ パスはビルド ディレクトリに対応しており、これはマッピング関係のファイル名: "bundle.js" です。 } } その後、localhost:8080/assets/bundle.js を通じてコンパイルされたリソースにアクセスできるようになります。ビルド ディレクトリに html ファイルがある場合は、次の方法を使用して js リソースにアクセスできます。 <!DOCTYPE html> <html lang="ja"> <ヘッド> <メタ文字セット="UTF-8"> <title>ドキュメント</title> </head> <本文> <script src="assets/bundle.js"></script> </本文> </html> この時点で、コンソールに次の出力が表示されます。 ここに画像の説明を入力してください 次の 2 つの出力に焦点を当てます。
この出力の理由は、実行されるコマンドが webpack-dev-server ホットリロード (HMR) webpack-dev-server の HMR モードを有効にするには、コマンド ラインに --hot を追加するだけです。これにより、HotModuleReplacementPlugin プラグインが webpack 構成に追加されます。したがって、HotModuleReplacementPlugin を有効にする最も簡単な方法は、インライン モードを使用することです。インライン モードでは、コマンド ラインに --inline --hot を追加するだけで自動的に実行されます。 関数 reloadApp() { if(ホット) { log("info", "[WDS] アプリのホットアップデート..."); window.postMessage("webpackHotUpdate" + currentHash, "*"); } それ以外 { log("info", "[WDS] アプリが更新されました。再読み込み中..."); ウィンドウの位置を再読み込みします。 } } webpack/hot/dev-server のログはすべて [HMR] で始まります (これは Webpack 自体のプラグインです)。 if(!更新されたモジュール) { console.warn("[HMR] 更新が見つかりません。完全にリロードする必要があります!"); console.warn("[HMR] (おそらくwebpack-dev-serverを再起動したため)"); ウィンドウの位置を再読み込みします。 戻る; } では、Node.js で HMR 関数を使用するにはどうすればよいでしょうか?この時点で、次の 3 つの構成ファイルを変更する必要があります。 1. Webpackエントリポイント(webpack/hot/dev-server)を追加します。 オプションのインラインの場合 var devClient = [require.resolve("../client/") + "?" + protocol + "://" + (options.public || (options.host + ":" + options.port))]; // 自動更新を実現するために、webpack-dev-server のクライアント エントリをバンドルに追加します if(options.hot) devClient.push("webpack/hot/dev-server"); //webpack-dev-server でのホット構成の処理は次のとおりです [].concat(wpOpt).forEach(function(wpOpt) { if(typeof wpOpt.entry === "object" && !Array.isArray(wpOpt.entry)) { Object.keys(wpOpt.entry).forEach(function(key) { wpOpt.entry[キー] = devClient.concat(wpOpt.entry[キー]); }); } それ以外 { wpOpt.entry = devClient.concat(wpOpt.entry); } }); } 上記3つの条件を満たすNode.jsの使い方は以下のとおりです。 var config = require("./webpack.config.js"); config.entry.app.unshift("webpack-dev-server/client?http://localhost:8080/", "webpack/hot/dev-server"); //条件1(webpack-dev-serverのクライアントとHMRのサーバーを追加) var コンパイラ = webpack(config); var server = 新しい webpackDevServer(コンパイラ、{ hot: true //条件 2 (--hot 構成、webpack-dev-server は HotModuleReplacementPlugin を自動的に追加します) ... }); サーバーを listen (8080); webpack-dev-server はプロキシを起動しますwebpack-dev-server の使用法 http プロキシ ミドルウェア リクエストを外部サーバーにプロキシする場合の構成例は次のとおりです。 プロキシ: { '/api': { ターゲット: 'https://other-server.example.com', 安全: 偽 } } // webpack.config.js 内 { 開発サーバー: { プロキシ: { '/api': { ターゲット: 'https://other-server.example.com', 安全: 偽 } } } } // 複数エントリ プロキシ: [ { コンテキスト: ['/api-v1/**', '/api-v2/**'], ターゲット: 'https://other-server.example.com', 安全: 偽 } ] この種のプロキシは多くの場合非常に重要です。たとえば、一部の静的ファイルはローカル サーバーを介して読み込むことができますが、一部の API リクエストはすべてリモート サーバーを介して完了します。もう 1 つのシナリオは、認証を担当するサーバーとアプリケーション自体を担当するサーバーなど、2 つの別々のサーバー間でリクエストを分割することです。以下は日常の開発で遭遇する例です。 (1)リクエストは「/msg/show.htm」のような相対パスで行われる。ただし、日常使用の場合は you.test.com、実稼働環境の場合は you.inc.com など、日常使用環境と実稼働環境の前に異なるドメイン名が追加されます。 (2)例えば、webpack-dev-serverをローカルで起動し、webpack-dev-serverを介してdaily serverにアクセスする場合、daily serverのアドレスが11.160.119.131であれば、次の設定で完了できます。 開発サーバー: { ポート: 8000、 プロキシ: { "/msg/show.htm": { ターゲット: "http://11.160.119.131/", 安全: 偽 } } } このとき、「/msg/show.htm」が要求された場合、実際に要求される URL アドレスは「http//11.160.119.131/msg/show.htm」になります。 (3) 開発環境で問題が発生しました。ローカル devServer が「http://30.11.160.255:8000/」またはより一般的な「http://0.0.0.0:8000/」で起動された場合、実際のサーバーはログインを要求する URL を返します。ただし、localhost でローカル devServer を起動すると、この問題は発生しません (考えられる理由としては、localhost にはバックエンドに必要な Cookie があるのに対し、他のドメイン名には Cookie がないため、プロキシ サーバーが通常のサーバーにアクセスするときに対応する Cookie を持たず、権限の確認が必要になることが挙げられます)。ローカルホストを指定する方法は ウィキペディア 完了するには、wcf はデフォルトで IP または localhost アクセスをサポートできる必要があります。もちろん、次のコードを追加することでこれを実行することもできます。 開発サーバー: { ポート: 8000、 ホスト:'localhost', プロキシ: { "/msg/show.htm": { ターゲット: "http://11.160.119.131/", 安全: 偽 } } } (4) webpack-dev-server の原理については、「リバース プロキシがリバース プロキシと呼ばれる理由」などの情報を参照して詳細を確認してください。実際、フォワード プロキシとリバース プロキシは、「フォワード プロキシは実際のクライアントを隠し、リバース プロキシは実際のサーバーを隠します」という 1 文で要約できます。 webpack-dev-server は実際にはプロキシ サーバーの役割を果たします。サーバー間に共通の同一生成元ポリシーはありません。webpack-dev-server が要求されると、実際のサーバーからデータを要求し、そのデータをブラウザーに送信します。 ブラウザ => localhost:8080 (プロキシなしの webpack-dev-server) => http://you.test.com ブラウザ => localhost:8080 (webpack-dev-server にはプロキシがあります) => http://you.test.com 上記の最初のケースは、プロキシがない場合です。localhost:8080 のページがフロントエンド ポリシーを介して http://you.test.com にアクセスすると、同一オリジン ポリシーが適用されます。つまり、2 番目のステップは、フロントエンド ポリシーを介して別のアドレスにアクセスすることです。ただし、2 番目のケースでは、2 番目のステップは実際にはプロキシ、つまりサーバー間の通信を介して完了し、同一生成元ポリシーの問題は発生しません。代わりに、プロキシ サーバーに直接アクセスして、ページを返します。ページ内の特定の条件を満たす一部のフロントエンド リクエスト (プロキシ、書き換え構成) については、すべてプロキシ サーバーによって完了されます。このようにして、同一生成元問題はプロキシ サーバーによって解決されます。 (5)上記はターゲットがIPの場合について説明しましたが、ターゲットをドメイン名で指定する場合はホストのバインドが必要になる場合があります。たとえば、ホストは以下のようにバインドされます。 開発サーバー: { ポート: 8000、 プロキシ: { "/msg/show.htm": { ターゲット: "http://youku.min.com/", 安全: 偽 } } } これは、ターゲットを IP アドレスにバインドするのとまったく同じです。一言でまとめると、「ターゲットは、特定の URL を満たす要求がどのホストに対応するか、つまりプロキシ サーバーがアクセスする実際のホスト アドレスを指定します。」 プロキシ: { '/some/path': { ターゲット: 'https://other-server.example.com', 安全: 偽、 バイパス: function(req, res, proxyOptions) { (req.headers.accept.indexOf('html')!== -1)の場合{ console.log('ブラウザ要求のプロキシをスキップします。'); '/index.html' を返します。 } } } } HTTP リクエストを検査または変更できる関数を提供することで、プロキシへのリクエストをオーバーライドすることもできます。次の例では、HTTP リクエストを書き換えます。主な機能は、URL の前の /api 部分を削除することです。 プロキシ: { '/api': { ターゲット: 'https://other-server.example.com', パス書き換え: {'^/api' : ''} } } pathRewrite 構成は http-proxy-middleware から取得されます。より多くの構成を表示できます http-proxy-middleware の公式ドキュメント。 historyApiFallback オプションHTML 5 の history API を使用する場合、404 エラーが発生したときに要求されたリソースとして index.html を使用する必要がある場合があります。この場合、次の構成を使用できます: historyApiFallback:true。ただし、output.publicPath を変更する場合はリダイレクト URL を指定する必要があり、historyApiFallback.index オプションを使用できます。 // 出力.publicPath: '/foo-app/' 履歴Apiフォールバック: { インデックス: '/foo-app/' } 静的リソースをリセットするには書き換えオプションを使用します 履歴Apiフォールバック: { 書き直し: [ // ランディングページとして views/landing.html を表示します { から: /^\/$/、から: '/views/landing.html' }, // /subpage で始まるすべてのルートの views/subpage.html を表示します { from: /^\/subpage/, to: '/views/subpage.html' }, // 他のすべてのページで views/404.html を表示します { から: /./、から: '/views/404.html' }, ]、 }, リソースリクエストに if (parsedUrl.pathname.indexOf('.') !== -1 && options.disableDotRule !== true) { ロガー( 「書き直さない」 要求方法、 要求URL、 「パスにドット (.) 文字が含まれているためです。」 ); 次の() を返します。 } rewriteTarget = options.index || '/index.html'; ロガー('Rewriting'、req.method、req.url、'to'、rewriteTarget); req.url = rewriteTarget; 次(); }; つまり、絶対リソースのリクエスト、つまり dotRule を満たすリクエストであっても、disableDotRule (ドットルールファイルの無効化リクエスト) が false の場合は、dotRule を満たすリソースは自分で処理することになりますので、index.html に誘導する必要はありません。 enableDotRule が true の場合、dotRule に適合するリソースは処理されず、直接 index.html に送られます。 歴史({ 無効ドットルール: true }) webpack-dev-server の詳細な設定var server = new WebpackDevServer(コンパイラ、{ contentBase: "/path/to/directory", //コンテンツベースの設定 hot: true, // HMR を有効にし、webpack-dev-server はクライアントコードに "webpackHotUpdate" メッセージを送信します historyApiFallback: false, //シングルページアプリケーション 404 は index.html にリダイレクトされます 圧縮: true、 // リソースのgzip圧縮を有効にするプロキシ: { "**": "http://localhost:9090" }, //http-proxy-middleware からのプロキシ設定 セットアップ: 関数(アプリ) { //webpack-dev-server 自体は、独自のルートを追加できる Express サーバーです // app.get('/some/path', function(req, res) { // res.json({ custom: 'response' }); // }); }, //Express サーバーの express.static メソッドのパラメータを設定します http://expressjs.com/en/4x/api.html#express.static 静的オプション: { }, //インライン モードでは、`error`、`warning`、`info`、`none` など、ブラウザーに表示されるログ レベルを制御するために使用されます。 クライアントログレベル: "情報"、 //コンソールにログを出力しない 静か: 偽、 //起動ログを出力しない 情報なし: 偽、 //webpackはファイルの変更を監視せず、リクエストが来るたびに再コンパイルします。lazy: true、 //ファイル名 filename: "bundle.js", //webpack のウォッチ設定、ファイルの変更をチェックする秒数 watchOptions: { 集計タイムアウト: 300、 投票: 1000 }, //output.path の仮想パス マッピング publicPath: "/assets/", //カスタム HTTP ヘッダーを設定します: { "X-Custom-Header": "yes" }, //パッケージステータス情報出力構成 stats: { colors: true }, //httpsに必要な証明書を設定します: { 証明書: fs.readFileSync("証明書ファイルへのパス.pem"), キー: fs.readFileSync("キーファイルへのパス.pem"), cacert: fs.readFileSync("cacert ファイルへのパス.pem") } }); server.listen(8080, "localhost", function() {}); // サーバーを閉じる(); 上記の他の構成は、filename と lazy を除いて理解しやすいです。lazy と filename の具体的な使用シナリオを分析し続けましょう。遅延フェーズでは、webpack-dev-server はcompiler.watch メソッドを呼び出さず、コンパイルする前にリクエストが到着するのを待機することがわかっています。ソースコードは次のとおりです。 開始ウォッチ: 関数() { var オプション = context.options; var コンパイラ = context.compiler; // 視聴を開始する if(!options.lazy) { var watching =compiler.watch(options.watchOptions, share.handleCompilerCallback); context.watching = 監視中; //context.watching は Watching オブジェクトをそのまま返します} else { // lazy の場合は、監視するのではなく、requested.context.state = true のときにコンパイルすることを意味します。 } } rebuild を呼び出すと、context.state がチェックされます。再コンパイルするたびに、context.state はcompiler.done! で true にリセットされます。 再構築: 関数rebuild() { //compiler.done で Stats オブジェクトが生成されていない場合は、forceRebuild を true に設定します //以前に構築されたことを示す統計がある場合は、runメソッドを呼び出します。if (context.state) { コンテキストの状態 = false; //遅延状態ではcontext.stateがtrueなので再構築する コンテキスト.コンパイラ.実行(share.handleCompilerCallback); } それ以外 { コンテキストを強制的に再構築する = true; } }, リクエストが来たときに再コンパイルを続行するために上記の再構築を呼び出す方法は次のとおりです。 handleRequest: 関数(ファイル名、processRequest、req) { // 遅延モードでは、バンドル要求時に再構築します if(context.options.lazy && (!context.options.filename || context.options.filename.test(filename))) 共有を再構築します。 //ファイル名にハッシュが含まれている場合は、fs を介してメモリからファイル名を読み取り、コールバックによってメッセージを直接クライアントに送信します。 if(HASH_REGEXP.test(ファイル名)) { 試す { if(context.fs.statSync(ファイル名).isFile()) { プロセスリクエスト(); 戻る; } } キャッチ(e) { } } プロセスリクエストを準備完了として共有します。 //コールバック関数はファイルの結果をクライアントに送信します}, その中で、processRequest はコンパイルされたリソースをクライアントに直接送信します。 関数 processRequest() { 試す { var stat = context.fs.statSync(ファイル名); //ファイル名を取得する if(!stat.isFile()) { ディレクトリが ファイル名 = pathJoin(ファイル名、context.options.index || "index.html"); //ファイル名 stat = context.fs.statSync(filename); if(!stat.isFile()) は "next" をスローします。 } それ以外 { 「次」をスローします。 } } } キャッチ(e) { goNext() を返します。 } // サーバーコンテンツ // ファイルに直接アクセスした場合は、それを読み取ります。フォルダーの場合は、フォルダーにアクセスします。 var content = context.fs.readFileSync(filename); コンテンツ = shared.handleRangeHeaders(コンテンツ、要求、リソース); res.setHeader("アクセス制御許可オリジン", "*"); // XHR などをサポートするため。 res.setHeader("Content-Type", mime.lookup(ファイル名) + "; charset=UTF-8"); res.setHeader("コンテンツの長さ", content.length); コンテキストオプションヘッダーの場合{ for(context.options.headersの変数名) { res.setHeader(名前、context.options.headers[名前]); } } // Express は自動的に statusCode を 200 に設定しますが、すべてのサーバーがそうするわけではありません (Koa)。 res.statusCode = res.statusCode || 200; res.send() でコンテンツを送信します。 それ以外の場合は res.end(content); } } したがって、遅延モードでは、ファイル名を指定しない場合、つまり各リクエストが Webpack 出力ファイル (チャンク) に対するものである場合、毎回再構築されます。ただし、ファイル名が指定されている場合は、ファイル名にアクセスしたときにのみ再構築されます。 これで、webpack-dev-server のコアコンセプトと事例に関するこの記事は終了です。webpack-dev-server のコアコンテンツの詳細については、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。 以下もご興味があるかもしれません:
|
>>: 1 つの記事で MySQL のプリコンパイルを理解する
背景最近、SQL 文を書くときに、IN と Exists のどちらを選択するか迷ったので、両方の方法...
MySQLサービス8.0.14のインストール(一般)の参考までに、具体的な内容は次のとおりです。イ...
目次1. プロジェクトの説明2. Nginxイメージの作成3. MySQLイメージの作成4. PHP...
使用状態useState は、関数コンポーネント内で呼び出すことで、コンポーネントに内部状態を追加し...
1.マージンとは何ですか?マージンは、要素の周囲のスペースの間隔を制御するために使用され、視覚的にス...
Docker は本当に素晴らしいです。特に、仮想マシンを使用する場合に比べて、Docker イメージ...
1 公式ウェブサイトから MySQL 5.6 バージョンの圧縮パッケージmysql-5.6.36-w...
ターミナルやコンソールで作業しているときは、メールを読むなど、もっと重要な作業があるかもしれないので...
目次スケルトンスクリーンとは何ですか?デモデザインのアイデア具体的な実装スケルトンスクリーンとは何で...
世界で最も有名なウェブサイトのロゴデザインにはどんなフォントが使われているかご存知ですか?これらのフ...
パフォーマンスの黄金律:エンドユーザーの応答時間のわずか 10% ~ 20% が HTML ドキュメ...
インストールREADMEに従ってインストールしてくださいドキュメントには、exa は Rust で実...
目次序文1. 準備2. インストール3. 環境変数を設定する1. 「新規」をクリックすると、ポップア...
データベースは、どのオブジェクトにどのフィールドが含まれているかを照会します。 *を選択 sysob...
この記事の例では、ボタンをクリックすることで画像を切り替えることを実現するJavaScriptの具体...