最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to use an external third party library in Stencil.js - Stack Overflow

programmeradmin3浏览0评论

I have a Stencil ponent and, as part of its business logic, I need to use an external Javascript file mylib.js. The Javascript file contains some business logic the Stencil ponent should use.

Here's my ponent:

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;

  ponentDidLoad() {
      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>
    );
  }
}

I keep getting the error

TypeScript Cannot find module 'src/js/mylib.js'.

I tried:

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

import 'src/js/mylib.js';

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

to no avail.

The mylib.js file is inside the js folder. The online documentation doesn't mention at all how to import external libraries. I've been successful importing moment.js but only because I installed it first by doing:

npm install moment

and then importing it inside the ponent by doing:

import moment from 'moment';

I also tried to "import" the external JS library by referencing it inside the index.html

<script src="assets/js/mylib.js"></script>

The library is available outside the ponent but not inside it

I have a Stencil ponent and, as part of its business logic, I need to use an external Javascript file mylib.js. The Javascript file contains some business logic the Stencil ponent should use.

Here's my ponent:

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;

  ponentDidLoad() {
      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>
    );
  }
}

I keep getting the error

TypeScript Cannot find module 'src/js/mylib.js'.

I tried:

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

import 'src/js/mylib.js';

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

to no avail.

The mylib.js file is inside the js folder. The online documentation doesn't mention at all how to import external libraries. I've been successful importing moment.js but only because I installed it first by doing:

npm install moment

and then importing it inside the ponent by doing:

import moment from 'moment';

I also tried to "import" the external JS library by referencing it inside the index.html

<script src="assets/js/mylib.js"></script>

The library is available outside the ponent but not inside it

Share Improve this question edited Mar 16, 2020 at 11:08 Gianluca Ghettini asked Mar 11, 2020 at 10:09 Gianluca GhettiniGianluca Ghettini 11.6k24 gold badges99 silver badges167 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 14

Since you're mentioning Moment.js, I'll explain how to load that first. It's possible to do it the way you did by importing it inside your ponent's module, however that will result in a large bundle size because the npm package of moment is not targeted for browsers but for use in Node.js projects where bundle size doesn't matter. Moment.js distributes browser bundles inside the package though. So what you can do is add a copy task to your Stencil output target to copy node_modules/moment/min/moment.min.js into your build directory:

// 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'
        }
      ]
    }
  ]
};

Then, as you already tried with your lib, you can load that script in src/index.html:

<script src="/lib/moment.min.js"></script>

However, your Typescript project won't know yet that moment is available in the global scope. That's easy to solve though, you can just add a declaration file somewhere in your project, e. g. src/global.d.ts with the content

import _moment from 'moment';

declare global {
    const moment: typeof _moment;
}

For your test files which run in the Node.js context, not a browser context, you can either import moment or also add moment to the global scope, by creating a file (e. g. jest-setup-file.js) with the content

global.moment = require('moment');

and then in stencil.config.ts you just add the setupFilesAfterEnv field to testing:

{
  testing: {
    setupFilesAfterEnv: ['<rootDir>/jest-setup-file.js']
  }
}

If you don't need the script in your whole application but only when a specific ponent is loaded, it makes more sense to only load the script from within that ponent. The easiest way to do that is to create a script element and add it to the DOM:

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

export class MyComp {
  ponentWillLoad() {
    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);
  }
}

Your script/library will most likely also contribute a variable to the global scope, so you'll have to let Typescript know again, which you can do using the declare keyword to declare that a variable exists in the global context (see https://www.typescriptlang/docs/handbook/declaration-files/by-example.html#global-variables).


As another example, here's a helper I wrote to load the google maps 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./maps/api/js?key=${googleApiKey}&libraries=places`;

    document.body.appendChild(script);
  });

(The google type es from the @types/googlemaps package)

I can then use this like

const maps = await importMapsApi();
const service = new maps.DistanceMatrixService();

To import files that aren't installed by NPM you can use relative paths prefixed with ./ or ../:

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

You can import everything that is exported from that JS file and even use named imports:

mylib.js

export function someFunction() {
  // some logic
}

dashboard-widget.tsx

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

const result = someFunction();
发布评论

评论列表(0)

  1. 暂无评论