【React npm】从零搭建react脚手架,发布组件库到npm,并实现按需加载(二)

news2025/1/24 2:29:46

发布react组件库

  • 前情回顾
  • 介绍
  • 搭建脚手架
  • 配置babelrc
  • 配置jsconfig
  • 写入组件demo
  • 修改主入口文件
  • 配置生产环境webpack
  • 配置package.json
  • 发布
  • 实现按需加载


前情回顾

前面写过一篇,发布单个组件到npm的:

https://blog.csdn.net/tuzi007a/article/details/129116273

实现了搭建react脚手架,并发布一个组件到npm。

情形较为简单。


介绍

本次实现发布一个包含多个组件的组件库,并实现组件和样式的按需加载。

实现目标:

  • 发布多个组件
  • 每个组件的内容和样式,单独存放在一个文件夹
  • 每个组件单独打包成一个文件夹,同样包含内容和样式
  • 用户可以通过import { xxx } from 'ui库'的形式引入组件
  • 用户可以全局引入样式,也可以不引入样式,实现样式按需加载

npm包demo地址:

https://www.npmjs.com/package/pub-multily-react-test03

项目gitee地址:

https://gitee.com/guozia007/pub-multily-react-test03

搭建脚手架

gitee上创建示例项目pub-multily-react-test03,并clone到本地,

在根目录下生成package.json文件:

npm init -y

在根目录下创建.gitignore文件

node_modules
dist
lib

开始安装一堆包:

// react18+相关
npm i react react-dom -D

// webpack5+相关
npm i webpack webpack-cli webpack-dev-server -D

// babel相关
npm i @babel/core babel-loader @babel/preset-env @babel/preset-react -D

// 基础脚手架需要的其他loader和plugin
npm i style-loader css-loader -D
npm i html-webpack-plugin -D
npm i mini-css-extract-plugin -D
npm i css-minimizer-webpack-plugin -D

在根目录下创建webpack配置文件,

开发环境:webpack.dev.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: "development",
  entry: './src/index.js',
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, 'public/index.html')
    })
  ],
  devtool: 'cheap-module-source-map',
  devServer: {
    port: 3001,
    open: true
  },
  resolve: {
    extensions: ['.js', '.jsx', '.json']
  }
}

开发环境的脚手架配置基本完成。


配置babelrc

在根目录下创建.babelrc.js,如下配置:

module.exports = {
  presets: [
    "@babel/preset-env", // js环境预设
    "@babel/preset-react" // 解析react相关语法的预设,比如jsx className等
  ]
}

配置jsconfig

在根目录下创建jsconfig.json

两篇和配置相关的文档:

https://zhuanlan.zhihu.com/p/55644953

https://juejin.cn/post/7004748084374831117

配置内容:

{
  "compilerOptions": {
    "outDir": "./lib/",  // 这里是实现import { xxx } from 'xxx'的关键
    "module": "ESNext", 
    "target": "ES5",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "jsx": "react",
    "allowJs": true,
    "allowSyntheticDefaultImports":true
  },
  "exclude": ["node_modules", "lib"]
}

写入组件demo

在根目录下创建src内容树结构如下:

+ src
+   index.js
+   MyButton
+ 	  index.jsx
+ 	  index.css
+   ShowImg
+ 	  index.jsx
+ 	  index.css

然后可看到项目结构如下:
在这里插入图片描述

内容如下:

// src/index.js

import React from "react";
import { createRoot } from 'react-dom/client';
import ShowImg from "./showImg";
import MyButton from "./button";

const root = createRoot(document.getElementById("root"));
root.render(
  // 要测试哪个组件,就在这里写哪个组件
  <MyButton bgc="pink" label="click me !" />
)
// src/MyButton/index.jsx

import React from "react";
import './index.css';

const MyButton = ({ label, bgc }) => {
  return (
    <div className="my-btn"
      style={{ backgroundColor: bgc || '#1EA7FD' }}
    >{ label || 'Button' }</div>
  )
}

export default MyButton;
/* src/MyButton/index.css */

.my-btn {
  min-width: 80px;
  display: inline-block;
  box-sizing: border-box;
  padding: 12px;
  border-radius: 4px;
  color: #fff;
  font-weight: 600;
  letter-spacing: 2px;
  user-select: none;
  cursor: pointer;
}
// src/ShowImg/index.jsx

