webpack polyfill

177 阅读2分钟

babel默认只转换js语法,而不转换新的API,比如IteratorGeneratorSetMapsProxyReflect,Symbol,Promise

如果想让这些方法运行,必须使用polyfill来转换

@babel/polyfill

是一个javascript库,可以为旧版浏览器提供缺失的 ECMAScript 新特性和 API。它通过在全局对象上添加缺失的方法和属性来模拟 ECMAScript 的新特性,以便在旧版浏览器中运行现代的 JavaScript 代码

@babel/polyfill 会将所有特性添加到全局对象上,因此它的体积很大

// 打包出的bundle会非常大
import '@babel/polyfill'; 
let sum = (a, b) => a + b; 
let promise = Promise.resolve(); 
console.log([1, 2, 3].find(item => item === 2));

useBuiltIns

useBuiltIns有三个选项:usageentryfalse

如果useBuiltIns为false@babel/preset-env 不会自动在代码中注入 polyfill,这意味着如果你使用了一些新的 ECMAScript 特性或 API,它们在旧版浏览器中可能不被支持,你需要手动引入对应的 polyfill

如果useBuiltIns为entry@babel/preset-env 会根据目标浏览器的版本和你的代码中使用的 ECMAScript 特性,自动引入对应的 polyfill,并将它们添加到入口文件中。根据浏览器兼容,引入浏览器不兼容的polyfill,需要在入口文件中手动添加import '@babel/polyfill',并且需要在入口文件指定core-js版本

import 'core-js/stable'; 
import 'regenerator-runtime/runtime'; 
let sum = (a, b) => a + b; 
let promise = Promise.resolve(); 
console.log([1, 2, 3].find(item => item === 2));
{ 
    test: /\.js?$/, 
    exclude: /node_modules/,
    use: { 
        loader: 'babel-loader', 
        options: { 
            presets: [["@babel/preset-env", { useBuiltIns: 'entry', corejs: 3 }]] 
        } 
    } 
}

如果useBuiltIns为usage,会根据配置的浏览器兼容,以及你代码中用到的 API 来进行 polyfill,实现了按需添加。不需要手动引入@babel/polyfill

{ 
    test: /\.js?$/, 
    exclude: /node_modules/,
    use: { 
        loader: 'babel-loader', 
        options: { 
            presets: [["@babel/preset-env", { useBuiltIns: 'usage', corejs: 3 }]] 
        } 
    } 
}

babel-runtime

  • Babel为了解决全局空间污染的问题,提供了单独的包babel-runtime用以提供编译模块的工具函数
  • 与 @babel/polyfill 不同,babel-runtime 并不会在全局对象上添加新的方法和属性,而是将这些方法和属性封装在一个独立的运行时库中,以便在编译后的代码中使用
  • 简单说 babel-runtime 更像是一种按需加载的实现,比如你哪里需要使用 Promise,只要在这个文件头部import Promise from 'babel-runtime/core-js/promise'就行了

babel-plugin-transform-runtime

  • 解决多个文件重复引用
  • 新API方法全局污染
  • 启动babel-plugin-transform-runtime插件,babel就会使用babel-runtime下的工具函数
  • 插件能够将这些工具函数代码转换成require语句,指向为对babel-runtime的引用
  • 就是可以在使用新的api时自动import babel-runtime里面的polyfill
    • 如当使用await/async时,自动引入babel-runtime/regenerator
    • 当我们使用 ES6 的静态事件或内置对象时,自动引入 babel-runtime/core-js
    • 移除内联babel helpers并替换使用babel-runtime/helpers 来替换
{ 
    test: /\.js?$/, 
    exclude: /node_modules/,
    use: { 
        loader: 'babel-loader', 
        options: { 
            presets: [["@babel/preset-env", { useBuiltIns: 'usage', corejs: 3 }]],
            plugins: [ 
            [ 
                "@babel/plugin-transform-runtime", 
                { corejs: 3, helpers: true, regenerator: true } 
            ], 
           ]
        } 
    } 
}

最佳实践

@babel/preset-env和plugin-transform-runtime二者都可以设置使用corejs来处理polyfill

项目开发

如果是项目开发,则可以全局污染。useBuiltLns使用usage,plugin-transform-runtime只使用其移除内联复用的辅助函数的特性,减小打包体积

{ 
    test: /\.js?$/, 
    exclude: /node_modules/,
    use: { 
        loader: 'babel-loader', 
        options: { 
            presets: [["@babel/preset-env", { useBuiltIns: 'usage', corejs: 3 }]],
            plugins: [ 
            [ 
                "@babel/plugin-transform-runtime", 
                { corejs: false} 
            ], 
           ]
        } 
    } 
}

类库开发

类库开发最好不使用污染全局变量的polyfillpolyfill@babel/plugin-transform-runtime处理

{ 
    test: /\.js?$/, 
    exclude: /node_modules/,
    use: { 
        loader: 'babel-loader', 
        options: { 
            presets: [["@babel/preset-env"]],
            plugins: [ 
            [ 
                "@babel/plugin-transform-runtime", 
                { corejs: {"version": 3}} 
            ], 
           ]
        } 
    } 
}