Использование Vega Charts в приложении Ioni c приводит к ошибкам при запуске на некоторых устройствах - PullRequest
7 голосов
/ 06 января 2020

К моему большому огорчению, я обнаружил, что приложение Ioni c 4, которое я разработал и успешно протестировал на своем телефоне Android (8.0), а также на iPhone, зависает на spla sh экран на планшете Android (8.1) и вылетает во время запуска на iPad. Используя методы adb logcat Diagnosti c, я заметил, что на ошибочном планшете Android в vendor-es5.js сообщалось о синтаксической ошибке, которая, когда я копался в www folder моего проекта и переходил к ссылочной строке из-за ошибки, которая говорит 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);

Я не знаю, почему это код проблематичен c на некоторых устройствах, и я не знаю, что вызывает этот код, который не является 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. Почему это происходит?
  2. Почему это влияет только на некоторые, а не на все устройства ?
  3. Можно ли обойти эту проблему, не переключаясь на другую графическую библиотеку?

Обновление # 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"
  }
}

Update # 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"

... с этими изменениями, я думаю Я получаю очевидный скомпилированный вывод es5 в файле www/vendor-es5.js, и мои результаты adb logcat не указывают на синтаксические ошибки. К сожалению, приложению все еще не удается пройти экран Spla sh (опять же, это касается только некоторых устройств).

Вот мой файл 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');

... на проблемном устройстве c, если я отфильтрую 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

1 Ответ

3 голосов
/ 11 января 2020

Прежде всего я хочу сказать, что это действительно vega ошибка пакета - я думаю, что это плохой способ доставки нетранслируемого кода через npm. Например, Angular Формат пакета гарантирует, что вы получите действительный код es5, если он вам нужен. Но vega не является четкой зависимостью angular, поэтому давайте ее решим.

Почему это происходит?

Поскольку некоторые разработчики поставляют пакеты в es6+ стандарте, и это нормально, пока вам не понадобится es5 совместимое приложение. По моему мнению, разработчики библиотек должны создавать и поставлять комплекты es5 и es6, иначе это будет головной болью для их пользователей (например, ваш случай с vega).

Почему это только влияет на некоторых, а не на всех устройствах?

Если честно, у меня очень ограниченный опыт разработки собственных мобильных устройств - все, что я могу здесь сказать, это то, что, например, мобильные Chrome и настольные Chrome имеют некоторые различия их двигатели. Это означает, что нет гарантии, что использование одного и того же программного обеспечения даст тот же результат. Иногда вы можете найти ошибку в мобильном браузере и не можете воспроизвести ее в настольном браузере.

Я думаю, что в вашем случае некоторые устройства с некоторыми браузерными движками могут использовать код es6 - а некоторые просто не могут.
Также в первой версии вашего вопроса были строки useragent - я думаю, продвинутый мобильный разработчики могут сказать больше, чем я.

Можно ли обойти эту проблему, не переключаясь на другую графическую библиотеку?

Да. Я создал репо с настройкой, очень похожей на вашу - простой ionic@4 проект, основанный на angular@8.

Ваш пакет теперь es5 и es6 смешан. Давайте сделаем его полностью es5 совместимым для работы в любом браузере (я тестировал этот проект даже в ie11).
Шаги, чтобы выполнить работу:

  1. Установка зависимостей. Они понадобятся нам в дальнейших шагах.
npm i -S regenerator-runtime
npm i -D @angular-builders/custom-webpack babel-loader @babel/core @babel/preset-env
Измените target свойство на es5 в tsconfig. "target": "es5" Мы проведем async/await, поэтому нам нужно добавить regenerator-runtime polyfill к polyfills.ts как import 'regenerator-runtime/runtime' Основной шаг. Измените компоновщики в angular.json и добавьте путь к webpack.config.js, чтобы использовать пользовательскую конфигурацию веб-пакета для 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 в папке root с правилами для переноса 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 по умолчанию.

...