您现在的位置是:首页 >技术教程 >记一次react公共组件打包发布流程网站首页技术教程

记一次react公共组件打包发布流程

不吃西红柿w 2024-10-11 00:01:02
简介记一次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.

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。