pro-collection / interview-question

目标:收集全网经典面试问题
713 stars 53 forks source link

[Webpack] 我如何实现在高版本浏览器上使用 es6 产物, 在低版本上使用 es5【热度: 122】 #1072

Open yanlele opened 6 days ago

yanlele commented 6 days ago

关键词:Webpack es6 产物

  1. 分别打包 ES5 和 ES6 产物(从一份 ES6 源码)

    • 配置 Webpack 和 Babel 基础环境
      • 首先,安装必要的依赖。除了 Webpack 本身相关的依赖外,还需要babel - loader@babel/core@babel/preset - env
        npm install webpack webpack - cli babel - loader @babel/core @babel/preset - env --save - dev
      • 创建 Webpack 配置文件(例如webpack.config.js),并在其中配置基本的模块规则,用于处理 JavaScript 文件。先配置一个简单的 ES6 打包规则:
        const path = require("path");
        module.exports = {
        entry: "./src/index.js", // 假设ES6源码入口文件是index.js
        output: {
        filename: "es6 - bundle.js",
        path: path.resolve(__dirname, "dist/es6"),
        },
        module: {
        rules: [
        {
         test: /\.js$/,
         exclude: /node_modules/,
         use: {
           loader: "babel - loader",
           options: {
             presets: [
               [
                 "@babel/preset - env",
                 {
                   targets: {
                     // 设置一个较高的浏览器版本支持ES6原生,这里假设现代浏览器都支持
                     chrome: "80",
                     firefox: "70",
                   },
                 },
               ],
             ],
           },
         },
        },
        ],
        },
        };
    • 添加 ES5 打包配置
      • 为了打包 ES5 产物,在 Webpack 配置文件中添加另一个配置对象。可以通过webpack - merge工具(需要安装webpack - merge)来合并基础配置和 ES5 特定配置,避免重复配置。
        const path = require("path");
        const webpackMerge = require("webpack - merge");
        const baseConfig = {
        entry: "./src/index.js",
        output: {
        filename: "es6 - bundle.js",
        path: path.resolve(__dirname, "dist/es6"),
        },
        module: {
        rules: [
        {
         test: /\.js$/,
         exclude: /node_modules/,
         use: {
           loader: "babel - loader",
           options: {
             presets: [
               [
                 "@babel/preset - env",
                 {
                   targets: {
                     chrome: "80",
                     firefox: "70",
                   },
                 },
               ],
             ],
           },
         },
        },
        ],
        },
        };
        const es5Config = {
        output: {
        filename: "es5 - bundle.js",
        path: path.resolve(__dirname, "dist/es5"),
        },
        module: {
        rules: [
        {
         test: /\.js$/,
         exclude: /node_modules/,
         use: {
           loader: "babel - loader",
           options: {
             presets: [
               [
                 "@babel/preset - env",
                 {
                   targets: {
                     // 设置较低的浏览器版本,确保转换为ES5语法
                     chrome: "50",
                     firefox: "40",
                   },
                   useBuiltIns: "usage",
                   corejs: 3,
                 },
               ],
             ],
           },
         },
        },
        ],
        },
        };
        module.exports = [baseConfig, webpackMerge(baseConfig, es5Config)];
      • 上述配置中,es5Config部分重新定义了输出文件名和路径,并且在babel - loader的选项中,通过@babel/preset - envtargets选项设置了较低的浏览器支持范围,这样就可以将源码转换为 ES5 语法并打包到dist/es5目录下的es5 - bundle.js文件。
  2. 区分加载 ES5 还是 ES6 产物

    • 使用浏览器特性检测脚本(HTML + JavaScript)
      • 在 HTML 文件(假设是index.html)中,可以添加一段 JavaScript 代码来检测浏览器是否支持 ES6 特性。例如,检测Promise是否存在(Promise是 ES6 的一个典型特性)。
        <!DOCTYPE html>
        <html lang="en">
        <head>
        <meta charset="UTF-8" />
        <title>Document</title>
        </head>
        <body>
        <script>
        if (typeof Promise === "undefined") {
         document.write('<script src="dist/es5/es5 - bundle.js"><\/script>');
        } else {
         document.write('<script src="dist/es6/es6 - bundle.js"><\/script>');
        }
        </script>
        </body>
        </html>
      • 这种方法简单直接,但有一定的局限性。如果浏览器对 ES6 的支持不完全(例如,支持部分 ES6 语法但不支持检测的特性),可能会导致加载错误的产物。
    • 使用服务端渲染(SSR)或构建时检测(更高级)
      • 如果是在服务端渲染的应用中,可以在服务端根据用户代理(User - Agent)来判断浏览器版本,进而选择加载 ES5 还是 ES6 产物。这需要在服务端代码(例如 Node.js 中的 Express 应用)中进行逻辑处理。
      • 另一种构建时检测的方法是,在构建过程中生成一个包含浏览器支持信息的配置文件。可以使用browserslist工具(@babel/preset - env内部也使用了这个工具来确定目标浏览器)结合一些自定义脚本,在构建时确定目标浏览器范围,然后生成一个配置文件(例如browser - config.json),内容可能是{"supportsES6": true}或者{"supportsES6": false}。在 HTML 文件中,通过读取这个配置文件来加载相应的产物。
        // 假设这是一个自定义脚本,用于生成browser - config.json
        const browserslist = require("browserslist");
        const fs = require("fs");
        const targets = {
        chrome: "50",
        firefox: "40",
        };
        const browsers = browserslist(Object.keys(targets).map((browser) => `${browser} >= ${targets[browser]}`));
        const supportsES6 = browsers.some(
        (browser) => browser.includes("Chrome >= 80") || browser.includes("Firefox >= 70")
        );
        fs.writeFileSync("browser - config.json", JSON.stringify({ supportsES6 }));
      • 然后在 HTML 文件中:
        <!DOCTYPE html>
        <html lang="en">
        <head>
        <meta charset="UTF-8" />
        </head>
        <body>
        <script>
        const browserConfig = JSON.parse(document.currentScript.getAttribute("data - config"));
        if (browserConfig.supportsES6) {
         document.write('<script src="dist/es6/es6 - bundle.js"><\/script>');
        } else {
         document.write('<script src="dist/es5/es5 - bundle.js"><\/script>');
        }
        </script>
        <script src="browser - config.json" data - config></script>
        </body>
        </html>