【React npm】从零搭建react脚手架,并发布组件到npm全流程(一)
介绍
如何写一个react组件,发布到npm,让大家伙儿都能用你写的组件呢?
自从萌生了这个念头儿,我就开始查各种资料,奈何资料搜了一堆,
都是比较零碎的,或者不符合我自身的需求,就只能自己耐着性子
去从头开始摸索。
我的需求是:
- 必须是react组件
- 必须是自己搭建的脚手架,因为react原生脚手架太大,又不方便
自定义 - 必须是最新的webpack5打包,因为webpack最流行
- 必须发布到npm能使用,体积还不能太大
问题
这个过程中,遇到不少问题:
- 需要哪些技术?
react脚手架怎么搭建?package.json怎么配置?npm怎么发包?- 组件我是用react写的,打包时还不能把react打包进来,要怎么处理?
- 发布之后,组件使用一堆报错,是什么原因,怎么处理?
这中间确实经常懵逼,毫无头绪。
于是,就硬着头皮,从搭建脚手架开始,复习webpack5的配置,
找资料,看文档,搜视频。边学边练。最终,搞懂了整个流程,
发布了自己第一个比较满意的npm包。
下面就开始说明整个发布流程。
准备
先解决第一个问题,需要哪些技术?
对于最基础的组件发布来说,一般需要这些:
- react相关知识
- package.json配置项的理解
- webpack5各种配置项要非常了解
- babel系列配置,比如预设,核心模块,插件等
- eslint
配置.gitignore
在项目根目录下创建文件.gitignore,然后写一些忽略项:
node_modules
dist
lib
这一步是为下面安装包做准备,如果已经有了该文件,可忽略这一步。
搭建react脚手架
先初始化生成package.json文件:
npm init -y
然后安装一些包:
// react react-dom在写react组件时需要引用
npm i react react-dom -D
// webpack webpack-cli做webpack配置时使用
npm i webpack webpack-cli -D
// 安装开发服务器,在开发模式下启动本地服务
npm i webpack-dev-server -D
// babel-loader用于转译js文件
// @babel-core是babel的核心库,必须安装
// @babel-preset-env是一个只能预设,用于编译js高级语法
npm i babel-loader @babel-core @babel/preset-env -D
// 为了编译react中的语法
npm i @babel/preset-react -D
// 开发模式下,我们需要在html模板里查看组件样式
// 它会把html文件打包,自动引入css样式和js代码
npm i html-webpack-plugin -D
// 生产模式下,需要把css文件独立打包
npm i mini-css-extract-plugin -D
// 压缩css代码
npm i css-minimizer-webpack-plugin -D
// style-loader会把css样式插入到html中
// css-loader会把import引入的css文件编译成css代码
npm i style-loader css-loader -D
开始搭建脚手架,为了方便理解,这里把开发模式和生产模式分开配置:
开发模式webpack.dev.js:
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
module: {
rules: [
// 处理css
{
test: /\.css$/,
use: [
'style-loader',
'css-loader'
]
},
// 处理js和jsx
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
},
plugins: [
new HtmlWebpackPlugin({
// 开发模式下打包html的时候,以index.html为模板
template: path.resolve(__dirname, 'public/index.html')
})
],
// 配置开发模式的source-map,便于我们调试代码,定位bug
devtool: 'cheap-module-source-map',
// 配置开发服务器
devServer: {
port: 3001,
open: true
},
resolve: {
// 自动补充扩展名,引入的时候就可以省略文件后缀
// 支持.js .jsx .json文件引入时隐藏后缀
extensions: ['.js', '.jsx', '.json']
},
// externals用来定义哪些通过import引入的包不要被打包到boundle中
// 也就是说,我们在写组件时引入的react和react-dom,不要打包
externals: {
react: {
root: 'React',
commonjs2: 'react',
commonjs: 'react',
amd: 'react',
},
'react-dom': {
root: 'ReactDOM',
commonjs2: 'react-dom',
commonjs: 'react-dom',
amd: 'react-dom',
}
}
}
生产模式webpack.prod.js:
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizaerPlugin = require('css-minimizer-webpack-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
// 打包到lib目录下
path: path.resolve(__dirname, 'lib'),
filename: 'index.js',
// 每次打包时,都自动清除原有的打包文件
clean: true,
// 发布到npm库的相关信息
// name是发布到npm时的库名,别人安装就是安装它
// type是暴露库的形式,umd就表示别人可以在所有模块定义下引入这个库
// 比如CommonJs AMD 和全局变量的形式
// export用来指定哪一个导出应该被暴露为一个库
// 'default'就是我们默认导出的库
library: {
name: 'publish-react-test01',
type: 'umd',
export: 'default'
},
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader'
]
},
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: 'babel-loader'
}
]
},
plugins: [
new MiniCssExtractPlugin({
// 因为我们在文件中引入就是publicTest.css
// 所以在打包后,也用这个名字,以免引入失败
filename: 'publicTest.css'
})
],
resolve: {
// 支持.js .jsx .json文件引入时隐藏后缀
extensions: ['.js', '.jsx', '.json']
},
// 压缩和优化的相关配置都写在optimization里
optimization: {
minimizer: [
new CssMinimizaerPlugin()
]
},
externals: {
react: {
root: 'React',
commonjs2: 'react',
commonjs: 'react',
amd: 'react',
},
'react-dom': {
root: 'ReactDOM',
commonjs2: 'react-dom',
commonjs: 'react-dom',
amd: 'react-dom',
}
}
}
配置babel
在上一步中,已经配置了babel-loader了,但是还需要给js语法做兼容,
也就是使用@babel/preset-env。
而且,我们还需要去编译jsx语法,这里需要用到@babel/preset-react。
二者在上一步中已经安装过了,这里就直接配置。
在根目录下创建文件.babelrc.js,并写入配置:
module.exports = {
presets: [
"@babel/preset-env",
"@babel/preset-react"
]
}
配置package.json
这里配置和发布相关的参数。
参考文档:
https://juejin.cn/post/7145001740696289317
下面做些简单配置:
{
// 这里默认的是我们项目的名称
// 发布前,这里最好和我们在webpack中配置的库名保持一致
"name": "publish-react-test01",
// 发布时改下版本号,只能升,不能降,也不能重复
// 不然会报错403
"version": "0.0.9",
"description": "练习发布react组件到npm",
// 模块的出口文件
"main": "lib/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
// 开发模式下启动 npm start
"start": "webpack serve --config webpack.dev.js",
// 生产模式下启动,打包项目 npm run build
"build": "webpack --config webpack.prod.js",
// 在已经登陆npm后,执行npm run pub就会直接打包并发布到npm
"pub": "npm run build && npm publish"
},
"repository": {
"type": "git",
"url": "https://gitee.com/guozia007/pulish-react-test01.git"
},
"keywords": [
"react",
"react component",
"practive"
],
// 我们只把打包后的lib目录发到npm
"files": [
"lib"
],
"author": "guozi007a",
"license": "MIT",
// 这里可以看到,我们所有的包都是安装到开发模式下的
"devDependencies": {
"@babel/core": "^7.20.12",
"@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",
"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"
},
// 用户要使用我们的库,就必须保证自己已经安装了
// react和react-dom的16.9.0以上版本
"peerDependencies": {
"react": ">=16.9.0",
"react-dom": ">=16.9.0"
},
// 浏览器兼容范围
"browserslist": [
"> 0.25%",
"last 2 versions",
"not dead"
]
}
配置.npmignore
这个文件是告诉npm,哪些文件不要发布到npm。
node_modules
.DS_Store
*.log
因为我们在package.json中已经配置了files参数,所以这里就随便写下
就可以了。
开发组件
配置基本完成,现在我们开始写组件吧。
写这样一个例子,初始状态下这个大号字颜色是黑色,

