为什么要将封装的组件发布到npm上?
- 在日常开发中,我们会基于项目对一些通用的组件进行封装,并在项目中进行调用。当一个组件被多个项目调用,需要升级维护时,就会出多次修改的情况,这种维护成本无疑是比较高的。所以,我们此时需要将组件发布到npm上,进行统一版本管理。项目中直接从npm上加载最新的包就可以了。
- 搭建一个自己或企业的私人组件仓库。
第一部分:封装一个react组件
1. 初始化一个package.json
yarn init -y
2. 安装相关插件包
package.json
"devDependencies": {"@babel/core": "^7.10.4","@hot-loader/react-dom": "^16.13.0","@babel/preset-env": "^7.10.4","@babel/preset-react": "^7.10.4","autoprefixer": "^9.8.4","babel-loader": "^8.1.0","clean-webpack-plugin": "^3.0.0","css-loader": "^3.6.0","html-webpack-plugin": "^4.3.0","optimize-css-assets-webpack-plugin": "^5.0.3","postcss-loader": "^3.0.0","style-loader": "^1.2.1","terser-webpack-plugin": "^3.0.6","webpack": "^4.43.0","webpack-cli": "^3.3.12","webpack-dev-server": "^3.11.0","webpack-merge": "^5.0.9","webpack-node-externals": "^2.5.0"},"dependencies": {"react": "^16.13.1","react-dom": "^16.13.1"}
3. 准备文件夹目录
4. 编写组件内容
4.1 src/components/change_button.js
import React, { useState, Component } from 'react';
import &#39;./change_button.css&#39;;class ChangeButton extends Component{constructor(props){super(props);this.state&#61;{btnTxt:&#39;Login&#39;}}render(){const {btnTxt}&#61;this.state;return(<div className&#61;&#39;button-container&#39; onClick&#61;{()&#61;>{ this.setState({btnTxt:btnTxt&#61;&#61;&#61;&#39;Login&#39;?&#39;Logout&#39;:&#39;Login&#39;})}}><span>{btnTxt}</span></div>)}
}export default ChangeButton;
注意&#xff1a;此处的组件内容不可以用hooks的形式去写&#xff0c;否则会编译报错&#xff0c;因为hooks只能在依赖于body&#xff0c;不能在独立的组件中使用。
4.2 src/components/change_button.css
.button-container{width: 100px;height: 40px;display: flex;align-items: center;justify-content: center;background-color: aquamarine;border-radius: 5px;
}.button-container:hover{cursor:pointer;
}
4.3 src/components/index.js
import ChangeButton from &#39;./change_button&#39;;export default ChangeButton;
4.4 src/index.js
import React from &#39;react&#39;;
import ReactDOM from &#39;react-dom&#39;;
import ChangeButton from &#39;./components/change_button&#39;;const App &#61; () &#61;> {return (<div><ChangeButton /></div>)
}
if (module.hot) {module.hot.accept()}ReactDOM.render(<App />, document.getElementById(&#39;root&#39;));
4.5 public/index.html
<!DOCTYPE html>
<html><head><meta charset&#61;"utf-8" /><title>React</title></head><body><div id&#61;"root" class&#61;"root"></div></body>
</html>
5. 编写webpack内容
5.1 webpack.base.config.js
const webpackConfigBase &#61; {module: {rules: [{test: /\.js$/,exclude: /node_modules/,use: {loader: &#39;babel-loader&#39;,options: {presets: [&#39;&#64;babel/preset-react&#39;],}}},{test: /\.css$/,use: [&#39;style-loader&#39;,&#39;css-loader&#39;,{loader: &#39;postcss-loader&#39;,options: {ident: &#39;postcss&#39;,plugins: (loader) &#61;> [require(&#39;autoprefixer&#39;)()],}}]}]}
}
module.exports &#61; webpackConfigBase
5.2 webpack.dev.config.js
const path &#61; require(&#39;path&#39;);
const webpack &#61; require(&#39;webpack&#39;);
const webpackConfigBase &#61; require(&#39;./webpack.base.config&#39;);
const HtmlWebpackPlugin &#61; require(&#39;html-webpack-plugin&#39;);
const { merge } &#61; require(&#39;webpack-merge&#39;);function resolve(relatedPath) {return path.join(__dirname, relatedPath)
}const webpackConfigDev &#61; {mode: &#39;development&#39;,entry: {app: [resolve(&#39;../src/index.js&#39;)],},output: {path: resolve(&#39;../lib&#39;), filename: &#39;change-button.js&#39;,},devtool: &#39;cheap-module-eval-source-map&#39;, devServer: {contentBase: resolve(&#39;../lib&#39;), hot: true,open: true, host: &#39;localhost&#39;,port: 8080,},plugins: [new HtmlWebpackPlugin({template: &#39;./public/index.html&#39;, }),new webpack.NamedModulesPlugin(), new webpack.HotModuleReplacementPlugin()]
}module.exports &#61; merge(webpackConfigBase, webpackConfigDev)
5.3 webpack.prod.config.js
const path &#61; require(&#39;path&#39;);
const webpack &#61; require(&#39;webpack&#39;);
const nodeExternals &#61; require(&#39;webpack-node-externals&#39;);
const webpackConfigBase &#61; require(&#39;./webpack.base.config&#39;);
const TerserJSPlugin &#61; require(&#39;terser-webpack-plugin&#39;);
const OptimizeCSSAssetsPlugin &#61; require(&#39;optimize-css-assets-webpack-plugin&#39;);
const { CleanWebpackPlugin } &#61; require(&#39;clean-webpack-plugin&#39;);
const { merge } &#61; require(&#39;webpack-merge&#39;);function resolve(relatedPath) {return path.join(__dirname, relatedPath)
}const webpackConfigProd &#61; {mode: &#39;production&#39;,entry: {app: [resolve(&#39;../src/components/index.js&#39;)], },output: {filename: &#39;change-button.js&#39;,path: resolve(&#39;../lib&#39;), libraryTarget:&#39;commonjs2&#39;},devtool: &#39;source-map&#39;, optimization: {minimizer: [new TerserJSPlugin({parallel: 4,terserOptions: {compress: {drop_console: true, },},}),new OptimizeCSSAssetsPlugin()],},externals: [nodeExternals()],plugins:[new CleanWebpackPlugin() ]
}
module.exports &#61; merge(webpackConfigBase, webpackConfigProd)
注意&#xff1a;
1.entry的入口文件位置&#xff0c;由开发环境的src/index.js改成了组件的出口src/components/index.js&#xff0c;表示此处只负责输出组件。
2.output的libraryTarget需要为commonjs2。
3.通过nodeExternals()将打包组件内的react等依赖给去除了&#xff0c;减小了包的体积&#xff0c;在引用该包时&#xff0c;只要其环境下有相关包&#xff0c;就可以正常使用。
5.4 在package.json中添加如下scripts&#xff0c;用来启动webpack
"scripts": {"build": "webpack --config ./scripts/webpack.prod.config.js","dev": "webpack-dev-server --config ./scripts/webpack.dev.config.js"},
6. 配置组件发布的相关信息
package.json
"name": "&#64;yangin/change-button","version": "1.0.0","main": "lib/change-button.js","author":"yangin","license": "MIT",
属性说明
name: 包名&#xff0c;当建自己的私有仓库时&#xff0c;建议在包名前加一个scoped&#xff0c;如&#64;yangin/change-button&#xff0c;而不是change-button&#xff0c;因为 npm 包特别的多&#xff0c;很容易重复。这样这个包就会是私有的&#xff0c;可以通过 npm publish --access&#61;public 将这个包变为共有的包。
version: 包的版本&#xff0c;每次发布包的版本不能和上次一样。详细规范可见这里
description&#xff1a;包的简介。
repository&#xff1a;适合写 Github 地址&#xff0c;建议写成&#xff1a;:username/:repository。
license&#xff1a;认证。不知道该用什么的&#xff0c;就写MIT 吧。
main&#xff1a;包的入口文件。就是引入这个包的时候去加载的入口文件。
keywords&#xff1a;添加一些关键词更容易使你的包被搜索到。
更详细的package.json配置可见官网。
至此&#xff0c;完成了组件的相关配置
7. 调试并生成组件包文件
7.1 通过在根目录下运行
yarn dev
来启动项目&#xff0c;并对组件内的代码进行调试修改&#xff08;因为已配置了热更新&#xff0c;所以可以实时看到修改效果&#xff09;。
7.2 打包组件
yarn build
根据webpack的配置&#xff0c;在lib目录下会生成一个change-button.js文件&#xff0c;而此文件&#xff0c;正是我们的插件文件。
也是我们将要发布的文件。
至此&#xff0c;我们完成了组件的开发与打包环节。
第二部分&#xff1a;调试验证
第1步&#xff1a;将组件映射到本地库
在组件项目的根目录下运行
yarn link
运行后&#xff0c;在yarn的link文件夹下会有一个文件的快捷键映射
如图&#xff0c;则表示映射成功。
第2步&#xff1a;新建一个项目pblog
create-react-app pblog
第3步&#xff1a;在pblog项目中引入&#64;yangin/change-button组件&#xff0c;并调用
yarn link &#64;yangin/change-button
则在pblog的node_modules文件夹下新增一个&#64;yangin/change-button文件夹
调用
第4步&#xff1a;在app.js中调用&#xff0c;最终运行结果如下
至此&#xff0c;则表示组件调试成功。
第三部分&#xff1a;将组件发布到npm
1.将源码发布到github上
1.1 在自己的github上新建一个respository&#xff0c;名称为change-button&#xff0c;并生成README.md
1.2 克隆仓库到本地&#xff0c;并将前面编写的项目文件内容拉到目录下。
git clone [仓库地址]
1.3 添加.gitignore文件
.gitignore
/node_modules
/yarn-err.log
1.4 提交项目至github。
至此&#xff0c;完成将项目源码发布至github上。
2.将组件包发布到npm上
2.1 准备npm账号,npm官网地址&#xff1a;https://www.npmjs.com/
2.2 在组件的项目根目录下登录npm
npm login
按照提示输入username、password、email
登录后&#xff0c;可以通过npm whoami来查看登录用户信息
npm whoami
2.3 发布组件到npm上
npm publish --access&#61;public
因为我们在给组件命名时&#xff0c;用的是&#64;yangin/change-button&#xff0c;此种为私有库命名方式&#xff0c;但此处需要将组将公开给大家共用&#xff0c;所以需要添加–access&#61;public。
至此&#xff0c;完成了将组件发布至npm上。
2.4 验证
在新项目中通过引入&#64;yangin/change-button组件&#xff0c;并调用来进行验证。
yarn add &#64;yangin/change-button
编写README&#xff0c;可参考资料&#xff1a;https://blog.csdn.net/weixin_33721344/article/details/88679068