Webpack4+Babel7+ES6兼容IE8的实现


Posted in Javascript onApril 10, 2019

前阵子重构了一个挺有意思的项目,是一个基于浏览器环境的数据采集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在各浏览器版本中的支持情况

https://kangax.github.io/compat-table/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/runtime。具体使用方法官方文档已经写的很详细了,笔者就不赘述了。这里纠正墨白同学文中的一个错误,就是@babel/polyfill现在已经支持按需加载,准确的说也不能算是错误,因为墨白同学在写这篇文章的时候还不支持按需加载。具体方法我就不细说了,文档里都有,配置下browserlist和@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!');

我用@babel/plugin-transform-modules-commonjs转成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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
用js 让图片在 div或dl里 居中,底部对齐
Jan 21 Javascript
jQuery Select(单选) 模拟插件 V1.3.62 改进版
Jul 17 Javascript
JQuery切换显示的效果实例代码
Feb 27 Javascript
深入理解JavaScript系列(44):设计模式之桥接模式详解
Mar 04 Javascript
bootstrap table 表格中增加下拉菜单末行出现滚动条的快速解决方法
Jan 05 Javascript
vue.js的提示组件
Mar 02 Javascript
JS去掉字符串前后空格或去掉所有空格的用法
Mar 25 Javascript
微信小程序 图片宽高自适应详解
May 11 Javascript
浅谈JS 数字和字符串之间相互转化的纠纷
Oct 20 Javascript
JavaScript之创意时钟项目(实例讲解)
Oct 23 Javascript
使用vue根据状态添加列表数据和删除列表数据的实例
Sep 29 Javascript
使用webpack4编译并压缩ES6代码的方法示例
Apr 24 Javascript
微信小程序第三方框架对比 之 wepy / mpvue / taro
Apr 10 #Javascript
用node撸一个监测复联4开售短信提醒的实现代码
Apr 10 #Javascript
从0到1搭建Element的后台框架的方法步骤
Apr 10 #Javascript
详解vue.js移动端配置flexible.js及注意事项
Apr 10 #Javascript
小程序分享模块超级详解(推荐)
Apr 10 #Javascript
关于JavaScript 数组你应该知道的事情(推荐)
Apr 10 #Javascript
Vue中computed、methods与watch的区别总结
Apr 10 #Javascript
You might like
php获取地址栏信息的代码
2008/10/08 PHP
PHP $_FILES中error返回值详解
2014/01/30 PHP
PHP中strcmp()和strcasecmp()函数字符串比较用法分析
2016/01/07 PHP
php简单计算年龄的方法(周岁与虚岁)
2016/12/06 PHP
圣诞节Merry Christmas给博客添加浪漫的下雪效果基于jquery实现
2012/12/27 Javascript
js控制分页打印、打印分页示例
2014/02/08 Javascript
js 获取时间间隔实现代码
2014/05/12 Javascript
如何动态加载外部Javascript文件
2015/12/02 Javascript
给Easyui-Datebox设置隐藏或者不可用的解决方法
2017/05/26 Javascript
利用Vue.js实现求职在线之职位查询功能
2017/07/03 Javascript
jQuery+vue.js实现的九宫格拼图游戏完整实例【附源码下载】
2017/09/12 jQuery
Vue-Router进阶之滚动行为详解
2017/09/13 Javascript
详解vue表单——小白速看
2018/04/08 Javascript
vue mounted组件的使用
2018/06/18 Javascript
浅析vue中的provide / inject 有什么用处
2019/11/10 Javascript
小程序实现左滑删除的效果的实例代码
2020/10/19 Javascript
jQuery实现推拉门效果
2020/10/19 jQuery
vue 如何从单页应用改造成多页应用
2020/10/23 Javascript
[01:39](回顾)各路豪强针锋相对,几经鏖战四强产生
2014/07/01 DOTA
[01:03:09]完美世界DOTA2联赛PWL S2 Forest vs SZ 第二场 11.25
2020/11/26 DOTA
python网络爬虫采集联想词示例
2014/02/11 Python
Python实现批量下载文件
2015/05/17 Python
python if not in 多条件判断代码
2016/09/21 Python
python scatter函数用法实例详解
2020/02/11 Python
HTML5中的Web Notification桌面通知功能的实现方法
2019/07/29 HTML / CSS
美国新蛋IT数码商城:Newegg.com
2016/07/21 全球购物
Belvilla德国:在线预订度假屋
2018/04/10 全球购物
SmartBuyGlasses荷兰:购买太阳镜和眼镜
2020/03/16 全球购物
新闻专业大学生找工作的自我评价
2013/10/30 职场文书
大学生专科毕业生自我评价
2013/11/17 职场文书
技校生自我鉴定
2013/12/08 职场文书
自动化职业生涯规划书范文
2014/01/03 职场文书
婚礼司仪主持词
2014/03/14 职场文书
法院授权委托书格式
2014/09/28 职场文书
2015年机关党建工作总结
2015/05/22 职场文书
如何给HttpServletRequest增加消息头
2021/06/30 Java/Android