android - 在 Ionic 应用程序中使用 Vega Charts 会导致在某些设备上启动时出现运行时错误

标签 android ionic-framework webpack vega vega-lite

令我懊恼的是,我发现我在我的 Android (8.0) 手机和 iPhone 上成功开发和测试的 Ionic 4 应用程序在 Android (8.1) 平板电脑的启动屏幕上卡住,并且在 iPad 上启动时崩溃。使用 adb logcat诊断技术,我观察到在错误的 Android 平板电脑上,vendor-es5.js 中报告了语法错误。 ,当我挖到我的项目的 www 文件夹并转到错误的引用行时,它说 SyntaxError: Unexpected token * ,我登陆的代码显然来自 node_modules/d3-delaunay/src/delaunay.js并且使用了 es6 幂运算符 ** , 具体来说:

r = 1e-8 * Math.sqrt((bounds[3] - bounds[1])**2 + (bounds[2] - bounds[0])**2);

我不知道为什么这段代码在某些设备上会出现问题,也不知道是什么导致了这段代码,它不是 es5 (?) 最终出现在 vendor-es5.js 中。文件没有被适本地转译。为了更进一步,我手动破解了 delaunay.js 文件,将所有求幂实例替换为其等效用法 Math.pow()果然,运行时更进一步,但最终在来自 node_modules/vega-dataflow/src/dataflow/load.js 的函数中再次搁浅。并提示SyntaxError: Unexpected token function ,特别是在这一行:

