[前端]低版本浏览器兼容问题,针对性polyfill方案,vite/nuxt/next打包降级

447 阅读2分钟

初衷

  • webpack 虽然可以一次性降级到ES5,但会导致JS变得非常冗余,之前项目ES6包降级到ES5,JS主包增加了120%,实在是不能忍~
  • vite 默认只支持ES6(chrome 80+),需要单独 legacy降级,但实际操作中会发现有不少问题,我们可以针对性解决,适配 chrome 60+内容
  • 由于新项目常常会使用到 nuxt/next SSR静态化解决方案,当使用高版本也会出现类似问题

全面降级polyfill方案有很多种,babel、corejs、legacy、polyfill家族等,这里只讲针对性,代码增加幅度最小

关于低版本浏览器兼容性问题处理,针对性polyfill方案

主要解决一下内容:

  • globalThis 报错
  • Object.entries
  • Object.assign
  • includes

React 18+ /Next 方案

可以在app.ts 或者 顶部的layout.ts 配置

 <html lang="zh">
            <head>
                {/* 内联 polyfill 脚本,确保最先执行 */}
                <script
                    dangerouslySetInnerHTML={{
                        __html: `
                        // 为低版本浏览器提供 globalThis polyfill
                        (function() {
                            if (typeof globalThis === "undefined") {
                                // 更安全的 globalThis polyfill 实现
                                if (typeof self !== "undefined") { self.globalThis = self; }
                                if (typeof window !== "undefined") { window.globalThis = window; }
                                if (typeof global !== "undefined") { global.globalThis = global; }
                            }
                        })();
                        `
                    }}
                />
                <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
                {/* 内联额外的浏览器兼容性 polyfill */}
                <script
                    dangerouslySetInnerHTML={{
                        __html: `
                        // 为低版本浏览器提供额外的 polyfill
                        (function() {
                            // 确保 Object.entries 可用
                            if (typeof Object.entries !== "function") {
                                Object.entries = function(obj) {
                                    var ownProps = Object.keys(obj);
                                    var i = ownProps.length;
                                    var resArray = new Array(i);
                                    while (i--) {
                                        resArray[i] = [ownProps[i], obj[ownProps[i]]];
                                    }
                                    return resArray;
                                };
                            }

                            // 确保 Array.prototype.includes 可用
                            if (!Array.prototype.includes) {
                                Array.prototype.includes = function(searchElement, fromIndex) {
                                    if (this == null) {
                                        throw new TypeError('"this" is null or not defined');
                                    }
                                    var o = Object(this);
                                    var len = o.length >>> 0;
                                    if (len === 0) {
                                        return false;
                                    }
                                    var n = fromIndex | 0;
                                    var k = Math.max(n >= 0 ? n : len + n, 0);
                                    while (k < len) {
                                        if (o[k] === searchElement) {
                                            return true;
                                        }
                                        k++;
                                    }
                                    return false;
                                };
                            }

                            // 确保 String.prototype.includes 可用
                            if (!String.prototype.includes) {
                                String.prototype.includes = function(search, start) {
                                    if (typeof start !== 'number') {
                                        start = 0;
                                    }
                                    if (start + search.length > this.length) {
                                        return false;
                                    } else {
                                        return this.indexOf(search, start) !== -1;
                                    }
                                };
                            }

                            // 确保 Object.assign 可用
                            if (typeof Object.assign !== 'function') {
                                Object.assign = function(target) {
                                    if (target == null) {
                                        throw new TypeError('Cannot convert undefined or null to object');
                                    }
                                    var to = Object(target);
                                    for (var index = 1; index < arguments.length; index++) {
                                        var nextSource = arguments[index];
                                        if (nextSource != null) {
                                            for (var nextKey in nextSource) {
                                                if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
                                                    to[nextKey] = nextSource[nextKey];
                                                }
                                            }
                                        }
                                    }
                                    return to;
                                };
                            }
                        })();
                        `
                    }}
                />
            </head>
            <body>
            </body>
        </html>

VUE3/nuxt3 方案

可以在nuxt.config.ts 或者 vite.config.ts 中添加

 script: [
    // 添加 Promise.allSettled polyfill
    {
        innerHTML: `
        if (!Promise.allSettled) {
            const rejectHandler = reason => ({ status: 'rejected', reason });

            const resolveHandler = value => ({ status: 'fulfilled', value });

            Promise.allSettled = function (promises) {
                const convertedPromises = promises.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));
                return Promise.all(convertedPromises);
            };
        }
    `,
        type: "text/javascript",
    },
    // 添加 globalThis polyfill
    {
        innerHTML: `
            if (typeof globalThis === 'undefined') {
                window.globalThis = window;
            }
        `,
        type: "text/javascript",
    },
],