将Webpack与Gulp 4结合使用

Avatar of Pascal Klau (@pascalaoms)
Pascal Klau (@pascalaoms) 发布

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200美元免费额度!

Webpack 现在非常火爆!Webpack 在模块打包和使用 Vue 或 React 等框架方面表现出色,但在处理静态资源(如 CSS)时则显得有些笨拙。您可能更习惯使用 Gulp 之类的工具来处理静态资源,并且这样做有一些很好的理由。

尽管如此,我们的静态项目中的 JavaScript 代码量正在不断增长,因此,为了弥补这一点,让我们在保持使用 Gulp 的同时,也利用 Webpack。在本文中,我们将具体使用 Gulp 4。我们将使用现代技术构建易于维护的工作流程,包括功能强大且实用的 **热模块替换** (HMR)。

您可能希望从此处开始

本文不完全适合初学者。如果您不熟悉 Webpack 或 Gulp,也许可以先学习以下教程。

Gulp 教程

Webpack 教程

演示

在 GitHub 上查看演示仓库。“hmr” 分支展示了如何设置热模块替换。

先决条件

运行以下命令以安装必要的包

npm install babel-core \
            babel-preset-es2015 \
            browser-sync \
            gulpjs/gulp#4.0 \
            webpack \
            webpack-dev-middleware \
            webpack-hot-middleware -D

从 Node v7.9.0 开始,ES6 模块 不受支持,这就是我们安装 Babel 以便在我们的任务中使用 import 语句和其他前沿 JS 功能的原因。

如果您不需要 HMR,可以随意将热中间件从上面列出的包中删除。开发中间件不依赖于它。

起始点

让我们开始吧!在项目的根目录中创建一个名为 tasks 的文件夹,并在其中创建三个文件:index.jswebpack.jsserver.js。由于 index 文件充当 gulpfile.js,而 webpack 文件充当 webpack.config.js,因此我们的项目根目录中的文件更少,看起来更整洁。

site 文件夹包含您网站的所有资源

╔ site
║   ╚═══ main.js
╠ tasks
║   ╠═══ index.js
║   ╠═══ server.js
║   ╚═══ webpack.js
╚ package.json

为了告诉 Gulp 任务位于何处,我们需要在 `package.json` 中添加标志

"scripts": {
  "dev": "gulp --require babel-register --gulpfile tasks",
  "build": "NODE_ENV=production gulp build --require babel-register --gulpfile tasks"
}

babel-register 命令处理 import 语句,而 --gulpfile 标志定义 gulpfile.js 或在我们的例子中 index.js 的路径。我们只需要引用 tasks 文件夹,因为就像在 HTML 中一样,名为 index 的文件标记了入口点。

设置基本的Webpack配置

在 `webpack.js` 中

import path from 'path'
import webpack from 'webpack'

let config = {
    entry: './main.js',
    output: {
        filename: './bundle.js',
        path: path.resolve(__dirname, '../site')
    },
    context: path.resolve(__dirname, '../site')
}

function scripts() {

    return new Promise(resolve => webpack(config, (err, stats) => {

        if (err) console.log('Webpack', err)

        console.log(stats.toString({ /* stats options */ }))

        resolve()
    }))
}

module.exports = { config, scripts }

请注意,我们不像许多教程那样直接导出对象,而是先将其放入变量中。这是必要的,以便我们可以在下面的 Gulp 任务 scripts 中以及下一步中的服务器中间件中使用此配置。

上下文

config.context 设置对于将所有路径相对于 site 文件夹设置是必要的。否则,它们将从 tasks 文件夹开始,这可能会导致以后出现混乱。

分离配置和任务

如果您有一个非常长的 Webpack 配置,您也可以将其与任务拆分为两个文件。

// webpack.js
export let config = { /* ... */ }
// scripts.js
import { config } from './webpack'
export function scripts() { /* ... */ }

热模块替换

以下是使 HMR 工作的方法。更改入口和插件

