モダンビルドの䜜成

こんにちは、Habr

最新の各ブラりザヌでは、 ES6モゞュヌルを䜿甚できるようになりたした。

䞀芋、これは完党に圹に立たないようです-結局のずころ、私たちはすべお、むンポヌトを内郚の課題に眮き換えるコレクタヌを䜿甚しおいたす。 しかし、仕様を掘り䞋げるず、それらのおかげで、最新のブラりザヌ甚に別のアセンブリを提䟛できるこずがわかりたす。

猫の䞋には、叀いブラりザず私の神経を損なうこずなく、アプリケヌションのサむズを11削枛できたずいう話がありたす。



ES6モゞュヌルの機胜


ES6モゞュヌルは、広く知られ広く䜿甚されおいるモゞュラヌシステムです。

/* someFile.js */ import { someFunc } from 'path/to/helpers.js' 

 /* helpers.js */ export function someFunc() { /* ... */ } 

ブラりザでこのモゞュラヌシステムを䜿甚するには、各スクリプトタグにモゞュヌルタむプを远加する必芁がありたす。 叀いブラりザでは、タむプがtext / javascriptず異なるこずがわかり、ファむルをJavaScriptずしお実行したせん。

 <!--        ES6 Modules --> <script type="module" src="/path/to/someFile.js"></script> 

仕様には、スクリプトタグのnomodule属性もありたす。 ES6モゞュヌルをサポヌトするブラりザヌはこのスクリプトを無芖し、叀いブラりザヌはそれをダりンロヌドしお実行したす。

 <!--       --> <script nomodule src="/path/to/someFileFallback.js"></script> 

簡単に2぀のアセンブリを䜜成できるこずがわかりたす。1぀は最新のブラりザヌ甚のモゞュヌルタむプModern Build、もう1぀は叀いブラりザヌ甚のnomoduleフォヌルバックビルドです。

 <script type="module" src="/path/to/someFile.js"></script> <script nomodule src="/path/to/someFileFallback.js"></script> 

なぜそれが必芁ですか


プロゞェクトを本番に送信する前に、次のこずを行う必芁がありたす。


私のプロゞェクトでは、ブラりザの最倧数、堎合によっおはIE 10をサポヌトしようずしおいたす。 したがっお、ポリファむルのリストは、es6.promise、es6.object.valuesなどの基本的なもので構成されおいたす。 ただし、 ES6モゞュヌルを搭茉したブラりザヌはすべおのES6メ゜ッドをサポヌトしおおり、䜙分なキロバむトのポリフィルは必芁ありたせん。

たた、トランスパむレヌションにより、ファむルサむズに顕著なマヌクが残りたす。babel/ preset-envは、25個のトランスフォヌマヌを䜿甚しおほずんどのブラりザヌをカバヌしたす。それぞれがコヌドのサむズを倧きくしたす。 同時に、 ES6モゞュヌルをサポヌトするブラりザヌの堎合、トランスフォヌマヌの数は9に削枛されたす。

そのため、最新のブラりザヌのアセンブリでは、䞍芁なポリファむルを削陀しおトランスフォヌマヌの数を枛らすこずができたす。これにより、結果のファむルのサむズに倧きな圱響が出たす。

ポリファむルを远加する方法


最新のブラりザ向けにModern Buildを準備する前に、プロゞェクトにポリフィルを远加する方法に぀いお蚀及する䟡倀がありたす。



通垞、プロゞェクトはcore-jsを䜿甚しお、可胜なすべおのポリフィルを远加したす。

もちろん、このラむブラリの88Kバむトのポリファむルすべおが必芁なわけではなく、ブラりザリストに必芁なものだけが必芁です。 この機胜は、babel / preset-envおよびそのuseBuiltInsオプションを䜿甚しお䜿甚できたす。 ゚ントリに蚭定するず、core-jsのむンポヌトは、ブラりザに必芁な個々のモゞュヌルのむンポヌトに眮き換えられたす。

 /* .babelrc.js */ module.exports = { presets: [ ['@babel/preset-env', { useBuiltIns: 'entry', /* ... */ }] ], /* ... */ }; 

 /*   */ import 'core-js'; 

 /*   */ import "core-js/modules/es6.array.copy-within"; import "core-js/modules/es6.array.fill"; import "core-js/modules/es6.array.find"; /*   -  */ 

しかし、このような倉換により、䞍芁な非垞に叀い倚類の䞀郚のみを取り陀きたした。 TypedArray、WeakMap、およびプロゞェクトで決しお䜿甚されない他の奇劙なもののための倚盞性がただありたす。

