使用 FuseBox 作为 React 的 webpack 打包替代方案

Avatar of Kingsley Silas
Kingsley Silas

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

如果您正在寻找 webpack 的替代打包器,您可能想看看 FuseBox。 它基于 webpack 提供的功能构建——代码拆分、热模块重载、动态导入等——但 FuseBox 中的代码拆分默认情况下需要 零配置(尽管 webpack 将在 截至 4.0 版 提供相同的功能)。

相反,FuseBox 是为简洁(以更简单的配置形式)和性能(通过包含积极的缓存方法)而构建的。 此外,它可以扩展以使用 大量插件,这些插件可以处理您在默认值之外需要的任何事情。

哦,对了,如果您是 TypeScript 的粉丝,您可能想知道 FuseBox 使其成为一等公民。 这意味着您可以用 Typescript 编写应用程序——无需配置!——它将默认情况下使用 Typescript 编译器来编译脚本。 不打算使用 Typescript? 不用担心,编译器将处理任何 JavaScript。 另一个好处!

为了说明启动和运行速度有多快,让我们构建一个应用程序的基本框架,它看起来类似于使用 create-react-app 构建的应用程序。 我们正在做的所有事情 将位于 GitHub 上,如果您想一起学习。

当然,FuseBox 不是 webpack 的唯一替代方案。 还有很多,事实上,Maks Akymenko 有一篇关于 Parcel 的好文章,这是另一个值得研究的绝佳替代方案。

基本设置

首先创建一个新的项目目录,并使用 npm 初始化它

## Create the directory
mkdir csstricks-fusebox-react && $_
## Initialize with npm default options
npm init -y

现在我们可以安装一些依赖项。 我们将使用 React 构建应用程序,因此我们需要它以及 react-dom。

npm install --save react react-dom

接下来,我们将安装 FuseBox 和 Typescript 作为依赖项。 我们也会将 Uglify 添加到其中,以帮助压缩我们的脚本,并添加对使用 Sass 编写样式的支持。

npm install --save-dev fuse-box typescript uglify-js node-sass

好了,现在让我们在项目目录的根目录中创建一个 src 文件夹(可以手动完成)。 在其中添加以下文件(app.jsindex.js),包括内容

// App.js

import * as React from "react";
import * as logo from "./logo.svg";

const App = () => {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <h1 className="App-title">Welcome to React</h1>
      </header>
      <p className="App-intro">
        To get started, edit `src/App.js` and save to reload.
      </p>
    </div>
  )
};

export default App;

您可能已经注意到我们正在导入 SVG 文件。 您可以直接从 GitHub 仓库 下载它。

# index.js

import * as React from "react";
import * as ReactDOM from "react-dom";
import App from "./App"

ReactDOM.render(
  <App />, document.getElementById('root')
);

您可以看到我们处理导入文件的方式与典型的 React 应用程序略有不同。 这是因为 FuseBox 默认情况下不会填充导入。

因此,我们没有这样做

import React from "react";

…我们这样做

import * as React from "react";
<!-- ./src/index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>CSSTricks Fusebox React</title>
    $css
  </head>

  <body>
    <noscript>
      You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
    $bundles
  </body>
</html>

样式并不是这篇文章的重点,但让我们添加一些样式来装饰一下。 我们将有两个样式表。 第一个用于 App 组件,并保存为 App.css

/* App.css */

.App {
  text-align: center;
}

.App-logo {
  animation: App-logo-spin infinite 20s linear;
  height: 80px;
}

.App-header {
  background-color: #222;
  height: 150px;
  padding: 20px;
  color: white;
}

.App-intro {
  font-size: large;
}

@keyframes App-logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform:
        rotate(360deg);
  }
}

第二个样式表用于 index.js,应保存为 index.css

/* index.css */
body {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
}

好的,我们完成了初始的整理工作。 现在让我们扩展 FuseBox,添加一些好东西!

插件和配置

我们之前说过,配置 FuseBox 的设计比 webpack 之类的东西简单得多——这是真的! 在应用程序的根目录中创建一个名为 fuse.js 的文件。

我们从导入我们将要使用的插件开始,所有插件都来自我们安装的 FuseBox 包。

const { FuseBox, CSSPlugin, SVGPlugin, WebIndexPlugin } = require("fuse-box");

接下来,我们将初始化一个 FuseBox 实例,并告诉它我们使用什么作为主目录,以及将编译后的资产放在哪里

const fuse = FuseBox.init({
  homeDir: "src",
  output: "dist/$name.js"
});

我们将让 FuseBox 知道我们打算使用 TypeScript 编译器

const fuse = FuseBox.init({
  homeDir: "src",
  output: "dist/$name.js",
  useTypescriptCompiler: true,
});

我们在配置文件的第一行标识了插件,但现在我们必须调用它们。 我们几乎按原样使用插件,但如果您想要更精细地控制选项,请务必查看 CSSPluginSVGPluginWebIndexPlugin 提供的功能。

const fuse = FuseBox.init({
  homeDir: "src",
  output: "dist/$name.js",
  useTypescriptCompiler: true,
  plugins: [
    CSSPlugin(),
    SVGPlugin(),
    WebIndexPlugin({
      template: "src/index.html"
    })
  ]
});

const { FuseBox, CSSPlugin, SVGPlugin, WebIndexPlugin } = require("fuse-box");

const fuse = FuseBox.init({
  homeDir: "src",
  output: "dist/$name.js",
  useTypescriptCompiler: true,
  plugins: [
    CSSPlugin(),
    SVGPlugin(),
    WebIndexPlugin({
      template: "src/index.html"
    })
  ]
});
fuse.dev();
fuse
  .bundle("app")
  .instructions(`>index.js`)
  .hmr()
  .watch()

fuse.run();