import React from "react";
import './index.css';

const ShowImg = ({ url }) => {
  return (
    <div className="show-img">
      <p className="show-img-tip">以下是要展示的图片:</p>
      <img src={url || ''} alt="" />
    </div>
  )
}

export default ShowImg;
/* src/ShowImg/index.css */

.show-img {
  width: 200px;
}

.show-img .show-img-tip {
  margin: 20px auto;
}

.show-img img {
  max-width: 200px;
  max-height: 200px;
}

保存文件。

然后在package.json中添加开发环境的指令:

"scripts": {
  // ...
  "start": "webpack serve --config webpack.dev.js"
}

执行npm start,可以在页面中看到测试效果。

这块比较简单,不具体说了,重点放在生产环境的处理。


修改主入口文件

主入口文件是src/index.js

在生产模式中,需要用它把开发的组件都批量导出,方便用户引入使用,如下:

// 批量导出

export { default as MyButton } from './MyButton';
export { default as ShowImg } from './ShowImg';

配置生产环境webpack

要实现组件分开打包,就要使用多入口打包,这里需要3个入口:

  • index: ‘./src/index.js’
  • ‘MyButton/index’: ‘./src/MyButton/index.js’,
  • ‘ShowImg/index’: ‘./src/ShowImg/index.js’,

如果还有其他组件,也是这种格式的入口,

所以这里的关键就是怎么动态的获取入口。

glob就是用来做这个事情的。

它会生成一个数组,用来存储入口地址。

fileNames:  [
  './src/index.js',
  './src/MyButton/index.jsx',
  './src/ShowImg/index.jsx'
]

我们通过正则匹配的方式,用地址动态生成入口名字即可。

安装glob

npm i glob -D

使用glob

const glob = require('glob');

/**
 * glob匹配规则
 * https://blog.csdn.net/feiying0canglang/article/details/125043362
 */
// 创建入口对象
const entries = {};
// 通过glob获取到入口地址数组
const fileNames = glob.sync('./src/**/*.js?(x)');
console.log('fileNames: ', fileNames);
// 遍历入口地址,去掉前后内容,留下中间部分,作为入口名称
fileNames.forEach(file => {
  const filePath = file.replace(/^\.\/src\/(.+)\.jsx?$/, '$1');
  entries[filePath] = file;
})
console.log('entries: ', entries);
//entries:  {
//  index: './src/index.js',
//  'MyButton/index': './src/MyButton/index.jsx',
//  'ShowImg/index': './src/ShowImg/index.jsx'   
//}

剩下内容就可以按部就班的配置了。

在根目录下创建webpack.prod.js

const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const glob = require('glob');

/**
 * glob匹配规则
 * https://blog.csdn.net/feiying0canglang/article/details/125043362
 */
const entries = {};
const fileNames = glob.sync('./src/**/*.js?(x)');
// console.log('fileNames: ', fileNames);
fileNames.forEach(file => {
  const filePath = file.replace(/^\.\/src\/(.+)\.jsx?$/, '$1');
  entries[filePath] = file;
})
// console.log('entries: ', entries);

module.exports = {
  mode: 'production',
  entry: entries,
  output: {
    path: path.resolve(__dirname, 'lib'),
    filename: '[name].js',
    library: {
      name: 'pub-multily-react-test03',
      type: 'umd'
    },
    clean: true
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,
          'css-loader'
        ]
      },
      {
        test: /\.jsx?$/,
        exclude: /node_modules/,
        loader: 'babel-loader'
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      // 如果想自定义生成的css文件的filename,可以这样配置
      // filename: (filePath) => {
      //   // console.log('filePath: ', filePath);
      //   return `${filePath.chunk.name.replace('/', '/style/')}.css`;
      // }
      filename: '[name].css'
    })
  ],
  optimization: {
    // 代码分隔
    splitChunks: {
      chunks: 'all',
      name: 'chunk'
    },
    minimizer: [
      new CssMinimizerPlugin() // 压缩css代码
    ]
  },
  resolve: {
    // 支持.js .jsx .json自动补全,不要忘了.
    extensions: ['.js', '.jsx', '.json']
  },
  // 外部扩展,不需要安装的依赖
  externals: {
    react: {
      root: 'React',
      commonjs2: 'react',
      commonjs: 'react',
      amd: 'react',
    },
    'react-dom': {
      root: 'ReactDOM',
      commonjs2: 'react-dom',
      commonjs: 'react-dom',
      amd: 'react-dom',
    }
  }
}

