Gulp + Webpack 还是 JUST Webpack?

Gulp + Webpack or JUST Webpack?

我看到有人将 gulp 与 webpack 一起使用。但是后来我看了webpack可以代替gulp?我在这里完全困惑...有人可以解释一下吗?

更新

最后我从gulp开始。我是现代前端的新手,只想快速起床 运行。一年多之后,我的脚已经很湿了,我准备转向 webpack。我建议以同样的方式开始的人走同样的路线。并不是说你不能尝试 webpack,而是说如果它看起来很复杂,首先从 gulp 开始......这没有错。

如果您不想要 gulp,是的,有咕噜声,但您也可以只在 package.json 中指定命令,然后在没有任务运行器的情况下从命令行调用它们只是为了起床和 运行 最初。例如:

"scripts": {
      "babel": "babel src -d build",
      "browserify": "browserify build/client/app.js -o dist/client/scripts/app.bundle.js",
      "build": "npm run clean && npm run babel && npm run prepare && npm run browserify",
      "clean": "rm -rf build && rm -rf dist",
      "copy:server": "cp build/server.js dist/server.js",
      "copy:index": "cp src/client/index.html dist/client/index.html",
      "copy": "npm run copy:server && npm run copy:index",
      "prepare": "mkdir -p dist/client/scripts/ && npm run copy",
      "start": "node dist/server"
    },

这个答案可能会有帮助。

...这是在 gulp 任务中使用 webpack 的示例。这更进一步并假设您的 webpack 配置是用 es6 编写的。

var gulp = require('gulp');
var webpack = require('webpack');
var gutil = require('gutil');
var babel = require('babel/register');
var config = require(path.join('../..', 'webpack.config.es6.js'));

gulp.task('webpack-es6-test', function(done){
   webpack(config).run(onBuild(done));
});

function onBuild(done) {
    return function(err, stats) {
        if (err) {
            gutil.log('Error', err);
            if (done) {
                done();
            }
        } else {
            Object.keys(stats.compilation.assets).forEach(function(key) {
                gutil.log('Webpack: output ', gutil.colors.green(key));
            });
            gutil.log('Webpack: ', gutil.colors.blue('finished ', stats.compilation.name));
            if (done) {
                done();
            }
        }
    }
}

我想您会发现,随着您的应用变得越来越复杂,您可能希望按照上面的示例将 gulp 与 webpack 任务一起使用。这允许你在你的构建中做一些更有趣的事情,而 webpack 加载器和插件实际上不会做,即。创建输出目录、启动服务器等。简而言之,webpack 实际上可以做这些事情,但您可能会发现它们对您的长期需求有限制。从 gulp -> webpack 获得的最大优势之一是您可以针对不同的环境自定义 webpack 配置,并让 gulp 在正确的时间执行正确的任务。这真的取决于你,但是 gulp 的 运行 webpack 没有错,事实上有一些漂亮的 interesting examples of how to do it. The example above is basically from jlongster.

我在不同的项目中使用了这两个选项。

这是我使用 gulpwebpack - https://github.com/iroy2000/react-reflux-boilerplate-with-webpack 组合在一起的一个样板文件。

我有一些其他项目只使用 webpacknpm tasks

而且它们都工作得很好。而且我认为它会导致您的任务有多复杂,以及您希望在配置中拥有多少控制权。

例如,如果您的任务很简单,比如说 devbuildtest ... 等(这是非常标准的),您完全可以只简单 webpacknpm tasks

但是,如果您的工作流程非常复杂,并且希望更好地控制您的配置(因为它是编码),您可以选择 gulp 路线。

但根据我的经验,webpack 生态系统提供了足够多的插件和加载器,我需要,所以我喜欢使用最低限度的方法,除非有一些你只能在 gulp 中做的事情。而且,如果您的系统中少了一件东西,它将使您的配置更容易。

现在很多时候,我看到人们实际上用 webpack 替换了 gulp and browsify

