gulp進階構建項目由淺入深css
閱讀目錄html
Gulp.src(globs[,options]) node
gulp.dest(path[,options]) jquery
gulp-rename(重命名) web
gulp-uglify(JS壓縮) 正則表達式
gulp基本安裝和使用
Gulp的構建過程:gulp是使用nodejs中的stream(流),首先經過gulp.src()方法獲取到咱們須要的文件流(stream),而後把文件流經過pipe()方法把流導入到gulp的插件中,最後經過插件處理後的流再經過pipe()方法導入到gulp.dest()中,gulp.dest()方法把流中的內容寫入到文件中。
1. Gulp安裝:
首先咱們須要安裝nodejs,而後進行全局安裝;安裝以下:
sudo npm install gulp –g
全局安裝後,還須要切換到項目的根目錄下,單獨爲單個項目進行安裝下;安裝以下:
sudo npm install gulp
若是想在安裝的時候把gulp寫進package.json文件的依賴中,則能夠加上 –save-dev
sudo npm install –save-dev gulp
2. 如何使用gulp?
在項目的根目錄下新建一個 gulpfile.js文件,以後就能夠定義一個任務了;
好比以下簡單的任務:代碼以下:
var gulp = require('gulp'); gulp.task('default',function(){ console.log('hello world'); });
如今我項目的目錄結構假如是以下樣子:
最後咱們進行命令行切換到項目的根目錄下,運行gulp命令後,就能夠在控制檯看到consoe.log的打印的消息了;
gulp API介紹
最多見的咱們使用四個API,gulp.task() gulp.src() gulp.dest() gulp.watch();
該方法是獲取咱們須要的文件流,這個流裏面的內容不是原始的文件流,而是一個虛擬文件對象流(Vinyl files);該方法有2個參數
globs類型是 String 或 Array , 該文件流能夠是一個單獨的字符串形式,也能夠是一個數組形式;
options是一個對象類型;該對象類型有一個咱們經常使用的base字段配置 options.base是常常會使用的到;
好比以下代碼:
var gulp = require('gulp'); var uglify = require('gulp-uglify'); gulp.task("uglify-js",function(){ return gulp.src("src/js/*.js") .pipe(uglify()) .pipe(gulp.dest('build')); }); gulp.task('default',['uglify-js']); // 寫入到 build/a.js 和 build/index.js
以下目錄結構:
咱們使用base字段來繼續編寫以下代碼:
var gulp = require('gulp'); var uglify = require('gulp-uglify'); gulp.task("uglify-js",function(){ return gulp.src("src/js/*.js",{ base: 'src' }) .pipe(uglify()) .pipe(gulp.dest('build')); }); gulp.task('default',['uglify-js']);
咱們再在項目的根目錄下面運行gulp命令能夠看到項目的目錄結構變爲以下:
所以咱們能夠理解base字段爲相對於路徑來進行打包,最後生成 build/js/*.js文件;
看看Gulp用到的glob的匹配規則:
Gulp內部使用了node-glob模塊來實現文件匹配功能。該文件匹配相似於JS中的正則表達式;以下匹配:
* 匹配文件路徑中的0個或者多個字符,可是不會匹配路徑分隔符。好比: 能夠匹配 abc.js,x.js,aaa,abc/(路徑分隔符出如今末尾也能夠匹配);可是不能匹配相似於這樣的路徑分隔符 abc/aa.js
*.* 能夠匹配a.xxx; xxxx.yyyy;等
*/*/*.js 能夠匹配a/b/c.js,但不是不能匹配 a/b.js 或者 a/b/c/d.js
** 能夠匹配路徑中的0個或者多個目錄及其子目錄。好比:能匹配abc,a/b.js,
a/b/c.js,x/y/z等等;
**/*.js 可以匹配a.js , a/a.js,a/aaa/aaaa/a.js等等;也就是說只要以.js結尾的,無論前面有多少個文件或者分隔符均可以匹配;
a/**/z 能匹配a/z,a/b/z, a/b/c/z等等。
a/**b/z 能匹配a/b/z,a/sb/z, 不是不能匹配a/x/y/xxb/z;
?.js 能匹配a.js,b.js,c.js,至關於js正則裏面的同樣匹配0個或者1個,優先匹配;
[xyz].js 能匹配x.js,y.js,z.js,相似於js正則同樣,中括號中的任意一個字符;
[^xyz].js 除了中括號中的x,y,z中的其餘的任意一個字符;
當有多種匹配模式的時候可使用數組,以下:
gulp.src([‘js/*.js’,’css/*.css’]);
咱們也能夠排除一些文件,可使用!, 好比以下代碼:
gulp.src([‘js/*.js’,’css/*.css’,’!reset.css’]) ; 匹配全部的js/目錄下的js文件及匹配css/目錄下的css文件,可是不包括reset.css文件;可是不能把排除寫在第一個元素位置;
好比以下代碼: gulp.src([‘!reset.css’,’css/*.css’]); 這樣的是排除不掉的,這種方式咱們在css中能夠理解爲後面的代碼覆蓋前面的,所以須要寫在後面才能排除當前的;
該方法能夠理解爲把目標的源文件經過pipe方法導入到gulp插件中,最後把文件流寫到目標文件中;若是該文件不存在的話,則會自動建立它;好比上面的gulp.src(),目錄結構一剛開始build目錄是沒有的,經過打包後自動建立build文件夾;
字段path: 文件被寫入的路徑(輸出的目錄),也能夠傳入一個函數,在函數中返回相應的路徑。
字段options也是一個對象類型;
Gulp.dest(path) 生成的文件路徑是相對於gulp.src()中有通配符開始出現的那部分路徑。
好比以下代碼:
var gulp = require('gulp'); var uglify = require('gulp-uglify'); gulp.task("uglify-js",function(){ return gulp.src("src/js/*.js") .pipe(uglify()) .pipe(gulp.dest('build')); }); gulp.task('default',['uglify-js']);
gulp.src()上面有通配符的是 *.js, 所以最後生成的文件路徑是 build/*.js;
可是若是沒有出現通配符的狀況下,好比以下代碼:
gulp.src("src/js/a.js")
.pipe(gulp.dest('build'));
那麼最後生成的路徑就是 build/a.js 了;
固然咱們能夠在gulp.src()方法中配置base屬性,若是沒有配置base屬性的話,那麼默認生成的路徑就是相對於通配符出現的那部分路徑;若是設置了base屬性的話,那麼就相對於base的那個設置的路徑;假如如今的源目錄結構爲src/js/下游不少js文件
好比以下代碼:
var gulp = require('gulp'); var uglify = require('gulp-uglify'); gulp.task("uglify-js",function(){ return gulp.src("src/js/*.js",{ base: 'src' }) .pipe(uglify()) .pipe(gulp.dest('build')); }); gulp.task('default',['uglify-js']);
是相對於src文件下的,所以最後生成的路徑爲 build/js/*.js
該方法是定義一個任務;
name: 是任務的名字;
deps: {Array} 類型是數組類型;一個包含任務列表的數組,這些任務會在你當前任務運行以前完成;好比以下代碼:
gulp.task('mytask', ['one', 'two', 'task', 'names'], function() {
// 作一些事
});
好比上面的代碼,咱們想要完成'mytask'這個任務的話,首先會執行依賴數組中的那些任務,可是若是依賴任務裏面又使用了異步的方法,好比使用setTimeout這樣的時候,那麼這個時候,我再執行mytask這個任務的時候,就不會等待該依賴任務完成後再執行了;好比以下代碼:
var gulp = require('gulp'); gulp.task('one',function(){ //one是一個異步執行的任務 setTimeout(function(){ console.log('one is done') },5000); }); //two任務雖然依賴於one任務,但並不會等到one任務中的異步操做完成後再執行 gulp.task('two',['one'], function(){ console.log('two is done'); }); gulp.task('default',['two']);
上面的例子中咱們執行two任務時,會先執行one任務,但不會去等待one任務中的異步操做完成後再執行two任務,而是緊接着執行two任務。因此two任務會在one任務中的異步操做完成以前就執行了。
可是若是咱們想等待one任務中的setTimeout執行完成後,再執行two這個任務該怎麼辦呢?
咱們可使用以下方法,代碼以下:
var gulp = require('gulp'); gulp.task('one',function(fn){ // fn 爲任務函數提供的回調 用來通知該任務已經完成 //one是一個異步執行的任務 setTimeout(function(){ console.log('one is done'); fn(); },1000); }); //two任務雖然依賴於one任務,但並不會等到one任務中的異步操做完成後再執行 gulp.task('two',['one'], function(){ console.log('two is done'); }); gulp.task('default',['two']);
用來監聽文件的變化,當文件發生改變的時候,咱們能夠用它來執行相應的任務;
參數以下:
glob: 爲要監聽的文件匹配模式,規則和gulp.src中的glob相同;
opts: 爲一個可選的配置對象,通常不怎麼用;
tasks: 爲文件變化後要執行的任務,爲一個數組;
好比代碼以下:
gulp.task('two', function(){
console.log('two is done');
});
gulp.watch('js/**/*.js',['two'])
gulp一些經常使用插件
用來重命名文件流中的文件。
安裝:npm install --save-dev gulp-rename
好比以下代碼:
var gulp = require('gulp'), rename = require('gulp-rename'), uglify = require("gulp-uglify"); gulp.task('rename', function () { gulp.src('src/**/*.js') .pipe(uglify()) //壓縮 .pipe(rename('index.min.js')) .pipe(gulp.dest('build/js')); }); gulp.task('default',['rename']); //關於gulp-rename的更多強大的用法請參考https://www.npmjs.com/package/gulp-rename
安裝:npm install --save-dev gulp-uglify
仍是上面的gulpfile.js代碼以下:
var gulp = require('gulp'), rename = require('gulp-rename'), uglify = require("gulp-uglify"); gulp.task('rename', function () { gulp.src('src/**/*.js') .pipe(uglify()) //壓縮 .pipe(rename('index.min.js')) .pipe(gulp.dest('build/js')); }); gulp.task('default',['rename']);
安裝:npm install --save-dev gulp-minify-css
代碼以下:
var gulp = require('gulp'), minifyCss = require("gulp-minify-css"); gulp.task('minify-css', function () { gulp.src('src/**/*.css') // 要壓縮的css文件 .pipe(minifyCss()) //壓縮css .pipe(gulp.dest('build')); }); gulp.task('default',['minify-css']);
安裝:npm install --save-dev gulp-minify-html
代碼以下:
var gulp = require('gulp'), minifyHtml = require("gulp-minify-html"); gulp.task('minify-html', function () { gulp.src('src/**/*.html') // 要壓縮的html文件 .pipe(minifyHtml()) //壓縮 .pipe(gulp.dest('build')); }); gulp.task('default',['minify-html']);
安裝:npm install --save-dev gulp-concat
代碼以下:
var gulp = require('gulp'), concat = require("gulp-concat"); gulp.task('concat', function () { gulp.src('src/**/*.js') //要合併的文件 .pipe(concat('index.js')) // 合併匹配到的js文件並命名爲 "index.js" .pipe(gulp.dest('build/js')); }); gulp.task('default',['concat']);
安裝:npm install –save-dev gulp-less
Gulpfile.js代碼以下:
var gulp = require('gulp'), less = require("gulp-less"); gulp.task('compile-less', function () { gulp.src('src/less/*.less') .pipe(less()) .pipe(gulp.dest('build/css')); }); gulp.task('default',['compile-less']);
安裝:npm install –save-dev gulp-sass
代碼以下:
var gulp = require('gulp'), sass = require("gulp-sass"); gulp.task('compile-sass', function () { gulp.src('src/sass/*.sass') .pipe(sass()) .pipe(gulp.dest('build/css')); }); gulp.task('default',['compile-sass']);
安裝:npm install –save-dev gulp-imagemin
代碼以下:
var gulp = require('gulp'); var imagemin = require('gulp-imagemin'); gulp.task('uglify-imagemin', function () { return gulp.src('src/images/*') .pipe(imagemin()) .pipe(gulp.dest('build/images')); }); gulp.task('default',['uglify-imagemin']);
browserify是一個使用node支持的CommonJS模塊標準 來爲瀏覽器編譯模塊的,能夠解決模塊及依賴管理;
先來看看使用gulp常見的問題:
1. 使用 gulp 過程當中,偶爾會遇到 Streaming not supported 這樣的錯誤。這一般是由於常規流與 vinyl 文件對象流有差別、
gulp 插件默認使用了只支持 buffer (不支持 stream)的庫。好比,不能把 Node 常規流直接傳遞給 gulp 及其插件。
好比以下代碼:會拋出異常的;
var gulp = require('gulp'); var uglify = require('gulp-uglify'); var concat = require('gulp-concat'); var rename = require('gulp-rename'); var fs = require('fs'); gulp.task('bundle', function() { return fs.createReadStream('./test.txt') .pipe(uglify()) .pipe(rename('bundle.min.js')) .pipe(gulp.dest('dist/')); }); gulp.task('default',['bundle']);
gulp 選擇默認使用內容轉換成 buffer 的 vinyl 對象流,以方便處理。固然,設置 buffer: false 選項,可讓 gulp 禁用 buffer:
好比以下gulpfile.js代碼以下:
var gulp = require('gulp'); var fs = require('fs'); gulp.task('bundle', function() { return gulp.src('./src/js/app.js', {buffer: false}).on('data', function(file) { var stream = file.contents; stream.on('data', function(chunk) { console.log('Read %d bytes of data', chunk.length); }); }); }) gulp.task('default',['bundle']);
運行以下:
2. Stream 和 Buffer 之間轉換
基於依賴的模塊返回的是 stream, 以及 gulp 插件對 stream 的支持狀況,有時須要把 stream 轉換爲 buffer。好比不少插件只支持 buffer,如 gulp-uglify、使用時能夠經過 gulp-buffer 轉換:Stream轉換Buffer
以下gulpfile.js代碼:
var gulp = require('gulp'); var source = require('vinyl-source-stream'); var buffer = require('gulp-buffer'); var uglify = require('gulp-uglify'); var fs = require('fs'); gulp.task('bundle', function() { return fs.createReadStream('./src/js/app.js') .pipe(source('app.min.js')) // 常規流轉換成 vinyl 對象 .pipe(buffer()) .pipe(uglify()) .pipe(gulp.dest('dist/')); }) gulp.task('default',['bundle']);
以下所示:
3. 從 Buffer 到 Stream之間轉換
也能夠經過使用 gulp-streamify(https://www.npmjs.com/package/gulp-streamify) 或者 gulp-stream (https://www.npmjs.com/package/gulp-stream)插件,讓只支持 buffer 的插件直接處理 stream。
以下gulpfile.js代碼:
var gulp = require('gulp'); var wrap = require('gulp-wrap'); var streamify = require('gulp-streamify'); var uglify = require('gulp-uglify'); var gzip = require('gulp-gzip'); gulp.task('bundle', function() { return gulp.src('./src/js/app.js', {buffer: false}) .pipe(wrap('(function(){<%= contents %>}());')) .pipe(streamify(uglify())) .pipe(gulp.dest('dist')) .pipe(gzip()) .pipe(gulp.dest('dist')); }); gulp.task('default',['bundle']);
以下所示:
4. 使用browserify進行Stream 向 Buffer 轉換
vinyl-source-stream + vinyl-buffer
vinyl-source-stream(https://www.npmjs.com/package/vinyl-source-stream) : 將常規流轉換爲包含 Stream 的 vinyl 對象;
vinyl-buffer(https://www.npmjs.com/package/vinyl-buffer) 將 vinyl 對象內容中的 Stream 轉換爲 Buffer。
gulpfile.js代碼以下:
var browserify = require('browserify'); var gulp = require('gulp'); var uglify = require('gulp-uglify'); var source = require('vinyl-source-stream'); var buffer = require('vinyl-buffer'); gulp.task('browserify', function() { return browserify('./src/js/app.js') .bundle() .pipe(source('bundle.js')) // gives streaming vinyl file object .pipe(buffer()) // convert from streaming to buffered vinyl file object .pipe(uglify()) .pipe(gulp.dest('./dist/js')); }); gulp.task('default',['browserify']);
vinyl-source-stream 使用指定的文件名bundle.js建立了一個 vinyl 文件對象實例,所以能夠再也不使用 gulp-rename(gulp.dest 將用此文件名寫入結果)。
以下所示:
5. 使用browserify多文件操做
5-1. 使用Gulp和Browserify單個文件操做也能夠以下:
var gulp = require('gulp'); var browserify = require('browserify'); var source = require('vinyl-source-stream'); gulp.task('browserify', function(){ return browserify( {entrieis:['./src/js/app.js']}) .bundle() .pipe(source("bundle.js")) .pipe(gulp.dest('dist')); }); gulp.task('default',['browserify']);
5-2 多文件操做以下:
var gulp = require('gulp'); var source = require('vinyl-source-stream'); var browserify = require('browserify'); var glob = require('glob'); var es = require('event-stream'); gulp.task('browserify', function(done) { glob('./src/**/*.js', function(err, files) { if(err) { done(err) }; var tasks = files.map(function(entry) { return browserify({ entries: [entry] }) .bundle() .pipe(source(entry)) .pipe(gulp.dest('./dest')); }); es.merge(tasks).on('end', done); }) }); gulp.task('default',['browserify']);
5-3 也可使用gulp.src和browserify一塊兒使用;代碼以下:
var gulp = require('gulp'); var source = require('vinyl-source-stream'); var browserify = require('browserify'); var glob = require('glob'); var es = require('event-stream'); var buffer = require('vinyl-buffer'); gulp.task('browserify', function(done) { gulp.src('./src/**/*.js',function(err,files) { if(err) { done(err) } files.forEach(function(file){ return browserify({ entries: [file] }) .bundle() .pipe(source(file)) .pipe(buffer()) .pipe(gulp.dest('./dest')); }); }); }); gulp.task('default',['browserify']);
browserify深刻學習;
1.前言:
以前咱們作項目的時候,好比須要jquery框架的話,咱們可能須要下載jquery源碼,而後引入到咱們的項目中,以後在html文件中像以下引入便可:
<script src="path/to/jquery.js"></script>
2.bower學習
以後咱們學習了 Bower,所以咱們安裝了Bower,而後進入咱們的項目文件根目錄中 在命令行中使用bower安裝jquery;以下命令:
bower install jquery
以後會在咱們的根目錄中生成一個 bower_components文件,裏面包含了jquery文件,所以咱們須要在咱們的html文件中這樣引入jquery了;
<script src="bower_components/jquery/dist/jquery.js"></script>
以下所示:
3. npm&Browserify學習
咱們如今又可使用 命令行用npm安裝jQuery。進入項目的根目錄後,運行以下命令:
npm install --save-dev jquery
接着咱們在命令行中全局安裝 browserify;命令以下:
sudo npm install -g browserify
如今咱們就能夠在命令行中使用 browserify命令了;
好比如今我在個人項目目錄下的源文件 src/js/a.js 下想要使用jquery的話,咱們能夠在a.js代碼以下引用:
var $ = require('jquery'); $(function(){ // 獲取頁面中的DOM元素 console.log($("#jquery2")); }); function a() { console.log("a.js"); } a();
再進入命令行相對應的js文件中,進行以下命令:
browserify a.js -o dest.js
執行命令後會在同目錄下生成dest.js,該文件包含jquery.js和a.js;而後咱們把dest文件引入到咱們須要的html文件中便可訪問;
4. gulp和Browserify 一塊兒使用
結合gulp一塊兒使用時,咱們只須要把Browserify安裝到咱們的項目內便可;所以進入項目的根目錄中,進行以下命令安裝:
npm install --save-dev browserify
而後在項目的根目錄中在gulpfile.js文件中加入以下代碼:
var gulp = require("gulp"); var browserify = require("browserify"); var sourcemaps = require("gulp-sourcemaps"); var source = require('vinyl-source-stream'); var buffer = require('vinyl-buffer'); gulp.task("browserify", function () { var b = browserify({ entries: "./src/js/a.js", debug: true }); return b.bundle() .pipe(source("bundle.js")) .pipe(buffer()) .pipe(sourcemaps.init({loadMaps: true})) .pipe(sourcemaps.write(".")) .pipe(gulp.dest("./dist")); }); gulp.task('default',['browserify']);
a.js代碼仍是以下:
var $ = require('jquery'); $(function(){ // 獲取頁面中的DOM元素 console.log($("#jquery2")); }); function a() { console.log("a.js"); } a();
進入項目的根目錄中運行命令 gulp便可,在目錄中會生成dist目錄(包含bundle.js和bundle.js.map)兩個文件;以後在html文件頁面上引入
dist目錄文件下的bundle.js便可;
在上面的代碼中,debug: true是告知Browserify在運行同時生成內聯sourcemap用於調試。
若是咱們把debug設置成false的話;在瀏覽器中訪問頁面,能夠看到以下:
若是咱們把debug設置成true的話,在瀏覽器中訪問頁面,能夠看到以下:
引入gulp-sourcemaps並設置loadMaps: true是爲了讀取上一步獲得的內聯sourcemap,並將其轉寫爲一個單獨的sourcemap文件。
若是咱們把loadMaps設置成false的話,咱們在瀏覽器訪問頁面以下圖所示:
若是咱們把loadMaps設置成true的話,咱們在瀏覽器訪問頁面以下圖所示:
vinyl-source-stream用於將Browserify的bundle()的輸出轉換爲Gulp可用的[vinyl][](一種虛擬文件格式)流。
vinyl-buffer用於將vinyl流轉化爲buffered vinyl文件(gulp-sourcemaps及大部分Gulp插件都須要這種格式)。
若是代碼比較多,可能一次編譯須要很長時間。這個時候,咱們可使用[watchify][]。它能夠在你修改文件後,
只從新編譯須要的部分(而不是Browserify本來的所有編譯),這樣,只有第一次編譯會花些時間,此後的即時變動刷新則十分迅速。
以下代碼:
var watchify = require('watchify'); var browserify = require('browserify'); var gulp = require('gulp'); var source = require('vinyl-source-stream'); var buffer = require('vinyl-buffer'); var gutil = require('gulp-util'); var sourcemaps = require('gulp-sourcemaps'); var assign = require('lodash.assign'); // 在這裏添加自定義 browserify 選項 var customOpts = { entries: ['./src/js/a.js'], debug: true }; var opts = assign({}, watchify.args, customOpts); var b = watchify(browserify(opts)); // 在這裏加入變換操做 // 好比: b.transform(coffeeify); gulp.task('js', bundle); // 這樣你就能夠運行 `gulp js` 來編譯文件了 b.on('update', bundle); // 當任何依賴發生改變的時候,運行打包工具 b.on('log', gutil.log); // 輸出編譯日誌到終端 function bundle() { return b.bundle() // 若是有錯誤發生,記錄這些錯誤 .on('error', gutil.log.bind(gutil, 'Browserify Error')) .pipe(source('bundle.js')) // 可選項,若是你不須要緩存文件內容,就刪除 .pipe(buffer()) // 可選項,若是你不須要 sourcemaps,就刪除 .pipe(sourcemaps.init({loadMaps: true})) // 從 browserify 文件載入 map // 在這裏將變換操做加入管道 .pipe(sourcemaps.write('./')) // 寫入 .map 文件 .pipe(gulp.dest('./dist')); } gulp.task('default',['js']);
5. 使用Browserify來組織JavaScript文件
仍是上面那個項目,假如src/js文件內有2個js文件,分別爲a.js和b.js;假如如今a.js想引用b.js的模塊,就像seajs那樣經過require來引用如何作?
如今咱們能夠在b.js這樣編寫代碼;把咱們的代碼模塊經過module.exports 或 exports模塊對外提供接口,和其餘的好比seajs同樣編寫代碼
便可:好比如今b.js代碼以下:
function b() {
console.log("b.js");
}
module.exports = b;
那麼a.js代碼以下:
var b = require('./b');
function a() {
b();
console.log("a.js");
}
a();
而後再在gulpfile.js文件代碼仍是以下:
var gulp = require("gulp"); var browserify = require("browserify"); var sourcemaps = require("gulp-sourcemaps"); var source = require('vinyl-source-stream'); var buffer = require('vinyl-buffer'); gulp.task("browserify", function () { var b = browserify({ entries: "./src/js/a.js", debug: true }); return b.bundle() .pipe(source("bundle.js")) .pipe(buffer()) .pipe(sourcemaps.init({loadMaps: true})) .pipe(sourcemaps.write(".")) .pipe(gulp.dest("./dist")); }); gulp.task('default',['browserify']);
在命令行中運行gulp,便可生成bundle.js文件;引用該文件便可解決模塊依賴的問題;咱們打開bundle.js文件查看代碼以下:
(function e(t,n,r){ /* function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require; if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'"); throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e]; return s(n?n:e)},l,l.exports,e,t,n,r)} return n[o].exports}var i=typeof require=="function"&&require; for(var o=0;o<r.length;o++)s(r[o]);return s */ })({1:[function(require,module,exports){ var b = require('./b'); function a() { b(); console.log("a.js"); } a(); },{"./b":2}],2:[function(require,module,exports){ function b() { console.log("b.js"); } module.exports = b; },{}]},{},[1])
該函數有3個參數,
第一個參數是一個對象;第二個參數是一個空對象{};第三個參數是一個[1];
第一個參數是一個object;它的每個key都是一個數字,做爲模塊的id,每個數字key對應的值是長度爲2的數組。能夠看下,第一個key數字1
模塊中的數組中的第一個元素是a.js代碼;數組中第二個元素是a模塊的依賴項,第二個key數字2模塊中數組第一個元素是b.js代碼;數組中的第二個
元素是空對象{};由於b模塊沒有依賴項;所以爲{};
咱們的文件代碼經過一個匿名函數被包裝起來,這樣作的好處是:咱們的瀏覽器中並無require這樣的解決依賴的東西,可是咱們又想像seajs,
requireJS等同樣使用require來引入文件解決模塊依賴的文件的時候,所以 Browserify實現了require、module、exports這3個關鍵字。
第2個參數幾乎老是空的{}。它若是有的話,也是一個模塊map;
第3個參數是一個數組,指定的是做爲入口的模塊id。a.js是入口模塊,它的id是1,因此這裏的數組就是[1]。
那麼 Browserify是如何實現了require、module、exports這3個關鍵字的呢?
咱們前面被註釋的代碼將解析require、module、exports這三個3個參數,而後讓一切運行起來。
這段代碼是一個函數,來自於browser-pack項目的[prelude.js][]。
上面咱們看到在Browserify打包文件的時候,它會自動使用匿名函數進行包裝;所以咱們在編寫代碼的時候通常能夠不須要考慮全局變量的問題了;
不須要在函數中添加像類型匿名函數的結構 (function(){})();
gulp.watch()方法能夠監聽文件的動態修改,它接受一個glob或者glob數組(和gulp.src()同樣)以及一個任務數組來執行回調。下面咱們來看下
gulp.watch()方法的使用;好比如今gulpfile.js任務代碼以下:
var gulp = require('gulp'); var concat = require('gulp-concat'); var uglify = require('gulp-uglify'); var paths = { scripts: ['src/js/**/*.js'], css: ['src/css/**/*.css'], // 把源文件html放在src下,會自動打包到指定目錄下 html: ['src/html/**/*.html'] }; gulp.task('scripts', function() { return gulp.src(paths.scripts) .pipe(concat('all.js')) .pipe(uglify()) .pipe(gulp.dest('build/js')); }); gulp.task('css', function() { return gulp.src(paths.css) .pipe(concat('all.css')) .pipe(gulp.dest('build/css')); }); // 監聽html文件的改變 gulp.task('html',function(){ return gulp.src(paths.html) .pipe(gulp.dest('html/')); }); // Rerun the task when a file changes gulp.task('watch', function() { gulp.watch(paths.scripts, ['scripts']); gulp.watch(paths.css, ['css']); gulp.watch(paths.html, ['html']); }); // The default task (called when you run `gulp` from cli) gulp.task('default', ['scripts', 'css', 'html','watch']);
監聽src文件下的js和css及html的文件的變化,咱們在相關的項目根目錄命令行中運行gulp後,當咱們改變css或者js文件或html的時候,
能夠監聽文件的動態修改,所以保存刷新瀏覽器便可生效,這是gulp-watch的基本功能;以下所示:
會把src下的文件css和js自動打包到build下,src下的html文件會打包到項目根目錄下的html文件下;
上面的gulp.watch 回調函數有一個包含觸發回調函數信息的event對象:好比咱們把gulp watch任務改爲以下:
當每次更改文件的時候 都會觸發change事件;代碼改成以下:
gulp.task('watch', function() { var watch1 = gulp.watch(paths.scripts, ['scripts']); var watch2 = gulp.watch(paths.css, ['css']); var watch3 = gulp.watch(paths.html, ['html']); watch1.on('change', function (event) { console.log('Event type: ' + event.type); // Event type: changed console.log('Event path: ' + event.path); // Event path: /Users/tugenhua/gulp/src/css/a.css }); watch2.on('change', function (event) { console.log('Event type: ' + event.type); // Event type: changed console.log('Event path: ' + event.path); // Event path: /Users/tugenhua/gulp/src/css/a.css }); watch3.on('change', function (event) { console.log('Event type: ' + event.type); // Event type: changed console.log('Event path: ' + event.path); // Event path: /Users/tugenhua/gulp/src/css/a.css }); });
除了change事件,還能夠監聽不少其餘的事件:
end 在watcher結束時觸發
error 在出現error時觸發
ready 在文件被找到並正被監聽時觸發
nomatch 在glob沒有匹配到任何文件時觸發
Watcher對象也包含了一些能夠調用的方法:
watcher.end() 中止watcher(以便中止執行後面的任務或者回調函數)
watcher.files() 返回watcher監聽的文件列表
watcher.add(glob) 將與指定glob相匹配的文件添加到watcher
watcher.remove(filepath) 從watcher中移除個別文件
上面是經過gulp-watch來動態監聽html,css和js文件的改變,可是須要從新刷新頁面才能生效;
該插件的做用是當文件被修改的時候,它能實時刷新網頁,這樣的話就不須要咱們實時刷新了;可是該插件須要在咱們服務器下生效;所以
咱們須要使用 gulp-connect 建立一個服務器;下面是gulpfile.js代碼以下;使用liveReload實現實時刷新;
var gulp = require('gulp'); var connect = require('gulp-connect'); var uglify = require("gulp-uglify"); var concat = require("gulp-concat"); var mincss = require("gulp-minify-css"); //自動刷新 var livereload = require("gulp-livereload"); /* 設置路徑 */ var paths = { src : "src/", css : "src/css/", scripts : "src/js/", scss : "src/scss/", img : "src/images/", html : "src/html/", build : "build" } // 建立一個webserver 服務器 gulp.task('webserver', function() { connect.server({ port: 8000, livereload: true }); }); gulp.task('scripts', function() { return gulp.src(paths.scripts+ "**/*.js") .pipe(concat('all.js')) .pipe(uglify()) .pipe(gulp.dest(paths.build + '/js')); }); gulp.task('css', function() { return gulp.src(paths.css+ "**/*.css") .pipe(concat('all.css')) .pipe(mincss()) .pipe(gulp.dest(paths.build + '/css')); }); // 監聽html文件的改變 gulp.task('html',function(){ return gulp.src(paths.html + "**/*.html") .pipe(gulp.dest('html/')); }); //reload server gulp.task('reload-dev',['scripts','css','html'],function() { return gulp.src(paths.src + '**/*.*') .pipe(connect.reload()); }); // Watch gulp.task('watch', function() { //監聽生產環境目錄變化 gulp.watch(paths.src + '**/*.*',['reload-dev']); }) gulp.task('default', ['webserver','reload-dev','watch']);
BroserSync在瀏覽器中展現變化的功能與LiveReload很是類似,可是它有更多的功能。實現靜態服務器,也是能實時刷新頁面的;BrowserSync也能夠在不一樣瀏覽器之間同步點擊翻頁、表單操做、滾動位置等功能。
安裝以下命令:
npm install --save-dev browser-sync
以下gulpfile文件是動態監聽js,css和html文件的變化實時更新;以下代碼:
var gulp = require('gulp'); var connect = require('gulp-connect'); var uglify = require("gulp-uglify"); var concat = require("gulp-concat"); var mincss = require("gulp-minify-css"); //自動刷新 var browserSync = require('browser-sync').create(); var reload = browserSync.reload; /* 設置路徑 */ var paths = { src : "src/", css : "src/css/", scripts : "src/js/", scss : "src/scss/", img : "src/images/", html : "src/html/", build : "build" } gulp.task('scripts', function() { return gulp.src(paths.scripts+ "**/*.js") .pipe(concat('all.js')) .pipe(uglify()) .pipe(gulp.dest(paths.build + '/js')) .pipe(reload({stream:true})); // inject into browsers }); gulp.task('css', function() { return gulp.src(paths.css+ "**/*.css") .pipe(concat('all.css')) .pipe(mincss()) .pipe(gulp.dest(paths.build + '/css')) .pipe(reload({stream:true})); // inject into browsers }); // 監聽html文件的改變 gulp.task('html',function(){ return gulp.src(paths.html + "**/*.html") .pipe(gulp.dest('html/')) .pipe(reload({stream:true})); // inject into browsers }); // 建立本地服務器,而且實時更新頁面文件 gulp.task('browser-sync', ['scripts','css','html'],function() { var files = [ '**/*.html', '**/*.css', '**/*.js' ]; browserSync.init(files,{ server: { //baseDir: "./html" } }); }); //gulp.task('default', ['webserver','reload-dev','watch']); gulp.task('default', ['browser-sync'], function () { gulp.watch("**/*.css", ['css']); gulp.watch("**/*.html", ['html']); gulp.watch("**/*.js", ['scripts']); });
對 browser-sync 更多的學習 請看文檔(http://www.browsersync.cn/docs/api/)
gulp構建小型項目的基本過程
好比我如今一個小項目的基本架構以下所示:
src文件夾:是源文件的目錄結構;build文件夾是經過構建後生成的文件;
src存放文件以下:
common(該目錄是存放公用的插件css文件和js文件)
html目錄是存放目前的html文件
images目錄存放全部在項目中用到的圖片
js目錄是在項目中用到的全部的js文件;
less文件是存放須要預編譯成css文件;
這上面幾個目錄都會經過gulpfile.js打包到build目錄下;經過上面的學習browserify(能夠解決js的模塊化依賴問題)及學習 browserSync(實現自動刷新效果),所以目前該項目打包有2個優勢:
1. 可使用require,exports,和moudle這三個參數實現js模塊化組織及加載的問題,它不須要依賴於seajs或者requireJS;
2. 能夠實時監聽html,css,js文件的修改,從而不須要刷新頁面,能夠提升工做效率;
如今我把package.json用到的依賴包放到下面來:
{ "name": "testProject", "version": "0.0.1", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "devDependencies": { "browser-sync": "^2.12.10", "browserify": "^13.0.1", "event-stream": "^3.3.2", "glob": "^7.0.3", "gulp": "^3.9.1", "gulp-buffer": "0.0.2", "gulp-clean": "^0.3.2", "gulp-concat": "^2.6.0", "gulp-connect": "^4.0.0", "gulp-gzip": "^1.3.0", "gulp-imagemin": "^3.0.1", "gulp-less": "^3.1.0", "gulp-livereload": "^3.8.1", "gulp-marked": "^1.0.0", "gulp-minify-css": "^1.2.4", "gulp-rename": "^1.2.2", "gulp-sourcemaps": "^1.6.0", "gulp-str-replace": "0.0.4", "gulp-streamify": "^1.0.2", "gulp-uglify": "^1.5.3", "gulp-util": "^3.0.7", "gulp-watch": "^4.3.6", "gulp-wrap": "^0.13.0", "lodash.assign": "^4.0.9", "node-glob": "^1.2.0", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", "watchify": "^3.7.0" } }
項目用到的話,直接npm install 就能夠把全部的包加載進來;
gulpfile.js代碼以下:
var gulp = require('gulp'); var less = require('gulp-less'); var mincss = require('gulp-minify-css'); var concat = require("gulp-concat"); var uglify = require("gulp-uglify"); var clean = require('gulp-clean'); var browserify = require("browserify"); var sourcemaps = require("gulp-sourcemaps"); var source = require('vinyl-source-stream'); var buffer = require('vinyl-buffer'); var replace = require('gulp-str-replace'); var imagemin = require('gulp-imagemin'); //自動刷新 var browserSync = require('browser-sync').create(); var reload = browserSync.reload; var fs = require('fs'); var fileContent = fs.readFileSync('./package.json'); var jsonObj = JSON.parse(fileContent); var argv = process.argv.pop(); var DEBUGGER = (argv === "-D" || argv === "-d") ? true : false; /* 基礎路徑 */ var paths = { css : 'src/common/css/', less : 'src/less/', scripts : "src/js/", img : "src/images/", html : "src/html/", build : "build", src : 'src' } var resProxy = "項目的真實路徑"; var prefix = "項目的真實路徑"+jsonObj.name; if(DEBUGGER) { resProxy = "http://localhost:3000/build"; prefix = "http://localhost:3000/build"; } // 先清理文件 gulp.task('clean-css',function(){ return gulp.src(paths.build + "**/*.css") .pipe(clean()); }); gulp.task('testLess', ['clean-css'],function () { return gulp.src([paths.less + '**/*.less',paths.css+'**/*.css']) .pipe(less()) .pipe(concat('index.css')) .pipe(mincss()) .pipe(replace({ original : { resProxy : /\@{3}RESPREFIX\@{3}/g, prefix : /\@{3}PREFIX\@{3}/g }, target : { resProxy : resProxy, prefix : prefix } })) .pipe(gulp.dest(paths.build + "/css")) .pipe(reload({stream:true})); }); // 監聽html文件的改變 gulp.task('html',function(){ return gulp.src(paths.html + "**/*.html") .pipe(replace({ original : { resProxy : /\@{3}RESPREFIX\@{3}/g, prefix : /\@{3}PREFIX\@{3}/g }, target : { resProxy : resProxy, prefix : prefix } })) .pipe(gulp.dest(paths.build+'/html')) .pipe(reload({stream:true})); }); // 對圖片進行壓縮 gulp.task('images',function(){ return gulp.src(paths.img + "**/*") .pipe(imagemin()) .pipe(gulp.dest(paths.build + "/images")); }); // 建立本地服務器,而且實時更新頁面文件 gulp.task('browser-sync', ['testLess','html','browserify'],function() { var files = [ '**/*.html', '**/*.css', '**/*.less', '**/*.js' ]; browserSync.init(files,{ server: { //baseDir: "./html" } }); }); // 解決js模塊化及依賴的問題 gulp.task("browserify",function () { var b = browserify({ entries: ["./src/js/index.js"], debug: true }); return b.bundle() .pipe(source("index.js")) .pipe(buffer()) .pipe(sourcemaps.init({loadMaps: true})) .pipe(gulp.dest("./build/js")) .pipe(uglify()) .pipe(sourcemaps.write(".")) .pipe(replace({ original : { resProxy : /\@{3}RESPREFIX\@{3}/g, prefix : /\@{3}PREFIX\@{3}/g }, target : { resProxy : resProxy, prefix : prefix } })) .pipe(gulp.dest("./build/js")) .pipe(reload({stream:true})); }); gulp.task('default',['testLess','html','images','browserify'],function () { gulp.watch(["**/*.less","**/*.css"], ['testLess']); gulp.watch("**/*.html", ['html']); gulp.watch("**/*.js", ['browserify']); }); gulp.task('server', ['browser-sync','images'],function () { gulp.watch(["**/*.less","**/*.css"], ['testLess']); gulp.watch("**/*.html", ['html']); gulp.watch("**/*.js", ['browserify']); });
若是咱們在命令行中 運行gulp server -d 那就是開發環境,會自動打開一個服務器;若是運行gulp的話,就是線上的正式環境了;代碼會經過replace插件替換成線上的環境;
使用replace插件的好處能夠這樣引入文件
<script src="@@@PREFIX@@@/js/index.js"></script>
<link rel="stylesheet" href="@@@PREFIX@@@/css/index.css"/>
全部的圖片均可以使用這樣的@@@PREFIX@@@來引入的,這樣的話就能夠指向本地的環境和線上的環境了;方便開發;