package.json中添加打包指令:

"script": {
  "build": "webpack --config webpack.prod.js"
}

执行命令npm run build可以看到打包结果:
在这里插入图片描述


配置package.json

{
  "name": "pub-multily-react-test03",
  "version": "0.0.7", // 版本号,发布前需要修改
  "description": "发布react多组件库",
  "main": "lib/index.js", // 库的入口文件
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack serve --config webpack.dev.js", // 开发环境启动项目
    "build": "webpack --config webpack.prod.js", // 生产环境打包项目
    "pub": "npm run build && npm publish" // 打包并发布项目到npm
  },
  "repository": { // 项目地址
    "type": "git",
    "url": "https://gitee.com/guozia007/pub-multily-react-test03.git"
  },
  "keywords": [ // 关键字,告诉用户这个库是干嘛的,也利于用户搜索
    "react",
    "react component",
    "ui",
    "framework",
    "component"
  ],
  "author": "guozi007a", // 作者
  "license": "MIT", // 证书格式
  "publishConfig": { // 发布到npm时,使用的npm源。配置之后,就不需要在发布时修改自己用的npm镜像了
    "registry": "https://registry.npmjs.org/" // npm原始镜像
  },
  "browserslist": [ // 支持的浏览器,做兼容用的
    ">= 0.25%",
    "last 1 version",
    "not dead"
  ],
  "files": [ // 要把哪些文件或目录发到npm
    "lib"
  ],
  "peerDependencies": { // 用户要安装的依赖,如果没有,会给用户警告
    "react": ">= 16.9.0", // react版本不低于16.9.0
    "react-dom": ">= 16.9.0" // react-dom版本不低于16.9.0
  },
  "devDependencies": { // 开发环境依赖,开发组件,一般都是安装到开发环境
    "@babel/core": "^7.21.0",
    "@babel/preset-env": "^7.20.2",
    "@babel/preset-react": "^7.18.6",
    "babel-loader": "^9.1.2",
    "css-loader": "^6.7.3",
    "css-minimizer-webpack-plugin": "^4.2.2",
    "glob": "^8.1.0",
    "html-webpack-plugin": "^5.5.0",
    "mini-css-extract-plugin": "^2.7.2",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "style-loader": "^3.3.1",
    "webpack": "^5.75.0",
    "webpack-cli": "^5.0.1",
    "webpack-dev-server": "^4.11.1"
  }
}


发布

不重复说明,直接npm run pub发布即可。


实现按需加载

打包后,可以看到在lib下多了一个index.css,这个是全局样式,

所有组件的样式都在这里面。

在这里插入图片描述
用户在使用组件的时候,就有两种方式,

第一种是使用全局样式:

import { MyButton } from 'pub-multily-react-test03';
import 'pub-multily-react-test03/lib/index.css';

第二种是使用哪个组件,就单独引入哪个样式:

import { MyButton } from 'pub-multily-react-test03';
import 'pub-multily-react-test03/lib/MyButton/index.css';

两种方式均可,但是对用户并不友好。

我们需要实现样式的按需加载,即用户不需要再引入样式,而是

根据用户使用的组件,来自动实现样式的引入。

这就需要用户(组件库的使用者)去安装和配置插件babel-plugin-import

文档:

https://github.com/umijs/babel-plugin-import

安装:

npm i babel-plugin-import

如果用户的项目的package.json中有babel配置项,请先把配置项移植到babel的配置文件中。

项目根目录下创建babel配置文件.babelrc.js,做如下配置:

