在Ionic App中使用Vega Charts在某些设备上启动时会导致运行时错误

时间:2020-01-06 03:47:39

标签: 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(?)的代码以{{1}结尾}文件,而未进行适当的编译。为了更进一步,我手动破解了delaunay.js文件,将所有幂运算实例替换为vendor-es5.js的等效用法,并且可以肯定的是,运行时进一步扩大了,但是最终在某个函数中再次搁浅了来自Math.pow(),并抱怨node_modules/vega-dataflow/src/dataflow/load.js,特别是在这一行:

SyntaxError: Unexpected token function

同样,显然async / await不是es5构造,所以为什么它以export async function request(url, format) { 结尾。在这一点上,我觉得这里有些系统上的错误,而且我没有能力去理解如何克服它,除非切换图形库?我想避免这种情况,所以我的问题是:

  1. 为什么会这样?
  2. 为什么只对某些设备(而非全部)产生影响?
  3. 有没有一种方法可以解决该问题而无需切换到其他图形库?

更新#1

因为它是Ionic4项目,所以它意味着它是Angular 8项目,并且这意味着它是Webpack项目(与平台的默认设置一样)。这是我的vendor-es5.js文件:

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

更新#2

继续尝试解决这一问题,我对{ "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" } } 进行了以下更新:

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" 文件中得到了明显的es5编译输出,而我的www/vendor-es5.js结果似乎没有表明语法错误。不幸的是,该应用程序仍然无法通过“启动画面”(再次出现在 some 设备上)。

这是我来自项目的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

1 个答案:

答案 0 :(得分:3)

首先,我想说这确实是vega包错误-我认为这是通过npm传递未编译代码的一种不好的方法。例如Angular Package Format保证您将获得es5有效的代码(如果需要)。但是vega并不是明确的angular依赖性,因此让我们解决它。

为什么会这样?

因为某些开发人员以es6+标准提供软件包,所以在您需要es5兼容的应用程序之前是可以的。在我看来,库开发人员应该构建并交付es5es6捆绑包,否则对他们的用户来说将是头痛的事情(例如您在vega的情况下)。

为什么它只对某些设备而不是所有设备产生影响?

说实话,我在原生移动开发方面的经验非常有限-我在这里只能说,例如,移动Chrome和台式机Chrome的引擎有所不同。这意味着不能保证使用相同的软件将提供相同的结果。有时您可以在移动浏览器中找到该错误,而无法在桌面浏览器中重现该错误。

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

是否有一种方法可以解决此问题而无需切换到其他图形库?

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

您的捆绑包现在es5es6混合在一起了。让我们完全es5兼容它以使其在任何浏览器中均可工作(即使在ie11中,我也测试了该项目)。
完成工作的步骤:

  1. 安装依赖项。我们将在进一步的步骤中需要它们。
npm i -S regenerator-runtime
npm i -D @angular-builders/custom-webpack babel-loader @babel/core @babel/preset-env
  1. target中将es5属性更改为tsconfig"target": "es5"
  2. 我们将转运async/await,因此我们需要将regenerator-runtime的polyfill作为polyfills.ts添加到import 'regenerator-runtime/runtime'
  3. 主要步骤。更改angular.json中的构建器,并将路径添加到webpack.config.js以对buildserve使用自定义Webpack配置:
       "build": {
          "builder": "@angular-builders/custom-webpack:browser",
          "options": {
            "customWebpackConfig": {
                 "path": "./webpack.config.js"
              },
...
        "serve": {
          "builder": "@angular-builders/custom-webpack:dev-server",
  1. 在根文件夹中创建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和平板电脑Samsung A中使用默认的Samsung浏览器。