我写了一个样本typescriptReact组件库文件夹结构如下所示,出口只有一个按钮组件。运行npm run build时,只生成一个js bundle。这个包有react库代码(在webpack配置中还没有使用externals属性),但没有导出的按钮组件和按钮标签(在包文件中搜索关键字button,没有结果)。还添加了bundle/build文件的代码。
不知道我哪里做错了。
在导出简单的javascript函数时,webpack bundle不会发生此问题。导出的JavaScript函数在生成的包中找到,但不在导出的React组件中找到。
更新:
在output.library.name我的webpack配置中添加www.example.com =“template-react-component-library”和output.library.type =“umd”后,我能够在webpack捆绑文件中看到按钮标记(导出组件的),但从template-react-component-library导入相同的按钮组件时,我得到一个空对象。此外,通过从具有相同webpack配置的库中暴露正常的javascript函数,我从库中导入javascript函数而不是空对象,并且也没有添加任何输出.库配置。
所以现在的问题是如何解决从库中导入组件时的空对象,以及为什么output.library在公开JavaScript函数和react组件时表现不同。
存储库链接:https://github.com/dhiren-eng/template-react-component-library
文件夹结构:
webpack.config.js:
const path = require("path")
module.exports = {
entry: "./src/index.ts",
output: {
filename: "main.js",
path: path.resolve(__dirname, 'build'),
clean: true
},
module: {
rules: [
{ test: /\.(jsx|js|tsx|ts)$/, exclude: /node_modules/, use: {loader: "babel-loader", options: {presets: ["@babel/preset-env","@babel/preset-react"]}} },
{ test: /\.(tsx|ts)$/, exclude: /node_modules/, use: ["ts-loader"] }
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
}
}
tsconfig.json:
{
compilerOptions: {
"jsx": "react",
"module": "ESNext",
"declaration": true,
"declarationDir": "types",
"sourceMap": true,
"outDir": "build",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"esModuleInterop": true
}}
安装在包下面。package.json:
{
"name": "template-react-component-library",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config webpack.config.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/cli": "^7.21.5",
"@babel/core": "^7.21.8",
"@babel/preset-env": "^7.21.5",
"@babel/preset-react": "^7.18.6",
"@types/react": "^18.0.27",
"babel-loader": "^9.1.2",
"react": "^18.2.0",
"ts-loader": "^9.4.2",
"typescript": "^4.9.5",
"webpack": "^5.76.2",
"webpack-cli": "^5.0.1"
}
src/components/Button/Button.tsx:
import React from "react";
export interface ButtonProps {
label: string;
}
const Button = (props: ButtonProps) => {
return <button>{props.label}</button>;
};
export default Button;
src/components/Button/index.ts:
export {default} from './Button'
src/components/index.ts:
export {default as Button} from './Button'
src/index.ts:
export {Button} from './components'
生成构建文件,main.js:
/*! For license information please see main.js.LICENSE.txt */
(()=>{"use strict";var t={408:(t,e)=>{Symbol.for("react.element"),Symbol.for("react.portal"),Symbol.for("react.fragment"),Symbol.for("react.strict_mode"),Symbol.for("react.profiler"),Symbol.for("react.provider"),Symbol.for("react.context"),Symbol.for("react.forward_ref"),Symbol.for("react.suspense"),Symbol.for("react.memo"),Symbol.for("react.lazy"),Symbol.iterator;var o={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},r=Object.assign,a={};function n(t,e,r){this.props=t,this.context=e,this.refs=a,this.updater=r||o}function c(){}function p(t,e,r){this.props=t,this.context=e,this.refs=a,this.updater=r||o}n.prototype.isReactComponent={},n.prototype.setState=function(t,e){if("object"!=typeof t&&"function"!=typeof t&&null!=t)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,t,e,"setState")},n.prototype.forceUpdate=function(t){this.updater.enqueueForceUpdate(this,t,"forceUpdate")},c.prototype=n.prototype;var s=p.prototype=new c;s.constructor=p,r(s,n.prototype),s.isPureReactComponent=!0;Array.isArray,Object.prototype.hasOwnProperty},294:(t,e,o)=>{o(408)}},e={};!function o(r){var a=e[r];if(void 0!==a)return a.exports;var n=e[r]={exports:{}};return t[r](n,n.exports,o),n.exports}(294)})();
运行webpack build后,将生成2个文件夹:
- tsconfig.json的outDir属性中提到的类型文件夹
- webpack生成的Build文件夹
1条答案
按热度按时间bvhaajcl1#
好吧,我该从哪里开始呢?首先我要说的是,我非常赞成以ESM(Ecma Script Module或简称module)格式分发库,因为它是最有前途的现代模块格式。
这并不意味着你不能以其他格式分发你的库(例如:UMD),但我将在我的回答中关注ESM,因为它是在React应用程序中使用组件库的首选方式。
配置Webpack
现在我们知道我们希望Webpack生成ESM输出,让我告诉你Webpack's support for ESM is still experimental,这样可能会有问题。
让我们修改
webpack.config.js
来生成ESM:使用此配置,输出将包含类似
import * as React from 'react'
的内容,这就是我们想要的。现在有一个问题,在TS转译Webpack出于某种原因不尊重来自TS配置的
"allowSyntheticDefaultImports": true
,这应该使import React from 'react'
工作,即使'react'
不提供任何默认导出。但由于某种原因,输出仍然试图从react
中检索default
,而react
并不存在。这导致了问题这意味着
react
没有正确解析。我在这里没有找到任何理想的解决方案,但对我有效的是:
1.将
externalsType: "import",
添加到webpack.config.js
。这将动态导入react
,但这并不理想,因为它会向输出添加大约2kB的运行时间。1.在源文件中使用
import * as React from 'react'
。这两种方法都可以工作,并且可以将您的组件与
import { Button } from 'your-lib';
一起使用。但他们俩也都很烂。备选
这就引出了一个问题,如果没有更合适的库打包器的话。我建议Rollup,直接或通过Vite。与Webpack不同,它对ESM格式提供了出色的开箱即用支持。
添加
rollup.config.js
这个配置是你的Webpack配置的精确镜像,并且更容易配置和阅读。当然你必须安装所需的依赖项
另一个副作用是Rollup的输出,至少在您的情况下,要小得多,基本上相当于:
而Webpack的要大得多。
PS:如果你遇到问题,因为Rollup配置使用
import
和export
,只需添加"type": "module",
到你的package.json
,这将迫使你的项目文件使用ESM而不是CommonJS(及其module.exports
和require()
)。