如何通过 Webpack 的外部使用 CDN 导入 ReactJS Material UI?
How to import ReactJS Material UI using a CDN through Webpack's externals?
问题:
我正在尝试使用 React and Material UI 创建一个网站(网络应用程序),使用 npm 它工作得很好。但是当我尝试将它们制作成 externals
并通过 CDN 导入它们时,我收到 Material UI 错误(React 工作正常)。
我的代码:
我在 index.html 中这样链接 CDN:
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@material-ui/core/umd/material-ui.production.min.js"></script>
<script src="app.min.js"></script>
在app.min.js中,我是这样导入的:
import { Component } from 'react';
import ReactDOM from 'react-dom';
import { Button } from '@material-ui/core';
我的尝试:
在webpack.config.js中,我尝试了以下(同样,只有Material UI导致错误):
使用字符串:
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
'@material-ui/core': 'Button'
}
给出:
Uncaught ReferenceError: Button is not defined
使用对象:
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
'@material-ui/core': {
Button: '@material-ui/core'
}
}
给出:
TypeError: Cannot read property 'Button' of undefined
手动执行,所以 Material UI 不在外部:
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
然后从 app.min.js 中删除缩小的 Material UI 代码,这使得代码不完整并且不会 运行.
通过 GitHub 问题和 SO 问题进行了搜索,但没有任何运气,一些链接:
- How should material-ui be externalized when bundling with webpack
知道如何解决这个问题吗?
解法::
在webpack.config.js中:
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
'material-ui': 'window["material-ui"]'
},
然后在 app.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Button } from 'material-ui';
解释:
如果你查看 material ui js 的 cdn 版本,你会发现它将其内容导出到 material-ui
命名空间。
如果你像这样配置 webpack:
'material-ui': 'material-ui'
webpack 会将其编译为:
这导致代码在不存在的全局环境中寻找 material
和 ui
。所以我们必须明确指定window["material-ui"]
在尝试使用微前端架构构建应用程序时,在这个问题上花费了太多时间后,我刚刚解决了这个问题。
TL;DR;
解决方法是在webpack.config.js
中加入以下内容:
module.exports = {
// rest of the config clipped for brevity
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
'react-router-dom': 'ReactRouterDOM',
'@material-ui/core': 'MaterialUI'
}
};
更多详情:
我正在构建一个由多个微前端组成的宏前端。每个微前端都是在 React 中开发的,导出为 Web 组件,并且将独立部署,这样每个微前端都可以通过 URL 获得,例如:
http://foo.example.com/main.js
http://bar.example.com/main.js
http://baz.example.com/main.js
这些将使用 <script>
标签导入到宏应用程序中。
宏应用托管在单独的域中,例如 http://example.com
。
我面临的问题是 Material UI(可能还有 React)在每个微应用程序中被多次初始化。
为了避免这种情况,我不得不使用上面的 webpack 配置块将所有这些库外部化。
我不得不做出 2 个让步。
我没有使用 create-react-app
和 react-scripts
来构建宏应用程序,因为该设置会隐藏 webpack 配置。为了公开 webpack 配置,我可以弹出 CRA 项目,或者使用一些其他模块,例如 react-app-rewired
等。感觉工作量太大了。这样做的缺点是我无法使用 BrowserRouter
并且不得不接受使用 HashRouter
进行客户端路由。
我无法使用 @material-ui/icons
中基于 SvgIcon
的图标,因为我找不到外部化 Material UI 图标的好方法.相反,我放入了 link 到 Material UI 图标样式表,并选择使用 @material-ui/core/Icon
中的 Icon
来呈现图标。使用基于 SvgIcon
的图标也会导致 Material UI 在微应用程序中被初始化,这是我试图避免的。解决方法的一个好处是 Icon
也可以与 Font Awesome
一起使用,因此至少所有图标都将在代码中保持一致。
总的来说,我对最终结果很满意。
聚会可能有点晚了,但我会添加一个对我有用的答案。
第 1 步:
从 unpkg 添加脚本标签。这和 cdnjs 的区别在于 unkpg 有一个 umd 选项。在您的特定情况下可能是也可能不是问题。这是给我的。
url:
https://unpkg.com/@material-ui/core@4.11.0/umd/material-ui.production.min.js
脚本标签:
<script src="https://unpkg.com/@material-ui/core@4.11.0/umd/material-ui.production.min.js"></script>
步骤 1b:
添加字体和字体图标外部资源,如 material-ui 文档中所述:
material-ui getting started - installation guide
roboto 字体:
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
字体图标:
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
第 2 步:
从 window.MaterialUI 解构你想使用的元素或使用方括号符号(但这里不需要,因为这个包放弃了 '-' 字符。
const { Button } = window['MaterialUI'];
第 3 步:
按照您 'normally' 的方式使用元素
<Button variant="contained" color="primary">
Primary
</Button>
问题:
我正在尝试使用 React and Material UI 创建一个网站(网络应用程序),使用 npm 它工作得很好。但是当我尝试将它们制作成 externals
并通过 CDN 导入它们时,我收到 Material UI 错误(React 工作正常)。
我的代码:
我在 index.html 中这样链接 CDN:
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
<script src="https://unpkg.com/@material-ui/core/umd/material-ui.production.min.js"></script>
<script src="app.min.js"></script>
在app.min.js中,我是这样导入的:
import { Component } from 'react';
import ReactDOM from 'react-dom';
import { Button } from '@material-ui/core';
我的尝试:
在webpack.config.js中,我尝试了以下(同样,只有Material UI导致错误):
使用字符串:
externals: { 'react': 'React', 'react-dom': 'ReactDOM', '@material-ui/core': 'Button' }
给出:
Uncaught ReferenceError: Button is not defined
使用对象:
externals: { 'react': 'React', 'react-dom': 'ReactDOM', '@material-ui/core': { Button: '@material-ui/core' } }
给出:
TypeError: Cannot read property 'Button' of undefined
手动执行,所以 Material UI 不在外部:
externals: { 'react': 'React', 'react-dom': 'ReactDOM' }
然后从 app.min.js 中删除缩小的 Material UI 代码,这使得代码不完整并且不会 运行.
通过 GitHub 问题和 SO 问题进行了搜索,但没有任何运气,一些链接:
- How should material-ui be externalized when bundling with webpack
知道如何解决这个问题吗?
解法::
在webpack.config.js中:
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
'material-ui': 'window["material-ui"]'
},
然后在 app.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Button } from 'material-ui';
解释:
如果你查看 material ui js 的 cdn 版本,你会发现它将其内容导出到 material-ui
命名空间。
如果你像这样配置 webpack:
'material-ui': 'material-ui'
webpack 会将其编译为:
这导致代码在不存在的全局环境中寻找 material
和 ui
。所以我们必须明确指定window["material-ui"]
在尝试使用微前端架构构建应用程序时,在这个问题上花费了太多时间后,我刚刚解决了这个问题。
TL;DR;
解决方法是在webpack.config.js
中加入以下内容:
module.exports = {
// rest of the config clipped for brevity
externals: {
'react': 'React',
'react-dom': 'ReactDOM',
'react-router-dom': 'ReactRouterDOM',
'@material-ui/core': 'MaterialUI'
}
};
更多详情:
我正在构建一个由多个微前端组成的宏前端。每个微前端都是在 React 中开发的,导出为 Web 组件,并且将独立部署,这样每个微前端都可以通过 URL 获得,例如:
http://foo.example.com/main.js
http://bar.example.com/main.js
http://baz.example.com/main.js
这些将使用 <script>
标签导入到宏应用程序中。
宏应用托管在单独的域中,例如 http://example.com
。
我面临的问题是 Material UI(可能还有 React)在每个微应用程序中被多次初始化。
为了避免这种情况,我不得不使用上面的 webpack 配置块将所有这些库外部化。
我不得不做出 2 个让步。
我没有使用
create-react-app
和react-scripts
来构建宏应用程序,因为该设置会隐藏 webpack 配置。为了公开 webpack 配置,我可以弹出 CRA 项目,或者使用一些其他模块,例如react-app-rewired
等。感觉工作量太大了。这样做的缺点是我无法使用BrowserRouter
并且不得不接受使用HashRouter
进行客户端路由。我无法使用
@material-ui/icons
中基于SvgIcon
的图标,因为我找不到外部化 Material UI 图标的好方法.相反,我放入了 link 到 Material UI 图标样式表,并选择使用@material-ui/core/Icon
中的Icon
来呈现图标。使用基于SvgIcon
的图标也会导致 Material UI 在微应用程序中被初始化,这是我试图避免的。解决方法的一个好处是Icon
也可以与Font Awesome
一起使用,因此至少所有图标都将在代码中保持一致。
总的来说,我对最终结果很满意。
聚会可能有点晚了,但我会添加一个对我有用的答案。
第 1 步:
从 unpkg 添加脚本标签。这和 cdnjs 的区别在于 unkpg 有一个 umd 选项。在您的特定情况下可能是也可能不是问题。这是给我的。 url: https://unpkg.com/@material-ui/core@4.11.0/umd/material-ui.production.min.js
脚本标签:
<script src="https://unpkg.com/@material-ui/core@4.11.0/umd/material-ui.production.min.js"></script>
步骤 1b:
添加字体和字体图标外部资源,如 material-ui 文档中所述:
material-ui getting started - installation guide
roboto 字体:
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
字体图标:
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
第 2 步:
从 window.MaterialUI 解构你想使用的元素或使用方括号符号(但这里不需要,因为这个包放弃了 '-' 字符。
const { Button } = window['MaterialUI'];
第 3 步:
按照您 'normally' 的方式使用元素
<Button variant="contained" color="primary">
Primary
</Button>