摘要:本文特此給大家介紹下如何使用配合來構(gòu)建基于的前端項(xiàng)目。最后,在目錄下會(huì)生成最終的項(xiàng)目文件。執(zhí)行單元測試本例中使用進(jìn)行單元測試。
隨著React、Angular2、Redux等前沿的前端框架越來越流行,使用webpack、gulp等工具構(gòu)建前端自動(dòng)化項(xiàng)目也隨之變得越來越重要。鑒于目前業(yè)界普遍更流行使用webpack來構(gòu)建es6(ECMAScript 2015)前端項(xiàng)目,網(wǎng)上的相關(guān)教程也比較多;相對來說使用gulp來構(gòu)建es6項(xiàng)目的中文教程就比較少。
經(jīng)過一段時(shí)間的摸索,我覺得其實(shí)使用gulp也可以很方便地構(gòu)建es6項(xiàng)目。以下是我感覺gulp和webpack主要的不同之處:
gulp的任務(wù)機(jī)制和流式管道函數(shù)和webpack的配置參數(shù)風(fēng)格有著顯著區(qū)別,它能使開發(fā)者更清晰地了解項(xiàng)目的構(gòu)建流程。
由于gulp是編程式風(fēng)格的,所以使用起來可定制化的功能也就更靈活一些,可應(yīng)對一些構(gòu)建過程較為復(fù)雜的情況。
本文特此給大家介紹下如何使用gulp配合browserify來構(gòu)建基于es6的前端項(xiàng)目。
Browserify vs Webpackbrowserify與webpack都是當(dāng)下流行的commonjs模塊(或es6模塊)合并打包工具,打包后的js文件可以直接運(yùn)行在瀏覽器環(huán)境中。
很多人都知道,webpack功能全面,可以對js、css、甚至圖片、字體文件統(tǒng)一進(jìn)行合并打包,并且插件豐富。而browserify的特點(diǎn)是職責(zé)單一,只負(fù)責(zé)js模塊合并打包,有些項(xiàng)目也并不需要將css等資源文件和js打包在一起的功能;它的代碼風(fēng)格也類似管道函數(shù),和gulp的契合度較高;在github上也可以找到相當(dāng)多的browserify插件,如熱替換、代碼分割等等。
示例項(xiàng)目有一篇文章對browserify和webpack的對比進(jìn)行了探討:webpack 跟 browserify 比到底有什么好?
本文中使用的示例項(xiàng)目是我為重構(gòu)過去搞的UI組件庫而建的項(xiàng)目,使用browserify構(gòu)建的分支地址請戳這里。這個(gè)項(xiàng)目目前已改用gulp+webpack構(gòu)建,但是保留了原先用browserify構(gòu)建的分支代碼可供參考。
項(xiàng)目結(jié)構(gòu)目錄項(xiàng)目目錄
dist (生產(chǎn)代碼目錄,存放生成合并后的各類文件)
js
構(gòu)建出的項(xiàng)目js文件
fonts
...
css
構(gòu)建出的項(xiàng)目css文件
examples (示例目錄)
src (開發(fā)代碼目錄)
styles (樣式文件目錄)
base.js (打包入口文件)
...
test (單元測試目錄)
vendor (第三方依賴庫)
babelHelpers.js
...
gulpfile.js (gulp配置文件)
package.json
LICENSE
README.md
示例項(xiàng)目目錄大體如上所示,其中使用babel進(jìn)行es6至es5轉(zhuǎn)換,并使用eslint進(jìn)行js代碼檢驗(yàn)。大家看到這里可能有疑問,為什么項(xiàng)目中沒有babel及eslint的配置文件.babelrc和.eslintrc呢?原因就是這些配置文件里的內(nèi)容其實(shí)是可以直接配置在gulpfile.js中的相關(guān)插件內(nèi)的。
配置package.json在這里只列出項(xiàng)目依賴的各種包,大致分為如下幾類:
{ ... "devDependencies": { /*browserify包及相關(guān)插件*/ "browserify": "^13.0.0", "vinyl-buffer": "^1.0.0", "vinyl-source-stream": "^1.1.0", "standalonify": "^0.1.3", /*babel相關(guān)插件*/ "babelify": "^7.2.0", "babel-plugin-external-helpers": "^6.4.0", "babel-plugin-transform-es2015-classes": "^6.5.2", "babel-plugin-transform-es2015-modules-commonjs": "^6.5.2", "babel-plugin-transform-object-assign": "^6.3.13", "babel-preset-es2015": "^6.3.13", "babel-preset-react": "^6.3.13", "babel-preset-stage-0": "^6.3.13", /*eslint相關(guān)插件*/ "babel-eslint": "^5.0.0", "estraverse": "^4.2.0", "estraverse-fb": "^1.3.1", /*gulp包及相關(guān)插件*/ "gulp": "^3.9.0", "gulp-clean": "^0.3.1", "gulp-concat": "^2.6.0", "gulp-cssnano": "^2.1.1", "gulp-eslint": "^2.0.0", "gulp-if": "^2.0.0", "gulp-jasmine": "^2.2.1", "gulp-less": "^3.0.5", "gulp-rename": "^1.2.2", "gulp-sequence": "^0.4.4", "gulp-uglify": "^1.5.1", /*postcss相關(guān)插件*/ "gulp-postcss": "^6.1.0", "autoprefixer": "^6.3.4", /*外部依賴包*/ "nornj": "^0.3.0", "react": "^0.14.8", "react-dom": "^0.14.8", /*其他依賴包*/ "jsdom": "^8.1.0", "yargs": "^4.2.0", ... }, ... }編寫gulpfile.js
gulpfile.js即為gulp的配置文件,其作用類似于webpack的webpack.config.js文件。在代碼風(fēng)格方面,與webpack.config.js的配置參數(shù)風(fēng)格不同的是,gulpfile.js更偏向編程風(fēng)格。gulpfile.js整體結(jié)構(gòu)如下所示:
//引入依賴的各種包: var gulp = require("gulp"), browserify = require("browserify"), ... //定義一些全局函數(shù)及變量 function getJsLibName() { ... } ... //定義各種任務(wù) gulp.task("build-all-js", ...); gulp.task("build-all-css", ...); gulp.task("build", ["build-all-js", "build-all-css", ...]); ... //定義默認(rèn)任務(wù) gulp.task("default", ["build"]);
使用gulp需要定義各種任務(wù)來處理各類文件的構(gòu)建生成。如例中所示,定義build-all-js任務(wù)來構(gòu)建js文件,執(zhí)行任務(wù)時(shí)須輸入命令:
gulp build-all-js
可以定義一個(gè)默認(rèn)任務(wù),一般在這個(gè)任務(wù)里依次執(zhí)行全部子任務(wù),執(zhí)行時(shí)輸入命令:
gulp
使用Browserify進(jìn)行js模塊合并關(guān)于gulp基礎(chǔ)使用方法的更多細(xì)節(jié)大家可以參考這篇文章:前端構(gòu)建工具gulpjs的使用介紹及技巧
配合gulp使用browserify需要引入的包:
var browserify = require("browserify"), source = require("vinyl-source-stream"), buffer = require("vinyl-buffer"), standalonify = require("standalonify"), argv = require("yargs").argv;
創(chuàng)建gulp任務(wù)build-js:
gulp.task("build-js", function () { return browserify({ entries: "./src/base.js" //指定打包入口文件 }) .plugin(standalonify, { //使打包后的js文件符合UMD規(guī)范并指定外部依賴包 name: "FlareJ", deps: { "nornj": "nj", "react": "React", "react-dom": "ReactDOM" } }) .transform(babelify, ...) //使用babel轉(zhuǎn)換es6代碼 .bundle() //合并打包 .pipe(source(getJsLibName())) //將常規(guī)流轉(zhuǎn)換為包含Stream的vinyl對象,并且重命名 .pipe(buffer()) //將vinyl對象內(nèi)容中的Stream轉(zhuǎn)換為Buffer .pipe(gulp.dest("./dist/js")); //輸出打包后的文件 }); function getJsLibName() { var libName = "flarej.js"; if (argv.min) { //按命令參數(shù)"--min"判斷是否為壓縮版 libName = "flarej.min.js"; } return libName; }
和webpack類似,browserify也需要指定打包的入口文件。在本例中只有一個(gè)入口文件,browserify會(huì)自動(dòng)分析文件內(nèi)依賴的各js模塊,最終生成一個(gè)完整的打包文件。
使用standalonify插件使打包后的js文符合UMD規(guī)范,并可以指定不將一些外部依賴包打進(jìn)包內(nèi)。一開始我使用了dependify,之后發(fā)現(xiàn)它生成的包有bug且作者又不維護(hù),于是就參考它重發(fā)了一個(gè)更完善的standalonify。使用這個(gè)插件打出來的包可以更好地生成依賴包的信息,此功能就類似于webpack中的externals參數(shù)。例如UMD中的AMD部分會(huì)這樣生成:
... else if (typeof define === "function" && define.amd) { define(["nornj","react","react-dom"], ...) ...
其實(shí)使用browserify自帶的standalone屬性也可以打出UMD包,并配合browserify-shim插件也可以排除外部依賴包,但是打包后依賴包的信息只能定義為全局的。
然后使用bundle方法進(jìn)行js模塊合并打包,如代碼為es6環(huán)境則需在此之前執(zhí)行transform方法進(jìn)行es6轉(zhuǎn)es5。
browserify在打包后須要進(jìn)行Stream轉(zhuǎn)換才可和gulp配合,在這里需要使用vinyl-source-stream和vinyl-buffer這兩個(gè)包。
在使用vinyl-source-stream時(shí)可以將打包文件重命名,此時(shí)可用yargs包提供的獲取命令參數(shù)功能來決定是否使用壓縮版命名。例如命名為壓縮版需輸入命令:
gulp build-js --min
最后使用gulp.dest方法指定打包后文件保存的目錄。
使用Babel將es6代碼轉(zhuǎn)換為es5關(guān)于browserify更詳細(xì)的技術(shù)資料大家可以參考這篇文章:browserify使用手冊
由于es6代碼目前大部分瀏覽器還未能完全支持,因此一般都需要轉(zhuǎn)換為es5后執(zhí)行。本示例中使用babel配合browserify在打包的過程中進(jìn)行轉(zhuǎn)換,babel的版本為6.0+。需要引入babelify,這個(gè)包是browserify的一個(gè)transform插件。使用方法如下:
gulp.task("build-js", function () { return browserify({ entries: "./src/base.js" }) .plugin(standalonify, ...) .transform(babelify, { //此處babel的各配置項(xiàng)格式與.babelrc文件相同 presets: [ "es2015", //轉(zhuǎn)換es6代碼 "stage-0", //指定轉(zhuǎn)換es7代碼的語法提案階段 "react" //轉(zhuǎn)換React的jsx ], plugins: [ "transform-object-assign", //轉(zhuǎn)換es6 Object.assign插件 "external-helpers", //將es6代碼轉(zhuǎn)換后使用的公用函數(shù)多帶帶抽出來保存為babelHelpers ["transform-es2015-classes", { "loose": false }], //轉(zhuǎn)換es6 class插件 ["transform-es2015-modules-commonjs", { "loose": false }] //轉(zhuǎn)換es6 module插件 ... ] }) .bundle() ... });
babelify插件的配置項(xiàng)格式與.babelrc文件完全相同。在babel升級6.0+后與之前的5.x差別較大,它拆分為了很多個(gè)模塊需要分別引入。這些模塊都需要多帶帶安裝各自的npm包,具體請查看package.json文件。
presets項(xiàng)需要使用es2015、stage-x、react三個(gè)模塊:
es2015,用于轉(zhuǎn)換es6代碼
stage-x,用于轉(zhuǎn)換更新的es7語法,x是指es7不同階段的語法提案,目前有0-3可用
react,用于轉(zhuǎn)換React的jsx代碼。
plugins項(xiàng)可引入轉(zhuǎn)換時(shí)需要的插件。一般來說babel-preset-es2015這個(gè)包中已經(jīng)包含了大多數(shù)轉(zhuǎn)換es6代碼的模塊,但也有部分模塊需要在plugins中引入。例如:
transform-object-assign,用于轉(zhuǎn)換Object.assign
如轉(zhuǎn)換時(shí)使用loose模式(設(shè)置了loose為true時(shí)代碼才可適應(yīng)IE8,默認(rèn)為false),則需要多帶帶引入這些模塊的包。如transform-es2015-classes即為轉(zhuǎn)換es6 class的包,如有需要可設(shè)置loose模式為true。
external-helpers,這個(gè)模塊的作用是將babel轉(zhuǎn)換后的一些公用函數(shù)多帶帶抽出來,這樣就可以減少轉(zhuǎn)換后的冗余代碼量。具體使用方法為先全局安裝babel:
npm install babel-cli -g
然后執(zhí)行命令:
babel-external-helpers #可加-t參數(shù)按不同方式生成,值為global|umd|var,默認(rèn)為global
這樣就可以在命令行中生成babelHelpers的代碼,然后將之保存為babelHelpers.js,在本例中放在vendor目錄內(nèi)。
生成最終的js代碼由于本例中使用external-helpers方式進(jìn)行es6轉(zhuǎn)換,故需要將babelHelpers.js與browserify打包后的項(xiàng)目js文件進(jìn)行連接合并:
var concat = require("gulp-concat"), sequence = require("gulp-sequence"), gulpif = require("gulp-if"), uglify = require("gulp-uglify"); //定義連接js任務(wù) gulp.task("concat-js", function () { var jsLibName = getJsLibName(); return gulp.src(["./vendor/babelHelpers.js", "./dist/js/" + jsLibName]) .pipe(concat(jsLibName)) .pipe(gulpif(argv.min, uglify())) .pipe(gulp.dest("./dist/js")); }); //將兩個(gè)任務(wù)串聯(lián)起來 gulp.task("build-all-js", sequence("build-js", "concat-js"));
先使用gulp-concat插件將babelHelpers.js和項(xiàng)目js文件進(jìn)行連接合并。
然后使用gulp-if插件判斷當(dāng)前執(zhí)行命令是否輸入了--min參數(shù),如果是則使用gulp-uglify插件進(jìn)行js代碼壓縮。
定義build-all-js任務(wù)來將build-js和concat-js任務(wù)串聯(lián)起來,但是需要使用gulp-sequence插件才能保證這兩個(gè)任務(wù)是按順序執(zhí)行的。
最后,在/dist/js目錄下會(huì)生成最終的項(xiàng)目js文件。
執(zhí)行單元測試本例中使用jasmine進(jìn)行單元測試。代碼比較簡單,執(zhí)行所有test目錄內(nèi)以"Spec"結(jié)尾的文件:
var jasmine = require("gulp-jasmine"); gulp.task("test", function () { return gulp.src(["./test/**/**Spec.js"]) .pipe(jasmine()); });
執(zhí)行命令:
gulp test
即可在命令行中查看測試結(jié)果。
執(zhí)行js代碼檢驗(yàn)本例中使用eslint進(jìn)行js代碼檢驗(yàn),需引入gulp-eslint插件:
var eslint = require("gulp-eslint"); gulp.task("eslint", function () { return gulp.src(["./src/**/*.js"]) //獲取src目錄內(nèi)全部js文件 .pipe(eslint({ //此處eslint的各配置項(xiàng)格式與.eslintrc文件相同 "rules": { "camelcase": [2, { "properties": "always" }], "comma-dangle": [2, "never"], "semi": [2, "always"], "quotes": [2, "single"], "strict": [2, "global"] }, "parser": "babel-eslint" })) .pipe(eslint.format()) .pipe(eslint.failAfterError()); });
執(zhí)行命令:
gulp eslint
即可在命令行中查看js代碼檢測結(jié)果。
另外如果是在es6環(huán)境下使用gulp-eslint,那么還需要安裝babel-eslint這個(gè)包。此處有個(gè)小坑,就是babel-eslint包是依賴estraverse和estraverse-fb包的,但這兩個(gè)包其實(shí)卻需要多帶帶安裝。
生成css及字體文件例中的css及字體文件也需要合并構(gòu)建,這里只簡單介紹一下構(gòu)建css的流程:
var less = require("gulp-less"), cssnano = require("gulp-cssnano"), postcss = require("gulp-postcss"), autoprefixer = require("autoprefixer"); function getCssLibName() { var libName = "flarej.css"; if (argv.min) { libName = "flarej.min.css"; } return libName; } //構(gòu)建項(xiàng)目css文件 gulp.task("build-css", function () { return gulp.src("./src/styles/base.less") .pipe(less()) //轉(zhuǎn)換less .pipe(rename(getCssLibName())) //重命名轉(zhuǎn)換后的css文件 .pipe(gulp.dest("./dist/css")); }); //將normalize.css與項(xiàng)目css進(jìn)行合并 gulp.task("concat-css", function () { var cssLibName = getCssLibName(); return gulp.src(["./vendor/normalize.css", "./dist/css/" + cssLibName]) .pipe(concat(cssLibName)) //連接合并 .pipe(gulpif(argv.min, cssnano())) //執(zhí)行css壓縮 .pipe(postcss([autoprefixer({ browsers: ["last 50 versions"] })])) //自動(dòng)補(bǔ)廠商前綴 .pipe(gulp.dest("./dist/css")); }); //將兩個(gè)任務(wù)串聯(lián)起來 gulp.task("build-all-css", sequence("build-css", "concat-css"));構(gòu)建全部代碼
本例中的gulp默認(rèn)任務(wù)即為構(gòu)建全部代碼,輸入命令:
gulp #可加"--min"參數(shù)構(gòu)建壓縮版
即可執(zhí)行,具體構(gòu)建流程如下:
更多細(xì)節(jié)大家可以查看本文示例的源代碼。
(完)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/79173.html
摘要:以前一直對前端構(gòu)建工具的理解不深,經(jīng)過幾天的研究特意來總結(jié)一下,第一次寫博客,有寫錯(cuò)的請多多見諒,該文章我也從其他博客拷了一些內(nèi)容,如果有冒犯之處,請指出。強(qiáng)大的設(shè)計(jì)使得它更像是一個(gè)構(gòu)建平臺,而不只是一個(gè)打包工具。 以前一直對前端構(gòu)建工具的理解不深,經(jīng)過幾天的研究特意來總結(jié)一下,第一次寫博客,有寫錯(cuò)的請多多見諒,該文章我也從其他博客拷了一些內(nèi)容,如果有冒犯之處,請指出。 如今,網(wǎng)頁不再...
摘要:已經(jīng)轉(zhuǎn)碼成了已經(jīng)轉(zhuǎn)碼成了合并壓縮并重命名的文件使用如果我們使用了中的,通過進(jìn)行模塊化開發(fā),那么通過轉(zhuǎn)碼后,將被轉(zhuǎn)碼成符合規(guī)范的和等,但是瀏覽器還是不認(rèn)識,這時(shí)可以使用對代碼再次進(jìn)行構(gòu)建。 一說起ES6,總會(huì)順帶看到webpack、babel、browserify還有一些認(rèn)都不認(rèn)識的blabla名詞,對于gulp才會(huì)一點(diǎn)點(diǎn)的我來說,內(nèi)心簡直是崩潰的,上網(wǎng)查了一些文章,探索著用gulp搭起...
摘要:自定義前端構(gòu)建工具生成器近期公司前端一直在做效率提升,流程優(yōu)化,很榮幸這個(gè)擔(dān)子落在了我身上,除了一些培訓(xùn),分享之外,自己弄了個(gè)基于的前端構(gòu)建環(huán)境生成器,在此分享給大家,覺得有用的請?jiān)囉谩#怀鲆饬系脑挘瑯?gòu)建環(huán)境已經(jīng)生成完畢了。 自定義前端構(gòu)建工具生成器generator-pg-cloud 近期公司前端一直在做效率提升,流程優(yōu)化,很榮幸這個(gè)擔(dān)子落在了我身上,除了一些培訓(xùn),分享之外,自己...
摘要:承接前一篇做一個(gè)合格的前端,自動(dòng)化構(gòu)建工具入門教程故而整理了如下插件資源大全。接下來我會(huì)逐一開源觀點(diǎn)網(wǎng)開發(fā)過程中的前后端技術(shù),如全文索引自定義富文本編輯器圖片上傳壓縮水印等等。 承接前一篇《做一個(gè)合格的前端,gulp自動(dòng)化構(gòu)建工具入門教程》故而整理了如下gulp插件資源大全。**【我的新作觀點(diǎn)網(wǎng):http://www.guandn.com (觀點(diǎn)網(wǎng)是一個(gè)獵獲新奇、收獲知識、重在獨(dú)立思考...
摘要:是大臉書出的一個(gè)前端開發(fā)框架。與其說是一個(gè)框架,我更加認(rèn)為更是一種模式,從年月份開始接觸,我就認(rèn)為這個(gè)框架以后一定會(huì)火。是一個(gè)單向數(shù)據(jù)流的框架,不同于和的雙向數(shù)據(jù)綁定的單向數(shù)據(jù)流可以數(shù)據(jù)模式更加單一,更利于前端的維護(hù)。 React是大臉書出的一個(gè)前端開發(fā)框架。與其說是一個(gè)框架,我更加認(rèn)為React更是一種模式,從2015年10月份開始接觸React,我就認(rèn)為這個(gè)框架以后一定會(huì)火。Rea...
閱讀 2322·2021-11-08 13:13
閱讀 1253·2021-10-09 09:41
閱讀 1696·2021-09-02 15:40
閱讀 3194·2021-08-17 10:13
閱讀 2553·2019-08-29 16:33
閱讀 3129·2019-08-29 13:17
閱讀 3141·2019-08-29 11:00
閱讀 3303·2019-08-26 13:40