为什么要将封装的组件发布到npm上?
yarn init -y
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"
}
4.1 src/components/change_button.js
import React, { useState, Component } from 'react';
import './change_button.css';
class ChangeButton extends Component{
constructor(props){
super(props);
this.state={
btnTxt:'Login'
}
}
render(){
const {btnTxt}=this.state;
return(
<div className='button-container' onClick={()=>{ this.setState({btnTxt:btnTxt==='Login'?'Logout':'Login'})}}>
<span>{btnTxt}</span>
</div>
)
}
}
export default ChangeButton;
注意:此处的组件内容不可以用hooks的形式去写,否则会编译报错,因为hooks只能在依赖于body,不能在独立的组件中使用。
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 './change_button';
export default ChangeButton;
4.4 src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import ChangeButton from './components/change_button';
const App = () => {
return (
<div>
<ChangeButton />
</div>
)
}
//要实现局部热更新,必须要添加此句
if (module.hot) {module.hot.accept()}
ReactDOM.render(<App />, document.getElementById('root'));
4.5 public/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>React</title>
</head>
<body>
<div id="root" class="root"></div>
</body>
</html>
5.1 webpack.base.config.js
const webpackConfigBase = {
//module此处为loader区域,一般文件内容解析,处理放在此处,如babel,less,postcss转换等
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-react'],
}
}
},
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
{
loader: 'postcss-loader',
options: {
ident: 'postcss',
plugins: (loader) => [
require('autoprefixer')()
],
}
}
]
}
]
}
}
module.exports = webpackConfigBase
5.2 webpack.dev.config.js
const path = require('path');
const webpack = require('webpack');
const webpackConfigBase = require('./webpack.base.config');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { merge } = require('webpack-merge');
function resolve(relatedPath) {
return path.join(__dirname, relatedPath)
}
const webpackConfigDev = {
mode: 'development',
entry: {
app: [resolve('../src/index.js')],
},
output: {
path: resolve('../lib'),
filename: 'change-button.js',
},
devtool: 'cheap-module-eval-source-map',
devServer: {
contentBase: resolve('../lib'),
hot: true,
open: true,
host: 'localhost',
port: 8080,
},
plugins: [
new HtmlWebpackPlugin({template: './public/index.html', }),
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
]
}
module.exports = merge(webpackConfigBase, webpackConfigDev)
5.3 webpack.prod.config.js
const path = require('path');
const webpack = require('webpack');
const nodeExternals = require('webpack-node-externals');
const webpackConfigBase = require('./webpack.base.config');
const TerserJSPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { merge } = require('webpack-merge');
function resolve(relatedPath) {
return path.join(__dirname, relatedPath)
}
const webpackConfigProd = {
mode: 'production',
entry: {
app: [resolve('../src/components/index.js')],
},
output: {
filename: 'change-button.js',
path: resolve('../lib'),
libraryTarget:'commonjs2'
},
devtool: 'source-map', //或使用'cheap-module-source-map'、'none'
optimization: {
minimizer: [
// 压缩js代码
new TerserJSPlugin({// 多进程压缩
parallel: 4,// 开启多进程压缩
terserOptions: {
compress: {
drop_console: true, // 删除所有的 `console` 语句
},
},
}),
//压缩css代码
new OptimizeCSSAssetsPlugin()
],
},
externals: [nodeExternals()],
plugins:[
new CleanWebpackPlugin() //每次执行都将清空一下./dist目录
]
}
module.exports = merge(webpackConfigBase, webpackConfigProd)
注意:
1.entry的入口文件位置,由开发环境的src/index.js改成了组件的出口src/components/index.js,表示此处只负责输出组件。
2.output的libraryTarget需要为commonjs2。
3.通过nodeExternals()将打包组件内的react等依赖给去除了,减小了包的体积,在引用该包时,只要其环境下有相关包,就可以正常使用。
5.4 在package.json中添加如下scripts,用来启动webpack
"scripts": {
"build": "webpack --config ./scripts/webpack.prod.config.js",
"dev": "webpack-dev-server --config ./scripts/webpack.dev.config.js"
},
package.json
"name": "@yangin/change-button",
"version": "1.0.0",
"main": "lib/change-button.js",
"author":"yangin",
"license": "MIT",
属性说明
name: 包名,当建自己的私有仓库时,建议在包名前加一个scoped,如@yangin/change-button,而不是change-button,因为 npm 包特别的多,很容易重复。这样这个包就会是私有的,可以通过 npm publish --access=public 将这个包变为共有的包。
version: 包的版本,每次发布包的版本不能和上次一样。详细规范可见这里
description:包的简介。
repository:适合写 Github 地址,建议写成::username/:repository。
license:认证。不知道该用什么的,就写MIT 吧。
main:包的入口文件。就是引入这个包的时候去加载的入口文件。
keywords:添加一些关键词更容易使你的包被搜索到。
更详细的package.json配置可见官网。
至此,完成了组件的相关配置
7.1 通过在根目录下运行
yarn dev
来启动项目,并对组件内的代码进行调试修改(因为已配置了热更新,所以可以实时看到修改效果)。
7.2 打包组件
yarn build
根据webpack的配置,在lib目录下会生成一个change-button.js文件,而此文件,正是我们的插件文件。
也是我们将要发布的文件。
至此,我们完成了组件的开发与打包环节。
在组件项目的根目录下运行
yarn link
运行后,在yarn的link文件夹下会有一个文件的快捷键映射
如图,则表示映射成功。
create-react-app pblog
yarn link @yangin/change-button
则在pblog的node_modules文件夹下新增一个@yangin/change-button文件夹
调用
1.1 在自己的github上新建一个respository,名称为change-button,并生成README.md
1.2 克隆仓库到本地,并将前面编写的项目文件内容拉到目录下。
git clone [仓库地址]
1.3 添加.gitignore文件
.gitignore
/node_modules
/yarn-err.log
1.4 提交项目至github。
至此,完成将项目源码发布至github上。
2.1 准备npm账号,npm官网地址:https://www.npmjs.com/
2.2 在组件的项目根目录下登录npm
npm login
按照提示输入username、password、email
登录后,可以通过npm whoami来查看登录用户信息
npm whoami
2.3 发布组件到npm上
npm publish --access=public
因为我们在给组件命名时,用的是@yangin/change-button,此种为私有库命名方式,但此处需要将组将公开给大家共用,所以需要添加–access=public。
至此,完成了将组件发布至npm上。
2.4 验证
在新项目中通过引入@yangin/change-button组件,并调用来进行验证。
yarn add @yangin/change-button
编写README,可参考资料:https://blog.csdn.net/weixin_33721344/article/details/88679068