使用外部插件对应用程序进行反应 [英] React application with external plugins

查看:95
本文介绍了使用外部插件对应用程序进行反应的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm building a React application bundled using Parcel or Webpack.
The application should be able to embed external React components
developed by third-parties and hosted elsewhere as modern javascript modules:

// https://example.com/scripts/hello-plugin.js
import React from 'react';

export default class HelloPlugin extends React.Component {
    render() {
        return "Hello from external plugin!";
    }
}

主机应用程序使用这样的异步导入来加载这些组件,例如:

Host application loads these components using asynchronous import like this, for example:

// 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模块.

But looks like bundlers don't allow importing arbitrary urls as external javascript modules.

Webpack发出构建警告:依赖项的请求是表达式",并且导入无效.包裹不会报告任何错误,但是在运行时发生import(url)时会失败.

Webpack emits build warnings: "the request of a dependency is an expression" and the import doesn't work. Parcel doesn't report any errors, but fails when import(url) occurs at runtime.

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

Webpack author recommends using scriptjs or little-loader for loading external scripts.
There is a working sample that loads an UMD component from arbitrary URL like this:

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}`,
            })
        }
    });
}

此外,我看到一个类似的问题,一年前回答了,其中建议的方法也涉及通过窗口对象传递变量.

Also, I saw a similar question answered a year ago where the suggested approach also involves passing variables via a window object.

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

But I'd like to avoid using globals given that most modern browsers support modules out of the box.

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

I'm wondering if it's possible. Perhaps, any way to instruct the bundler that my import(url) is not a request for the code-split chunk of a host application, but a request for loading an external Javascript module.

推荐答案

在Webpack的上下文中,您可以执行以下操作:

In the context of Webpack, you could do something like this:

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

然后您的 plugin 文件将具有类似...

Then your plugin file would have something like...

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

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

Notice you can send stuff from your app's runtime to the plugin at the initialization (i.e. when invoking main at the plugin) of the plugins so you don't end up depending on global variables.

由于Webpack记住(缓存)给定的URL已经加载,因此您可以免费获得缓存,因此随后导入该URL的调用将立即解决.

You get caching for free as Webpack remembers (caches) that the given URL has already loaded so subsequent calls to import that URL will resolve immediately.

注意:这似乎可以在Chrome,Safari和浏览器中使用. Firefox,但不是Edge.我从不打扰在IE或其他浏览器中进行测试.

Note: this seems to work in Chrome, Safari & firefox but not Edge. I never bothered testing in IE or other browsers.

我已经尝试在插件端使用UMD格式进行相同类型的加载,但这似乎不适用于Webpack加载内容的方式.实际上,有趣的是,声明为全局变量的变量不会最终出现在运行时的window对象中.您必须明确地执行window.aGlobalValue = ...才能在全局范围内获得某些东西.

I've tried doing this same sort of load with UMD format on the plugin side and that doesn't seem to work with the way Webpack loads stuff. In fact it's interesting that variables declared as globals, don't end up in the window object of your runtime. You'd have to explicitly do window.aGlobalValue = ... to get something on the global scope.

很显然,您还可以在应用程序中使用requirejs或类似的东西,然后让您的插件遵循该API.

Obviously you could also use requirejs - or similar - in your app and then just have your plugins follow that API.

这篇关于使用外部插件对应用程序进行反应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