使用webpack.externals排除依赖时出现Object.defineProperty调用错误
问题背景
我在构建用户脚本模板时,依赖tsyringe和reflect-metadata两个包,希望通过Tampermonkey的@require字段从jsdelivr CDN加载它们,而非打包进最终文件。用Webpack的externals属性传入package.json的dependencies内容后,打包成功但运行时报错:
Uncaught (in promise) TypeError: Object.defineProperty called on non-object
错误指向tsyringe代码中的Object.defineProperty(exports, "__esModule", { value: !0 })语句,尝试使用webpack-node-externals也未解决问题。
问题原因
Webpack默认的externals配置针对CommonJS模块,但Tampermonkey加载的CDN包多为UMD或全局变量形式。直接将dependencies传入externals,会导致Webpack生成的代码错误地以CommonJS模块的方式引用全局包,触发Object.defineProperty的非对象调用错误。
解决方案
1. 精准配置Webpack externals
替换原有externals配置,明确指定两个包的全局变量映射:
// webpack.config.ts externals: { 'tsyringe': { root: 'tsyringe', // 对应CDN包暴露的全局变量 commonjs: 'tsyringe', commonjs2: 'tsyringe', amd: 'tsyringe' }, 'reflect-metadata': { root: 'Reflect', // reflect-metadata暴露的全局变量是Reflect commonjs: 'reflect-metadata', commonjs2: 'reflect-metadata', amd: 'reflect-metadata' } }
2. 保证用户脚本依赖加载顺序
Tampermonkey脚本头部必须先加载reflect-metadata,再加载tsyringe(tsyringe依赖前者初始化):
// ==UserScript== // @name 你的用户脚本名称 // @require https://cdn.jsdelivr.net/npm/reflect-metadata@latest/Reflect.js // @require https://cdn.jsdelivr.net/npm/tsyringe@latest/dist/tsyringe.umd.js // ==/UserScript==
3. 验证TypeScript配置
确保tsconfig.json开启装饰器相关选项,否则tsyringe的依赖注入会失效:
// tsconfig.json { "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true } }
原理说明
通过明确externals的root字段,Webpack会生成正确的代码引用全局变量,而非尝试用CommonJS的exports方式处理,避免了非对象上调用Object.defineProperty的错误。同时严格的依赖加载顺序保证了tsyringe初始化时,reflect-metadata的API已经可用。
内容的提问来源于stack exchange,提问作者Kenya-West

