Webアプリケーションがますます複雑になるにつれて、Webアプリをスケーラブルにすることが最も重要になります。以前はアドホックJavaScriptとjQueryを作成するだけで十分でしたが、現在、Webアプリを構築するには、次のようなはるかに高度な規律と正式なソフトウェア開発手法が必要です。
Webには、独自の開発上の課題もいくつかあります。たとえば、ウェブページは多くの非同期リクエストを行うため、ウェブアプリのパフォーマンスは、それぞれ独自の小さなオーバーヘッド(ヘッダー、ハンドシェイクなど)を持つ数百のJSファイルとCSSファイルをリクエストする必要があるために大幅に低下する可能性があります。この特定の問題は、多くの場合、ファイルをバンドルすることで解決できるため、数百の個別のファイルではなく、単一のバンドルされたJSファイルとCSSファイルのみをリクエストします。
また、ES5の互換性を維持しながらES6コードを活用するために、ネイティブJSやCSSにコンパイルされるSASSやJSXなどの言語プリプロセッサやBabelなどのJSトランスパイラーを使用することもよくあります。
これは、Webアプリ自体のロジックの記述とは関係のないかなりの数のタスクに相当します。ここでタスクランナーが登場します。タスクランナーの目的は、これらすべてのタスクを自動化して、アプリの作成に集中しながら、強化された開発環境から利益を得ることができるようにすることです。タスクランナーを構成したら、ターミナルで1つのコマンドを呼び出すだけです。
使用します gulp 非常に開発者にやさしく、習得が容易で、すぐに理解できるため、タスクランナーとして。
GulpのAPIは、次の4つの関数で構成されています。
C言語でのオペレーティングシステムプログラミング
gulp.src
gulp.dest
gulp.task
gulp.watch
たとえば、次の4つの関数のうち3つを使用するサンプルタスクを次に示します。
gulp.task('my-first-task', function() { gulp.src('/public/js/**/*.js') .pipe(concat()) .pipe(minify()) .pipe(gulp.dest('build')) });
my-first-task
の場合が実行され、グロブパターンに一致するすべてのファイル/public/js/**/*.js
縮小されてからbuild
に転送されますフォルダ。
これの美しさは.pipe()
にありますチェーン。入力ファイルのセットを取得し、それらを一連の変換にパイプしてから、出力ファイルを返します。さらに便利にするために、minify()
などの実際の配管変換は、多くの場合NPMライブラリによって実行されます。その結果、パイプ内のファイルの名前を変更する以外に、独自の変換を作成する必要があることは実際には非常にまれです。
Gulpを理解するための次のステップは、タスクの依存関係の配列を理解することです。
gulp.task('my-second-task', ['lint', 'bundle'], function() { ... });
ここで、my-second-task
lint
の後にのみコールバック関数を実行しますおよびbundle
タスクが完了しました。これにより、関心の分離が可能になります。LESS
の変換など、単一の責任を持つ一連の小さなタスクを作成します。 CSS
に移動し、タスクの依存関係の配列を介して他のすべてのタスクを呼び出すだけの一種のマスタータスクを作成します。
最後に、gulp.watch
があります。これは、globファイルパターンの変更を監視し、変更が検出されると、一連のタスクを実行します。
gulp.task('my-third-task', function() { gulp.watch('/public/js/**/*.js', ['lint', 'reload']) })
上記の例では、/public/js/**/*.js
に一致するファイルへの変更lint
をトリガーしますおよびreload
仕事。 gulp.watch
の一般的な使用法これは、ブラウザでライブリロードをトリガーすることです。これは、開発に非常に役立つ機能であり、一度体験すると、それなしでは生きていけません。
そして、そのように、あなたはgulp
について本当に知る必要があるすべてを理解しています。
CommonJSパターンを使用する場合、JavaScriptファイルをバンドルすることはそれらを連結することほど簡単ではありません。むしろ、一連のindex.js
を持つエントリポイント(通常はapp.js
またはrequire
と呼ばれます)があります。またはimport
ファイルの先頭にあるステートメント:
var Component1 = require('./components/Component1'); var Component2 = require('./components/Component2');
import Component1 from './components/Component1'; import Component2 from './components/Component2';
app.js
の残りのコードの前に依存関係を解決する必要があり、それらの依存関係自体にさらに解決すべき依存関係がある場合があります。さらに、あなたはrequire
かもしれませんアプリケーションの複数の場所で同じ依存関係がありますが、その依存関係を解決したいのは1回だけです。ご想像のとおり、依存関係ツリーが数レベル深くなると、JavaScriptをバンドルするプロセスはかなり複雑になります。これは、 Browserify とWebpackが入ります。
Webpack はバンドラーですが、Gulpはタスクランナーであるため、これら2つのツールが一般的に一緒に使用されることを期待できます。代わりに、特にReactコミュニティの間で、Webpackを使用する傾向が高まっています。 代わりに ガルプの。どうしてこれなの?
簡単に言えば、Webpackは非常に強力なツールであるため、タスクランナーを介して実行するタスクの大部分をすでに実行できます。たとえば、Webpackには、バンドルの縮小化とソースマップのオプションがすでに用意されています。さらに、Webpackは、ライブリロードとホットリロードの両方をサポートするwebpack-dev-server
と呼ばれるカスタムサーバーを介してミドルウェアとして実行できます(これらの機能については後で説明します)。ローダーを使用することで、ES6からES5へのトランスパイル、およびCSSプリプロセッサとポストプロセッサを追加することもできます。それは本当に、Webpackが独立して処理できない主要なタスクとしてユニットテストとリンティングを残すだけです。少なくとも半ダースの潜在的なgulpタスクを2つに削減したことを考えると、多くの開発者は代わりにNPMスクリプトを直接使用することを選択します。これにより、プロジェクトにGulpを追加するオーバーヘッドが回避されます(これについては後で説明します)。 。
Webpackを使用することの主な欠点は、構成がかなり難しいことです。これは、プロジェクトをすばやく立ち上げて実行しようとしている場合は魅力的ではありません。
3つの異なるタスクランナー設定でプロジェクトを設定します。各セットアップは、次のタスクを実行します。
3つのセットアップは次のようになります。
アプリケーションは使用します React フロントエンド用。もともとはフレームワークにとらわれないアプローチを使用したかったのですが、Reactを使用するとHTMLファイルが1つだけ必要であり、ReactはCommonJSパターンで非常にうまく機能するため、実際にはタスクランナーの責任が簡素化されます。
各セットアップの長所と短所について説明しますので、プロジェクトのニーズに最適なセットアップのタイプについて情報に基づいた決定を下すことができます。
アプローチごとに1つずつ、3つのブランチを持つGitリポジトリをセットアップしました( リンク )。各セットアップのテストは次のように簡単です。
git checkout npm prune (optional) npm install gulp (or npm start, depending on the setup)
各ブランチのコードを詳しく調べてみましょう…
- app - components - fonts - styles - index.html - index.js - index.test.js - routes.js
単純なHTMLファイル。 Reactアプリケーションがロードされ、バンドルされたJSファイルとCSSファイルを1つだけ使用します。実際、Webpack開発のセットアップでは、bundle.css
も必要ありません。
これは、アプリのJSエントリポイントとして機能します。基本的に、Reactルーターをdiv
にロードするだけです。 ID付きapp
先に述べたように。
このファイルはルートを定義します。 URL /
、/about
および/contact
HomePage
、AboutPage
、およびContactPage
にマップされますそれぞれコンポーネント。
llcのさまざまな種類は何ですか
これは、ネイティブJavaScriptの動作をテストする一連の単体テストです。実際の本番品質のアプリでは、Reactコンポーネント(少なくとも状態を操作するコンポーネント)ごとに単体テストを作成し、React固有の動作をテストします。ただし、この投稿では、ウォッチモードで実行できる機能ユニットテストを用意するだけで十分です。
これは、すべてのページビューのコンテナと考えることができます。各ページには、コンポーネントと、ページビュー自体に評価されるthis.props.children
が含まれています(例:ブラウザのContactPage
にある場合)。
components / home / HomePage.js
これが私たちのホームビューです。 /contact
を使用することを選択しましたブートストラップのグリッドシステムはレスポンシブページの作成に優れているためです。ブートストラップを適切に使用すると、小さいビューポート用に作成する必要のあるメディアクエリの数が大幅に削減されます。
残りのコンポーネント(react-bootstrap
、Header
、AboutPage
)も同様に構造化されています(ContactPage
マークアップ、状態操作なし)。
それでは、スタイリングについて詳しく説明しましょう。
Reactコンポーネントのスタイルを設定するための私の好ましいアプローチは、コンポーネントごとに1つのスタイルシートを用意することです。そのスタイルは、その特定のコンポーネントにのみ適用されるようにスコープされています。私のReactコンポーネントのそれぞれで、トップレベルのreact-bootstrap
に気付くでしょう。コンポーネントの名前と一致するクラス名があります。したがって、たとえば、div
マークアップは次のようにラップされています。
HomePage.js
関連する ...
もあります次のように構成されたファイル:
HomePage.scss
このアプローチがなぜそれほど役立つのですか?その結果、高度にモジュール化されたCSSが実現し、不要なカスケード動作の問題が大幅に解消されます。
2つのReactコンポーネント@import '../../styles/variables'; .HomePage { // Content here }
があるとします。およびComponent1
。どちらの場合も、Component2
をオーバーライドする必要がありますフォントサイズ。
h2
/* Component1.scss */ .Component1 { h2 { font-size: 30px; } } /* Component2.scss */ .Component2 { h2 { font-size: 60px; } }
h2
のフォントサイズおよびComponent1
コンポーネントが隣接しているか、一方のコンポーネントが他方の内部にネストされているかに関係なく、は独立しています。理想的には、これはコンポーネントのスタイリングが完全に自己完結型であることを意味します。つまり、コンポーネントはマークアップのどこに配置されていてもまったく同じように見えます。実際には、それは必ずしもそれほど単純ではありませんが、それは確かに正しい方向への大きな一歩です。
Rubyのレールとは
コンポーネントごとのスタイルに加えて、Component2
が好きです。グローバルスタイルシートstyles
と、特定の責任を処理するSASSパーシャル(この場合、フォントと変数のそれぞれglobal.scss
と_fonts.scss
)を含むフォルダー。グローバルスタイルシートを使用すると、アプリ全体の一般的なルックアンドフィールを定義できます。ヘルパーパーシャルは、必要に応じてコンポーネントごとのスタイルシートでインポートできます。
各ブランチの共通コードについて詳しく説明したので、最初のタスクランナー/バンドラーアプローチに焦点を移しましょう。
これは、22のインポートと150行のコードを含む驚くほど大きなgulpfileになります。したがって、簡潔にするために、_variables.scss
、js
、css
、server
、およびwatch
のみを確認します。タスクの詳細。
default
このアプローチは、いくつかの理由でかなり醜いです。一つには、タスクは3つの別々の部分に分割されます。まず、Browserifyバンドルオブジェクト// Browserify specific configuration const b = browserify({ entries: [config.paths.entry], debug: true, plugin: PROD ? [] : [hmr, watchify], cache: {}, packageCache: {} }) .transform('babelify'); b.on('update', bundle); b.on('log', gutil.log); (...) gulp.task('js', bundle); (...) // Bundles our JS using Browserify. Sourcemaps are used in development, while minification is used in production. function bundle() { return b.bundle() .on('error', gutil.log.bind(gutil, 'Browserify Error')) .pipe(source('bundle.js')) .pipe(buffer()) .pipe(cond(PROD, minifyJS())) .pipe(cond(!PROD, sourcemaps.init({loadMaps: true}))) .pipe(cond(!PROD, sourcemaps.write())) .pipe(gulp.dest(config.paths.baseDir)); }
を作成し、いくつかのオプションを渡し、いくつかのイベントハンドラーを定義します。次に、Gulpタスク自体があります。これは、名前付き関数をインライン化するのではなく、コールバックとして渡す必要があります(b
はまったく同じコールバックを使用するため)。これには、b.on('update')
を渡すだけのGulpタスクの優雅さはほとんどありません。いくつかの変更をパイプします。
もう1つの問題は、これにより、gulp.src
、html
をリロードするためのさまざまなアプローチが必要になることです。およびcss
ブラウザで。私たちのGulpを見てjs
仕事:
watch
HTMLファイルが変更されると、gulp.task('watch', () => { livereload.listen({basePath: 'dist'}); gulp.watch(config.paths.html, ['html']); gulp.watch(config.paths.css, ['css']); gulp.watch(config.paths.js, () => { runSequence('lint', 'test'); }); });
タスクが再実行されます。
html
最後のパイプ呼び出しgulp.task('html', () => { return gulp.src(config.paths.html) .pipe(gulp.dest(config.paths.baseDir)) .pipe(cond(!PROD, livereload())); });
livereload()
の場合はNODE_ENV
ではなく、ブラウザの更新をトリガーします。
同じロジックがCSSウォッチにも使用されます。 CSSファイルが変更されると、production
タスクが再実行され、css
の最後のパイプが実行されますタスクトリガーcss
ブラウザを更新します。
ただし、livereload()
時計はjs
を呼び出しませんまったくタスク。代わりに、Browserifyのイベントハンドラーjs
まったく異なるアプローチ(つまり、ホットモジュールの交換)を使用してリロードを処理します。このアプローチの不一致は苛立たしいものですが、残念ながら 増分 ビルドします。単純にb.on('update', bundle)
と呼んだ場合livereload()
の終わりに関数、これは再構築します 全体 個々のJSファイルの変更に関するJSバンドル。このようなアプローチは明らかに拡張性がありません。 JSファイルが多いほど、各リバンドルにかかる時間が長くなります。突然、500ミリ秒のリバンドルに30秒かかり始めます。これは、アジャイル開発を実際に阻害します。
bundle
ここでの最初の問題は、面倒なベンダーのCSSの組み込みです。新しいベンダーのCSSファイルがプロジェクトに追加されるたびに、要素をgulp.task('css', () => { return gulp.src( [ 'node_modules/bootstrap/dist/css/bootstrap.css', 'node_modules/font-awesome/css/font-awesome.css', config.paths.css ] ) .pipe(cond(!PROD, sourcemaps.init())) .pipe(sass().on('error', sass.logError)) .pipe(concat('bundle.css')) .pipe(cond(PROD, minifyCSS())) .pipe(cond(!PROD, sourcemaps.write())) .pipe(gulp.dest(config.paths.baseDir)) .pipe(cond(!PROD, livereload())); });
に追加するようにgulpfileを変更することを忘れないでください。実際のソースコードの関連する場所にインポートを追加するのではなく、配列。
もう1つの主な問題は、各パイプの複雑なロジックです。 gulp.src
というNPMライブラリを追加する必要がありましたパイプに条件付きロジックを設定するだけで、最終結果はあまり読みにくくなります(どこでもトリプルブラケット!)。
gulp-cond
このタスクは非常に簡単です。これは基本的に、gulp.task('server', () => { nodemon({ script: 'server.js' }); });
を実行するコマンドライン呼び出しnodemon server.js
のラッパーです。ノード環境で。 server.js
nodemon
の代わりに使用されますファイルに変更を加えると、ファイルが再起動します。デフォルトでは、node
で実行中のプロセスを再開します どれか JSファイルの変更。そのため、nodemon
を含めることが重要です。その範囲を制限するファイル:
nodemon.json
サーバーコードを確認しましょう。
{ 'watch': 'server.js' }
これにより、ノード環境に基づいてサーバーとポートのベースディレクトリが設定され、expressのインスタンスが作成されます。
const baseDir = process.env.NODE_ENV === 'production' ? 'build' : 'dist'; const port = process.env.NODE_ENV === 'production' ? 8080: 3000; const app = express();
これによりapp.use(require('connect-livereload')({port: 35729})); app.use(express.static(path.join(__dirname, baseDir)));
が追加されますミドルウェア(ライブリロードのセットアップに必要)と静的ミドルウェア(静的アセットの処理に必要)。
connect-livereload
これは単純なAPIルートです。 app.get('/api/sample-route', (req, res) => { res.send({ website: 'ApeeScape', blogPost: true }); });
に移動した場合ブラウザに次のように表示されます。
localhost:3000/api/sample-route
実際のバックエンドでは、APIルート専用のフォルダー全体、DB接続を確立するための個別のファイルなどがあります。このサンプルルートは、設定したフロントエンドの上にバックエンドを簡単に構築できることを示すために含まれているだけです。
{ website: 'ApeeScape', blogPost: true }
これはキャッチオールルートです。つまり、ブラウザに入力したURLに関係なく、サーバーは唯一のapp.get('*', (req, res) => { res.sendFile(path.join(__dirname, './', baseDir ,'/index.html')); });
を返します。ページ。その場合、クライアント側でルートを解決するのはReactルーターの責任です。
index.html
これは、指定したポートをリッスンし、指定したURLの新しいタブでブラウザーを開くようにExpressインスタンスに指示します。
これまでのところ、サーバーのセットアップについて私が気に入らないのは次のとおりです。
app.listen(port, () => { open(`http://localhost:${port}`); });
すでにapp.use(require('connect-livereload')({port: 35729}));
を使用していることを考えるとgulpfileでは、これにより、livereloadを使用する必要がある2つの別々の場所が作成されます。
さて、最後になりましたが、重要なことです。
gulp-livereload
これは、単にgulp.task('default', (cb) => { runSequence('clean', 'lint', 'test', 'html', 'css', 'js', 'fonts', 'server', 'watch', cb); });
と入力したときに実行されるタスクです。ターミナルに。奇妙なことに、gulp
を使用する必要がありますタスクを順番に実行するため。通常、一連のタスクは並行して実行されますが、これが常に望ましい動作であるとは限りません。たとえば、runSequence
が必要です。タスクはclean
の前に実行されますファイルを移動する前に、宛先フォルダーが空であることを確認します。 gulp 4がリリースされると、html
がサポートされます。およびgulp.series
メソッドはネイティブですが、今のところ、セットアップにこのわずかな癖を残しておく必要があります。
それを超えて、これは実際にはかなりエレガントです。アプリの作成とホスティング全体が1つのコマンドで実行され、ワークフローの任意の部分を理解することは、実行シーケンスで個々のタスクを調べるのと同じくらい簡単です。さらに、シーケンス全体を小さなチャンクに分割して、アプリを作成およびホストするためのよりきめ細かいアプローチをとることができます。たとえば、gulp.parallel
という別のタスクを設定できます。 validate
を実行しますおよびlint
タスク。または、test
を使用することもできます実行されるタスクhost
およびserver
。タスクをオーケストレーションするこの機能は、特にアプリケーションが拡張され、より自動化されたタスクが必要になるため、非常に強力です。
watch
if (argv.prod) { process.env.NODE_ENV = 'production'; } let PROD = process.env.NODE_ENV === 'production';
を使用するNPMライブラリでは、コマンドラインフラグをGulpに提供できます。ここでは、yargs
の場合、ノード環境を本番環境に設定するようにgulpfileに指示します。 --prod
に渡されますターミナルで。私たちのgulp
次に、変数は、gulpfileの開発動作と本番動作を区別するための条件として使用されます。たとえば、PROD
に渡すオプションの1つ構成は次のとおりです。
browserify
これはplugin: PROD ? [] : [hmr, watchify]
を示します本番モードでプラグインを使用せず、browserify
を使用するおよびhmr
他の環境のプラグイン。
これwatchify
条件付きは、本番環境と開発用に別々のgulpfileを作成する必要がなく、最終的には多くのコードの繰り返しが含まれるため、非常に便利です。代わりに、PROD
のようなことができます本番環境でデフォルトのタスクを実行するには、またはgulp --prod
gulp html --prod
のみを実行します生産におけるタスク。一方、Gulpパイプラインにhtml
などのステートメントが散らばっているのは以前に見ました。最も読みやすいものではありません。結局、ブール変数アプローチを使用するか、2つの別々のgulpfileを設定するかは好みの問題です。
次に、Gulpをタスクランナーとして使用し続け、BrowserifyをWebpackに置き換えるとどうなるかを見てみましょう。
ソフトウェアプロジェクトのドキュメントを準備する方法
突然、gulpfileの長さはわずか99行で、インポートは12回になり、以前の設定から大幅に削減されました。デフォルトのタスクを確認すると、次のようになります。
.pipe(cond(!PROD, livereload()))
これで、完全なWebアプリのセットアップに必要なタスクは9つではなく5つになり、劇的に改善されました。
さらに、gulp.task('default', (cb) => { runSequence('lint', 'test', 'build', 'server', 'watch', cb); });
の必要性を排除しました。私たちのlivereload
タスクは単純になりました:
watch
これは、gulpwatcherがいかなる種類の再バンドル動作もトリガーしていないことを意味します。追加のボーナスとして、転送する必要はありませんgulp.task('watch', () => { gulp.watch(config.paths.js, () => { runSequence('lint', 'test'); }); });
index.html
から〜app
またはdist
もう。
タスクの削減に焦点を戻すと、build
、html
、css
およびjs
タスクはすべて単一のfonts
に置き換えられました仕事:
build
十分に単純です。 gulp.task('build', () => { runSequence('clean', 'html'); return gulp.src(config.paths.entry) .pipe(webpack(require('./webpack.config'))) .pipe(gulp.dest(config.paths.baseDir)); });
を実行しますおよびclean
順番にタスク。それらが完了したら、エントリポイントを取得し、Webpackにパイプして、html
を渡します。ファイルを構成して、結果のバンドルをwebpack.config.js
に送信します(ノード環境に応じて、baseDir
またはdist
のいずれか)。
Webpack構成ファイルを見てみましょう。
これはかなり大きくて威圧的な設定ファイルなので、build
に設定されている重要なプロパティのいくつかを説明しましょう。オブジェクト。
module.exports
これにより、Webpackが使用するソースマップのタイプが設定されます。 Webpackは、すぐに使用できるソースマップをサポートするだけでなく、実際にはさまざまなソースマップオプションをサポートしています。各オプションは、ソースマップの詳細と再構築速度(変更時に再バンドルするのにかかる時間)のバランスが異なります。これは、開発に「安価な」ソースマップオプションを使用して高速リロードを実現し、本番環境ではより高価なソースマップオプションを使用できることを意味します。
devtool: PROD ? 'source-map' : 'eval-source-map',
これがバンドルのエントリポイントです。配列が渡されることに注意してください。つまり、複数のエントリポイントを持つことができます。この場合、予想されるエントリポイントがありますentry: PROD ? './app/index' : [ 'webpack-hot-middleware/client?reload=true', // reloads the page if hot module reloading fails. './app/index' ]
同様にapp/index.js
ホットモジュールのリロード設定の一部として使用されるエントリポイント。
webpack-hot-middleware
これは、コンパイルされたバンドルが出力される場所です。最も紛らわしいオプションはoutput: { path: PROD ? __dirname + '/build' : __dirname + '/dist', publicPath: '/', filename: 'bundle.js' },
です。バンドルがサーバー上でホストされる場所のベースURLを設定します。したがって、たとえば、publicPath
の場合がpublicPath
の場合、バンドルは/public/assets
の下に表示されますサーバー上。
/public/assets/bundle.js
これにより、プロジェクト内のどのフォルダをサーバーのルートディレクトリとして使用するかがサーバーに指示されます。
Webpackがプロジェクトで作成されたバンドルをサーバー上のバンドルにどのようにマッピングしているかについて混乱した場合は、次の点に注意してください。
devServer: { contentBase: PROD ? './build' : './app' }
+ path
:プロジェクトのソースコード内のバンドルの正確な場所filename
(ルートとして、contentBase
)+ /
:サーバー上のバンドルの場所publicPath
これらは、Webpackの機能を何らかの方法で強化するプラグインです。たとえば、plugins: PROD ? [ new webpack.optimize.OccurenceOrderPlugin(), new webpack.DefinePlugin(GLOBALS), new ExtractTextPlugin('bundle.css'), new webpack.optimize.DedupePlugin(), new webpack.optimize.UglifyJsPlugin({compress: {warnings: false}}) ] : [ new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin() ],
縮小に責任があります。
webpack.optimize.UglifyJsPlugin
これらはローダーです。基本的に、loaders: [ {test: /.js$/, include: path.join(__dirname, 'app'), loaders: ['babel']}, { test: /.css$/, loader: PROD ? ExtractTextPlugin.extract('style', 'css?sourceMap'): 'style!css?sourceMap' }, { test: /.scss$/, loader: PROD ? ExtractTextPlugin.extract('style', 'css?sourceMap!resolve-url!sass?sourceMap') : 'style!css?sourceMap!resolve-url!sass?sourceMap' }, gif)(?S*)?$/, loader: 'url?limit=100000&name=img/[name].[ext]', woff2 ]
を介してロードされたファイルを前処理します。ステートメント。ローダーをチェーン接続できるという点で、Gulpパイプにいくぶん似ています。
ローダーオブジェクトの1つを調べてみましょう。
require()
{test: /.scss$/, loader: 'style!css?sourceMap!resolve-url!sass?sourceMap'}
プロパティは、ファイルが指定された正規表現パターン(この場合はtest
)に一致する場合に、指定されたローダーが適用されることをWebpackに通知します。 /.scss$/
プロパティは、ローダーが実行するアクションに対応します。ここでは、loader
、style
、css
をチェーンしています。およびresolve-url
逆の順序で実行されるローダー。
sass
が見つからないことを認めなければなりません構文は非常にエレガントです。結局のところ、プログラムの中で右から左に何かを読まなければならないのはいつですか。それにもかかわらず、ローダーはwebpackの非常に強力な機能です。実際、先ほど触れたローダーを使用すると、SASSファイルをJavaScriptに直接インポートできます。たとえば、ベンダーとグローバルスタイルシートをエントリポイントファイルにインポートできます。
loader3!loader2!loader1
同様に、ヘッダーコンポーネントにimport React from 'react'; import {render} from 'react-dom'; import {Router, browserHistory} from 'react-router'; import routes from './routes'; // CSS imports import '../node_modules/bootstrap/dist/css/bootstrap.css'; import '../node_modules/font-awesome/css/font-awesome.css'; import './styles/global.scss'; render(, document.getElementById('app'));
を追加できます。コンポーネントに関連付けられたスタイルシートをインポートします。これは、他のすべてのコンポーネントにも当てはまります。
私の意見では、これはJavaScript開発の世界における革命的な変化とほぼ見なすことができます。 ローダーがこれらすべてを処理するため、CSSのバンドル、ミニファイ、またはソースマップについて心配する必要はありません。ホットモジュールのリロードでさえ、CSSファイルで機能します。次に、同じファイルでJSとCSSのインポートを処理できるため、開発が概念的に単純になります。一貫性が高まり、コンテキストの切り替えが少なくなり、推論が容易になります。
この機能の仕組みを簡単に説明すると、WebpackはCSSをJSバンドルにインライン化します。実際、Webpackは画像やフォントに対してもこれを行うことができます。
import './Header.scss'
URLローダーはWebpackに、画像とフォントが100 KB未満の場合はデータURLとしてインライン化し、それ以外の場合は個別のファイルとして提供するように指示します。もちろん、カットオフサイズを10KBなどの別の値に構成することもできます。
これがWebpackの構成です。かなりの量のセットアップがあることは認めますが、それを使用することの利点は単に驚異的です。 Browserifyにはプラグインと変換がありますが、追加機能の点でWebpackローダーと比較することはできません。
このセットアップでは、タスクの自動化にgulpfileに依存するのではなく、npmスクリプトを直接使用します。
gif)(?S*)?$/, loader: 'url?limit=100000&name=img/[name].[ext]', ttf)(?S*)?$/, loader: 'url?limit=100000&name=fonts/[name].[ext]'
開発ビルドと本番ビルドを実行するには、'scripts': { 'start': 'npm-run-all --parallel lint:watch test:watch build', 'start:prod': 'npm-run-all --parallel lint test build:prod', 'clean-dist': 'rimraf ./dist && mkdir dist', 'clean-build': 'rimraf ./build && mkdir build', 'clean': 'npm-run-all clean-dist clean-build', 'test': 'mocha ./app/**/*.test.js --compilers js:babel-core/register', 'test:watch': 'npm run test -- --watch', 'lint': 'esw ./app/**/*.js', 'lint:watch': 'npm run lint -- --watch', 'server': 'nodemon server.js', 'server:prod': 'cross-env NODE_ENV=production nodemon server.js', 'build-html': 'node tools/buildHtml.js', 'build-html:prod': 'cross-env NODE_ENV=production node tools/buildHtml.js', 'prebuild': 'npm-run-all clean-dist build-html', 'build': 'webpack', 'postbuild': 'npm run server', 'prebuild:prod': 'npm-run-all clean-build build-html:prod', 'build:prod': 'cross-env NODE_ENV=production webpack', 'postbuild:prod': 'npm run server:prod' }
と入力しますそれぞれnpm start
。
これは、99〜150行のコードを19 NPMスクリプトに削減したことを考えると、gulpfileよりも確かにコンパクトです。本番スクリプトを除外した場合は12行になります(ほとんどの場合、ノード環境が本番環境に設定された開発スクリプトをミラーリングします)。 )。欠点は、これらのコマンドが、Gulpタスクの対応するコマンドと比較してやや不可解であり、表現力がそれほど高くないことです。たとえば、(少なくとも私が知っている限りでは)単一のnpmスクリプトで特定のコマンドを直列に実行し、他のコマンドを並列に実行する方法はありません。どちらか一方です。
ただし、このアプローチには大きな利点があります。 npm run start:prod
などのNPMライブラリを使用するコマンドラインから直接、それぞれに同等のGulpラッパーをインストールする必要はありません(この場合、mocha
)。
NPMをインストールする代わりに
次のパッケージをインストールします。
コーリーハウスの投稿を引用して、 NPMスクリプトのためにGulpとGruntを残した理由 :
私はGulpの大ファンでした。しかし、私の最後のプロジェクトでは、gulpfileに数百行と約12のGulpプラグインが含まれることになりました。 Gulpを使用して、Webpack、Browsersync、ホットリロード、Mochaなどを統合するのに苦労していました。どうして?さて、いくつかのプラグインは私のユースケースには不十分なドキュメントを持っていました。一部のプラグインは、必要なAPIの一部のみを公開していました。 1つには、少数のファイルしか監視しないという奇妙なバグがありました。コマンドラインに出力するときに別のストリップされた色。
彼はGulpの3つの主要な問題を指定しています。
私はこれらすべてに同意する傾向があります。
国境調整税対関税
gulp-mocha
などのライブラリはいつでも更新され、関連するeslint
ライブラリには対応する更新が必要です。ライブラリメンテナが興味を失った場合、ライブラリのgulpバージョンはネイティブバージョンと同期しなくなります。新しいライブラリが作成されるときも同じことが言えます。誰かがライブラリを作成した場合gulp-eslint
それに追いつくと、突然、対応するxyz
が必要になりますgulpタスクで使用するライブラリ。
ある意味で、このアプローチは拡張性がありません。理想的には、ネイティブライブラリを使用できるGulpのようなアプローチが必要です。
gulp-xyz
などのライブラリはこの問題を大幅に軽減するのに役立ちますが、それでもgulp-plumber
でのエラー報告は事実です。あまり役に立ちません。 1つのパイプでも未処理の例外がスローされると、ソースコードで問題の原因とはまったく関係がないと思われる問題のスタックトレースが取得されます。これにより、デバッグが悪夢になる場合があります。エラーが不可解であるか、誤解を招く可能性がある場合、GoogleやStackOverflowで検索しても実際には役に立ちません。
しばしば私はそれが小さいことに気づきますgulp
ライブラリのドキュメントは非常に限られている傾向があります。これは、著者が通常、主に自分で使用するためにライブラリを作成しているためだと思います。さらに、Gulpプラグインとネイティブライブラリ自体の両方のドキュメントを確認する必要があるのが一般的です。つまり、多くのコンテキスト切り替えと2倍の読み取りが必要になります。
それぞれのオプションには長所と短所がありますが、BrowserifyよりもWebpackの方が、GulpよりもNPMスクリプトの方が望ましいことは私にはかなり明らかです。 Gulpは確かにNPMスクリプトよりも表現力があり便利ですが、追加されたすべての抽象化で代償を払うことになります。
すべての組み合わせがアプリに最適であるとは限りませんが、圧倒的な数の開発依存関係と苛立たしいデバッグエクスペリエンスを回避したい場合は、NPMスクリプトを使用したWebpackが最適です。この記事が、次のプロジェクトに適したツールを選択するのに役立つことを願っています。
関連: