詳解從 0 發(fā)布 react 組件到 npm 上

我發(fā)布了我的第一個(gè) npm 組件,一個(gè)基于 react 的 3d 標(biāo)簽云組件。在這途中我也是遇到了很多的坑,花在完善整個(gè)發(fā)布流程的時(shí)間遠(yuǎn)多于寫(xiě)這個(gè)組件本身的時(shí)間,所以我記錄下我覺(jué)得一個(gè)正常的 react 組件的發(fā)布流程

最后記錄這篇文章花的時(shí)間比我完成整個(gè)組件的時(shí)間都多,最終希望能給新手帶來(lái)幫助

在整個(gè)發(fā)布組件的過(guò)程我做了如下幾件事兒:

  1. 開(kāi)發(fā)組件
  2. 編寫(xiě) Readme
  3. 推送到 github,并且把 demo 放到 github page 上
  4. 發(fā)布組件到 npm 上

開(kāi)發(fā)組件

創(chuàng)建項(xiàng)目文件夾并初始化 npm package ,確保你創(chuàng)建的組件名稱(chēng)沒(méi)有在 npm 上被使用過(guò), 這里我們用 react-demo 作為示例

mkdir react-demo
cd react-demo
npm init

npm init 是生成初始的 package.json 的命令,在 npm init 的時(shí)候,你可以根據(jù)你自己的需要進(jìn)行填寫(xiě)你的組件信息?;蛘咧苯邮褂?npm init -y 采用默認(rèn)的,后面自己再去修改。

首先安裝 react 相關(guān)的包:

npm i react react-dom -D

采用 babel 編譯相關(guān)的依賴(lài):

npm i @babel/cli @babel/core @babel/preset-env @babel/preset-react -D

采用 webpack 做構(gòu)建,webpack-dev-server 作為本地開(kāi)發(fā)服務(wù)器,所以需要安裝如下依賴(lài):

npm i webpack webpack-cli webpack-dev-server -D

我這里為了簡(jiǎn)單演示,只安裝 babel-loader 用來(lái)編譯 jsx,其他 loader 安裝自己的需要自己安裝。

npm i babel-loader -D

另外再安裝一個(gè) webpack 插件 html-webpack-plugin ,用來(lái)生成 html:

npm i html-webpack-plugin -D

然后再添加上常規(guī)的 startbuild 腳本,package.json 如下:

{
  "name": "react-demo",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --open development",
    "build": "webpack --mode production"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.5",
    "html-webpack-plugin": "^3.2.0",
    "react": "^16.7.0",
    "react-dom": "^16.7.0",
    "webpack": "^4.29.0",
    "webpack-cli": "^3.2.1",
    "webpack-dev-server": "^3.1.14"
  },
  "dependencies": {}
}

當(dāng)然,你也可以直接把我這個(gè) package.json 復(fù)制過(guò)去,然后 npm install 進(jìn)行依賴(lài)的安裝,也可以一個(gè)一個(gè)的安裝。

一個(gè)最基本的組件只需要編譯 jsx,所以我這里沒(méi)有安裝 css 以及處理其他的 loader,這篇文章的重點(diǎn)不是講 webpack 的,所以其他的自行解決,有 webpack 問(wèn)題可以私聊我。

然后我們?cè)賱?chuàng)建如下的目錄結(jié)構(gòu):

├── example // 示例代碼,在自己測(cè)試的時(shí)候可以把測(cè)試文件放到 src 里
│   └── src // 示例源代碼
│       ├── index.html // 示例 html
│       └── app.js // 添加到 react-dom 的文件
├── package.json 
├── src // 組件源代碼
│   └── index.js // 組件源代碼文件
├── .babelrc
├── .editorconfig // 不必須的,但是建議有
├── .gitignore // 如果要放到 github 上,這個(gè)是需要有的
└── webpack.config.js

下面我們?cè)賱?chuàng)建一個(gè)最簡(jiǎn)單的組件,來(lái)進(jìn)行演示:

/*** src/index.js ***/
import React from 'react';
const ReactDemo = () => (
 <h1>這是我的第一個(gè) react npm 組件</h1>
);
export default ReactDemo;

