使用 Fusebox 和 React

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.js 和 index.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: [ // HIGHLIGHT
    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 的信息,它的 网站文档 显然是一个很好的起点。以下链接也有助于更全面地了解其他人如何在项目中设置和使用它。