如何在 Stencil.js 中使用外部第三方库

Gia*_*ini 1 javascript web-component stenciljs stencil-component

我有一个 Stencil 组件,作为其业务逻辑的一部分,我需要使用外部 Javascript 文件mylib.js。Javascript 文件包含一些 Stencil 组件应该使用的业务逻辑。

这是我的组件:

import { Component, Element, Prop, h, Host } from '@stencil/core';
import moment from 'moment';
import mylib from 'src/js/mylib.js';

@Component({
  tag: 'dashboard-widget',
  styleUrl: 'dashboard-widget.css',
  shadow: false
})
export class DashboardWidget {

  @Element() el: HTMLElement;

  @Prop() canvas: HTMLElement;
  @Prop() channelName: string = "";
  @Prop() channelValue: string = "";
  @Prop() isBlinking: boolean = true;

  componentDidLoad() {
      console.log(mylib.test());
  }

  render() {
    return (
      <Host>
      <div class="card-content card-content-padding">
          <b>{this.channelName}</b>
          <h1 class="dashboard-card-value">{this.channelValue}</h1>
          <canvas class="dashboard-card-canvas"></canvas>
      </div>
      </Host>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

我不断收到错误

打字稿找不到模块“src/js/mylib.js”。

我试过:

import mylib from 'src/js/mylib.js';

import 'src/js/mylib.js';

import 'mylib' from 'src/js/mylib.js';
Run Code Online (Sandbox Code Playgroud)

无济于事。

mylib.js文件位于js文件夹内。在线文档根本没有提到如何导入外部库。我已经成功导入了 moment.js,但这只是因为我首先通过以下方式安装了它:

npm install moment
Run Code Online (Sandbox Code Playgroud)

然后通过执行以下操作将其导入到组件中:

import moment from 'moment';
Run Code Online (Sandbox Code Playgroud)

我还尝试通过在 index.html 中引用它来“导入”外部 JS 库

<script src="assets/js/mylib.js"></script>
Run Code Online (Sandbox Code Playgroud)

该库在组件外部可用,但在组件内部不可用

Sim*_*sch 6

既然您提到了 Moment.js,我将首先解释如何加载它。可以通过将它导入到组件的模块中来按照您的方式执行此操作,但是这将导致包大小过大,因为 moment 的 npm 包不是针对浏览器的,而是用于在包大小不适用的 Node.js 项目中使用的没关系。不过,Moment.js 在包内分发浏览器包。因此,您可以做的是将复制任务添加到您的 Stencil 输出目标以复制node_modules/moment/min/moment.min.js到您的构建目录中:

// stencil.config.ts

import { Config } from '@stencil/core';

export const config: Config = {
  namespace: 'my-app',
  outputTargets: [
    {
      type: 'www',
      copy: [
        {
          src: '../node_modules/moment/min/moment.min.js',
          dest: 'lib/moment.min.js'
        }
      ]
    }
  ]
};
Run Code Online (Sandbox Code Playgroud)

然后,正如您已经尝试过使用 lib,您可以在src/index.html以下位置加载该脚本:

<script src="/lib/moment.min.js"></script>
Run Code Online (Sandbox Code Playgroud)

但是,您的 Typescript 项目还不知道它moment在全局范围内可用。不过这很容易解决,您只需在项目中的某处添加一个声明文件,例如src/global.d.ts包含内容

import _moment from 'moment';

declare global {
    const moment: typeof _moment;
}
Run Code Online (Sandbox Code Playgroud)

对于在 Node.js 上下文而不是浏览器上下文中运行的测试文件,您可以导入 moment 或添加moment到全局范围,方法是创建一个jest-setup-file.js包含内容的文件(例如)

global.moment = require('moment');
Run Code Online (Sandbox Code Playgroud)

然后在stencil.config.ts您只需将该setupFilesAfterEnv字段添加到testing

{
  testing: {
    setupFilesAfterEnv: ['<rootDir>/jest-setup-file.js']
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您不需要在整个应用程序中使用该脚本而仅在加载特定组件时才需要,则仅从该组件内加载脚本更有意义。最简单的方法是创建一个script元素并将其添加到 DOM:

declare const MyLib: any; // assuming that `mylib.js` adds `MyLib` to the global scope

export class MyComp {
  componentWillLoad() {
    const src = "assets/js/mylib.js";

    if (document.querySelector(`script[src="${src}"]`)) {
      return; // already exists
    }

    const script = document.createElement('script');
    script.src = src;

    document.head.appendChild(script);
  }
}
Run Code Online (Sandbox Code Playgroud)

您的脚本/库很可能还会为全局范围贡献一个变量,因此您必须再次让 Typescript 知道,您可以使用declare关键字来声明全局上下文中存在变量(请参阅https:// www.typescriptlang.org/docs/handbook/declaration-files/by-example.html#global-variables)。


再举一个例子,这是我写的一个帮助程序来加载谷歌地图 api:

export const importMapsApi = async () =>
  new Promise<typeof google.maps>((resolve, reject) => {
    if ('google' in window) {
      return resolve(google.maps);
    }

    const script = document.createElement('script');

    script.onload = () => resolve(google.maps);
    script.onerror = reject;
    script.src = `https://maps.googleapis.com/maps/api/js?key=${googleApiKey}&libraries=places`;

    document.body.appendChild(script);
  });
Run Code Online (Sandbox Code Playgroud)

google类型来自@types/googlemaps包装)

然后我可以像这样使用

const maps = await importMapsApi();
const service = new maps.DistanceMatrixService();
Run Code Online (Sandbox Code Playgroud)


Tho*_*mas 2

要导入 NPM 未安装的文件,您可以使用前缀为./或 的相对路径../

import mylib from '../../js/mylib.js';
Run Code Online (Sandbox Code Playgroud)

您可以导入从该 JS 文件导出的所有内容,甚至可以使用命名导入:

mylib.js

export function someFunction() {
  // some logic
}
Run Code Online (Sandbox Code Playgroud)

仪表板小部件.tsx

import { someFunction } from '../../js/mylib.js`;

const result = someFunction();
Run Code Online (Sandbox Code Playgroud)