reactjs - 使用外部插件 react 应用程序

标签 reactjs typescript webpack module parcel

我正在构建一个使用 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>,
    })
}

但看起来 bundler 不允许将任意 url 作为外部 javascript 模块导入。

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

Webpack作者推荐使用scriptjs或者little-loader加载外部脚本。
a working sample从任意 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}`,
            })
        }
    });
}

此外,我还看到一个 similar question一年前回答,其中建议的方法还涉及通过窗口对象传递变量。

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

我想知道这是否可能。也许,以任何方式指示 bundler 我的 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。

关于reactjs - 使用外部插件 react 应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50948995/

相关文章:

typescript - "export as namespace foo"的目的是什么?

typescript - 带有 typescript 的 Yarn 工作区 SyntaxError : Unexpected token {

angular - TypeScript - 检查类型

webpack - React 可加载 JSON 映射文件,其中包含从多个位置引用的组件

reactjs - 如何设置 webpack.config 以在 Reactjs 中使用 jsx-html-class

javascript - 在 Reactjs 中测试窗口按键事件

javascript - TypeScript:如何确保功能组件的两个 Prop (数组)具有相同的长度?

javascript - 根据变量改变 React 中组件的顺序?

android - React Native WebView 加载错误处理

typescript - 使用 typescript 时 html-webpack-plugin 上的错误太多