前言
大家好,我是Jade。这也是我第一次写文章,也是想记录一下这个过程,我相信还有很多小伙伴还没有接触过或者自己从0到1搭建过npm组件,所以我就想记录一下我的搭建过程。过程中还是有很多不清楚和不了解的地方,文章中有错误以及问题还望帮忙指出(手动抱拳)。
相信大家都用过npm
吧,里面有各种各样的包。在工作当中,我们有很多代码都是重复的CV,我们能否自己写一个发布上去呢?答案当然是可以的,让我们用webpack及react
来搭建一个简易版的~
说明:webpack比较灵活,可以配置自己想要的功能或者编写符合公司业务逻辑的组件库,也比较容易集成第三方库,
如antd
。如果你只想开发一个类似lodash
这种工具包可以使用rollup
打包工具。 偷偷告诉你,这也是个很重要的面试题哦~
起步
1、创建文件夹,并初始化
npm init or yarn init
这里会生成package.json文件,文件中会有一些配置项,在此处列举几个常见的
{
"name": "@panyushan/study", // npm包名,此处有个坑 下文会指出
"version": "1.0.0", // 版本号
"main": "lib/index.js", // 入口文件
"license": "ISC", //许可
"description":"此包用于学习", //包的描述信息
"author":"panyushan", // 作者
"files": ["lib/*.js"], // 项目包含的文件名数组
"keywords": ["study","webpack","react"], // 关键词,用于搜索
"repository": { // 项目仓库地址
"type" : "git",
"url" : "https://github.com/panyushan2019/-panyushan-study"
},
"scripts": { // 执行脚本
"start": "webpack serve --open --config ./config/webpack.dev.js",
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --config ./config/webpack.prod.js"
},
"devDependencies": {
},
"dependencies": {
}
}
2、创建src并在src下创建index.js
index.js
import React from "react";
import { Button } from 'antd'
const MyButton = () => {
return <Button>测试button</Button>
}
export default MyButton;
此处使用的antd第三方组件库
npm install antd react react-dom webpack webpack-cli
or
yarn add antd react react-dom webpack webpack-cli注意:webpack版本是v5+,react版本是v17+。不同版本的配置及用法有所不同,如有版本问题请参考官方网站
webpack配置
首先我们肯定要在本地调试我们的代码,确认无误才能打包发布。其次要把公共配置提取出来(使用webpack-merge进行合并)
,以便我们统一配置及修改,所以我们需要创建三个不同的配置文件。
webpack.dev.js //开发环境
webpack.prod.js //生产环境
webpack.base.js //公共配置
1、安装babel、loader等插件
npm install @babel/cli @babel/core @babel/preset-env @babel/preset-react babel-loader css-loader html-webpack-plugin style-loader webpack-dev-server less less-loader
or
yarn add @babel/cli @babel/core @babel/preset-env @babel/preset-react babel-loader css-loader html-webpack-plugin style-loader webpack-dev-server less less-loader
在项目根目录下配置.babelrc文件
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
如有用到less、sass、ts等安装相应loader配置即可,请参考官方网站
2、webpack.base.js配置
const path = require("path");
module.exports = {
module: {
rules: [
{
test: /\.(js|jsx)$/,
use: "babel-loader",
exclude: /node_modules/
},
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/
},
{
test: /\.less$/,
use: [
{
loader: "style-loader"
},
{
loader: "css-loader",
options: {
modules: {
mode: "local",
localIdentName: "[local]__[hash:base64:5]",
},
}
},
{
loader: "less-loader"
}
]
},
{
test: /\.css$/i,
use: ["style-loader", "css-loader"]
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: "asset/resource"
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: "asset/resource"
}
]
},
resolve: {
alias: {
"@": path.resolve(__dirname, "../src/")
},
extensions: [".js", ".jsx", ".ts", ".tsx",'css','less']
}
};
3、webpack.dev.js
const path = require("path");
const { merge } = require("webpack-merge");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const base = require("./webpack.base.js");
module.exports = merge(base, {
mode: "development",
entry: {
index: "./example/app.js"
},
devtool: "inline-source-map",
devServer: {
static: path.resolve(__dirname, "../example")
},
plugins: [
new HtmlWebpackPlugin({
title: "Development",
template: path.join(__dirname, "../example/index.html"),
filename: "index.html"
})
],
output: {
filename: "bundle.js",
path: path.resolve(__dirname, "../example")
}
});
4、webpack.prod.js
const path = require("path");
const { merge } = require("webpack-merge");
const common = require("./webpack.base.js");
module.exports = merge(common, {
entry: {
index: "./index.js"
},
mode: "production",
externals: {
react: 'react', //打包时候排除react
ReactDOM:'react-dom',
antd:'antd'
},
output: {
filename: "[name].js",
library: {
name: "MyButton",
type: "umd",
export: 'default',
},
path: path.resolve(__dirname, "../lib"),
clean: true
}
});
相关使用语法可参考官方网站此处不做过多叙述(本文主要讲如何搭建,而不是api的使用)
注意:开发环境和生产环境打包的出入口不一样也就是entry和output,请注意区分
开发环境
1、在根目录创建example文件夹并创建index.html和app.js并将自己的组件以样式导入并挂载
import React from "react";
import ReactDOM from "react-dom";
import MyButton from '../index.js';
function App() {
return <MyButton />;
}
ReactDOM.render(<App />, document.getElementById("app"));
2、创建在package.json中创建script脚本(每次在终端输入--config --open等命令不累吗)
"scripts": {
"start": "webpack serve --open --config ./config/webpack.dev.js"
},
3、yarn start
至此我们开发环境就ok了,你可以调试自己的代码,并封装出可复用的高质量组件
生产环境
在package.json中创建生产环境script脚本
"build": "webpack --config ./config/webpack.prod.js"
你以为这就完了?
首先你不可预测你的此生产包是否通过npm install 能够正常使用,万一直接提交发布,用户一用发现有bug那岂不是啪啪打脸~
生产环境测试
1、jest编写测试脚本(此处不做过多叙述,请自行学习了解)
2、使用npm link 或 yarn link
- 首先在自己组件项目目录下的终端输入
npm link(可以帮助我们模拟包安装后的状态,它会在系统中做一个快捷方式映射,让本地的包就好像 install 过一样,可以直接使用)
- 然后在另一个空项目根目录终端中(可以使用脚手架来创建)使用
npm link [package-name]
或yarn link [package-name]
- 上面的
[package-name]
就是package.json
中的name
,也就是包的名称 - 最后在测试项目中
(通过脚手架创建的项目)
运行start 这样子我们的组件库才算完整的开发完成
发布到npm
这里对于初次接触的小伙伴会有一些坑等着你~
- 首先你要有npm账号,如果没有去官方网站注册一个
- 在终端输入
npm login
,然后输入账号密码以及邮箱,可以使用npm whoami
来查看是否处于登录 - 在终端执行
npm publish
,大功告成~
相信你一定有以下问题,来让我们看一下:
1、在测试项目start运行报错
Error : Invalid hook call . HoOkS can only be called inside of the bOdy ofa function cOmponent . This COuld happen for one of the following reasons :
1. You might have mismatching versions of React and the renderer ( such as React DOM )2. You might be breaking the Rules of HoOKS
3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem .
根据react官方给的提示是 存在多个不同版本的react,这是因为link的时候A库调用B,B使用的是npm link调试,会导致react的依赖出现两次,所以需要在B中link到A下面的react,重启项目。说白了就是将组件库的react link到调试库。还有个简单暴力的方法,直接将组件库node_modules中的react库删除即可
2、npm publish报错(可能会有以下情况,请自行根据错误信息排查)
- 没有使用 npm login 登录
- 没有切换镜像源
(如果你的是淘宝镜像源,需要切换回来)
- 已有重复的包名
(修改package.json里的name或者使用scope)
如果你使用@[package-name]/[package-name] 作为包名
则你需要使用 npm publish --access=public来进行发布
- 没有验证npm邮箱
(我碰到了这个问题,因为npm注册时会发送邮件让你验证,我当时没有验证,回头再找的时候已经失效了,登录上npm重新验证一下就行)
- 无权限,使用
npm adduser
添加就行了 暂时想到这些,后续有再补充~