手写插件发布到 NPM
# 如何创建一个前端 React 组件并发布到 NPM
造轮子是非常有效的学习方法。在熟练掌握 API 的搬运方法之后,我们可以通过自己造轮子来进一步掌握和理解更底层的知识。自己完成一个组件的开发之后,我们可以打包上传到 NPM 来分享自己的成果。在后面的步骤中,将会实现以下几个小目标:
- 配置开发环境
- 开发组件
- 打包组件,并在测试项目中引入打包组件模块,验证组件功能
- 发布到 NPM
# 初始化
开始一个最基本的 React 工程,我们至少需要以下几项配置:
- React: 用于开发组件
- React dom: 渲染组件
- Babel: 用于转义 JSX
- webpack: 打包组件
我在这个例子里面做了一个叫做 react-tiny-autosuggest 的组件。首先创建 project 并且初始化。
mkdir react-tiny-autosuggest
cd react-tiny-autosuggest
npm init -y
2
3
这里我们需要改动三个地方:
- main: 这里是我们组件的入口文件。开发者在 import 我们的组件的时候会引入这里 export 的内容
- files: 申明将要发布到 npm 的文件。如果省略掉这一项,所有文件包括源代码会被一起上传到 npm
- scripts: 申明命令行可用的各种指令。
// package.json
"main": "dist/bundle.js",
"files": ["dist"],
"scripts": {
"start": "webpack-dev-server --config webpack.dev.config.js",
"dev": "webpack-dev-server --config webpack.dev.config.js",
"build": "webpack --config webpack.prod.config.js"
},
2
3
4
5
6
7
8
接下来安装依赖
npm i react react-dom
npm i -D babel-loader @babel/core @babel/preset-env @babel/preset-react webpack webpack-dev-server webpack-cli html-webpack-plugin webpack-node-externals css-loader style-loader
2
接下来配置 webpack。这里分成两份配置,一份用于开发环境 (development) ,一份用于单独打包组件用于生产环境(production)。
在开发环境下,我们需要搭建一个完整的项目,让我们可以开发组件,并可以将其引入其他组件,渲染到浏览器中看到效果。同时我们也需要一些开发工具的支持,比如 HMR(hot module reloa) 组件热更新和详细的报错信息。
在生产环境下,只需要打包组件本身,不包括工程里面的其他组件。同时我们需要压缩文件体积,尽量减小组件打包之后的体积。
# Webpack 配置
下面是我们的 webpack 开发配置
const path = require('path');
const htmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/app.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, '../dist'),
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.cm\.styl$/,
loader: 'style-loader!css-loader?modules&camelCase&localIdentName=[local]-[hash:base64:5]!stylus-loader'
}
]
},
devServer: {
contentBase: './dist'
},
plugins: [
new htmlWebpackPlugin({
template: 'public/index.html'
})
],
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
production
打包配置,区别是改变了 entry
,因为我们只需要单独的组件,并且改变了 libraryTarget
,这个配置项的默认参数是 var
,我们需要改成 commonjs2
,这样可以通过模块系统引入这个组件。另一点区别是使用了 nodeExternals
使得打包的组件中不包括任何node\_modules
里面的第三方组件,起到减小体积的作用。
const path = require('path');
const nodeExternals = require('webpack-node-externals');
module.exports = {
mode: 'production',
entry: './src/autosuggest.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, '../dist'),
libraryTarget: 'commonjs2'
},
module: {
rules: [
{
test: /\.js$/,
use: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test: /\.cm\.styl$/,
loader: 'style-loader!css-loader?modules&camelCase&localIdentName=[local]-[hash:base64:5]!stylus-loader'
}
]
},
externals: [nodeExternals()]
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
在 package.json 中,我们可以通过 --config
指定 webpack
使用哪一套配置。在这个 demo
里使用了 stylus
来写样式文档,所以添加了相应的css pre-processo
r,把 stylus
语法 转化为 css
语法。并且在引入 css
的时候使用了模块化 css
以避免全局命名冲突。
# .babelrc
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
2
3
# 开发组件
完成以上配置以后,我们可以在 src
文件夹里面开发自己组件。运行 npm run dev
,让 webpack-dev-server
渲染到浏览器中,实时看到效果。
项目目录

index.js 文件中引入 src
中 components
组件并向外暴露
# 打包并验证
打包组件,只需要运行 npm run build
就可以了。
接下来可以通过 npm link
把打包之后的组件引入到 global node\_modules
中,然后在验证 demo
中再通过 npm link react-tiny-autosuggest
引入这个组件,并验证是否符合预期。
// At development directory
npm run build
npm link
cd [test project folder]
npm link react-tiny-autosuggest
2
3
4
5
6
接下下 demo
里面就可以直接 import AutoSuggest from 'react-tiny-autosuggest'
了。
注意
package.json 里面的配置信息非常重要,我解释一下几个重要的配置。
name
: 包名,如果你学习的话建议加一个scoped
,就是我上面的 @taoweng/react-demo 而不是 react-demo,因为 npm 包特别的多,很容易重复。这样这个包就会是私有的,可以通过npm publish --access=public
将这个包变为共有的包。version
: 包的版本,每次发布包的版本不能和上次一样。详细规范可见description
:包的简介。repository
:适合写 Github 地址,建议写成::username/:repository。license
:认证。不知道该用什么的,就写MIT 吧。main
:包的入口文件。就是引入这个包的时候去加载的入口文件。必须配置为打包后的bundle.js
的路径keywords
:添加一些关键词更容易使你的包被搜索到。
# 发布到 NPM
发布组件到 npm: npm publish
取消发布: npm unpublish
更行版本: 更改 package.json 里面的版本号并重新发布