NPM scripts 可以做与 gulp 相同的事情,但代码减少了大约 50 倍。事实上,根本没有代码,只有命令行参数。

例如,您描述的用例中您希望针对不同的环境使用不同的代码。

使用 Webpack + NPM Scripts,就是这么简单:

"prebuild:dev": "npm run clean:wwwroot",
"build:dev": "cross-env NODE_ENV=development webpack --config config/webpack.development.js --hot --profile --progress --colors --display-cached",
"postbuild:dev": "npm run copy:index.html && npm run rename:index.html",

"prebuild:production": "npm run clean:wwwroot",
"build:production": "cross-env NODE_ENV=production webpack --config config/webpack.production.js --profile --progress --colors --display-cached --bail",
"postbuild:production": "npm run copy:index.html && npm run rename:index.html",

"clean:wwwroot": "rimraf -- wwwroot/*",
"copy:index.html": "ncp wwwroot/index.html Views/Shared",
"rename:index.html": "cd ../PowerShell && elevate.exe -c renamer --find \"index.html\" --replace \"_Layout.cshtml\" \"../MyProject/Views/Shared/*\"",

现在你只需维护两个 webpack 配置脚本,一个用于开发模式,webpack.development.js,一个用于生产模式,webpack.production.js。我还利用了一个 webpack.common.js,其中包含在所有环境中共享的 webpack 配置,并使用 webpackMerge 来合并它们。

由于 NPM 脚本很酷,它允许轻松链接,类似于 gulp 的做法 Streams/pipes。

在上面的示例中,要为开发而构建,您只需转到命令行并执行 npm run build:dev

  1. NPM 将首先 运行 prebuild:dev
  2. 然后 build:dev
  3. 最后 postbuild:dev

prepost 前缀告诉 NPM 执行哪个顺序。

如果你注意到,使用 Webpack + NPM 脚本,你可以 运行 原生程序,例如 rimraf,而不是 gulp-wrapper 用于原生程序,例如gulp-rimraf。您还可以 运行 原生 Windows .exe 文件,就像我在此处使用 elevate.exe 或 Linux 或 Mac.

上的原生 *nix 文件一样

尝试用 gulp 做同样的事情。您将不得不等待有人来为您要使用的本机程序编写 gulp-wrapper。此外,您可能需要像这样编写复杂的代码:(直接取自 angular2-seed 存储库)

Gulp开发代码

import * as gulp from 'gulp';
import * as gulpLoadPlugins from 'gulp-load-plugins';
import * as merge from 'merge-stream';
import * as util from 'gulp-util';
import { join/*, sep, relative*/ } from 'path';

import { APP_DEST, APP_SRC, /*PROJECT_ROOT, */TOOLS_DIR, TYPED_COMPILE_INTERVAL } from '../../config';
import { makeTsProject, templateLocals } from '../../utils';

const plugins = <any>gulpLoadPlugins();

let typedBuildCounter = TYPED_COMPILE_INTERVAL; // Always start with the typed build.

/**
 * Executes the build process, transpiling the TypeScript files (except the spec and e2e-spec files) for the development
 * environment.
 */