FuseBox 允许我们配置一个 开发服务器。 我们可以定义端口、SSL 证书,甚至在构建时在浏览器中打开应用程序。

在这个示例中,我们将简单地使用默认环境

fuse.dev();

在接下来的捆绑指令之前定义开发环境非常重要

fuse
  .bundle("app")
  .instructions(`>index.js`)
  .hmr()
  .watch()

这是什么鬼东西? 当我们初始化 FuseBox 实例时,我们使用 dist/$name.js 指定了一个输出。 $name 的值由 bundle() 方法提供。 在我们的例子中,我们将值设置为 app。 这意味着当应用程序被捆绑时,输出目标将是 dist/app.js

instructions() 方法定义了 FuseBox 如何处理代码。 在我们的例子中,我们告诉它从 index.js 开始,并在加载后执行它。

hmr() 方法用于我们需要在文件更改时更新用户的情况,这通常涉及在文件更改时更新浏览器。 同时,watch() 在每次保存更改后重新捆绑捆绑的代码。

有了这些,我们将通过在配置文件的末尾使用 fuse.run() 启动构建过程来结束它。 以下是我们刚刚介绍的所有内容放在一起

const { FuseBox, CSSPlugin, SVGPlugin, WebIndexPlugin } = require("fuse-box");

const fuse = FuseBox.init({
  homeDir: "src",
  output: "dist/$name.js",
  useTypescriptCompiler: true,
  plugins: [
    CSSPlugin(),
    SVGPlugin(),
    WebIndexPlugin({
      template: "src/index.html"
    })
  ]
});
fuse.dev();
fuse
  .bundle("app")
  .instructions(`>index.js`)
  .hmr()
  .watch()

fuse.run();

现在,我们可以通过运行 node fuse 从终端运行应用程序。 这将启动构建过程,该过程创建包含捆绑代码和我们在配置中指定的模板的 dist 文件夹。 构建过程完成后,我们可以将浏览器指向 http://localhost:4444/ 来查看我们的应用程序。

使用 Sparky 运行任务

FuseBox 包含一个任务运行器,可用于自动执行构建过程。 它被称为 Sparky,您可以将其视为类似于 Grunt 和 Gulp,区别在于它构建在 FuseBox 之上,并内置访问 FuseBox 插件和 FuseBox API。

我们不必使用它,但任务运行器通过自动执行我们原本必须手动执行的操作来使开发变得更加容易,而且使用专门为 FuseBox 设计的任务运行器是有意义的。

要使用它,我们将更新我们在 fuse.js 中的配置,从在文件顶部添加一些导入开始

const { src, task, context } = require("fuse-box/sparky");

接下来,我们将定义一个上下文,它看起来与我们已经拥有的类似。 我们基本上将我们所做的事情包装在一个上下文中,并使用 setConfig(),然后在返回中初始化 FuseBox

context({
  setConfig() {
    return FuseBox.init({
      homeDir: "src",
      output: "dist/$name.js",
      useTypescriptCompiler: true,
      plugins: [
        CSSPlugin(),
        SVGPlugin(),
        WebIndexPlugin({
          template: "src/index.html"
        })
      ]
    });
  },
  createBundle(fuse) {
    return fuse
      .bundle("app")
      .instructions(`> index.js`)
      .hmr();
  }
});

可以将类、函数或普通对象传递给上下文。 在上述情况下,我们正在传递函数,特别是 setConfig()createBundle()setConfig() 初始化 FuseBox 并设置插件。 createBundle() 做了您可能从名称中推断出的事情,即捆绑代码。 再次强调,与我们之前所做的事情的不同之处在于我们将这两种功能嵌入到不同的函数中,这些函数包含在上下文对象中。

我们希望我们的任务运行器运行任务,对吧? 以下是我们可以定义的几个示例

task("clean", () => src("dist").clean("dist").exec());
task("default", ["clean"], async (context) => {
  const fuse = context.setConfig();
  fuse.dev();
  context.createBundle(fuse);
  await fuse.run()
});

第一个任务将负责清理 dist 目录。 第一个参数是任务的名称,第二个参数是任务运行时调用的函数。
要调用第一个任务,我们可以在终端中执行 node fuse clean

当一个任务命名为 default(这是第二个任务中的第一个参数)时,该任务将是运行 node fuse 时默认调用的任务——在本例中,这是我们配置中的第二个任务。 其他任务需要在终端中显式调用,例如 node fuse <task_name>

因此,我们的第二个任务是默认任务,它传递了三个参数。 第一个是任务的名称(default),第二个(["clean"])是一个在执行任务本身之前应该调用的依赖项数组,第三个是一个函数(fuse.dev()),它获取初始化的 FuseBox 实例并开始捆绑和构建过程。

现在,我们可以在终端中使用 node fuse 来运行这些操作。 您可以选择将它们添加到您的 package.json 文件中,如果您觉得更舒适和熟悉。 脚本部分将如下所示

"scripts": {
  "start": "node fuse",
  "clean": "node fuse clean"
},

总结!

总而言之,FuseBox 是一个很有趣的 Webpack 替代方案,可以满足你所有的应用程序打包需求。正如我们所见,它提供了我们都喜欢的 Webpack 的强大功能,但配置过程要简单得多,这使得它更容易上手,这得益于内置的 Typescript 支持、性能考虑以及旨在利用 FuseBox API 的任务运行器。

我们所看的是一个相当简单的示例。在实践中,你可能会使用更复杂的应用程序,但概念和原则是一样的。很高兴知道 FuseBox 能够处理超出其内置功能的内容,但初始设置仍然非常简化。

如果你想了解更多关于 FuseBox 的信息,它有一个 网站文档,显然是不错的起点。以下链接也有助于你更深入地了解其他人如何在项目中设置和使用它。