entry: {
  main: [
    './main.js',
    'webpack/hot/dev-server',
    'webpack-hot-middleware/client'
  ]
},

/* ... */

plugins: [
  new webpack.HotModuleReplacementPlugin()
]

确保为生产环境禁用额外的入口和 HMR 插件。Webpack Merge 包有助于为开发和生产环境设置不同的环境。

BrowserSync

现在设置 BrowserSync 任务

import gulp from 'gulp'
import Browser from 'browser-sync'
import webpack from 'webpack'
import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'

import { config as webpackConfig } from './webpack'

const browser = Browser.create()
const bundler = webpack(webpackConfig)

export function server() {

    let config = {
        server: 'site',
        middleware: [
            webpackDevMiddleware(bundler, { /* options */ }),
            webpackHotMiddleware(bundler)
        ],
    }

    browser.init(config)

    gulp.watch('site/*.js').on('change', () => browser.reload())
}

**开发中间件** 使 BrowserSync 能够处理在 webpack.js 中定义的入口。为了向其提供此信息,我们导入了配置模块。另一方面,**热中间件** 检查 app 组件(例如 Vue.js 的 `.vue` 文件)中的更改以注入。

由于我们无法热重载 main.js 等文件,因此我们对其进行 **监视**,并在更改时重新加载窗口。同样,如果您不需要 HMR,请删除 **webpackHotMiddleware**。

导入所有任务

`index.js` 文件包含所有任务

import gulp from 'gulp'

import { scripts } from './webpack'
import { server }  from './server'

export const dev   = gulp.series( server )
export const build = gulp.series( scripts )

export default dev

导出的变量定义了在哪个命令下运行哪些任务。默认导出使用 gulp 运行。

如果您为 Webpack 分离了开发和生产环境,您可能希望运行一个 gulp build 任务,该任务利用生产选项。为此,我们单独导入 scripts 任务,因为我们不需要在此处启动服务器。

在开发过程中,Webpack 由 BrowserSync 运行,因此在 dev 命令中放置 scripts 任务没有必要。

正在运行的任务

要开始开发,您不能仅仅运行gulpgulp build,因为它会在项目根目录中查找gulpfile.js。我们必须运行npm命令npm run devnpm run build来使用定义的标志。

扩展

现在您可以想象扩展和编写更多任务是多么容易。在一个文件中导出任务,并在index.js中导入它。干净且易于维护!

为了让您了解如何设置项目文件夹,这是我的个人设置

╔ build
╠ src
╠ tasks
║   ╠═══ config.js => project wide
║   ╠═══ icons.js  => optimize/concat SVG
║   ╠═══ images.js => optimize images
║   ╠═══ index.js  => run tasks
║   ╠═══ misc.js   => copy, delete
║   ╠═══ server.js => start dev server
║   ╠═══ styles.js => CSS + preprocessor
║   ╚═══ webpack.js
╚ package.json

再次,为什么要同时使用Webpack和Gulp?

静态文件处理

Gulp 比 Webpack 更擅长处理静态资源。 Copy Webpack 插件 也可以将文件从源文件夹复制到构建文件夹,但是当涉及到监视文件删除或更改(例如覆盖图像)时,gulp.watch 是更安全的选择。

服务器环境

Webpack 还通过 Webpack Dev Server 提供本地服务器环境,但使用 BrowserSync 有一些你可能不想错过的功能

  • 针对非应用程序项目的 CSS/HTML/图像注入
  • 开箱即用的多设备测试
  • 包含一个管理面板以进行更多控制
  • 带宽限制,用于速度和加载测试

编译时间

Git 上的这篇文章Hub 中所述,Sass 由 node-sass 处理的速度比 Webpack 的 sass-loader、css-loader 和 extract-text-webpack-plugin 组合处理的速度快得多。

便利性

在 Webpack 中,您必须例如将 CSS 和 SVG 文件导入到 JavaScript 中才能处理它们,这有时可能非常棘手且令人困惑。使用 Gulp,您无需调整工作流程。