配置 grunt-babel 支持 React 17 新的 JSX Transform

解读

这道题表面上是“配一个插件”,但面试官真正想验证的是:

  1. 你是否清楚 React 17 新旧 JSX Transform 的差异(不再需要 import React from 'react');
  2. 能否在** Grunt 生态里正确串联 babel 插件与 preset**,而不是只会 webpack;
  3. 是否理解国内镜像源、锁定版本、团队协作等工程化细节;
  4. 有没有可验证、可回滚、可分环境的落地习惯。
    答得过于简单(“装个 preset-react 就行”)会被追问“如何证明生效”“如何兼容老代码”;答得过于复杂(“自己写 AST 插件”)会被质疑 ROI。因此,给出一条最贴近企业 CI/CD 实践的“刚刚好”路径才是高分关键。

知识点

  • @babel/preset-react 从 7.16 起把 runtime 默认值从 classic 改成 automatic,对应 React 17 的 jsx-runtime;
  • grunt-babel 只是 Babel 的壳,核心在 .babelrcbabel.config.js
  • Gruntfile 里要同时配 grunt-contrib-clean、grunt-contrib-copy、grunt-contrib-watch,保证“编译—替换—刷新”闭环;
  • 国内镜像务必锁版本npm i -D @babel/core@7.22.0 @babel/preset-react@7.22.0 --save-exact),否则次日构建就崩;
  • 需要双重验证:产物里不再出现 React.createElement 即生效;再跑一个 eslint-plugin-react 规则 react/react-in-jsx-scope: off 确保源码无 import React
  • 老项目共存策略:在 babel.config.js 里用 overrides 字段给 .classic.jsx 文件留退路,降低迁移风险。

答案

  1. 安装并锁定版本(淘宝源示例)
npm config set registry https://registry.npmmirror.com
npm i -D grunt-babel@8.0.0 @babel/core@7.22.0 @babel/preset-react@7.22.0 --save-exact
  1. 在项目根新建 babel.config.js,只放关键两行:
module.exports = {
  presets: [
    ['@babel/preset-react', { runtime: 'automatic' }] // 显式声明,防同事误改
  ]
};
  1. Gruntfile.js 精简但可扩展的写法:
module.exports = function(grunt) {
  grunt.initConfig({
    clean: { dist: 'dist' },
    babel: {
      options: { configFile: './babel.config.js' },
      dist: {
        files: [{
          expand: true,
          cwd: 'src',
          src: ['**/*.{js,jsx}'],
          dest: 'dist',
          ext: '.js'
        }]
      }
    },
    watch: {
      jsx: {
        files: ['src/**/*.{js,jsx}'],
        tasks: ['clean', 'babel']
      }
    }
  });

  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-babel');
  grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.registerTask('default', ['clean', 'babel']);
};
  1. 验证步骤
  • 编译后全局搜 React.createElement结果为 0 处命中
  • import React from 'react' 删掉,页面正常渲染,控制台无 React is not defined
  • package.json 补一条脚本 "build": "grunt",让 Jenkins/GitHub Actions 直接跑,保证 CI 与本地同源

拓展思考

  • 如何灰度迁移:先用 overrides 让新模块走 automatic,旧模块继续 classic,配合 eslint-disable-next-line react/react-in-jsx-scope 逐步清理;
  • 如何与 TypeScript 结合grunt-babelgrunt-ts 并存时,把 allowJs 打开,让 tsc 只做类型检查,产物仍交给 Babel 做 JSX 降级,速度提升 30%;
  • 如何提速二次编译:在 babel.config.js 里加 cacheDirectory: true,并把 .grunt-cache 写进 .gitignoreCI 中缓存该目录可让增量构建降到 2 s 内
  • 如何防止新同事误改配置:在 README.md 中写明“任何 Babel 升级需走 MR + 双评审”,并在 postinstall 脚本里校验 package-lock.json@babel/preset-react 版本号,不一致直接退出安装
  • 如何与测试链路打通:在 grunt-contrib-jest 里同样引用上述 babel.config.js保证单测、集成、生产三路编译一致,避免“本地跑得过、线上起不来”的经典翻车。