前段时间遇到了一个vue-cli导致的bug,解决了好久才解决,所以有空写篇文章记录一下。1.出现了奇怪的bug。有用户反映我们的在线网站在ie11下打不开,即使打开兼容模式也无法打开我们的网站,页面是空白的。初步判断,由于css样式资源和页面资源已经加载到位,排除网络环境问题后,让用户打开控制台截图看一下。白屏的原因是由于JS执行报错导致后续逻辑执行和渲染受阻。代码中有执行错误逻辑,但是根据打包后的压缩js文件无法判断问题。如果你有权限访问sentry或badjs等报错模块,那么你可以根据报错信息快速定位到源码上的问题。但是问题又来了。引入哨兵其实是最合法、合规、正确的引入方式。应该是页面资源加载的前端。有些人会在head标签中阻止它先加载,这样无论页面后面发生资源加载错误,HTML解析错误还是JS逻辑执行错误都可以捕获并报告。但是一开始并没有考虑到这一点,而是直接在vue-cli中的main.js中引入,也就是说如果第一次打包的bundle执行错误,sentry将无法完成初始化,导致无法报告错误内容。根据用户的截图,无法快速确定问题代码和错误内容,于是让windows电脑的同事用ie11打开网址查看错误内容。果然,报错不止一处。找到对应的代码行:第二个问题主要是在使用v-model绑定类型为radio值的input时,还自定义了属性:checked,导致在最终编译好的代码,而官方环境中的bundle包一般默认是'usestrict',所以触发了strict模式,ie11报错。根据属性名可以快速定位和修改问题。最后,回到第一个问题,弱智IE不支持类吗?是的,不支持,但是为什么我们打包的产品里面会有莫名其妙的ES6代码呢?按理说vue-cli已经帮我们引入了babel-loader来转换ES6代码,为什么还是出现class呢?带着这个疑问,我们继续往下看。2.分析原因,确定问题根据报错文件chunk-vendors,我们在vue.config.js中打开不同chunk的配置文件:optimization:{splitChunks:{cacheGroups:{echarts:{name:'chunk-echarts',test:/[\\/]node_modules[\\/]echarts[\\/]/,chunks:'all',priority:10,reuseExistingChunk:true,enforce:true,},demo:{name:'chunk-demo',test:/[\\/]src[\\/]views[\\/]demo[\\/]/,chunks:'all',priority:20,reuseExistingChunk:true,enforce:true,},page:{name:'chunk-page',test:/[\\/]src[\\/]/,chunks:'all',priority:10,reuseExistingChunk:true,enforce:true,},供应商:{na我:'chunk-vendors',测试:/[\\/]node_modules[\\/]/,chunks:'all',优先级:5,reuseExistingChunk:true,enforce:true,},},},},es6代码好像来自node_modules下的chunk包,为什么呢?接下来为了进一步判断我们的es6代码是从哪个第三方库导入的,我们需要暂时关闭uglify压缩代码,配置如下:module.exports={...optimization:{minimize:false,...}};执行以上配置后,就可以关闭webpack4默认的压缩配置了。这个时候我们会在我们打包好的产品里找,大的const和let,我们要找的罪魁祸首,class:对应的第三方库的地址为./node_modules/dom7/dist/dom7.modular.js接下来我们继续排查这个包是在哪里被引用的,很快就在package-lock.json中锁定了目标:swiper是一个面向移动端用户的carousel第三方库,这里引用了dom7模块。我们看一下swiper的源码。在swiper.esm.bundle.js的源码打包产品中,我们确实看到了这样的介绍:import{$,addClass,removeClass,hasClass,toggleClass,attr,removeAttr,data,transform,transitionastransition$1,on,off,trigger,transitionEndastransitionEnd$1,outerWidth,outerHeight,offset,css,each,html,text,is,index,eq,append,prepend,next,nextAll,prev,prevAll,parent,parents,closest,find,children,remove,add,styles}from'dom7/dist/dom7.modular';但是为什么要介绍esm的产品代码呢?默认情况下webpack不会帮我们引入主字段对应的产品吗?查看webpack官方文档,我们可以看到有这样一个配置项:resolve.mainFields:当从npm包中导入模块时(例如import*asD3from"d3"),这个选项会决定包.json中使用哪个字段导入模块取决于webpack配置中指定的target,默认值也会不同。在webpack默认配置下,mainfields字段指定module为第一个入口。之所以这样设计是为了顺应时代潮流,让大家使用es6导出的模块,逐步淘汰之前的Commonjs模块规范。毕竟,进出口可以带来更多的利益。我们的vue-cli在构建编译的时候,默认的target是node,所以我们的mainFields字段也默认是['module','main']。原因分析:这里我们已经明白了问题的关键,因为webpack不会对node_modules中引入的第三方库中的代码进行二次ES6转码处理,webpack会引入默认指向的module字段封装的产品,模块产品一般为ES6标准输出,主产品一般为commonjs或UMD标准输出,所以部分ES6代码残留导致IE11不兼容,最终导致报错。3.修改配置,确定问题所在。接下来我们只需要根据vue-cli官方文档自行配置webpack配置即可:configureWebpack:{resolve:{mainFields:['main','module'],},部分加载器和插件默认引入vue-cli,所以里面已经有了webpack的配置。Vue-cli允许用户通过公开configureWebpack参数来重新配置webpack。如果作为对象传入,会和webpack内部的配置对象进行merge操作。修改完配置后,最后查看我们打包好的产品。确实es6没有const,let,class等语法,大功告成,bug解决了。当然,如果你的网站需要兼容大部分浏览器和不同的场景,你也需要为你的代码引入polyfills。毕竟ES6转ES5只是针对部分语法,而ES6加入的一些API是不会转换的,这时候只能通过引入pre-polyfill来实现兼容。谢谢观看~
