vue 前端项目支持低版本 chrome 的解决办法

vue 前端项目支持低版本 chrome 的解决办法

最近项目提出了一个需求:在 xp 系统打开 mes v5 的 web 系统。这个需求之前从来没有提到过,于是我仔细研究了下。

需求分析

对比 4.5 版本的 mes 系统(v4.5),v4.5 的运用的技术年代较为久一些,甚至可以对 ie8 做到部分兼容,要知道 xp 系统当年是默认 ie8 的,所以自然就是支持 xp 系统的。

然后 5.0 的新版 mes,由于没有 xp 系统的电脑,就使用 ie11 与低版本 chrome 模拟 xp 系统浏览器环境,在查证之后得知 chrome 最后支持 xp 的软件版本是 49(现版本已经到 94 了),于是下载下载 49 版本 chrome 作为测试浏览器。令人惊讶的是 chrome v49 可以打开 5.1.3 的 mes 系统,所以问题已经比较明确了,是 5.1.3 之后的改动导致了不兼容的现象

偶然的思考

想知道解决问题的办法直接跳到下一部分。

在此之前我想了一些与技术无关,与行业有关的一个问题:一个软件产品是否可以无期限的满足用户的要求,如果不能的话应该支持多久?前一个问题自然是不能,从各大厂商(尤其互联网)抛弃 ie8 这个老古董开始,新技术越来越对年代久远的设备与系统难以支持,xp 系统占有率已经可以忽略不计,仅存的 ie8 用户如果使用默认的 ie 浏览器甚至不能正常看视频和网购。而第二个问题可以想的更多一点。

常见的软件升级通常有两种策略:

第一:if it don’t break, don’t upgrade。只要还能用,就打死不升级。

第二:always brand-new。所有部件永远保持最新版本。

第一种策略常见于生产环境,第二种策略嘛,常见与发烧友与折腾党的开发机。

两种方式显然难以兼得,在使用第一种模式时,最好祈祷生产环境永远不会出问题。如果出现未知的 bug,处理起来就相当麻烦。曾经看到的一个例子:说是国外的一个机场,他们某个关键程序是在几十年前写的,期间一直没有升级维护过,运行很正常,后来突然遇到了个未知 bug 导致程序错误,所有飞机不能正常起飞降落,由于这个程序的作者早已离开,所用的编成语言已经没有人使用了,至今还懂的人少之又少,能够处理这种复杂业务的人更无从找起。后来经过雇佣两个计算机专家,最终机场花费大量人力物力,耽误几天的时间把 bug 修复好了。

当然肯定会有些奇葩用户,既要保证系统的稳定性,十好几年前的系统都不升级,同时又要保证软件用到最新技术,对于这种需求先是要分析能不能实现,在能实现的情况下,这些用户又打算付出多少报酬。

对于 xp 系统来说,诞生至今已有 20 年的历史,特别是在 2014 年微软已经全面停止了对其维护更新,但是依然保留了对个人或企业的小范围支持,价格是高昂的,十几台 xp 系统的设备维护就可高达上百万美元。

问题复现

在 v5 的 web 端引入移动端组件之后,低版本浏览器上出现了一个报错:

Uncaught SyntaxError: Unexpected token ...

很明显是一个语法不兼容的报错,因为这种 … 是 es6 的扩展运算符,没有经过编译就放入正式包中了。这在手机等移动端来说并不是问题,移动端设备迭代快,软件更新也方便,除了十年前的设备(基本已经不能用了吧),都支持现代 js 语法。

所以将新引入的 vuetify 加入编译范围,依然报错:

exports is not defined

又出现了似乎 commonJs 规范的代码运行在浏览器端的问题。

解决办法

几经波折依然没能完全解决,于是决定升级 babel 编译版本,弃用这一套 4 年前的编译插件。从 package.josn 入手,将开发依赖(devDependencies)中的 "babel-“开头的一系列插件换成”@babel/" 开头的,于是最终形成了以下内容:

