【问题标题】:How can Babel be used without bundler, but with a dev-experience similar to webpack?Babel 如何在没有 bundler 的情况下使用,但具有类似于 webpack 的开发体验?
【发布时间】:2019-03-19 05:41:04
【问题描述】:

我正在尝试使用ES6 modules 构建一个不捆绑的新项目。我还是想用babel-7把TypeScript和JSX翻译成JS。我发现很难弄清楚如何为其设置开发服务器。我找不到任何与webpack-dev-server(热模块重新加载、浏览器同步、文件观察器)类似的“babel-dev-server”。

一种可能性是使用browser sync 作为静态服务器,例如dist 并并行运行 babel src --out-dir dist --watch 之类的东西。但这不包括热重载,对我来说似乎有点笨拙。此外,如果您可以为 JS 文件提供哈希以更好地控制缓存,那么它对于构建和开发步骤仍然有用。或者我可以配置一个像 webpack 这样的构建工具,使它不执行捆绑但仍然执行一些转换(比如将哈希放在导入文件名中)?

【问题讨论】:

  • 你的最终目标是什么?为什么要避免使用 webpack-dev-server?如果您想要热模块重新加载等,那么这将是一个很好的工具。
  • sourcemap 能解决你的问题吗?它使您可以在浏览器上查看单独的文件。
  • @ToddChaffee 我相信由于 HTTP/2 和 import/export 的原生浏览器支持,捆绑既不是最佳的也不是必需的。它对代码的更改如此之大,以至于您无法确切知道它是如何工作的,并使开发过程变得复杂(例如,我发现 webpack 和捆绑的原理真的很难向初学者理解或解释)。这就是为什么我想尝试如何在没有 webpack 的情况下生活,或者至少没有“神奇”的捆绑过程。

标签: javascript webpack babeljs es6-modules hot-reload


【解决方案1】:

原型制作方式

一种非常简单的方法是将服务器和转译视为单独的步骤

您可以使用独立版本的 babel 作为您加载的第一个脚本,这样您就可以在 javascript 文件的 html 文档中编写 jsx 而无需编译它们。

只需添加来自 https://cdnjs.com/libraries/babel-standalone/ 的 cdn 链接作为脚本,如下所示:

<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
    <script src="/your/jsx/here.js"></script>
    <script>
      // or here
    </script>
  </head>
  <body>
    <div id="application" />
    <noscript>This app needs javascript enabled in order to run.</noscript>
  </body>
</html>

这将允许您使用任何监视文件的网络服务器真正快速地制作原型。您可以使用任何任务运行插件(即 grunt 或 gulp)来执行此操作,或者如果您使用的是 Visual Studio,请查看 LiveServer 插件。

当您转向生产级时,您可能不想包含整个 babel 库。请参阅其他两种方法。

Webpack 方式

您问的是如何在不捆绑的情况下使用 webpack。这可以使用file loader plugin 来完成,使用glob 模式分别加载每个文件。请确保这是否确实是您需要的。如果您只想调试代码并在编译后将其与原始文件相关联,那么您只需要使用捆绑和sourcemaps 的标准 webpack 配置。

Taskrunner 方式

一种更好地控制每个文件的处理方式的方法是,您可以使用任务运行程序为您执行编译步骤。以下是 taskrunner https://gulpjs.com/ 的简化示例配置。

gulpfile.js

const gulp = require('gulp');
const watch = require('gulp-watch');
const webpackStream = require('webpack-stream');
const webpack = require('webpack');
const eslint = require('gulp-eslint');

gulp.task('watch', function() {
  return watch('src/**.js', ['compile']);
});

gulp.task('lint', function() {
  return gulp.src(['src/*.js', 'src/*/*.js'])
    .pipe(eslint({
      parser: 'babel-eslint',
      parserOptions: {
        ecmaFeatures: {
          jsx: true
        },
        sourceType: 'module'
      }
    }))
    .pipe(eslint.format())
    .pipe(eslint.failAfterError());
});

gulp.task('compile', ['lint'], function() {
  return gulp.src('src/main.js')
    .pipe(webpackStream({
      output: {
        filename: 'main.js',
        libraryTarget: 'commonjs2',
        sourceMapFilename: 'main.js.map',
      },
      plugins: [],
      module: {
        loaders: [
          {
            test: /\.js$/,
            loader: 'babel-loader',
            query: {
              presets: [
                require.resolve('babel-preset-es2015'),
                require.resolve('babel-preset-stage-0'),
              ],
            },
          },
        ],
      },
    }), webpack)
    .pipe(gulp.dest('dist/'));
});

