從 0 開始發布一個 react 組件到 npm
翻譯自: A guide to building a React component with Webpack 4, publishing to npm, with a demo on GitHub…
當你開發完一個 React 組件後也許你想過把它貢獻給社區,但是你又不知道如何讓其他人能使用,這篇文章將指導你完成這個目標
假設你是通過 create-react-app 創建的項目,然後你要發布的組件是包含在你項目中的,要發布該組件的話可能需要對你的工作流做一些特殊的處理
在這個教程中包含了從項目搭建到發布整個過程,在開始前你最好先了解一下 react-scripts,這樣的話你才能更好的理解我講的
主要目標包括:
- 在本地創建一個 demo , 通過 webpack4 的配置實現文件改變後頁面自動刷新
- 將編譯後的組件發布到 npm 上去,用戶直接可以使用
- 在 GitHub Pages 上發布一個可以在線預覽的 Demo
創建組件和DEMO
創建項目文件夾並初始化 npm package ,確保你創建的組件名稱沒有在 [npm](npm) 上被使用過, 這裡我們用 my-component作為示例
mkdir my-componentcd my-componentnpm init
運行 npm init 問題提示列表可以採用默認的選項 (譯者註:默認配置可以使用 npm init -y)
對於本地 demo 組件我們需要 react,所以接下來我們安裝 react 到我們項目開發依賴中來, 後面我會介紹怎樣讓用戶知道我們發布的組件有需要 react 依賴
npm i react react-dom -D
我們的項目將通過 webpack進行構建, Babel 進行編譯,webpack-dev-server 作為本地開發伺服器,接下來我們將他們添加到項目的開發依賴中去
npm i webpack webpack-cli webpack-dev-server html-webpack-plugin style-loader css-loader babel-core babel-loader babel-preset-env babel-preset-react -D
這時上面安裝的依賴已經被添加到根目錄下的 package.json中了,接下來我們添加一個 start的腳本,用於啟動我們本地開發的伺服器, start如下:
{ "name": "my-component", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1", "start": "webpack-dev-server --mode development" }, "author": "", "license": "ISC", "devDependencies": { "babel-core": "^6.26.3", "babel-loader": "^7.1.4", "babel-preset-env": "^1.7.0", "babel-preset-react": "^6.24.1", "css-loader": "^0.28.11", "html-webpack-plugin": "^3.2.0", "react": "^16.4.0", "react-dom": "^16.4.0", "style-loader": "^0.21.0", "webpack": "^4.9.1", "webpack-cli": "^2.1.4", "webpack-dev-server": "^3.1.4" }}
現在讓在我們的項目中創建組件和示例代碼目錄,目錄樹結構如下
├── example // 示例代碼存放目錄│ └── src├── node_modules├── package.json└── src // 組件源代碼和樣式存放目錄
下面我將創建一個非常簡單的組件以作為示例
/*** src/index.js ***/import React from react;import ./styles.css;const MyComponent = () => ( <h1>Hello from My Component</h1>);export default MyComponent;
/*** src/styles.css ***/h1 { color: red;}
( 社區一定會愛上它的
接下來添加一個 demo
<!-- examples/src/index.html --><html><head> <title>My Component Demo</title> <meta charset="utf-8"> <meta name="viewport" content="width_=device-width, initial-scale=1, shrink-to-fit=no"></head><body> <noscript> You need to enable JavaScript to run this app. </noscript> <div id="root"></div></body></html>
/*** examples/src/index.js ***/import React from react;import { render} from react-dom;import MyComponent from ../../src;const App = () => ( <MyComponent />);render(<App />, document.getElementById("root"));
注意 demo 中的 MyComponent 是從 ../../src中導入的
接下來配置 webpack, 在項目根路徑下創建 webpack.config.js文件
/*** webpack.config.js ***/const path = require(path);const HtmlWebpackPlugin = require("html-webpack-plugin");const htmlWebpackPlugin = new HtmlWebpackPlugin({ template: path.join(__dirname, "examples/src/index.html"), filename: "./index.html"});module.exports = { entry: path.join(__dirname, "examples/src/index.js"), module: { rules: [{ test: /.(js|jsx)$/, use: "babel-loader", exclude: /node_modules/ },{ test: /.css$/, use: ["style-loader", "css-loader"] }]}, plugins: [htmlWebpackPlugin], resolve: { extensions: [".js", ".jsx"] }, devServer: { port: 3001}};
Webpack 的配置文件主要做了如下事情:
- 使用 example/src/index.js作為項目入口,處理資源文件的依賴關係
- 通過 babel-loader來編譯處理 js和jsx文件
- 通過style-loader 和 css-loader來處理 css 依賴和注入內聯樣式
- 通過html-webpack-plugin自動注入編譯打包好的腳本文件
- 為 demo 啟動埠為 3001 的服務
最後需要指定 Babel 需要對哪些文件進行編譯,毫無疑問 React 中使用的 JSX 文件需要被編譯,讓它轉換成被主流瀏覽器都支持的 ES5 ,通用的配置也很簡單,只需要添加一對 presets,在項目根目錄下添加文件.babelrc
{ "presets": ["env", "react"]}
接下來運行 demo
npm start
啟動完成後打開瀏覽器輸入 http://localhost:3001,你將會在頁面上看到你寫的組件,你可以修改你的代碼並保存,頁面將會自動刷新,我們的開發環境已經處於監控模式
接下來繼續完成第二項目標
發布用戶能直接使用的組件到 npm 上
發布到 npm 是一個很簡單自動化的工作,只是在發布前需要做如下工作
我們要發布被 babel 編譯且被壓縮後的版本,要讓沒有使用 babel 的項目也能夠正常的使用,比如不能出現 JSX 語法
首先需要安裝 babel cli
npm i babel-cli -D
現在我們添加 transpile腳本,以便使用 Babel 編譯我們的源代碼,同時拷貝一些靜態文件(如:css 文件)到目標打包目錄dist下
同時指定被編譯後的版本為組件的主入口,更改後的 package.json如下
{ "name": "my-component", "version": "1.0.0", "description": "", "main": "dist/index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1", "start": "webpack-dev-server --mode development", "transpile": "babel src -d dist --copy-files" }, "author": "", "license": "ISC", "devDependencies": { "babel-cli": "^6.26.0", "babel-core": "^6.26.0", "babel-loader": "^7.1.4", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "css-loader": "^0.28.11", "html-webpack-plugin": "^3.2.0", "react": "^16.3.2", "react-dom": "^16.3.2", "style-loader": "^0.20.3", "webpack": "^4.5.0", "webpack-cli": "^2.0.14", "webpack-dev-server": "^3.1.3" }}
嘗試編譯
npm run transpile
現在在我們項目根目錄下面會有一個 dist 目錄,包含了 index.js 的編譯版本,和拷貝的樣式文件styles.css,這些文件是用戶可以直接 可以import到他們項目的文件,接下來我們再在我們的工作流中添加一個腳本prepublishOnly ,這個腳本會在每次我們需要發布我們的組件到 npm上去的時候會自動執行,他能確保我們每次發布上去的代碼都是最新代碼編譯的
另外我們需要告訴用戶在用戶我們的組件的時候對於 React 版本的要求,peerDependency 能夠很好的表達這個信息,同時在我們的發布的組件包中不會包含 react , 這樣也減小了包的大小,更加重要是可以避免在用戶的項目中存在多個 react 版本,更改後的 package.json如下
{ "name": "my-component", "version": "1.0.0", "description": "", "main": "dist/index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1", "start": "webpack-dev-server --mode development", "transpile": "babel src -d dist", "prepublishOnly": "npm run transpile" }, "author": "", "license": "ISC", "peerDependencies": { "react": "^16.3.0", "react-dom": "^16.3.0" }, "devDependencies": { "babel-cli": "^6.26.0", "babel-core": "^6.26.0", "babel-loader": "^7.1.4", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "css-loader": "^0.28.11", "html-webpack-plugin": "^3.2.0", "react": "^16.3.1", "react-dom": "^16.3.1", "style-loader": "^0.20.3", "webpack": "^4.5.0", "webpack-cli": "^2.0.14", "webpack-dev-server": "^3.1.3" }}
最後讓我們在項目的根目錄下添加.npmignore文件,告訴 npm,我們項目中哪些文件和文件夾是在發布的包中被忽略掉的
# .npmignore srcexamples.babelrc.gitignorewebpack.config.js
發布我們的組件到 npm 上
npm publish
這時你去瀏覽器的 npm 主頁,應該就能看到我們剛才發布的新包了,恭喜你,你的組件已經成功發布!
接下來讓我們完成最後一項目標
在 GitHub Pages 上發布一個在線 demo
在 GitHub Pages 託管在線 Demo 是免費的,需要使用 webpack 來構建我們的生產環境版本,然後發布到 GitHub 倉庫指定的分支上去,接下來讓我們自動化完成這些吧!
首先,我們需要藉助一個幫助維護特性分支的包,我們還沒有對我們的項目添加 git 代碼版本控制,稍等片刻
npm i gh-pages -D
然後在 package.json 中添加三個腳本
{ "name": "my-component", "version": "1.0.0", "description": "", "main": "dist/index.js", "scripts": { "test": "echo "Error: no test specified" && exit 1", "start": "webpack-dev-server --mode development", "transpile": "babel src -d dist --copy-files", "prepublishOnly": "npm run transpile", "build": "webpack --mode production", "deploy": "gh-pages -d examples/dist", "publish-demo": "npm run build && npm run deploy" }, "author": "", "license": "ISC", "peerDependencies": { "react": "^16.3.0", "react-dom": "^16.3.0" }, "devDependencies": { "babel-cli": "^6.26.0", "babel-core": "^6.26.0", "babel-loader": "^7.1.4", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", "css-loader": "^0.28.11", "gh-pages": "^1.1.0", "html-webpack-plugin": "^3.2.0", "react": "^16.3.2", "react-dom": "^16.3.2", "style-loader": "^0.20.3", "webpack": "^4.5.0", "webpack-cli": "^2.0.14", "webpack-dev-server": "^3.1.3" }}
build 腳本目的是用 webpack 幫我們構建一個 boundled, 和壓縮生產環境代碼,這裡我們需要告訴 webpack 哪個文件是我們項目輸出的結果
/*** webpack.config.js ***/const path = require(path);const HtmlWebpackPlugin = require("html-webpack-plugin");const htmlWebpackPlugin = new HtmlWebpackPlugin({ template: path.join(__dirname, "examples/src/index.html"), filename: "./index.html"});module.exports = { entry: path.join(__dirname, "examples/src/index.js"), output: { path: path.join(__dirname, "examples/dist"), filename: "bundle.js" }, module: { rules: [{ test: /.(js|jsx)$/, use: "babel-loader", exclude: /node_modules/ },{ test: /.css$/, use: ["style-loader", "css-loader"] }] }, plugins: [htmlWebpackPlugin], resolve: { extensions: [".js", ".jsx"] }, devServer: { port: 3001 }};
來,試一試
npm run build
你會發現生成版本的代碼已經打包到了 examples/dist
現在來對項目添加 git 版本控制,在項目的根目錄下添加 .gitignore文件,需要對一些中間過程代碼文件進行排除
# .gitignorenode_modulesdist
接著去 GitHub 為它創建一個倉庫,按照隨後屏幕上出現的提示執行命令行 ...or create a new respository on the command line,將會在本地初始化一個本地倉庫,並連接到遠程倉庫上
現在我們的本地和遠程的倉庫都已經創建並連接上了,準備將 demo 發布到託管環境了,這時,首先需要去這個項目的倉庫中為它新建一個 gh-pages 的分支,deploy 腳本就是為了幫我們干這個事的
npm run deploy
點擊設置連接到你的 github 倉庫頁面,然後滾動到 github pages 欄目,你將會看到你的 demo 在線連接地址,恭喜你 上線了!
最後我們使用 publish-demo腳本叫 build 和 deploy 腳本合併在一起簡化我們的工作流
npm run publish-demo
後話
後面當你需要發布一個新的版本時,你只需要更新一下 package.json 裡面的 version 版本號,然後執行 npm publish 和 npm run publish-demo,發布新版本到 npm 是分分鐘的事兒,發布新的 demo 到 GitHub Pages 最多也只需要 20分鐘
當然你想讓你的組件能在社區被發現,你還需要做一些其他事情,由於文章篇幅有限,這裡就不展開講了,就簡單列舉一下吧
- 添加 README.md ,描述你的組件是做什麼的,在線 demo 的鏈接,使用示例,以及組件 API
- 添加自動化測試
- 在 package.json 中填充 description 和 repository欄位
- 考慮給你的組件添加許可
感謝你的閱讀!
推薦閱讀:
※關於npm install 找不到module的一個解決方案
※npx: npm 5.2.0 內置的包執行器
※npm的應用場景,剛需在何處?簡單的寫寫頁面是否需要npm包管理工具?
※從零開始教你寫一個NPM包