跟着React
、Angular2
、Redux
等前沿的前端框架愈来愈盛行,运用webpack
、gulp
等东西构建前端自动化项目也随之变得愈来愈重要。鉴于现在业界广泛更盛行运用webpack
来构建es6(ECMAScript 2015)前端项目,网上的相干教程也比较多;相对来说运用gulp
来构建es6项目标中文教程就比较少。
经由一段时间的探索,我以为实在运用gulp
也能够很方便地构建es6项目。以下是我觉得gulp
和webpack
重要的差异之处:
gulp
的使命机制和流式管道函数和webpack
的设置参数作风有着明显区分,它能使开辟者更清楚地相识项目标构建流程。因为
gulp
是编程式作风的,所以运用起来可定制化的功用也就更天真一些,可应对一些构建历程较为庞杂的状况。
本文特此给人人引见下怎样运用gulp
合营browserify
来构建基于es6的前端项目。
Browserify vs Webpack
browserify
与webpack
都是当下盛行的commonjs模块(或es6模块)兼并打包东西,打包后的js文件能够直接运行在浏览器环境中。
很多人都晓得,webpack
功用周全,能够对js、css、以至图片、字体文件一致举行兼并打包,而且插件雄厚。而browserify
的特点是职责单一,只担任js模块兼并打包,有些项目也并不需要将css等资本文件和js打包在一起的功用;它的代码作风也相似管道函数,和gulp
的契合度较高;在github上也能够找到相当多的browserify
插件,如热替代、代码支解等等。
有一篇文章对
browserify
和webpack
的对照举行了讨论:webpack 跟 browserify 比到底有什么好?
示例项目
本文中运用的示例项目是我为重构过去搞的UI组件库而建的项目,运用browserify
构建的分支地点请戳这里。这个项目现在已改用gulp
+webpack
构建,然则保留了原先用browserify
构建的分支代码可供参考。
项目构造目次
项目目次
dist (临盆代码目次,寄存天生兼并后的种种文件)
js
构建出的项目js文件
fonts
…
css
构建出的项目css文件
examples (示例目次)
src (开辟代码目次)
styles (款式文件目次)
base.js (打包进口文件)
…
test (单元测试目次)
vendor (第三方依靠库)
babelHelpers.js
…
gulpfile.js (gulp设置文件)
package.json
LICENSE
README.md
示例项目目次大抵如上所示,个中运用babel
举行es6至es5转换,并运用eslint
举行js代码磨练。人人看到这里能够有疑问,为何项目中没有babel及eslint的设置文件.babelrc
和.eslintrc
呢?缘由就是这些设置文件里的内容实际上是能够直接设置在gulpfile.js
中的相干插件内的。
设置package.json
在这里只列出项目依靠的种种包,大抵分为以下几类:
{
...
"devDependencies": {
/*browserify包及相干插件*/
"browserify": "^13.0.0",
"vinyl-buffer": "^1.0.0",
"vinyl-source-stream": "^1.1.0",
"standalonify": "^0.1.3",
/*babel相干插件*/
"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相干插件*/
"babel-eslint": "^5.0.0",
"estraverse": "^4.2.0",
"estraverse-fb": "^1.3.1",
/*gulp包及相干插件*/
"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相干插件*/
"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文件。在代码作风方面,与webpack.config.js的设置参数作风差异的是,gulpfile.js更倾向编程作风。gulpfile.js团体构造以下所示:
//引入依靠的种种包:
var gulp = require('gulp'),
browserify = require('browserify'),
...
//定义一些全局函数及变量
function getJsLibName() {
...
}
...
//定义种种使命
gulp.task('build-all-js', ...);
gulp.task('build-all-css', ...);
gulp.task('build', ['build-all-js', 'build-all-css', ...]);
...
//定义默许使命
gulp.task('default', ['build']);
运用gulp
需要定义种种使命来处置惩罚种种文件的构建天生。如例中所示,定义build-all-js使命来构建js文件,实行使命时须输入敕令:
gulp build-all-js
能够定义一个默许使命,平常在这个使命里顺次实行悉数子使命,实行时输入敕令:
gulp
关于
gulp
基本运用要领的更多细节人人能够参考这篇文章:前端构建东西gulpjs的运用引见及技能
运用Browserify举行js模块兼并
合营gulp
运用browserify
需要引入的包:
var browserify = require('browserify'),
source = require('vinyl-source-stream'),
buffer = require('vinyl-buffer'),
standalonify = require('standalonify'),
argv = require('yargs').argv;
建立gulp
使命build-js:
gulp.task('build-js', function () {
return browserify({
entries: './src/base.js' //指定打包进口文件
})
.plugin(standalonify, { //使打包后的js文件相符UMD范例并指定外部依靠包
name: 'FlareJ',
deps: {
'nornj': 'nj',
'react': 'React',
'react-dom': 'ReactDOM'
}
})
.transform(babelify, ...) //运用babel转换es6代码
.bundle() //兼并打包
.pipe(source(getJsLibName())) //将通例流转换为包括Stream的vinyl对象,而且重定名
.pipe(buffer()) //将vinyl对象内容中的Stream转换为Buffer
.pipe(gulp.dest('./dist/js')); //输出打包后的文件
});
function getJsLibName() {
var libName = 'flarej.js';
if (argv.min) { //按敕令参数"--min"推断是不是为紧缩版
libName = 'flarej.min.js';
}
return libName;
}
和
webpack
相似,browserify
也需要指定打包的进口文件。在本例中只要一个进口文件,browserify
会自动剖析文件内依靠的各js模块,终究天生一个完整的打包文件。运用
standalonify
插件使打包后的js文相符UMD范例,并能够指定不将一些外部依靠包打进包内。一开始我运用了dependify
,以后发明它天生的包有bug且作者又不保护,于是就参考它重发了一个更完美的standalonify
。运用这个插件打出来的包能够更好地天生依靠包的信息,此功用就相似于webpack
中的externals参数。比方UMD中的AMD部份会如许天生:
...
else if (typeof define === 'function' && define.amd) { define(["nornj","react","react-dom"], ...)
...
实在运用browserify
自带的standalone属性也能够打出UMD包,并合营browserify-shim插件也能够消除外部依靠包,然则打包后依靠包的信息只能定义为全局的。
然后运用bundle要领举行js模块兼并打包,如代码为es6环境则需在此之前实行transform要领举行es6转es5。
browserify
在打包后需要举行Stream转换才可和gulp
合营,在这里需要运用vinyl-source-stream
和vinyl-buffer
这两个包。在运用
vinyl-source-stream
时能够将打包文件重定名,此时可用yargs
包供应的猎取敕令参数功用来决议是不是运用紧缩版定名。比方定名为紧缩版需输入敕令:gulp build-js --min
末了运用gulp.dest要领指定打包后文件保留的目次。
关于
browserify
更细致的技术资料人人能够参考这篇文章:browserify运用手册
运用Babel将es6代码转换为es5
因为es6代码现在大部份浏览器还未能完整支撑,因而平常都需要转换为es5后实行。本示例中运用babel
合营browserify
在打包的历程当中举行转换,babel
的版本为6.0+。需要引入babelify
,这个包是browserify
的一个transform插件。运用要领以下:
gulp.task('build-js', function () {
return browserify({
entries: './src/base.js'
})
.plugin(standalonify, ...)
.transform(babelify, { //此处babel的各设置项花样与.babelrc文件雷同
presets: [
'es2015', //转换es6代码
'stage-0', //指定转换es7代码的语法提案阶段
'react' //转换React的jsx
],
plugins: [
'transform-object-assign', //转换es6 Object.assign插件
'external-helpers', //将es6代码转换后运用的公用函数零丁抽出来保留为babelHelpers
['transform-es2015-classes', { "loose": false }], //转换es6 class插件
['transform-es2015-modules-commonjs', { "loose": false }] //转换es6 module插件
...
]
})
.bundle()
...
});
babelify
插件的设置项花样与.babelrc
文件完整雷同。在babel
晋级6.0+后与之前的5.x差异较大,它拆分为了很多个模块需要离别引入。这些模块都需要零丁装置各自的npm包,详细请检察package.json文件。presets项需要运用es2015、stage-x、react三个模块:
es2015
,用于转换es6代码stage-x
,用于转换更新的es7语法,x是指es7差异阶段的语法提案,现在有0-3可用react
,用于转换React的jsx代码。
plugins项可引入转换时需要的插件。平常来说babel-preset-es2015这个包中已包括了大多数转换es6代码的模块,但也有部份模块需要在plugins中引入。比方:
transform-object-assign
,用于转换Object.assign如转换时运用loose形式(设置了loose为true时代码才可顺应IE8,默许为false),则需要零丁引入这些模块的包。如
transform-es2015-classes
即为转换es6 class的包,若有需要可设置loose形式为true。external-helpers
,这个模块的作用是将babel转换后的一些公用函数零丁抽出来,如许就能够削减转换后的冗余代码量。详细运用要领为先全局装置babel:npm install babel-cli -g
然后实行敕令:
babel-external-helpers #可加-t参数按差异体式格局天生,值为global|umd|var,默许为global
如许就能够在敕令行中天生babelHelpers的代码,然后将之保留为babelHelpers.js,在本例中放在vendor目次内。
天生终究的js代码
因为本例中运用external-helpers体式格局举行es6转换,故需要将babelHelpers.js与browserify
打包后的项目js文件举行衔接兼并:
var concat = require('gulp-concat'),
sequence = require('gulp-sequence'),
gulpif = require('gulp-if'),
uglify = require('gulp-uglify');
//定义衔接js使命
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'));
});
//将两个使命串连起来
gulp.task('build-all-js', sequence('build-js', 'concat-js'));
先运用
gulp-concat
插件将babelHelpers.js和项目js文件举行衔接兼并。然后运用
gulp-if
插件推断当前实行敕令是不是输入了--min
参数,如果是则运用gulp-uglify
插件举行js代码紧缩。定义build-all-js使命来将build-js和concat-js使命串连起来,然则需要运用
gulp-sequence
插件才保证这两个使命是按递次实行的。末了,在/dist/js目次下会天生终究的项目js文件。
实行单元测试
本例中运用jasmine
举行单元测试。代码比较简朴,实行一切test目次内以”Spec”末端的文件:
var jasmine = require('gulp-jasmine');
gulp.task("test", function () {
return gulp.src(["./test/**/**Spec.js"])
.pipe(jasmine());
});
实行敕令:
gulp test
即可在敕令行中检察测试效果。
实行js代码磨练
本例中运用eslint
举行js代码磨练,需引入gulp-eslint
插件:
var eslint = require('gulp-eslint');
gulp.task('eslint', function () {
return gulp.src(['./src/**/*.js']) //猎取src目次内悉数js文件
.pipe(eslint({ //此处eslint的各设置项花样与.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());
});
实行敕令:
gulp eslint
即可在敕令行中检察js代码检测效果。
别的如果是在es6环境下运用gulp-eslint
,那末还需要装置babel-eslint
这个包。此处有个小坑,就是babel-eslint
包是依靠estraverse
和estraverse-fb
包的,但这两个包实在却需要零丁装置。
天生css及字体文件
例中的css及字体文件也需要兼并构建,这里只简朴引见一下构建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;
}
//构建项目css文件
gulp.task('build-css', function () {
return gulp.src('./src/styles/base.less')
.pipe(less()) //转换less
.pipe(rename(getCssLibName())) //重定名转换后的css文件
.pipe(gulp.dest('./dist/css'));
});
//将normalize.css与项目css举行兼并
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())) //实行css紧缩
.pipe(postcss([autoprefixer({ browsers: ['last 50 versions'] })])) //自动补厂商前缀
.pipe(gulp.dest('./dist/css'));
});
//将两个使命串连起来
gulp.task('build-all-css', sequence('build-css', 'concat-css'));
构建悉数代码
本例中的gulp
默许使命即为构建悉数代码,输入敕令:
gulp #可加"--min"参数构建紧缩版
即可实行,详细构建流程以下:
更多细节人人能够检察本文示例的源代码。
(完)