使用外部插件响应应用程序



我正在构建一个使用 Parcel 或 Webpack 捆绑的 React 应用程序。
该应用程序应该能够嵌入由第三方开发并在其他地方托管的外部 React 组件
作为现代 JavaScript 模块:

// https://example.com/scripts/hello-plugin.js
import React from 'react';
export default class HelloPlugin extends React.Component {
render() {
return "Hello from external plugin!";
}
}

主机应用程序使用异步导入加载这些组件,如下所示:

// createAsyncComponent.tsx
import * as React from 'react';
import { asyncComponent } from 'react-async-component';
export default function createAsyncComponent(url: string) {
return asyncComponent({
resolve: () => import(url).then(component => component.default),
LoadingComponent: () => <div>Loading {url}....</div>,
ErrorComponent: ({ error }) => <div>Couldn't load {url}: {error.message}</div>,
})
}

但看起来捆绑器不允许将任意 url 作为外部 JavaScript 模块导入。

Webpack 发出构建警告:"依赖项的请求是一个表达式",导入不起作用。包裹不报告任何错误,但在运行时发生导入(url(时失败。

Webpack 作者建议使用 scriptjs 或 little-loader 来加载外部脚本。
有一个工作示例从任意 URL 加载 UMD 组件,如下所示:

public componentDidMount() {
// expose dependencies as globals
window["React"] = React;
window["PropTypes"] = PropTypes;
// async load of remote UMD component
$script(this.props.url, () => {
const target = window[this.props.name];
if (target) {
this.setState({
Component: target,
error: null,
})
} else {
this.setState({
Component: null,
error: `Cannot load component at ${this.props.url}`,
})
}
});
}

另外,我在一年前看到了一个类似的问题,其中建议的方法还涉及通过窗口对象传递变量。

但我想避免使用全局变量,因为大多数现代浏览器都支持开箱即用的模块。

我想知道这是否可能。也许,任何方法都可以指示捆绑器我的import(url(不是对主机应用程序的代码拆分块的请求,而是加载外部Javascript模块的请求。

在 Webpack 的上下文中,你可以做这样的事情:

import(/* webpackIgnore: true */'https://any.url/file.js')
.then((response) => {
response.main({ /* stuff from app plugins need... */ });
});

然后你的插件文件会有类似的东西...

const main = (args) => console.log('The plugin was started.');
export { main };
export default main;

请注意,您可以在插件初始化时(即在插件上调用main时(将内容从应用程序的运行时发送到插件,这样您就不会最终依赖于全局变量。

您可以免费获得缓存,因为 Webpack 会记住(缓存(给定的 URL 已经加载,因此后续导入该 URL 的调用将立即解决。

注意:这似乎适用于Chrome,Safari和Firefox,但不适用于Edge。我从不费心在IE或其他浏览器中进行测试。

我尝试在插件端使用 UMD 格式进行同样的加载,但这似乎不适用于 Webpack 加载内容的方式。事实上,有趣的是,声明为全局变量的变量最终不会出现在运行时的窗口对象中。您必须显式执行window.aGlobalValue = ...才能在全局范围内获取某些内容。

显然,您也可以在您的应用程序中使用 requirejs 或类似内容,然后让您的插件遵循该 API。

听 Webpack 作者。你还不能做你想用 Webpack 做的事情。

您将不得不遵循他建议的路线。

最新更新