接下來(lái)添加一個(gè) demo

<!-- examples/src/index.html -->
<html>
<head>
    <title>My First React Component</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
    <div id="root"></div>
</body>
</html>
/*** examples/src/app.js ***/
import React from 'react'
import { render } from 'react-dom'
import ReactDemo from '../../src'

const App = () => <ReactDemo />
render(<App />, document.getElementById('root'))

注意 demo 中的 ReactDemo 是從 ../../src 中導(dǎo)入的

接下來(lái)配置非常簡(jiǎn)單的 webpack, 在項(xiàng)目根路徑下創(chuàng)建 webpack.config.js 文件

const path = require('path');
const HtmlWebpackPlugin = require("html-webpack-plugin");
const htmlWebpackPlugin = new HtmlWebpackPlugin({
    template: path.join(__dirname, "./example/src/index.html"),
    filename: "./index.html"
});

module.exports = {
    entry: path.join(__dirname, "./example/src/app.js"),
    output: {
        path: path.join(__dirname, "example/dist"),
        filename: "bundle.js"
    },
    module: {
        rules: [{
            test: /\.(js|jsx)$/,
            use: "babel-loader",
            exclude: /node_modules/
        }]
    },
    plugins: [htmlWebpackPlugin],
    resolve: {
        extensions: [".js", ".jsx"]
    },
    devServer: {
        port: 3001
    }
};

Webpack 的配置文件主要做了如下事情:

  • 使用 example/src/index.js 作為項(xiàng)目入口,處理資源文件的依賴(lài)關(guān)系
  • 通過(guò) babel-loader 來(lái)編譯處理 js 和 jsx 文件
  • 通過(guò) html-webpack-plugin 自動(dòng)注入編譯打包好的腳本文件
  • 為 demo 啟動(dòng)端口為 3001 的服務(wù)

然后再配置一下 babel,咱們的 babel 主要做兩件事,將 jsx 編譯成 es5,然后再加一個(gè)通用的 env,所以 .babelrc 配置如下:

{
    "presets": ["@babel/preset-env", "@babel/preset-react"]
}

可以看到之前的 package.json ,我這里 babel 安裝的是 7.x,那么 babel-loader 就應(yīng)該是 8.x 才行,然后 babel 7.x 相對(duì)于之前的配置是不同的,要用這個(gè)配置,版本一定要跟我的相同,不然配置可能會(huì)不一樣。

然后現(xiàn)在執(zhí)行 npm start,然后再訪(fǎng)問(wèn) localhost:3001 就可以訪(fǎng)問(wèn)到了。

編寫(xiě) README

編寫(xiě) README,如果你不知道該如何編寫(xiě),我給你提幾點(diǎn)建議,你可以選擇你覺(jué)得必要的點(diǎn)來(lái)寫(xiě):

  1. logo
  2. 官方主頁(yè)
  3. 介紹
  4. 安裝
  5. 快速開(kāi)始
  6. 功能列表
  7. 截圖
  8. todoList
  9. 不足之處
  10. FAQ
  11. Change Log(更新日志)

添加徽章

當(dāng)你寫(xiě)完 README 之后,我們將添加一些來(lái)自 shields.io 的時(shí)髦徽章,讓人們知道我們又酷又專(zhuān)業(yè)。

image

想添加什么樣的徽章看自己喜歡吧,種類(lèi)有很多。

可以點(diǎn)擊這里看我之前寫(xiě)的 3d 標(biāo)簽云的 README。

現(xiàn)在基本上可以發(fā)布了,但是要是能提供一個(gè)在線(xiàn)的 demo 讓別人在用這個(gè)組件的時(shí)候可以看到效果就更好了。

在 GitHub Pages 上發(fā)布一個(gè)在線(xiàn) demo

發(fā)布在線(xiàn) demo 可以直接用 Github Pages 來(lái)幫助我們托管,通過(guò) webpack 構(gòu)建生產(chǎn)環(huán)境版本,然后發(fā)到 Github 上去即可。

首先去 Github 創(chuàng)建一個(gè)用來(lái)存放你組件代碼的倉(cāng)庫(kù)。