module.exports =  {
  "presets": [
    // ... 用户原有的preset
  ],
  "plugins": [
    // ... 用户原有的plugin

    // 如下是babel-plugin-import插件的配置
    [
      "import",
      {
        // 要实现按需加载的库名
        "libraryName": "pub-multily-react-test03",
        // 库的目录,默认是lib可自行更改
        "libraryDirectory": "lib",
        // 是否要把组件的目录名改成小写形式,即my-button,默认为true
        "camel2DashComponentName": false,
        // "style"是单个样式所在的相对路径,按需加载样式时会按照"style"的路径去找css样式文件
        // name是组件的目录名,如MyButton
        // "style": true,意思是路径为MyButton/style
        // "style": "css",意思是路径为MyButton/style/css
        // 还可以自定义如下,意思是要加载的样式文件是MyButton/index.css
        "style": (name) => `${name}/index.css`
      },
      // 如果你的@babel版本低于7,这句配置不用写
      "pub-multily-react-test03"
    ]
  ]
}

项目还有很多可以优化的地方,后续继续优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/363653.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Anaconda环境配置

1.进入清华大学镜像网站Index of /anaconda/archive/ | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror&#xff0c;下载稳定版Anaconda3-5.2.0&#xff0c;如下图。2.放到整理好的文件夹中&#xff0c;双击安装包进行安装。3.安装过程中需要改变的默认值如下&#xff…

Linux 基础知识之文件系统

目录一、文件系统1.文件种类2.Linux和Windows文件后缀的不同3.查看文件类型3.绝对路径与相对路径二、系统分区三、目录结构一、文件系统 1.文件种类 Linux中一切皆文件。目光所及&#xff0c;皆是文件。文件的种类共有七种&#xff0c;每种文件都有自己的独特标识&#xff1a;…

MYSQL 密码修改 (四种方式)

注 &#xff1a; 我们所谓的密码修改肯定是先指的是 你已经清楚用户的原密码&#xff0c;是对原密码进行了修改并不是你忘记了密码&#xff0c;然后设置新密码~&#xff01;&#xff01;方式一 &#xff1a; 使用 mysqladmin示例 &#xff1a; [rootbogon ~]# mysqladmin -uroo…

python文件编译为pyc后运行

一、pyc文件我们开发一个python脚本&#xff0c;文件的后缀为.py。如果运行这个py文件&#xff0c;Python内部会先将源码文件&#xff08;.py文件&#xff09;编译成字节码&#xff08;byte code&#xff09;文件&#xff08;.pyc文件&#xff09;。接着运行编译后的字节码&…

【Spark分布式内存计算框架——离线综合实战】5. 业务报表分析

第三章 业务报表分析 一般的系统需要使用报表来展示公司的运营情况、 数据情况等&#xff0c;本章节对数据进行一些常见报表的开发&#xff0c;广告数据业务报表数据流向图如下所示&#xff1a; 具体报表的需求如下&#xff1a; 相关报表开发说明如下&#xff1a; 第一、数据…

【总结】python3启动web服务引发的一系列问题

背景 在某行的实施项目&#xff0c;需要使用python3环境运行某些py脚本。 由于行内交付的机器已自带python3 &#xff0c;没有采取自行安装python3&#xff0c;但是运行python脚本时报没有tornado module。 错误信息 ModuleNotFoundError&#xff1a;No module named ‘torn…

Unity截屏时将背景的透明度设为0

常用的截屏函数是&#xff1a; UnityEngine.ScreenCapture.CaptureScreenshot(fileName, 5); //5代表dpi大小&#xff0c;数字越大越清晰但是这样保存图片是不能将黑色背景的透明度设为0&#xff0c;最终还是24bit图。 如果将背景透明度设为0而渲染物体透明度设为255&#xff…

学插画的线上机构排名

学插画哪个线上机构好&#xff0c;5个靠谱的插画网课推荐&#xff01;给大家梳理了国内5家专业的插画师培训班&#xff0c;最新5大插画班排行榜&#xff0c;各有优势和特色&#xff01; 一&#xff1a;插画线上培训机构排名 1、轻微课&#xff08;五颗星&#xff09; 主打课程有…

【C语言】函数栈帧的创建与销毁

Yan-英杰的主页 悟已往之不谏 知来者之可追 目录 ​0.ebp和esp是如何来维护栈帧的呢&#xff1f; 1.为什么局部变量的值不初始化是随机的&#xff1f; ​2.局部变量是怎么创建的&#xff1f; ​3 .函数是如何传参的&#xff1f;传参的顺序是怎样的 4.函数是如何调用的 ​…

scrapy-redis分布式爬虫学习记录

