フロントアセンブリの構築と自動化にはかなりの時間を費やしました。 このタスクは興味深いもので、話す価値があります。
コレクターができること:
- 開発および実稼働環境のフロントエンドプロジェクトを収集します。
- プロジェクトごとに複数のjs / cssバンドルを収集します。
- ブラウザでCommonJSモジュールのスタイルを使用します。
- ES6構文を使用します。
- スプライト、写真など。
入門
考えを簡単にするために、プロジェクトテンプレートを使用してリポジトリへのリンクをすぐにスローします: github.com/alexfedoseev/js-app-starter
入手方法
npmがインストールされていることを確認してください。
必要なグローバルモジュールをインストールします(まだインストールされていない場合):
リポジトリの分岐を作成します。
プロジェクトの依存関係をインストールします(リポジトリのルートで実行):
開発環境でプロジェクトをビルドし、ローカルサーバーを起動します。
ブラウザーを開き、 lvh.mehaps500に移動します
npm -v
必要なグローバルモジュールをインストールします(まだインストールされていない場合):
npm install -g gulp browserify babel jade stylus http-server
リポジトリの分岐を作成します。
git clone https://github.com/alexfedoseev/js-app-starter.git
プロジェクトの依存関係をインストールします(リポジトリのルートで実行):
npm install
開発環境でプロジェクトをビルドし、ローカルサーバーを起動します。
npm start
ブラウザーを開き、 lvh.mehaps500に移動します
コレクターとしてGulpを使用します。
アセンブリプロセスには何が含まれ、どの技術が使用されますか。
- HTMLアセンブリ
テンプレート: ジェイド - CSSアセンブリ
プリプロセッサ: スタイラス
プレフィックス: 自動プレフィックス - JSビルド
モジュラーシステム: Browserify + Babel (ES6トランスパイラー)
コード品質チェック: jsHint - 画像の最適化
オプティマイザー: Imagemin - 必要な場合:スプライトアセンブリ、json処理、フォントおよびその他のファイルのパブリックフォルダーへのコピー
スプライトコレクター: スプライトスミス
JSON処理: gulp-json-editor
一般的に、SlimとSassが好きですが、Ruby to Ruby、JS to JS:フロントエンドプロジェクトでは、npmのピースのみを使用します。 必要に応じて、任意のツールを交換できます。
プロジェクト構造
Github| dist/ | lib/ |-- gulp/ |-- helpers/ |-- tasks/ |-- config.js | node_modules/ | public/ |-- css/ |-- files/ |-- fonts/ |-- img/ |-- js/ |-- json/ |-- favicon.ico |-- index.html | src/ |-- css/ |-- files/ |-- fonts/ |-- html/ |-- img/ |-- js/ |-- json/ |-- sprite/ |-- favicon.ico | .gitignore | .npmignore | gulpfile.js | npm-shrinkwrap.json | package.json
.gitignore & .npmignore
これらのファイルの中には、コミットと公開時にgitとnpmが無視するもののリストがあります。
node_modules /
npmを介してインストールするすべてのモジュールは、このディレクトリに分類されます。
npm-shrinkwrap.json
node_modules /の内容をリポジトリに保持しません。 代わりに、このファイルを介してすべての依存関係を送信しています。 コマンド`npm shrinkwrap`によって自動的に生成されます。
package.json
これは、グローバルプロジェクト設定を含むファイルです。 彼に戻ります。
gulpfile.js
通常、プロジェクトをビルドするためにすべてのタスクがここに保存されますが、今回の場合は、環境変数の値を決定するだけで、gulpタスクが含まれるフォルダーにさらにスローされます。
lib / gulp /
ここに、コレクターのすべての設定とタスクを保存します。
|-config.js
タスク自体の編集を最小限に抑えるために、すべてのタスクの設定を別のファイルに取り出します。
|-ヘルパー/
コレクターのヘルパーメソッド。
|-タスク/
そして、gulpタスク自体。
src /
プロジェクトのソース。
公開/
アセンブリの結果。 このフォルダーのすべてのコンテンツはコレクターによって生成され、新しいアセンブリが完全にクリアされる前に、ここには何も保存されません。
dist /
時々私はオープンソースのモジュールを書きます。 このフォルダーには、アセンブリ後に、書かれたjsライブラリーの通常バージョンと縮小バージョンがあります。 public /ディレクトリは、デモのリポジトリとして使用されます。 通常のサイトまたはランディングページを作成している場合、それらは必要ありません。
プロジェクトのセットアップ
package.json
これは、グローバルプロジェクト設定が保存されるファイルです。
彼の内部の詳細な説明はここにあります: browsenpm.org/package.json
以下では、いくつかの重要な部分のみに焦点を当てます。
{ // "name": "js-app-starter", // // / js+css "version": "0.0.1", // js-, , // `require('your-lib')` "main": "./dist/app.js", // browserify // , ES6 ES5 "browserify": { "transform": [ "babelify" ] }, // ( ) "scripts": { "start": "NODE_ENV=development http-server -a lvh.me -p 3500 & gulp", "build": "NODE_ENV=production gulp build" }, // jshint ( ) "lintOptions": { "esnext": true ... }, // Frontend "dependencies": { "jquery": "^2.1.3" ... }, // Development "devDependencies": { "gulp": "^3.8.11" ... } }
Github
コンソールコマンド
package.jsonでは、コンソールコマンドのエイリアスを指定できます。エイリアスは、開発プロセス中に頻繁に実行されます。
"scripts": { "start": "NODE_ENV=development http-server -a lvh.me -p 3500 & gulp", "build": "NODE_ENV=production gulp build" }
開発アセンブリ
プロジェクトで作業を開始する前に、次のものが必要です。
- ソースからコンパイルします(デバッグ用のソースマップを使用)
- ソースファイルを変更するときにプロジェクトを再構築する「オブザーバー」を起動します
- ローカルサーバーを起動
# , npm start # NODE_ENV=development http-server -a lvh.me -p 3500 & gulp
部品に分解する
# NODE_ENV=development # lvh.me 3500 http-server -a lvh.me -p 3500 # gulp gulp
生産組立
プロジェクトをリリースする準備ができたら、生産アセンブリを行います。
# Ctrl+C, , # , npm run build # NODE_ENV=production gulp build
部品に分解する
# NODE_ENV=production # gulp- `build` gulp build
ガルプ
Gulpに渡します。 タスクの構造は、 Dan Telloのコレクターから取得されます。
ダイビングの前に、通常のgulpタスクの実行順序に関する短いコメント:
var gulp = require('gulp'); gulp.task('task_1', ['pre_task_1', 'pre_task_2'], function() { console.log('task_1 is done'); }); // `task_1`, `task_1 is done` // `gulp task_1` // `task_1` `['pre_task_1', 'pre_task_2']` // , 'pre_task_1' & 'pre_task_2' - , // , // `task_1` , 2 pre- -
次に、何をどの順序で収集するかを考えてみましょう。
開発アセンブリ
`npm start`はgulp コマンドを実行します。 次に何が起こるか:
- Gulpは、現在のディレクトリでgulpfile.jsを探します。 通常、すべてのタスクがその中にスタックされますが、ここでは、彼は単に環境変数の値を決定し、gulpタスクを含むフォルダーにさらに投げ込みます。
コメント付きのコード/* file: gulpfile.js */ // , var requireDir = require('require-dir'); // , // development & production global.devBuild = process.env.NODE_ENV !== 'production'; // requireDir('./lib/gulp/tasks', { recurse: true });
- ディレクトリに転送された後、コレクターは`default`という名前のタスクを検索し、最初にソース上で「オブザーバー」を起動してから、
- `public /`および`dist /`フォルダをクリアします
- lintit jsファイル
- スプライトを収集します
その後、プロジェクトが組み立てられます(html、css、jsなど)。
コメント付きのコードデフォルト
/* file: lib/gulp/tasks/default.js */ var gulp = require('gulp'); // `default`, `watch` gulp.task('default', ['watch']);
見る
/* file: lib/gulp/tasks/watch.js */ var gulp = require('gulp'), finder = require('../helpers/finder'), // config = require('../config'); // // `watch`, `watching` & `build` gulp.task('watch', ['watching', 'build'], function() { // `css`, `images` & `html` // gulp gulp.watch(finder(config.css.src), ['css']); gulp.watch(finder(config.images.src), ['images']); gulp.watch(finder(config.html.src), ['html']); }); gulp.task('watching', function() { // `isWatching`, // , global.isWatching = true; });
建てる
/* file: lib/gulp/tasks/build.js */ var gulp = require('gulp'); // `build`, : // `clean` - `public/` & `dist/` // `lint` - jshint js- ( ) // `sprite` - gulp.task('build', ['clean', 'lint', 'sprite'], function() { // , , `bundle` // `gulp.start` deprecated, // sync/async Gulp 4.0, // gulp.start('bundle'); }); // gulp.task('bundle', ['scripts', 'css', 'images', 'html', 'copy'], function() { // dev-, `doBeep` = true // `notifier` // ( ) if (devBuild) global.doBeep = true; });
生産組立
すべてが彼女の方が簡単です。 「npm run build」は、「 gulp build」コマンドを起動します。このコマンドは、ターゲットフォルダーlint js-codeをクリアし、スプライトを収集してから、プロジェクトをビルドします(ソースマップなし)。 上記のコメント付きのコード。
Gulpタスク設定ファイル
すべての主要なタスク構成は、個別のlib / gulp / config.jsファイルに移動されます 。
/* file: lib/gulp/config.js */ var pkg = require('../../package.json'), // package.json bundler = require('./helpers/bundler'); // /* */ var _src = './src/', // _dist = './dist/', // _public = './public/'; // var _js = 'js/', // javascript _css = 'css/', // css _img = 'img/', // _html = 'html/'; // html /* * js / css * * : app.js, app.css - * admin.js, admin.css - * * : your-lib.js - * your-lib.jquery.js - jquery- * */ var bundles = [ { name : 'app', // global : 'app', // , , compress : true, // ? saveToDist : true // `/dist`? (true - , false - ) } ]; module.exports = { /* */ };
Github
HTMLアセンブリ
テンプレートにはJadeを使用します。 パーシャルの挿入、インラインjavascript、変数、ミックスイン、その他多くのクールなものを使用できます。
ガルプ
構成
タスク
/* file: lib/gulp/config.js */ html: { src: _src + _html, // jade- dest: _public, // params: { // jade pretty: devBuild, // html? locals: { // , pkgVersion: pkg.version // `pkgVersion` } } }
Github
タスク
/* file: lib/gulp/tasks/html.js */ var gulp = require('gulp'), jade = require('gulp-jade'), jadeInherit = require('gulp-jade-inheritance'), gulpif = require('gulp-if'), changed = require('gulp-changed'), filter = require('gulp-filter'), notifier = require('../helpers/notifier'), config = require('../config').html; gulp.task('html', function(cb) { // jade- src/html gulp.src(config.src + '*.jade') // dev-, watcher .pipe(gulpif(devBuild, changed(config.dest))) // .pipe(jadeInherit({basedir: config.src})) // - ( `_` ) .pipe(filter(function(file) { return !/\/_/.test(file.path) || !/^_/.test(file.relative); })) // jade html .pipe(jade(config.params)) // html- .pipe(gulp.dest(config.dest)) // .on('end', function() { notifier('html'); // ( + ) cb(); // gulp-callback, }); });
Github
ソースコード
Src / htmlフォルダー構造
すべてのパーシャルは `_`(アンダースコア)で始まるため、アセンブリ中にそれらをフィルターして無視できます。
ヘルパー/ _variables.jade
必要なパラメーターを変数に保存します。 たとえば、電話がページの複数の場所にある場合、変数に保存してテンプレートで使用することをお勧めします。
ヘルパー/ _mixins.jade
頻繁に使用されるブロックは、ミックスインでラップできます。
index.jade
メインページのスケルトン。
meta / _head.jade
ヘッドコンテンツ。
| src |-- html |-- index.jade # |-- components/ # |-- _header.jade |-- helpers/ # , |-- _params.jade |-- _mixins.jade |-- meta/ # head, . |-- _head.jade
Github
すべてのパーシャルは `_`(アンダースコア)で始まるため、アセンブリ中にそれらをフィルターして無視できます。
ヘルパー/ _variables.jade
必要なパラメーターを変数に保存します。 たとえば、電話がページの複数の場所にある場合、変数に保存してテンプレートで使用することをお勧めします。
/* file: src/html/helpers/_variables.jade */ - var release = pkgVersion // gulp- - var phone = '8 800 CALL-ME-NOW' //
Github
ヘルパー/ _mixins.jade
頻繁に使用されるブロックは、ミックスインでラップできます。
/* file: src/html/helpers/_mixins.jade */ mixin phoneLink(phoneString) - var cleanPhone = phoneString.replace(/\(|\)|\s|\-/g, '') a(href="tel:#{cleanPhone}")= phoneString // // +phoneLink(phone)
Github
index.jade
メインページのスケルトン。
/* file: src/html/index.jade */ include helpers/_variables // include helpers/_mixins // doctype html html head include meta/_head body include components/_header include components/_some_component include components/_footer
Github
meta / _head.jade
ヘッドコンテンツ。
/* file: src/html/meta/_head.jade */ meta(charset="utf-8") ... // , js/css link(rel="stylesheet" href="css/app.min.css?v=#{release}") script(src="js/app.min.js?v=#{release}") ...
Github
JavaScriptアセンブリ
Browserifyはモジュラーシステムとして使用します。 これにより、ブラウザでCommonJSモジュールを直接接続するスタイルを使用できます。 さらに、ES6構文を使用できるようになりました。Browselifyがjsをビルドする前に、 BabelはそれをES5に変換します。 ビルドする前に、jsHintを実行してコードの品質を確認します。
Browserifyには1つの欠点があります。外部依存関係(jQueryプラグインなど)を使用してライブラリを作成すると、正しいUMDラッパーを作成できなくなります。 この場合、Browserifyを連結に置き換え、手でラッパーを記述します。
バンドルについて
プロジェクトでは、いくつかのjs / cssのセットを作成する必要がある場合があります。
たとえば、front + adminと記述します。 または、2つのバージョンのライブラリ:依存関係のない、jQueryプラグインの形式。 これらのアセンブリは分離する必要があります。 これを行うには、コレクターの設定で、配列を作成します。
js / cssコレクターは、対応するエンドポイントファイル( `app.js`または` app.styl`)のjs / cssソースを含むフォルダーを検索します。 このエンドポイントファイルを通じて、すべてのバンドルの依存関係を管理します。 以下にその構造を示します。
バンドルをコレクターに渡す前に、設定でオブジェクトを形成する`bundler`ヘルパーに配列を渡します。
たとえば、front + adminと記述します。 または、2つのバージョンのライブラリ:依存関係のない、jQueryプラグインの形式。 これらのアセンブリは分離する必要があります。 これを行うには、コレクターの設定で、配列を作成します。
/* file: lib/gulp/config.js */ /* */ var bundles = [ { name : 'myLib', // global : 'myLib', // , compress : true, // ? ( ) saveToDist : true // `/dist`? } ]; /* / */ var bundles = [ { name : 'app', // global : false, // compress : true, // ? saveToDist : false // `/dist`? }, name : 'admin', global : false, compress : true, saveToDist : false } ];
Github
js / cssコレクターは、対応するエンドポイントファイル( `app.js`または` app.styl`)のjs / cssソースを含むフォルダーを検索します。 このエンドポイントファイルを通じて、すべてのバンドルの依存関係を管理します。 以下にその構造を示します。
バンドルをコレクターに渡す前に、設定でオブジェクトを形成する`bundler`ヘルパーに配列を渡します。
ガルプ
構成
タスク
/* file: lib/gulp/config.js */ scripts: { bundles: bundler(bundles, _js, _src, _dist, _public), // banner: '/** ' + pkg.name + ' v' + pkg.version + ' **/\n', // min.js extensions: ['.jsx'], // lint: { // jshint options: pkg.lintOptions, dir: _src + _js } }
Github
タスク
/* file: lib/gulp/tasks/scripts.js */ var gulp = require('gulp'), browserify = require('browserify'), watchify = require('watchify'), uglify = require('gulp-uglify'), sourcemaps = require('gulp-sourcemaps'), derequire = require('gulp-derequire'), source = require('vinyl-source-stream'), buffer = require('vinyl-buffer'), rename = require('gulp-rename'), header = require('gulp-header'), gulpif = require('gulp-if'), notifier = require('../helpers/notifier'), config = require('../config').scripts; gulp.task('scripts', function(cb) { // - var queue = config.bundles.length; // , , // bundle- // var buildThis = function(bundle) { // bundle browserify var pack = browserify({ // sourcemaps cache: {}, packageCache: {}, fullPaths: devBuild, // end-point (app.js) entries: bundle.src, // , // browserify UMD- // bundle.global standalone: bundle.global, // extensions: config.extensions, // sourcemaps? debug: devBuild }); // var build = function() { return ( // browserify- pack.bundle() // browserify- vinyl .pipe(source(bundle.destFile)) // , `require` .pipe(derequire()) // dev-, `public/` ( - )) .pipe(gulpif(devBuild, gulp.dest(bundle.destPublicDir))) // `dist` - .pipe(gulpif(bundle.saveToDist, gulp.dest(bundle.destDistDir))) // sourcemaps .pipe(gulpif(bundle.compress, buffer())) // dev- — sourcemaps .pipe(gulpif(bundle.compress && devBuild, sourcemaps.init({loadMaps: true}))) // .pipe(gulpif(bundle.compress, uglify())) // `.min` .pipe(gulpif(bundle.compress, rename({suffix: '.min'}))) // production - .pipe(gulpif(!devBuild, header(config.banner))) // sourcemaps .pipe(gulpif(bundle.compress && devBuild, sourcemaps.write('./'))) // `/dist` .pipe(gulpif(bundle.saveToDist, gulp.dest(bundle.destDistDir))) // `public` .pipe(gulp.dest(bundle.destPublicDir)) // callback handleQueue ( ) .on('end', handleQueue) ); }; // watchers if (global.isWatching) { // browserify- watchify pack = watchify(pack); // - pack.on('update', build); } // var handleQueue = function() { // , notifier(bundle.destFile); // if (queue) { // 1 queue--; // , , if (queue === 0) cb(); } }; return build(); }; // config.bundles.forEach(buildThis); });
Github
ソースコード
Src / jsフォルダー構造
app.js
このファイルを通じて、jsコンポーネントのすべての依存関係と実行順序を駆動します。 ファイル名はバンドルの名前と一致する必要があります。
| src/ |-- js/ |-- components/ # |-- helpers/ # js- |-- app.js # end-point
Github
app.js
このファイルを通じて、jsコンポーネントのすべての依存関係と実行順序を駆動します。 ファイル名はバンドルの名前と一致する必要があります。
/* file: src/js/app.js */ /* Vendor */ import $ from 'jquery'; /* Components */ import myComponent from './components/my-component'; /* App */ $(document).ready(() => { myComponent(); });
Github
npmに依存関係がない場合の対処方法
このような場合、 browserify-shimを使用します。これは、通常のライブラリをCommonJS互換モジュールに変換できるプラグインです。 それで、 nQueryにはないjQueryプラグイン`maskedinput`があります。
変換を「package.json」に追加し、依存関係設定を設定します。
その後、モジュールを接続できます。
変換を「package.json」に追加し、依存関係設定を設定します。
/* file: package.json */ "browserify": { "transform": [ "babelify", "browserify-shim" // ] }, // `browserify-shim` // github: https://github.com/thlorenz/browserify-shim "browser": { "maskedinput": "./path/to/jquery.maskedinput.js" }, "browserify-shim": { "maskedinput": { "exports": "maskedinput", "depends": [ "jquery:jQuery" ] } }
その後、モジュールを接続できます。
require('maskedinput');
CSSアセンブリ
プリプロセッサとしてStylusを使用します。 さらに、ベンダープレフィックスを手で登録しないように、css 自動プレフィックスを使用します。
ガルプ
構成
タスク
/* file: lib/gulp/config.js */ css: { bundles: bundler(bundles, _css, _src, _dist, _public), // src: _src + _css, // watcher params: {}, // stylus - autoprefixer: { // autoprefixer browsers: ['> 1%', 'last 2 versions'], // cascade: false // , }, compress: {} // - }
Github
タスク
/* file: lib/gulp/tasks/css.js */ var gulp = require('gulp'), process = require('gulp-stylus'), prefix = require('gulp-autoprefixer'), compress = require('gulp-minify-css'), gulpif = require('gulp-if'), rename = require('gulp-rename'), notifier = require('../helpers/notifier'), config = require('../config').css; /* css- js- */ gulp.task('css', function(cb) { var queue = config.bundles.length; var buildThis = function(bundle) { var build = function() { return ( gulp.src(bundle.src) .pipe(process(config.params)) .pipe(prefix(config.autoprefixer)) .pipe(gulpif(bundle.compress, compress(config.compress))) .pipe(gulpif(bundle.compress, rename({suffix: '.min'}))) .pipe(gulp.dest(bundle.destPublicDir)) .on('end', handleQueue) ); }; var handleQueue = function() { notifier(bundle.destFile); if (queue) { queue--; if (queue === 0) cb(); } }; return build(); }; config.bundles.forEach(buildThis); });
Github
ソースコード
SRC / CSSフォルダー構造
app.styl
このファイルを介して、cssコンポーネントが接続される順序を制御します。 ファイル名はバンドルの名前と一致する必要があります。
| src/ |-- css/ |-- components/ # |-- header.styl |-- footer.styl |-- globals/ |-- fonts.styl # |-- global.styl # |-- normalize.styl # / |-- variables.styl # |-- z-index.styl # z- |-- helpers/ |-- classes.styl # |-- mixins.styl # |-- sprite/ |-- sprite.json # json, gulp.spritesmith |-- sprite.styl # json css- |-- vendor/ # css |-- app.styl # end-point
Github
app.styl
このファイルを介して、cssコンポーネントが接続される順序を制御します。 ファイル名はバンドルの名前と一致する必要があります。
/* file: src/css/app.styl */ @import "helpers/mixins" @import "helpers/classes" @import "globals/variables" @import "globals/normalize" @import "globals/z-index" @import "globals/fonts" @import "globals/global" @import "sprite/sprite" @import "vendor/*" @import "components/*"
Github
他のすべてのタスク-写真、スプライト、クリーニングなど-は、追加のコメントを必要としません(実際、私は既に落書きにうんざりしています)。 ソースはリポジトリにあります: github.com/alexfedoseev/js-app-starter
妨害や追加がある場合-ここでのコメントまたはGithubでの問題 /プルリクエストを通じてフィードバックをお待ちしております。 頑張って!