export async function request(url, format) {

同样,显然 async/await 不是 es5 构造,那么为什么它会以 vendor-es5.js 结尾? .在这一点上,我觉得这里出现了系统性错误,除了切换图形库之外,我还没有能力理解如何克服它?如果可能的话,我想避免这种情况,所以我的问题是:
  • 为什么会这样?
  • 为什么它只对部分设备产生影响,而不是对所有设备?
  • 有没有一种方法可以在不切换到其他图形库的情况下解决该问题?

  • 更新 #1

    因为它是一个 Ionic4 项目,这意味着它是一个 Angular 8 项目,这意味着它是一个 Webpack 项目(就像平台的默认设置一样)。所以这是我的angular.json文件:

    {
      "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
      "version": 1,
      "defaultProject": "app",
      "newProjectRoot": "projects",
      "projects": {
        "app": {
          "root": "",
          "sourceRoot": "src",
          "projectType": "application",
          "prefix": "app",
          "schematics": {},
          "architect": {
            "build": {
              "builder": "@angular-devkit/build-angular:browser",
              "options": {
                "outputPath": "www",
                "index": "src/index.html",
                "main": "src/main.ts",
                "polyfills": "src/polyfills.ts",
                "tsConfig": "tsconfig.app.json",
                "assets": [
                  {
                    "glob": "**/*",
                    "input": "src/assets",
                    "output": "assets"
                  },
                  {
                    "glob": "**/*.svg",
                    "input": "node_modules/ionicons/dist/ionicons/svg",
                    "output": "./svg"
                  }
                ],
                "styles": [
                  {
                    "input": "src/theme/variables.scss"
                  },
                  {
                    "input": "src/global.scss"
                  }
                ],
                "scripts": []
              },
              "configurations": {
                "production": {
                  "fileReplacements": [
                    {
                      "replace": "src/environments/environment.ts",
                      "with": "src/environments/environment.prod.ts"
                    }
                  ],
                  "optimization": true,
                  "outputHashing": "all",
                  "sourceMap": false,
                  "extractCss": true,
                  "namedChunks": false,
                  "aot": true,
                  "extractLicenses": true,
                  "vendorChunk": false,
                  "buildOptimizer": true,
                  "budgets": [
                    {
                      "type": "initial",
                      "maximumWarning": "2mb",
                      "maximumError": "5mb"
                    }
                  ]
                },
                "ci": {
                  "progress": false
                }
              }
            },
            "serve": {
              "builder": "@angular-devkit/build-angular:dev-server",
              "options": {
                "browserTarget": "app:build"
              },
              "configurations": {
                "production": {
                  "browserTarget": "app:build:production"
                },
                "ci": {
                  "progress": false
                }
              }
            },
            "extract-i18n": {
              "builder": "@angular-devkit/build-angular:extract-i18n",
              "options": {
                "browserTarget": "app:build"
              }
            },
            "test": {
              "builder": "@angular-devkit/build-angular:karma",
              "options": {
                "main": "src/test.ts",
                "polyfills": "src/polyfills.ts",
                "tsConfig": "tsconfig.spec.json",
                "karmaConfig": "karma.conf.js",
                "styles": [],
                "scripts": [],
                "assets": [
                  {
                    "glob": "favicon.ico",
                    "input": "src/",
                    "output": "/"
                  },
                  {
                    "glob": "**/*",
                    "input": "src/assets",
                    "output": "/assets"
                  }
                ]
              },
              "configurations": {
                "ci": {
                  "progress": false,
                  "watch": false
                }
              }
            },
            "lint": {
              "builder": "@angular-devkit/build-angular:tslint",
              "options": {
                "tsConfig": [
                  "tsconfig.app.json",
                  "tsconfig.spec.json",
                  "e2e/tsconfig.json"
                ],
                "exclude": ["**/node_modules/**"]
              }
            },
            "e2e": {
              "builder": "@angular-devkit/build-angular:protractor",
              "options": {
                "protractorConfig": "e2e/protractor.conf.js",
                "devServerTarget": "app:serve"
              },
              "configurations": {
                "production": {
                  "devServerTarget": "app:serve:production"
                },
                "ci": {
                  "devServerTarget": "app:serve:ci"
                }
              }
            },
            "ionic-cordova-build": {
              "builder": "@ionic/angular-toolkit:cordova-build",
              "options": {
                "browserTarget": "app:build"
              },
              "configurations": {
                "production": {
                  "browserTarget": "app:build:production"
                }
              }
            },
            "ionic-cordova-serve": {
              "builder": "@ionic/angular-toolkit:cordova-serve",
              "options": {
                "cordovaBuildTarget": "app:ionic-cordova-build",
                "devServerTarget": "app:serve"
              },
              "configurations": {
                "production": {
                  "cordovaBuildTarget": "app:ionic-cordova-build:production",
                  "devServerTarget": "app:serve:production"
                }
              }
            }
          }
        }
      },
      "cli": {
        "defaultCollection": "@ionic/angular-toolkit"
      },
      "schematics": {
        "@ionic/angular-toolkit:component": {
          "styleext": "scss"
        },
        "@ionic/angular-toolkit:page": {
          "styleext": "scss"
        }
      }
    }
    

    ...这是我的 package.json 的相关子集项目文件:

    {
      "dependencies": {
        "@angular/common": "~8.1.2",
        "@angular/core": "~8.1.2",
        "@angular/forms": "~8.1.2",
        "@angular/http": "^7.2.15",
        "@angular/platform-browser": "~8.1.2",
        "@angular/platform-browser-dynamic": "~8.1.2",
        "@angular/router": "~8.1.2",
        "@ionic-native/core": "^5.15.1",
        "@ionic/angular": "^4.7.1",
        "vega": "~5.6.0",
        "vega-lite": "^3.4.0",
        "vega-themes": "^2.4.0",
        "zone.js": "~0.9.1"
      },
      "devDependencies": {
        "@angular-devkit/architect": "~0.801.2",
        "@angular-devkit/build-angular": "~0.801.2",
        "@angular-devkit/core": "~8.1.2",
        "@angular-devkit/schematics": "~8.1.2",
        "@angular/cli": "~8.1.2",
        "@angular/compiler": "~8.1.2",
        "@angular/compiler-cli": "~8.1.2",
        "@angular/language-service": "~8.1.2",
        "@ionic/angular-toolkit": "~2.0.0",
        "@types/jasmine": "~3.3.8",
        "@types/jasminewd2": "~2.0.3",
        "@types/node": "~8.9.4",
        "codelyzer": "^5.0.0",
        "jasmine-core": "~3.4.0",
        "jasmine-spec-reporter": "~4.2.1",
        "karma": "~4.1.0",
        "karma-chrome-launcher": "~2.2.0",
        "karma-coverage-istanbul-reporter": "~2.0.1",
        "karma-jasmine": "~2.0.1",
        "karma-jasmine-html-reporter": "^1.4.0",
        "protractor": "~5.4.0",
        "ts-node": "~7.0.0",
        "tslint": "~5.15.0",
        "typescript": "~3.4.3"
      }
    }
    

    更新 #2

    继续尝试解决这个问题,我对 package.json 进行了以下一组更新。 :
      "dependences": 
        "tslib": added => "^1.10.0" 
        "vega": "~5.6.0" => "^5.9.0"
        "vega-lite": "^3.4.0" => "^4.0.2"
    
      "devDependencies": 
        "@angular/compiler": "~8.1.2" => "~8.2.9"
        "@angular/compiler-cli": "~8.1.2" => "~8.2.9"
        "typescript": "~3.4.3" => "~3.5.3"
    

    ...通过这些更改,我想我在 www/vendor-es5.js 中得到了明显的 es5 编译输出。文件和我的adb logcat结果似乎没有指示语法错误。不幸的是,该应用程序仍然无法通过启动画面(这只是在某些设备上的情况)。

    这是我的tsconfig.json项目中的文件:

    {
      "compileOnSave": false,
      "compilerOptions": {
        "baseUrl": "./",
        "outDir": "./dist/out-tsc",
        "sourceMap": true,
        "declaration": false,
        "module": "esnext",
        "moduleResolution": "node",
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "importHelpers": true,
        "target": "es2015",
        "typeRoots": [
          "node_modules/@types"
        ],
        "lib": [
          "es2018",
          "dom"
        ]
      },
      "angularCompilerOptions": {
        "fullTemplateTypeCheck": true,
        "strictInjectionParameters": true
      }
    }
    

    ...就 vega 的使用而言它的症结在于:

        const theme = vega.fivethirtyeight;
        this._view = new vega.View(vega.parse(vegaSpec, theme), {})
          .initialize(this.container.nativeElement)
          .logLevel(vega.Warn)
          .renderer('svg');
    

    ... 如果我过滤 adb logcat,则在有问题的设备上输出到 E (错误)行,我看到了:
    01-10 09:17:27.650  6413  6413 E ApkAssets: Error while loading asset assets/natives_blob_64.bin: java.io.FileNotFoundException: assets/natives_blob_64.bin
    01-10 09:17:27.651  6413  6413 E ApkAssets: Error while loading asset assets/snapshot_blob_64.bin: java.io.FileNotFoundException: assets/snapshot_blob_64.bin
    01-10 09:17:27.680  6413  6413 E         : appName=xxxxxx, acAppName=/system/bin/surfaceflinger
    01-10 09:17:27.680  6413  6413 E         : 0
    01-10 09:17:27.683  6413  6413 E         : appName=xxxxxx, acAppName=vStudio.Android.Camera360
    01-10 09:17:27.683  6413  6413 E         : 0
    01-10 09:17:27.781  6413  6413 E MPlugin : Unsupported class: com.mediatek.common.telephony.IOnlyOwnerSimSupport
    01-10 09:17:28.153  6413  6464 E libEGL  : validate_display:99 error 3008 (EGL_BAD_DISPLAY)
    01-10 09:17:28.432  6413  6464 E         : appName=xxxxxx, acAppName=vStudio.Android.Camera360
    01-10 09:17:28.433  6413  6464 E         : 0
    01-10 09:17:28.436  6413  6464 E         : appName=xxxxxx, acAppName=vStudio.Android.Camera360
    01-10 09:17:28.436  6413  6464 E         : 0
    01-10 09:17:28.437  6413  6464 E         : appName=xxxxxx, acAppName=vStudio.Android.Camera360
    01-10 09:17:28.437  6413  6464 E         : 0
    01-10 09:17:30.514  6413  6455 E         : appName=xxxxxx, acAppName=vStudio.Android.Camera360
    01-10 09:17:30.514  6413  6455 E         : 0
    01-10 09:17:30.515  6413  6455 E         : app
    

    ...而且这里是W (警告)行:
    01-10 09:17:27.835  6413  6413 W chromium: [WARNING:password_handler.cc(33)] create-->contents = 0x9c66ec00, delegate = 0xa4b7edd0
    01-10 09:17:27.835  6413  6413 W chromium: [WARNING:password_handler.cc(41)] attaching to web_contents 
    01-10 09:17:27.837  6413  6413 W cr_AwContents: onDetachedFromWindow called when already detached. Ignoring
    01-10 09:17:28.185  6413  6455 W libEGL  : [ANDROID_RECORDABLE] format: 1
    01-10 09:17:28.209  6413  6464 W VideoCapabilities: Unrecognized profile/level 1/32 for video/mp4v-es
    01-10 09:17:28.209  6413  6464 W VideoCapabilities: Unrecognized profile/level 32768/2 for video/mp4v-es
    01-10 09:17:28.209  6413  6464 W VideoCapabilities: Unrecognized profile/level 32768/64 for video/mp4v-es
    01-10 09:17:28.244  6413  6455 W libEGL  : [ANDROID_RECORDABLE] format: 1
    01-10 09:17:28.248  6413  6464 W VideoCapabilities: Unsupported mime video/x-ms-wmv
    01-10 09:17:28.253  6413  6464 W VideoCapabilities: Unsupported mime video/divx
    01-10 09:17:28.262  6413  6464 W VideoCapabilities: Unsupported mime video/xvid
    01-10 09:17:28.268  6413  6464 W VideoCapabilities: Unsupported mime video/flv1
    01-10 09:17:28.274  6413  6464 W VideoCapabilities: Unrecognized profile/level 1/32 for video/mp4v-es
    01-10 09:17:28.485  6413  6413 W cr_BindingManager: Cannot call determinedVisibility() - never saw a connection for the pid: 6413
    01-10 09:17:28.568  6413  6413 W cr_BindingManager: Cannot call determinedVisibility() - never saw a connection for the pid: 6413
    

    最佳答案

    首先我想说的是真的是vega包错误 - 我认为通过 npm 传递未编译的代码是一种糟糕的方式。例如 Angular Package Format如果需要,保证您将获得 es5 有效代码。但是vega不清楚angular依赖所以让我们解决它。

    为什么会这样?

    因为有些开发者在 es6+ 中提供包标准,直到您需要 es5兼容的应用程序。在我看来,图书馆开发人员应该构建和交付 es5es6 bundle ,否则他们的用户会很头疼(例如您的情况 vega )。

    为什么它只对部分设备产生影响,而不是对所有设备?

    老实说,我在原生移动开发方面的经验非常有限——我在这里只能说,例如移动 Chrome 和桌面 Chrome 在它们的引擎上存在一些差异。这意味着不能保证使用相同的软件会提供相同的结果。有时您可以在移动浏览器中找到该错误,而无法在桌面浏览器中重现它。

    我认为在您的情况下,某些带有某些浏览器引擎的设备可以使用 es6代码 - 有些就是不能。
    在您的问题的第一个版本中,还有用户代理字符串 - 我认为高级移动开发人员可以说比我更多。

    有没有一种方法可以在不切换到其他图形库的情况下解决该问题?

    是的。
    我创建了一个 repo设置与您的非常相似 - 简单 ionic@4项目基于 angular@8 .

    您的 bundle 现在是 es5es6混合。让我们全力以赴es5兼容在任何浏览器中工作(我甚至在 ie11 中测试了这个项目)。
    完成工作的步骤:

  • 安装依赖项。我们将在进一步的步骤中需要它们。
  • npm i -S regenerator-runtime
    npm i -D @angular-builders/custom-webpack babel-loader @babel/core @babel/preset-env
    
  • 更改target属性(property)给 es5tsconfig . "target": "es5"
  • 我们将转译 async/await所以我们需要regenerator-runtime polyfill 被添加到 polyfills.tsimport 'regenerator-runtime/runtime'
  • 主要步骤。在 angular.json 中更改构建器并将路径添加到 webpack.config.jsbuild 使用自定义 webpack 配置和 serve :
  •        "build": {
              "builder": "@angular-builders/custom-webpack:browser",
              "options": {
                "customWebpackConfig": {
                     "path": "./webpack.config.js"
                  },
    ...
            "serve": {
              "builder": "@angular-builders/custom-webpack:dev-server",
    
  • 创建 webpack.config.js在根文件夹中,带有用于转换 vega 及其依赖项的规则。我以非常迫切的方式找到了它们。
  • // these dependencies are es6!!!
    const transpileList = ['node_modules/vega', 'node_modules/d3', 'node_modules/delaunator'];
    
    module.exports = function(base) {
        return {
            ...base,
            module: {
                ...base.module,
                rules: [
                    ...base.module.rules,
                    {
                        test: function(fileName) {
                            return transpileList.some(name => fileName.includes(name)) && fileName.endsWith('.js');
                        },
                        use: {
                            loader: 'babel-loader',
                            options: {
                                presets: ['@babel/preset-env']
                            }
                        }
                    }
                ]
            }
        }
    }
    

    完成这些步骤后,我希望您的应用程序可以在任何 es5 中运行。环境。我在桌面上试过 ie11和平板电脑三星 A 与默认三星浏览器。

    关于android - 在 Ionic 应用程序中使用 Vega Charts 会导致在某些设备上启动时出现运行时错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59606443/

    相关文章:

    Android FCM - 如何只显示一个通知

    angular - 未满足的对等依赖@ionic-native/native-audio

    webpack - 如何使用 Webpack 包含包许可信息?

    android - Ionic 无法构建 Android 应用程序,未找到 ANDROID_HOME,但已设置

    iphone - 手机计步器应用程序

    ionic-framework - Ionic显示联系人类似于本地设备联系人列表

    angularjs - 如何从设备中选择多个图像?

    Angular-CLI 全局 scss 变量

    javascript - 如何使用 webpack 在 Angular 应用程序中动态加载 Assets (pdf)?

    ListView 中的 Android Spinners,我必须使用 ViewHolder 吗?