webpack babel 使用方法整理

webpack babel-loader 基础配置

Babel

  • ES6对JavaScript做了大量改造,加入了大量的方法扩展和suger,让JS灵活性和应用型变的更强,同时写起来也更爽。
  • 但是由于ES6 2014年才正式发布,并且加入了大量扩展,造成很多旧版浏览器不能很好的支持ES6的语法及扩展(特别是IE!)。
  • 所以为了兼容旧版浏览器,在构建/打包项目时,希望将ES6的语法转为浏览器都可以支持的ES5。这时就需要使用babeljs帮助我们做这些事。

babel官网 :https://babeljs.io/
babel中文网: https://babel.bootcss.com/

推荐查看babel官方网站,babel中文网的翻译并不是最新版本

webpack基础配置

  • 在webpack中,babel通过loader的形式调用。
  • 首先需要安装babel-loader及babel核心库@babel/core
    npm install --save-dev babel-loader @babel/core
  • 千万别忘了安装@babel/preset-env预设包
    npm install --save-dev @babel/preset-env
  • 之后需要在webpack的config文件中通过模块的方式引用。由于开发过程中有可能使用typescrpt,所以在打包时,需要将.js文件,react的.jsx以及typescript的.ts文件都使用babel编译为ES5语法。
module.exports = {
    // ... other options
    module: {
        rules:[{
            test: /.(js|ts)x?$/,
            loader: 'babel-loader',
            options:{
                presets: ["@babel/preset-env"]
            },
            exclude: /node_modules/
        },
        {
            //... other loader options
        }]
    }
}
  • 配置好了之后执行npx webpack试试看:
    // 源代码:
    
    let array = [1,2,3,4,5];
    
    array.map(item => item + 1)
    // 打包后:
    
    var array = [1, 2, 3, 4, 5];
    
    array.map(function (item) {
        return item + 1;
    });
  • 可以发现,在打包后let关键字被转为了var关键字;Array.map方法中的箭头函数(item) => item + 1也被成功转为了ES5的function

但是!Array.map 是 ES6 数组扩展中新增的方法!尽管 babel 把 ES6 的语法转换为了 ES5 ,旧版本浏览器依然不支持 Array.map!

如果想要让低版本浏览器也能支持 ES6 的新增扩展,正常情况下就需要自己模拟实现 ES6 的扩展方法,然后通过prototype将方法插入JS对象的原型中,扩展原型方法。

那么,懒癌患者和前端新人怎么办?

不用怕!babeljs团队已经为我们准备好了一套比较完整的方法扩展(懒癌患者福音),我们只需要把扩展库引入就能让旧版浏览器支持 ES6 的新增扩展了 [/坏笑]


为代码添加 ES6 支持

  • babeljs 提供了两种使用方法扩展的方式@babel/polyfill@babel/plugin-transform-runtime

@babel/polyfill

  • @babel/polyfill 官方给出的解释为,它可以效仿一个完整的 ES2015+ 运行环境,并意图运行于一个应用中而不是一个库/工具。
  • 按照惯例,在使用@babel/polyfill前,需要先进行安装
    npm install --save @babel/polyfill
  • 安装之后只需要在业务代码头部引入就可以让旧版浏览器也支持 ES6,来测试一下
  • 这里使用Promise测试,因为Promise是 ES6 新增的对象,并且IE所有版本浏览器在正常情况下均不支持

首先不添加 @babel/polyfill ,直接在控制台输出 Promise

    // index.js
    
    console.log(Promise) // 在控制台输出 Promise

使用 IE9 打开后,控制台抛出错误

IE9 控制台输出

然后我们引入 @babel/polyfill 后,再在控制台中输入 Promise

    // 源代码:
    
    require( '@babel/polyfill') // 使用commonJS语法引入polyfill
    
    console.log(Promise) // 在控制台输出 Promise

依然使用 IE9 打开,可以发现控制台中成功打印出了 Promise 的构造函数

IE9 控制台输出
  • 至此,IE也可以运行使用 ES6 编写的代码了! [/赞]

此处又有但是!可以运行并不意味着配置就完成了。

  • 来看一下webpack的打包输出信息
webpack bundle info

what?!
index.js文件里边只写了两行代码,打包后main.js的的大小竟然有207kb!
要是把业务代码都写进去,页面打开速度还不爆炸了!


  • 这是因为,@babel/polyfill 在模拟 ES2015 环境时,会将添加的 ES6 方法一起打包到 main.js 文件当中。