然后把你的項(xiàng)目初始化成 git 項(xiàng)目:

git init

再添加遠(yuǎn)程倉(cāng)庫(kù),將本地倉(cāng)庫(kù)和遠(yuǎn)程倉(cāng)庫(kù)關(guān)聯(lián)起來(lái)。

git remote add origin git@github.com:crazylxr/react-demo.git

接下來(lái)我們可以安裝 gh-pages 來(lái)幫助我們發(fā)布到 github pages:

npm i gh-pages -D

為了方便記憶,后續(xù)能更快的發(fā)布,這些命令我們可以寫(xiě)成 npm-scriprt,所以我們?cè)黾觾蓚€(gè)腳本:

{
  "name": "@taoweng/react-demo",
  "version": "1.0.0",
  "description": "react demo",
  "main": "lib/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --open development",
    "build": "webpack --mode production",
    "deploy": "gh-pages -d examples/dist",
    "publish-demo": "npm run build && npm run deploy"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.5",
    "gh-pages": "^2.0.1",
    "html-webpack-plugin": "^3.2.0",
    "react": "^16.7.0",
    "react-dom": "^16.7.0",
    "webpack": "^4.29.0",
    "webpack-cli": "^3.2.1",
    "webpack-dev-server": "^3.1.14"
  },
  "dependencies": {}
}

添加了 deploy 腳本和 publish-demo,以后需要發(fā)布 demo 的時(shí)候只需要 npm run publish-demo 即可。

然后我們就可以 build 項(xiàng)目之后再將 expamples/dist 發(fā)布到 gh-pages 分支:

npm run build
npm run deploy

或者直接

npm run publish-demo

注意:這里只會(huì)將 expample/src 下的文件發(fā)布到 ph-pages 分支,master 分支依然沒(méi)有到 github 上,如果你要把源碼放到 github 的 master 或者其他分支上,還是需要自己 push 的。

這個(gè)時(shí)候,我們可以通過(guò) crazylxr.github.io/react-demo 訪(fǎng)問(wèn)到我們寫(xiě)的 demo。crazylxr 是 github 的 username,react-demo 是倉(cāng)庫(kù)名,注意改成你自己的。

編譯源碼

我們現(xiàn)在的源碼是 jsx 的,所以我們需要通過(guò) babeljsx 編譯為正常瀏覽器能訪(fǎng)問(wèn)的代碼。我們可以通過(guò) babel-cli 來(lái)編譯我們代碼,直接編譯 src 目錄,到 lib 文件夾。更多命令見(jiàn) babel-cli

npx babel src --out-dir lib

執(zhí)行完這個(gè)命令,就把生成一個(gè) lib 文件夾,然后里面的 index.js 就是編譯過(guò)后的文件,是可以直接發(fā)布到 npm 的文件。

然后將這個(gè)編譯命令寫(xiě)到 script 里,package.json 如下:

{
  "name": "@taoweng/react-demo",
  "version": "1.0.0",
  "description": "react demo",
  "main": "lib/index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --open development",
    "build": "webpack --mode production",
    "compile": "npx babel src --out-dir lib",
    "deploy": "gh-pages -d example/dist",
    "publish-demo": "npm run build && npm run deploy"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.5",
    "gh-pages": "^2.0.1",
    "html-webpack-plugin": "^3.2.0",
    "react": "^16.7.0",
    "react-dom": "^16.7.0",
    "webpack": "^4.29.0",
    "webpack-cli": "^3.2.1",
    "webpack-dev-server": "^3.1.14"
  },
  "dependencies": {}
}

那么以后要編譯 src 下面的代碼,只需要執(zhí)行:

npm run compile

現(xiàn)在我們已經(jīng)有編譯好的代碼了,接下來(lái)就可以發(fā)布到 npm 供其他人使用了。

發(fā)布 npm 包

在發(fā)布以前我們是需要一些準(zhǔn)備:

注冊(cè) npm 賬戶(hù):

