使用 React 和 Webpack 编写可嵌入的 Javascript 插件

IT技术 javascript reactjs webpack
2021-04-14 10:47:07

我希望能够将我的 React 应用程序与 Webpack 捆绑在一起,以便可以使用与客户端相关的一堆配置来获取、调用和初始化放在 CDN 上的分布式副本。

阅读完这篇这篇之后,我正在按如下方式设置我的 webpack 入口文件:

// ... React requires etc.

(() => {
  this.MyApp = (config) => {
    // some constructor code here
  }

  MyApp.prototype.init = () => {
    ReactDOM.render(<MyReactApp config={MyApp.config} />, someSelector);
  }
})();

我的想法是在我的客户中,我可以执行以下操作:

<script src="./bundle.js" type="text/javascript"></script>
<script type="text/javascript">
  MyApp.init({
    some: "config"
  });
</script>

我的MyApp#init函数将在客户端的某个容器中呈现我的 React 应用程序。

我是否以正确的方式思考这个问题?有没有更简单或更有效的方法来解决这个问题?

我的错误是Uncaught TypeError: Cannot set property 'MyApp' of undefined,因为this在 IIFE 里面是undefined. 我真的很想了解为什么会发生这种情况以及如何解决它的建议。

提前致谢!

2个回答

所以我找到了一个解决方案,如here所述

如果我更改我的webpack.config.js文件以向output对象添加以下属性,即

var config = {
  // ...
  output: {
    // ...
    library: 'MyApp',
    libraryTarget: 'umd',
    umdNamedDefine: true,
  }
}

这将我与 webpack 捆绑在一起的文件指定为 UMD module,因此如果我在该文件中有一个函数并将其导出...

export const init = (config) => {
  ReactDOM.render(<MyReactApp config={config} />, someSelector);
}

然后,我可以在我的客户端中执行以下操作。

<script src="./bundle.js" type="text/javascript"></script>
<script type="text/javascript">
  MyApp.init({
    some: "config"
  }); 
</script>

我的 React 应用程序呈现。

如果有人认为这是一种愚蠢的做法,我很乐意听到!

更多关于 Webpack 配置的信息

请记住,我已经有一段时间没有碰过这段代码了。鉴于它是 Javascript,世界可能已经发生了变化,有些做法可能已经过时。

这是我的 React 入口点文件 ( src/index.js)

import 'babel-polyfill';
import React from 'react';
import { render } from 'react-dom';
import Root from './components/Root';
import configureStore from './lib/configureStore';

const store = configureStore();

export const init = (config) => {
  render(
    <Root store={store} config={config} />, 
    document.querySelector(config.selector || "")
  );
}

这是我的 Webpack 配置 ( webpack.config.js)

var webpack = require('webpack');
var path = require('path');
var loaders = require('./webpack.loaders');

module.exports = {
    entry: [
        'webpack-dev-server/client?http://0.0.0.0:8080', // WebpackDevServer host and port
        'webpack/hot/only-dev-server',
        './src/index.js' // Your appʼs entry point
    ],
    devtool: process.env.WEBPACK_DEVTOOL || 'source-map',
    output: {
        path: path.join(__dirname, 'public'),
        filename: 'bundle.js',
        library: 'Foo',
        libraryTarget: 'umd',
        umdNamedDefine: true,
    },
    resolve: {
        extensions: ['', '.js', '.jsx']
    },
    module: {
        loaders: loaders
    },
    devServer: {
        contentBase: "./public",
            noInfo: true, //  --no-info option
            hot: true,
            inline: true
        },
    plugins: [
        new webpack.NoErrorsPlugin()
    ]
};

如您所见,我的 Webpack 配置输出bundle.js了我的前端将摄取的内容。

@jasan 我们仍在使用这种方法,它不会给我们带来任何我们所知道的问题。
2021-05-22 10:47:07
@To_wave 我想作为init功能的一部分,您可以将包含客户端详细信息的请求 ping 回您的服务器 - 这可能有效吗?
2021-05-31 10:47:07
@ThomasValadez 它在bundle.js.
2021-06-04 10:47:07
@mattsch 这仍然是您使用的方法吗?你遇到过什么问题吗?或者它工作得很好
2021-06-16 10:47:07
嗨,马奇。这是一个很好的解决方案。我唯一的问题是如何检查小部件是否集成在客户的网站上。因此,假设有一个应用程序并构建一个小部件。我会检查一些用户(通过 ID 或主机名)在他们的网站上嵌入了一个小部件的应用程序。
2021-06-18 10:47:07

你的class周围有围栏。

MyApp 需要导出或附加到全局窗口对象,然后才能像这样调用它。

在您的情况下,您实际上不是在调用 MyApp.init(),而是在调用 window.MyApp.init(),但是全局对象 window 没有附加对象 MyApp。

// ... Simple attaching MyApp to the window (as a function)
window.MyApp = (config) => {
  ...
}

// ... Using class and export
class MyApp {
  constructor(config){...}
}
export default MyApp

// or simply attach to the window
window.MyApp = MyApp

我更喜欢使用导出创建类和导出module。然后创建另一个文件仅用于附加到窗口。因为像这样将类附加到窗口不被认为是最佳实践。

// Import module and attach it to the window
import MyApp from '.my-app'
window.MyApp = MyApp

您可以查找将module导出为 UMD、AMD...的高级选项。