首页 > 解决方案 > ReferenceError:未定义 HTMLVideoElement

问题描述

尝试为 SSR 构建 Angular 9 Universal 项目时出现此错误:

/Users/my-project/dist/server.js:28676 Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__metadata"])("design:type", HTMLVideoElement)

ReferenceError: HTMLVideoElement is not defined

我在我的项目中使用mat-video播放器,我怀疑这是导致错误的原因,但我不确定如何解决这个问题。有任何想法吗?

我的tsconfig.json

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

标签: angulartypescriptangular-universalreferenceerror

解决方案


我终于让它工作了!1周后!

我有同样的问题:

HTMLVideoElement 未定义

尽管环境与 OP 提供的环境不同,但解决方案是相同的,以避免执行 HTMLVideoElement 代码。

我将 pixi.js 与 Angular Universal 一起用于服务器端渲染 (SSR)。

ng add @nguniversal/express-engine我按照https://angular.io/guide/universal上的文档运行,在我的 Angular 应用程序中设置 SSR

我通过避免导致问题的 pixi.js 代码解决了这个问题。即:

PIXI.Texture.from('assets/image.png');

此调用在此处的函数签名中引用 HTMLVideoElement ... https://pixijs.download/dev/docs/PIXI.Texture.html#from

我改用 PIXI.Loader() 来加载纹理:

this.loader = new PIXI.Loader()
this.texturesArray.forEach((texture: string) => this.loader.add(texture))
this.loader.onComplete.add(this.onTexturesLoaded)
this.loader.load()

问题消失了,现在我的服务器启动正常。

我还必须通过添加属性来更改 tsconfig.server.json 中的“compilerOptions”:

"module": "commonjs"

这是我为任何感兴趣的人提供的最终工作 server.ts 代码:

import 'zone.js/dist/zone-node'

import { ngExpressEngine } from '@nguniversal/express-engine'
import express from 'express'
import { join } from 'path'
import { APP_BASE_HREF } from '@angular/common'
import { existsSync } from 'fs'
import { cloneDeep } from 'lodash-es'
import { mockCanvas } from './mock-canvas'

const fs = require('fs')
const path = require('path')
const filename = path.join(__dirname, '../browser', 'index.html')
const template = fs.readFileSync(filename).toString()

const domino = require('domino')
const win = domino.createWindow(template)
win.HTMLCanvasElement.prototype = cloneDeep(win.HTMLCanvasElement.prototype)
mockCanvas(win)

global['window'] = win
global['document'] = win.document

import { AppServerModule } from './src/main.server'

// The Express app is exported so that it can be used by serverless Functions.
export function app(): express.Express {
  const server = express()
  const distFolder = join(process.cwd(), 'dist/gematriangle/browser')
  const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index'

  // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
  server.engine('html', ngExpressEngine({
    bootstrap: AppServerModule
  }))

  server.set('view engine', 'html')
  server.set('views', distFolder)

  // Example Express Rest API endpoints
  // server.get('/api/**', (req, res) => { });
  // Serve static files from /browser
  server.get('*.*', express.static(distFolder, {
    maxAge: '1y'
  }))

  // All regular routes use the Universal engine
  server.get('*', (req: any, res: any) => {
    res.render(indexHtml, {req, providers: [{provide: APP_BASE_HREF, useValue: req.baseUrl}]})
  })

  return server
}

function run(): void {
  const port = process.env.PORT || 4000

  // Start up the Node server
  const server = app()
  server.listen(port, () => {
    console.log(`Node Express server listening on http://localhost:${port}`)
  })
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire
const mainModule = __non_webpack_require__.main
const moduleFilename = mainModule && mainModule.filename || ''
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
  run()
}

export * from './src/main.server'

和 PIXI 也需要的 mockCanvas 看起来像这样:

export function mockCanvas(window: any) {
  window.HTMLCanvasElement.prototype.getContext = function() {
    return {
      fillRect: function() {},
      clearRect: function() {},
      getImageData: function(x: any, y: any, w: any, h: any) {
        return {
          data: new Array(w * h * 4)
        }
      },
      putImageData: function() {},
      createImageData: function(): any { return []},
      setTransform: function() {},
      drawImage: function() {},
      save: function() {},
      fillText: function() {},
      restore: function() {},
      beginPath: function() {},
      moveTo: function() {},
      lineTo: function() {},
      closePath: function() {},
      stroke: function() {},
      translate: function() {},
      scale: function() {},
      rotate: function() {},
      arc: function() {},
      fill: function() {},
      measureText: function() {
        return {width: 0}
      },
      transform: function() {},
      rect: function() {},
      clip: function() {}
    }
  }

  window.HTMLCanvasElement.prototype.toDataURL = function() {
    return ''
  }
}

推荐阅读