export = () => {
  let tsProject: any;
  let typings = gulp.src([
    'typings/index.d.ts',
    TOOLS_DIR + '/manual_typings/**/*.d.ts'
  ]);
  let src = [
    join(APP_SRC, '**/*.ts'),
    '!' + join(APP_SRC, '**/*.spec.ts'),
    '!' + join(APP_SRC, '**/*.e2e-spec.ts')
  ];

  let projectFiles = gulp.src(src);
  let result: any;
  let isFullCompile = true;

  // Only do a typed build every X builds, otherwise do a typeless build to speed things up
  if (typedBuildCounter < TYPED_COMPILE_INTERVAL) {
    isFullCompile = false;
    tsProject = makeTsProject({isolatedModules: true});
    projectFiles = projectFiles.pipe(plugins.cached());
    util.log('Performing typeless TypeScript compile.');
  } else {
    tsProject = makeTsProject();
    projectFiles = merge(typings, projectFiles);
  }

  result = projectFiles
    .pipe(plugins.plumber())
    .pipe(plugins.sourcemaps.init())
    .pipe(plugins.typescript(tsProject))
    .on('error', () => {
      typedBuildCounter = TYPED_COMPILE_INTERVAL;
    });

  if (isFullCompile) {
    typedBuildCounter = 0;
  } else {
    typedBuildCounter++;
  }

  return result.js
    .pipe(plugins.sourcemaps.write())
// Use for debugging with Webstorm/IntelliJ
// https://github.com/mgechev/angular2-seed/issues/1220
//    .pipe(plugins.sourcemaps.write('.', {
//      includeContent: false,
//      sourceRoot: (file: any) =>
//        relative(file.path, PROJECT_ROOT + '/' + APP_SRC).replace(sep, '/') + '/' + APP_SRC
//    }))
    .pipe(plugins.template(templateLocals()))
    .pipe(gulp.dest(APP_DEST));
};

Gulp 生产代码

import * as gulp from 'gulp';
import * as gulpLoadPlugins from 'gulp-load-plugins';
import { join } from 'path';

import { TMP_DIR, TOOLS_DIR } from '../../config';
import { makeTsProject, templateLocals } from '../../utils';

const plugins = <any>gulpLoadPlugins();

const INLINE_OPTIONS = {
  base: TMP_DIR,
  useRelativePaths: true,
  removeLineBreaks: true
};

/**
 * Executes the build process, transpiling the TypeScript files for the production environment.
 */

export = () => {
  let tsProject = makeTsProject();
  let src = [
    'typings/index.d.ts',
    TOOLS_DIR + '/manual_typings/**/*.d.ts',
    join(TMP_DIR, '**/*.ts')
  ];
  let result = gulp.src(src)
    .pipe(plugins.plumber())
    .pipe(plugins.inlineNg2Template(INLINE_OPTIONS))
    .pipe(plugins.typescript(tsProject))
    .once('error', function () {
      this.once('finish', () => process.exit(1));
    });


  return result.js
    .pipe(plugins.template(templateLocals()))
    .pipe(gulp.dest(TMP_DIR));
};

实际的 gulp 代码比这个复杂得多,因为这只是 repo 中几十个 gulp 文件中的两个。

那么,哪一个对你来说更容易呢?

在我看来,NPM scripts 在有效性和易用性方面都远远超过了 gulp 和 g运行t,所有前端开发人员都应该考虑在他们的工作流程中使用它,因为它是一个主要的节省时间。

更新

我遇到过一种情况,我想将 Gulp 与 NPM 脚本和 Webpack 结合使用。

例如,当我需要在 iPad 或 Android 设备上进行 远程调试 时,我需要启动额外的服务器。在过去,我 运行 所有服务器都是单独的进程,在 IntelliJ IDEA(或 Webstorm)中使用 "Compound" 运行 配置很容易。但是,如果我需要停止并重新启动它们,则必须关闭 5 个不同的服务器选项卡,这很乏味,而且输出分布在不同的 windows.

gulp 的一个好处是可以将所有独立进程的输出链接到一个控制台 window,它成为所有子服务器的父级。

所以我创建了一个非常简单的 gulp 任务,直接 运行 我的 NPM 脚本或命令,所以所有的输出都出现在一个 window 中,我可以很容易地通过关闭 gulp 任务 window.

一次结束所有 5 个服务器

Gulp.js

/**
 * Gulp / Node utilities
 */
var gulp = require('gulp-help')(require('gulp'));
var utils = require('gulp-util');
var log = utils.log;
var con = utils.colors;

/**
 * Basic workflow plugins
 */
var shell = require('gulp-shell'); // run command line from shell
var browserSync = require('browser-sync');

/**
 * Performance testing plugins
 */
var ngrok = require('ngrok');

// Variables
var serverToProxy1 = "localhost:5000";
var finalPort1 = 8000;