可以使用gulp watch 运行此示例文件。它会观察文件是否有机会以及何时触发其他任务。

我只有一个 webpack 的例子,但你可以用任何其他编译器组件替换它,或者如果你想要的话,甚至可以编写你自己的编译步骤 (可能你不需要)

通过这种方式,您可以精确控制文件经过的每一步。其中大部分(以及更多)也可以使用 Webpack 方式 来实现。但是,当将每个文件作为单独的包处理时,它的缺点是将其所有样板插入每个已处理文件的顶部。最终,common chunks plugin 可能会有所作为。

【讨论】:

  • 非常感谢您的解释!特别是在开发周期中,我发现@babel/standalone 的方法令人兴奋(我会使用 babel7 ;-))。我发现其他方法对于让应用程序为生产做好准备非常有用。我还不会“接受”你的答案,因为我想先测试它(如果我能找到时间)如何组装它,以及它是否能按我希望的那样工作。
  • 太好了,如果您在实施过程中遗漏了某些特定内容,请告诉我。也许我们也可以帮助其他人。
【解决方案2】:

使用最新版本的Snowpack(以前的@pika/web)现在应该可以了!

来自他们的网站:

TL;DR - 使用 Snowpack,您可以构建现代 Web 应用程序(使用 React、Vue 等)而无需打包程序(如 Webpack、Parcel、Rollup)。每次点击保存时,不再等待您的捆绑器重建您的网站。相反,每个更改都会立即反映在浏览器中。

还有他们的“工作原理”:

  1. 无需捆绑每次更改,只需在 npm 安装后立即运行一次 Snowpack。
  2. Snowpack 将您的依赖项作为单个 JS 文件重新安装到新的 web_modules/ 目录中。它永远不会触及您的源代码。
  3. 编写代码,通过 ESM 导入导入这些依赖项,然后在浏览器中全部运行。
  4. 跳过捆绑步骤,点击保存后,您的更改会立即反映在浏览器中。
  5. 继续使用您最喜欢的 Web 框架和构建工具!支持 Babel 和 TypeScript。

查看https://www.snowpack.dev/ 了解更多信息,他们的文档做得很好,看起来很有前途!

【讨论】:

    【解决方案3】:

    使用 webpack 和源映射,它不会改变你的代码。虽然最初设置这可能是一个挑战,但一旦您开始使用它,您就可以在浏览器调试工具中查看原始源代码,就像它们在磁盘上显示的一样。 VS Code 编辑器也很好地支持了此功能,允许您直接在编辑器中设置断点并查看变量的值,而无需使用浏览器开发人员工具。

    但是,如果您仍然打算让它与您的原始源文件一起使用,那么您的 ES6 代码应该只适用于大多数现代浏览器是正确的

    对于实时重新加载,您可以查看npm livereload package

    或者你可以自己动手并弄清楚 webpack-dev-server 是如何做到的。他们使用 chokidar npm 包来观察文件系统的变化,然后他们通过 web 套接字通知浏览器。您可能只需稍加努力就可以将相似的东西拼凑在一起。

    这里是webpack-dev-server initiates it:

    const watcher = chokidar.watch(watchPath, options);
    
    watcher.on('change', () => {
      this.sockWrite(this.sockets, 'content-changed');
    });
    

    显然有一些 JavaScript 在浏览器中运行,等待该消息的 websocket。

    【讨论】:

    • 经过简短的审查后,它似乎变得相当复杂。您必须使用 websocket 实现自己的节点服务器,然后在客户端和服务器端实现热重载功能。在我的简短回顾中,我仍然无法弄清楚这是如何工作的。熟悉节点服务器、websockets 和 HMR 原理绝对是一个很好的练习,但不幸的是我目前没有足够的空闲时间。供参考:medium.com/@maheshsenni/… 可能是一个很好的资源。
    【解决方案4】:

    你可以使用像 Emit All 这样的 Webpack 插件。

    【讨论】:

    • 看起来不错。是否可以与 webpack-dev-server 集成有经验吗?
    • @PutziSan 这应该不是问题。