博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于Gulp + Browserify构建es6环境下的自动化前端项目
阅读量:5788 次
发布时间:2019-06-18

本文共 9091 字,大约阅读时间需要 30 分钟。

随着React、Angular2、Redux等前沿的前端框架越来越流行,使用webpack、gulp等工具构建前端自动化项目也随之变得越来越重要。鉴于目前业界普遍更流行使用webpack来构建es6(ECMAScript 2015)前端项目,网上的相关教程也比较多;相对来说使用gulp来构建es6项目的中文教程就比较少。

经过一段时间的摸索,我觉得其实使用gulp也可以很方便地构建es6项目。以下是我感觉gulpwebpack主要的不同之处:

  • gulp的任务机制和流式管道函数和webpack的配置参数风格有着显著区别,它能使开发者更清晰地了解项目的构建流程。
  • 由于gulp是编程式风格的,所以使用起来可定制化的功能也就更灵活一些,可应对一些构建过程较为复杂的情况。

本文特此给大家介绍下如何使用gulp配合browserify来构建基于es6的前端项目。

Browserify vs Webpack

browserifywebpack都是当下流行的commonjs模块(或es6模块)合并打包工具,打包后的js文件可以直接运行在浏览器环境中。

很多人都知道,webpack功能全面,可以对js、css、甚至图片、字体文件统一进行合并打包,并且插件丰富。而browserify的特点是职责单一,只负责js模块合并打包,有些项目也并不需要将css等资源文件和js打包在一起的功能;它的代码风格也类似管道函数,和gulp的契合度较高;在github上也可以找到相当多的browserify插件,如、等等。

有一篇文章对browserifywebpack的对比进行了探讨:

示例项目

本文中使用的示例项目是我为重构过去搞的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基础使用方法的更多细节大家可以参考这篇文章:

使用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-streamvinyl-buffer这两个包。
  • 在使用vinyl-source-stream时可以将打包文件重命名,此时可用yargs包提供的获取命令参数功能来决定是否使用压缩版命名。例如命名为压缩版需输入命令:
gulp build-js --min
  • 最后使用gulp.dest方法指定打包后文件保存的目录。

关于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三个模块:
  1. es2015,用于转换es6代码
  2. stage-x,用于转换更新的es7语法,x是指es7不同阶段的语法提案,目前有0-3可用
  3. react,用于转换React的jsx代码。
  • plugins项可引入转换时需要的插件。一般来说babel-preset-es2015这个包中已经包含了大多数转换es6代码的模块,但也有部分模块需要在plugins中引入。例如:
  1. transform-object-assign,用于转换Object.assign
  2. 如转换时使用loose模式(设置了loose为true时代码才可适应IE8,默认为false),则需要单独引入这些模块的包。如transform-es2015-classes即为转换es6 class的包,如有需要可设置loose模式为true。
  3. 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包是依赖estraverseestraverse-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"参数构建压缩版

即可执行,具体构建流程如下:

779841-20160411191731848-451111060.png

更多细节大家可以查看本文示例的。

(完)

转载于:https://www.cnblogs.com/jesy/p/5192815.html

你可能感兴趣的文章
VS2017+EF+Mysql生成实体数据模型(解决闪退的坑)
查看>>
C++多态、继承的简单分析
查看>>
库克称未来苹果用户可自己决定是否降频 网友:你是在搞笑吗?
查看>>
6倍性能差100TB容量,阿里云POLARDB咋实现?
查看>>
linux 安装 MySQLdb for python
查看>>
Sublime Text 2 技巧
查看>>
使用fscanf()函数从磁盘文件读取格式化数据
查看>>
参加婚礼
查看>>
h5 audio相关手册
查看>>
刚毕业从事java开发需要掌握的技术
查看>>
CSS Custom Properties 自定义属性
查看>>
vim
查看>>
MVVM计算器(下)
查看>>
C++中指针和引用的区别
查看>>
簡單分稀 iptables 記錄 udp 微軟 138 端口
查看>>
Java重写equals方法和hashCode方法
查看>>
Spark API编程动手实战-07-join操作深入实战
查看>>
H3C-路由策略
查看>>
centos 修改字符界面分辨率
查看>>
LNMP之Mysql主从复制(四)
查看>>