ReactJS与ExtJS3兼容问题:core-js致ExtJS3方法失效(IE11环境)
我之前碰到过类似框架间原型污染导致的冲突,你的情况是@babel/polyfill依赖的core-js给数组、可迭代对象的原型添加了values()方法,而ExtJS3的XTemplate在处理模板时,通过with(values)语句访问变量——当模板引用的属性不存在于当前数据对象时,会顺着原型链查找,误把core-js添加的values方法当成了数据属性,最终导致vs变量被赋值为这个方法而非预期的数据,破坏了XTemplate的渲染逻辑。
下面是几个可行的解决方法,按推荐程度排序:
方法1:改用@babel/plugin-transform-runtime替代@babel/polyfill(推荐)
这个方法从根源上避免了全局原型污染,因为@babel/plugin-transform-runtime不会修改全局对象的原型,而是使用沙箱化的polyfill实现,React需要的ES6+特性会通过runtime模块引入,完全不会影响ExtJS3的代码。
步骤:
- 移除项目入口文件中对
@babel/polyfill的引入(比如import '@babel/polyfill'或者require('@babel/polyfill')) - 安装必要依赖:
npm install --save-dev @babel/plugin-transform-runtime npm install --save @babel/runtime-corejs3
- 修改你的Babel配置文件(
.babelrc或babel.config.js),添加插件配置:
{ "presets": ["@babel/preset-react"], // 保留你原有的React预设 "plugins": [ ["@babel/plugin-transform-runtime", { "corejs": 3, // 指定使用core-js 3版本 "helpers": true, "regenerator": true, "useESModules": false }] ] }
重新构建项目后,core-js的polyfill就不会再污染全局原型,ExtJS3的XTemplate就能正常工作了。
方法2:给Ext.XTemplate打猴子补丁,移除with语句的使用
如果你暂时无法切换polyfill方案,可以通过重写ExtJS3的compileTpl方法,去掉导致原型链查找的with语句,改用显式的变量引用,这样就不会误取到原型上的values方法。
在你的项目初始化代码(确保在ExtJS3加载之后、模板使用之前执行)中添加以下代码:
Ext.override(Ext.XTemplate, { compileTpl: function(tpl){ var fm = Ext.util.Format, useF = this.disableFormats !== true, sep = Ext.isGecko ? "+" : ",", body; function fn(m, name, format, args, math){ if(name.substr(0, 4) == 'xtpl'){ return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent, xindex, xcount)'+sep+"'"; } var v; if(name === '.'){ v = 'values'; }else if(name === '#'){ v = 'xindex'; }else if(name.indexOf('.') != -1){ v = name; }else{ v = "values['" + name + "']"; } if(math){ v = '(' + v + math + ')'; } if (format && useF) { args = args ? ',' + args : ""; if(format.substr(0, 5) != "this."){ format = "fm." + format + '('; }else{ format = 'this.call("'+ format.substr(5) + '", '; args = ", values"; } } else { args= ''; format = "("+v+" === undefined ? '' : "; } return "'"+ sep + format + v + args + ")"+sep+"'"; } function codeFn(m, code){ return "'"+ sep +'('+code+')'+sep+"'"; } // 替换with语句为显式变量声明,避免原型链查找 var explicitVars = 'var values = arguments[0], parent = arguments[1], xindex = arguments[2], xcount = arguments[3]; '; if(Ext.isGecko){ body = "tpl.compiled = function(values, parent, xindex, xcount){ " + explicitVars + " return '" + tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'") .replace(this.re, fn).replace(this.codeRe, codeFn) + "';};"; }else{ body = [ "tpl.compiled = function(values, parent, xindex, xcount){ " + explicitVars + " return ['", tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'") .replace(this.re, fn).replace(this.codeRe, codeFn), "'].join('');};" ].join(''); } eval(body); return this; } });
这个补丁会修改XTemplate编译生成的函数逻辑,不再使用with(values),而是直接引用传入的参数,彻底避免了原型链查找带来的冲突。
方法3:临时移除原型上的values方法(不推荐)
这个方法仅作为应急方案,因为直接删除原型方法可能会影响React或其他依赖这个方法的代码。如果必须使用@babel/polyfill,可以在加载完polyfill后手动删除Array.prototype.values:
// 在入口文件加载完@babel/polyfill后执行 if (Array.prototype.values) { // 可选:保存原方法,后续需要时恢复 // window._originalArrayValues = Array.prototype.values; delete Array.prototype.values; }
注意:这个方法风险较高,React的某些代码可能依赖Array.prototype.values遍历数组,删除后可能导致React组件出错,所以尽量不要长期使用。
内容的提问来源于stack exchange,提问作者Marty