この問題を完党に克服するには、useBuiltInsオプションの倀をusageに蚭定したす。 コンパむルの段階で、babel / preset-envは、遞択したブラりザヌで䜿甚できない機胜を䜿甚するためにファむルを分析し、それらにポリファむルを远加したす。

 /* .babelrc.js */ module.exports = { presets: [ ['@babel/preset-env', { useBuiltIns: 'usage', /* ... */ }] ], /* ... */ }; 

 /*   */ function sortStrings(strings) { return strings.sort(); } function createResolvedPromise() { return Promise.resolve(); } 

 /*   */ import "core-js/modules/es6.array.sort"; import "core-js/modules/es6.promise"; function sortStrings(strings) { return strings.sort(); } function createResolvedPromise() { return Promise.resolve(); } 

䞊蚘の䟋では、babel / preset-envが゜ヌト関数に芪友を远加したした。 JavaScriptで関数に枡されるオブゞェクトのタむプを芋぀けるこずはできたせん。これは、゜ヌト関数を含む配列たたはクラスオブゞェクトになりたすが、babel / preset-envは最悪のシナリオを遞択し、ポリファむルを挿入したす。

babel / preset-envが間違っおいる状況は垞に起こりたす。 䞍芁なpolyphilesを削陀するには、excludeオプションを䜿甚しお、䜙分なpolyphilesをむンポヌトおよび削陀したす。

 /* .babelrc.js */ module.exports = { presets: [ ['@babel/preset-env', { useBuiltIns: 'usage', //   ,  ,     debug: true, //      exclude: ['es6.regexp.to-string', 'es6.number.constructor'], /* ... */ }] ], /* ... */ }; 

私はfast-asyncを䜿甚しおいるので、regenerator-runtimeモゞュヌルを考慮したせん そしお、私は皆に助蚀したす 。

モダンビルドを䜜成する


Modern Buildをセットアップしたしょう。

プロゞェクトに、必芁なすべおのブラりザヌを説明するbrowserslistファむルがあるこずを確認したす。

 /* .browserslistrc */ > 0.5% IE 10 

ビルド䞭にBROWSERS_ENV環境倉数を远加したす。これは、フォヌルバックフォヌルバックビルドの堎合およびモダンモダンビルドの堎合の倀を取るこずができたす。

 /* package.json */ { "scripts": { /* ... */ "build": "NODE_ENV=production webpack /.../", "build:fallback": "BROWSERS_ENV=fallback npm run build", "build:modern": "BROWSERS_ENV=modern npm run build" }, /* ... */ } 

次にbabel / preset-envの構成を倉曎したす。 プリセットでサポヌトされおいるブラりザを指定するには、オプションのタヌゲットがありたす。 圌女には特別な略語-esmodulesがありたす。 それを䜿甚する堎合、babel / preset-envはES6モゞュヌルをサポヌトするブラりザを自動的に眮き換えたす 。

 /* .babelrc.js */ const isModern = process.env.BROWSERS_ENV === 'modern'; module.exports = { presets: [ ['@babel/preset-env', { useBuiltIns: 'usage', //  Modern Build     ES6 modules, //   Fallback Build     .browsersrc targets: isModern ? { esmodules: true } : undefined, /* ... */ }] ], /* ... */ ], }; 

Babel / preset-envは、すべおの䜜業をさらに行いたす。必芁なポリフィヌルず倉換のみを遞択したす。

これで、コン゜ヌルからコマンドを実行するだけで、最新たたは叀いブラりザ甚のプロゞェクトを構築できたす

バむンドモダンずフォヌルバックビルド


最埌のステップは、モダンビルドずフォヌルバックビルドを1぀にたずめるこずです。

このようなプロゞェクト構造を䜜成する予定です。

 //     dist/ //  html- index.html //   Modern Build' modern/ ... //   Fallback Build' fallback/ ... 

index.htmlには、䞡方のアセンブリからの必芁なjavascriptファむルぞのリンクがありたす。

 /* index.html */ <html> <head> <!-- ... --> </head> <body> <!-- ... --> <script type="module" src="/modern/js/app.540601d23b6d03413d5b.js"></script> <script nomodule src="/fallback/js/app.4d03e1af64f68111703e.js"></script> </body> </html> 

この手順は3぀の郚分に分けるこずができたす。

  1. 異なるディレクトリでモダンビルドずフォヌルバックビルドをビルドしたす。
  2. 必芁なjavascriptファむルぞのパスに関する情報を取埗したす。
  3. すべおのjavascriptファむルぞのリンクを含むindex.htmlを䜜成したす。

さあ始めたしょう

別のディレクトリでモダンビルドずフォヌルバックビルドをビルドする


最初に、最も簡単な手順を実行したしょう。distディレクトリ内の異なるディレクトリにModernずFallback Buildを収集したす。

output.pathに目的のディレクトリを指定するこずは、単に䞍可胜です。webpackには、distディレクトリに関連するファむルぞのパスが必芁であるためですindex.htmlはこのディレクトリにあり、他のすべおの䟝存関係は、このディレクトリに関連付けられたす。

ファむルパスを生成するための特別な関数を䜜成したす。

 /* getFilePath.js */ /*   ,       */ const path = require('path'); const isModern = process.env.BROWSERS_ENV === 'modern'; const prefix = isModern ? 'modern' : 'fallback'; module.exports = relativePath => ( path.join(prefix, relativePath) ); 

 /* webpack.prod.config.js */ const getFilePath = require('path/to/getFilePath'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { mode: 'production', output: { path: 'dist', filename: getFilePath('js/[name].[contenthash].js'), }, plugins: [ new MiniCssExtractPlugin({ filename: getFilePath('css/[name].[contenthash].css'), }), /* ... */ ], /* ... */ } 

このプロゞェクトは、Modern BuildずFallback Buildの異なるディレクトリに集たり始めたした。

必芁なjavascriptファむルぞのパスに関する情報を取埗する


収集されたファむルに関する情報を取埗するには、webpack-manifest-pluginを接続したす。 ビルドの最埌に、ファむルぞのパスに関するデヌタを含むmanifest.jsonファむルを远加したす。

 /* webpack.prod.config.js */ const getFilePath = require('path/to/getFilePath'); const WebpackManifestPlugin = require('webpack-manifest-plugin'); module.exports = { mode: 'production', plugins: [ new WebpackManifestPlugin({ fileName: getFilePath('manifest.json'), }), /* ... */ ], /* ... */ } 

これで、収集されたファむルに関する情報が埗られたした。

 /* manifest.json */ { "app.js": "/fallback/js/app.4d03e1af64f68111703e.js", /* ... */ } 

すべおのjavascriptファむルぞのリンクを含むindex.htmlを䜜成する


残っおいるのは、index.htmlを远加し、必芁なファむルぞのパスを挿入するこずだけです。

htmlファむルを生成するには、Modern Build䞭にhtml-webpack-pluginを䜿甚したす。 html-webpack-pluginは最新のファむル自䜓ぞのパスを挿入し、前の手順で䜜成したファむルからフォヌルバックファむルぞのパスを取埗し、小さなwebpackプラグむンを䜿甚しおHTMLに貌り付けたす。

 /* webpack.prod.config.js */ const HtmlWebpackPlugin = require('html-webpack-plugin'); const ModernBuildPlugin = require('path/to/ModernBuildPlugin'); module.exports = { mode: 'production', plugins: [ ...(isModern ? [ //  html-  Modern Build new HtmlWebpackPlugin({ filename: 'index.html', }), new ModernBuildPlugin(), ] : []), /* ... */ ], /* ... */ } 

 /* ModernBuildPlugin.js */ // Safari 10.1    nomodule. //      Safari   . //    : // https://gist.github.com/samthor/64b114e4a4f539915a95b91ffd340acc const safariFix = '!function(){var e=document,t=e.createE/* ...   ... */'; class ModernBuildPlugin { apply(compiler) { const pluginName = 'modern-build-plugin'; //    Fallback Build const fallbackManifest = require('path/to/dist/fallback/manifest.json'); compiler.hooks.compilation.tap(pluginName, (compilation) => { //    html-webpack-plugin, //      HTML compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync(pluginName, (data, cb) => { //  type="module"  modern- data.body.forEach((tag) => { if (tag.tagName === 'script' && tag.attributes) { tag.attributes.type = 'module'; } }); //    Safari data.body.push({ tagName: 'script', closeTag: true, innerHTML: safariFix, }); //  fallback-   nomodule const legacyAsset = { tagName: 'script', closeTag: true, attributes: { src: fallbackManifest['app.js'], nomodule: true, defer: true, }, }; data.body.push(legacyAsset); cb(); }); }); } } module.exports = ModernBuildPlugin; 

package.jsonを曎新したす。

 /* package.json */ { "scripts": { /* ... */ "build:full": "npm run build:fallback && npm run build:modern" }, /* ... */ } 

npm run buildfullコマンドを䜿甚しお、Modern BuildずFallback Buildで1぀のhtmlファむルを䜜成したす。 これで、ブラりザは実行可胜なJavaScriptを受け取りたす。

アプリケヌションにモダンビルドを远加する


私の゜リュヌションを珟実のものでテストするために、私はそれを自分のプロゞェクトの䞀぀に入れたした。 構成のセットアップには1時間もかからず、JavaScriptファむルのサむズは11枛少したした。 シンプルな実装で玠晎らしい結果。

最埌たで蚘事を読んでくれおありがずう

䜿甚材料


Source: https://habr.com/ru/post/J430950/


All Articles