前言
-
本文主要記錄本人在使用
NPM
釋出具有樣式的 react元件 時的完整實踐流程,在這過程中踩了許多坑,花在完善釋出腳手架的時間遠多於開發元件的時間,於是記錄下整個過程,希望能給大家提供幫助。 -
下文的內容主要包括:
- 釋出 react元件 的腳手架搭建。
- 開發元件。
- 打包元件,並在測試專案中引入打包元件模組,驗證元件功能。
- 釋出到
NPM
。
腳手架搭建
建立專案
- 首先建立專案資料夾,並使用
npm init
命令生成初始的配置檔案 package.json,具體命令如下:
mkdir react-component-npm-cli
cd react-component-npm-cli
npm init
複製程式碼
- 在使用
npm init
命令 的時候,會提示我們輸入專案的名稱、版本號、作者等,可以一路回車,稍後進行修改。或者直接使用下面的命令採用預設配置,後面再根據自己的需要進行修改:
npm init -y
複製程式碼
- 這一步完成後,我們的專案資料夾裡就會新增一個 package.json 檔案。接下來完善腳手架,建立如下的目錄結構:
├── config # webpack配置
├── webpack.base.js # 公共配置
├── webpack.dev.config.js # 開發環境配置
└── webpack.prod.config.js # 打包釋出環境配置
├── example # 開發時預覽程式碼
├── src # 示例程式碼目錄
├── app.js # 入口 js 檔案
└── index.html # 入口 html 檔案
├── lib # 元件打包結果目錄
├── node_modules # 安裝依賴時自動生成
├── src # 元件原始碼目錄
├── index.css # 元件樣式
└── index.js # 元件原始碼
├── .babelrc # babel 配置
├── .npmignore # 指定釋出 npm 的時候需要忽略的檔案和資料夾
├── README.md
└── package.json
複製程式碼
安裝依賴
- 首先安裝 react 相關的依賴:
npm i react react-dom -D
複製程式碼
- 接著安裝 babel 編譯相關的依賴:
npm i @babel/cli @babel/core @babel/preset-env @babel/preset-react -D
複製程式碼
- 本專案採用 webpack 做構建,同時使用 webpack-dev-server 作為本地開發伺服器,使用 webpack-merge 合併webpack配置,因此使用下面的命令安裝相關依賴:
npm i webpack webpack-cli webpack-dev-server webpack-merge -D
複製程式碼
- 同時,使用 babel-loader 編譯 jsx,使用 MiniCssExtractPlugin 提取 css,安裝命令如下:
npm i babel-loader mini-css-extract-plugin -D
複製程式碼
- 最後安裝 style-loader 和 css-loader ,二者組合在一起使我們能夠把樣式表嵌入 webpack 打包後的 js 檔案中(開發環境使用)。
- 執行完以上命令,此時 package.json 中包含的依賴資訊如下:
{
"devDependencies": {
// babel 用於將 es6+ 的程式碼轉換成 es5
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5", // 根據目標環境實現按需轉碼
"@babel/preset-react": "^7.0.0", // 讓babel支援react語法
"babel-loader": "^8.0.6", // 編譯 jsx
"css-loader": "^3.2.0", // 將 css 裝換成js
"mini-css-extract-plugin": "^0.8.0", // 提取css
"react": "^16.9.0",
"react-dom": "^16.9.0",
"style-loader": "^1.0.0", // 將 css 裝換成js
"webpack": "^4.39.3",
"webpack-cli": "^3.3.7", // webpack4之後需要額外安裝webpack-cli
"webpack-dev-server": "^3.8.0", // 開發時預覽元件所用的服務,在檔案變化時會自動重新整理頁面
"webpack-merge": "^4.2.2" // 用於合併webpack配置
},
"dependencies": {}
}
複製程式碼
配置 webpack 和 babel
- 如前面給出的目錄結構所示,在 config 目錄下我們建立 3 個 webpack 配置檔案:
- 公共配置檔案:
webpack.base.js
。 - 開發環境配置檔案:
webpack.dev.config.js
。 - 打包釋出環境配置檔案:
webpack.prod.config.js
。
- 公共配置檔案:
- 由於開發和釋出打包時 webpack 配置有一部分是公共而且重複的,所以把這部分的配置單獨拿出來放到
webpack.base.js
中,該檔案內容如下:
module.exports = {
module: {
rules: [
{
// 使用 babel-loader 來編譯處理 js 和 jsx 檔案
test: /\.(js|jsx)$/,
use: "babel-loader",
exclude: /node_modules/
}
]
},
};
複製程式碼
- 開發時採用的 webpack 配置寫在
webpack.dev.config.js
中,內容如下:
const path = require('path');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.js'); // 引用公共配置
const devConfig = {
mode: 'development', // 開發模式
entry: path.join(__dirname, "../example/src/app.js"), // 專案入口,處理資原始檔的依賴關係
output: {
path: path.join(__dirname, "../example/src/"),
filename: "bundle.js", // 使用webpack-dev-sevrer啟動開發服務時,並不會實際在`src`目錄下生成bundle.js,打包好的檔案是在記憶體中的,但並不影響我們使用。
},
module: {
rules: [
{
test: /\.css$/,
exclude: /\.min\.css$/,
loader: ['style-loader','css-loader?modules'],
},
{
test: /\.min\.css$/,
loader: ['style-loader','css-loader'],
},
]
},
devServer: {
contentBase: path.join(__dirname, '../example/src/'),
compress: true,
host: '127.0.0.1', // webpack-dev-server啟動時要指定ip,不能直接通過localhost啟動,不指定會報錯
port: 3001, // 啟動埠為 3001 的服務
open: true // 自動開啟瀏覽器
},
};
module.exports = merge(devConfig, baseConfig); // 將baseConfig和devConfig合併為一個配置
複製程式碼
- 打包元件時採用的 webpack 配置寫在
webpack.prod.config.js
中,內容如下:
const path = require('path');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base.js'); // 引用公共的配置
const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 用於將元件的css打包成單獨的檔案輸出到`lib`目錄中
const prodConfig = {
mode: 'production', // 開發模式
entry: path.join(__dirname, "../src/index.js"),
output: {
path: path.join(__dirname, "../lib/"),
filename: "index.js",
libraryTarget: 'umd', // 採用通用模組定義
libraryExport: 'default', // 相容 ES6 的模組系統、CommonJS 和 AMD 模組規範
},
module: {
rules: [
{
test: /\.css$/,
loader: [MiniCssExtractPlugin.loader,'css-loader?modules'],
},
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "main.min.css" // 提取後的css的檔名
})
],
externals: { // 定義外部依賴,避免把react和react-dom打包進去
react: {
root: "React",
commonjs2: "react",
commonjs: "react",
amd: "react"
},
"react-dom": {
root: "ReactDOM",
commonjs2: "react-dom",
commonjs: "react-dom",
amd: "react-dom"
}
},
};
module.exports = merge(prodConfig, baseConfig); // 將baseConfig和prodConfig合併為一個配置
複製程式碼
- 完成 webpack 配置檔案的編寫之後,需要在 package.json 中的
scripts
欄位中配置相關的啟動、打包和釋出命令,在 package.json 中新增的指令碼內容如下:
// package.json
...
"scripts": {
"start": "webpack-dev-server --config config/webpack.dev.config.js", // 使用webpack-dev-server啟動一個開發服務用於預覽元件效果
"build": "webpack --config config/webpack.prod.config.js", // 打包元件
"pub": "npm run build && npm publish", // 打包元件併發布到npm
},
...
複製程式碼
配置 babel
- 我們需要使用 babel 把我們的程式碼編譯成 es5 版本。在專案根目錄下建好的
.babelrc
檔案內補充以下內容:
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
複製程式碼
- 以上則完成了腳手架的搭建過程,接下來就可以開始開發元件。
開發元件
- 為了演示,我將在 src 目錄中建立一個簡單的
具有樣式
的元件,大家可根據自己的需要進行修改,index.js
檔案的具體內容如下:
<!-- src/index.js -->
import React from 'react';
import * as styles from './index.css';
class ReactDemo extends React.Component{
render () {
return <div className={styles.wrapper}>hello world</div>
}
}
export default ReactDemo;
複製程式碼
- 樣式檔案
index.css
檔案的具體內容如下:
<!-- src/index.css -->
.wrapper{
background: red;
}
複製程式碼
- 為了在開發時實時預覽元件的效果,則需要在 example 目錄下新建
index.html
和app.js
兩個檔案,index.html
的內容如下:
<!-- 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>
<script src="bundle.js"></script> <!-- 這句十分重要 -->
</body>
</html>
複製程式碼
- 細心的同學會⚠️注意⚠️到,
index.html
檔案中引入了檔名為bundle.js
的指令碼,原因是在開發過程(使用webpack-dev-sevrer啟動開發服務)時,並不會實際在 src 目錄下生成bundle.js
,打包好的檔案是在記憶體中的,如果要實時預覽效果,需要在 html 中引入(也可使用html-webpack-plugin外掛注入,這裡不進行具體講解)。 app.js
的內容如下:
/*** 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'))
複製程式碼
- 現在在根目錄執行
npm start
,當看到如下圖的結果,則表示編譯成功: - 此時訪問
127.0.0.1:3001
則可以看到下圖的結果:
元件打包及功能測試
- 完成元件的開發,再發包之前,需要對自己的元件的功能進行打包和測試。
元件打包
- 前面已經在 package.json 中新增了打包元件的命令,然而,在執行
npm run build
之前,還需要在 package.json 中修改main
欄位,作用是宣告元件的入口檔案。
開發者在
import
我們的元件的時候會引入main
欄位中export
的內容。
- 截止到目前為止,我的 package.json 完整內容如下,為便於理解,我加上註釋,如果要直接使用刪掉註釋即可:
{
"name": "react-demo-component", // 釋出的 npm 包的名字,確保獨一無二(先在 npm 上搜,若與已有的包重名會報錯)
"version": "1.0.0", // npm 包版本
"description": "A test component demo", // 包的描述
"main": "lib/index.js", // *重點*:宣告元件的入口檔案
"scripts": { // 執行指令碼
"start": "webpack-dev-server --config config/webpack.dev.config.js", // 使用webpack-dev-server啟動一個開發服務用於預覽元件效果
"build": "webpack --config config/webpack.prod.config.js", // 打包元件
"pub": "npm run build && npm publish", // 打包元件併發布到npm
},
"keywords": [ // 關鍵字(在npm網站上搜尋 npm 包的關鍵詞)
"react",
"hello world"
],
"author": "feSmallBlack", // npm 包的作者
"license": "ISC", // 版權許可證
"devDependencies": { // 開發環境依賴
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.6",
"css-loader": "^3.2.0",
"mini-css-extract-plugin": "^0.8.0",
"react": "^16.9.0",
"react-dom": "^16.9.0",
"style-loader": "^1.0.0",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.7",
"webpack-dev-server": "^3.8.0",
"webpack-merge": "^4.2.2"
},
"dependencies": {} 生產環境依賴
}
複製程式碼
- 接下來,則可以使用
npm run build
打包我們開發好的元件,出現下面的結果則代表打包成功:
- 此時,專案根目錄的 lib 目錄下會出現以下兩個檔案:
- 這兩個檔案則是我們要釋出的包。
元件功能測試
- 雖然躍躍欲試忍不住要發包,但是!在發包之前,為了對大家和自己負責,避免頻繁的撤包操作,最好就是先對自己的元件進行試用。
- 我們可以使用
npm link
把打包之後的元件引入到全域性 node_modules 中,相關命令如下:
// At development directory
npm run build
npm link
複製程式碼
- 執行
npm link
命令後的結果如下: - 在 example/src/app.js 檔案中進行試用,相關命令如下:
// At development directory
cd example/src
npm link react-demo-component
複製程式碼
- 然後修改 example/src/app.js 的內容:
/*** examples/src/app.js ***/
import React from 'react'
import { render } from 'react-dom'
import ReactDemo from 'react-demo-component';
import 'react-demo-component/lib/main.min.css'; // !需要引入樣式!
// import ReactDemo from '../../src'
const App = () => <ReactDemo />
render(<App />, document.getElementById('root'))
複製程式碼
- 出現下面的結果,則代表打包的元件功能驗證通過:
- 接下來將介紹喜聞樂見的最後一個環節,釋出元件到
NPM
!
釋出元件
配置.npmignore
- 如果專案中沒有編寫 .npmignore 檔案,則需要在 package.json 中新增
files
欄位,用於申明將要釋出到NPM
的檔案。如果省略掉這一項,所有檔案包括原始碼會被一起上傳到NPM
。 - 本文采用寫 .npmignore 檔案的方式,實現僅釋出打包後的元件程式碼。 .npmignore 檔案的具體內容如下:
# 指定釋出 npm 的時候需要忽略的檔案和資料夾
# npm 預設不會把 node_modules 發上去
config # webpack配置
example # 開發時預覽程式碼
src # 元件原始碼目錄
.babelrc # babel 配置
複製程式碼
註冊 npm 賬號
- 要釋出一個 npm 包,我們需使用如下命令新增一個 npm 的賬號(如果已經新增過的這一步可以跳過):
npm adduser
複製程式碼
- 按提示資訊輸入自己的使用者名稱、密碼及郵箱後,如果命令列輸出
Logged in as <user> on https://registry.npmjs.org/
,則說明賬號註冊並登陸成功。npm 會把登陸資訊記錄並暫存在/Users/<user>/.npmrc
配置檔案中。 - 如果已經有 npm 賬號,可以直接使用
npm login
登入。 - ⚠️注意:由於國內使用 npm 官方源安裝包的時候比較慢,基本上在國內開發都會修改 npm 源地址,在發包之前,一定要切換到 npm 源才可以,不然就會報出如下錯誤:
error: no_perms Private mode enable, only admin can publish this module
複製程式碼
- 可以使用
npm config list
檢視當前使用的源地址,如果不是官方源地址,則可以通過下面的命令切換 npm 源:
npm config set registry http://registry.npmjs.org
複製程式碼
- 也可以使用 nrm 來進行 npm 的源管理,礙於本文篇幅,這裡就不再進行解釋(傳送門?:使用NRM進行NPM的源管理)
- 成功切換到官方源後,則可以使用下面命令將我們的元件釋出到
NPM
:
npm run pub
// 上面的命令效果與下面的命令效果一樣
npm build
npm publish
複製程式碼
- 當看到下面的結果,則表示發包成功:
- 此時在 npm 上也可以檢視你剛釋出的包:
- 別人也可以使用下面的命令下載你的包:
npm i react-demo-component // 假設你的包名字叫react-demo-component
複製程式碼
- 使用方法:
// 元件中引入
import ReactDemo from 'react-demo-component';
// 如果給元件寫了樣式,需要手動匯入css檔案
import 'react-demo-component/lib/main.min.css';
複製程式碼
- 取消釋出(最好不要,別人可能下載了你的包):
npm unpublish react-demo-component --force // 假設你的包名字叫react-demo-component
複製程式碼
- 如果你想學習關於 vue 元件的發包,好文傳送?你可以寫一個自己的npm包。
- 原始碼地址:github.com/chongtianHo…
- 以上則是全文的介紹內容,相關配置均經過本人嘔心瀝血的親身實踐,如有問題歡迎留言。