点击按钮后,字体颜色变成粉色,或者用户传入的颜色值。
首先,在根目录下创建src目录,
在src下创建三个文件:
src
- index.js // 入口文件
- PublicTest.jsx // 我们的组件
- publicTest.css // 组件样式
代码如下:
// index.js
import React from "react";
import { createRoot } from 'react-dom/client';
import PublicTest from './PublicTest';
const root = createRoot(document.getElementById("root"));
root.render(
<PlulicTest />
);
// PublicTest.jsx
import React, { useState } from "react";
import './publicTest.css';
const PublicTest = (props) => {
const { color } = props;
const [hcolor, setHcolor] = useState('#000');
return <div className="public-test">
<h1 style={{color: `${hcolor}`}}>hello react!</h1>
<button className="btn"
onClick={() => {setHcolor(color || 'pink')}}
>click me</button>
</div>
}
export default PublicTest;
/* publicTest.css */
.public-test .btn {
width: 80px;
height: 30px;
cursor: pointer;
}
启动项目,npm start,可以看到我们预期的效果:

点击按钮后:

很棒,组件完成!
准备发布
发布之前我们还需做些准备工作:
- 改造
index.js代码。毕竟我们是要让别人用,就要把组件导出去,
而不是在index中自己加入html中使用。 - 注册npm账号
- 熟悉npm发布流程
改造index.js代码如下:
import PublicTest from './PublicTest';
export default PublicTest;
然后到https://www.npmjs.com/中注册账号。
然后来到项目终端:
// 检查npm当前使用的镜像是否为原始镜像
npm config get registry
// 如果不是,就切换npm源到原始镜像 否则报错E404
npm config set registry https://registry.npmjs.org/
// 登陆 如果曾经登陆过,就不需要登陆了
// 然后会要求输入username password email等
npm login
// 检查package.json的name和库名是否一致,版本号是否需要更换
以上都没问题了,就开始编写README.md文档,
该文档会自动跟着发布到npm库,目的是介绍自己的库,
告诉用户,他该如何使用这个库。
// 安装
npm i publish-react-test01
// 引入
import PublicTest from 'publish-react-test01';
// 使用示例1 无传参
const Demo = () => {
return <PublicTest />
}
// 使用示例2 有传参
const Demo = () => {
return <PublicTest color="deeppink" />
}
发布和检验
以上准备工作做好之后,开始发布:
npm run pub
发布后,可以到自己的npm库里面查看。比如:
https://www.npmjs.com/package/publish-react-test01
启动一个react项目,来测试一下,发布的包是否可用。
按照我们写的文档来操作:
安装:
npm i publish-react-test01
引入:
import PublicTest from 'publish-react-test01';
在组件中使用:
import React from 'react';
import PublicTest from 'publish-react-test01';
const App = () => {
return <PublicTest />
}
export default App;
npm start启动项目,可以在浏览器中看到效果:

点击按钮:

完美!
思考和改进
虽然完成了,但毕竟还只是初级,还有很多地方是可以改进的,
比如:
- 在该库中,是否可以一次发布多个组件?
- 库中如何添加标准化操作?如eslint, husky, prettier等
- 是否可以先检验,后发布?比如使用storybook
- 更新库应该如何做,是否可以实现自动化?
- 打包体积仍然有几kb,是否还可以减小包体积?
总之,后续将继续学习和改进。
更多推荐
所有评论(0)