基于esbuild Hybrid插件优化多IIFE文件与单ESM包构建问询
优化SlickGrid的IIFE/ESM双构建方案
核心目标
同时输出两类产物:
- 独立IIFE文件:挂载到
window.Slick,支持通过<script>标签按需加载扩展 - 单ESM包:保留原生模块化导入导出逻辑
且源码中无需添加冗余的IIFE/ESM环境判断,通过esbuild插件自动适配两种构建模式。
优化后的esbuild插件实现
1. 插件核心逻辑
利用esbuild的onLoad钩子拦截源码文件,根据构建格式自动处理导入导出:
- IIFE模式:将命名导入替换为
window.Slick的属性引用,将导出挂载到window.Slick - ESM模式:完全保留源码中的标准模块化语法
2. 插件代码
const fs = require('fs'); module.exports = function SlickBuildPlugin(options = {}) { const isEsmBuild = options.format === 'esm'; return { name: 'slick-build-plugin', setup(build) { // 处理源码文件的导入导出逻辑 build.onLoad({ filter: /\.js$/ }, async args => { let content = await fs.promises.readFile(args.path, 'utf8'); if (!isEsmBuild) { // 替换命名导入为window.Slick属性引用 content = content.replace( /import\s+{([^}]+)}\s+from\s+'slickgrid\/([^']+)';/g, (_, imports) => { return imports.split(',').map(item => { const name = item.trim(); return `const ${name} = window.Slick.${name};`; }).join('\n'); } ); // 将导出挂载到window.Slick content = content.replace( /export\s+{([^}]+)};/g, (_, exports) => { return exports.split(',').map(item => { const name = item.trim(); return `window.Slick.${name} = ${name};`; }).join('\n'); } ); } return { contents: content, loader: 'js' }; }); // 处理入口文件,IIFE模式包裹自执行函数 build.onLoad({ filter: /entry\.js$/ }, async args => { let content = await fs.promises.readFile(args.path, 'utf8'); if (!isEsmBuild) { content = `(function() {\n${content}\n})();`; } return { contents: content, loader: 'js' }; }); } }; };
3. 构建配置
在esbuild中定义双构建任务,分别指定格式并传入插件:
const esbuild = require('esbuild'); const SlickBuildPlugin = require('./slick-build-plugin'); // 构建IIFE包 esbuild.build({ entryPoints: ['src/entry.js'], outfile: 'dist/slickgrid.iife.js', format: 'iife', globalName: 'Slick', plugins: [SlickBuildPlugin({ format: 'iife' })], bundle: true, }).catch(() => process.exit(1)); // 构建ESM包 esbuild.build({ entryPoints: ['src/entry.js'], outfile: 'dist/slickgrid.esm.js', format: 'esm', plugins: [SlickBuildPlugin({ format: 'esm' })], bundle: true, }).catch(() => process.exit(1));
源码编写规范
源码直接使用标准ESM语法,无需任何环境判断:
// src/extensions/slickGridFilter.js import { Grid } from 'slickgrid/core'; export class GridFilter { constructor(grid) { this.grid = grid; } // 业务逻辑实现 }
插件会自动完成适配:
- IIFE构建时:将
import { Grid } from 'slickgrid/core'替换为const Grid = window.Slick.Grid;,将export { GridFilter }替换为window.Slick.GridFilter = GridFilter; - ESM构建时:完全保留原有导入导出逻辑
按需加载使用示例
IIFE格式
用户可通过<script>标签按需加载扩展:
<script src="dist/slickgrid.iife.js"></script> <script src="dist/extensions/slickGridFilter.iife.js"></script> <script> const grid = new Slick.Grid('#grid', data, columns, options); const filter = new Slick.GridFilter(grid); </script>
ESM格式
支持原生import按需加载:
import { Grid } from './dist/slickgrid.esm.js'; import { GridFilter } from './dist/extensions/slickGridFilter.esm.js'; const grid = new Grid('#grid', data, columns, options); const filter = new GridFilter(grid);
内容的提问来源于stack exchange,提问作者ghiscoding
相关产品推荐
相关产品推荐

