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/

你可能感兴趣的:(webpack babel 使用方法整理)