在 Ionic 应用程序中使用 Vega 图表会导致在某些设备上启动时出现运行时错误
Using Vega Charts in an Ionic App causes runtime errors in launching on some devices
令我懊恼的是,我发现我开发的 Ionic 4 应用程序在 Android (8.0) phone 以及 iPhone,在 Android (8.1) 平板电脑的初始屏幕上冻结,在 iPad 上启动时崩溃。使用 adb logcat
诊断技术,我观察到在 er运行t 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
文件没有被适当地 t运行spiled。为了更进一步,我手动修改了那个 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
兼容应用程序之前它是可以的。在我看来,图书馆开发人员应该构建和交付 es5
和 es6
捆绑包,否则他们的用户会很头疼(就像您使用 vega
的情况)。
为什么它只对部分而非所有设备产生影响?
老实说,我在本机移动开发方面的经验非常有限 - 我在这里只能说的是,例如移动 Chrome 和桌面 Chrome 在它们的引擎上存在一些差异。这意味着不能保证使用相同的软件会提供相同的结果。有时您可以在移动浏览器中找到错误,但无法在桌面浏览器中重现。
我认为在您的情况下,某些带有某些浏览器引擎的设备可以使用 es6
代码——而有些则不能。
同样在您的问题的第一个版本中有用户代理字符串 - 我认为高级移动开发人员可以比我说更多使用。
有没有一种方法可以在不切换到其他图形库的情况下解决这个问题?
是的。
我创建了一个 repo,其设置与您的非常相似 - 基于 angular@8
的简单 ionic@4
项目。
您的捆绑包现在是 es5
和 es6
的混合。让我们让它完全 es5
兼容以在任何浏览器中工作(我什至在 ie11
中测试了这个项目)。
完成工作的步骤:
- 安装依赖项。我们将在后续步骤中需要它们。
npm i -S regenerator-runtime
npm i -D @angular-builders/custom-webpack babel-loader @babel/core @babel/preset-env
- 将
target
属性 更改为 tsconfig
中的 es5
。 "target": "es5"
- 我们将转译
async/await
,因此我们需要将 regenerator-runtime
polyfill 添加到 polyfills.ts
作为 import 'regenerator-runtime/runtime'
- 主要步骤。更改
angular.json
中的构建器并将路径添加到 webpack.config.js
以使用自定义 webpack 配置 build
和 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
和平板电脑 Samsung A 中尝试使用默认的三星浏览器。
令我懊恼的是,我发现我开发的 Ionic 4 应用程序在 Android (8.0) phone 以及 iPhone,在 Android (8.1) 平板电脑的初始屏幕上冻结,在 iPad 上启动时崩溃。使用 adb logcat
诊断技术,我观察到在 er运行t 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
文件没有被适当地 t运行spiled。为了更进一步,我手动修改了那个 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
兼容应用程序之前它是可以的。在我看来,图书馆开发人员应该构建和交付 es5
和 es6
捆绑包,否则他们的用户会很头疼(就像您使用 vega
的情况)。
为什么它只对部分而非所有设备产生影响?
老实说,我在本机移动开发方面的经验非常有限 - 我在这里只能说的是,例如移动 Chrome 和桌面 Chrome 在它们的引擎上存在一些差异。这意味着不能保证使用相同的软件会提供相同的结果。有时您可以在移动浏览器中找到错误,但无法在桌面浏览器中重现。
我认为在您的情况下,某些带有某些浏览器引擎的设备可以使用 es6
代码——而有些则不能。
同样在您的问题的第一个版本中有用户代理字符串 - 我认为高级移动开发人员可以比我说更多使用。
有没有一种方法可以在不切换到其他图形库的情况下解决这个问题?
是的。
我创建了一个 repo,其设置与您的非常相似 - 基于 angular@8
的简单 ionic@4
项目。
您的捆绑包现在是 es5
和 es6
的混合。让我们让它完全 es5
兼容以在任何浏览器中工作(我什至在 ie11
中测试了这个项目)。
完成工作的步骤:
- 安装依赖项。我们将在后续步骤中需要它们。
npm i -S regenerator-runtime
npm i -D @angular-builders/custom-webpack babel-loader @babel/core @babel/preset-env
- 将
target
属性 更改为tsconfig
中的es5
。"target": "es5"
- 我们将转译
async/await
,因此我们需要将regenerator-runtime
polyfill 添加到polyfills.ts
作为import 'regenerator-runtime/runtime'
- 主要步骤。更改
angular.json
中的构建器并将路径添加到webpack.config.js
以使用自定义 webpack 配置build
和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
和平板电脑 Samsung A 中尝试使用默认的三星浏览器。