使用 grunt-jsonschema 生成 Vue 表单组件

解读

面试官抛出这道题,并不是让你背诵 grunt-jsonschema 的 API,而是考察三件事:

  1. 你是否真的用 Grunt 干过“把一份 JSON Schema 转成可直接投入生产的 Vue 单文件组件”这种脏活累活;
  2. 你是否理解国内前端工程化交付链路的痛点(后端给 Swagger / YApi 导出的 Schema 前端得手工写表单,效率低、容易错);
  3. 你能否在不 eject 掉老项目 Grunt 体系的前提下,把新轮子无缝接入,兼顾存量与未来。

一句话:这道题考的是**“老基建(Grunt)+ 新需求(Vue 表单自动化)”的落地能力**。

知识点

  1. grunt-jsonschema 插件本质:它只是一个“任务包装器”,核心逻辑在底层的 jsonschema-to-vue-form 类库(社区有开源实现,如 @lljj/vue-json-schema-form)。Grunt 任务负责读文件、调库、写文件、触发后续任务
  2. Schema 方言差异:国内后端普遍用 OpenAPI 3.0 / Swagger 2.0 导出的 Schema,字段名风格下划线、必填字段 required 数组、枚举值 enum 等,与 jsonschema-to-vue-form 默认支持的 Draft-07 有差异,需要预处理脚本做归一化
  3. Vue 版本兼容:老项目可能还是 Vue2 + Options API,新组件默认生成的是 Vue3 Composition API,必须在任务里根据 package.json 的 vue 版本号动态切换模板引擎
  4. 组件资产沉淀:生成的 .vue 文件需要自动注入到 src/components/auto-form/ 目录,并同步更新 components.js barrel 文件,否则 webpack 的 require.context 扫不到。
  5. CI 卡点:国内很多公司用 GitLab-CI + 内网 Nexus,Grunt 任务必须在 docker 镜像里提前缓存 node_modules,否则每次装 grunt-jsonschema 会超时 10min+,被运维打回。
  6. 性能红线:400+ 字段的大表单,如果一次性生成会撑爆 Node 老生代内存(默认 1.4 G),需要分片流式写入并加 --max-old-space-size=4096。
  7. 合规审计:金融、政务项目要求生成的代码必须附带来源注释(“本文件由 grunt-jsonschema 自动生成,勿手动修改”),否则审计直接打回。

答案

下面给出一份可直接搬进国内老项目的 Gruntfile 片段,兼顾 Vue2/3 双版本、OpenAPI 方言、性能、合规、CI 缓存六大痛点。
(假设后端每天凌晨把最新 Swagger 推到 swagger/v1.json,我们要在 3 分钟内产出 200+ 表单组件并触发 Livereload)

module.exports = function(grunt) {
  // 1. 读项目 Vue 版本,决定模板
  const vueVersion = grunt.file.readJSON('package.json').dependencies.vue;
  const isVue2 = vueVersion.startsWith('^2.');

  // 2. 缓存目录,解决 CI 每次重装问题
  const cacheDir = process.env.CI ? '/tmp/grunt-cache' : '.grunt-cache';
  grunt.initConfig({
    // 2.1 先归一化 OpenAPI -> Draft-07
    swagger_convert: {
      options: {
        input: 'swagger/v1.json',
        output: '<%= cacheDir %>/schema.json',
        // 把下划线字段转驼峰,适配 Element-UI
        fieldMap: { snake_case: 'camelCase' }
      }
    },

    // 2.2 核心:schema -> Vue SFC
    jsonschema_vue: {
      options: {
        schema: '<%= cacheDir %>/schema.json',
        outDir: 'src/components/auto-form',
        // 根据 Vue 版本切换模板
        templateDir: isVue2 ? 'build/grunt-templates/vue2' : 'build/grunt-templates/vue3',
        // 大表单分片
        chunkSize: 50,
        // 合规注释
        banner: '/* 本文件由 grunt-jsonschema 自动生成,勿手动修改 */'
      }
    },

    // 2.3 barrel 文件自动更新
    concat: {
      componentsIndex: {
        src: ['src/components/auto-form/*.vue'],
        dest: 'src/components/auto-form/index.js',
        options: {
          process: function(src, filepath) {
            const name = filepath.match(/([^/]+)\.vue$/)[1];
            return `export { default as ${name} } from './${name}.vue';`;
          }
        }
      }
    },

    // 2.4 监听
    watch: {
      swagger: {
        files: ['swagger/v1.json'],
        tasks: ['swagger_convert', 'jsonschema_vue', 'concat:componentsIndex'],
        options: { livereload: 35729 }
      }
    }
  });

  // 3. 任务注册
  grunt.registerTask('default', ['swagger_convert', 'jsonschema_vue', 'concat:componentsIndex']);
};

关键踩坑提示

  • 如果公司内网 Nexus 托管了私有 @scope 包,务必在 .npmrc 里先配置 registry,否则 grunt-jsonschema 安装会 404。
  • 生成的组件默认用 Element-UI 的 el-form,如果项目用 Ant Design Vue,需要在模板里把 el- 前缀批量替换为 a-,并改校验规则绑定方式。
  • Vue2 项目需要额外引入 v-model.sync 补丁,否则双向绑定失效;模板里要加 model: { prop: 'value', event: 'change' }

拓展思考

  1. 如何做到“只生成差异”
    国内后端 Schema 每天小改,全量生成会导致 Git 提交 200+ 文件,CodeReview 爆炸。可以在任务里先对 Schema 做深度 diff,只重写变更的模型,然后用 grunt-git-modified 插件把本次受影响的组件路径写入临时文件,后续jest 单元测试只跑这些文件,3 分钟缩短到 30 秒。

  2. 如何回滚
    金融场景要求可回溯到任意一天的表单组件快照。可以把生成结果强制提交到独立分支 auto-form-history,并用 GitLab CI 的 only: variables 做每日归档;紧急回滚时直接 git checkout auto-form-history -- src/components/auto-form 即可,无需重新跑 Grunt

  3. 如何平滑迁移到 Vite / esbuild
    老项目暂时不能扔 Grunt,但新模块想用 Vite。可以把 grunt-jsonschema 生成的组件同时输出到两套目录

    • src/components/auto-form 供老 Webpack 用
    • vite-modules/auto-form 供 Vite 用
      然后在 vite.config.ts 里加 optimizeDeps.include: ['@lljj/vue-json-schema-form']让 Vite 把依赖预构建,避免二次打包;两套目录通过 gitignore 的 negative pattern 保证互不污染,最终实现“Grunt 与 Vite 共存”,给老板一个过渡方案。