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

news/2024/4/25 17:23:05/文章来源:https://blog.csdn.net/tuzi007a/article/details/129164805

发布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/55644953https://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.jsimport 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.jsximport 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.jsximport 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.luyixian.cn/news_show_72797.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【HTML】HTML 表单 ⑤ ( form 表单域 )

文章目录一、form 表单域1、form 表单域作用2、form 表单域语法3、form 表单域 Get 请求4、form 表单域 Post 请求一、form 表单域 1、form 表单域作用 从 input 表单 , textarea 文本域 , select 下拉菜单 中收集了用户信息 , 需要通过 form 表单域 发送给 服务器端 ; 2、fo…

第十一章 - 模糊匹配(like)、正则匹配(REGEXP)、文本处理函数、时间处理函数

第十一章 - 模糊匹配&#xff08;like&#xff09;、正则匹配&#xff08;REGEXP&#xff09;、文本处理函数、时间处理函数模糊匹配和正则匹配like%通配符_通配符REGEXP 正则匹配文本拼接concat&#xff08;&#xff09;substring()substring_index()一些文本处理函数时间处理…

【计算机组成原理】求寻址范围(按字/按字节)

今天一道计算机组成原理的复习题一直没搞懂怎么做的&#xff0c;网上讲解求寻址范围题型的资料和视频也很少&#xff0c;花了半个晚上才搞懂。 首先&#xff0c;理解存储体&#xff0c;存储单元&#xff0c;存储字&#xff0c;存储元。&#xff08;文章末尾详细解释了) 以下是…

2022年休闲游戏市场总结

在预测 2023 年之前&#xff0c;我们先回顾一下 2022 年。从上一年发生的事件中往往能看到未来趋势的影子&#xff0c;所以 2022 年的总结至关重要。一、2022年总结回顾1、流行游戏类型回顾 2022 年&#xff0c;三种超休闲游戏表现最为突出&#xff1a;跑酷游戏&#xff1a;跑酷…

你知道IT运维的本质是什么吗?

大家好&#xff0c;我是技福的小咖老师。 之前看到个文章&#xff0c;说运维的本质是“可视化”&#xff0c;甚至还有人说是DevOps。不可否认&#xff0c;“可视化”是运维过程中非常重要的一个环节&#xff1b;DevOps则是开发运维一体化非常重要的工具。 究其根本&#xff0…

leetcode重点题目分类别记录(一)数据结构类

算法题分类别记录数组排序归并排序合并两有序数组归并排序快速排序荷兰旗问题快速排序堆排序基数排序滑动窗口/双指针N数之和四数相加链表环形链表重排链表LRU缓存栈与队列栈实现队列/队列实现栈最小栈/最小队列单调队列单调栈哈希表字符串字符串处理字符串匹配KMP子串二叉树二…

Dart的安装及环境变量配置

本文介绍dart的安装步骤及环境变量配置&#xff0c;以及如何在vscode中进行开发环境配置。一、dart的安装访问dart官网https://dart.cn/&#xff0c;点击网站右上角的获取DART SDK进行下载页面。如下图&#xff0c;选择下载SDK的zip压缩文件。根据自己的操作系统情况选择合适版…

吉卜力风格水彩画怎么画?

著名的水彩艺术家陈坚曾说&#xff1a;“水彩是用水润调和形成的饱和度极高的艺术画面&#xff0c;在纸上晕染的画面面积、强度等具有许多随意性&#xff0c;天空的颜色乌云密布&#xff0c;都是很随意的&#xff0c;难以模仿。” 是的&#xff0c;水彩画的妙处就在于不确定的…

apk中代码执行adb指令实现

背景&#xff1a;想要在android apk中直接使用adb指令&#xff0c;从而不需要把手机通过数据线方式连接到电脑&#xff0c;在电脑端执行adb指令。 一、权限相关 想要在apk代码中执行adb命令&#xff0c;涉及到执行权限。 首先手机需要有root权限。其次就算手机已经root了&…

(18)目标检测算法之数据集标签格式转换:json2txt、xml2txt

目标检测算法之数据集标签格式转换&#xff1a;json2txt、xml2txt 目标检测最常见的模型&#xff1a;YOLO&#xff0c;常见的几种标注方式&#xff1a;矩形框、旋转矩形框、实例分割中的多边形标注等类型&#xff0c;根据其标注标签&#xff0c;目标检测主要有以下两种转换方式…

Word中批量调整图片大小

