Webpack4+Babel7+ES6兼容IE8的实现
前阵子重构了一个挺有意思的项目,是一个基于浏览器环境的数据采集sdk。公司各个产品的前端页面中都嵌入了这个sdk,用于采集用户的行为数据,上传到公司的大数据平台,为后续的运营决策分析提供数据支撑。
笔者接手这个项目的时候,前任开发者已经把功能都写差不多了。唯一需要做的就是做下模块化拆分和代码规范,以便后续的开发维护。模块化拆分用webpack,代码规范用eslint。既然要重构,那就顺手用es6重写吧。callback也不要了,全换成promise,async、await也用起来,反正怎么爽怎么写。
问pm浏览器最低兼容到哪个版本,pm说兼容公司各个产品所兼容的最低版本就行。和公司各个产品的前端负责人沟通后发现,居然有兼容ie8的,真是我了个fk。
google了一下webpack+babel+es6兼容ie8,果然坑很多。试了好几篇博客给出的方案,都跑不通。也没怎么研究具体哪里有问题,因为那些解决方案里面的webpack和babel都是旧版的,跑通了也不高兴用。笔者分析了那些博客中提出的几个关键性问题,然后参考webpack和babel最新的官方文档,总结出一套最新的webpack4+babel7+es6兼容ie8的方案。
es6兼容ie8需要解决四个问题
语法支持
ie浏览器不支持es6的语法,只在ie10、ie11中支持了部分es6的api,所以在ie浏览器中使用es6需要把es6的代码编译成es5才能执行。方法也很简单,就是用babel-loader。这部分没什么坑,所以我也就不细说了。给个网站,大家可以自行查看es5、es6在各浏览器版本中的支持情况
es3保留关键字
如果在ie8下通过object.propertyname的方式使用es3中的保留关键字(比如default、class、catch),就会报错
script1048: 缺少标识符
webpack有一款loader插件es3ify-loader专门用来处理es3的关键字兼容问题。这个插件的作用就是把这些保留字给你加上引号,使用字符串的形式引用。
// 编译前 function(t) { return t.default; } // 编译后 function(t) { return t["default"]; }
然而,笔者亲身实践后发现,uglifyjs本来就已经提供了对ie浏览器的支持,不需要额外引入es3ify-loader。webpack默认的uglifyjs配置不支持ie8,需要手动配下。
{ mode: 'production', optimization: { minimizer: [ new uglifyjsplugin({ uglifyoptions: { ie8: true } }) ] } }
执行环境
解决了前面两个问题只能保证语法上不报错,但使用es6中的api(比如promise)还是会报错。另外,ie8对es5的api支持也很差,只支持了少量的api,有些api还只是支持部分功能(比如object.defineproperty)。所以,要在ie8中完美运行es6的代码,不仅需要填充es6的api,还要填充es5的api。
babel为此提供了两种解决方案: 、。具体使用方法官方文档已经写的很详细了,笔者就不赘述了。这里纠正墨白同学文中的一个错误,就是@babel/polyfill现在已经支持按需加载,准确的说也不能算是错误,因为墨白同学在写这篇文章的时候还不支持按需加载。具体方法我就不细说了,文档里都有,配置下和@babel/preset-env的usebuiltsins属性就可以了。
我只说下我在实际开发过程中碰到的坑。
虽然@babel/polyfill、@babel/runtime都支持按需加载,但都只能识别出业务代码中使用到的缺失的api,如果第三方库有用到这些缺失的api,babel不能识别出来,自然也就不能填充进来。比如regenerator-runtime中用到的object.create和array.prototype.foreach。解决办法是,在入口文件处手动引入缺失的api。
模块化加载
笔者原来是想用es6的模块化加载方案,因为这样可以利用webpack的tree shaking,移除冗余代码,使打包出来的文件体积更小。但在ie8下测试发现object.defineproperty会报错'accessors not supported!'。报错代码如下
if ('get' in attributes || 'set' in attributes) throw typeerror('accessors not supported!');
我用转成commonjs加载就可以把这个坑绕过去,但同时也意味着放弃了tree shaking。
总结
package.json
{ "devdependencies": { "@babel/core": "^7.2.2", "@babel/plugin-transform-runtime": "^7.2.0", "@babel/preset-env": "^7.1.0", "@babel/runtime": "^7.3.4", "babel-loader": "^8.0.4", "core-js": "^3.0.1", "uglifyjs-webpack-plugin": "^2.0.1", "webpack": "^4.20.2", "webpack-cli": "^3.1.2", "webpack-dev-server": "^3.1.9", "webpack-merge": "^4.1.4" } }
webpack配置
{ module: { rules: [ { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', options: { presets: [ '@babel/preset-env' ], plugins: [ [ '@babel/plugin-transform-runtime' ], [ // 笔者为了兼容ie8才用了这个插件,代价是不能tree shaking // 没有ie8兼容需求的同学可以把这个插件去掉 '@babel/plugin-transform-modules-commonjs' ] ] } } } ] }, optimization: { minimizer: [ new uglifyjsplugin({ sourcemap: true, uglifyoptions: { ie8: true, } }) ] } }
入口文件按需引入缺失的api
require('core-js/features/object/define-property') require('core-js/features/object/create') require('core-js/features/object/assign') require('core-js/features/array/for-each') require('core-js/features/array/index-of') require('core-js/features/function/bind') require('core-js/features/promise')
最后附上文章开头提到的sdk源码,笔者已将公司业务相关代码去除,将通用部分开源。https://github.com/xttech/dc-sdk-js
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: JS实现li标签的删除
下一篇: 如何检查一个对象是否为空
推荐阅读
-
js与jQuery实现的兼容多浏览器Ajax请求实例
-
用JS实现复制内容到剪切板,兼容PC和手机端,支持SAFARI浏览器的方法
-
一款超酷的js+css3实现的3D标签云特效兼容ie7/8/9
-
javascript实现的图片预览和上传功能示例【兼容IE 9】
-
兼容主流浏览器的jQuery+CSS 实现遮罩层的简单代码
-
html5实现完美兼容各大浏览器的播放器
-
IE兼容css3圆角的实现代码
-
ms sql server中实现的unix时间戳函数(含生成和格式化,可以和mysql兼容)
-
Jquery实现兼容各大浏览器的Enter回车切换输入焦点的方法教程
-
jQuery实现的鼠标拖动画矩形框示例【可兼容IE8】