在這里](https://www.npmjs.com/) 注冊(cè)一個(gè) npm 賬號(hào)。

登錄

在終端輸入:

npm adduser

也可以用:

npm login

然后你會(huì)得到一個(gè)讓你輸入username、password 和 **email ** 的提示,把它們填在相應(yīng)的位置。

關(guān)于 package.json 需要注意的點(diǎn)

package.json 里面的配置信息非常重要,我解釋一下幾個(gè)重要的配置。

  • name: 包名,如果你學(xué)習(xí)的話(huà)建議加一個(gè) scoped,就是我上面的 @taoweng/react-demo 而不是 react-demo,因?yàn)?npm 包特別的多,很容易重復(fù)。這樣這個(gè)包就會(huì)是私有的,可以通過(guò) npm publish --access=public 將這個(gè)包變?yōu)楣灿械陌?/p>

  • version: 包的版本,每次發(fā)布包的版本不能和上次一樣。詳細(xì)規(guī)范可見(jiàn)這里

  • description:包的簡(jiǎn)介。

  • repository:適合寫(xiě) Github 地址,建議寫(xiě)成::username/:repository。

  • license:認(rèn)證。不知道該用什么的,就寫(xiě)MIT 吧。

  • main:包的入口文件。就是引入這個(gè)包的時(shí)候去加載的入口文件。

  • keywords:添加一些關(guān)鍵詞更容易使你的包被搜索到。

更詳細(xì)的 package.json 配置可見(jiàn)官網(wǎng)

我這里簡(jiǎn)單的添加了這些信息:

{
  "name": "@taoweng/react-demo",
  "version": "1.0.0",
  "description": "react demo",
  "main": "lib/index.js",
  "repository": "crazylxr/react-demo",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "webpack-dev-server --open development",
    "build": "webpack --mode production",
    "compile": "npx babel src --out-dir lib",
    "deploy": "gh-pages -d example/dist",
    "publish-demo": "npm run build && npm run deploy"
  },
  "keywords": ["react", "demo"],
  "author": "taoweng",
  "license": "MIT",
  "devDependencies": {
    "@babel/cli": "^7.2.3",
    "@babel/core": "^7.2.2",
    "@babel/preset-env": "^7.3.1",
    "@babel/preset-react": "^7.0.0",
    "babel-loader": "^8.0.5",
    "gh-pages": "^2.0.1",
    "html-webpack-plugin": "^3.2.0",
    "react": "^16.7.0",
    "react-dom": "^16.7.0",
    "webpack": "^4.29.0",
    "webpack-cli": "^3.2.1",
    "webpack-dev-server": "^3.1.14"
  },
  "dependencies": {}
}

這些配置信息都會(huì)在 npm 包的頁(yè)面顯示出來(lái)的,所以能填還是填一下:

image

最后我們?cè)陧?xiàng)目中添加 .npmignore 文件,跟 .gitignore 的作用一樣,就是在發(fā)布 npm 的時(shí)候需要忽略的文件和文件夾:

# .npmignore 
src
examples
.babelrc
.gitignore
webpack.config.js

這個(gè)時(shí)候我們就可以發(fā)布到 npm 了:

npm publish

如果你是私有包,可以這樣發(fā)布:

npm publish --access=public

結(jié)語(yǔ)

以后發(fā)布新版本的時(shí)候,只需要更改一下 package.json 里面的 version 版本號(hào),然后執(zhí)行 npm publish 和 npm run publish-demo 就可以同步 npm 和 demo。

不過(guò)如果想讓你的組件在社區(qū)里給更多人用,你需要把 README 寫(xiě)得更好一點(diǎn),然后添加好自動(dòng)化測(cè)試,不然別人不太敢用。

另外在寫(xiě)組件之前可以先了解下有沒(méi)有類(lèi)似的組件了,如果有就直接用吧,咱們就站在巨人的肩膀上,把自己寶貴的時(shí)間放在創(chuàng)造價(jià)值上。

最后整個(gè)項(xiàng)目的源代碼見(jiàn) github

參考文章

另外

同時(shí)你也可以在這些地方找到這篇文章:

  1. 個(gè)人網(wǎng)站
  2. github blog

另外有興趣可以關(guān)注我的公眾號(hào):前端桃園

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容