本文主要記錄本人在使用 NPM
發佈具備樣式的 react組件 時的完整實踐流程,在這過程當中踩了許多坑,花在完善發佈腳手架的時間遠多於開發組件的時間,因而記錄下整個過程,但願能給你們提供幫助。css
下文的內容主要包括:html
NPM
。npm init
命令生成初始的配置文件 package.json,具體命令以下:mkdir react-component-npm-cli
cd react-component-npm-cli
npm init
複製代碼
npm init
命令 的時候,會提示咱們輸入項目的名稱、版本號、做者等,能夠一路回車,稍後進行修改。或者直接使用下面的命令採用默認配置,後面再根據本身的須要進行修改:npm init -y
複製代碼
├── 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
複製代碼
npm i react react-dom -D
複製代碼
npm i @babel/cli @babel/core @babel/preset-env @babel/preset-react -D
複製代碼
npm i webpack webpack-cli webpack-dev-server webpack-merge -D
複製代碼
npm i babel-loader mini-css-extract-plugin -D
複製代碼
{
"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.base.js
。webpack.dev.config.js
。webpack.prod.config.js
。webpack.base.js
中,該文件內容以下:module.exports = {
module: {
rules: [
{
// 使用 babel-loader 來編譯處理 js 和 jsx 文件
test: /\.(js|jsx)$/,
use: "babel-loader",
exclude: /node_modules/
}
]
},
};
複製代碼
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,
port: 3001, // 啓動端口爲 3001 的服務
open: true // 自動打開瀏覽器
},
};
module.exports = merge(devConfig, baseConfig); // 將baseConfig和devConfig合併爲一個配置
複製代碼
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合併爲一個配置
複製代碼
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
},
...
複製代碼
.babelrc
文件內補充如下內容:{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}
複製代碼
具備樣式
的組件,你們可根據本身的須要進行修改,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;
}
複製代碼
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
,當看到以下圖的結果,則表示編譯成功:
localhost:3001
則能夠看到下圖的結果:
npm run build
以前,還須要在 package.json 中修改 main
字段,做用是聲明組件的入口文件。開發者在
import
咱們的組件的時候會引入main
字段中export
的內容。vue
{
"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
打包咱們開發好的組件,出現下面的結果則表明打包成功:npm link
把打包以後的組件引入到全局 node_modules 中,相關命令以下:// At development directory
npm run build
npm link
複製代碼
npm link
命令後的結果以下:
// At development directory
cd example/src
npm link react-demo-component
複製代碼
/*** 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
!files
字段,用於申明將要發佈到 NPM
的文件。若是省略掉這一項,全部文件包括源代碼會被一塊兒上傳到 NPM
。# 指定發佈 npm 的時候須要忽略的文件和文件夾
# npm 默認不會把 node_modules 發上去
config # webpack配置
example # 開發時預覽代碼
src # 組件源代碼目錄
.babelrc # babel 配置
複製代碼
npm adduser
複製代碼
Logged in as <user> on https://registry.npmjs.org/
,則說明帳號註冊並登錄成功。npm 會把登錄信息記錄並暫存在 /Users/<user>/.npmrc
配置文件中。npm login
登陸。error: no_perms Private mode enable, only admin can publish this module
複製代碼
npm config list
查看當前使用的源地址,若是不是官方源地址,則能夠經過下面的命令切換 npm 源:npm config set registry http://registry.npmjs.org
複製代碼
NPM
:npm run pub
// 上面的命令效果與下面的命令效果同樣
npm build
npm publish
複製代碼
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
複製代碼