您现在的位置是:首页 >技术教程 >记一次react公共组件打包发布流程网站首页技术教程
记一次react公共组件打包发布流程
最近有个需求,需要我做一个公共组件并且发布在npm上,由于之前并没有这方面经验所以在过程中耗费了比较多的时间(大部分时间在疯狂百度),所以在完成需求后写一篇博文记录一下。
一、新建目录结构
1、新建src文件夹用于放源码
2、新建webpack.config.js文件,用于配置webpack
3、新建public文件夹,用于存放不随打包改变的相关文件,如html、ico图标等
4、新建.gitignore文件,用于配置在上传npm包时需要忽略的文件信息(也可以用.npmignore)
5、新建package.json文件,用于配置组件相关信息
构建完成后,如下图所示:
二、webpack.config.js配置
1、由于我打算用webpack进行打包,并且是react项目,所以需要下载webpack以及相关loader
npm i webpack webpack-cli react react-dom babel-loader @babel/core @babel/preset-env @babel/preset-react css-loader sass-loader style-loader
2、配置config.js文件信息
const path = require('path');
module.exports = function(){
return {
mode:"production",
entry: path.resolve(__dirname, 'src/index/index.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: `index.js`,
//意思是把我们的输出作为组件,没有这个在react项目中识别不到export导出的组件
library: {
name: 'lg-select-modal',
type: 'umd', // 以库的形式导出入口文件时,输出的类型,这里是通过umd的方式来暴露library,适用于使用方import的方式导入npm包
umdNamedDefine: true
}
},
module: {
rules: [
{
test: /.(css|scss)$/i,
use: ["style-loader", "css-loader", "sass-loader"],
},
{
test: /.(js|jsx)$/,
loader: 'babel-loader',
exclude: /(node_modules|dist|build|example)/
}
],
}
}
}
以上是最基本的配置信息,为了打包规范化、简洁化,我还在其基础上做了以下几方面优化:
(1)js和css文件打包分离
如果不进行分离,打包后的index.js文件将会包含css样式信息,这样导致代码混杂,并且单一文件很大。
这里我引入mini-css-extract-plugin插件:
npm i mini-css-extract-plugin
配置如下:
module: {
rules: [
{
test: /.(css|scss)$/i,
use: ["style-loader", MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
},
{
test: /.(js|jsx)$/,
loader: 'babel-loader',
exclude: /(node_modules|dist|build|example)/
}
],
},
plugins: [
new MiniCssExtractPlugin({
filename: `index.css`, //打包模块的css文件放置到这里
})
]
打包后效果如下:
(2)其他资源分离
引入copy-webpack-plugin插件,每次打包时自动将public文件夹内的文件放入打包后的dist文件夹中。
npm i copy-webpack-plugin
配置如下:
plugins: [
new MiniCssExtractPlugin({
filename: `index.css`, //打包模块的css文件放置到这里
}),
new CopyWebpackPlugin(['public'])
]
(3)每次打包前清空dist文件夹
由于每次打包后都会自动把打包后的文件放在dist文件夹内,为了避免上次打包后的文件造成影响,所以引入clean-webpack-plugin插件,打包前清空dist文件夹
配置如下:
plugins: [
new MiniCssExtractPlugin({
filename: `index.css`, //打包模块的css文件放置到这里
}),
new CopyWebpackPlugin(['public']),
new CleanWebpackPlugin()
]
(4)部分依赖不打包,通过require导入
externals字段配置如下:
const CopyWebpackPlugin = require('copy-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const path = require('path');
module.exports = function(){
return {
mode:"production",
entry: path.resolve(__dirname, 'src/index/index.js'),
output: {
path: path.resolve(__dirname, 'dist'),
filename: `index.js`, //打包模块js放置到 JS/xxx/xxx.js
//意思是把我们的输出作为组件,没有这个在react项目中识别不到export导出的组件
library: {
name: 'lg-select-modal',
type: 'umd', // 以库的形式导出入口文件时,输出的类型,这里是通过umd的方式来暴露library,适用于使用方import的方式导入npm包
umdNamedDefine: true
}
},
module: {
rules: [
{
test: /.(css|scss)$/i,
use: ["style-loader", MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
},
{
test: /.(js|jsx)$/,
loader: 'babel-loader',
exclude: /(node_modules|dist|build|example)/
}
],
},
plugins: [
new MiniCssExtractPlugin({
filename: `index.css`, //打包模块的css文件放置到这里
}),
new CopyWebpackPlugin(['public']),
new CleanWebpackPlugin()
],
//打包时不将以下依赖打包进index.js,需要引用组件的项目提前npm好
externals: {
'react': 'react',
'react-dom': 'react-dom'
}
}
}
因为我在开发是是用到了react和react-dom,如果将它们也一起打包,则会造成打包后文件较大,并且可能会在引入组件时报错(注:血与泪的经验之谈),如下:
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons
官网解释可能是因为存在多个react副本导致,所以需要配置externals属性,让你的组件不自带相关依赖,而通过require方式使用外部系统的依赖来执行。(这也要求引入我组件的项目提前下载好react和react-dom)
三、配置package.json
{
"name": "public-component",
"version": "1.0.0",
"description": "public component",
"main": "dist/index.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"build": "webpack"
},
"keywords": [
"component"
],
"author": "pengzhiwen",
"license": "ISC",
"peerDependencies": {
"react-dom": ">=16.13.1",
"react": ">=16.13.1"
},
"dependencies": {
"axios": "^1.4.0",
"jquery": "^3.6.4"
},
"exports": {
".": "./dist/index.js",
"./dist/index.css": "./dist/index.css"
},
"devDependencies": {
"@babel/core": "^7.19.6",
"@babel/preset-env": "^7.19.4",
"@babel/preset-react": "^7.18.6",
"antd": "^4.23.2",
"babel-core": "^6.26.3",
"babel-loader": "^8.0.5",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"clean-webpack-plugin": "^4.0.0",
"copy-webpack-plugin": "^5.1.2",
"css-loader": "^2.1.1",
"file-loader": "^6.2.0",
"globby": "^13.1.2",
"html-webpack-plugin": "^5.5.0",
"mini-css-extract-plugin": "^2.7.5",
"node-sass": "^4.12.0",
"react": "^17.0.1",
"react-custom-scrollbars": "^4.2.1",
"react-dom": "^17.0.1",
"react-hot-loader": "^4.13.1",
"react-redux": "^8.0.5",
"redux": "^4.2.1",
"redux-thunk": "^2.4.2",
"sass": "^1.55.0",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"url-loader": "^4.1.1",
"webpack": "^5.82.0",
"webpack-cli": "^4.10.0"
}
}
dependencies、peerDependencies、devDependencies根据项目实际进行配置,主要手动配置以下几个属性:
name: 包名称
version: 版本号,每次打包更新都需要调整
main: 包入口文件
exports: 包指定路径导出内容
更多详情可去官网查看,这里不再赘述。
四、配置gitignore文件
这里写好在npm上传包时需要忽略的文件信息
/node_modules
/config
/example
/public
/src
/index.d.ts
/srcTs
/.babelrc
/tsconfig.json
/package-lock.json
/webpack.config.js
/webpack.config-example.js
五、打包组件并发布
配置完成后接下来就是进行打包操作,在打包时注意,由于我是作为组件发布,所以在入口文件index.js中需要通过export把组件导出,而不是通过ReactDOM进行挂载:
import React from "react";
import App from "./component/app";
import { Provider } from "react-redux";
import store from "./store";
import "./index.scss";
//作为组件导出,npm publish
function RootApp(props){
return (
<React.StrictMode>
<Provider store={store}>
<App {...props} />
</Provider>
</React.StrictMode>
)
}
// ReactDOM.render(<RootApp />, document.getElementById("root"));
export default RootApp;
执行webpack命令,你就会在你的项目根目录下发现多了个dist文件夹,这就是打包后的项目
打包完成后,就发布到npm上,操作如下:
(1)当前项目根目录下打开cmd
(2)执行命令: npm login 后,输入npm账号密码
(3)执行命令: npm publish
(4)发布成功
常见问题:
1、引入组件时提示
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons
解决方法:可查看上面webpack.config.js中的配置,externals字段
2、在项目引入组件后,打包项目部署在服务器上报错
Uncaught TypeError: Cannot call a class as a function
解决方法:webpack.config.js中的mode属性改成development,如下:
3、引入组件后启动项目报错
Package path . is not exported from package
解决方法:package.json里配置exports属性,添加".": "./dist/index.js",详情可参考上面示例。
别问我为什么知道解决,那也曾是我逝去的头发....
前人栽树后人乘凉,希望这篇文章能够帮助到你,end.