当一个文档中图片较多&#xff0c;又需要调整图片大小时&#xff0c;这时可以通过“宏”执行代码来批量调整。打开一个Word文档。“AltF8"键打开宏。设置“宏名”&#xff0c;并单击“创建”。创建完宏后&#xff0c;将进入Visual Basic 编辑器界面。在代码编辑区全选&…

【面试题】TCP如何保证传输可靠性?TCP流量控制实现、拥塞控制、ARQ协议、停止等待ARQ、连续ARQ

文章目录1. TCP 如何保证传输的可靠性&#xff1f;2.TCP 如何实现流量控制&#xff1f;3.TCP 的拥塞控制是怎么实现的&#xff1f;3.ARQ 协议了解吗?4.停止等待 ARQ 协议5.连续 ARQ 协议1. TCP 如何保证传输的可靠性&#xff1f; 基于数据块传输 &#xff1a;应用数据被分割成…

前端编译、JIT编译、AOT编译

一、前端编译&#xff1a;java设计之初就是强调跨平台&#xff0c;通过javac将源文件编译成于平台无关的class文件&#xff0c; 它定义了执行 Java 程序所需的所有信息&#xff08;许多Java"语法糖"&#xff0c;是在这个阶段完成的&#xff0c;不依赖虚拟机&#xff…

01-MySQL基础-简介安装navicat使用SQL(DDL、DML、(DCL)、DML)

文章目录MySQL基础1&#xff0c;数据库相关概念1.1 数据库1.2 数据库管理系统1.3 常见的数据库管理系统1.4 SQL2&#xff0c;MySQL2.1~2.4 mysql安装2.5 MySQL数据模型3&#xff0c;SQL概述3.1 SQL简介3.2 通用语法3.3 SQL分类4&#xff0c;DDL:操作数据库4.1 查询4.2 创建数据…

Java笔记026-集合/数组、Collection接口、ArrayList、Vector、LinkedList

集合集合的理解和好处保存多个数据使用的是数组&#xff0c;分析数组的弊端数组1、长度开始必须指定&#xff0c;而且一旦指定&#xff0c;不能更改2、保存的必须为同一类型的元素3、使用数组进行增加/删除元素的示意代码-比较麻烦Person数组扩容示意代码Person[] pers new Pe…

手把手搭建springboot项目05-springboot整合Redis及其业务场景

目录前言一、食用步骤1.1 安装步骤1.1.1 客户端安装1.2 添加依赖1.3 修改配置1.4 项目使用1.5 序列化二、应用场景2.1 缓存2.2.分布式锁2.2.1 redis实现2.2.2 使用Redisson 作为分布式锁2.3 全局ID、计数器、限流2.4 购物车2.5 消息队列 (List)2.6 点赞、签到、打卡 (Set)2.7 筛…

Liunx服务器安装SVN

一、下载svn安装包链接&#xff1a;https://pan.baidu.com/s/1gkS0tef2kQP6nvXOS64hUw 提取码&#xff1a;cyuw二、SVN安装部署通过sftp将文件拉取到目的主机路径&#xff1a;/usr/package 跳转文件路径: cd /usr/package 执行解压命令:tar -zxvf subversion-1.14.2.tar.gz 执行…

idea启动报错If you already have a 64-bit JDK installed, define a JAVA HOME variable

IDEA启动报错&#xff0c;如下图所示&#xff1a; 解决方法&#xff1a; 1.根据以下路径找到文件idea64.exe.vmoptions &#xff0c;路径如下图所示&#xff1a; C:\Users\Thinkpad\AppData\Roaming\JetBrains\IntelliJIdea2020.3\idea64.exe.vmoptions 其中Thinkpad是电脑的…

j-vxe-table 下拉搜索选择框数据加载过多导致前端崩溃问题

Jeeg-boot j-vxe-table 下拉搜索选择框数据加载过多导致前端崩溃问题 最近用到了Jeeg-boot j-vxe-table的组件&#xff0c;这组件时真J8难用&#xff0c;还好多BUG&#xff0c;想用个slot插槽也用不了&#xff0c;好像官方写了个基础就没怎么管了。&#x1f611; 问题&#xf…

JavaEE-初识Servlet

目录Servlet 是什么?完成一个servlet程序1.创建一个maven项目2.引入依赖3.创建目录4.编写Servlet代码5.打包6.部署7.验证程序第三方工具简化Servlet 是什么? Servlet 是一种实现动态页面的技术. 是一组 Tomcat 提供给程序猿的 API, 帮助程序猿简单高效的开发一个 web app. …