{
  "name": "uma-unimax-mes",
  "version": "5.1.3",
  "description": "uma-unimax-mes",
  "author": "EpicHust",
  "private": true,
  "scripts": {
  
  },
  "dependencies": {
  
  },
  "devDependencies": {
    "@babel/core": "7.12.3",
    "@babel/plugin-syntax-jsx": "7.12.1",
    "@babel/plugin-proposal-object-rest-spread": "7.12.1",
    "@babel/plugin-transform-classes": "7.12.1",
    "@babel/plugin-transform-modules-commonjs": "7.12.1",
    "@babel/plugin-transform-runtime": "7.12.1",
    "@babel/preset-env": "7.12.1",
    "@babel/preset-stage-2": "7.8.3",
    "@babel/helper-module-imports": "7.12.1",
    "@babel/register": "7.12.1",
    "@babel/runtime-corejs2": "7.12.1",
    "@vue/babel-helper-vue-jsx-merge-props": "1.2.1",
    "@vue/babel-preset-jsx": "1.2.1",
    "autoprefixer": "7.1.2",
    "babel-eslint": "7.1.1",
    "babel-jest": "26.6.3",
    "babel-loader": "8.1.0",
    "babel-plugin-dynamic-import-node": "1.2.0",
    "chalk": "2.3.0",
    "chromedriver": "2.27.2",
    "copy-webpack-plugin": "6.3.2",
    "cross-spawn": "5.0.1",
    "css-loader": "0.28.0",
    "eslint": "3.19.0",
    "eslint-config-standard": "10.2.1",
    "eslint-friendly-formatter": "3.0.0",
    "eslint-loader": "1.7.1",
    "eslint-plugin-html": "3.0.0",
    "eslint-plugin-import": "2.7.0",
    "eslint-plugin-node": "5.2.0",
    "eslint-plugin-promise": "3.5.0",
    "eslint-plugin-standard": "3.0.1",
    "eventsource-polyfill": "0.9.6",
    "extract-text-webpack-plugin": "3.0.0",
    "file-loader": "^1.1.6",
    "friendly-errors-webpack-plugin": "1.7.0",
    "gulp": "^4.0.2",
    "gulp-clean": "^0.4.0",
    "gulp-concat": "2.6.1",
    "gulp-load-plugins": "1.5.0",
    "gulp-replace": "0.6.1",
    "gulp-shell": "0.6.5",
    "html-webpack-plugin": "4.5.2",
    "jest": "21.2.0",
    "jest-serializer-vue": "0.3.0",
    "mini-css-extract-plugin": "1.3.5",
    "mkdirp": "^1.0.4",
    "nightwatch": "0.9.12",
    "node-notifier": "5.1.2",
    "null-loader": "^4.0.1",
    "optimize-css-assets-webpack-plugin": "5.0.4",
    "ora": "1.2.0",
    "portfinder": "1.0.13",
    "postcss-import": "11.0.0",
    "postcss-loader": "2.0.8",
    "prettier": "^2.1.2",
    "quill-image-extend-module": "^1.1.2",
    "rimraf": "2.6.0",
    "sass": "^1.32.8",
    "sass-loader": "^7.3.1",
    "selenium-server": "3.0.1",
    "semver": "5.3.0",
    "shelljs": "0.7.6",
    "url-loader": "0.5.8",
    "vue-jest": "1.0.2",
    "vue-loader": "^15.9.7",
    "vue-style-loader": "4.1.2",
    "vue-template-compiler": "2.6.10",
    "vuetify-loader": "^1.7.2",
    "webpack": "4.46.0",
    "webpack-bundle-analyzer": "^3.9.0",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "3.11.2",
    "webpack-merge": "^5.7.3"
  },
}

改变了 package.json 的内容后,需要删除 node_modules 重新 npm install 下载依赖才能起作用。

然后在 webpack.base.conf.js 中,对 babel 编译配置进行一些修改,改动后的内容如下:

module: {
    rules: [
      ...(config.dev.useEslint ? [createLintingRule()] : []),
      {
        test: /\.vue$/,
        loader: "vue-loader",
        // options: vueLoaderConfig
      },
      {
        test: /\.js$/,
        use: [
          {
            loader: "babel-loader",
            options: {
              configFile: resolve("babel.config.js"),
              include: [
                resolve("src/"),
                // resolve("test"),
                // resolve("node_modules/element-ui-verify/dist"),
                resolve("node_modules/vuetify/lib/"),
              ],
            },
          },
        ],
        exclude: function (modulePath) {
          return /node_modules/.test(modulePath) && !/vuetify\\lib/.test(modulePath);
        },
      },
      // 以下省略...

在 webpack.base.conf.js 其中指定了 configFile,原本是 .babelrc 文件,不过由于未知原因无法作用到 node_modules 内部的依赖插件,所以换作 babel.config.js。

我们把根目录下的.babelrc 文件删掉,新增 babel.config.js,内容如下:

module.exports = {
  presets: [
    [
      "@babel/preset-env",
      {
        // "modules": "umd",
        targets: {
          chrome: "49",
          ie: "11",
        },
        loose: true,
        useBuiltIns: "usage",
      },
    ],
    "@vue/babel-preset-jsx",
  ],
  comments: false,
  plugins: [
    [
      "@babel/plugin-transform-runtime",
      {
        corejs: 2,
      },
    ],
    "@babel/plugin-transform-classes",
    [
      "@babel/plugin-proposal-object-rest-spread",
      {
        loose: true,
      },
    ],
  ]
};

预设(presets)和插件(plugins)都使用了升级后的 babel 插件,如此一来,问题就能得到解决。

经过重新 npm run build,得到打包后的正式版代码。