目录 1. scrapy-redis是什么&#xff1f; 2. scrapy-redis工作原理 3.分布式架构 4. scrapy-redis的源码分析 5. 部署scrapy-redis 6. scrapy-redis的基本使用 6.1 redis数据库基本表项 6.2 在scrapy项目的基础进行更改 7. redis数据转存入mysql数据库 课程推荐&#…

大学生成人插画培训机构盘点

成人插画培训机构哪个好&#xff0c;成人学插画如何选培训班&#xff1f;给大家梳理了国内较好的插画培训机构排名&#xff0c;各有优势和特色&#xff0c;供大家参考&#xff01; 一&#xff1a;国内成人插画培训机构排名 1、轻微课&#xff08;五颗星&#xff09; 主打课程有…

Head First设计模式---3.装饰者模式

3.1装饰者模式 亦称&#xff1a; 装饰者模式、装饰器模式、Wrapper、Decorator 装饰模式是一种结构型设计模式&#xff0c; 允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。 举个例子&#xff1a;天气很冷&#xff0c;我们一件一件穿衣服&#xff0c…

学习Flask之五、数据库

学习Flask之五、数据库 数据库有组织的存贮应用数据。根据需要应用发布查询追踪特定部分。网络应用最常用的数据库是基于关系模式的&#xff0c;也称为SQL数据库&#xff0c;引用结构化查询语句。但是近年来&#xff0c;面向文档和键值的数据库&#xff0c;非正式的统称为NoSQ…

乐友商城学习笔记(一)

SpringCloud 什么是SpringCloud 在SpringBoot基础上构建的微服务框架固定步骤 1.引入组件的启动器2.覆盖默认配置3.在引导类上添加相应的注解 eureka 注册中心&#xff0c;服务的注册与发现服务端 1.引入服务器启动器&#xff1a;eureka-server2.添加了配置 spring.applicati…

【Git】使用Git上传项目到远程仓库Gitee码云步骤详解

电脑里存放了很多项目&#xff0c;有的备份&#xff0c;有的没备份&#xff0c;如果不仔细分类管理的话&#xff0c;时间一长&#xff0c;到时看到那就会觉得非常杂乱&#xff0c;很难整理&#xff0c;这里有一个叫源代码托管&#xff0c;用过它的都知道&#xff0c;方便管理和…

如何下载阅读Spring源码-全过程详解

这篇文章记录了下载spring源码和在IDEA中打开运行的全过程&#xff0c;并且记录了过程中遇到的问题和解决方案&#xff0c;适合需要学习spring源码的同学阅读。 1.spring源码下载地址 通过Git下载spring-framework项目源码&#xff1a; git clone https://github.com/spring…

Document-Level event Extraction via human-like reading process 论文解读

Document-Level event Extraction via human-like reading process 论文&#xff1a;2202.03092v1.pdf (arxiv.org) 代码&#xff1a;无 期刊/会议&#xff1a;ICASSP 2022 摘要 文档级事件抽取(DEE)特别困难&#xff0c;因为它提出了两个挑战:论元分散和多事件。第一个挑战…

TPM 2.0实例探索2 —— LUKS磁盘加密(1)

本文大部分内容取自&#xff1a; LUKS磁盘格式_小写的毛毛的博客-CSDN博客_luks 如何破解LUKS加密 一、LUKS介绍 1. 什么是LUKS LUKS是“Linux Unified Key Setup”的简写&#xff0c;是 Linux 硬盘加密的标准。LUKS通过提供标准的磁盘格式&#xff0c;不仅可以促进发行版之…

短链或H5唤醒(跳转)APP应用

唤醒APP(两种方法) 一.短链唤醒(跳转)app ⭐ 短链跳转到APP&#xff0c;当如果用户手机不存在APP(某个应用)将会进入到官网页面。 app links实现 在android studio菜单栏Tools->App Links Ass点击,效果图如下 2.配置如下 点击ok,生成如下效果图 3.完成第二步后,会自动…

深度解读 | 数据资产管理面临诸多挑战,做好这5个措施是关键

日前&#xff0c;大数据技术标准推进委员会&#xff08;中国通信标准化协会下&#xff08;CCSA&#xff09;的专业技术委员会&#xff0c;简称TC601&#xff09;发布《数据资产管理实践白皮书》&#xff08;6.0 版&#xff09;&#xff08;以下简称&#xff1a;报告&#xff09…