那么,在当前代码中,我们只用到了Promise方法,能不能只把Promise的方法扩展打包?

  • 肯定是可以的,查看一下相关文档,会发现在配置@babel/preset-env时,可以支持设置一个useBuiltIns参数,支持3个值

    • entry : 只支持引入一次@babel/polyfill,如果多次引用会抛出错误
    • usage : 只会将文件中用到的 ES6 方法引用到文件中
    • false : 默认值,不会自动识别文件中使用的 ES6 方法,会将@babel/polyfill作为整体进行填充
  • 按照需求,最适合的应该是usage,它可以识别用到的 ES6 方法,只引入这些方法的扩展。这就很开心了。

修改一下配置试试吧。

// webpack.config.js

module.exports = {
    // ... other options
    module: {
        rules:[{
            test: /.(js|ts)x?$/,
            loader: 'babel-loader',
            options:{
-               presets: ["@babel/preset-env"],

+               presets: ["@babel/preset-env",{
+                   "useBuiltIns": "usage"
+               }],
            },
            exclude: /node_modules/
        },
        {
            //... other loader options
        }]
    }
}

修改完成之后,再执行一次打包

webpack bundle info

可以看到,打包后的文件大小降至29.5kb。
(这里由于使用了其他插件,所以文件依然比较大,在未使用其他插件情况下,打包出来的文件大约3kb)

  • 到这里,@babel/polyfill的配置才算是比较符合需求了

之所以说以上针对babel的配置是比较符合要求,是因为 @babel/polyfill 虽然可以帮助开发者注入使用到的 ES6 方法,但是它是以 全局变量 的形式将方法注入。如果只是业务开发使用,问题并不是很大。但是如果在开发类库或UI组件时,全局注入的方式会造成变量的 全局污染 。这不是我们期望的结果。我们更希望在注入方法的同时保持全局环境的清洁。这就需要用到 @babel/plugin-transform-runtime 插件

@babel/plugin-transform-runtime

  • @babel/plugin-transform-runtime会以闭包的形式注入 ES6 方法,可以最大限度的保证全局环境不被污染
  • 首先,依然需要安装插件
    npm install --save-dev @babel/plugin-transform-runtime
    npm install --save @babel/runtime
  • 安装完成后,需要在配置文件中调用插件,替换"@babel/preset-env"的相关配置
// webpack.config.js

module.exports = {
    // ... other options
    module: {
        rules:[{
            test: /.(js|ts)x?$/,
            loader: 'babel-loader',
            options:{
-               presets: ["@babel/preset-env",{
-                   "useBuiltIns": "usage"
-               }],

+               plugins: [
+                   ["@babel/plugin-transform-runtime", {
+                       "corejs": 2,
+                       "helpers": true,
+                       "regenerator": true,
+                       "useESModules": false
+                   }]
+               ]
            },
            exclude: /node_modules/
        },
        {
            //... other loader options
        }]
    }
}

  • 按照官方文档的说明,如果给corejs参数配置number类型的值,则需要使用@babel/runtime-corejs2代替@babel/runtime注入,如果值为false则不需要做任何修改
  • 由于这里使用了number类型的值,所以需要卸载@babel/runtime
npm uninstall --save @babel/runtime
  • 然后安装@babel/runtime-corejs2
npm install --save @babel/runtime-corejs2
  • 替换完成后,运行打包命令即可

.babelrc

  • 在webpack中,如果babel的配置比较多, options 的配置就会变的相当长一旦出现修改,找起来会相当酸爽。

可以将options中的所有配置都移动到.babelrc文件中单独书写

  • 首先在根目录下创建一个.babelrc文件
touch .babelrc
  • 然后将babel的相关文件从配置文件中拿出来,丢进.babelrc文件中
// webpack.config.js

module.exports = {
    // ... other options
    module: {
        rules:[{
            test: /.(js|ts)x?$/,
            loader: 'babel-loader',
-           options:{
-               plugins: [
-                   ["@babel/plugin-transform-runtime", {
-                       "corejs": 2,
-                       "helpers": true,
-                       "regenerator": true,
-                       "useESModules": false
-                   }]
-               ]
-           },
            exclude: /node_modules/
        },
        {
            //... other loader options
        }]
    }
}
{
    "plugins": [
        ["@babel/plugin-transform-runtime", {
            "corejs": 2,
            "helpers": true,
            "regenerator": true,
            "useESModules": false
        }]
    ]              
}

再次运行打包命令后,使用IE9打开,可以发现Promise方法依然可以被正常打印出来

目前通过babel转换后的代码只能支持 IE9+ 无法支持 IE8 及以下浏览器。原因是打包后的文件当中包含了Object.defineProperty方法,IE8 及以下浏览器不支持此方法。暂时还未找到比较完美的解决方案。希望有相关经验经验的小伙伴能帮助解答。

以上是对webpack使用babel进行打包的一些总结,如果有不正确的地方,欢迎各位大佬指出

参考资料

  1. 慕课网 DellLee 大大的课程:基础到实战 手把手带你掌握新版Webpack4
  2. babel官方网站: https://babeljs.io/
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容