如何在 Stencil.js 中使用外部第三方库
How to use an external third party library in Stencil.js
我有一个 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>
);
}
}
我一直收到错误消息
TypeScript Cannot find module 'src/js/mylib.js'.
我试过了:
import mylib from 'src/js/mylib.js';
import 'src/js/mylib.js';
import 'mylib' from 'src/js/mylib.js';
无济于事。
mylib.js
文件位于 js
文件夹中。
在线文档根本没有提到如何导入外部库。我已经成功导入 moment.js 但只是因为我首先通过以下方式安装了它:
npm install moment
然后通过执行以下操作将其导入到组件中:
import moment from 'moment';
我还尝试通过在 index.html
中引用它来 "import" 外部 JS 库
<script src="assets/js/mylib.js"></script>
库在组件外部可用,但在组件内部不可用
既然你提到了 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'
}
]
}
]
};
然后,正如您已经尝试过的那样,您可以在 src/index.html
:
中加载该脚本
<script src="/lib/moment.min.js"></script>
但是,您的 Typescript 项目还不知道 moment
在全局范围内可用。不过这很容易解决,您只需在项目的某处添加一个声明文件,例如。 G。 src/global.d.ts
与内容
import _moment from 'moment';
declare global {
const moment: typeof _moment;
}
对于 运行 在 Node.js 上下文而非浏览器上下文中的测试文件,您可以导入时刻或将 moment
添加到全局范围,方法是创建一个内容为
的文件(例如jest-setup-file.js
)
global.moment = require('moment');
然后在 stencil.config.ts
中,您只需将 setupFilesAfterEnv
字段添加到 testing
:
{
testing: {
setupFilesAfterEnv: ['<rootDir>/jest-setup-file.js']
}
}
如果您不需要在整个应用程序中使用该脚本,而仅在加载特定组件时才需要,则仅从该组件中加载脚本更有意义。最简单的方法是创建一个 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);
}
}
你的 script/library 很可能也会为全局范围贡献一个变量,所以你必须再次让 Typescript 知道,你可以使用 declare
关键字来声明一个变量存在于全局上下文中(参见 https://www.typescriptlang.org/docs/handbook/declaration-files/by-example.html#global-variables)。
作为另一个例子,这是我写的一个帮助程序来加载 google 地图 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);
});
(google
类型来自 @types/googlemaps
包)
然后我可以像这样使用它
const maps = await importMapsApi();
const service = new maps.DistanceMatrixService();
要导入 NPM 未安装的文件,您可以使用以 ./
或 ../
:
为前缀的相对路径
import mylib from '../../js/mylib.js';
您可以导入从该 JS 文件导出的所有内容,甚至可以使用命名导入:
mylib.js
export function someFunction() {
// some logic
}
仪表板-widget.tsx
import { someFunction } from '../../js/mylib.js`;
const result = someFunction();
我有一个 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>
);
}
}
我一直收到错误消息
TypeScript Cannot find module 'src/js/mylib.js'.
我试过了:
import mylib from 'src/js/mylib.js';
import 'src/js/mylib.js';
import 'mylib' from 'src/js/mylib.js';
无济于事。
mylib.js
文件位于 js
文件夹中。
在线文档根本没有提到如何导入外部库。我已经成功导入 moment.js 但只是因为我首先通过以下方式安装了它:
npm install moment
然后通过执行以下操作将其导入到组件中:
import moment from 'moment';
我还尝试通过在 index.html
中引用它来 "import" 外部 JS 库<script src="assets/js/mylib.js"></script>
库在组件外部可用,但在组件内部不可用
既然你提到了 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'
}
]
}
]
};
然后,正如您已经尝试过的那样,您可以在 src/index.html
:
<script src="/lib/moment.min.js"></script>
但是,您的 Typescript 项目还不知道 moment
在全局范围内可用。不过这很容易解决,您只需在项目的某处添加一个声明文件,例如。 G。 src/global.d.ts
与内容
import _moment from 'moment';
declare global {
const moment: typeof _moment;
}
对于 运行 在 Node.js 上下文而非浏览器上下文中的测试文件,您可以导入时刻或将 moment
添加到全局范围,方法是创建一个内容为
jest-setup-file.js
)
global.moment = require('moment');
然后在 stencil.config.ts
中,您只需将 setupFilesAfterEnv
字段添加到 testing
:
{
testing: {
setupFilesAfterEnv: ['<rootDir>/jest-setup-file.js']
}
}
如果您不需要在整个应用程序中使用该脚本,而仅在加载特定组件时才需要,则仅从该组件中加载脚本更有意义。最简单的方法是创建一个 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);
}
}
你的 script/library 很可能也会为全局范围贡献一个变量,所以你必须再次让 Typescript 知道,你可以使用 declare
关键字来声明一个变量存在于全局上下文中(参见 https://www.typescriptlang.org/docs/handbook/declaration-files/by-example.html#global-variables)。
作为另一个例子,这是我写的一个帮助程序来加载 google 地图 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);
});
(google
类型来自 @types/googlemaps
包)
然后我可以像这样使用它
const maps = await importMapsApi();
const service = new maps.DistanceMatrixService();
要导入 NPM 未安装的文件,您可以使用以 ./
或 ../
:
import mylib from '../../js/mylib.js';
您可以导入从该 JS 文件导出的所有内容,甚至可以使用命名导入:
mylib.js
export function someFunction() {
// some logic
}
仪表板-widget.tsx
import { someFunction } from '../../js/mylib.js`;
const result = someFunction();