// When the user enters "gulp" on the command line, the default task will automatically be called. This default task below, will run all other tasks automatically.

// Default task
gulp.task('default', function (cb) {
   console.log('Starting dev servers!...');
   gulp.start(
      'devserver:jit',
      'nodemon',
      'browsersync',
      'ios_webkit_debug_proxy'
      'ngrok-url',
      // 'vorlon',
      // 'remotedebug_ios_webkit_adapter'
   );
});

gulp.task('nodemon', shell.task('cd ../backend-nodejs && npm run nodemon'));
gulp.task('devserver:jit', shell.task('npm run devserver:jit'));
gulp.task('ios_webkit_debug_proxy', shell.task('npm run ios-webkit-debug-proxy'));
gulp.task('browsersync', shell.task(`browser-sync start --proxy ${serverToProxy1} --port ${finalPort1} --no-open`));
gulp.task('ngrok-url', function (cb) {
   return ngrok.connect(finalPort1, function (err, url) {
      site = url;
      log(con.cyan('ngrok'), '- serving your site from', con.yellow(site));
      cb();
   });
});
// gulp.task('vorlon', shell.task('vorlon'));
// gulp.task('remotedebug_ios_webkit_adapter', shell.task('remotedebug_ios_webkit_adapter'));

在我看来,运行 5 个任务仍然有相当多的代码,但它可以达到目的。需要注意的是 gulp-shell 似乎没有正确地 运行 某些命令,例如 ios-webkit-debug-proxy。所以我不得不创建一个只执行相同命令的 NPM 脚本,然后它就可以工作了。

所以我主要使用 NPM 脚本来完成我的所有任务,但偶尔当我需要同时 运行 一堆服务器时,我会启动我的 Gulp 任务来帮忙。为正确的工作选择正确的工具。

更新 2

我现在使用名为 concurrently 的脚本,它与上面的 gulp 任务执行相同的操作。它 运行 并行运行多个 CLI 脚本,并将它们全部通过管道传输到同一个控制台 window,而且使用起来非常简单。再一次,不需要代码(好吧,代码在 node_module for concurrently 中,但您不必担心)

// NOTE: If you need to run a command with spaces in it, you need to use 
// double quotes, and they must be escaped (at least on windows).
// It doesn't seem to work with single quotes.

"run:all": "concurrently \"npm run devserver\" nodemon browsersync ios_webkit_debug_proxy ngrok-url"

这 运行 所有 5 个脚本并行传输到一个终端。惊人的!所以这一点,我很少使用 gulp,因为有很多 cli 脚本可以在没有代码的情况下完成相同的任务。

我建议你阅读这些深入比较的文章。

Gulp和Webpack的概念有很大的不同。你告诉 Gulp how 一步一步把前端代码放在一起,但是你告诉 Webpack what 你想要通过一个配置文件。

这是我写的一篇简短文章(阅读 5 分钟),解释了我对差异的理解:https://medium.com/@Maokai/compile-the-front-end-from-gulp-to-webpack-c45671ad87fe

我们公司在过去的一年里从 Gulp 迁移到了 Webpack。虽然花了一些时间,但我们想出了如何将我们在 Gulp 中所做的一切移动到 Webpack。所以对我们来说,我们在 Gulp 中所做的一切我们也可以通过 Webpack 完成,但反之则不行。

从今天开始,我建议只使用 Webpack 并避免混合使用 Gulp 和 Webpack,这样您和您的团队就不需要学习和维护两者,尤其是因为它们需要截然不同的思维方式.

老实说,我认为最好是同时使用两者。

  • Webpack 所有 javascript 相关。
  • Gulp 所有 css 相关。

我仍然需要找到一个像样的解决方案来用 webpack 打包 css,到目前为止,我很高兴使用 gulp 用于 css 和 webpack 用于 javascript。

我也使用 npm 脚本作为 @Tetradev 描述的。特别是因为我使用的是 Visual Studio,而 NPM Task runner 相当 可靠 Webpack Task Runner 相当 有问题