描述在 grunt 中实现基座应用按需加载子应用
解读
国内微前端落地场景里,基座应用(主应用) 往往采用“先加载壳,后拉子”的按需策略,以减少首屏资源体积。Grunt 作为老牌任务运行器,本身不负责运行时调度,但可以通过构建期拆分产物 + 运行时动态注入的方式,让基座在浏览器端按路由或事件懒加载子应用。面试时,考官想确认两点:
- 你能否用 Grunt 把子应用构建成可独立消费的 UMD/ES 模块包;
- 你能否在基座侧提供轻量运行时脚本完成动态加载,并保证公共依赖去重与沙箱隔离。
知识点
- 多入口 Grunt 配置:利用
grunt-webpack或grunt-browserify把每个子应用打成独立 bundle,输出到dist/subApps/[name]/目录,并生成manifest.json(含name、url、deps、css字段)。 - 文件指纹与版本:通过
grunt-filerev给 js/css 加 hash,保证增量发布;grunt-manifest把最新映射写入subApps.json,供基座运行时拉取。 - 运行时加载器:基座引入 2k 左右的
subAppLoader.js,内部用System.import(或原生import())按路由匹配拉取对应entry.js;拉取前用window.__sharedDeps做 externals 去重,拉取后把子应用生命周期函数bootstrap/mount/unmount注册到全局路由表。 - 沙箱与样式隔离:构建期通过
grunt-postcss给子应用样式加前缀选择器(.subApp-[name]),运行时用ShadowDOM或proxySandbox做 JS 隔离。 - 本地联调:
grunt-contrib-connect起服务,配合grunt-contrib-watch监听子应用源码,文件变动后热重编并推送websocket事件,基座收到后重新执行 import() 实现无刷新子应用替换。
答案
-
构建阶段
在Gruntfile.js中定义subApps多任务:- 用
grunt-webpack配置entry: { subApp1: './src/subApp1/index.js', subApp2: './src/subApp2/index.js' },output.library='subApp_[name]',libraryTarget='umd'; - 通过
externals: { vue: 'window.Vue', rxjs: 'window.Rx }把基座已托管的依赖排除,减少子包体积; - 并行执行
grunt-contrib-uglify压缩、grunt-contrib-cssmin合并样式; - 最后执行自定义任务
grunt.registerTask('genManifest', function () { … })把每个子应用的js/css/hash写入dist/subApps/manifest.json。
- 用
-
部署阶段
CI 把dist/subApps/整体推到阿里云 OSS 或公司 CDN,manifest.json设置Cache-Control: no-cache,保证基座每次都能拿到最新映射。 -
运行阶段
基座在router.beforeEach中判断即将进入的路由对应哪个子应用,若未加载则:const { js, css } = await fetch('/subApps/manifest.json').then(r=>r.json()).then(m=>m[subAppName]); if(css) loadCSS(css); // 动态插入 <link> const module = await import(js); // 按需拉取 await module.bootstrap(); // 启动子应用 await module.mount({ container: '#subApp-view', baseRoute: '/subApp1' });卸载时调用
module.unmount()并移除样式表,完成干净销毁。
通过以上三步,即可在纯 Grunt 体系内实现“构建期拆包 + 运行时按需”的微前端方案,兼顾老项目渐进改造与首屏性能优化。
拓展思考
- 共享依赖版本冲突:若子应用依赖的 Vue 版本不一致,可在构建期用
grunt-replace把子应用内的Vue重命名为Vue_subApp1,运行时再挂载到window,避免全局变量污染。 - 模块联邦替代方案:如果团队后续迁到 webpack5,可用
ModuleFederationPlugin把子应用打成remoteEntry.js,Grunt 侧通过grunt-execute调用 webpack 完成混合构建,实现更细粒度共享。 - 灰度发布:在
manifest.json里增加grayUsers字段,基座根据用户 ID 判断拉取js还是js?gray=1,实现子应用级灰度,无需整站回滚。