Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
yanlele opened this issue Nov 23, 2024 · 0 comments
Labels
工程化 web 项目工程化
Milestone

Comments

@yanlele
Copy link
Member

yanlele commented Nov 23, 2024

关键词: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>
@yanlele yanlele added the 工程化 web 项目工程化 label Nov 23, 2024
@yanlele yanlele added this to the milestone Nov 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
工程化 web 项目工程化
Projects
None yet
Development

No branches or pull requests

1 participant