创见博客
webpack 处理 JS 资源:Babel、Polyfill 与 ESLint
七崽爱吃小饼干2026/09/15阅读 0专栏 webpack构建基础

很多初学者会误以为「用了 webpack 就能写 ES6+」,其实这是个误解。webpack 对 JavaScript 的「理解」仅限于模块化语法——它能识别 import / export,从而构建依赖图并把模块打包在一起。但除此之外的新语法,webpack 本身并不会帮你转译。本文讲清这个能力边界,并介绍处理 JS 资源所需的 loader 与 ESLint 等工具。

一、webpack 到底能处理什么

webpack 是一个模块打包工具,不是编译器。它对 JS 的处理可以概括为:

  • 能识别模块语法:import、export、动态 import()。
  • 不做语法降级:箭头函数、let/const、模板字符串、解构、可选链 ?.、空值合并 ??、async/await 等,webpack 原样保留。

也就是说,下面这段代码 webpack 打包后依然保持原样:

js
const fn = (a, b) => a?.b ?? b;
const res = await fetch('/api');

如果目标浏览器不支持这些语法,页面就会直接报错。webpack 不会替你解决兼容性问题——这正是 babel-loader 等工具存在的意义。

二、babel-loader:转译 JS 语法

Babel 是事实上的 JS 编译器,babel-loader 让 webpack 在打包时调用 Babel。

1. 安装

bash
npm install babel-loader @babel/core @babel/preset-env --save-dev

2. 配置 loader

js
module.exports = {
  module: {
    rules: [
      {
        test: /\.m?js$/,
        exclude: /node_modules/, // 第三方包通常已编译,无需重复处理
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env'],
          },
        },
      },
    ],
  },
};

3. preset-env 与 browserslist

@babel/preset-env 会根据目标环境,自动决定要启用哪些语法转换插件。目标环境通过 browserslist 声明,可写在 package.json 或 .browserslistrc:

json
{
  "browserslist": [
    "> 0.5%",
    "last 2 versions",
    "not dead"
  ]
}

这样既能覆盖目标浏览器,又不会把代码转译得过于老旧。

4. 开启 Babel 缓存

Babel 转译较慢,建议开启缓存:

js
{
  loader: 'babel-loader',
  options: {
    cacheDirectory: true,
    presets: ['@babel/preset-env'],
  },
}

三、Polyfill:补齐缺失的 API

语法转换解决的是「写法不兼容」,但像 Promise、Array.prototype.includes、Object.assign 这类内置 API,光靠语法转译是补不上的,需要 polyfill。

在 webpack 5 + Babel 7 中,推荐用 core-js:

bash
npm install core-js --save-dev

然后通过 useBuiltIns: 'usage' 按需注入:

js
{
  loader: 'babel-loader',
  options: {
    presets: [
      [
        '@babel/preset-env',
        {
          useBuiltIns: 'usage', // 按实际用到的 API 自动引入
          corejs: 3,
        },
      ],
    ],
  },
}

useBuiltIns 有三个常用取值:

  • false:不自动引入 polyfill(默认)。
  • 'entry':在入口 import 'core-js',全量引入。
  • 'usage':按代码实际使用情况按需引入,产物体积更小。

使用 transform-runtime 避免污染全局

如果开发的是类库,不希望 polyfill 污染使用者的全局环境,可以用 @babel/plugin-transform-runtime:

bash
npm install @babel/plugin-transform-runtime @babel/runtime --save-dev
js
{
  loader: 'babel-loader',
  options: {
    presets: ['@babel/preset-env'],
    plugins: [
      ['@babel/plugin-transform-runtime', { corejs: 3 }],
    ],
  },
}

它把 helper 和 polyfill 从内联改为按模块引入,避免重复注入。

四、处理 TypeScript

TS 文件有两种常见处理方式。

方式一:ts-loader(类型检查完整,但较慢)

bash
npm install ts-loader typescript --save-dev
js
{
  test: /\.tsx?$/,
  use: 'ts-loader',
  exclude: /node_modules/,
}

方式二:babel-loader + @babel/preset-typescript(转译快,但只去类型、不做类型检查)

bash
npm install @babel/preset-typescript --save-dev
js
{
  loader: 'babel-loader',
  options: {
    presets: ['@babel/preset-env', '@babel/preset-typescript'],
  },
}

若追求类型安全,可再单独跑 tsc --noEmit 做检查,把类型检查与打包解耦。

五、ESLint:约束代码规范

转译解决「能不能运行」,ESLint 解决「写得好不好」。在 webpack 中集成 ESLint 需要 eslint-webpack-plugin(webpack 5 已弃用旧的 eslint-loader)。

1. 安装

bash
npm install eslint eslint-webpack-plugin --save-dev

2. 配置

js
const ESLintPlugin = require('eslint-webpack-plugin');

module.exports = {
  plugins: [
    new ESLintPlugin({
      extensions: ['js', 'jsx', 'ts', 'tsx'],
      exclude: 'node_modules',
      fix: true, // 自动修复可修复的问题
      failOnError: true,
    }),
  ],
};

配合项目根目录的 eslint.config.js(或旧版 .eslintrc.*)定义规则。这样每次构建都会检查代码,问题会直接在终端和浏览器中提示。

六、其他处理 JS 的工具

  • terser-webpack-plugin:生产环境代码压缩。webpack 5 在 mode: 'production' 下已内置,一般无需手动配置。
  • thread-loader:把耗时的 loader 放到 worker 线程执行,加快大型项目构建。
  • swc-loader / esbuild-loader:基于 Rust/Go 的极速转译方案,可作为 babel-loader 的性能替代。

七、完整配置示例

js
const path = require('path');
const ESLintPlugin = require('eslint-webpack-plugin');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'js/[name].[contenthash:8].js',
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.m?jsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            cacheDirectory: true,
            presets: [
              ['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }],
              '@babel/preset-react',
            ],
          },
        },
      },
      {
        test: /\.tsx?$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }],
              '@babel/preset-typescript',
            ],
          },
        },
      },
    ],
  },
  plugins: [
    new ESLintPlugin({
      extensions: ['js', 'jsx', 'ts', 'tsx'],
      fix: true,
    }),
  ],
};

小结

webpack 只负责「把模块拼起来」,真正让新语法在旧环境跑起来的是 Babel,补齐内置 API 的是 polyfill(core-js),保证代码质量的是 ESLint。三者职责不同、缺一不可:

工具解决的问题
babel-loaderES6+ 语法转译为兼容代码
core-js + preset-env补齐 Promise、includes 等内置 API
@babel/preset-typescript / ts-loader处理 TypeScript
eslint-webpack-plugin代码规范与静态检查
terser-webpack-plugin生产环境压缩混淆

理清这条分工链,就不会再纠结「为什么我用了 webpack 代码还是不兼容」了。

评论
0/100